diff options
author | dartraiden <wowemuh@gmail.com> | 2023-01-02 21:10:29 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2023-01-02 21:10:29 +0300 |
commit | 1979fd80424d16b2e489f9b57d01d9c7811d25a2 (patch) | |
tree | 960d42c5fe4a51f0fe2850bea91256e226bce221 | |
parent | adfbbb217d4f4a05acf198755f219a5223d31c27 (diff) |
Update copyrights
1144 files changed, 104894 insertions, 104894 deletions
diff --git a/build/m_version.h.in b/build/m_version.h.in index 5d36bc5209..144cd82a14 100644 --- a/build/m_version.h.in +++ b/build/m_version.h.in @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (c) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/docs/readme.txt b/docs/readme.txt index 01db61c31d..e4521423ba 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -91,7 +91,7 @@ Miranda NG is released under the terms of the GNU General Public License. See "LICENSE" for more details.
Miranda IM is copyright 2000-2012 by the people listed in contributors.txt.
-Miranda NG is copyright 2012-2022 by the people listed in contributors.txt.
+Miranda NG is copyright 2012-2023 by the people listed in contributors.txt.
The plugins included with this release are copyrighted by their authors. See the
documentation for each plugin for more information.
diff --git a/include/chat_resource.h b/include/chat_resource.h index 8f4a2c1c56..fc31071881 100644 --- a/include/chat_resource.h +++ b/include/chat_resource.h @@ -1,36 +1,36 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#define IDC_SRMM_COLOR 3001 -#define IDC_SRMM_BKGCOLOR 3002 -#define IDC_SRMM_BOLD 3003 -#define IDC_SRMM_ITALICS 3004 -#define IDC_SRMM_UNDERLINE 3005 -#define IDC_SRMM_FILTER 3006 -#define IDC_SRMM_CHANMGR 3007 -#define IDC_SRMM_SHOWNICKLIST 3008 -#define IDC_SRMM_HISTORY 3009 -#define IDC_SRMM_NICKLIST 3010 -#define IDC_SRMM_LOG 3011 -#define IDC_SRMM_MESSAGE 3012 +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define IDC_SRMM_COLOR 3001
+#define IDC_SRMM_BKGCOLOR 3002
+#define IDC_SRMM_BOLD 3003
+#define IDC_SRMM_ITALICS 3004
+#define IDC_SRMM_UNDERLINE 3005
+#define IDC_SRMM_FILTER 3006
+#define IDC_SRMM_CHANMGR 3007
+#define IDC_SRMM_SHOWNICKLIST 3008
+#define IDC_SRMM_HISTORY 3009
+#define IDC_SRMM_NICKLIST 3010
+#define IDC_SRMM_LOG 3011
+#define IDC_SRMM_MESSAGE 3012
diff --git a/include/delphi/m_crypto.inc b/include/delphi/m_crypto.inc index 83dbb60d5a..cccc5baea0 100644 --- a/include/delphi/m_crypto.inc +++ b/include/delphi/m_crypto.inc @@ -1,7 +1,7 @@ {
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_avatars.h b/include/m_avatars.h index 72e8fe9d82..f27a4baf9d 100644 --- a/include/m_avatars.h +++ b/include/m_avatars.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-12 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_awaymsg.h b/include/m_awaymsg.h index becbb9107a..ce54edd711 100644 --- a/include/m_awaymsg.h +++ b/include/m_awaymsg.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_button.h b/include/m_button.h index 3e647ca1ad..222feafc86 100644 --- a/include/m_button.h +++ b/include/m_button.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_button_int.h b/include/m_button_int.h index 4b6996c961..8dd4a7dbe5 100644 --- a/include/m_button_int.h +++ b/include/m_button_int.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_chat_int.h b/include/m_chat_int.h index 31e2163d4e..135be0f7f8 100644 --- a/include/m_chat_int.h +++ b/include/m_chat_int.h @@ -2,7 +2,7 @@ Chat module interface for Miranda NG
-Copyright (c) 2014-22 George Hazan
+Copyright (c) 2014-23 George Hazan
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/include/m_clc.h b/include/m_clc.h index 3fbb1096cc..23c139cd76 100644 --- a/include/m_clc.h +++ b/include/m_clc.h @@ -1,282 +1,282 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_CLC_H__ -#define M_CLC_H__ 1 - -#define CLISTCONTROL_CLASS "CListControl" -#define CLISTCONTROL_CLASSW L"CListControl" - -//styles -#define CLS_MANUALUPDATE 0x0001 //todo -#define CLS_SHOWHIDDEN 0x0002 -#define CLS_HIDEOFFLINE 0x0004 //hides all offline users -#define CLS_CHECKBOXES 0x0008 -#define CLS_MULTICOLUMN 0x0010 //not true multi-column, just for ignore/vis options -#define CLS_HIDEEMPTYGROUPS 0x0020 //note: this flag will be spontaneously removed if the 'new subgroup' menu item is clicked, for obvious reasons -#define CLS_USEGROUPS 0x0040 -#define CLS_NOHIDEOFFLINE 0x0080 //overrides CLS_HIDEOFFLINE and the per-group hideoffline setting -#define CLS_GREYALTERNATE 0x0100 //make every other line slightly grey -#define CLS_GROUPCHECKBOXES 0x0200 //put checkboxes on groups too (managed by CLC) -#define CLS_CONTACTLIST 0x0400 //this control will be the main contact list (v. 0.3.4.3+ 2004/11/02) - -#define CLS_EX_DISABLEDRAGDROP 0x00000001 -#define CLS_EX_EDITLABELS 0x00000002 -#define CLS_EX_SHOWSELALWAYS 0x00000004 -#define CLS_EX_TRACKSELECT 0x00000008 -#define CLS_EX_SHOWGROUPCOUNTS 0x00000010 -#define CLS_EX_DIVIDERONOFF 0x00000020 -#define CLS_EX_HIDECOUNTSWHENEMPTY 0x00000040 -#define CLS_EX_NOTRANSLUCENTSEL 0x00000080 -#define CLS_EX_LINEWITHGROUPS 0x00000100 -#define CLS_EX_QUICKSEARCHVISONLY 0x00000200 -#define CLS_EX_SORTGROUPSALPHA 0x00000400 -#define CLS_EX_NOSMOOTHSCROLLING 0x00000800 - -#define CLM_FIRST 0x1000 //this is the same as LVM_FIRST -#define CLM_LAST 0x1100 - -//messages, compare with equivalent TVM_s in the MSDN -#define CLM_ADDCONTACT (CLM_FIRST+0) //wParam = hContact -#define CLM_ADDGROUP (CLM_FIRST+1) //wParam = hGroup -#define CLM_AUTOREBUILD (CLM_FIRST+2) -#define CLM_DELETEITEM (CLM_FIRST+3) //wParam = hItem -#define CLM_EDITLABEL (CLM_FIRST+4) //wParam = hItem -#define CLM_ENDEDITLABELNOW (CLM_FIRST+5) //wParam = cancel, 0 to save -#define CLM_ENSUREVISIBLE (CLM_FIRST+6) //wParam = hItem, lParam = partialOk -#define CLE_TOGGLE -1 -#define CLE_COLLAPSE 0 -#define CLE_EXPAND 1 -#define CLE_INVALID 0xFFFF -#define CLM_EXPAND (CLM_FIRST+7) //wParam = hItem, lParam = CLE_ -#define CLM_FINDCONTACT (CLM_FIRST+8) //wParam = hContact, returns an hItem -#define CLM_FINDGROUP (CLM_FIRST+9) //wParam = hGroup, returns an hItem -#define CLM_GETBKCOLOR (CLM_FIRST+10) //returns a COLORREF -#define CLM_GETCHECKMARK (CLM_FIRST+11) //wParam = hItem, returns 1 or 0 -#define CLM_GETCOUNT (CLM_FIRST+12) //returns the total number of items -#define CLM_GETEDITCONTROL (CLM_FIRST+13) //returns the HWND, or NULL -#define CLM_GETEXPAND (CLM_FIRST+14) //wParam = hItem, returns a CLE_, CLE_INVALID if not a group -#define CLM_GETEXTRACOLUMNS (CLM_FIRST+15) //returns number of extra columns -#define CLM_GETEXTRAIMAGE (CLM_FIRST+16) //wParam = hItem, lParam = MAKELPARAM(iColumn (0 based),0), returns iImage or EMPTY_EXTRA_ICON -#define CLM_GETEXTRAIMAGELIST (CLM_FIRST+17) //returns HIMAGELIST -#define CLM_GETFONT (CLM_FIRST+18) //wParam = fontId, see clm_setfont. returns hFont. -#define CLM_GETINDENT (CLM_FIRST+19) //wParam = new group indent -#define CLM_GETISEARCHSTRING (CLM_FIRST+20) //lParam = (char*)pszStr, max 120 bytes, returns number of chars in string -#define CLM_GETITEMTEXT (CLM_FIRST+21) //wParam = hItem, lParam = (wchar_t*)pszStr, max 120 bytes -#define CLM_GETSCROLLTIME (CLM_FIRST+22) //returns time in ms -#define CLM_GETSELECTION (CLM_FIRST+23) //returns hItem -#define CLM_SETEXTRASPACE (CLM_FIRST+24) //wParam=extra space between icons - -#define CLCHT_ABOVE 0x0001 //above client area -#define CLCHT_BELOW 0x0002 //below client area -#define CLCHT_TOLEFT 0x0004 //left of client area -#define CLCHT_TORIGHT 0x0008 //right of client area -#define CLCHT_NOWHERE 0x0010 //in client area, not on an item -#define CLCHT_ONITEMICON 0x0020 -#define CLCHT_ONITEMCHECK 0x0040 -#define CLCHT_ONITEMLABEL 0x0080 -#define CLCHT_ONITEMINDENT 0x0100 //to the left of an item icon -#define CLCHT_ONITEMEXTRA 0x0200 //on an extra icon, HIBYTE(HIWORD()) says which -#define CLCHT_ONITEM 0x03E0 -#define CLCHT_INLEFTMARGIN 0x0400 -#define CLCHT_BELOWITEMS 0x0800 //in client area but below last item -#define CLM_HITTEST (CLM_FIRST+25) //lParam = MAKELPARAM(x,y) (relative to control), wParam = (PDWORD)&hitTest (see encoding of HitTest() in clc.h, can be NULL) returns hItem or NULL -#define CLM_SELECTITEM (CLM_FIRST+26) //wParam = hItem -#define CLB_TOPLEFT 0 -#define CLB_STRETCHV 1 -#define CLB_STRETCHH 2 //and tile vertically -#define CLB_STRETCH 3 -#define CLBM_TYPE 0x00FF -#define CLBF_TILEH 0x1000 -#define CLBF_TILEV 0x2000 -#define CLBF_PROPORTIONAL 0x4000 -#define CLBF_SCROLL 0x8000 - -#define CLM_SETBKCOLOR (CLM_FIRST+28) //wParam = a COLORREF, default is GetSysColor(COLOR_3DFACE) -#define CLM_SETCHECKMARK (CLM_FIRST+29) //wParam = hItem, lParam = 1 or 0 -#define CLM_SETEXTRACOLUMNS (CLM_FIRST+30) //wParam = number of extra columns (zero to EXTRA_ICON_COUNT from clc.h, currently 16) -#define CLM_SETEXTRAIMAGE (CLM_FIRST+31) //wParam = hItem, lParam = MAKELPARAM(iColumn (0 based),iImage). iImage = EMPTY_EXTRA_ICON is a blank -#define CLM_SETEXTRAIMAGELIST (CLM_FIRST+32) //lParam = HIMAGELIST - -#define FONTID_CONTACTS 0 -#define FONTID_INVIS 1 -#define FONTID_OFFLINE 2 -#define FONTID_NOTONLIST 3 -#define FONTID_GROUPS 4 -#define FONTID_GROUPCOUNTS 5 -#define FONTID_DIVIDERS 6 -#define FONTID_OFFINVIS 7 -#define FONTID_STATUSMSG 8 -#define FONTID_GROUPSCLOSED 9 -#define FONTID_CONTACTSHOVER 10 -#define FONTID_MAX 18 - -#define CLM_SETFONT (CLM_FIRST+33) //wParam = hFont, lParam = MAKELPARAM(fRedraw,fontId) -#define CLM_SETITEMTEXT (CLM_FIRST+35) //wParam = hItem, lParam = (char*)pszNewText -#define CLM_SETSCROLLTIME (CLM_FIRST+36) //wParam = time in ms, default 200 - -#define CLM_SETHIDEEMPTYGROUPS (CLM_FIRST+38) //wParam = TRUE/FALSE -#define GREYF_UNFOCUS 0x80000000 -#define MODEF_OFFLINE 0x40000000 -//and use the PF2_ #defines from m_protosvc.h - -#define CLM_GETHIDEOFFLINEROOT (CLM_FIRST+40) //returns TRUE/FALSE -#define CLM_SETHIDEOFFLINEROOT (CLM_FIRST+41) //wParam = TRUE/FALSE -#define CLM_SETUSEGROUPS (CLM_FIRST+42) //wParam = TRUE/FALSE -#define CLM_SETOFFLINEMODES (CLM_FIRST+43) //for 'hide offline', wParam = PF2_ flags and MODEF_OFFLINE -#define CLM_GETEXSTYLE (CLM_FIRST+44) //returns CLS_EX_ flags -#define CLM_SETEXSTYLE (CLM_FIRST+45) //wParam = CLS_EX_ flags - -typedef struct { - int cbSize; - const wchar_t *pszText; - HANDLE hParentGroup; - uint32_t flags; - HICON hIcon; //todo -} CLCINFOITEM; -#define CLCIIF_BELOWGROUPS 1 //put it between groups and contacts, default is at top -#define CLCIIF_BELOWCONTACTS 2 //put it at the bottom -#define CLCIIF_CHECKBOX 0x40 //give this item a check box -#define CLCIIF_GROUPFONT 0x80 //draw the item using FONTID_GROUPS - -#define CLM_ADDINFOITEMA (CLM_FIRST+48) //lParam = &cii, returns hItem -#define CLM_ADDINFOITEMW (CLM_FIRST+53) //lParam = &cii, returns hItem -#if defined(_UNICODE) - #define CLM_ADDINFOITEM CLM_ADDINFOITEMW -#else - #define CLM_ADDINFOITEM CLM_ADDINFOITEMA -#endif - - //the order of info items is never changed, so make sure you add them in the - // order you want them to remain -#define CLCIT_INVALID -1 -#define CLCIT_GROUP 0 -#define CLCIT_CONTACT 1 -#define CLCIT_DIVIDER 2 -#define CLCIT_INFO 3 -#define CLM_GETITEMTYPE (CLM_FIRST+49) //wParam = hItem, returns a CLCIT_ -#define CLGN_ROOT 0 -#define CLGN_CHILD 1 -#define CLGN_PARENT 2 -#define CLGN_NEXT 3 -#define CLGN_PREVIOUS 4 -#define CLGN_NEXTCONTACT 5 -#define CLGN_PREVIOUSCONTACT 6 -#define CLGN_NEXTGROUP 7 -#define CLGN_PREVIOUSGROUP 8 - -#define CLM_GETNEXTITEM (CLM_FIRST+50) //wParam = flag, lParam = hItem, returns an hItem -#define CLM_GETTEXTCOLOR (CLM_FIRST+51) //wParam = FONTID_, returns COLORREF -#define CLM_SETTEXTCOLOR (CLM_FIRST+52) //wParam = FONTID_, lParam = COLORREF - -//notifications (most are omitted because the control processes everything) -#define CLNF_ISGROUP 1 -#define CLNF_ISINFO 2 -#ifdef _MSC_VER -typedef struct { - NMHDR hdr; - HANDLE hItem; - int action; - int iColumn; //-1 if not on an extra column - uint32_t flags; - POINT pt; -} NMCLISTCONTROL; -#endif -#define CLN_FIRST (0U-100U) -#define CLN_EXPANDED (CLN_FIRST-0) //hItem = hGroup, action = CLE_* -#define CLN_LISTREBUILT (CLN_FIRST-1) -#define CLN_ITEMCHECKED (CLN_FIRST-2) //todo //hItem, action, flags valid -#define CLN_DRAGGING (CLN_FIRST-3) //hItem, pt, flags valid. only sent when cursor outside window, return nonzero if processed -#define CLN_DROPPED (CLN_FIRST-4) //hItem, pt, flags valid. only sent when cursor outside window, return nonzero if processed -#define CLN_LISTSIZECHANGE (CLN_FIRST-5) //pt.y valid. the vertical height of the visible items in the list has changed. -#define CLN_OPTIONSCHANGED (CLN_FIRST-6) //nothing valid. If you set some extended options they have been overwritten and should be re-set -#define CLN_DRAGSTOP (CLN_FIRST-7) //hItem, flags valid. sent when cursor goes back in to the window having been outside, return nonzero if processed -#define CLN_NEWCONTACT (CLN_FIRST-8) //hItem, flags valid. sent when a new contact is added without a full list rebuild -#define CLN_CONTACTMOVED (CLN_FIRST-9) //hItem, flags valid. sent when contact is moved without a full list rebuild -#define CLN_CHECKCHANGED (CLN_FIRST-10) //hItem, flags valid. sent when any check mark is changed, but only for one change if there are many -//NM_CLICK //hItem, iColumn, pt, flags valid -//NM_KEYDOWN //NMKEY structure, only sent when key is not already processed, return nonzero to prevent further processing - -// clist window tree messages -#define M_CREATECLC (WM_USER+1) -#define M_SETALLEXTRAICONS (WM_USER+2) - -//an infotip for an item should be shown now -//wParam = 0 -//lParam = (LPARAM)(CLCINFOTIP*)&it -//Return nonzero if you process this, because it makes no sense for more than -//one plugin to grab it. -//It is up to the plugin to decide the best place to put the infotip. Normally -//it's a few pixels below and to the right of the cursor -//This event is called after the mouse has been stationary over a contact for -//(by default) 200ms, but see below. -//Everything is in screen coordinates. -typedef struct { - int cbSize; - int isTreeFocused; //so the plugin can provide an option - int isGroup; //0 if it's a contact, 1 if it's a group - HANDLE hItem; //handle to group or contact - POINT ptCursor; - RECT rcItem; -} CLCINFOTIP; -#define ME_CLC_SHOWINFOTIP "CLC/ShowInfoTip" - -typedef struct { - int cbSize; - int isTreeFocused; //so the plugin can provide an option - HANDLE hItem; //handle to group or contact - POINT ptCursor; - RECT rcItem; - int extraIndex; - HWND hwnd; -} CLCEXTRAINFOTIP; -#define ME_CLC_SHOWEXTRAINFOTIP "CLC/ShowExtraInfoTip" - -//it's time to destroy an infotip -//wParam = 0 -//lParam = (LPARAM)(CLCINFOTIP*)&it -//Only cbSize, isGroup and hItem are set -//Return nonzero if you process this. -//This is sent when the mouse moves off a contact when clc/showinfotip has -//previously been called. -//If you don't want this behaviour, you should have grabbed the mouse capture -//yourself and made your own arrangements. -#define ME_CLC_HIDEINFOTIP "CLC/HideInfoTip" - -//set the hover time before the infotip hooks are called -//wParam = newTime -//lParam = 0 -//Returns 0 on success or nonzero on failure -//The value of this setting is applied to all current CLC windows, and saved -//to be applied to all future windows, including after restarts. -//newTime is in ms. -//The default is 750ms. -#define MS_CLC_SETINFOTIPHOVERTIME "CLC/SetInfoTipHoverTime" - -//get the hover time before the infotip hooks are called -//wParam = lParam = 0 -//Returns the time in ms -#define MS_CLC_GETINFOTIPHOVERTIME "CLC/GetInfoTipHoverTime" - -#endif // M_CLC_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CLC_H__
+#define M_CLC_H__ 1
+
+#define CLISTCONTROL_CLASS "CListControl"
+#define CLISTCONTROL_CLASSW L"CListControl"
+
+//styles
+#define CLS_MANUALUPDATE 0x0001 //todo
+#define CLS_SHOWHIDDEN 0x0002
+#define CLS_HIDEOFFLINE 0x0004 //hides all offline users
+#define CLS_CHECKBOXES 0x0008
+#define CLS_MULTICOLUMN 0x0010 //not true multi-column, just for ignore/vis options
+#define CLS_HIDEEMPTYGROUPS 0x0020 //note: this flag will be spontaneously removed if the 'new subgroup' menu item is clicked, for obvious reasons
+#define CLS_USEGROUPS 0x0040
+#define CLS_NOHIDEOFFLINE 0x0080 //overrides CLS_HIDEOFFLINE and the per-group hideoffline setting
+#define CLS_GREYALTERNATE 0x0100 //make every other line slightly grey
+#define CLS_GROUPCHECKBOXES 0x0200 //put checkboxes on groups too (managed by CLC)
+#define CLS_CONTACTLIST 0x0400 //this control will be the main contact list (v. 0.3.4.3+ 2004/11/02)
+
+#define CLS_EX_DISABLEDRAGDROP 0x00000001
+#define CLS_EX_EDITLABELS 0x00000002
+#define CLS_EX_SHOWSELALWAYS 0x00000004
+#define CLS_EX_TRACKSELECT 0x00000008
+#define CLS_EX_SHOWGROUPCOUNTS 0x00000010
+#define CLS_EX_DIVIDERONOFF 0x00000020
+#define CLS_EX_HIDECOUNTSWHENEMPTY 0x00000040
+#define CLS_EX_NOTRANSLUCENTSEL 0x00000080
+#define CLS_EX_LINEWITHGROUPS 0x00000100
+#define CLS_EX_QUICKSEARCHVISONLY 0x00000200
+#define CLS_EX_SORTGROUPSALPHA 0x00000400
+#define CLS_EX_NOSMOOTHSCROLLING 0x00000800
+
+#define CLM_FIRST 0x1000 //this is the same as LVM_FIRST
+#define CLM_LAST 0x1100
+
+//messages, compare with equivalent TVM_s in the MSDN
+#define CLM_ADDCONTACT (CLM_FIRST+0) //wParam = hContact
+#define CLM_ADDGROUP (CLM_FIRST+1) //wParam = hGroup
+#define CLM_AUTOREBUILD (CLM_FIRST+2)
+#define CLM_DELETEITEM (CLM_FIRST+3) //wParam = hItem
+#define CLM_EDITLABEL (CLM_FIRST+4) //wParam = hItem
+#define CLM_ENDEDITLABELNOW (CLM_FIRST+5) //wParam = cancel, 0 to save
+#define CLM_ENSUREVISIBLE (CLM_FIRST+6) //wParam = hItem, lParam = partialOk
+#define CLE_TOGGLE -1
+#define CLE_COLLAPSE 0
+#define CLE_EXPAND 1
+#define CLE_INVALID 0xFFFF
+#define CLM_EXPAND (CLM_FIRST+7) //wParam = hItem, lParam = CLE_
+#define CLM_FINDCONTACT (CLM_FIRST+8) //wParam = hContact, returns an hItem
+#define CLM_FINDGROUP (CLM_FIRST+9) //wParam = hGroup, returns an hItem
+#define CLM_GETBKCOLOR (CLM_FIRST+10) //returns a COLORREF
+#define CLM_GETCHECKMARK (CLM_FIRST+11) //wParam = hItem, returns 1 or 0
+#define CLM_GETCOUNT (CLM_FIRST+12) //returns the total number of items
+#define CLM_GETEDITCONTROL (CLM_FIRST+13) //returns the HWND, or NULL
+#define CLM_GETEXPAND (CLM_FIRST+14) //wParam = hItem, returns a CLE_, CLE_INVALID if not a group
+#define CLM_GETEXTRACOLUMNS (CLM_FIRST+15) //returns number of extra columns
+#define CLM_GETEXTRAIMAGE (CLM_FIRST+16) //wParam = hItem, lParam = MAKELPARAM(iColumn (0 based),0), returns iImage or EMPTY_EXTRA_ICON
+#define CLM_GETEXTRAIMAGELIST (CLM_FIRST+17) //returns HIMAGELIST
+#define CLM_GETFONT (CLM_FIRST+18) //wParam = fontId, see clm_setfont. returns hFont.
+#define CLM_GETINDENT (CLM_FIRST+19) //wParam = new group indent
+#define CLM_GETISEARCHSTRING (CLM_FIRST+20) //lParam = (char*)pszStr, max 120 bytes, returns number of chars in string
+#define CLM_GETITEMTEXT (CLM_FIRST+21) //wParam = hItem, lParam = (wchar_t*)pszStr, max 120 bytes
+#define CLM_GETSCROLLTIME (CLM_FIRST+22) //returns time in ms
+#define CLM_GETSELECTION (CLM_FIRST+23) //returns hItem
+#define CLM_SETEXTRASPACE (CLM_FIRST+24) //wParam=extra space between icons
+
+#define CLCHT_ABOVE 0x0001 //above client area
+#define CLCHT_BELOW 0x0002 //below client area
+#define CLCHT_TOLEFT 0x0004 //left of client area
+#define CLCHT_TORIGHT 0x0008 //right of client area
+#define CLCHT_NOWHERE 0x0010 //in client area, not on an item
+#define CLCHT_ONITEMICON 0x0020
+#define CLCHT_ONITEMCHECK 0x0040
+#define CLCHT_ONITEMLABEL 0x0080
+#define CLCHT_ONITEMINDENT 0x0100 //to the left of an item icon
+#define CLCHT_ONITEMEXTRA 0x0200 //on an extra icon, HIBYTE(HIWORD()) says which
+#define CLCHT_ONITEM 0x03E0
+#define CLCHT_INLEFTMARGIN 0x0400
+#define CLCHT_BELOWITEMS 0x0800 //in client area but below last item
+#define CLM_HITTEST (CLM_FIRST+25) //lParam = MAKELPARAM(x,y) (relative to control), wParam = (PDWORD)&hitTest (see encoding of HitTest() in clc.h, can be NULL) returns hItem or NULL
+#define CLM_SELECTITEM (CLM_FIRST+26) //wParam = hItem
+#define CLB_TOPLEFT 0
+#define CLB_STRETCHV 1
+#define CLB_STRETCHH 2 //and tile vertically
+#define CLB_STRETCH 3
+#define CLBM_TYPE 0x00FF
+#define CLBF_TILEH 0x1000
+#define CLBF_TILEV 0x2000
+#define CLBF_PROPORTIONAL 0x4000
+#define CLBF_SCROLL 0x8000
+
+#define CLM_SETBKCOLOR (CLM_FIRST+28) //wParam = a COLORREF, default is GetSysColor(COLOR_3DFACE)
+#define CLM_SETCHECKMARK (CLM_FIRST+29) //wParam = hItem, lParam = 1 or 0
+#define CLM_SETEXTRACOLUMNS (CLM_FIRST+30) //wParam = number of extra columns (zero to EXTRA_ICON_COUNT from clc.h, currently 16)
+#define CLM_SETEXTRAIMAGE (CLM_FIRST+31) //wParam = hItem, lParam = MAKELPARAM(iColumn (0 based),iImage). iImage = EMPTY_EXTRA_ICON is a blank
+#define CLM_SETEXTRAIMAGELIST (CLM_FIRST+32) //lParam = HIMAGELIST
+
+#define FONTID_CONTACTS 0
+#define FONTID_INVIS 1
+#define FONTID_OFFLINE 2
+#define FONTID_NOTONLIST 3
+#define FONTID_GROUPS 4
+#define FONTID_GROUPCOUNTS 5
+#define FONTID_DIVIDERS 6
+#define FONTID_OFFINVIS 7
+#define FONTID_STATUSMSG 8
+#define FONTID_GROUPSCLOSED 9
+#define FONTID_CONTACTSHOVER 10
+#define FONTID_MAX 18
+
+#define CLM_SETFONT (CLM_FIRST+33) //wParam = hFont, lParam = MAKELPARAM(fRedraw,fontId)
+#define CLM_SETITEMTEXT (CLM_FIRST+35) //wParam = hItem, lParam = (char*)pszNewText
+#define CLM_SETSCROLLTIME (CLM_FIRST+36) //wParam = time in ms, default 200
+
+#define CLM_SETHIDEEMPTYGROUPS (CLM_FIRST+38) //wParam = TRUE/FALSE
+#define GREYF_UNFOCUS 0x80000000
+#define MODEF_OFFLINE 0x40000000
+//and use the PF2_ #defines from m_protosvc.h
+
+#define CLM_GETHIDEOFFLINEROOT (CLM_FIRST+40) //returns TRUE/FALSE
+#define CLM_SETHIDEOFFLINEROOT (CLM_FIRST+41) //wParam = TRUE/FALSE
+#define CLM_SETUSEGROUPS (CLM_FIRST+42) //wParam = TRUE/FALSE
+#define CLM_SETOFFLINEMODES (CLM_FIRST+43) //for 'hide offline', wParam = PF2_ flags and MODEF_OFFLINE
+#define CLM_GETEXSTYLE (CLM_FIRST+44) //returns CLS_EX_ flags
+#define CLM_SETEXSTYLE (CLM_FIRST+45) //wParam = CLS_EX_ flags
+
+typedef struct {
+ int cbSize;
+ const wchar_t *pszText;
+ HANDLE hParentGroup;
+ uint32_t flags;
+ HICON hIcon; //todo
+} CLCINFOITEM;
+#define CLCIIF_BELOWGROUPS 1 //put it between groups and contacts, default is at top
+#define CLCIIF_BELOWCONTACTS 2 //put it at the bottom
+#define CLCIIF_CHECKBOX 0x40 //give this item a check box
+#define CLCIIF_GROUPFONT 0x80 //draw the item using FONTID_GROUPS
+
+#define CLM_ADDINFOITEMA (CLM_FIRST+48) //lParam = &cii, returns hItem
+#define CLM_ADDINFOITEMW (CLM_FIRST+53) //lParam = &cii, returns hItem
+#if defined(_UNICODE)
+ #define CLM_ADDINFOITEM CLM_ADDINFOITEMW
+#else
+ #define CLM_ADDINFOITEM CLM_ADDINFOITEMA
+#endif
+
+ //the order of info items is never changed, so make sure you add them in the
+ // order you want them to remain
+#define CLCIT_INVALID -1
+#define CLCIT_GROUP 0
+#define CLCIT_CONTACT 1
+#define CLCIT_DIVIDER 2
+#define CLCIT_INFO 3
+#define CLM_GETITEMTYPE (CLM_FIRST+49) //wParam = hItem, returns a CLCIT_
+#define CLGN_ROOT 0
+#define CLGN_CHILD 1
+#define CLGN_PARENT 2
+#define CLGN_NEXT 3
+#define CLGN_PREVIOUS 4
+#define CLGN_NEXTCONTACT 5
+#define CLGN_PREVIOUSCONTACT 6
+#define CLGN_NEXTGROUP 7
+#define CLGN_PREVIOUSGROUP 8
+
+#define CLM_GETNEXTITEM (CLM_FIRST+50) //wParam = flag, lParam = hItem, returns an hItem
+#define CLM_GETTEXTCOLOR (CLM_FIRST+51) //wParam = FONTID_, returns COLORREF
+#define CLM_SETTEXTCOLOR (CLM_FIRST+52) //wParam = FONTID_, lParam = COLORREF
+
+//notifications (most are omitted because the control processes everything)
+#define CLNF_ISGROUP 1
+#define CLNF_ISINFO 2
+#ifdef _MSC_VER
+typedef struct {
+ NMHDR hdr;
+ HANDLE hItem;
+ int action;
+ int iColumn; //-1 if not on an extra column
+ uint32_t flags;
+ POINT pt;
+} NMCLISTCONTROL;
+#endif
+#define CLN_FIRST (0U-100U)
+#define CLN_EXPANDED (CLN_FIRST-0) //hItem = hGroup, action = CLE_*
+#define CLN_LISTREBUILT (CLN_FIRST-1)
+#define CLN_ITEMCHECKED (CLN_FIRST-2) //todo //hItem, action, flags valid
+#define CLN_DRAGGING (CLN_FIRST-3) //hItem, pt, flags valid. only sent when cursor outside window, return nonzero if processed
+#define CLN_DROPPED (CLN_FIRST-4) //hItem, pt, flags valid. only sent when cursor outside window, return nonzero if processed
+#define CLN_LISTSIZECHANGE (CLN_FIRST-5) //pt.y valid. the vertical height of the visible items in the list has changed.
+#define CLN_OPTIONSCHANGED (CLN_FIRST-6) //nothing valid. If you set some extended options they have been overwritten and should be re-set
+#define CLN_DRAGSTOP (CLN_FIRST-7) //hItem, flags valid. sent when cursor goes back in to the window having been outside, return nonzero if processed
+#define CLN_NEWCONTACT (CLN_FIRST-8) //hItem, flags valid. sent when a new contact is added without a full list rebuild
+#define CLN_CONTACTMOVED (CLN_FIRST-9) //hItem, flags valid. sent when contact is moved without a full list rebuild
+#define CLN_CHECKCHANGED (CLN_FIRST-10) //hItem, flags valid. sent when any check mark is changed, but only for one change if there are many
+//NM_CLICK //hItem, iColumn, pt, flags valid
+//NM_KEYDOWN //NMKEY structure, only sent when key is not already processed, return nonzero to prevent further processing
+
+// clist window tree messages
+#define M_CREATECLC (WM_USER+1)
+#define M_SETALLEXTRAICONS (WM_USER+2)
+
+//an infotip for an item should be shown now
+//wParam = 0
+//lParam = (LPARAM)(CLCINFOTIP*)&it
+//Return nonzero if you process this, because it makes no sense for more than
+//one plugin to grab it.
+//It is up to the plugin to decide the best place to put the infotip. Normally
+//it's a few pixels below and to the right of the cursor
+//This event is called after the mouse has been stationary over a contact for
+//(by default) 200ms, but see below.
+//Everything is in screen coordinates.
+typedef struct {
+ int cbSize;
+ int isTreeFocused; //so the plugin can provide an option
+ int isGroup; //0 if it's a contact, 1 if it's a group
+ HANDLE hItem; //handle to group or contact
+ POINT ptCursor;
+ RECT rcItem;
+} CLCINFOTIP;
+#define ME_CLC_SHOWINFOTIP "CLC/ShowInfoTip"
+
+typedef struct {
+ int cbSize;
+ int isTreeFocused; //so the plugin can provide an option
+ HANDLE hItem; //handle to group or contact
+ POINT ptCursor;
+ RECT rcItem;
+ int extraIndex;
+ HWND hwnd;
+} CLCEXTRAINFOTIP;
+#define ME_CLC_SHOWEXTRAINFOTIP "CLC/ShowExtraInfoTip"
+
+//it's time to destroy an infotip
+//wParam = 0
+//lParam = (LPARAM)(CLCINFOTIP*)&it
+//Only cbSize, isGroup and hItem are set
+//Return nonzero if you process this.
+//This is sent when the mouse moves off a contact when clc/showinfotip has
+//previously been called.
+//If you don't want this behaviour, you should have grabbed the mouse capture
+//yourself and made your own arrangements.
+#define ME_CLC_HIDEINFOTIP "CLC/HideInfoTip"
+
+//set the hover time before the infotip hooks are called
+//wParam = newTime
+//lParam = 0
+//Returns 0 on success or nonzero on failure
+//The value of this setting is applied to all current CLC windows, and saved
+//to be applied to all future windows, including after restarts.
+//newTime is in ms.
+//The default is 750ms.
+#define MS_CLC_SETINFOTIPHOVERTIME "CLC/SetInfoTipHoverTime"
+
+//get the hover time before the infotip hooks are called
+//wParam = lParam = 0
+//Returns the time in ms
+#define MS_CLC_GETINFOTIPHOVERTIME "CLC/GetInfoTipHoverTime"
+
+#endif // M_CLC_H__
diff --git a/include/m_clist.h b/include/m_clist.h index 6967401e0e..3eb05692cc 100644 --- a/include/m_clist.h +++ b/include/m_clist.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_clistint.h b/include/m_clistint.h index 55e9f5c26e..f2d8c70467 100644 --- a/include/m_clistint.h +++ b/include/m_clistint.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_cluiframes.h b/include/m_cluiframes.h index b7e9ae2836..0944c2632c 100644 --- a/include/m_cluiframes.h +++ b/include/m_cluiframes.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-02 Richard Hughes, Roland Rabien & Tristan Van de Vreede
This program is free software; you can redistribute it and/or
diff --git a/include/m_contacts.h b/include/m_contacts.h index 49e2f98566..3cea1e17d6 100644 --- a/include/m_contacts.h +++ b/include/m_contacts.h @@ -1,7 +1,7 @@ /*
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/include/m_core.h b/include/m_core.h index 9d00b7463f..6f2b88f631 100644 --- a/include/m_core.h +++ b/include/m_core.h @@ -1,673 +1,673 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_CORE_H__ -#define M_CORE_H__ 1 - -#ifdef _MSC_VER - #include <sal.h> -#endif - -#include <stdarg.h> -#include <stdint.h> -#include <stdlib.h> - -#ifndef M_TYPES_H__ - #include <m_types.h> -#endif - -#ifdef MIR_CORE_EXPORTS - #define MIR_CORE_EXPORT MIR_EXPORT -#else - #define MIR_CORE_EXPORT MIR_IMPORT -#endif - -#define MIR_CORE_DLL(T) MIR_CORE_EXPORT T MIR_SYSCALL -#define MIR_C_CORE_DLL(T) MIR_CORE_EXPORT T MIR_CDECL - -#ifdef MIR_APP_EXPORTS - #define MIR_APP_EXPORT MIR_EXPORT - typedef struct NetlibUser* HNETLIBUSER; - typedef struct NetlibConnection* HNETLIBCONN; - typedef struct NetlibBoundPort* HNETLIBBIND; -#else - #define MIR_APP_EXPORT MIR_IMPORT - DECLARE_HANDLE(HNETLIBUSER); - DECLARE_HANDLE(HNETLIBCONN); - DECLARE_HANDLE(HNETLIBBIND); -#endif - -typedef struct TMO_IntMenuItem* HGENMENU; - -class CMPluginBase; -typedef const CMPluginBase* HPLUGIN; - -#define MIR_APP_DLL(T) MIR_APP_EXPORT T MIR_SYSCALL - -#pragma warning(disable:4201 4127 4312 4706) - -#if defined(__cplusplus) -extern "C" -{ -#endif - -/////////////////////////////////////////////////////////////////////////////// -// command line support - -MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine); -MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t *ptszParameter); - -/////////////////////////////////////////////////////////////////////////////// -// database functions - -typedef uint32_t MCONTACT; -#define INVALID_CONTACT_ID (MCONTACT(-1)) - -typedef uint32_t MEVENT; - -/////////////////////////////////////////////////////////////////////////////// -// events, hooks & services - -#define MAXMODULELABELLENGTH 64 - -typedef int (*MIRANDAHOOK)(WPARAM, LPARAM); -typedef int (*MIRANDAHOOKPARAM)(WPARAM, LPARAM, LPARAM); -typedef int (*MIRANDAHOOKOBJ)(void*, WPARAM, LPARAM); -typedef int (*MIRANDAHOOKOBJPARAM)(void*, WPARAM, LPARAM, LPARAM); - -typedef INT_PTR (*MIRANDASERVICE)(WPARAM, LPARAM); -typedef INT_PTR (*MIRANDASERVICEPARAM)(WPARAM, LPARAM, LPARAM); -typedef INT_PTR (*MIRANDASERVICEOBJ)(void*, WPARAM, LPARAM); -typedef INT_PTR (*MIRANDASERVICEOBJPARAM)(void*, WPARAM, LPARAM, LPARAM); - -#ifdef _WIN64 - #define CALLSERVICE_NOTFOUND ((INT_PTR)0x8000000000000000) -#else - #define CALLSERVICE_NOTFOUND ((int)0x80000000) -#endif - -MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name); -MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent); -MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook); -MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam = 0, LPARAM lParam = 0); -MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam = 0, LPARAM lParam = 0); -MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam = 0, LPARAM lParam = 0); -MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam = 0, LPARAM lParam = 0); - -MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc); -MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam = 0); -MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object); -MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam); -MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message); - -// executes the event handler if event is missing -MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc); - -MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook); -MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject); -MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE pModule); - -MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc); -MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam); -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object); -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam); -MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc); -MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService); -MIR_CORE_DLL(bool) ServiceExists(const char *name); - -MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam = 0, LPARAM lParam = 0); -MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam = 0, LPARAM lParam = 0); - -MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(MIR_SYSCALL *func)(void *), void *arg); -MIR_CORE_DLL(int) CallFunctionAsync(void (MIR_SYSCALL *func)(void *), void *arg); -MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst); -MIR_CORE_DLL(void) KillObjectServices(void* pObject); - -MIR_APP_DLL(int) ProtoServiceExists(const char *szModule, const char *szService); -MIR_APP_DLL(INT_PTR) CallProtoService(const char *szModule, const char *szService, WPARAM wParam = 0, LPARAM lParam = 0); - -/////////////////////////////////////////////////////////////////////////////// -// exceptions - -typedef uint32_t (MIR_CDECL *pfnExceptionFilter)(uint32_t code, EXCEPTION_POINTERS *info); - -MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter(void); -MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter pMirandaExceptFilter); - -/////////////////////////////////////////////////////////////////////////////// -// icons support - -struct IconItem -{ - char *szDescr, *szName; - int defIconID, size; - HANDLE hIcolib; -}; - -struct IconItemT -{ - wchar_t *tszDescr; - char *szName; - int defIconID, size; - HANDLE hIcolib; -}; - -MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin); -MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin); - -/////////////////////////////////////////////////////////////////////////////// -// language packs support - -MIR_CORE_DLL(unsigned int) mir_hash(const void *key, unsigned int len); - -#pragma optimize("gt", on) -__forceinline unsigned int mir_hashstr(const char *key) -{ - if (key == nullptr) return 0; - else { - unsigned int len = (unsigned int)strlen((const char*)key); - return mir_hash(key, len); -} } - -__forceinline unsigned int mir_hashstrW(const wchar_t *key) -{ - if (key == nullptr) return 0; - else { - unsigned int len = (unsigned int)wcslen((const wchar_t*)key); - return mir_hash(key, len * sizeof(wchar_t)); -} } -#pragma optimize("", on) - -#define mir_hashstrT mir_hashstrW - -/////////////////////////////////////////////////////////////////////////////// -// lists - -typedef int (*FSortFunc)(void*, void*); // sort function prototype - -// Assumes first 32 bit value of the data is the numeric key -// and uses it to perform sort/search operations, this results -// in much better performance as no compare function calls needed -// Incredibly useful for Hash Tables -#define NumericKeySort (FSortFunc)(void*) -1 -#define HandleKeySort (FSortFunc)(void*) -2 -#define PtrKeySort (FSortFunc)(void*) -3 - -typedef struct -{ - void** items; - int realCount; - int limit; - int increment; - - FSortFunc sortFunc; -} - SortedList; - -MIR_CORE_DLL(SortedList*) List_Create(int p_limit, int p_increment); -MIR_CORE_DLL(void) List_Destroy(SortedList* p_list); -MIR_CORE_DLL(void*) List_Find(SortedList* p_list, void* p_value); -MIR_CORE_DLL(int) List_GetIndex(SortedList* p_list, void* p_value, int* p_index); -MIR_CORE_DLL(int) List_IndexOf(SortedList* p_list, void* p_value); -MIR_CORE_DLL(int) List_Insert(SortedList* p_list, void* p_value, int p_index); -MIR_CORE_DLL(int) List_InsertPtr(SortedList* list, void* p); -MIR_CORE_DLL(int) List_Remove(SortedList* p_list, int index); -MIR_CORE_DLL(int) List_RemovePtr(SortedList* list, void* p); -MIR_CORE_DLL(void) List_Copy(SortedList* s, SortedList* d, size_t itemSize); -MIR_CORE_DLL(void) List_ObjCopy(SortedList* s, SortedList* d, size_t itemSize); - -/////////////////////////////////////////////////////////////////////////////// -// logging functions - -MIR_CORE_DLL(HANDLE) mir_createLog(const char *pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options); -MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger); - -MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...); -MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...); - -MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args); -MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args); - -/////////////////////////////////////////////////////////////////////////////// -// md5 functions - -typedef struct mir_md5_state_s { - uint32_t count[2]; /* message length in bits, lsw first */ - uint32_t abcd[4]; /* digest buffer */ - uint8_t buf[64]; /* accumulate block */ -} mir_md5_state_t; - -MIR_CORE_DLL(void) mir_md5_init(mir_md5_state_t *pms); -MIR_CORE_DLL(void) mir_md5_append(mir_md5_state_t *pms, const uint8_t *data, size_t nbytes); -MIR_CORE_DLL(void) mir_md5_finish(mir_md5_state_t *pms, uint8_t digest[16]); -MIR_CORE_DLL(void) mir_md5_hash(const uint8_t *data, size_t len, uint8_t digest[16]); - -/////////////////////////////////////////////////////////////////////////////// -// memory functions - -MIR_C_CORE_DLL(void*) mir_alloc(size_t); -MIR_C_CORE_DLL(void*) mir_calloc(size_t); -MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t); -MIR_C_CORE_DLL(void) mir_free(void* ptr); - -MIR_CORE_DLL(size_t) mir_strlen(const char *p); -MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p); - -MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src); -MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src); - -MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len); -MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len); - -MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src); -MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src); - -MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len); -MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len); - -MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2); -MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n); -MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2); -MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n); - -MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2); -MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n); -MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2); -MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n); - -MIR_CORE_DLL(char*) mir_strdup(const char* str); -MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t* str); - -MIR_CORE_DLL(char*) mir_strndup(const char* str, size_t len); -MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len); - -MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2); - -/////////////////////////////////////////////////////////////////////////////// -// print functions - -MIR_CORE_DLL(int) mir_snprintf(_Pre_notnull_ _Always_(_Post_z_) char *buffer, size_t count, _Printf_format_string_ const char* fmt, ...); -MIR_CORE_DLL(int) mir_snwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t *buffer, size_t count, _Printf_format_string_ const wchar_t* fmt, ...); -MIR_CORE_DLL(int) mir_vsnprintf(_Pre_notnull_ _Always_(_Post_z_) char *buffer, size_t count, _Printf_format_string_ const char* fmt, va_list va); -MIR_CORE_DLL(int) mir_vsnwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t *buffer, size_t count, _Printf_format_string_ const wchar_t* fmt, va_list va); - -/////////////////////////////////////////////////////////////////////////////// -// protocol functions - -struct PROTO_INTERFACE; - -MIR_APP_DLL(INT_PTR) ProtoBroadcastAck(const char *szModule, MCONTACT hContact, int type, int result, HANDLE hProcess, LPARAM lParam = 0); -MIR_APP_DLL(void) ProtoBroadcastAsync(const char *szModule, MCONTACT hContact, int type, int result, HANDLE hProcess, LPARAM lParam = 0); - -// avatar support functions - -// returns image extension by a PA_* constant or empty string for PA_FORMAT_UNKNOWN -MIR_APP_DLL(const wchar_t*) ProtoGetAvatarExtension(int format); - -// detects image format by extension -MIR_APP_DLL(int) ProtoGetAvatarFormat(const wchar_t *ptszFileName); - -// detects image format by its contents -MIR_APP_DLL(int) ProtoGetAvatarFileFormat(const wchar_t *ptszFileName); - -// returns the mime type according to a picture type (PA_*) passed -MIR_APP_DLL(const char*) ProtoGetAvatarMimeType(int iFileType); - -// returns the picture type (PA_*) according to a mime type passed -MIR_APP_DLL(int) ProtoGetAvatarFormatByMimeType(const char *pwszMimeType); - -// returns the image format and extension by the first bytes of picture -// ptszExtension might be NULL -#if defined( __cplusplus ) - MIR_APP_DLL(int) ProtoGetBufferFormat(const void *buf, const wchar_t **ptszExtension = nullptr); -#else - MIR_APP_DLL(int) ProtoGetBufferFormat(const void *buf, const wchar_t **ptszExtension); -#endif - -/////////////////////////////////////////////////////////////////////////////// -// sha1 functions - -#define MIR_SHA1_HASH_SIZE 20 -#define MIR_SHA_BLOCKSIZE 64 - -struct mir_sha1_ctx -{ - uint32_t H[5]; - uint32_t W[80]; - int lenW; - uint32_t sizeHi, sizeLo; -}; - -MIR_CORE_DLL(void) mir_sha1_init(mir_sha1_ctx *ctx); -MIR_CORE_DLL(void) mir_sha1_append(mir_sha1_ctx *ctx, const uint8_t *dataIn, size_t len); -MIR_CORE_DLL(void) mir_sha1_finish(mir_sha1_ctx *ctx, uint8_t hashout[MIR_SHA1_HASH_SIZE]); -MIR_CORE_DLL(void) mir_sha1_hash(uint8_t *dataIn, size_t len, uint8_t hashout[MIR_SHA1_HASH_SIZE]); - -/////////////////////////////////////////////////////////////////////////////// -// sha256 functions - -#define MIR_SHA256_HASH_SIZE 32 - -struct SHA256_CONTEXT -{ - uint32_t h0, h1, h2, h3, h4, h5, h6, h7; - uint32_t nblocks; - uint8_t buf[MIR_SHA_BLOCKSIZE]; - int count; -}; - -MIR_CORE_DLL(void) mir_sha256_init(SHA256_CONTEXT *ctx); -MIR_CORE_DLL(void) mir_sha256_write(SHA256_CONTEXT *ctx, const void *dataIn, size_t len); -MIR_CORE_DLL(void) mir_sha256_final(SHA256_CONTEXT *ctx, uint8_t hashout[MIR_SHA256_HASH_SIZE]); -MIR_CORE_DLL(void) mir_sha256_hash(const void *dataIn, size_t len, uint8_t hashout[MIR_SHA256_HASH_SIZE]); - -/////////////////////////////////////////////////////////////////////////////// -// strings - -MIR_CORE_DLL(void*) mir_base64_decode(const char *input, size_t *outputLen); -MIR_CORE_DLL(char*) mir_base64_encode(const void *input, size_t inputLen); -MIR_CORE_DLL(char*) mir_base64_encodebuf(const void *input, size_t inputLen, char *output, size_t outLen); - -__forceinline size_t mir_base64_encode_bufsize(size_t inputLen) -{ - return 4 * ((inputLen + 2) / 3) + 1; -} - -MIR_CORE_DLL(char*) rtrim(char *str); -MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str); - -MIR_CORE_DLL(char*) ltrim(char *str); // returns pointer to the beginning of string -MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str); - -MIR_CORE_DLL(char*) ltrimp(char *str); // returns pointer to the trimmed portion of string -MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str); - -MIR_CORE_DLL(char*) strdel(char *str, size_t len); -MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len); - -MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask); -MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask); - -MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask); -MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask); - -MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest); -MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest); - -MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len); -MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len); - -__forceinline char* lrtrim(char *str) { return ltrim(rtrim(str)); }; -__forceinline char* lrtrimp(char *str) { return ltrimp(rtrim(str)); }; - -#if defined( __cplusplus ) - MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src); - MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src); -#else - MIR_CORE_DLL(char*) replaceStr(char **dest, const char *src); - MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t **dest, const wchar_t *src); -#endif - -#ifndef _MSC_VER - MIR_CORE_DLL(char*) strlwr(char *str); - MIR_CORE_DLL(char*) strupr(char *str); - MIR_CORE_DLL(char*) strrev(char *str); -#endif - -/////////////////////////////////////////////////////////////////////////////// -// text conversion functions - -union MAllStrings -{ - char *a; // utf8 or ansi strings - wchar_t *w; // strings of WCHARs -}; - -union MAllCStrings -{ - const char *a; // utf8 or ansi strings - const wchar_t *w; // strings of WCHARs -}; - -union MAllStringArray -{ - char **a; // array of utf8 or ansi strings - wchar_t **w; // array of strings of WCHARs -}; - -union MAllCStringArray -{ - const char **a; // array of utf8 or ansi strings - const wchar_t **w; // array of strings of WCHARs -}; - -MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage); -MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src); -MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage); -MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src); - -/////////////////////////////////////////////////////////////////////////////// -// threads - -typedef void (MIR_CDECL *pThreadFunc)(void *param); -typedef unsigned (MIR_SYSCALL *pThreadFuncEx)(void *param); -typedef unsigned (MIR_CDECL *pThreadFuncOwner)(void *owner, void *param); - -#if defined( __cplusplus ) - MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner = nullptr); -#else - MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner); -#endif -MIR_CORE_DLL(INT_PTR) Thread_Pop(void); -MIR_CORE_DLL(void) Thread_Wait(void); - -#if defined( __cplusplus ) -MIR_CORE_DLL(HANDLE) mir_forkthread(pThreadFunc aFunc, void *arg = nullptr); -MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void *arg = nullptr, unsigned *pThreadID = nullptr); -MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg = nullptr, unsigned *pThreadID = nullptr); -#else -MIR_CORE_DLL(HANDLE) mir_forkthread(pThreadFunc aFunc, void *arg); -MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void *arg, unsigned *pThreadID); -MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID); -#endif - -MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName); - -MIR_CORE_DLL(void) KillObjectThreads(void* pObject); - -/////////////////////////////////////////////////////////////////////////////// -// utf8 interface - -MIR_CORE_DLL(BOOL) Utf8CheckString(const char* str); -MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen); // returns 0 on error - -MIR_CORE_DLL(char*) mir_utf8decode(char* str, wchar_t** ucs2); -MIR_CORE_DLL(char*) mir_utf8decodecp(char* str, int codepage, wchar_t** ucs2); -MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char* str); - -MIR_CORE_DLL(char*) mir_utf8encode(const char* str); -MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage); -MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* str); - -MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src); - -__forceinline char* mir_utf8decodeA(const char* src) -{ - char *tmp = mir_strdup(src); - mir_utf8decode(tmp, nullptr); - return tmp; -} - -/////////////////////////////////////////////////////////////////////////////// -// Window subclassing - -#ifdef _MSC_VER - -MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc); -MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc); -MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam); -MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc); - -MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst); - -/////////////////////////////////////////////////////////////////////////////// -// Windows utilities - -MIR_CORE_DLL(BOOL) IsWinVerVistaPlus(); -MIR_CORE_DLL(BOOL) IsWinVer7Plus(); -MIR_CORE_DLL(BOOL) IsWinVer8Plus(); -MIR_CORE_DLL(BOOL) IsWinVer81Plus(); -MIR_CORE_DLL(BOOL) IsWinVer10Plus(); - -MIR_CORE_DLL(BOOL) IsFullScreen(); -MIR_CORE_DLL(BOOL) IsWorkstationLocked(); -MIR_CORE_DLL(BOOL) IsScreenSaverRunning(); -MIR_CORE_DLL(BOOL) IsTerminalDisconnected(); - -#endif // _MSC_VER - -// returns OS version in version of Windows NT xx.xx -MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize); - -// returns full OS version -MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize); - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) UnloadCoreModule(void); - -#if defined(__cplusplus) -} - -/////////////////////////////////////////////////////////////////////////////// -// The UUID structure below is used to for plugin UUID's and module type definitions - -struct MUUID -{ - unsigned long a; - unsigned short b; - unsigned short c; - unsigned char d[8]; -}; - -__forceinline bool operator==(const MUUID& p1, const MUUID& p2) -{ - return memcmp(&p1, &p2, sizeof(MUUID)) == 0; -} -__forceinline bool operator!=(const MUUID& p1, const MUUID& p2) -{ - return memcmp(&p1, &p2, sizeof(MUUID)) != 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// C++ templates - -template <typename T> -HANDLE mir_forkThread(void(MIR_CDECL *pFunc)(T* param), T *arg) -{ - return mir_forkthread((pThreadFunc)pFunc, arg); -} - -template <size_t _Size> -inline int mir_snprintf(_Pre_notnull_ _Always_(_Post_z_) char(&buffer)[_Size], _In_z_ _Printf_format_string_ const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - int ret = mir_vsnprintf(buffer, _Size, fmt, args); - va_end(args); - return ret; -} - -template <size_t _Size> -inline int mir_snwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t(&buffer)[_Size], _In_z_ _Printf_format_string_ const wchar_t* fmt, ...) -{ - va_list args; - va_start(args, fmt); - int ret = mir_vsnwprintf(buffer, _Size, fmt, args); - va_end(args); - return ret; -} - -template <size_t _Size> -inline int mir_vsnprintf(_Pre_notnull_ _Always_(_Post_z_) char(&buffer)[_Size], _In_z_ _Printf_format_string_ const char* fmt, va_list va) -{ - return mir_vsnprintf(buffer, _Size, fmt, va); -} - -template <size_t _Size> -inline int mir_vsnwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t(&buffer)[_Size], _In_z_ _Printf_format_string_ const wchar_t* fmt, va_list va) -{ - return mir_vsnwprintf(buffer, _Size, fmt, va); -} - -MIR_CORE_DLL(char *) mir_base64_encode(const class MBinBuffer &buf); - -#endif - -#ifdef _MSC_VER - #ifndef MIR_CORE_EXPORTS - #pragma comment(lib, "mir_core.lib") - #endif - - #ifndef MIR_APP_EXPORTS - #pragma comment(lib, "mir_app.lib") - #endif -#else - MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode); - MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath); - - template <size_t _Size> - inline wchar_t* wcsncpy_s(wchar_t(&buffer)[_Size], const wchar_t *src, size_t len) - { - return wcsncpy(buffer, src, (len == _TRUNCATE) ? _Size : len); - } - - inline wchar_t* wcsncpy_s(wchar_t *dst, size_t dstLen, const wchar_t *src, size_t len) - { - return wcsncpy(dst, src, (len == _TRUNCATE) ? dstLen : len); - } - - inline wchar_t* wcsncat_s(wchar_t *dst, size_t dstLen, const wchar_t *src, size_t len) - { - return wcsncat(dst, src, (len == _TRUNCATE) ? dstLen : len); - } - - template <size_t _Size> - inline char* strncpy_s(char(&buffer)[_Size], const char *src, size_t len) - { - return strncpy(buffer, src, (len == _TRUNCATE) ? _Size : len); - } - - inline char* strncpy_s(char *dst, size_t dstLen, const char *src, size_t len) - { - return strncpy(dst, src, (len == _TRUNCATE) ? dstLen : len); - } - - inline char* strncat_s(char *dst, size_t dstLen, const char *src, size_t len) - { - return strncat(dst, src, (len == _TRUNCATE) ? dstLen : len); - } -#endif - -#endif // M_CORE_H +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CORE_H__
+#define M_CORE_H__ 1
+
+#ifdef _MSC_VER
+ #include <sal.h>
+#endif
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifndef M_TYPES_H__
+ #include <m_types.h>
+#endif
+
+#ifdef MIR_CORE_EXPORTS
+ #define MIR_CORE_EXPORT MIR_EXPORT
+#else
+ #define MIR_CORE_EXPORT MIR_IMPORT
+#endif
+
+#define MIR_CORE_DLL(T) MIR_CORE_EXPORT T MIR_SYSCALL
+#define MIR_C_CORE_DLL(T) MIR_CORE_EXPORT T MIR_CDECL
+
+#ifdef MIR_APP_EXPORTS
+ #define MIR_APP_EXPORT MIR_EXPORT
+ typedef struct NetlibUser* HNETLIBUSER;
+ typedef struct NetlibConnection* HNETLIBCONN;
+ typedef struct NetlibBoundPort* HNETLIBBIND;
+#else
+ #define MIR_APP_EXPORT MIR_IMPORT
+ DECLARE_HANDLE(HNETLIBUSER);
+ DECLARE_HANDLE(HNETLIBCONN);
+ DECLARE_HANDLE(HNETLIBBIND);
+#endif
+
+typedef struct TMO_IntMenuItem* HGENMENU;
+
+class CMPluginBase;
+typedef const CMPluginBase* HPLUGIN;
+
+#define MIR_APP_DLL(T) MIR_APP_EXPORT T MIR_SYSCALL
+
+#pragma warning(disable:4201 4127 4312 4706)
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// command line support
+
+MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine);
+MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t *ptszParameter);
+
+///////////////////////////////////////////////////////////////////////////////
+// database functions
+
+typedef uint32_t MCONTACT;
+#define INVALID_CONTACT_ID (MCONTACT(-1))
+
+typedef uint32_t MEVENT;
+
+///////////////////////////////////////////////////////////////////////////////
+// events, hooks & services
+
+#define MAXMODULELABELLENGTH 64
+
+typedef int (*MIRANDAHOOK)(WPARAM, LPARAM);
+typedef int (*MIRANDAHOOKPARAM)(WPARAM, LPARAM, LPARAM);
+typedef int (*MIRANDAHOOKOBJ)(void*, WPARAM, LPARAM);
+typedef int (*MIRANDAHOOKOBJPARAM)(void*, WPARAM, LPARAM, LPARAM);
+
+typedef INT_PTR (*MIRANDASERVICE)(WPARAM, LPARAM);
+typedef INT_PTR (*MIRANDASERVICEPARAM)(WPARAM, LPARAM, LPARAM);
+typedef INT_PTR (*MIRANDASERVICEOBJ)(void*, WPARAM, LPARAM);
+typedef INT_PTR (*MIRANDASERVICEOBJPARAM)(void*, WPARAM, LPARAM, LPARAM);
+
+#ifdef _WIN64
+ #define CALLSERVICE_NOTFOUND ((INT_PTR)0x8000000000000000)
+#else
+ #define CALLSERVICE_NOTFOUND ((int)0x80000000)
+#endif
+
+MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name);
+MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent);
+MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook);
+MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam = 0, LPARAM lParam = 0);
+MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam = 0, LPARAM lParam = 0);
+MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam = 0, LPARAM lParam = 0);
+MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam = 0, LPARAM lParam = 0);
+
+MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc);
+MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam = 0);
+MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object);
+MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam);
+MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message);
+
+// executes the event handler if event is missing
+MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc);
+
+MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook);
+MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject);
+MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE pModule);
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc);
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam);
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object);
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam);
+MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc);
+MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService);
+MIR_CORE_DLL(bool) ServiceExists(const char *name);
+
+MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam = 0, LPARAM lParam = 0);
+MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam = 0, LPARAM lParam = 0);
+
+MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(MIR_SYSCALL *func)(void *), void *arg);
+MIR_CORE_DLL(int) CallFunctionAsync(void (MIR_SYSCALL *func)(void *), void *arg);
+MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst);
+MIR_CORE_DLL(void) KillObjectServices(void* pObject);
+
+MIR_APP_DLL(int) ProtoServiceExists(const char *szModule, const char *szService);
+MIR_APP_DLL(INT_PTR) CallProtoService(const char *szModule, const char *szService, WPARAM wParam = 0, LPARAM lParam = 0);
+
+///////////////////////////////////////////////////////////////////////////////
+// exceptions
+
+typedef uint32_t (MIR_CDECL *pfnExceptionFilter)(uint32_t code, EXCEPTION_POINTERS *info);
+
+MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter(void);
+MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter pMirandaExceptFilter);
+
+///////////////////////////////////////////////////////////////////////////////
+// icons support
+
+struct IconItem
+{
+ char *szDescr, *szName;
+ int defIconID, size;
+ HANDLE hIcolib;
+};
+
+struct IconItemT
+{
+ wchar_t *tszDescr;
+ char *szName;
+ int defIconID, size;
+ HANDLE hIcolib;
+};
+
+MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin);
+MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin);
+
+///////////////////////////////////////////////////////////////////////////////
+// language packs support
+
+MIR_CORE_DLL(unsigned int) mir_hash(const void *key, unsigned int len);
+
+#pragma optimize("gt", on)
+__forceinline unsigned int mir_hashstr(const char *key)
+{
+ if (key == nullptr) return 0;
+ else {
+ unsigned int len = (unsigned int)strlen((const char*)key);
+ return mir_hash(key, len);
+} }
+
+__forceinline unsigned int mir_hashstrW(const wchar_t *key)
+{
+ if (key == nullptr) return 0;
+ else {
+ unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+ return mir_hash(key, len * sizeof(wchar_t));
+} }
+#pragma optimize("", on)
+
+#define mir_hashstrT mir_hashstrW
+
+///////////////////////////////////////////////////////////////////////////////
+// lists
+
+typedef int (*FSortFunc)(void*, void*); // sort function prototype
+
+// Assumes first 32 bit value of the data is the numeric key
+// and uses it to perform sort/search operations, this results
+// in much better performance as no compare function calls needed
+// Incredibly useful for Hash Tables
+#define NumericKeySort (FSortFunc)(void*) -1
+#define HandleKeySort (FSortFunc)(void*) -2
+#define PtrKeySort (FSortFunc)(void*) -3
+
+typedef struct
+{
+ void** items;
+ int realCount;
+ int limit;
+ int increment;
+
+ FSortFunc sortFunc;
+}
+ SortedList;
+
+MIR_CORE_DLL(SortedList*) List_Create(int p_limit, int p_increment);
+MIR_CORE_DLL(void) List_Destroy(SortedList* p_list);
+MIR_CORE_DLL(void*) List_Find(SortedList* p_list, void* p_value);
+MIR_CORE_DLL(int) List_GetIndex(SortedList* p_list, void* p_value, int* p_index);
+MIR_CORE_DLL(int) List_IndexOf(SortedList* p_list, void* p_value);
+MIR_CORE_DLL(int) List_Insert(SortedList* p_list, void* p_value, int p_index);
+MIR_CORE_DLL(int) List_InsertPtr(SortedList* list, void* p);
+MIR_CORE_DLL(int) List_Remove(SortedList* p_list, int index);
+MIR_CORE_DLL(int) List_RemovePtr(SortedList* list, void* p);
+MIR_CORE_DLL(void) List_Copy(SortedList* s, SortedList* d, size_t itemSize);
+MIR_CORE_DLL(void) List_ObjCopy(SortedList* s, SortedList* d, size_t itemSize);
+
+///////////////////////////////////////////////////////////////////////////////
+// logging functions
+
+MIR_CORE_DLL(HANDLE) mir_createLog(const char *pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options);
+MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger);
+
+MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...);
+MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...);
+
+MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args);
+MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args);
+
+///////////////////////////////////////////////////////////////////////////////
+// md5 functions
+
+typedef struct mir_md5_state_s {
+ uint32_t count[2]; /* message length in bits, lsw first */
+ uint32_t abcd[4]; /* digest buffer */
+ uint8_t buf[64]; /* accumulate block */
+} mir_md5_state_t;
+
+MIR_CORE_DLL(void) mir_md5_init(mir_md5_state_t *pms);
+MIR_CORE_DLL(void) mir_md5_append(mir_md5_state_t *pms, const uint8_t *data, size_t nbytes);
+MIR_CORE_DLL(void) mir_md5_finish(mir_md5_state_t *pms, uint8_t digest[16]);
+MIR_CORE_DLL(void) mir_md5_hash(const uint8_t *data, size_t len, uint8_t digest[16]);
+
+///////////////////////////////////////////////////////////////////////////////
+// memory functions
+
+MIR_C_CORE_DLL(void*) mir_alloc(size_t);
+MIR_C_CORE_DLL(void*) mir_calloc(size_t);
+MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t);
+MIR_C_CORE_DLL(void) mir_free(void* ptr);
+
+MIR_CORE_DLL(size_t) mir_strlen(const char *p);
+MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p);
+
+MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src);
+MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src);
+
+MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len);
+MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len);
+
+MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src);
+MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src);
+
+MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len);
+MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len);
+
+MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2);
+MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n);
+MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2);
+MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n);
+
+MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2);
+MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n);
+MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2);
+MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n);
+
+MIR_CORE_DLL(char*) mir_strdup(const char* str);
+MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t* str);
+
+MIR_CORE_DLL(char*) mir_strndup(const char* str, size_t len);
+MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len);
+
+MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2);
+
+///////////////////////////////////////////////////////////////////////////////
+// print functions
+
+MIR_CORE_DLL(int) mir_snprintf(_Pre_notnull_ _Always_(_Post_z_) char *buffer, size_t count, _Printf_format_string_ const char* fmt, ...);
+MIR_CORE_DLL(int) mir_snwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t *buffer, size_t count, _Printf_format_string_ const wchar_t* fmt, ...);
+MIR_CORE_DLL(int) mir_vsnprintf(_Pre_notnull_ _Always_(_Post_z_) char *buffer, size_t count, _Printf_format_string_ const char* fmt, va_list va);
+MIR_CORE_DLL(int) mir_vsnwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t *buffer, size_t count, _Printf_format_string_ const wchar_t* fmt, va_list va);
+
+///////////////////////////////////////////////////////////////////////////////
+// protocol functions
+
+struct PROTO_INTERFACE;
+
+MIR_APP_DLL(INT_PTR) ProtoBroadcastAck(const char *szModule, MCONTACT hContact, int type, int result, HANDLE hProcess, LPARAM lParam = 0);
+MIR_APP_DLL(void) ProtoBroadcastAsync(const char *szModule, MCONTACT hContact, int type, int result, HANDLE hProcess, LPARAM lParam = 0);
+
+// avatar support functions
+
+// returns image extension by a PA_* constant or empty string for PA_FORMAT_UNKNOWN
+MIR_APP_DLL(const wchar_t*) ProtoGetAvatarExtension(int format);
+
+// detects image format by extension
+MIR_APP_DLL(int) ProtoGetAvatarFormat(const wchar_t *ptszFileName);
+
+// detects image format by its contents
+MIR_APP_DLL(int) ProtoGetAvatarFileFormat(const wchar_t *ptszFileName);
+
+// returns the mime type according to a picture type (PA_*) passed
+MIR_APP_DLL(const char*) ProtoGetAvatarMimeType(int iFileType);
+
+// returns the picture type (PA_*) according to a mime type passed
+MIR_APP_DLL(int) ProtoGetAvatarFormatByMimeType(const char *pwszMimeType);
+
+// returns the image format and extension by the first bytes of picture
+// ptszExtension might be NULL
+#if defined( __cplusplus )
+ MIR_APP_DLL(int) ProtoGetBufferFormat(const void *buf, const wchar_t **ptszExtension = nullptr);
+#else
+ MIR_APP_DLL(int) ProtoGetBufferFormat(const void *buf, const wchar_t **ptszExtension);
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// sha1 functions
+
+#define MIR_SHA1_HASH_SIZE 20
+#define MIR_SHA_BLOCKSIZE 64
+
+struct mir_sha1_ctx
+{
+ uint32_t H[5];
+ uint32_t W[80];
+ int lenW;
+ uint32_t sizeHi, sizeLo;
+};
+
+MIR_CORE_DLL(void) mir_sha1_init(mir_sha1_ctx *ctx);
+MIR_CORE_DLL(void) mir_sha1_append(mir_sha1_ctx *ctx, const uint8_t *dataIn, size_t len);
+MIR_CORE_DLL(void) mir_sha1_finish(mir_sha1_ctx *ctx, uint8_t hashout[MIR_SHA1_HASH_SIZE]);
+MIR_CORE_DLL(void) mir_sha1_hash(uint8_t *dataIn, size_t len, uint8_t hashout[MIR_SHA1_HASH_SIZE]);
+
+///////////////////////////////////////////////////////////////////////////////
+// sha256 functions
+
+#define MIR_SHA256_HASH_SIZE 32
+
+struct SHA256_CONTEXT
+{
+ uint32_t h0, h1, h2, h3, h4, h5, h6, h7;
+ uint32_t nblocks;
+ uint8_t buf[MIR_SHA_BLOCKSIZE];
+ int count;
+};
+
+MIR_CORE_DLL(void) mir_sha256_init(SHA256_CONTEXT *ctx);
+MIR_CORE_DLL(void) mir_sha256_write(SHA256_CONTEXT *ctx, const void *dataIn, size_t len);
+MIR_CORE_DLL(void) mir_sha256_final(SHA256_CONTEXT *ctx, uint8_t hashout[MIR_SHA256_HASH_SIZE]);
+MIR_CORE_DLL(void) mir_sha256_hash(const void *dataIn, size_t len, uint8_t hashout[MIR_SHA256_HASH_SIZE]);
+
+///////////////////////////////////////////////////////////////////////////////
+// strings
+
+MIR_CORE_DLL(void*) mir_base64_decode(const char *input, size_t *outputLen);
+MIR_CORE_DLL(char*) mir_base64_encode(const void *input, size_t inputLen);
+MIR_CORE_DLL(char*) mir_base64_encodebuf(const void *input, size_t inputLen, char *output, size_t outLen);
+
+__forceinline size_t mir_base64_encode_bufsize(size_t inputLen)
+{
+ return 4 * ((inputLen + 2) / 3) + 1;
+}
+
+MIR_CORE_DLL(char*) rtrim(char *str);
+MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str);
+
+MIR_CORE_DLL(char*) ltrim(char *str); // returns pointer to the beginning of string
+MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str);
+
+MIR_CORE_DLL(char*) ltrimp(char *str); // returns pointer to the trimmed portion of string
+MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str);
+
+MIR_CORE_DLL(char*) strdel(char *str, size_t len);
+MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len);
+
+MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask);
+MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask);
+
+MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask);
+MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask);
+
+MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest);
+MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest);
+
+MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len);
+MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len);
+
+__forceinline char* lrtrim(char *str) { return ltrim(rtrim(str)); };
+__forceinline char* lrtrimp(char *str) { return ltrimp(rtrim(str)); };
+
+#if defined( __cplusplus )
+ MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src);
+ MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src);
+#else
+ MIR_CORE_DLL(char*) replaceStr(char **dest, const char *src);
+ MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t **dest, const wchar_t *src);
+#endif
+
+#ifndef _MSC_VER
+ MIR_CORE_DLL(char*) strlwr(char *str);
+ MIR_CORE_DLL(char*) strupr(char *str);
+ MIR_CORE_DLL(char*) strrev(char *str);
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// text conversion functions
+
+union MAllStrings
+{
+ char *a; // utf8 or ansi strings
+ wchar_t *w; // strings of WCHARs
+};
+
+union MAllCStrings
+{
+ const char *a; // utf8 or ansi strings
+ const wchar_t *w; // strings of WCHARs
+};
+
+union MAllStringArray
+{
+ char **a; // array of utf8 or ansi strings
+ wchar_t **w; // array of strings of WCHARs
+};
+
+union MAllCStringArray
+{
+ const char **a; // array of utf8 or ansi strings
+ const wchar_t **w; // array of strings of WCHARs
+};
+
+MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage);
+MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src);
+MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage);
+MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src);
+
+///////////////////////////////////////////////////////////////////////////////
+// threads
+
+typedef void (MIR_CDECL *pThreadFunc)(void *param);
+typedef unsigned (MIR_SYSCALL *pThreadFuncEx)(void *param);
+typedef unsigned (MIR_CDECL *pThreadFuncOwner)(void *owner, void *param);
+
+#if defined( __cplusplus )
+ MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner = nullptr);
+#else
+ MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner);
+#endif
+MIR_CORE_DLL(INT_PTR) Thread_Pop(void);
+MIR_CORE_DLL(void) Thread_Wait(void);
+
+#if defined( __cplusplus )
+MIR_CORE_DLL(HANDLE) mir_forkthread(pThreadFunc aFunc, void *arg = nullptr);
+MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void *arg = nullptr, unsigned *pThreadID = nullptr);
+MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg = nullptr, unsigned *pThreadID = nullptr);
+#else
+MIR_CORE_DLL(HANDLE) mir_forkthread(pThreadFunc aFunc, void *arg);
+MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void *arg, unsigned *pThreadID);
+MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID);
+#endif
+
+MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName);
+
+MIR_CORE_DLL(void) KillObjectThreads(void* pObject);
+
+///////////////////////////////////////////////////////////////////////////////
+// utf8 interface
+
+MIR_CORE_DLL(BOOL) Utf8CheckString(const char* str);
+MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen); // returns 0 on error
+
+MIR_CORE_DLL(char*) mir_utf8decode(char* str, wchar_t** ucs2);
+MIR_CORE_DLL(char*) mir_utf8decodecp(char* str, int codepage, wchar_t** ucs2);
+MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char* str);
+
+MIR_CORE_DLL(char*) mir_utf8encode(const char* str);
+MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage);
+MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* str);
+
+MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src);
+
+__forceinline char* mir_utf8decodeA(const char* src)
+{
+ char *tmp = mir_strdup(src);
+ mir_utf8decode(tmp, nullptr);
+ return tmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Window subclassing
+
+#ifdef _MSC_VER
+
+MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc);
+MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc);
+MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam);
+MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc);
+
+MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst);
+
+///////////////////////////////////////////////////////////////////////////////
+// Windows utilities
+
+MIR_CORE_DLL(BOOL) IsWinVerVistaPlus();
+MIR_CORE_DLL(BOOL) IsWinVer7Plus();
+MIR_CORE_DLL(BOOL) IsWinVer8Plus();
+MIR_CORE_DLL(BOOL) IsWinVer81Plus();
+MIR_CORE_DLL(BOOL) IsWinVer10Plus();
+
+MIR_CORE_DLL(BOOL) IsFullScreen();
+MIR_CORE_DLL(BOOL) IsWorkstationLocked();
+MIR_CORE_DLL(BOOL) IsScreenSaverRunning();
+MIR_CORE_DLL(BOOL) IsTerminalDisconnected();
+
+#endif // _MSC_VER
+
+// returns OS version in version of Windows NT xx.xx
+MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize);
+
+// returns full OS version
+MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize);
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) UnloadCoreModule(void);
+
+#if defined(__cplusplus)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// The UUID structure below is used to for plugin UUID's and module type definitions
+
+struct MUUID
+{
+ unsigned long a;
+ unsigned short b;
+ unsigned short c;
+ unsigned char d[8];
+};
+
+__forceinline bool operator==(const MUUID& p1, const MUUID& p2)
+{
+ return memcmp(&p1, &p2, sizeof(MUUID)) == 0;
+}
+__forceinline bool operator!=(const MUUID& p1, const MUUID& p2)
+{
+ return memcmp(&p1, &p2, sizeof(MUUID)) != 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// C++ templates
+
+template <typename T>
+HANDLE mir_forkThread(void(MIR_CDECL *pFunc)(T* param), T *arg)
+{
+ return mir_forkthread((pThreadFunc)pFunc, arg);
+}
+
+template <size_t _Size>
+inline int mir_snprintf(_Pre_notnull_ _Always_(_Post_z_) char(&buffer)[_Size], _In_z_ _Printf_format_string_ const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ int ret = mir_vsnprintf(buffer, _Size, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+template <size_t _Size>
+inline int mir_snwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t(&buffer)[_Size], _In_z_ _Printf_format_string_ const wchar_t* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ int ret = mir_vsnwprintf(buffer, _Size, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+template <size_t _Size>
+inline int mir_vsnprintf(_Pre_notnull_ _Always_(_Post_z_) char(&buffer)[_Size], _In_z_ _Printf_format_string_ const char* fmt, va_list va)
+{
+ return mir_vsnprintf(buffer, _Size, fmt, va);
+}
+
+template <size_t _Size>
+inline int mir_vsnwprintf(_Pre_notnull_ _Always_(_Post_z_) wchar_t(&buffer)[_Size], _In_z_ _Printf_format_string_ const wchar_t* fmt, va_list va)
+{
+ return mir_vsnwprintf(buffer, _Size, fmt, va);
+}
+
+MIR_CORE_DLL(char *) mir_base64_encode(const class MBinBuffer &buf);
+
+#endif
+
+#ifdef _MSC_VER
+ #ifndef MIR_CORE_EXPORTS
+ #pragma comment(lib, "mir_core.lib")
+ #endif
+
+ #ifndef MIR_APP_EXPORTS
+ #pragma comment(lib, "mir_app.lib")
+ #endif
+#else
+ MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode);
+ MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath);
+
+ template <size_t _Size>
+ inline wchar_t* wcsncpy_s(wchar_t(&buffer)[_Size], const wchar_t *src, size_t len)
+ {
+ return wcsncpy(buffer, src, (len == _TRUNCATE) ? _Size : len);
+ }
+
+ inline wchar_t* wcsncpy_s(wchar_t *dst, size_t dstLen, const wchar_t *src, size_t len)
+ {
+ return wcsncpy(dst, src, (len == _TRUNCATE) ? dstLen : len);
+ }
+
+ inline wchar_t* wcsncat_s(wchar_t *dst, size_t dstLen, const wchar_t *src, size_t len)
+ {
+ return wcsncat(dst, src, (len == _TRUNCATE) ? dstLen : len);
+ }
+
+ template <size_t _Size>
+ inline char* strncpy_s(char(&buffer)[_Size], const char *src, size_t len)
+ {
+ return strncpy(buffer, src, (len == _TRUNCATE) ? _Size : len);
+ }
+
+ inline char* strncpy_s(char *dst, size_t dstLen, const char *src, size_t len)
+ {
+ return strncpy(dst, src, (len == _TRUNCATE) ? dstLen : len);
+ }
+
+ inline char* strncat_s(char *dst, size_t dstLen, const char *src, size_t len)
+ {
+ return strncat(dst, src, (len == _TRUNCATE) ? dstLen : len);
+ }
+#endif
+
+#endif // M_CORE_H
diff --git a/include/m_crypto.h b/include/m_crypto.h index 142236d4cd..3e7f2ffebf 100644 --- a/include/m_crypto.h +++ b/include/m_crypto.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_database.h b/include/m_database.h index 5f2cd63102..d0bc296bd9 100644 --- a/include/m_database.h +++ b/include/m_database.h @@ -1,7 +1,7 @@ ///////////////////////////////////////////////////////////////////////////////////////// // Miranda NG: the free IM client for Microsoft* Windows* // -// Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +// Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) // Copyright (c) 2000-08 Miranda ICQ/IM project, // all portions of this codebase are copyrighted to the people // listed in contributors.txt. diff --git a/include/m_db_int.h b/include/m_db_int.h index 19d66ce353..74a1cfc955 100644 --- a/include/m_db_int.h +++ b/include/m_db_int.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/include/m_descbutton.h b/include/m_descbutton.h index 2c5b883938..c922f412d2 100644 --- a/include/m_descbutton.h +++ b/include/m_descbutton.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2007-12 Miranda ICQ/IM project,
Copyright (c) 2007 Artem Shpynov
diff --git a/include/m_email.h b/include/m_email.h index 2bbb299cff..b7e17aaa3a 100644 --- a/include/m_email.h +++ b/include/m_email.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_extraicons.h b/include/m_extraicons.h index d49e31367a..737a449640 100644 --- a/include/m_extraicons.h +++ b/include/m_extraicons.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2009 Ricardo Pescuma Domenecci
This is free software; you can redistribute it and/or
diff --git a/include/m_file.h b/include/m_file.h index 11bf688324..41b4409589 100644 --- a/include/m_file.h +++ b/include/m_file.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_findadd.h b/include/m_findadd.h index 7898361cf8..c8c58b76c4 100644 --- a/include/m_findadd.h +++ b/include/m_findadd.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_gui.h b/include/m_gui.h index 2f1aa7edf4..48041ff696 100644 --- a/include/m_gui.h +++ b/include/m_gui.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/include/m_history.h b/include/m_history.h index 943a749611..2fb8c66cd6 100644 --- a/include/m_history.h +++ b/include/m_history.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_hotkeys.h b/include/m_hotkeys.h index 1b9152d12f..fce9552511 100644 --- a/include/m_hotkeys.h +++ b/include/m_hotkeys.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_http.h b/include/m_http.h index 873446279e..4ff992ff2b 100644 --- a/include/m_http.h +++ b/include/m_http.h @@ -1,121 +1,121 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (ń) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_HTTP_H__ -#define M_HTTP_H__ - -/* List of known http error codes */ - -// 1xx Informational -#define HTTP_CODE_CONTINUE 100 -#define HTTP_CODE_SWITCHING_PROTOCOLS 101 -#define HTTP_CODE_PROCESSING 102 - -// 2xx Success -#define HTTP_CODE_OK 200 -#define HTTP_CODE_CREATED 201 -#define HTTP_CODE_ACCEPTED 202 -#define HTTP_CODE_NON_AUTHORITATIVE_INFORMATION 203 -#define HTTP_CODE_NO_CONTENT 204 -#define HTTP_CODE_RESET_CONTENT 205 -#define HTTP_CODE_PARTIAL_CONTENT 206 -#define HTTP_CODE_MULTI_STATUS 207 - -#define HTTP_CODE_SUCCESS(code) ((code) <= (HTTP_CODE_MULTI_STATUS) && (code) >= (HTTP_CODE_OK)) - -// 3xx Redirection -#define HTTP_CODE_MULTIPLE_CHOICES 300 -#define HTTP_CODE_MOVED_PERMANENTLY 301 -#define HTTP_CODE_FOUND 302 -#define HTTP_CODE_SEE_OTHER 303 -#define HTTP_CODE_NOT_MODIFIED 304 -#define HTTP_CODE_USE_PROXY 305 -#define HTTP_CODE_SWITCH_PROXY 306 -#define HTTP_CODE_TEMPORARY_REDIRECT 307 -#define HTTP_CODE_PERMANENT_REDIRECT 308 - -// 4xx Client Error -#define HTTP_CODE_BAD_REQUEST 400 -#define HTTP_CODE_UNAUTHORIZED 401 -#define HTTP_CODE_PAYMENT_REQUIRED 402 -#define HTTP_CODE_FORBIDDEN 403 -#define HTTP_CODE_NOT_FOUND 404 -#define HTTP_CODE_METHOD_NOT_ALLOWED 405 -#define HTTP_CODE_NOT_ACCEPTABLE 406 -#define HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED 407 -#define HTTP_CODE_REQUEST_TIMEOUT 408 -#define HTTP_CODE_CONFLICT 409 -#define HTTP_CODE_GONE 410 -#define HTTP_CODE_LENGTH_REQUIRED 411 -#define HTTP_CODE_PRECONDITION_FAILED 412 -#define HTTP_CODE_REQUEST_ENTITY_TOO_LARGE 413 -#define HTTP_CODE_REQUEST_URI_TOO_LONG 414 -#define HTTP_CODE_UNSUPPORTED_MEDIA_TYPE 415 -#define HTTP_CODE_REQUESTED_RANGE_NOT_SATISFIABLE 416 -#define HTTP_CODE_EXPECTATION_FAILED 417 -#define HTTP_CODE_IM_A_TEAPOT 418 -#define HTTP_CODE_AUTHENTICATION_TIMEOUT 419 -#define HTTP_CODE_METHOD_FAILURE 420 -#define HTTP_CODE_ENHANCE_YOUR_CALM 420 -#define HTTP_CODE_MISDIRECTED_REQUEST 421 -#define HTTP_CODE_UNPROCESSABLE_ENTITY 422 -#define HTTP_CODE_LOCKED 423 -#define HTTP_CODE_FAILED_DEPENDENCY 424 -#define HTTP_CODE_UNORDERED_COLLECTION 425 -#define HTTP_CODE_UPGRADE_REQUIRED 426 -#define HTTP_CODE_PRECONDITION_REQUIRED 428 -#define HTTP_CODE_TOO_MANY_REQUESTS 429 -#define HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE 431 -#define HTTP_CODE_LOGIN_TIMEOUT 440 -#define HTTP_CODE_NO_RESPONSE 444 -#define HTTP_CODE_RETRY_WITH 449 -#define HTTP_CODE_BLOCKED_BY_PARENTAL_CONTROL 450 -#define HTTP_CODE_UNAVAILABLE_FOR_LEGAL_REASONS 451 -#define HTTP_CODE_REDIRECT 451 -#define HTTP_CODE_REQUEST_HEADER_TOO_LARGE 494 -#define HTTP_CODE_CERT_ERROR 495 -#define HTTP_CODE_NO_CERT 496 -#define HTTP_CODE_HTTP_TO_HTTPS 497 -#define HTTP_CODE_TOKEN_EXPIRED_INVALID 498 -#define HTTP_CODE_CLIENT_CLOSED_REQUEST 499 -#define HTTP_CODE_TOKEN_REQUIRED 499 - -// 5xx Server Error -#define HTTP_CODE_INTERNAL_SERVER_ERROR 500 -#define HTTP_CODE_NOT_IMPLEMENTED 501 -#define HTTP_CODE_BAD_GATEWAY 502 -#define HTTP_CODE_SERVICE_UNAVAILABLE 503 -#define HTTP_CODE_GATEWAY_TIMEOUT 504 -#define HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED 505 -#define HTTP_CODE_VARIANT_ALSO_NEGOTIATES 506 -#define HTTP_CODE_INSUFFICIENT_STORAGE 507 -#define HTTP_CODE_LOOP_DETECTED 508 -#define HTTP_CODE_BANDWIDTH_LIMIT_EXCEEDED 509 -#define HTTP_CODE_NOT_EXTENDED 510 -#define HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED 511 -#define HTTP_CODE_WEB_SERVER_IS_DOWN 521 -#define HTTP_CODE_NETWORK_READ_TIMEOUT_ERROR 598 -#define HTTP_CODE_NETWORK_CONNECT_TIMEOUT_ERROR 599 - +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (ń) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_HTTP_H__
+#define M_HTTP_H__
+
+/* List of known http error codes */
+
+// 1xx Informational
+#define HTTP_CODE_CONTINUE 100
+#define HTTP_CODE_SWITCHING_PROTOCOLS 101
+#define HTTP_CODE_PROCESSING 102
+
+// 2xx Success
+#define HTTP_CODE_OK 200
+#define HTTP_CODE_CREATED 201
+#define HTTP_CODE_ACCEPTED 202
+#define HTTP_CODE_NON_AUTHORITATIVE_INFORMATION 203
+#define HTTP_CODE_NO_CONTENT 204
+#define HTTP_CODE_RESET_CONTENT 205
+#define HTTP_CODE_PARTIAL_CONTENT 206
+#define HTTP_CODE_MULTI_STATUS 207
+
+#define HTTP_CODE_SUCCESS(code) ((code) <= (HTTP_CODE_MULTI_STATUS) && (code) >= (HTTP_CODE_OK))
+
+// 3xx Redirection
+#define HTTP_CODE_MULTIPLE_CHOICES 300
+#define HTTP_CODE_MOVED_PERMANENTLY 301
+#define HTTP_CODE_FOUND 302
+#define HTTP_CODE_SEE_OTHER 303
+#define HTTP_CODE_NOT_MODIFIED 304
+#define HTTP_CODE_USE_PROXY 305
+#define HTTP_CODE_SWITCH_PROXY 306
+#define HTTP_CODE_TEMPORARY_REDIRECT 307
+#define HTTP_CODE_PERMANENT_REDIRECT 308
+
+// 4xx Client Error
+#define HTTP_CODE_BAD_REQUEST 400
+#define HTTP_CODE_UNAUTHORIZED 401
+#define HTTP_CODE_PAYMENT_REQUIRED 402
+#define HTTP_CODE_FORBIDDEN 403
+#define HTTP_CODE_NOT_FOUND 404
+#define HTTP_CODE_METHOD_NOT_ALLOWED 405
+#define HTTP_CODE_NOT_ACCEPTABLE 406
+#define HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED 407
+#define HTTP_CODE_REQUEST_TIMEOUT 408
+#define HTTP_CODE_CONFLICT 409
+#define HTTP_CODE_GONE 410
+#define HTTP_CODE_LENGTH_REQUIRED 411
+#define HTTP_CODE_PRECONDITION_FAILED 412
+#define HTTP_CODE_REQUEST_ENTITY_TOO_LARGE 413
+#define HTTP_CODE_REQUEST_URI_TOO_LONG 414
+#define HTTP_CODE_UNSUPPORTED_MEDIA_TYPE 415
+#define HTTP_CODE_REQUESTED_RANGE_NOT_SATISFIABLE 416
+#define HTTP_CODE_EXPECTATION_FAILED 417
+#define HTTP_CODE_IM_A_TEAPOT 418
+#define HTTP_CODE_AUTHENTICATION_TIMEOUT 419
+#define HTTP_CODE_METHOD_FAILURE 420
+#define HTTP_CODE_ENHANCE_YOUR_CALM 420
+#define HTTP_CODE_MISDIRECTED_REQUEST 421
+#define HTTP_CODE_UNPROCESSABLE_ENTITY 422
+#define HTTP_CODE_LOCKED 423
+#define HTTP_CODE_FAILED_DEPENDENCY 424
+#define HTTP_CODE_UNORDERED_COLLECTION 425
+#define HTTP_CODE_UPGRADE_REQUIRED 426
+#define HTTP_CODE_PRECONDITION_REQUIRED 428
+#define HTTP_CODE_TOO_MANY_REQUESTS 429
+#define HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE 431
+#define HTTP_CODE_LOGIN_TIMEOUT 440
+#define HTTP_CODE_NO_RESPONSE 444
+#define HTTP_CODE_RETRY_WITH 449
+#define HTTP_CODE_BLOCKED_BY_PARENTAL_CONTROL 450
+#define HTTP_CODE_UNAVAILABLE_FOR_LEGAL_REASONS 451
+#define HTTP_CODE_REDIRECT 451
+#define HTTP_CODE_REQUEST_HEADER_TOO_LARGE 494
+#define HTTP_CODE_CERT_ERROR 495
+#define HTTP_CODE_NO_CERT 496
+#define HTTP_CODE_HTTP_TO_HTTPS 497
+#define HTTP_CODE_TOKEN_EXPIRED_INVALID 498
+#define HTTP_CODE_CLIENT_CLOSED_REQUEST 499
+#define HTTP_CODE_TOKEN_REQUIRED 499
+
+// 5xx Server Error
+#define HTTP_CODE_INTERNAL_SERVER_ERROR 500
+#define HTTP_CODE_NOT_IMPLEMENTED 501
+#define HTTP_CODE_BAD_GATEWAY 502
+#define HTTP_CODE_SERVICE_UNAVAILABLE 503
+#define HTTP_CODE_GATEWAY_TIMEOUT 504
+#define HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED 505
+#define HTTP_CODE_VARIANT_ALSO_NEGOTIATES 506
+#define HTTP_CODE_INSUFFICIENT_STORAGE 507
+#define HTTP_CODE_LOOP_DETECTED 508
+#define HTTP_CODE_BANDWIDTH_LIMIT_EXCEEDED 509
+#define HTTP_CODE_NOT_EXTENDED 510
+#define HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED 511
+#define HTTP_CODE_WEB_SERVER_IS_DOWN 521
+#define HTTP_CODE_NETWORK_READ_TIMEOUT_ERROR 598
+#define HTTP_CODE_NETWORK_CONNECT_TIMEOUT_ERROR 599
+
#endif // M_HTTP_H__
\ No newline at end of file diff --git a/include/m_icolib.h b/include/m_icolib.h index 07255a5a4b..6f51d04250 100644 --- a/include/m_icolib.h +++ b/include/m_icolib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_idle.h b/include/m_idle.h index 2c6d466d77..8725aaa715 100644 --- a/include/m_idle.h +++ b/include/m_idle.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_ignore.h b/include/m_ignore.h index e5e97b8ea3..03c5b4c39b 100644 --- a/include/m_ignore.h +++ b/include/m_ignore.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_imgsrvc.h b/include/m_imgsrvc.h index 1f347be779..012fdd5571 100644 --- a/include/m_imgsrvc.h +++ b/include/m_imgsrvc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-12 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_import.h b/include/m_import.h index 6bfcc63de2..296e99dbae 100644 --- a/include/m_import.h +++ b/include/m_import.h @@ -1,59 +1,59 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_IMPORT_H__ -#define M_IMPORT_H__ 1 - -// launches the import wizard with given file name & options -// always returns 0 - -// Custom import options -#define IOPT_ADDUNKNOWN 0x00000001 -#define IOPT_MSGSENT 0x00000002 -#define IOPT_MSGRECV 0x00000004 -#define IOPT_AUTHREQ 0x00000020 -#define IOPT_ADDED 0x00000040 -#define IOPT_FILESENT 0x00000080 -#define IOPT_FILERECV 0x00000100 -#define IOPT_OTHERSENT 0x00000200 -#define IOPT_OTHERRECV 0x00000400 -#define IOPT_HISTORY 0x000007FE - -#define IOPT_SYSTEM 0x00000800 -#define IOPT_CONTACTS 0x00001000 -#define IOPT_GROUPS 0x00002000 -#define IOPT_SYS_SETTINGS 0x00004000 -#define IOPT_COMPLETE 0x00007FFE - -#define IOPT_CHECKDUPS 0x00010000 - -struct MImportOptions -{ - const wchar_t *pwszFileName; - uint32_t dwFlags; // IOPT_* flags combination -}; - -#define MS_IMPORT_RUN "Import/Run" - -#endif // M_IMPORT_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_IMPORT_H__
+#define M_IMPORT_H__ 1
+
+// launches the import wizard with given file name & options
+// always returns 0
+
+// Custom import options
+#define IOPT_ADDUNKNOWN 0x00000001
+#define IOPT_MSGSENT 0x00000002
+#define IOPT_MSGRECV 0x00000004
+#define IOPT_AUTHREQ 0x00000020
+#define IOPT_ADDED 0x00000040
+#define IOPT_FILESENT 0x00000080
+#define IOPT_FILERECV 0x00000100
+#define IOPT_OTHERSENT 0x00000200
+#define IOPT_OTHERRECV 0x00000400
+#define IOPT_HISTORY 0x000007FE
+
+#define IOPT_SYSTEM 0x00000800
+#define IOPT_CONTACTS 0x00001000
+#define IOPT_GROUPS 0x00002000
+#define IOPT_SYS_SETTINGS 0x00004000
+#define IOPT_COMPLETE 0x00007FFE
+
+#define IOPT_CHECKDUPS 0x00010000
+
+struct MImportOptions
+{
+ const wchar_t *pwszFileName;
+ uint32_t dwFlags; // IOPT_* flags combination
+};
+
+#define MS_IMPORT_RUN "Import/Run"
+
+#endif // M_IMPORT_H__
diff --git a/include/m_jabber.h b/include/m_jabber.h index 22ad790591..54cef3d584 100644 --- a/include/m_jabber.h +++ b/include/m_jabber.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/include/m_json.h b/include/m_json.h index e9a0545217..f87e01bf82 100644 --- a/include/m_json.h +++ b/include/m_json.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_langpack.h b/include/m_langpack.h index e879b9925a..26f37ff380 100644 --- a/include/m_langpack.h +++ b/include/m_langpack.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-12 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_message.h b/include/m_message.h index 731314d8a1..1adc08a5e2 100644 --- a/include/m_message.h +++ b/include/m_message.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_metacontacts.h b/include/m_metacontacts.h index b73c14346d..01388f72ce 100644 --- a/include/m_metacontacts.h +++ b/include/m_metacontacts.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2004-07 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
Copyright (c) 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/include/m_netlib.h b/include/m_netlib.h index b36badd926..b2f52be372 100644 --- a/include/m_netlib.h +++ b/include/m_netlib.h @@ -1,852 +1,852 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-12 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_NETLIB_H__ -#define M_NETLIB_H__ 1 - -#include "m_utils.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// this module was created in 0.1.2.2 -// All error codes are returned via GetLastError() (or WSAGetLastError(): -// they're the same). -// This module is thread-safe where it is sensible for it to be so. This -// basically means that you can call anything from any thread, but don't try -// to predict what will happen if you try to recv() on the same connection from -// two different threads at the same time. -// Note that because the vast majority of the routines in this module return -// a pointer, I have decided to diverge from the rest of Miranda and go with -// the convention that functions return false on failure and nonzero on success. - -struct NETLIBHTTPREQUEST; -struct NETLIBOPENCONNECTION; - -#define NETLIB_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.112 Safari/537.36" - -///////////////////////////////////////////////////////////////////////////////////////// -// Initialises the netlib for a set of connections -// Returns a HNETLIBUSER to be used for future netlib calls, NULL on failure -// NOTE: Netlib is loaded after any plugins, so you need to wait until -// ME_SYSTEM_MODULESLOADED before calling this function -// Netlib settings are stored under the module szSettingsModule -// All netlib settings being with "NL". -// The default settings for registered users that don't have any settings stored -// in the database are the same as those displayed by the <All connections> page -// of the netlib options page. -// Errors: ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY, ERROR_DUP_NAME - -struct NETLIBUSER -{ - char *szSettingsModule; // used for db settings and log - MAllStrings szDescriptiveName; // used in options dialog, already translated - uint32_t flags; - int minIncomingPorts; // only if NUF_INCOMING. Will be used for validation of user input. -}; - -#define NUF_INCOMING 0x01 // binds incoming ports -#define NUF_OUTGOING 0x02 // makes outgoing plain connections -#define NUF_NOOPTIONS 0x08 // don't create an options page for this. szDescriptiveName is never used. -#define NUF_HTTPCONNS 0x10 // at least some connections are made for HTTP communication. Enables the HTTP proxy option in options. -#define NUF_NOHTTPSOPTION 0x20 // disable the HTTPS proxy option in options. Use this if all communication is HTTP. -#define NUF_UNICODE 0x40 // if set ptszDescriptiveName points to Unicode, otherwise it points to ANSI string - -EXTERN_C MIR_APP_DLL(HNETLIBUSER) Netlib_RegisterUser(const NETLIBUSER *pDescr); - -///////////////////////////////////////////////////////////////////////////////////////// -// Assign a Netlib user handle a set of dynamic HTTP headers to be used with all -// -// HTTP connections that enable the HTTP-use-sticky headers flag. -// The headers persist until cleared with lParam = NULL. -// -// All memory should be allocated by the caller using malloc() from MS_SYSTEM_GET_MMI -// Once it has passed to Netlib, Netlib is the owner of it, the caller should not refer to the memory -// In any way after this point. -// -// NOTE: The szHeaders parameter should be a NULL terminated string following the HTTP header syntax. -// This string will be injected verbatim, thus the user should be aware of setting strings that are not -// headers. This service is NOT THREAD SAFE, only a single thread is expected to set the headers and a single -// thread reading the pointer internally, stopping race conditions and mutual exclusion don't happen. -// -// Version 0.3.2a+ (2003/10/27) -// - -EXTERN_C MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders); - -/* Notes on HTTP gateway usage -When a connection is initiated through an HTTP proxy using -MS_NETLIB_OPENCONNECTION, netlib will GET nlu.szHttpGatewayHello and read -the replied headers. Once this succeeds nlu.pfnHttpGatewayInit will be called -with a valid handle to the connection, the NETLIBOPENCONNECTION structure that -MS_NETLIB_OPENCONNECTION was called with, and the replied HTTP headers as its -parameters. This function is responsible for recving and parsing the data then -calling MS_NETLIB_SETHTTPPROXYINFO with the appropriate information. -nlu.pfnHttpGatewayInit should return nonzero on success. If it returns zero -then the entire connection attempt will return signalling failure. If your -function needs to return an error code it can do so via SetLastError(). -If nlu.pfnHttpGatewayInit returns success without having called -MS_NETLIB_SETHTTPPROXYINFO then the connection attempt will fail anyway. -If you need more fine-tuned control over the GET/POST URLs than just appending -sequence numbers you can call MS_NETLIB_SETHTTPPROXYINFO from within your -wrap/unwrap functions (see below). - -Just prior to MS_NETLIB_OPENCONNECTION returning nlu.pfnHttpGatewayBegin is -called with the handle to the connection and the NETLIBOPENCONNECTION structure -as its parameters. This is for gateways that need special non-protocol -initialisation. If you do send any packets in this function, you probably want -to remember to use the MSG_NOHTTPGATEWAYWRAP flag. This function pointer can be -NULL if this functionality isn't needed. This function must return nonzero on -success. If it fails the connect attempt will return failure without changing -LastError. - -Whenever MS_NETLIB_SEND is called on a connection through an HTTP proxy and -the MSG_NOHTTPGATEWAYWRAP flags is not set and nlu.pfnHttpGatewayWrapSend is -not NULL, nlu.pfnHttpGatewayWrapSend will be called *instead* of sending the -data. It is this function's responsibility to wrap the sending data -appropriately for transmission and call pfnNetlibSend to send it again. -The flags parameter to nlu.pfnHttpGatewayWrapSend should be passed straight -through to the pfnNetlibSend call. It has already been ORed with -MSG_NOHTTPGATEWAYWRAP. nlu.pfnHttpGatewayWrapSend should return the a -number of the same type as MS_NETLIB_SEND, ie the number of bytes sent or -SOCKET_ERROR. The number of wrapping bytes should be subtracted so that the -return value appears as if the proxy wasn't there. -pfnNetlibSend() is identical to CallService(MS_NETLIB_SEND, ...) but it's -quicker to call using this pointer than to do the CallService() lookup again. - -Whenever an HTTP reply is received inside MS_NETLIB_RECV the headers and data -are read into memory. If the headers indicate success then the data is passed -to nlu.pfnHttpGatewayUnwrapRecv (if it's non-NULL) for processing. This -function should remove (and do other processing if necessary) all HTTP proxy -specific headers and return a pointer to the buffer whose size is returned in -*outBufLen. If the buffer needs to be resized then NetlibRealloc() should be -used for that purpose, *not* your own CRT's realloc(). NetlibRealloc() behaves -identically to realloc() so it's possible to free the original buffer and -create a new one if that's the most sensible way to write your parser. -If errors are encountered you should SetLastError() and return NULL; -MS_NETLIB_RECV will return SOCKET_ERROR. If the passed buffer unwraps to -contain no actual data you should set *outBufLen to 0 but make sure you return -some non-NULL buffer that can be freed. - -When you call MS_NETLIB_SEND or MS_NETLIB_RECV from any of these functions, you -should use the MSG_DUMPPROXY flag so that the logging is neat. -*/ - -#define PROXYTYPE_SOCKS4 1 -#define PROXYTYPE_SOCKS5 2 -#define PROXYTYPE_HTTP 3 -#define PROXYTYPE_HTTPS 4 -#define PROXYTYPE_IE 5 - -struct NETLIBUSERSETTINGS -{ - int cbSize; // to be filled in before calling - int useProxy; // 1 or 0 - int proxyType; // a PROXYTYPE_ - char *szProxyServer; // can be NULL - int wProxyPort; // host byte order - int useProxyAuth; // 1 or 0. Always 0 for SOCKS4 - char *szProxyAuthUser; // can be NULL, always used by SOCKS4 - char *szProxyAuthPassword; // can be NULL - int useProxyAuthNtlm; // 1 or 0, only used by HTTP, HTTPS - int dnsThroughProxy; // 1 or 0 - int specifyIncomingPorts; // 1 or 0 - char *szIncomingPorts; // can be NULL. Of form "1024-1050, 1060-1070, 2000" - int specifyOutgoingPorts; // 0.3.3a+ - char *szOutgoingPorts; // 0.3.3a+ - int enableUPnP; // 0.6.1+ only for NUF_INCOMING - int validateSSL; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Gets the user-configured settings for a netlib user -// -// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -// The pointers referred to in the returned struct will remain valid until -// the hUser handle is closed, or until the user changes the settings in the -// options page, so it's best not to rely on them for too long. -// Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(int) Netlib_GetUserSettings(HNETLIBUSER nlu, NETLIBUSERSETTINGS *result); - -///////////////////////////////////////////////////////////////////////////////////////// -//Gets the user-configured settings for a netlib user idetified by name -// -//Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -//This function behaves like Netlib_GetUserSettings but the user is identified -//by the name provided by registration. When the name is not found NETLIBUSERSETTINGS is set to NULL. -//Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(int) Netlib_GetUserSettingsByName(char * UserSettingsName, NETLIBUSERSETTINGS *result); - -///////////////////////////////////////////////////////////////////////////////////////// -// Changes the user-configurable settings for a netlib user -// -// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -// This function is only really useful for people that specify NUF_NOOPTIONS -// and want to create their own options. -// Even if a setting is not active (eg szProxyAuthPassword when useProxyAuth is -// zero) that settings is still set for use in the options dialog. -// Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(int) Netlib_SetUserSettings(HNETLIBUSER nlu, const NETLIBUSERSETTINGS *result); - -///////////////////////////////////////////////////////////////////////////////////////// -//Changes the user-configurable settings for a netlib user idetified by name -// -//Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -//This function behaves like Netlib_SetUserSettings but the user is identified -//by the name provided by registration. Nothing will be changed when the name is not found. -//Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(int) Netlib_SetUserSettingsByName(char * UserSettingsName, NETLIBUSERSETTINGS *result); - -///////////////////////////////////////////////////////////////////////////////////////// -// Closes a netlib handle -// -// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -// This function should be called on all handles returned by netlib functions -// once you are done with them. If it's called on a socket-type handle, the -// socket will be closed. -// Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(int) Netlib_CloseHandle(HANDLE h); - -///////////////////////////////////////////////////////////////////////////////////////// -// Open a port and wait for connections on it -// -// Returns a HANDLE on success, NULL on failure -// hUser should have been returned by MS_NETLIB_REGISTERUSER -// This function does the equivalent of socket(), bind(), getsockname(), -// listen(), accept() -// Internally this function creates a new thread which waits around in accept() -// for new connections. When one is received it calls nlb.pfnNewConnection *from -// this new thread* and then loops back to wait again. -// Close the returned handle to end the thread and close the open port. -// Errors: ERROR_INVALID_PARAMETER, any returned by socket() or bind() or -// listen() or getsockname() -// -// Notes: -// -// During development of 0.3.1a+ (2003/07/04) passing wPort != 0 -// will result in an attempt to bind on the port given in wPort -// if this port is taken then you will get an error, so be sure to check -// for such conditions. -// -// passing wPort != 0 is for people who need to open a set port for -// daemon activities, usually passing wPort == 0 is what you want and -// will result in a free port given by the TCP/IP socket layer and/or -// seeded from the user selected port ranges. -// -// also note that wPort if != 0, will have be converted to network byte order -// -/* pExtra was added during 0.3.4+, prior its just two args, since we use the cdecl convention -it shouldnt matter */ - -typedef void (*NETLIBNEWCONNECTIONPROC)(HNETLIBCONN hNewConnection, uint32_t dwRemoteIP, void *pExtra); - -struct NETLIBBIND -{ - NETLIBNEWCONNECTIONPROC pfnNewConnection; - - // function to call when there's a new connection. Params are: the - // new connection, IP of remote machine (host byte order) - uint32_t dwInternalIP; // set on return, host byte order - uint32_t dwExternalIP; // set on return, host byte order - uint16_t wPort, wExPort; // set on return, host byte order - void *pExtra; // argument is sent to callback -}; - -EXTERN_C MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb); - -///////////////////////////////////////////////////////////////////////////////////////// -// Open a connection -// -// Returns a HNETLIBCONN to the new connection on success, NULL on failure -// hUser must have been returned by MS_NETLIB_REGISTERUSER -// Internally this function is the equivalent of socket(), gethostbyname(), -// connect() -// If NLOCF_HTTP is set and hUser is configured for an HTTP or HTTPS proxy then -// this function will connect() to the proxy server only, without performing any -// initialisation conversation. -// If hUser is configured for an HTTP proxy and does not support HTTP gateways -// and you try to open a connection without specifying NLOCF_HTTP then this -// function will first attempt to open an HTTPS connection, if that fails it -// will try a direct connection, if that fails it will return failure with the -// error from the connect() during the direct connection attempt. -// Errors: ERROR_INVALID_PARAMETER, any returned by socket(), gethostbyname(), -// connect(), MS_NETLIB_SEND, MS_NETLIB_RECV, select() -// ERROR_TIMEOUT (during proxy communication) -// ERROR_BAD_FORMAT (very invalid proxy reply) -// ERROR_ACCESS_DENIED (by proxy) -// ERROR_CONNECTION_UNAVAIL (socks proxy can't connect to identd) -// ERROR_INVALID_ACCESS (proxy refused identd auth) -// ERROR_INVALID_DATA (proxy returned invalid code) -// ERROR_INVALID_ID_AUTHORITY (proxy requires use of auth method that's not supported) -// ERROR_GEN_FAILURE (socks5/https general failure) -// ERROR_CALL_NOT_IMPLEMENTED (socks5 command not supported) -// ERROR_INVALID_ADDRESS (socks5 address type not supported) -// HTTP: anything from nlu.pfnHttpGatewayInit, nlu.pfnHttpGatewayBegin, -// MS_NETLIB_SENDHTTPREQUEST or MS_NETLIB_RECVHTTPHEADERS - -#define NLOCF_HTTP 0x0001 // this connection will be used for HTTP communications. If configured for an HTTP/HTTPS proxy the connection is opened as if there was no proxy. -#define NLOCF_STICKYHEADERS 0x0002 // this connection should send the sticky headers associated with NetLib user apart of any HTTP request -#define NLOCF_UDP 0x0008 // this connection is UDP -#define NLOCF_SSL 0x0010 // this connection is SSL - -EXTERN_C MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(HNETLIBUSER nlu, const char *szHost, int port, int timeout = 0, int flags = 0); - -///////////////////////////////////////////////////////////////////////////////////////// -// Sets the required information for an HTTP proxy connection -// -// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -// This function is designed to be called from within pfnHttpGatewayInit -// See notes below MS_NETLIB_REGISTERUSER. -// Errors: ERROR_INVALID_PARAMETER - -#define NLHPIF_USEGETSEQUENCE 0x0001 // append sequence numbers to GET requests -#define NLHPIF_USEPOSTSEQUENCE 0x0002 // append sequence numbers to POST requests -#define NLHPIF_GETPOSTSAMESEQUENCE 0x0004 // GET and POST use the same sequence -#define NLHPIF_HTTP11 0x0008 // HTTP 1.1 proxy - -struct NETLIBHTTPPROXYINFO -{ - uint32_t flags; - int firstGetSequence, firstPostSequence; - int combinePackets; - char *szHttpPostUrl; - char *szHttpGetUrl; -}; - -EXTERN_C MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN hConnection, const NETLIBHTTPPROXYINFO *nlhpi); - -///////////////////////////////////////////////////////////////////////////////////////// -// Gets the SOCKET associated with a netlib handle -// -// Returns the SOCKET on success, INVALID_SOCKET on failure -// hNetlibHandle should have been returned by MS_NETLIB_BINDPORT or -// MS_NETLIB_OPENCONNECTION only. -// Be careful how you use this socket because you might be connected via an -// HTTP proxy in which case calling send() or recv() will totally break things. -// Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(UINT_PTR) Netlib_GetSocket(HNETLIBCONN hConnection); - -///////////////////////////////////////////////////////////////////////////////////////// - -#define Netlib_GetBase64DecodedBufferSize(cchEncoded) (((cchEncoded)>>2)*3) -#define Netlib_GetBase64EncodedBufferSize(cbDecoded) (((cbDecoded)*4+11)/12*4+1) - -///////////////////////////////////////////////////////////////////////////////////////// -// Gets HNETLIBUSER owner of a connection - -EXTERN_C MIR_APP_DLL(HNETLIBUSER) Netlib_GetConnNlu(HNETLIBCONN hConn); - -///////////////////////////////////////////////////////////////////////////////////////// -// Gets the fake User-Agent header field to make some sites happy - -EXTERN_C MIR_APP_DLL(char*) Netlib_GetUserAgent(); - -///////////////////////////////////////////////////////////////////////////////////////// -// Converts numerical representation of IP in SOCKADDR_INET into string representation with IP and port -// IPv4 will be supplied in formats address:port or address -// IPv6 will be supplied in formats [address]:port or [address] -// Returns pointer to the string or NULL if not successful - -struct sockaddr_in; -EXTERN_C MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr); -EXTERN_C MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, sockaddr_in *addr); - -///////////////////////////////////////////////////////////////////////////////////////// -// Gets connection Information -// IPv4 will be supplied in formats address:port or address -// IPv6 will be supplied in formats [address]:port or [address] -// Returns 0 if successful - -struct NETLIBCONNINFO -{ - char szIpPort[64]; - unsigned dwIpv4; - uint16_t wPort; -}; - -EXTERN_C MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN hConnection, NETLIBCONNINFO *connInfo); - -///////////////////////////////////////////////////////////////////////////////////////// -// Gets connection Information -// -// Returns (INT_PTR)(NETLIBIPLIST*) numeric IP address address array -// the last element of the array is all 0s, 0 if not successful - -struct NETLIBIPLIST -{ - unsigned cbNum; - char szIp[1][64]; -}; - -EXTERN_C MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly); - -///////////////////////////////////////////////////////////////////////////////////////// -// Send an HTTP request over a connection -// -// Returns number of bytes sent on success, SOCKET_ERROR on failure -// hConnection must have been returned by MS_NETLIB_OPENCONNECTION -// Note that if you use NLHRF_SMARTAUTHHEADER and NTLM authentication is in use -// then the full NTLM authentication transaction occurs, comprising sending the -// domain, receiving the challenge, then sending the response. -// nlhr.resultCode and nlhr.szResultDescr are ignored by this function. -// Errors: ERROR_INVALID_PARAMETER, anything returned by MS_NETLIB_SEND - -struct NETLIBHTTPHEADER -{ - char *szName; - char *szValue; -}; - -EXTERN_C MIR_APP_DLL(char*) Netlib_GetHeader(const NETLIBHTTPREQUEST *pRec, const char *pszName); - -///////////////////////////////////////////////////////////////////////////////////////// - -#define REQUEST_RESPONSE 0 // used by structure returned by MS_NETLIB_RECVHTTPHEADERS -#define REQUEST_GET 1 -#define REQUEST_POST 2 -#define REQUEST_CONNECT 3 -#define REQUEST_HEAD 4 -#define REQUEST_PUT 5 -#define REQUEST_DELETE 6 -#define REQUEST_PATCH 7 - -#define NLHRF_MANUALHOST 0x00000001 // do not remove any host and/or protocol portion of szUrl before sending it -#define NLHRF_HTTP11 0x00000010 // use HTTP 1.1 -#define NLHRF_PERSISTENT 0x00000020 // preserve connection on exit, open connection provided in the nlc field of the reply - // it should be supplied in nlc field of request for reuse or closed if not needed -#define NLHRF_SSL 0x00000040 // use SSL connection -#define NLHRF_NOPROXY 0x00000080 // do not use proxy server -#define NLHRF_REDIRECT 0x00000100 // handle HTTP redirect requests (response 30x), the resulting url provided in szUrl of the response -#define NLHRF_NODUMP 0x00010000 // never dump this to the log -#define NLHRF_NODUMPHEADERS 0x00020000 // don't dump http headers (only useful for POSTs and MS_NETLIB_HTTPTRANSACTION) -#define NLHRF_DUMPPROXY 0x00040000 // this transaction is a proxy communication. For dump filtering only. -#define NLHRF_DUMPASTEXT 0x00080000 // dump posted and reply data as text. Headers are always dumped as text. -#define NLHRF_NODUMPSEND 0x00100000 // do not dump sent message. - -struct NETLIBHTTPREQUEST -{ - int cbSize; - int requestType; // a REQUEST_ - uint32_t flags; - char *szUrl; - NETLIBHTTPHEADER *headers; // If this is a POST request and headers doesn't contain a Content-Length it'll be added automatically - int headersCount; - char *pData; // data to be sent in POST request. - int dataLength; // must be 0 for REQUEST_GET/REQUEST_CONNECT - int resultCode; - char *szResultDescr; - HNETLIBCONN nlc; - int timeout; - - __forceinline const char *operator[](const char *pszName) { - return Netlib_GetHeader(this, pszName); - } -}; - -EXTERN_C MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN hConnection, NETLIBHTTPREQUEST *pRec); - -///////////////////////////////////////////////////////////////////////////////////////// -// Receives HTTP headers -// -// Returns a pointer to a NETLIBHTTPREQUEST structure on success, NULL on failure. -// Call Netlib_FreeHttpRequest() to free this. -// hConnection must have been returned by MS_NETLIB_OPENCONNECTION -// nlhr->pData = NULL and nlhr->dataLength = 0 always. The requested data should -// be retrieved using MS_NETLIB_RECV once the header has been parsed. -// If the headers haven't finished within 60 seconds the function returns NULL -// and ERROR_TIMEOUT. -// Errors: ERROR_INVALID_PARAMETER, any from MS_NETLIB_RECV or select() -// ERROR_HANDLE_EOF (connection closed before headers complete) -// ERROR_TIMEOUT (headers still not complete after 60 seconds) -// ERROR_BAD_FORMAT (invalid character or line ending in headers, or first line is blank) -// ERROR_BUFFER_OVERFLOW (each header line must be less than 4096 chars long) -// ERROR_INVALID_DATA (first header line is malformed ("http/[01].[0-9] [0-9]+ .*", or no colon in subsequent line) - -EXTERN_C MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags = 0); - -///////////////////////////////////////////////////////////////////////////////////////// -// Free the memory used by a NETLIBHTTPREQUEST structure -// -// Returns true on success, false on failure (!! this is different to most of the rest of Miranda, but consistent with netlib) -// This should only be called on structures returned by -// MS_NETLIB_RECVHTTPHEADERS or MS_NETLIB_HTTPTRANSACTION. Calling it on an -// arbitrary structure will have disastrous results. -// Errors: ERROR_INVALID_PARAMETER - -EXTERN_C MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST*); - -///////////////////////////////////////////////////////////////////////////////////////// -// smart pointer for NETLIBHTTPREQUEST via a call of Netlib_FreeHttpRequest() - -#ifdef __cplusplus -class NLHR_PTR -{ -protected: - NETLIBHTTPREQUEST *_p; - -public: - __forceinline explicit NLHR_PTR(NETLIBHTTPREQUEST *p) : _p(p) {} - - __forceinline NETLIBHTTPREQUEST* operator=(INT_PTR i_p) - { - return operator=((NETLIBHTTPREQUEST*)i_p); - } - __forceinline NETLIBHTTPREQUEST* operator=(NETLIBHTTPREQUEST *p) - { - if (_p) - Netlib_FreeHttpRequest(_p); - _p = p; - return _p; - } - __forceinline operator NETLIBHTTPREQUEST*() const { return _p; } - __forceinline NETLIBHTTPREQUEST* operator->() const { return _p; } - __forceinline ~NLHR_PTR() - { - Netlib_FreeHttpRequest(_p); - } -}; - -struct MIR_APP_EXPORT MHttpRequest : public NETLIBHTTPREQUEST, public MZeroedObject -{ - MHttpRequest(); - ~MHttpRequest(); - - CMStringA m_szUrl; - CMStringA m_szParam; - void *pUserInfo = nullptr; - - void AddHeader(const char *szName, const char *szValue); -}; - -template <class T> -class MTHttpRequest : public MHttpRequest -{ -public: - __forceinline MTHttpRequest() - {} - - typedef void (T::*MTHttpRequestHandler)(NETLIBHTTPREQUEST*, struct AsyncHttpRequest*); - MTHttpRequestHandler m_pFunc = nullptr; -}; - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const INT_PARAM&); -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const INT64_PARAM&); -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const CHAR_PARAM&); -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const WCHAR_PARAM&); - -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// Do an entire HTTP transaction -// -// Returns a pointer to another NETLIBHTTPREQUEST structure on success, NULL on failure. -// Call Netlib_FreeHttpRequest() to free this. -// hUser must have been returned by MS_NETLIB_REGISTERUSER -// nlhr.szUrl should be a full HTTP URL. If it does not start with http:// , that -// will be assumed (but it's best not to use this fact, for reasons of -// extensibility). -// This function is the equivalent of MS_NETLIB_OPENCONNECTION, -// MS_NETLIB_SENDHTTPREQ, MS_NETLIB_RECVHTTPHEADERS, MS_NETLIB_RECV, -// MS_NETLIB_CLOSEHANDLE -// nlhr.headers will be augmented with the following headers unless they have -// already been set by the caller: -// "Host" (regardless of whether it is requested in nlhr.flags) -// "User-Agent" (of the form "Miranda/0.1.2.2 (alpha)" or "Miranda/0.1.2.2") -// "Content-Length" (for POSTs only. Set to nlhr.dataLength) -// If you do not want to send one of these headers, create a nlhr.headers with -// szValue = NULL. -// In the return value headers, headerCount, pData, dataLength, resultCode and -// szResultDescr are all valid. -// In the return value pData[dataLength] == 0 always, as an extra safeguard -// against programming slips. -// Note that the function can succeed (ie not return NULL) yet result in an HTTP -// error code. You should check that resultCode == 2xx before proceeding. -// Errors: ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY, anything from the above -// list of functions - -EXTERN_C MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER hNlu, NETLIBHTTPREQUEST *pRequest); - -///////////////////////////////////////////////////////////////////////////////////////// -// Send data over a connection -// -// Returns the number of bytes sent on success, SOCKET_ERROR on failure -// Errors: ERROR_INVALID_PARAMETER -// anything from send(), nlu.pfnHttpGatewayWrapSend() -// HTTP proxy: ERROR_GEN_FAILURE (http result code wasn't 2xx) -// anything from socket(), connect(), -// MS_NETLIB_SENDHTTPREQUEST, MS_NETLIB_RECVHTTPHEADERS -// flags: - -#define MSG_NOHTTPGATEWAYWRAP 0x010000 // don't wrap the outgoing packet using nlu.pfnHttpGatewayWrapSend -#define MSG_NODUMP 0x020000 // don't dump this packet to the log -#define MSG_DUMPPROXY 0x040000 // this is proxy communiciation. For dump filtering only. -#define MSG_DUMPASTEXT 0x080000 // this is textual data, don't dump as hex -#define MSG_RAW 0x100000 // send as raw data, bypass any HTTP proxy stuff -#define MSG_DUMPSSL 0x200000 // this is SSL traffic. For dump filtering only. -#define MSG_NOTITLE 0x400000 // skip date, time & protocol from dump - -EXTERN_C MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN hConn, const char *buf, int len, int flags = 0); - -///////////////////////////////////////////////////////////////////////////////////////// -// Receive data over a connection -// -// Returns the number of bytes read on success, SOCKET_ERROR on failure, -// 0 if the connection has been closed -// Flags supported: MSG_PEEK, MSG_NODUMP, MSG_DUMPPROXY, MSG_NOHTTPGATEWAYWRAP, -// MSG_DUMPASTEXT, MSG_RAW -// On using MSG_NOHTTPGATEWAYWRAP: Because packets through an HTTP proxy are -// batched and cached and stuff, using this flag is not a guarantee that it -// will be obeyed, and if it is it may even be propogated to future calls -// even if you don't specify it then. Because of this, the flag should be -// considered an all-or-nothing thing: either use it for the entire duration -// of a connection, or not at all. -// Errors: ERROR_INVALID_PARAMETER, anything from recv() -// HTTP proxy: ERROR_GEN_FAILURE (http result code wasn't 2xx) -// ERROR_INVALID_DATA (no Content-Length header in reply) -// ERROR_NOT_ENOUGH_MEMORY (Content-Length very large) -// ERROR_HANDLE_EOF (connection closed before Content-Length bytes recved) -// anything from select(), MS_NETLIB_RECVHTTPHEADERS, -// nlu.pfnHttpGatewayUnwrapRecv, socket(), connect(), -// MS_NETLIB_SENDHTTPREQUEST - -EXTERN_C MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN hConn, char *buf, int len, int flags = 0); - -///////////////////////////////////////////////////////////////////////////////////////// -// Determine the status of one or more connections -// Returns the number of ready connections, SOCKET_ERROR on failure, 0 if the timeout expired. -// All handles passed to this function must have been returned by either -// MS_NETLIB_OPENCONNECTION or MS_NETLIB_BINDPORT. -// The last handle in each list must be followed by either NULL or INVALID_HANDLE_VALUE. -// Errors: ERROR_INVALID_HANDLE, ERROR_INVALID_DATA, anything from select() - -struct NETLIBSELECT -{ - uint32_t dwTimeout; // in milliseconds, INFINITE is acceptable - HNETLIBCONN hReadConns[FD_SETSIZE + 1]; - HNETLIBCONN hWriteConns[FD_SETSIZE + 1]; - HNETLIBCONN hExceptConns[FD_SETSIZE + 1]; -}; - -EXTERN_C MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls); - -struct NETLIBSELECTEX -{ - uint32_t dwTimeout; // in milliseconds, INFINITE is acceptable - HNETLIBCONN hReadConns[FD_SETSIZE + 1]; - HNETLIBCONN hWriteConns[FD_SETSIZE + 1]; - HNETLIBCONN hExceptConns[FD_SETSIZE + 1]; - - BOOL hReadStatus[FD_SETSIZE+1]; /* out, [in, expected to be FALSE] */ - BOOL hWriteStatus[FD_SETSIZE+1]; /* out, [in, expected to be FALSE] */ - BOOL hExceptStatus[FD_SETSIZE+1]; /* out, [in, expected to be FALSE] */ -}; - -EXTERN_C MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls); - -///////////////////////////////////////////////////////////////////////////////////////// -// Shutdown connection - -EXTERN_C MIR_APP_DLL(void) Netlib_Shutdown(HNETLIBCONN h); - -///////////////////////////////////////////////////////////////////////////////////////// -// Create a packet receiver -// -// Returns a HANDLE on success, NULL on failure -// The packet receiver implements the common situation where you have variable -// length packets coming in over a connection and you want to split them up -// in order to handle them. -// The major limitation is that the buffer is created in memory, so you can't -// have arbitrarily large packets. -// Errors: ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY - -EXTERN_C MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN hConnection, int iMaxSize); - -///////////////////////////////////////////////////////////////////////////////////////// -// Get the next set of packets from a packet receiver -// -// Returns the total number of bytes available in the buffer, 0 if the -// connection was closed, SOCKET_ERROR on error. -// hPacketRecver must have been returned by MS_NETLIB_CREATEPACKETRECVER -// If nlpr.bytesUsed is set to zero and the buffer is already full up to -// maxPacketSize, it is assumed that too large a packet has been received. All -// data in the buffer is discarded and receiving is begun anew. This will -// probably cause alignment problems so if you think this is likely to happen -// then you should deal with it yourself. -// Closing the packet receiver will not close the associated connection, but -// will discard any bytes still in the buffer, so if you intend to carry on -// reading from that connection, make sure you have processed the buffer first. -// This function is the equivalent of a memmove() to remove the first bytesUsed -// from the buffer, select() if dwTimeout is not INFINITE, then MS_NETLIB_RECV. -// Errors: ERROR_INVALID_PARAMETER, ERROR_TIMEOUT, -// anything from select(), MS_NETLIB_RECV - -struct NETLIBPACKETRECVER -{ - uint32_t dwTimeout; // fill before calling. In milliseconds. INFINITE is valid - int bytesUsed; // fill before calling. This many bytes are removed from the start of the buffer. Set to 0 on return - int bytesAvailable; // equal to the return value, unless the return value is 0 - int bufferSize; // same as parameter to MS_NETLIB_CREATEPACKETRECVER - uint8_t *buffer; // contains the recved data -}; - -EXTERN_C MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam); - -///////////////////////////////////////////////////////////////////////////////////////// -// Sets a gateway polling timeout interval -// -// Returns previous timeout value -// Errors: -1 - -EXTERN_C MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN hConnection, int iTimeout); - -///////////////////////////////////////////////////////////////////////////////////////// -// netlib log funcitons - -EXTERN_C MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr); -EXTERN_C MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr); - -EXTERN_C MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...); -EXTERN_C MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...); - -EXTERN_C MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *buf, size_t len, bool bIsSent, int flags); - -// Inits a required security provider. Right now only NTLM is supported -// Returns HANDLE = NULL on error or non-null value on success -// Known providers: Basic, NTLM, Negotiate, Kerberos, GSSAPI - (Kerberos SASL) -EXTERN_C MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProviderName, const wchar_t *szPrincipal = nullptr); - -// Destroys a security provider's handle, provided by Netlib_InitSecurityProvider. -// Right now only NTLM is supported -EXTERN_C MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hProvider); - -// Returns the NTLM response string. The result value should be freed using mir_free -EXTERN_C MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *szLogin, wchar_t *szPass, unsigned &complete); - -///////////////////////////////////////////////////////////////////////////////////////// -// SSL/TLS support - -#if !defined(HSSL_DEFINED) -DECLARE_HANDLE(HSSL); -#endif - -// Makes connection SSL -// Returns 0 on failure 1 on success -EXTERN_C MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *host); - -// negotiates SSL session, verifies cert, returns NULL if failed -EXTERN_C MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify); - -// return true if there is either unsend or buffered received data (ie. after peek) -EXTERN_C MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl); - -// reads number of bytes, keeps in buffer if peek != 0 -EXTERN_C MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek); - -// writes data to the SSL socket -EXTERN_C MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num); - -// closes SSL session, but keeps socket open -EXTERN_C MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl); - -// frees all data associated with the SSL socket -EXTERN_C MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl); - -// gets TLS channel binging data for a socket -EXTERN_C MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer); - -///////////////////////////////////////////////////////////////////////////////////////// -// WebSocket support - -struct WSHeader -{ - WSHeader() - { - memset(this, 0, sizeof(*this)); - } - - bool bIsFinal, bIsMasked; - int opCode, firstByte; - size_t payloadSize, headerSize; -}; - -// connects to a WebSocket server -EXTERN_C MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER, const char *szHost, NETLIBHTTPHEADER *pHeaders = nullptr); - -// validates that the provided buffer contains full WebSocket datagram -EXTERN_C MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize); - -// sends a packet to WebSocket -EXTERN_C MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData); -EXTERN_C MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t strLen); - -///////////////////////////////////////////////////////////////////////////////////////// -// Netlib hooks (0.8+) - -// WARNING: these hooks are being called in the context of the calling thread, without switching -// to the first thread, like all another events do. The hook procedure should be ready for the -// multithreaded mode -// -// Parameters: -// wParam: NETLIBNOTIFY* - points to the data being sent/received -// lParam: NETLIBUSER* - points to the protocol definition - -struct NETLIBNOTIFY -{ - const char *buf; - int len; - int flags; - int result; // amount of bytes really sent/received -}; - -#define ME_NETLIB_FASTRECV "Netlib/OnRecv" // being called on every receive -#define ME_NETLIB_FASTSEND "Netlib/OnSend" // being called on every send -#define ME_NETLIB_FASTDUMP "Netlib/OnDump" // being called on every dump - -struct NETLIBCONNECTIONEVENTINFO -{ - BOOL connected; // 1-opening socket 0-closing socket - BOOL listening; // 1-bind 0-connect - SOCKADDR_IN local; // local IP+port (always used) - SOCKADDR_IN remote; // remote IP+port (only connect (opening + closing only if no proxy)) - SOCKADDR_IN proxy; // proxy IP+port (only connect when used) - char *szSettingsModule; // name of the registered Netlib user that requested the action -}; - -//This event is sent as a new port is bound or a new connection opened. -//It is NOT sent for sigle HTTP(S) requests. -//wParam=(WPARAM)(NETLIBCONNECTIONEVENTINFO*)hInfo -//lParam=(LPARAM)0 (not used) -#define ME_NETLIB_EVENT_CONNECTED "Netlib/Event/Connected" - -//This event is sent if coneection or listening socket is closed. -//It is NOT sent for sigle HTTP(S) requests. -//wParam=(WPARAM)(NETLIBCONNECTIONEVENTINFO*)hInfo -//lParam=(LPARAM)0 (not used) -#define ME_NETLIB_EVENT_DISCONNECTED "Netlib/Event/Disconnected" - -#endif // M_NETLIB_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-12 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_NETLIB_H__
+#define M_NETLIB_H__ 1
+
+#include "m_utils.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this module was created in 0.1.2.2
+// All error codes are returned via GetLastError() (or WSAGetLastError():
+// they're the same).
+// This module is thread-safe where it is sensible for it to be so. This
+// basically means that you can call anything from any thread, but don't try
+// to predict what will happen if you try to recv() on the same connection from
+// two different threads at the same time.
+// Note that because the vast majority of the routines in this module return
+// a pointer, I have decided to diverge from the rest of Miranda and go with
+// the convention that functions return false on failure and nonzero on success.
+
+struct NETLIBHTTPREQUEST;
+struct NETLIBOPENCONNECTION;
+
+#define NETLIB_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.112 Safari/537.36"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Initialises the netlib for a set of connections
+// Returns a HNETLIBUSER to be used for future netlib calls, NULL on failure
+// NOTE: Netlib is loaded after any plugins, so you need to wait until
+// ME_SYSTEM_MODULESLOADED before calling this function
+// Netlib settings are stored under the module szSettingsModule
+// All netlib settings being with "NL".
+// The default settings for registered users that don't have any settings stored
+// in the database are the same as those displayed by the <All connections> page
+// of the netlib options page.
+// Errors: ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY, ERROR_DUP_NAME
+
+struct NETLIBUSER
+{
+ char *szSettingsModule; // used for db settings and log
+ MAllStrings szDescriptiveName; // used in options dialog, already translated
+ uint32_t flags;
+ int minIncomingPorts; // only if NUF_INCOMING. Will be used for validation of user input.
+};
+
+#define NUF_INCOMING 0x01 // binds incoming ports
+#define NUF_OUTGOING 0x02 // makes outgoing plain connections
+#define NUF_NOOPTIONS 0x08 // don't create an options page for this. szDescriptiveName is never used.
+#define NUF_HTTPCONNS 0x10 // at least some connections are made for HTTP communication. Enables the HTTP proxy option in options.
+#define NUF_NOHTTPSOPTION 0x20 // disable the HTTPS proxy option in options. Use this if all communication is HTTP.
+#define NUF_UNICODE 0x40 // if set ptszDescriptiveName points to Unicode, otherwise it points to ANSI string
+
+EXTERN_C MIR_APP_DLL(HNETLIBUSER) Netlib_RegisterUser(const NETLIBUSER *pDescr);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Assign a Netlib user handle a set of dynamic HTTP headers to be used with all
+//
+// HTTP connections that enable the HTTP-use-sticky headers flag.
+// The headers persist until cleared with lParam = NULL.
+//
+// All memory should be allocated by the caller using malloc() from MS_SYSTEM_GET_MMI
+// Once it has passed to Netlib, Netlib is the owner of it, the caller should not refer to the memory
+// In any way after this point.
+//
+// NOTE: The szHeaders parameter should be a NULL terminated string following the HTTP header syntax.
+// This string will be injected verbatim, thus the user should be aware of setting strings that are not
+// headers. This service is NOT THREAD SAFE, only a single thread is expected to set the headers and a single
+// thread reading the pointer internally, stopping race conditions and mutual exclusion don't happen.
+//
+// Version 0.3.2a+ (2003/10/27)
+//
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders);
+
+/* Notes on HTTP gateway usage
+When a connection is initiated through an HTTP proxy using
+MS_NETLIB_OPENCONNECTION, netlib will GET nlu.szHttpGatewayHello and read
+the replied headers. Once this succeeds nlu.pfnHttpGatewayInit will be called
+with a valid handle to the connection, the NETLIBOPENCONNECTION structure that
+MS_NETLIB_OPENCONNECTION was called with, and the replied HTTP headers as its
+parameters. This function is responsible for recving and parsing the data then
+calling MS_NETLIB_SETHTTPPROXYINFO with the appropriate information.
+nlu.pfnHttpGatewayInit should return nonzero on success. If it returns zero
+then the entire connection attempt will return signalling failure. If your
+function needs to return an error code it can do so via SetLastError().
+If nlu.pfnHttpGatewayInit returns success without having called
+MS_NETLIB_SETHTTPPROXYINFO then the connection attempt will fail anyway.
+If you need more fine-tuned control over the GET/POST URLs than just appending
+sequence numbers you can call MS_NETLIB_SETHTTPPROXYINFO from within your
+wrap/unwrap functions (see below).
+
+Just prior to MS_NETLIB_OPENCONNECTION returning nlu.pfnHttpGatewayBegin is
+called with the handle to the connection and the NETLIBOPENCONNECTION structure
+as its parameters. This is for gateways that need special non-protocol
+initialisation. If you do send any packets in this function, you probably want
+to remember to use the MSG_NOHTTPGATEWAYWRAP flag. This function pointer can be
+NULL if this functionality isn't needed. This function must return nonzero on
+success. If it fails the connect attempt will return failure without changing
+LastError.
+
+Whenever MS_NETLIB_SEND is called on a connection through an HTTP proxy and
+the MSG_NOHTTPGATEWAYWRAP flags is not set and nlu.pfnHttpGatewayWrapSend is
+not NULL, nlu.pfnHttpGatewayWrapSend will be called *instead* of sending the
+data. It is this function's responsibility to wrap the sending data
+appropriately for transmission and call pfnNetlibSend to send it again.
+The flags parameter to nlu.pfnHttpGatewayWrapSend should be passed straight
+through to the pfnNetlibSend call. It has already been ORed with
+MSG_NOHTTPGATEWAYWRAP. nlu.pfnHttpGatewayWrapSend should return the a
+number of the same type as MS_NETLIB_SEND, ie the number of bytes sent or
+SOCKET_ERROR. The number of wrapping bytes should be subtracted so that the
+return value appears as if the proxy wasn't there.
+pfnNetlibSend() is identical to CallService(MS_NETLIB_SEND, ...) but it's
+quicker to call using this pointer than to do the CallService() lookup again.
+
+Whenever an HTTP reply is received inside MS_NETLIB_RECV the headers and data
+are read into memory. If the headers indicate success then the data is passed
+to nlu.pfnHttpGatewayUnwrapRecv (if it's non-NULL) for processing. This
+function should remove (and do other processing if necessary) all HTTP proxy
+specific headers and return a pointer to the buffer whose size is returned in
+*outBufLen. If the buffer needs to be resized then NetlibRealloc() should be
+used for that purpose, *not* your own CRT's realloc(). NetlibRealloc() behaves
+identically to realloc() so it's possible to free the original buffer and
+create a new one if that's the most sensible way to write your parser.
+If errors are encountered you should SetLastError() and return NULL;
+MS_NETLIB_RECV will return SOCKET_ERROR. If the passed buffer unwraps to
+contain no actual data you should set *outBufLen to 0 but make sure you return
+some non-NULL buffer that can be freed.
+
+When you call MS_NETLIB_SEND or MS_NETLIB_RECV from any of these functions, you
+should use the MSG_DUMPPROXY flag so that the logging is neat.
+*/
+
+#define PROXYTYPE_SOCKS4 1
+#define PROXYTYPE_SOCKS5 2
+#define PROXYTYPE_HTTP 3
+#define PROXYTYPE_HTTPS 4
+#define PROXYTYPE_IE 5
+
+struct NETLIBUSERSETTINGS
+{
+ int cbSize; // to be filled in before calling
+ int useProxy; // 1 or 0
+ int proxyType; // a PROXYTYPE_
+ char *szProxyServer; // can be NULL
+ int wProxyPort; // host byte order
+ int useProxyAuth; // 1 or 0. Always 0 for SOCKS4
+ char *szProxyAuthUser; // can be NULL, always used by SOCKS4
+ char *szProxyAuthPassword; // can be NULL
+ int useProxyAuthNtlm; // 1 or 0, only used by HTTP, HTTPS
+ int dnsThroughProxy; // 1 or 0
+ int specifyIncomingPorts; // 1 or 0
+ char *szIncomingPorts; // can be NULL. Of form "1024-1050, 1060-1070, 2000"
+ int specifyOutgoingPorts; // 0.3.3a+
+ char *szOutgoingPorts; // 0.3.3a+
+ int enableUPnP; // 0.6.1+ only for NUF_INCOMING
+ int validateSSL;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Gets the user-configured settings for a netlib user
+//
+// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+// The pointers referred to in the returned struct will remain valid until
+// the hUser handle is closed, or until the user changes the settings in the
+// options page, so it's best not to rely on them for too long.
+// Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(int) Netlib_GetUserSettings(HNETLIBUSER nlu, NETLIBUSERSETTINGS *result);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//Gets the user-configured settings for a netlib user idetified by name
+//
+//Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+//This function behaves like Netlib_GetUserSettings but the user is identified
+//by the name provided by registration. When the name is not found NETLIBUSERSETTINGS is set to NULL.
+//Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(int) Netlib_GetUserSettingsByName(char * UserSettingsName, NETLIBUSERSETTINGS *result);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Changes the user-configurable settings for a netlib user
+//
+// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+// This function is only really useful for people that specify NUF_NOOPTIONS
+// and want to create their own options.
+// Even if a setting is not active (eg szProxyAuthPassword when useProxyAuth is
+// zero) that settings is still set for use in the options dialog.
+// Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SetUserSettings(HNETLIBUSER nlu, const NETLIBUSERSETTINGS *result);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//Changes the user-configurable settings for a netlib user idetified by name
+//
+//Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+//This function behaves like Netlib_SetUserSettings but the user is identified
+//by the name provided by registration. Nothing will be changed when the name is not found.
+//Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SetUserSettingsByName(char * UserSettingsName, NETLIBUSERSETTINGS *result);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Closes a netlib handle
+//
+// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+// This function should be called on all handles returned by netlib functions
+// once you are done with them. If it's called on a socket-type handle, the
+// socket will be closed.
+// Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(int) Netlib_CloseHandle(HANDLE h);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Open a port and wait for connections on it
+//
+// Returns a HANDLE on success, NULL on failure
+// hUser should have been returned by MS_NETLIB_REGISTERUSER
+// This function does the equivalent of socket(), bind(), getsockname(),
+// listen(), accept()
+// Internally this function creates a new thread which waits around in accept()
+// for new connections. When one is received it calls nlb.pfnNewConnection *from
+// this new thread* and then loops back to wait again.
+// Close the returned handle to end the thread and close the open port.
+// Errors: ERROR_INVALID_PARAMETER, any returned by socket() or bind() or
+// listen() or getsockname()
+//
+// Notes:
+//
+// During development of 0.3.1a+ (2003/07/04) passing wPort != 0
+// will result in an attempt to bind on the port given in wPort
+// if this port is taken then you will get an error, so be sure to check
+// for such conditions.
+//
+// passing wPort != 0 is for people who need to open a set port for
+// daemon activities, usually passing wPort == 0 is what you want and
+// will result in a free port given by the TCP/IP socket layer and/or
+// seeded from the user selected port ranges.
+//
+// also note that wPort if != 0, will have be converted to network byte order
+//
+/* pExtra was added during 0.3.4+, prior its just two args, since we use the cdecl convention
+it shouldnt matter */
+
+typedef void (*NETLIBNEWCONNECTIONPROC)(HNETLIBCONN hNewConnection, uint32_t dwRemoteIP, void *pExtra);
+
+struct NETLIBBIND
+{
+ NETLIBNEWCONNECTIONPROC pfnNewConnection;
+
+ // function to call when there's a new connection. Params are: the
+ // new connection, IP of remote machine (host byte order)
+ uint32_t dwInternalIP; // set on return, host byte order
+ uint32_t dwExternalIP; // set on return, host byte order
+ uint16_t wPort, wExPort; // set on return, host byte order
+ void *pExtra; // argument is sent to callback
+};
+
+EXTERN_C MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Open a connection
+//
+// Returns a HNETLIBCONN to the new connection on success, NULL on failure
+// hUser must have been returned by MS_NETLIB_REGISTERUSER
+// Internally this function is the equivalent of socket(), gethostbyname(),
+// connect()
+// If NLOCF_HTTP is set and hUser is configured for an HTTP or HTTPS proxy then
+// this function will connect() to the proxy server only, without performing any
+// initialisation conversation.
+// If hUser is configured for an HTTP proxy and does not support HTTP gateways
+// and you try to open a connection without specifying NLOCF_HTTP then this
+// function will first attempt to open an HTTPS connection, if that fails it
+// will try a direct connection, if that fails it will return failure with the
+// error from the connect() during the direct connection attempt.
+// Errors: ERROR_INVALID_PARAMETER, any returned by socket(), gethostbyname(),
+// connect(), MS_NETLIB_SEND, MS_NETLIB_RECV, select()
+// ERROR_TIMEOUT (during proxy communication)
+// ERROR_BAD_FORMAT (very invalid proxy reply)
+// ERROR_ACCESS_DENIED (by proxy)
+// ERROR_CONNECTION_UNAVAIL (socks proxy can't connect to identd)
+// ERROR_INVALID_ACCESS (proxy refused identd auth)
+// ERROR_INVALID_DATA (proxy returned invalid code)
+// ERROR_INVALID_ID_AUTHORITY (proxy requires use of auth method that's not supported)
+// ERROR_GEN_FAILURE (socks5/https general failure)
+// ERROR_CALL_NOT_IMPLEMENTED (socks5 command not supported)
+// ERROR_INVALID_ADDRESS (socks5 address type not supported)
+// HTTP: anything from nlu.pfnHttpGatewayInit, nlu.pfnHttpGatewayBegin,
+// MS_NETLIB_SENDHTTPREQUEST or MS_NETLIB_RECVHTTPHEADERS
+
+#define NLOCF_HTTP 0x0001 // this connection will be used for HTTP communications. If configured for an HTTP/HTTPS proxy the connection is opened as if there was no proxy.
+#define NLOCF_STICKYHEADERS 0x0002 // this connection should send the sticky headers associated with NetLib user apart of any HTTP request
+#define NLOCF_UDP 0x0008 // this connection is UDP
+#define NLOCF_SSL 0x0010 // this connection is SSL
+
+EXTERN_C MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(HNETLIBUSER nlu, const char *szHost, int port, int timeout = 0, int flags = 0);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Sets the required information for an HTTP proxy connection
+//
+// Returns nonzero on success, 0 on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+// This function is designed to be called from within pfnHttpGatewayInit
+// See notes below MS_NETLIB_REGISTERUSER.
+// Errors: ERROR_INVALID_PARAMETER
+
+#define NLHPIF_USEGETSEQUENCE 0x0001 // append sequence numbers to GET requests
+#define NLHPIF_USEPOSTSEQUENCE 0x0002 // append sequence numbers to POST requests
+#define NLHPIF_GETPOSTSAMESEQUENCE 0x0004 // GET and POST use the same sequence
+#define NLHPIF_HTTP11 0x0008 // HTTP 1.1 proxy
+
+struct NETLIBHTTPPROXYINFO
+{
+ uint32_t flags;
+ int firstGetSequence, firstPostSequence;
+ int combinePackets;
+ char *szHttpPostUrl;
+ char *szHttpGetUrl;
+};
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN hConnection, const NETLIBHTTPPROXYINFO *nlhpi);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Gets the SOCKET associated with a netlib handle
+//
+// Returns the SOCKET on success, INVALID_SOCKET on failure
+// hNetlibHandle should have been returned by MS_NETLIB_BINDPORT or
+// MS_NETLIB_OPENCONNECTION only.
+// Be careful how you use this socket because you might be connected via an
+// HTTP proxy in which case calling send() or recv() will totally break things.
+// Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(UINT_PTR) Netlib_GetSocket(HNETLIBCONN hConnection);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define Netlib_GetBase64DecodedBufferSize(cchEncoded) (((cchEncoded)>>2)*3)
+#define Netlib_GetBase64EncodedBufferSize(cbDecoded) (((cbDecoded)*4+11)/12*4+1)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Gets HNETLIBUSER owner of a connection
+
+EXTERN_C MIR_APP_DLL(HNETLIBUSER) Netlib_GetConnNlu(HNETLIBCONN hConn);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Gets the fake User-Agent header field to make some sites happy
+
+EXTERN_C MIR_APP_DLL(char*) Netlib_GetUserAgent();
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Converts numerical representation of IP in SOCKADDR_INET into string representation with IP and port
+// IPv4 will be supplied in formats address:port or address
+// IPv6 will be supplied in formats [address]:port or [address]
+// Returns pointer to the string or NULL if not successful
+
+struct sockaddr_in;
+EXTERN_C MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr);
+EXTERN_C MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, sockaddr_in *addr);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Gets connection Information
+// IPv4 will be supplied in formats address:port or address
+// IPv6 will be supplied in formats [address]:port or [address]
+// Returns 0 if successful
+
+struct NETLIBCONNINFO
+{
+ char szIpPort[64];
+ unsigned dwIpv4;
+ uint16_t wPort;
+};
+
+EXTERN_C MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN hConnection, NETLIBCONNINFO *connInfo);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Gets connection Information
+//
+// Returns (INT_PTR)(NETLIBIPLIST*) numeric IP address address array
+// the last element of the array is all 0s, 0 if not successful
+
+struct NETLIBIPLIST
+{
+ unsigned cbNum;
+ char szIp[1][64];
+};
+
+EXTERN_C MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Send an HTTP request over a connection
+//
+// Returns number of bytes sent on success, SOCKET_ERROR on failure
+// hConnection must have been returned by MS_NETLIB_OPENCONNECTION
+// Note that if you use NLHRF_SMARTAUTHHEADER and NTLM authentication is in use
+// then the full NTLM authentication transaction occurs, comprising sending the
+// domain, receiving the challenge, then sending the response.
+// nlhr.resultCode and nlhr.szResultDescr are ignored by this function.
+// Errors: ERROR_INVALID_PARAMETER, anything returned by MS_NETLIB_SEND
+
+struct NETLIBHTTPHEADER
+{
+ char *szName;
+ char *szValue;
+};
+
+EXTERN_C MIR_APP_DLL(char*) Netlib_GetHeader(const NETLIBHTTPREQUEST *pRec, const char *pszName);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define REQUEST_RESPONSE 0 // used by structure returned by MS_NETLIB_RECVHTTPHEADERS
+#define REQUEST_GET 1
+#define REQUEST_POST 2
+#define REQUEST_CONNECT 3
+#define REQUEST_HEAD 4
+#define REQUEST_PUT 5
+#define REQUEST_DELETE 6
+#define REQUEST_PATCH 7
+
+#define NLHRF_MANUALHOST 0x00000001 // do not remove any host and/or protocol portion of szUrl before sending it
+#define NLHRF_HTTP11 0x00000010 // use HTTP 1.1
+#define NLHRF_PERSISTENT 0x00000020 // preserve connection on exit, open connection provided in the nlc field of the reply
+ // it should be supplied in nlc field of request for reuse or closed if not needed
+#define NLHRF_SSL 0x00000040 // use SSL connection
+#define NLHRF_NOPROXY 0x00000080 // do not use proxy server
+#define NLHRF_REDIRECT 0x00000100 // handle HTTP redirect requests (response 30x), the resulting url provided in szUrl of the response
+#define NLHRF_NODUMP 0x00010000 // never dump this to the log
+#define NLHRF_NODUMPHEADERS 0x00020000 // don't dump http headers (only useful for POSTs and MS_NETLIB_HTTPTRANSACTION)
+#define NLHRF_DUMPPROXY 0x00040000 // this transaction is a proxy communication. For dump filtering only.
+#define NLHRF_DUMPASTEXT 0x00080000 // dump posted and reply data as text. Headers are always dumped as text.
+#define NLHRF_NODUMPSEND 0x00100000 // do not dump sent message.
+
+struct NETLIBHTTPREQUEST
+{
+ int cbSize;
+ int requestType; // a REQUEST_
+ uint32_t flags;
+ char *szUrl;
+ NETLIBHTTPHEADER *headers; // If this is a POST request and headers doesn't contain a Content-Length it'll be added automatically
+ int headersCount;
+ char *pData; // data to be sent in POST request.
+ int dataLength; // must be 0 for REQUEST_GET/REQUEST_CONNECT
+ int resultCode;
+ char *szResultDescr;
+ HNETLIBCONN nlc;
+ int timeout;
+
+ __forceinline const char *operator[](const char *pszName) {
+ return Netlib_GetHeader(this, pszName);
+ }
+};
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN hConnection, NETLIBHTTPREQUEST *pRec);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Receives HTTP headers
+//
+// Returns a pointer to a NETLIBHTTPREQUEST structure on success, NULL on failure.
+// Call Netlib_FreeHttpRequest() to free this.
+// hConnection must have been returned by MS_NETLIB_OPENCONNECTION
+// nlhr->pData = NULL and nlhr->dataLength = 0 always. The requested data should
+// be retrieved using MS_NETLIB_RECV once the header has been parsed.
+// If the headers haven't finished within 60 seconds the function returns NULL
+// and ERROR_TIMEOUT.
+// Errors: ERROR_INVALID_PARAMETER, any from MS_NETLIB_RECV or select()
+// ERROR_HANDLE_EOF (connection closed before headers complete)
+// ERROR_TIMEOUT (headers still not complete after 60 seconds)
+// ERROR_BAD_FORMAT (invalid character or line ending in headers, or first line is blank)
+// ERROR_BUFFER_OVERFLOW (each header line must be less than 4096 chars long)
+// ERROR_INVALID_DATA (first header line is malformed ("http/[01].[0-9] [0-9]+ .*", or no colon in subsequent line)
+
+EXTERN_C MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags = 0);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Free the memory used by a NETLIBHTTPREQUEST structure
+//
+// Returns true on success, false on failure (!! this is different to most of the rest of Miranda, but consistent with netlib)
+// This should only be called on structures returned by
+// MS_NETLIB_RECVHTTPHEADERS or MS_NETLIB_HTTPTRANSACTION. Calling it on an
+// arbitrary structure will have disastrous results.
+// Errors: ERROR_INVALID_PARAMETER
+
+EXTERN_C MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST*);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// smart pointer for NETLIBHTTPREQUEST via a call of Netlib_FreeHttpRequest()
+
+#ifdef __cplusplus
+class NLHR_PTR
+{
+protected:
+ NETLIBHTTPREQUEST *_p;
+
+public:
+ __forceinline explicit NLHR_PTR(NETLIBHTTPREQUEST *p) : _p(p) {}
+
+ __forceinline NETLIBHTTPREQUEST* operator=(INT_PTR i_p)
+ {
+ return operator=((NETLIBHTTPREQUEST*)i_p);
+ }
+ __forceinline NETLIBHTTPREQUEST* operator=(NETLIBHTTPREQUEST *p)
+ {
+ if (_p)
+ Netlib_FreeHttpRequest(_p);
+ _p = p;
+ return _p;
+ }
+ __forceinline operator NETLIBHTTPREQUEST*() const { return _p; }
+ __forceinline NETLIBHTTPREQUEST* operator->() const { return _p; }
+ __forceinline ~NLHR_PTR()
+ {
+ Netlib_FreeHttpRequest(_p);
+ }
+};
+
+struct MIR_APP_EXPORT MHttpRequest : public NETLIBHTTPREQUEST, public MZeroedObject
+{
+ MHttpRequest();
+ ~MHttpRequest();
+
+ CMStringA m_szUrl;
+ CMStringA m_szParam;
+ void *pUserInfo = nullptr;
+
+ void AddHeader(const char *szName, const char *szValue);
+};
+
+template <class T>
+class MTHttpRequest : public MHttpRequest
+{
+public:
+ __forceinline MTHttpRequest()
+ {}
+
+ typedef void (T::*MTHttpRequestHandler)(NETLIBHTTPREQUEST*, struct AsyncHttpRequest*);
+ MTHttpRequestHandler m_pFunc = nullptr;
+};
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const INT_PARAM&);
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const INT64_PARAM&);
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const CHAR_PARAM&);
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest*, const WCHAR_PARAM&);
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Do an entire HTTP transaction
+//
+// Returns a pointer to another NETLIBHTTPREQUEST structure on success, NULL on failure.
+// Call Netlib_FreeHttpRequest() to free this.
+// hUser must have been returned by MS_NETLIB_REGISTERUSER
+// nlhr.szUrl should be a full HTTP URL. If it does not start with http:// , that
+// will be assumed (but it's best not to use this fact, for reasons of
+// extensibility).
+// This function is the equivalent of MS_NETLIB_OPENCONNECTION,
+// MS_NETLIB_SENDHTTPREQ, MS_NETLIB_RECVHTTPHEADERS, MS_NETLIB_RECV,
+// MS_NETLIB_CLOSEHANDLE
+// nlhr.headers will be augmented with the following headers unless they have
+// already been set by the caller:
+// "Host" (regardless of whether it is requested in nlhr.flags)
+// "User-Agent" (of the form "Miranda/0.1.2.2 (alpha)" or "Miranda/0.1.2.2")
+// "Content-Length" (for POSTs only. Set to nlhr.dataLength)
+// If you do not want to send one of these headers, create a nlhr.headers with
+// szValue = NULL.
+// In the return value headers, headerCount, pData, dataLength, resultCode and
+// szResultDescr are all valid.
+// In the return value pData[dataLength] == 0 always, as an extra safeguard
+// against programming slips.
+// Note that the function can succeed (ie not return NULL) yet result in an HTTP
+// error code. You should check that resultCode == 2xx before proceeding.
+// Errors: ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY, anything from the above
+// list of functions
+
+EXTERN_C MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER hNlu, NETLIBHTTPREQUEST *pRequest);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Send data over a connection
+//
+// Returns the number of bytes sent on success, SOCKET_ERROR on failure
+// Errors: ERROR_INVALID_PARAMETER
+// anything from send(), nlu.pfnHttpGatewayWrapSend()
+// HTTP proxy: ERROR_GEN_FAILURE (http result code wasn't 2xx)
+// anything from socket(), connect(),
+// MS_NETLIB_SENDHTTPREQUEST, MS_NETLIB_RECVHTTPHEADERS
+// flags:
+
+#define MSG_NOHTTPGATEWAYWRAP 0x010000 // don't wrap the outgoing packet using nlu.pfnHttpGatewayWrapSend
+#define MSG_NODUMP 0x020000 // don't dump this packet to the log
+#define MSG_DUMPPROXY 0x040000 // this is proxy communiciation. For dump filtering only.
+#define MSG_DUMPASTEXT 0x080000 // this is textual data, don't dump as hex
+#define MSG_RAW 0x100000 // send as raw data, bypass any HTTP proxy stuff
+#define MSG_DUMPSSL 0x200000 // this is SSL traffic. For dump filtering only.
+#define MSG_NOTITLE 0x400000 // skip date, time & protocol from dump
+
+EXTERN_C MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN hConn, const char *buf, int len, int flags = 0);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Receive data over a connection
+//
+// Returns the number of bytes read on success, SOCKET_ERROR on failure,
+// 0 if the connection has been closed
+// Flags supported: MSG_PEEK, MSG_NODUMP, MSG_DUMPPROXY, MSG_NOHTTPGATEWAYWRAP,
+// MSG_DUMPASTEXT, MSG_RAW
+// On using MSG_NOHTTPGATEWAYWRAP: Because packets through an HTTP proxy are
+// batched and cached and stuff, using this flag is not a guarantee that it
+// will be obeyed, and if it is it may even be propogated to future calls
+// even if you don't specify it then. Because of this, the flag should be
+// considered an all-or-nothing thing: either use it for the entire duration
+// of a connection, or not at all.
+// Errors: ERROR_INVALID_PARAMETER, anything from recv()
+// HTTP proxy: ERROR_GEN_FAILURE (http result code wasn't 2xx)
+// ERROR_INVALID_DATA (no Content-Length header in reply)
+// ERROR_NOT_ENOUGH_MEMORY (Content-Length very large)
+// ERROR_HANDLE_EOF (connection closed before Content-Length bytes recved)
+// anything from select(), MS_NETLIB_RECVHTTPHEADERS,
+// nlu.pfnHttpGatewayUnwrapRecv, socket(), connect(),
+// MS_NETLIB_SENDHTTPREQUEST
+
+EXTERN_C MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN hConn, char *buf, int len, int flags = 0);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Determine the status of one or more connections
+// Returns the number of ready connections, SOCKET_ERROR on failure, 0 if the timeout expired.
+// All handles passed to this function must have been returned by either
+// MS_NETLIB_OPENCONNECTION or MS_NETLIB_BINDPORT.
+// The last handle in each list must be followed by either NULL or INVALID_HANDLE_VALUE.
+// Errors: ERROR_INVALID_HANDLE, ERROR_INVALID_DATA, anything from select()
+
+struct NETLIBSELECT
+{
+ uint32_t dwTimeout; // in milliseconds, INFINITE is acceptable
+ HNETLIBCONN hReadConns[FD_SETSIZE + 1];
+ HNETLIBCONN hWriteConns[FD_SETSIZE + 1];
+ HNETLIBCONN hExceptConns[FD_SETSIZE + 1];
+};
+
+EXTERN_C MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls);
+
+struct NETLIBSELECTEX
+{
+ uint32_t dwTimeout; // in milliseconds, INFINITE is acceptable
+ HNETLIBCONN hReadConns[FD_SETSIZE + 1];
+ HNETLIBCONN hWriteConns[FD_SETSIZE + 1];
+ HNETLIBCONN hExceptConns[FD_SETSIZE + 1];
+
+ BOOL hReadStatus[FD_SETSIZE+1]; /* out, [in, expected to be FALSE] */
+ BOOL hWriteStatus[FD_SETSIZE+1]; /* out, [in, expected to be FALSE] */
+ BOOL hExceptStatus[FD_SETSIZE+1]; /* out, [in, expected to be FALSE] */
+};
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Shutdown connection
+
+EXTERN_C MIR_APP_DLL(void) Netlib_Shutdown(HNETLIBCONN h);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Create a packet receiver
+//
+// Returns a HANDLE on success, NULL on failure
+// The packet receiver implements the common situation where you have variable
+// length packets coming in over a connection and you want to split them up
+// in order to handle them.
+// The major limitation is that the buffer is created in memory, so you can't
+// have arbitrarily large packets.
+// Errors: ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY
+
+EXTERN_C MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN hConnection, int iMaxSize);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the next set of packets from a packet receiver
+//
+// Returns the total number of bytes available in the buffer, 0 if the
+// connection was closed, SOCKET_ERROR on error.
+// hPacketRecver must have been returned by MS_NETLIB_CREATEPACKETRECVER
+// If nlpr.bytesUsed is set to zero and the buffer is already full up to
+// maxPacketSize, it is assumed that too large a packet has been received. All
+// data in the buffer is discarded and receiving is begun anew. This will
+// probably cause alignment problems so if you think this is likely to happen
+// then you should deal with it yourself.
+// Closing the packet receiver will not close the associated connection, but
+// will discard any bytes still in the buffer, so if you intend to carry on
+// reading from that connection, make sure you have processed the buffer first.
+// This function is the equivalent of a memmove() to remove the first bytesUsed
+// from the buffer, select() if dwTimeout is not INFINITE, then MS_NETLIB_RECV.
+// Errors: ERROR_INVALID_PARAMETER, ERROR_TIMEOUT,
+// anything from select(), MS_NETLIB_RECV
+
+struct NETLIBPACKETRECVER
+{
+ uint32_t dwTimeout; // fill before calling. In milliseconds. INFINITE is valid
+ int bytesUsed; // fill before calling. This many bytes are removed from the start of the buffer. Set to 0 on return
+ int bytesAvailable; // equal to the return value, unless the return value is 0
+ int bufferSize; // same as parameter to MS_NETLIB_CREATEPACKETRECVER
+ uint8_t *buffer; // contains the recved data
+};
+
+EXTERN_C MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Sets a gateway polling timeout interval
+//
+// Returns previous timeout value
+// Errors: -1
+
+EXTERN_C MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN hConnection, int iTimeout);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// netlib log funcitons
+
+EXTERN_C MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr);
+EXTERN_C MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr);
+
+EXTERN_C MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...);
+EXTERN_C MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...);
+
+EXTERN_C MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *buf, size_t len, bool bIsSent, int flags);
+
+// Inits a required security provider. Right now only NTLM is supported
+// Returns HANDLE = NULL on error or non-null value on success
+// Known providers: Basic, NTLM, Negotiate, Kerberos, GSSAPI - (Kerberos SASL)
+EXTERN_C MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProviderName, const wchar_t *szPrincipal = nullptr);
+
+// Destroys a security provider's handle, provided by Netlib_InitSecurityProvider.
+// Right now only NTLM is supported
+EXTERN_C MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hProvider);
+
+// Returns the NTLM response string. The result value should be freed using mir_free
+EXTERN_C MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *szLogin, wchar_t *szPass, unsigned &complete);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SSL/TLS support
+
+#if !defined(HSSL_DEFINED)
+DECLARE_HANDLE(HSSL);
+#endif
+
+// Makes connection SSL
+// Returns 0 on failure 1 on success
+EXTERN_C MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *host);
+
+// negotiates SSL session, verifies cert, returns NULL if failed
+EXTERN_C MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify);
+
+// return true if there is either unsend or buffered received data (ie. after peek)
+EXTERN_C MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl);
+
+// reads number of bytes, keeps in buffer if peek != 0
+EXTERN_C MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek);
+
+// writes data to the SSL socket
+EXTERN_C MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num);
+
+// closes SSL session, but keeps socket open
+EXTERN_C MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl);
+
+// frees all data associated with the SSL socket
+EXTERN_C MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl);
+
+// gets TLS channel binging data for a socket
+EXTERN_C MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WebSocket support
+
+struct WSHeader
+{
+ WSHeader()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+
+ bool bIsFinal, bIsMasked;
+ int opCode, firstByte;
+ size_t payloadSize, headerSize;
+};
+
+// connects to a WebSocket server
+EXTERN_C MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER, const char *szHost, NETLIBHTTPHEADER *pHeaders = nullptr);
+
+// validates that the provided buffer contains full WebSocket datagram
+EXTERN_C MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize);
+
+// sends a packet to WebSocket
+EXTERN_C MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData);
+EXTERN_C MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t strLen);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Netlib hooks (0.8+)
+
+// WARNING: these hooks are being called in the context of the calling thread, without switching
+// to the first thread, like all another events do. The hook procedure should be ready for the
+// multithreaded mode
+//
+// Parameters:
+// wParam: NETLIBNOTIFY* - points to the data being sent/received
+// lParam: NETLIBUSER* - points to the protocol definition
+
+struct NETLIBNOTIFY
+{
+ const char *buf;
+ int len;
+ int flags;
+ int result; // amount of bytes really sent/received
+};
+
+#define ME_NETLIB_FASTRECV "Netlib/OnRecv" // being called on every receive
+#define ME_NETLIB_FASTSEND "Netlib/OnSend" // being called on every send
+#define ME_NETLIB_FASTDUMP "Netlib/OnDump" // being called on every dump
+
+struct NETLIBCONNECTIONEVENTINFO
+{
+ BOOL connected; // 1-opening socket 0-closing socket
+ BOOL listening; // 1-bind 0-connect
+ SOCKADDR_IN local; // local IP+port (always used)
+ SOCKADDR_IN remote; // remote IP+port (only connect (opening + closing only if no proxy))
+ SOCKADDR_IN proxy; // proxy IP+port (only connect when used)
+ char *szSettingsModule; // name of the registered Netlib user that requested the action
+};
+
+//This event is sent as a new port is bound or a new connection opened.
+//It is NOT sent for sigle HTTP(S) requests.
+//wParam=(WPARAM)(NETLIBCONNECTIONEVENTINFO*)hInfo
+//lParam=(LPARAM)0 (not used)
+#define ME_NETLIB_EVENT_CONNECTED "Netlib/Event/Connected"
+
+//This event is sent if coneection or listening socket is closed.
+//It is NOT sent for sigle HTTP(S) requests.
+//wParam=(WPARAM)(NETLIBCONNECTIONEVENTINFO*)hInfo
+//lParam=(LPARAM)0 (not used)
+#define ME_NETLIB_EVENT_DISCONNECTED "Netlib/Event/Disconnected"
+
+#endif // M_NETLIB_H__
diff --git a/include/m_options.h b/include/m_options.h index b60b84a711..e2cdc1122d 100644 --- a/include/m_options.h +++ b/include/m_options.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_protocols.h b/include/m_protocols.h index 889de6882a..927443bb69 100644 --- a/include/m_protocols.h +++ b/include/m_protocols.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_protoint.h b/include/m_protoint.h index 1eea83eaa8..972ca5a9da 100644 --- a/include/m_protoint.h +++ b/include/m_protoint.h @@ -1,313 +1,313 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_PROTOINT_H__ -#define M_PROTOINT_H__ 1 - -#include <m_system.h> -#include <m_protosvc.h> -#include <m_database.h> -#include <m_genmenu.h> -#include <m_utils.h> - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol helpers - -struct PROTO_INTERFACE; - -// Call it in the very beginning of your proto's constructor -EXTERN_C MIR_APP_DLL(void) ProtoConstructor(PROTO_INTERFACE *pThis, const char *pszModuleName, const wchar_t *ptszUserName); - -// Call it in the very end of your proto's destructor -EXTERN_C MIR_APP_DLL(void) ProtoDestructor(PROTO_INTERFACE *pThis); - -#if defined( __cplusplus ) -typedef void (MIR_CDECL PROTO_INTERFACE::*ProtoThreadFunc)(void*); -EXTERN_C MIR_APP_DLL(void) ProtoForkThread(PROTO_INTERFACE *pThis, ProtoThreadFunc, void *param); -EXTERN_C MIR_APP_DLL(HANDLE) ProtoForkThreadEx(PROTO_INTERFACE *pThis, ProtoThreadFunc, void *param, UINT* threadID); -EXTERN_C MIR_APP_DLL(void) ProtoWindowAdd(PROTO_INTERFACE *pThis, HWND hwnd); -EXTERN_C MIR_APP_DLL(void) ProtoWindowRemove(PROTO_INTERFACE *pThis, HWND hwnd); - -typedef int (MIR_CDECL PROTO_INTERFACE::*ProtoEventFunc)(WPARAM, LPARAM); -EXTERN_C MIR_APP_DLL(void) ProtoHookEvent(PROTO_INTERFACE *pThis, const char* szName, ProtoEventFunc pFunc); -EXTERN_C MIR_APP_DLL(HANDLE) ProtoCreateHookableEvent(PROTO_INTERFACE *pThis, const char* szService); - -typedef INT_PTR (MIR_CDECL PROTO_INTERFACE::*ProtoServiceFunc)(WPARAM, LPARAM); -EXTERN_C MIR_APP_DLL(void) ProtoCreateService(PROTO_INTERFACE *pThis, const char* szService, ProtoServiceFunc); - -typedef INT_PTR (MIR_CDECL PROTO_INTERFACE::*ProtoServiceFuncParam)(WPARAM, LPARAM, LPARAM); -EXTERN_C MIR_APP_DLL(void) ProtoCreateServiceParam(PROTO_INTERFACE *pThis, const char* szService, ProtoServiceFuncParam, LPARAM); -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// interface declaration - -enum ProtoMenuItemType -{ - PROTO_MENU_REQ_AUTH, - PROTO_MENU_GRANT_AUTH, - PROTO_MENU_REVOKE_AUTH, - PROTO_MENU_LOAD_HISTORY -}; - -struct MIR_APP_EXPORT PROTO_INTERFACE : public MZeroedObject -{ - -protected: - MWindowList m_hWindowList = 0; // list of all windows which belong to this protocol's instance - -public: - int m_iStatus; // current protocol status - int m_iDesiredStatus; // status to be set after logging in - int m_iXStatus; // extanded status - int m_iVersion; // version 2 or higher designate support of Unicode services - wchar_t* m_tszUserName; // human readable protocol's name - char* m_szModuleName; // internal protocol name, also its database module name - HANDLE m_hProtoIcon = 0; // icon to be displayed in the account manager - HNETLIBUSER m_hNetlibUser = 0; // network agent - HGENMENU m_hmiMainMenu = 0; // if protocol menus are displayed in the main menu, this is the root - - PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName); - ~PROTO_INTERFACE(); - - ////////////////////////////////////////////////////////////////////////////////////// - // Helpers - - __forceinline INT_PTR ProtoBroadcastAck(MCONTACT hContact, int type, int hResult, HANDLE hProcess, LPARAM lParam = 0) { - return ::ProtoBroadcastAck(m_szModuleName, hContact, type, hResult, hProcess, lParam); } - __forceinline void ProtoBroadcastAsync(MCONTACT hContact, int type, int hResult, HANDLE hProcess, LPARAM lParam = 0) { - return ::ProtoBroadcastAsync(m_szModuleName, hContact, type, hResult, hProcess, lParam); } - - __forceinline INT_PTR delSetting(const char *name) { return db_unset(0, m_szModuleName, name); } - __forceinline INT_PTR delSetting(MCONTACT hContact, const char *name) { return db_unset(hContact, m_szModuleName, name); } - - __forceinline bool getBool(const char *name, bool defaultValue = false) { - return db_get_b(0, m_szModuleName, name, defaultValue) != 0; } - __forceinline bool getBool(MCONTACT hContact, const char *name, bool defaultValue = false) { - return db_get_b(hContact, m_szModuleName, name, defaultValue) != 0; } - - MBinBuffer getBlob(const char *pSetting); - MBinBuffer getBlob(MCONTACT hContact, const char *pSetting); - - __forceinline bool isChatRoom(MCONTACT hContact) { return getBool(hContact, "ChatRoom", false); } - - __forceinline int getByte(const char *name, uint8_t defaultValue = 0) { - return db_get_b(0, m_szModuleName, name, defaultValue); } - __forceinline int getByte(MCONTACT hContact, const char *name, uint8_t defaultValue = 0) { - return db_get_b(hContact, m_szModuleName, name, defaultValue); } - - __forceinline int getWord(const char *name, uint16_t defaultValue = 0) { - return db_get_w(0, m_szModuleName, name, defaultValue); } - __forceinline int getWord(MCONTACT hContact, const char *name, uint16_t defaultValue = 0) { - return db_get_w(hContact, m_szModuleName, name, defaultValue); } - - __forceinline uint32_t getDword(const char *name, uint32_t defaultValue = 0) { - return db_get_dw(0, m_szModuleName, name, defaultValue); } - __forceinline uint32_t getDword(MCONTACT hContact, const char *name, uint32_t defaultValue = 0) { - return db_get_dw(hContact, m_szModuleName, name, defaultValue); } - - __forceinline INT_PTR getString(const char *name, DBVARIANT *result) { - return db_get_s(0, m_szModuleName, name, result, DBVT_ASCIIZ); } - __forceinline INT_PTR getString(MCONTACT hContact, const char *name, DBVARIANT *result) { - return db_get_s(hContact, m_szModuleName, name, result, DBVT_ASCIIZ); } - - __forceinline INT_PTR getUString(const char *name, DBVARIANT *result) { - return db_get_s(0, m_szModuleName, name, result, DBVT_UTF8); } - __forceinline INT_PTR getUString(MCONTACT hContact, const char *name, DBVARIANT *result) { - return db_get_s(hContact, m_szModuleName, name, result, DBVT_UTF8); } - - __forceinline INT_PTR getWString(const char *name, DBVARIANT *result) { - return db_get_s(0, m_szModuleName, name, result, DBVT_WCHAR); } - __forceinline INT_PTR getWString(MCONTACT hContact, const char *name, DBVARIANT *result) { - return db_get_s(hContact, m_szModuleName, name, result, DBVT_WCHAR); } - - __forceinline char* getStringA(const char *name, const char *szValue = nullptr) { - return db_get_sa(0, m_szModuleName, name, szValue); } - __forceinline char* getStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) { - return db_get_sa(hContact, m_szModuleName, name, szValue); } - - __forceinline char* getUStringA(const char *name, const char *szValue = nullptr) { - return db_get_utfa(0, m_szModuleName, name, szValue); } - __forceinline char* getUStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) { - return db_get_utfa(hContact, m_szModuleName, name, szValue); } - - __forceinline wchar_t* getWStringA(const char *name, const wchar_t *szValue = nullptr) { - return db_get_wsa(0, m_szModuleName, name, szValue); } - __forceinline wchar_t* getWStringA(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr) { - return db_get_wsa(hContact, m_szModuleName, name, szValue); } - - __forceinline CMStringA getMStringA(const char *name, const char *szValue = nullptr) { - return db_get_sm(0, m_szModuleName, name, szValue); } - __forceinline CMStringA getMStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) { - return db_get_sm(hContact, m_szModuleName, name, szValue); } - - __forceinline CMStringW getMStringW(const char *name, const wchar_t *szValue = nullptr) { - return db_get_wsm(0, m_szModuleName, name, szValue); } - __forceinline CMStringW getMStringW(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr) { - return db_get_wsm(hContact, m_szModuleName, name, szValue); } - - __forceinline void setByte(const char *name, uint8_t value) { db_set_b(0, m_szModuleName, name, value); } - __forceinline void setByte(MCONTACT hContact, const char *name, uint8_t value) { db_set_b(hContact, m_szModuleName, name, value); } - - __forceinline void setWord(const char *name, uint16_t value) { db_set_w(0, m_szModuleName, name, value); } - __forceinline void setWord(MCONTACT hContact, const char *name, uint16_t value) { db_set_w(hContact, m_szModuleName, name, value); } - - __forceinline void setDword(const char *name, uint32_t value) { db_set_dw(0, m_szModuleName, name, value); } - __forceinline void setDword(MCONTACT hContact, const char *name, uint32_t value) { db_set_dw(hContact, m_szModuleName, name, value); } - - __forceinline void setString(const char *name, const char* value) { db_set_s(0, m_szModuleName, name, value); } - __forceinline void setString(MCONTACT hContact, const char *name, const char* value) { db_set_s(hContact, m_szModuleName, name, value); } - - __forceinline void setUString(const char *name, const char* value) { db_set_utf(0, m_szModuleName, name, value); } - __forceinline void setUString(MCONTACT hContact, const char *name, const char* value) { db_set_utf(hContact, m_szModuleName, name, value); } - - __forceinline void setWString(const char *name, const wchar_t* value) { db_set_ws(0, m_szModuleName, name, value); } - __forceinline void setWString(MCONTACT hContact, const char *name, const wchar_t* value) { db_set_ws(hContact, m_szModuleName, name, value); } - - __forceinline Contacts AccContacts() const { return Contacts(m_szModuleName); } - - ////////////////////////////////////////////////////////////////////////////////////// - // Service functions - - void debugLogA(const char *szFormat, ...); - void debugLogW(const wchar_t *wszFormat, ...); - - void setAllContactStatuses(int iStatus, bool bSkipChats = true); - - void ReportSelfAvatarChanged(); - - void WindowSubscribe(HWND hwnd); - void WindowUnsubscribe(HWND hwnd); - - HGENMENU GetMenuItem(ProtoMenuItemType); - - ////////////////////////////////////////////////////////////////////////////////////// - // Virtual functions - - virtual MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr); - virtual MCONTACT AddToListByEvent(int flags, int iContact, MEVENT hDbEvent); - - virtual int Authorize(MEVENT hDbEvent); - virtual int AuthDeny(MEVENT hDbEvent, const wchar_t *szReason); - virtual int AuthRecv(MCONTACT hContact, PROTORECVEVENT *); - virtual int AuthRequest(MCONTACT hContact, const wchar_t *szMessage); - - virtual HANDLE FileAllow(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szPath); - virtual int FileCancel(MCONTACT hContact, HANDLE hTransfer); - virtual int FileDeny(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szReason); - virtual int FileResume(HANDLE hTransfer, int action, const wchar_t *szFilename); - - virtual INT_PTR GetCaps(int type, MCONTACT hContact = 0); - virtual int GetInfo(MCONTACT hContact, int infoType); - - virtual HANDLE SearchBasic(const wchar_t *id); - virtual HANDLE SearchByEmail(const wchar_t *email); - virtual HANDLE SearchByName(const wchar_t *nick, const wchar_t *firstName, const wchar_t *lastName); - virtual HWND SearchAdvanced(HWND owner); - virtual HWND CreateExtendedSearchUI(HWND owner); - - virtual int RecvContacts(MCONTACT hContact, PROTORECVEVENT *); - virtual int RecvFile(MCONTACT hContact, PROTORECVFILE *); - virtual MEVENT RecvMsg(MCONTACT hContact, PROTORECVEVENT *); - - virtual int SendContacts(MCONTACT hContact, int flags, int nContacts, MCONTACT *hContactsList); - virtual HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles); - virtual int SendMsg(MCONTACT hContact, int flags, const char *msg); - - virtual int SetApparentMode(MCONTACT hContact, int mode); - virtual int SetStatus(int iNewStatus); - - virtual HANDLE GetAwayMsg(MCONTACT hContact); - virtual int RecvAwayMsg(MCONTACT hContact, int mode, PROTORECVEVENT *evt); - virtual int SetAwayMsg(int iStatus, const wchar_t *msg); - - virtual int UserIsTyping(MCONTACT hContact, int type); - - ////////////////////////////////////////////////////////////////////////////////////// - // events - - // builds the account's protocol menu - virtual void OnBuildProtoMenu(void); - - // called when an account's contact is added - virtual void OnContactAdded(MCONTACT); - - // called when an account's contact is deleted - virtual void OnContactDeleted(MCONTACT); - - // called when an event is altered in database - virtual void OnEventEdited(MCONTACT, MEVENT); - - // called when an account gets physically removed from the database - virtual void OnErase(); - - // the analog of ME_SYSTEM_MODULESLOADED for an account - virtual void OnModulesLoaded(void); - - // same for ME_SYSTEM_SHUTDOWN - virtual void OnShutdown(void); - - // same for ME_SYSTEM_OKTOEXIT - virtual bool IsReadyToExit(void); -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Basic class for all protocols written in C++ - -template<class T> struct PROTO : public PROTO_INTERFACE -{ - typedef PROTO_INTERFACE CSuper; - - __forceinline PROTO(const char *szProto, const wchar_t *tszUserName) : - PROTO_INTERFACE(szProto, tszUserName) - {} - - __forceinline HANDLE CreateProtoEvent(const char *name) { - return ::ProtoCreateHookableEvent(this, name); } - - typedef int(MIR_CDECL T::*MyEventFunc)(WPARAM, LPARAM); - __forceinline void HookProtoEvent(const char *name, MyEventFunc pFunc) { - ::ProtoHookEvent(this, name, (ProtoEventFunc)pFunc); } - - typedef void(MIR_CDECL T::*MyThreadFunc)(void*); - __forceinline void ForkThread(MyThreadFunc pFunc, void *param = nullptr) { - ::ProtoForkThread(this, (ProtoThreadFunc)pFunc, param); } - HANDLE __forceinline ForkThreadEx(MyThreadFunc pFunc, void *param, UINT *pThreadId) { - return ::ProtoForkThreadEx(this, (ProtoThreadFunc)pFunc, param, pThreadId); } - - typedef INT_PTR(MIR_CDECL T::*MyServiceFunc)(WPARAM, LPARAM); - __forceinline void CreateProtoService(const char *name, MyServiceFunc pFunc) { - ::ProtoCreateService(this, name, (ProtoServiceFunc)pFunc); } - - typedef INT_PTR(MIR_CDECL T::*MyServiceFuncParam)(WPARAM, LPARAM, LPARAM); - __forceinline void CreateProtoServiceParam(const char *name, MyServiceFuncParam pFunc, LPARAM param) { - ::ProtoCreateServiceParam(this, name, (ProtoServiceFuncParam)pFunc, param); } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(PROTO_INTERFACE *) Proto_GetInstance(const char *szModule); -MIR_APP_DLL(PROTO_INTERFACE *) Proto_GetInstance(MCONTACT hContact); - -#endif // M_PROTOINT_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_PROTOINT_H__
+#define M_PROTOINT_H__ 1
+
+#include <m_system.h>
+#include <m_protosvc.h>
+#include <m_database.h>
+#include <m_genmenu.h>
+#include <m_utils.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// protocol helpers
+
+struct PROTO_INTERFACE;
+
+// Call it in the very beginning of your proto's constructor
+EXTERN_C MIR_APP_DLL(void) ProtoConstructor(PROTO_INTERFACE *pThis, const char *pszModuleName, const wchar_t *ptszUserName);
+
+// Call it in the very end of your proto's destructor
+EXTERN_C MIR_APP_DLL(void) ProtoDestructor(PROTO_INTERFACE *pThis);
+
+#if defined( __cplusplus )
+typedef void (MIR_CDECL PROTO_INTERFACE::*ProtoThreadFunc)(void*);
+EXTERN_C MIR_APP_DLL(void) ProtoForkThread(PROTO_INTERFACE *pThis, ProtoThreadFunc, void *param);
+EXTERN_C MIR_APP_DLL(HANDLE) ProtoForkThreadEx(PROTO_INTERFACE *pThis, ProtoThreadFunc, void *param, UINT* threadID);
+EXTERN_C MIR_APP_DLL(void) ProtoWindowAdd(PROTO_INTERFACE *pThis, HWND hwnd);
+EXTERN_C MIR_APP_DLL(void) ProtoWindowRemove(PROTO_INTERFACE *pThis, HWND hwnd);
+
+typedef int (MIR_CDECL PROTO_INTERFACE::*ProtoEventFunc)(WPARAM, LPARAM);
+EXTERN_C MIR_APP_DLL(void) ProtoHookEvent(PROTO_INTERFACE *pThis, const char* szName, ProtoEventFunc pFunc);
+EXTERN_C MIR_APP_DLL(HANDLE) ProtoCreateHookableEvent(PROTO_INTERFACE *pThis, const char* szService);
+
+typedef INT_PTR (MIR_CDECL PROTO_INTERFACE::*ProtoServiceFunc)(WPARAM, LPARAM);
+EXTERN_C MIR_APP_DLL(void) ProtoCreateService(PROTO_INTERFACE *pThis, const char* szService, ProtoServiceFunc);
+
+typedef INT_PTR (MIR_CDECL PROTO_INTERFACE::*ProtoServiceFuncParam)(WPARAM, LPARAM, LPARAM);
+EXTERN_C MIR_APP_DLL(void) ProtoCreateServiceParam(PROTO_INTERFACE *pThis, const char* szService, ProtoServiceFuncParam, LPARAM);
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// interface declaration
+
+enum ProtoMenuItemType
+{
+ PROTO_MENU_REQ_AUTH,
+ PROTO_MENU_GRANT_AUTH,
+ PROTO_MENU_REVOKE_AUTH,
+ PROTO_MENU_LOAD_HISTORY
+};
+
+struct MIR_APP_EXPORT PROTO_INTERFACE : public MZeroedObject
+{
+
+protected:
+ MWindowList m_hWindowList = 0; // list of all windows which belong to this protocol's instance
+
+public:
+ int m_iStatus; // current protocol status
+ int m_iDesiredStatus; // status to be set after logging in
+ int m_iXStatus; // extanded status
+ int m_iVersion; // version 2 or higher designate support of Unicode services
+ wchar_t* m_tszUserName; // human readable protocol's name
+ char* m_szModuleName; // internal protocol name, also its database module name
+ HANDLE m_hProtoIcon = 0; // icon to be displayed in the account manager
+ HNETLIBUSER m_hNetlibUser = 0; // network agent
+ HGENMENU m_hmiMainMenu = 0; // if protocol menus are displayed in the main menu, this is the root
+
+ PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName);
+ ~PROTO_INTERFACE();
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ __forceinline INT_PTR ProtoBroadcastAck(MCONTACT hContact, int type, int hResult, HANDLE hProcess, LPARAM lParam = 0) {
+ return ::ProtoBroadcastAck(m_szModuleName, hContact, type, hResult, hProcess, lParam); }
+ __forceinline void ProtoBroadcastAsync(MCONTACT hContact, int type, int hResult, HANDLE hProcess, LPARAM lParam = 0) {
+ return ::ProtoBroadcastAsync(m_szModuleName, hContact, type, hResult, hProcess, lParam); }
+
+ __forceinline INT_PTR delSetting(const char *name) { return db_unset(0, m_szModuleName, name); }
+ __forceinline INT_PTR delSetting(MCONTACT hContact, const char *name) { return db_unset(hContact, m_szModuleName, name); }
+
+ __forceinline bool getBool(const char *name, bool defaultValue = false) {
+ return db_get_b(0, m_szModuleName, name, defaultValue) != 0; }
+ __forceinline bool getBool(MCONTACT hContact, const char *name, bool defaultValue = false) {
+ return db_get_b(hContact, m_szModuleName, name, defaultValue) != 0; }
+
+ MBinBuffer getBlob(const char *pSetting);
+ MBinBuffer getBlob(MCONTACT hContact, const char *pSetting);
+
+ __forceinline bool isChatRoom(MCONTACT hContact) { return getBool(hContact, "ChatRoom", false); }
+
+ __forceinline int getByte(const char *name, uint8_t defaultValue = 0) {
+ return db_get_b(0, m_szModuleName, name, defaultValue); }
+ __forceinline int getByte(MCONTACT hContact, const char *name, uint8_t defaultValue = 0) {
+ return db_get_b(hContact, m_szModuleName, name, defaultValue); }
+
+ __forceinline int getWord(const char *name, uint16_t defaultValue = 0) {
+ return db_get_w(0, m_szModuleName, name, defaultValue); }
+ __forceinline int getWord(MCONTACT hContact, const char *name, uint16_t defaultValue = 0) {
+ return db_get_w(hContact, m_szModuleName, name, defaultValue); }
+
+ __forceinline uint32_t getDword(const char *name, uint32_t defaultValue = 0) {
+ return db_get_dw(0, m_szModuleName, name, defaultValue); }
+ __forceinline uint32_t getDword(MCONTACT hContact, const char *name, uint32_t defaultValue = 0) {
+ return db_get_dw(hContact, m_szModuleName, name, defaultValue); }
+
+ __forceinline INT_PTR getString(const char *name, DBVARIANT *result) {
+ return db_get_s(0, m_szModuleName, name, result, DBVT_ASCIIZ); }
+ __forceinline INT_PTR getString(MCONTACT hContact, const char *name, DBVARIANT *result) {
+ return db_get_s(hContact, m_szModuleName, name, result, DBVT_ASCIIZ); }
+
+ __forceinline INT_PTR getUString(const char *name, DBVARIANT *result) {
+ return db_get_s(0, m_szModuleName, name, result, DBVT_UTF8); }
+ __forceinline INT_PTR getUString(MCONTACT hContact, const char *name, DBVARIANT *result) {
+ return db_get_s(hContact, m_szModuleName, name, result, DBVT_UTF8); }
+
+ __forceinline INT_PTR getWString(const char *name, DBVARIANT *result) {
+ return db_get_s(0, m_szModuleName, name, result, DBVT_WCHAR); }
+ __forceinline INT_PTR getWString(MCONTACT hContact, const char *name, DBVARIANT *result) {
+ return db_get_s(hContact, m_szModuleName, name, result, DBVT_WCHAR); }
+
+ __forceinline char* getStringA(const char *name, const char *szValue = nullptr) {
+ return db_get_sa(0, m_szModuleName, name, szValue); }
+ __forceinline char* getStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) {
+ return db_get_sa(hContact, m_szModuleName, name, szValue); }
+
+ __forceinline char* getUStringA(const char *name, const char *szValue = nullptr) {
+ return db_get_utfa(0, m_szModuleName, name, szValue); }
+ __forceinline char* getUStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) {
+ return db_get_utfa(hContact, m_szModuleName, name, szValue); }
+
+ __forceinline wchar_t* getWStringA(const char *name, const wchar_t *szValue = nullptr) {
+ return db_get_wsa(0, m_szModuleName, name, szValue); }
+ __forceinline wchar_t* getWStringA(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr) {
+ return db_get_wsa(hContact, m_szModuleName, name, szValue); }
+
+ __forceinline CMStringA getMStringA(const char *name, const char *szValue = nullptr) {
+ return db_get_sm(0, m_szModuleName, name, szValue); }
+ __forceinline CMStringA getMStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) {
+ return db_get_sm(hContact, m_szModuleName, name, szValue); }
+
+ __forceinline CMStringW getMStringW(const char *name, const wchar_t *szValue = nullptr) {
+ return db_get_wsm(0, m_szModuleName, name, szValue); }
+ __forceinline CMStringW getMStringW(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr) {
+ return db_get_wsm(hContact, m_szModuleName, name, szValue); }
+
+ __forceinline void setByte(const char *name, uint8_t value) { db_set_b(0, m_szModuleName, name, value); }
+ __forceinline void setByte(MCONTACT hContact, const char *name, uint8_t value) { db_set_b(hContact, m_szModuleName, name, value); }
+
+ __forceinline void setWord(const char *name, uint16_t value) { db_set_w(0, m_szModuleName, name, value); }
+ __forceinline void setWord(MCONTACT hContact, const char *name, uint16_t value) { db_set_w(hContact, m_szModuleName, name, value); }
+
+ __forceinline void setDword(const char *name, uint32_t value) { db_set_dw(0, m_szModuleName, name, value); }
+ __forceinline void setDword(MCONTACT hContact, const char *name, uint32_t value) { db_set_dw(hContact, m_szModuleName, name, value); }
+
+ __forceinline void setString(const char *name, const char* value) { db_set_s(0, m_szModuleName, name, value); }
+ __forceinline void setString(MCONTACT hContact, const char *name, const char* value) { db_set_s(hContact, m_szModuleName, name, value); }
+
+ __forceinline void setUString(const char *name, const char* value) { db_set_utf(0, m_szModuleName, name, value); }
+ __forceinline void setUString(MCONTACT hContact, const char *name, const char* value) { db_set_utf(hContact, m_szModuleName, name, value); }
+
+ __forceinline void setWString(const char *name, const wchar_t* value) { db_set_ws(0, m_szModuleName, name, value); }
+ __forceinline void setWString(MCONTACT hContact, const char *name, const wchar_t* value) { db_set_ws(hContact, m_szModuleName, name, value); }
+
+ __forceinline Contacts AccContacts() const { return Contacts(m_szModuleName); }
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Service functions
+
+ void debugLogA(const char *szFormat, ...);
+ void debugLogW(const wchar_t *wszFormat, ...);
+
+ void setAllContactStatuses(int iStatus, bool bSkipChats = true);
+
+ void ReportSelfAvatarChanged();
+
+ void WindowSubscribe(HWND hwnd);
+ void WindowUnsubscribe(HWND hwnd);
+
+ HGENMENU GetMenuItem(ProtoMenuItemType);
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // Virtual functions
+
+ virtual MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr);
+ virtual MCONTACT AddToListByEvent(int flags, int iContact, MEVENT hDbEvent);
+
+ virtual int Authorize(MEVENT hDbEvent);
+ virtual int AuthDeny(MEVENT hDbEvent, const wchar_t *szReason);
+ virtual int AuthRecv(MCONTACT hContact, PROTORECVEVENT *);
+ virtual int AuthRequest(MCONTACT hContact, const wchar_t *szMessage);
+
+ virtual HANDLE FileAllow(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szPath);
+ virtual int FileCancel(MCONTACT hContact, HANDLE hTransfer);
+ virtual int FileDeny(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szReason);
+ virtual int FileResume(HANDLE hTransfer, int action, const wchar_t *szFilename);
+
+ virtual INT_PTR GetCaps(int type, MCONTACT hContact = 0);
+ virtual int GetInfo(MCONTACT hContact, int infoType);
+
+ virtual HANDLE SearchBasic(const wchar_t *id);
+ virtual HANDLE SearchByEmail(const wchar_t *email);
+ virtual HANDLE SearchByName(const wchar_t *nick, const wchar_t *firstName, const wchar_t *lastName);
+ virtual HWND SearchAdvanced(HWND owner);
+ virtual HWND CreateExtendedSearchUI(HWND owner);
+
+ virtual int RecvContacts(MCONTACT hContact, PROTORECVEVENT *);
+ virtual int RecvFile(MCONTACT hContact, PROTORECVFILE *);
+ virtual MEVENT RecvMsg(MCONTACT hContact, PROTORECVEVENT *);
+
+ virtual int SendContacts(MCONTACT hContact, int flags, int nContacts, MCONTACT *hContactsList);
+ virtual HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles);
+ virtual int SendMsg(MCONTACT hContact, int flags, const char *msg);
+
+ virtual int SetApparentMode(MCONTACT hContact, int mode);
+ virtual int SetStatus(int iNewStatus);
+
+ virtual HANDLE GetAwayMsg(MCONTACT hContact);
+ virtual int RecvAwayMsg(MCONTACT hContact, int mode, PROTORECVEVENT *evt);
+ virtual int SetAwayMsg(int iStatus, const wchar_t *msg);
+
+ virtual int UserIsTyping(MCONTACT hContact, int type);
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // events
+
+ // builds the account's protocol menu
+ virtual void OnBuildProtoMenu(void);
+
+ // called when an account's contact is added
+ virtual void OnContactAdded(MCONTACT);
+
+ // called when an account's contact is deleted
+ virtual void OnContactDeleted(MCONTACT);
+
+ // called when an event is altered in database
+ virtual void OnEventEdited(MCONTACT, MEVENT);
+
+ // called when an account gets physically removed from the database
+ virtual void OnErase();
+
+ // the analog of ME_SYSTEM_MODULESLOADED for an account
+ virtual void OnModulesLoaded(void);
+
+ // same for ME_SYSTEM_SHUTDOWN
+ virtual void OnShutdown(void);
+
+ // same for ME_SYSTEM_OKTOEXIT
+ virtual bool IsReadyToExit(void);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Basic class for all protocols written in C++
+
+template<class T> struct PROTO : public PROTO_INTERFACE
+{
+ typedef PROTO_INTERFACE CSuper;
+
+ __forceinline PROTO(const char *szProto, const wchar_t *tszUserName) :
+ PROTO_INTERFACE(szProto, tszUserName)
+ {}
+
+ __forceinline HANDLE CreateProtoEvent(const char *name) {
+ return ::ProtoCreateHookableEvent(this, name); }
+
+ typedef int(MIR_CDECL T::*MyEventFunc)(WPARAM, LPARAM);
+ __forceinline void HookProtoEvent(const char *name, MyEventFunc pFunc) {
+ ::ProtoHookEvent(this, name, (ProtoEventFunc)pFunc); }
+
+ typedef void(MIR_CDECL T::*MyThreadFunc)(void*);
+ __forceinline void ForkThread(MyThreadFunc pFunc, void *param = nullptr) {
+ ::ProtoForkThread(this, (ProtoThreadFunc)pFunc, param); }
+ HANDLE __forceinline ForkThreadEx(MyThreadFunc pFunc, void *param, UINT *pThreadId) {
+ return ::ProtoForkThreadEx(this, (ProtoThreadFunc)pFunc, param, pThreadId); }
+
+ typedef INT_PTR(MIR_CDECL T::*MyServiceFunc)(WPARAM, LPARAM);
+ __forceinline void CreateProtoService(const char *name, MyServiceFunc pFunc) {
+ ::ProtoCreateService(this, name, (ProtoServiceFunc)pFunc); }
+
+ typedef INT_PTR(MIR_CDECL T::*MyServiceFuncParam)(WPARAM, LPARAM, LPARAM);
+ __forceinline void CreateProtoServiceParam(const char *name, MyServiceFuncParam pFunc, LPARAM param) {
+ ::ProtoCreateServiceParam(this, name, (ProtoServiceFuncParam)pFunc, param); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(PROTO_INTERFACE *) Proto_GetInstance(const char *szModule);
+MIR_APP_DLL(PROTO_INTERFACE *) Proto_GetInstance(MCONTACT hContact);
+
+#endif // M_PROTOINT_H__
diff --git a/include/m_protosvc.h b/include/m_protosvc.h index 3aaea79a1e..e6f1df3ab2 100644 --- a/include/m_protosvc.h +++ b/include/m_protosvc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-09 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_regexp.h b/include/m_regexp.h index f59b2985dc..51bb1cd1ea 100644 --- a/include/m_regexp.h +++ b/include/m_regexp.h @@ -1,106 +1,106 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef MIM_REGEXP_H -#define MIM_REGEXP_H - -#include <pcre.h> - -class MRegexp16 -{ - pcre16 *m_pattern = nullptr; - pcre16_extra *m_extra = nullptr; - bool m_bIsValid = false; - const wchar_t *m_prevText; - int m_nMatches = 0, m_start = 0; - int m_offsets[100]; - - MRegexp16(const MRegexp16&); // never applied - -public: - MRegexp16() - {} - - MRegexp16(const wchar_t *pwszPattern) - { - compile(pwszPattern); - } - - void compile(const wchar_t *pwszPattern) - { - m_bIsValid = false; - - int erroffset; - const char *err; - m_pattern = ::pcre16_compile(pwszPattern, 0, &err, &erroffset, nullptr); - if (m_pattern == nullptr) - return; - - m_extra = ::pcre16_study(m_pattern, 0, &err); - if (m_extra == nullptr) - return; - - m_bIsValid = true; - } - - ~MRegexp16() - { - if (m_pattern) ::pcre16_free(m_pattern); - if (m_extra) ::pcre16_free(m_extra); - } - - __forceinline bool isValid() const { return m_bIsValid; } - __forceinline int numMatches() const { return m_nMatches; } - - __forceinline int getPos() const { return m_offsets[0]; } - __forceinline int getLength() const { return m_offsets[1] - m_offsets[0]; } - - CMStringW getMatch() - { - return CMStringW(m_prevText + getPos(), getLength()); - } - - CMStringW getGroup(int i) - { - if (i >= m_nMatches) - return L""; - - return CMStringW(m_prevText + m_offsets[i*2], m_offsets[i*2 + 1] - m_offsets[i*2]); - } - - int match(const wchar_t *pwszText) - { - return m_nMatches = ::pcre16_exec(m_pattern, m_extra, m_prevText = pwszText, (int)mir_wstrlen(pwszText), 0, 0, m_offsets, _countof(m_offsets)); - } - - int nextMatch(const wchar_t *pwszText) - { - m_nMatches = ::pcre16_exec(m_pattern, m_extra, m_prevText = pwszText, (int)mir_wstrlen(pwszText), m_start, 0, m_offsets, _countof(m_offsets)); - m_start = (m_nMatches >= 0) ? m_offsets[1] : 0; - - return m_nMatches; - } -}; - -#endif // MIM_REGEXP_H +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef MIM_REGEXP_H
+#define MIM_REGEXP_H
+
+#include <pcre.h>
+
+class MRegexp16
+{
+ pcre16 *m_pattern = nullptr;
+ pcre16_extra *m_extra = nullptr;
+ bool m_bIsValid = false;
+ const wchar_t *m_prevText;
+ int m_nMatches = 0, m_start = 0;
+ int m_offsets[100];
+
+ MRegexp16(const MRegexp16&); // never applied
+
+public:
+ MRegexp16()
+ {}
+
+ MRegexp16(const wchar_t *pwszPattern)
+ {
+ compile(pwszPattern);
+ }
+
+ void compile(const wchar_t *pwszPattern)
+ {
+ m_bIsValid = false;
+
+ int erroffset;
+ const char *err;
+ m_pattern = ::pcre16_compile(pwszPattern, 0, &err, &erroffset, nullptr);
+ if (m_pattern == nullptr)
+ return;
+
+ m_extra = ::pcre16_study(m_pattern, 0, &err);
+ if (m_extra == nullptr)
+ return;
+
+ m_bIsValid = true;
+ }
+
+ ~MRegexp16()
+ {
+ if (m_pattern) ::pcre16_free(m_pattern);
+ if (m_extra) ::pcre16_free(m_extra);
+ }
+
+ __forceinline bool isValid() const { return m_bIsValid; }
+ __forceinline int numMatches() const { return m_nMatches; }
+
+ __forceinline int getPos() const { return m_offsets[0]; }
+ __forceinline int getLength() const { return m_offsets[1] - m_offsets[0]; }
+
+ CMStringW getMatch()
+ {
+ return CMStringW(m_prevText + getPos(), getLength());
+ }
+
+ CMStringW getGroup(int i)
+ {
+ if (i >= m_nMatches)
+ return L"";
+
+ return CMStringW(m_prevText + m_offsets[i*2], m_offsets[i*2 + 1] - m_offsets[i*2]);
+ }
+
+ int match(const wchar_t *pwszText)
+ {
+ return m_nMatches = ::pcre16_exec(m_pattern, m_extra, m_prevText = pwszText, (int)mir_wstrlen(pwszText), 0, 0, m_offsets, _countof(m_offsets));
+ }
+
+ int nextMatch(const wchar_t *pwszText)
+ {
+ m_nMatches = ::pcre16_exec(m_pattern, m_extra, m_prevText = pwszText, (int)mir_wstrlen(pwszText), m_start, 0, m_offsets, _countof(m_offsets));
+ m_start = (m_nMatches >= 0) ? m_offsets[1] : 0;
+
+ return m_nMatches;
+ }
+};
+
+#endif // MIM_REGEXP_H
diff --git a/include/m_skin.h b/include/m_skin.h index 30168b167d..7d7c66ea40 100644 --- a/include/m_skin.h +++ b/include/m_skin.h @@ -1,6 +1,6 @@ // Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+// Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
// Copyright (c) 2000-08 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/include/m_srmm_int.h b/include/m_srmm_int.h index bb7d29f411..c9ac8c14b7 100644 --- a/include/m_srmm_int.h +++ b/include/m_srmm_int.h @@ -1,301 +1,301 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_SRMM_INT_H__ -#define M_SRMM_INT_H__ 1 - -#include <m_gui.h> - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// toolbar button internal representation - -#define MIN_CBUTTONID 4000 -#define MAX_CBUTTONID 5000 - -#define BBSF_IMBUTTON (1<<0) -#define BBSF_CHATBUTTON (1<<1) -#define BBSF_CANBEHIDDEN (1<<2) -#define BBSF_NTBSWAPED (1<<3) -#define BBSF_NTBDESTRUCT (1<<4) - -struct CustomButtonData : public MZeroedObject -{ - ~CustomButtonData() - {} - - int m_dwPosition; // default order pos of button, counted from window edge (left or right) - - int m_dwButtonID; // id of button used while button creation and to store button info in DB - ptrA m_pszModuleName; // module name without spaces and underline symbols (e.g. "tabsrmm") - - int m_dwButtonCID; // button's control id - int m_dwArrowCID; // only use with BBBF_ISARROWBUTTON flag - - ptrW m_pwszText; // button's text - ptrW m_pwszTooltip; // button's tooltip - - int m_iButtonWidth; // must be 22 for regular button and 33 for button with arrow - HANDLE m_hIcon; // Handle to icolib registred icon - - bool m_bIMButton, m_bChatButton; - bool m_bCanBeHidden, m_bCantBeHidden, m_bHidden, m_bSeparator, m_bDisabled, m_bPushButton; - bool m_bRSided; - uint8_t m_opFlags; - HPLUGIN m_pPlugin; - uint32_t m_dwOrigPosition; - struct THotkeyItem *m_hotkey; - - struct { - bool bit1 : 1, bit2 : 1, bit3 : 1, bit4 : 1; - } m_dwOrigFlags; -}; - -// gets the required button or NULL, if i is out of boundaries -EXTERN_C MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i); - -// retrieves total number of toolbar buttons -EXTERN_C MIR_APP_DLL(int) Srmm_GetButtonCount(void); - -// emulates a click on a toolbar button -EXTERN_C MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndFrom, BOOL code); - -// these messages are sent to the message windows if toolbar buttons are changed -#define WM_CBD_FIRST (WM_USER+0x600) - -// wParam = 0 (ignored) -// lParam = (CustomButtonData*)pointer to button or null if any button can be changed -#define WM_CBD_UPDATED (WM_CBD_FIRST+1) - -// wParam = button id -// lParam = (CustomButtonData*)pointer to button -#define WM_CBD_REMOVED (WM_CBD_FIRST+2) - -// wParam = 0 (ignored) -// lParam = 0 (ignored) -#define WM_CBD_LOADICONS (WM_CBD_FIRST+3) - -// wParam = 0 (ignored) -// lParam = 0 (ignored) -#define WM_CBD_RECREATE (WM_CBD_FIRST+4) - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log window container - -class CMsgDialog; - -class MIR_APP_EXPORT CSrmmLogWindow -{ - CSrmmLogWindow(const CSrmmLogWindow &) = delete; - CSrmmLogWindow &operator=(const CSrmmLogWindow &) = delete; - -protected: - CMsgDialog &m_pDlg; - - CSrmmLogWindow(CMsgDialog &pDlg) : - m_pDlg(pDlg) - {} - -public: - virtual ~CSrmmLogWindow() {} - - virtual void Attach() = 0; - virtual void Detach() = 0; - - virtual bool AtBottom() = 0; - virtual void Clear() = 0; - virtual int GetType() = 0; - virtual HWND GetHwnd() = 0; - virtual wchar_t* GetSelection() = 0; - virtual void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) = 0; - virtual void LogEvents(struct LOGINFO *, bool) = 0; - virtual void Resize() = 0; - virtual void ScrollToBottom() = 0; - virtual void UpdateOptions() {}; - - virtual INT_PTR Notify(WPARAM, LPARAM) { return 0; } -}; - -typedef CSrmmLogWindow *(MIR_CDECL *pfnSrmmLogCreator)(CMsgDialog &pDlg); - -EXTERN_C MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder); -EXTERN_C MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE); - -///////////////////////////////////////////////////////////////////////////////////////// -// Standard built-in RTF logger class - -class MIR_APP_EXPORT CRtfLogWindow : public CSrmmLogWindow -{ -protected: - CCtrlRichEdit &m_rtf; - -public: - CRtfLogWindow(CMsgDialog &pDlg); - ~CRtfLogWindow() override; - - virtual INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam); - - //////////////////////////////////////////////////////////////////////////////////////// - void Attach() override; - void Detach() override; - - bool AtBottom() override; - void Clear() override; - HWND GetHwnd() override; - wchar_t* GetSelection() override; - int GetType() override; - void Resize() override; - void ScrollToBottom() override; - - INT_PTR Notify(WPARAM, LPARAM) override; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Basic SRMM window dialog - -#include <chat_resource.h> - -// message procedures' stubs -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - -class MIR_APP_EXPORT CSrmmBaseDialog : public CDlgBase -{ - friend class CRtfLogWindow; - - CSrmmBaseDialog(const CSrmmBaseDialog &) = delete; - CSrmmBaseDialog &operator=(const CSrmmBaseDialog &) = delete; - -protected: - CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, struct SESSION_INFO *si = nullptr); - - bool OnInitDialog() override; - void OnDestroy() override; - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override; - - bool AllowTyping() const; - int NotifyEvent(int code); - #ifdef _WINDOWS - bool ProcessFileDrop(HDROP hDrop, MCONTACT hContact); - bool PasteFilesAsURL(HDROP hDrop); - #endif - bool ProcessHotkeys(int key, bool bShift, bool bCtrl, bool bAlt); - void RefreshButtonStatus(void); - void RunUserMenu(HWND hwndOwner, struct USERINFO *ui, const POINT &pt); - -protected: - CSrmmLogWindow *m_pLog = nullptr; - CCtrlRichEdit m_message; - SESSION_INFO *m_si; - COLORREF m_clrInputBG, m_clrInputFG; - - // user typing support; - uint32_t m_nLastTyping = 0; - uint8_t m_bShowTyping = 0; - int m_nTypeSecs = 0, m_nTypeMode = 0; - const USERINFO* m_pUserTyping = nullptr; - - CCtrlListBox m_nickList; - CCtrlButton m_btnColor, m_btnBkColor, m_btnOk; - CCtrlButton m_btnBold, m_btnItalic, m_btnUnderline; - CCtrlButton m_btnHistory, m_btnChannelMgr, m_btnNickList, m_btnFilter; - - void onClick_BIU(CCtrlButton *); - void onClick_Color(CCtrlButton *); - void onClick_BkColor(CCtrlButton *); - - void onClick_ChanMgr(CCtrlButton *); - void onClick_History(CCtrlButton *); - - void onDblClick_List(CCtrlListBox *); - -public: - MCONTACT m_hContact; - int m_iLogFilterFlags; - bool m_bFilterEnabled, m_bNicklistEnabled; - bool m_bFGSet, m_bBGSet; - bool m_bInMenu; - COLORREF m_iFG, m_iBG; - CTimer timerFlash, timerType; - - void ClearLog(); - void RedrawLog(); - void ShowColorChooser(int iCtrlId); - - virtual void AddLog(); - virtual void CloseTab() {} - virtual bool IsActive() const PURE; - virtual void LoadSettings() PURE; - virtual void SetStatusText(const wchar_t *, HICON) {} - virtual void ShowFilterMenu() {} - virtual void UpdateNickList() {} - virtual void UpdateOptions(); - virtual void UpdateStatusBar() {} - virtual void UpdateTitle() PURE; - - virtual LRESULT WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam); - virtual LRESULT WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam); - - __forceinline bool isChat() const { return m_si != nullptr; } - __forceinline SESSION_INFO *getChat() const { return m_si; } - __forceinline CSrmmLogWindow *log() const { return m_pLog; } - - __forceinline void setTyping(int nSecs, const USERINFO* pUser = nullptr) { - m_pUserTyping = pUser; - m_nTypeSecs = nSecs; - } - - __inline void* operator new(size_t size) { return calloc(1, size); } - __inline void operator delete(void *p) { free(p); } -}; - -#ifndef SRMM_OWN_STRUCTURES -class CMsgDialog : public CSrmmBaseDialog {}; -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// receives LOGSTREAMDATA* as the first parameter - -#ifdef _WINDOWS -EXTERN_C MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, uint8_t *pbBuff, LONG cb, LONG *pcb); -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// sends a message to all SRMM windows - -EXTERN_C MIR_APP_DLL(void) Srmm_Broadcast(UINT, WPARAM, LPARAM); - -///////////////////////////////////////////////////////////////////////////////////////// -// creates plugin-specific hot key for sending messages - -EXTERN_C MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription); - -///////////////////////////////////////////////////////////////////////////////////////// -// finds a SRMM window using hContact - -EXTERN_C MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact); -EXTERN_C MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact); - -#endif // M_MESSAGE_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_SRMM_INT_H__
+#define M_SRMM_INT_H__ 1
+
+#include <m_gui.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// toolbar button internal representation
+
+#define MIN_CBUTTONID 4000
+#define MAX_CBUTTONID 5000
+
+#define BBSF_IMBUTTON (1<<0)
+#define BBSF_CHATBUTTON (1<<1)
+#define BBSF_CANBEHIDDEN (1<<2)
+#define BBSF_NTBSWAPED (1<<3)
+#define BBSF_NTBDESTRUCT (1<<4)
+
+struct CustomButtonData : public MZeroedObject
+{
+ ~CustomButtonData()
+ {}
+
+ int m_dwPosition; // default order pos of button, counted from window edge (left or right)
+
+ int m_dwButtonID; // id of button used while button creation and to store button info in DB
+ ptrA m_pszModuleName; // module name without spaces and underline symbols (e.g. "tabsrmm")
+
+ int m_dwButtonCID; // button's control id
+ int m_dwArrowCID; // only use with BBBF_ISARROWBUTTON flag
+
+ ptrW m_pwszText; // button's text
+ ptrW m_pwszTooltip; // button's tooltip
+
+ int m_iButtonWidth; // must be 22 for regular button and 33 for button with arrow
+ HANDLE m_hIcon; // Handle to icolib registred icon
+
+ bool m_bIMButton, m_bChatButton;
+ bool m_bCanBeHidden, m_bCantBeHidden, m_bHidden, m_bSeparator, m_bDisabled, m_bPushButton;
+ bool m_bRSided;
+ uint8_t m_opFlags;
+ HPLUGIN m_pPlugin;
+ uint32_t m_dwOrigPosition;
+ struct THotkeyItem *m_hotkey;
+
+ struct {
+ bool bit1 : 1, bit2 : 1, bit3 : 1, bit4 : 1;
+ } m_dwOrigFlags;
+};
+
+// gets the required button or NULL, if i is out of boundaries
+EXTERN_C MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i);
+
+// retrieves total number of toolbar buttons
+EXTERN_C MIR_APP_DLL(int) Srmm_GetButtonCount(void);
+
+// emulates a click on a toolbar button
+EXTERN_C MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndFrom, BOOL code);
+
+// these messages are sent to the message windows if toolbar buttons are changed
+#define WM_CBD_FIRST (WM_USER+0x600)
+
+// wParam = 0 (ignored)
+// lParam = (CustomButtonData*)pointer to button or null if any button can be changed
+#define WM_CBD_UPDATED (WM_CBD_FIRST+1)
+
+// wParam = button id
+// lParam = (CustomButtonData*)pointer to button
+#define WM_CBD_REMOVED (WM_CBD_FIRST+2)
+
+// wParam = 0 (ignored)
+// lParam = 0 (ignored)
+#define WM_CBD_LOADICONS (WM_CBD_FIRST+3)
+
+// wParam = 0 (ignored)
+// lParam = 0 (ignored)
+#define WM_CBD_RECREATE (WM_CBD_FIRST+4)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SRMM log window container
+
+class CMsgDialog;
+
+class MIR_APP_EXPORT CSrmmLogWindow
+{
+ CSrmmLogWindow(const CSrmmLogWindow &) = delete;
+ CSrmmLogWindow &operator=(const CSrmmLogWindow &) = delete;
+
+protected:
+ CMsgDialog &m_pDlg;
+
+ CSrmmLogWindow(CMsgDialog &pDlg) :
+ m_pDlg(pDlg)
+ {}
+
+public:
+ virtual ~CSrmmLogWindow() {}
+
+ virtual void Attach() = 0;
+ virtual void Detach() = 0;
+
+ virtual bool AtBottom() = 0;
+ virtual void Clear() = 0;
+ virtual int GetType() = 0;
+ virtual HWND GetHwnd() = 0;
+ virtual wchar_t* GetSelection() = 0;
+ virtual void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) = 0;
+ virtual void LogEvents(struct LOGINFO *, bool) = 0;
+ virtual void Resize() = 0;
+ virtual void ScrollToBottom() = 0;
+ virtual void UpdateOptions() {};
+
+ virtual INT_PTR Notify(WPARAM, LPARAM) { return 0; }
+};
+
+typedef CSrmmLogWindow *(MIR_CDECL *pfnSrmmLogCreator)(CMsgDialog &pDlg);
+
+EXTERN_C MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder);
+EXTERN_C MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Standard built-in RTF logger class
+
+class MIR_APP_EXPORT CRtfLogWindow : public CSrmmLogWindow
+{
+protected:
+ CCtrlRichEdit &m_rtf;
+
+public:
+ CRtfLogWindow(CMsgDialog &pDlg);
+ ~CRtfLogWindow() override;
+
+ virtual INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ void Attach() override;
+ void Detach() override;
+
+ bool AtBottom() override;
+ void Clear() override;
+ HWND GetHwnd() override;
+ wchar_t* GetSelection() override;
+ int GetType() override;
+ void Resize() override;
+ void ScrollToBottom() override;
+
+ INT_PTR Notify(WPARAM, LPARAM) override;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Basic SRMM window dialog
+
+#include <chat_resource.h>
+
+// message procedures' stubs
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+class MIR_APP_EXPORT CSrmmBaseDialog : public CDlgBase
+{
+ friend class CRtfLogWindow;
+
+ CSrmmBaseDialog(const CSrmmBaseDialog &) = delete;
+ CSrmmBaseDialog &operator=(const CSrmmBaseDialog &) = delete;
+
+protected:
+ CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, struct SESSION_INFO *si = nullptr);
+
+ bool OnInitDialog() override;
+ void OnDestroy() override;
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override;
+
+ bool AllowTyping() const;
+ int NotifyEvent(int code);
+ #ifdef _WINDOWS
+ bool ProcessFileDrop(HDROP hDrop, MCONTACT hContact);
+ bool PasteFilesAsURL(HDROP hDrop);
+ #endif
+ bool ProcessHotkeys(int key, bool bShift, bool bCtrl, bool bAlt);
+ void RefreshButtonStatus(void);
+ void RunUserMenu(HWND hwndOwner, struct USERINFO *ui, const POINT &pt);
+
+protected:
+ CSrmmLogWindow *m_pLog = nullptr;
+ CCtrlRichEdit m_message;
+ SESSION_INFO *m_si;
+ COLORREF m_clrInputBG, m_clrInputFG;
+
+ // user typing support;
+ uint32_t m_nLastTyping = 0;
+ uint8_t m_bShowTyping = 0;
+ int m_nTypeSecs = 0, m_nTypeMode = 0;
+ const USERINFO* m_pUserTyping = nullptr;
+
+ CCtrlListBox m_nickList;
+ CCtrlButton m_btnColor, m_btnBkColor, m_btnOk;
+ CCtrlButton m_btnBold, m_btnItalic, m_btnUnderline;
+ CCtrlButton m_btnHistory, m_btnChannelMgr, m_btnNickList, m_btnFilter;
+
+ void onClick_BIU(CCtrlButton *);
+ void onClick_Color(CCtrlButton *);
+ void onClick_BkColor(CCtrlButton *);
+
+ void onClick_ChanMgr(CCtrlButton *);
+ void onClick_History(CCtrlButton *);
+
+ void onDblClick_List(CCtrlListBox *);
+
+public:
+ MCONTACT m_hContact;
+ int m_iLogFilterFlags;
+ bool m_bFilterEnabled, m_bNicklistEnabled;
+ bool m_bFGSet, m_bBGSet;
+ bool m_bInMenu;
+ COLORREF m_iFG, m_iBG;
+ CTimer timerFlash, timerType;
+
+ void ClearLog();
+ void RedrawLog();
+ void ShowColorChooser(int iCtrlId);
+
+ virtual void AddLog();
+ virtual void CloseTab() {}
+ virtual bool IsActive() const PURE;
+ virtual void LoadSettings() PURE;
+ virtual void SetStatusText(const wchar_t *, HICON) {}
+ virtual void ShowFilterMenu() {}
+ virtual void UpdateNickList() {}
+ virtual void UpdateOptions();
+ virtual void UpdateStatusBar() {}
+ virtual void UpdateTitle() PURE;
+
+ virtual LRESULT WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam);
+ virtual LRESULT WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam);
+
+ __forceinline bool isChat() const { return m_si != nullptr; }
+ __forceinline SESSION_INFO *getChat() const { return m_si; }
+ __forceinline CSrmmLogWindow *log() const { return m_pLog; }
+
+ __forceinline void setTyping(int nSecs, const USERINFO* pUser = nullptr) {
+ m_pUserTyping = pUser;
+ m_nTypeSecs = nSecs;
+ }
+
+ __inline void* operator new(size_t size) { return calloc(1, size); }
+ __inline void operator delete(void *p) { free(p); }
+};
+
+#ifndef SRMM_OWN_STRUCTURES
+class CMsgDialog : public CSrmmBaseDialog {};
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// receives LOGSTREAMDATA* as the first parameter
+
+#ifdef _WINDOWS
+EXTERN_C MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, uint8_t *pbBuff, LONG cb, LONG *pcb);
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sends a message to all SRMM windows
+
+EXTERN_C MIR_APP_DLL(void) Srmm_Broadcast(UINT, WPARAM, LPARAM);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// creates plugin-specific hot key for sending messages
+
+EXTERN_C MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// finds a SRMM window using hContact
+
+EXTERN_C MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact);
+EXTERN_C MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact);
+
+#endif // M_MESSAGE_H__
diff --git a/include/m_string.h b/include/m_string.h index 677385899a..8ddd07fb3d 100644 --- a/include/m_string.h +++ b/include/m_string.h @@ -1,1129 +1,1129 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#ifndef M_STRING_H__ -#define M_STRING_H__ - -#include <stdio.h> -#include <string.h> - -#ifdef _MSC_VER - #include <mbstring.h> -#else - #include <ctype.h> -#endif // _WINDOWS -#include <wchar.h> - -#include <m_core.h> - -#ifdef __MINGW32__ -#include <limits.h> - -__inline size_t strnlen(const char *string, size_t maxlen) -{ - const char *end = (const char *)memchr ((const void *)string, '\0', maxlen); - return end ? (size_t) (end - string) : maxlen; -} -__inline size_t wcsnlen(const wchar_t *string, size_t maxlen) -{ - const wchar_t *end = wmemchr (string, L'\0', maxlen); - return end ? (size_t) (end - string) : maxlen; -} - -/* FIXME: This may be wrong assumption about Langpack_GetDefaultCodePage */ -#define Langpack_GetDefaultCodePage() CP_THREAD_ACP -/* FIXME: This is unsafe */ -#define memcpy_s(dest,size,src,count) memcpy(dest,src,count) -/* FIXME: This is quite silly implementation of _mbsstr */ -#define _mbsstr(str,search) strstr((const char *)str,(const char *)search) -#define __max(x,y) (((x)<(y))?(y):(x)) -#endif /* __MINGW32__ */ - -///////////////////////////////////////////////////////////////////////////////////////// - -struct CMStringData; - -MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize); -MIR_CORE_DLL(void) mirstr_free(CMStringData *pData); -MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize); -MIR_CORE_DLL(CMStringData*) mirstr_getNil(); - -MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis); -MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis); -MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis); - -///////////////////////////////////////////////////////////////////////////////////////// - -enum CMStringDataFormat { FORMAT }; - -struct CMStringData -{ - int nDataLength; // Length of currently used data in XCHARs (not including terminating null) - int nAllocLength; // Length of allocated data in XCHARs (not including terminating null) - long nRefs; // Reference count: negative == locked - - __forceinline void* data() { return (this + 1); } - __forceinline void AddRef() { InterlockedIncrement(&nRefs); } - __forceinline bool IsLocked() const { return nRefs < 0; } - __forceinline bool IsShared() const { return nRefs > 1; } - - __forceinline void Lock() { mirstr_lock(this); } - __forceinline void Release() { mirstr_release(this); } - __forceinline void Unlock() { mirstr_unlock(this); } -}; - -template< typename BaseType = char > -class ChTraitsBase -{ -public: - typedef char XCHAR; - typedef LPSTR PXSTR; - typedef LPCSTR PCXSTR; - typedef wchar_t YCHAR; - typedef LPWSTR PYSTR; - typedef LPCWSTR PCYSTR; -}; - -template<> -class ChTraitsBase< wchar_t > -{ -public: - typedef wchar_t XCHAR; - typedef LPWSTR PXSTR; - typedef LPCWSTR PCXSTR; - typedef char YCHAR; - typedef LPSTR PYSTR; - typedef LPCSTR PCYSTR; -}; - -template< typename BaseType > -class CMSimpleStringT -{ -public: - typedef typename ChTraitsBase< BaseType >::XCHAR XCHAR; - typedef typename ChTraitsBase< BaseType >::PXSTR PXSTR; - typedef typename ChTraitsBase< BaseType >::PCXSTR PCXSTR; - typedef typename ChTraitsBase< BaseType >::YCHAR YCHAR; - typedef typename ChTraitsBase< BaseType >::PYSTR PYSTR; - typedef typename ChTraitsBase< BaseType >::PCYSTR PCYSTR; - -public: - explicit CMSimpleStringT(); - - CMSimpleStringT(const CMSimpleStringT& strSrc); - CMSimpleStringT(PCXSTR pszSrc); - CMSimpleStringT(const XCHAR* pchSrc, int nLength); - ~CMSimpleStringT(); - - CMSimpleStringT& operator=(const CMSimpleStringT& strSrc); - - __forceinline CMSimpleStringT& operator=(PCXSTR pszSrc) - { SetString(pszSrc); - return *this; - } - - __forceinline CMSimpleStringT& operator+=(const CMSimpleStringT& strSrc) - { Append(strSrc); - return *this; - } - - __forceinline CMSimpleStringT& operator+=(PCXSTR pszSrc) - { Append(pszSrc); - return *this; - } - - __forceinline CMSimpleStringT& operator+=(char ch) - { AppendChar(XCHAR(ch)); - return *this; - } - - __forceinline CMSimpleStringT& operator+=(unsigned char ch) - { AppendChar(XCHAR(ch)); - return *this; - } - - __forceinline CMSimpleStringT& operator+=(wchar_t ch) - { AppendChar(XCHAR(ch)); - return *this; - } - - __forceinline XCHAR operator[](int iChar) const - { return m_pszData[iChar]; - } - - __forceinline operator PCXSTR() const - { return m_pszData; - } - - __forceinline PCXSTR c_str() const - { return m_pszData; - } - - __forceinline int GetAllocLength() const - { return GetData()->nAllocLength; - } - - __forceinline XCHAR GetAt(int iChar) const - { return m_pszData[iChar]; - } - - __forceinline PXSTR GetBuffer(int nMinBufferLength) - { return PrepareWrite(nMinBufferLength); - } - - __forceinline int GetLength() const - { return GetData()->nDataLength; - } - - __forceinline PCXSTR GetString() const - { return m_pszData; - } - - __forceinline PCXSTR GetTail() const - { return m_pszData + GetData()->nDataLength; - } - - __forceinline bool IsEmpty() const - { return GetLength() == 0; - } - - __forceinline void Preallocate(int nLength) - { PrepareWrite(nLength); - } - - __forceinline void ReleaseBufferSetLength(int nNewLength) - { SetLength(nNewLength); - } - - void Append(PCXSTR pszSrc); - void Append(PCXSTR pszSrc, int nLength); - void AppendChar(XCHAR ch); - void Append(const CMSimpleStringT& strSrc); - - void Empty(); - void FreeExtra(); - - PXSTR GetBuffer(); - PXSTR GetBufferSetLength(int nLength); - - PXSTR LockBuffer(); - void UnlockBuffer(); - - void ReleaseBuffer(int nNewLength = -1); - - void Truncate(int nNewLength); - void SetAt(int iChar, XCHAR ch); - void SetString(PCXSTR pszSrc); - void SetString(PCXSTR pszSrc, int nLength); - -public: - template <typename Basetype> - friend CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType> &str1, const CMSimpleStringT<BaseType> &str2); - - template <typename Basetype> - friend CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType> &str1, PCXSTR psz2); - - template <typename Basetype> - friend CMSimpleStringT<BaseType> operator+(PCXSTR psz1, const CMSimpleStringT<BaseType> &str2); - - static void MIR_SYSCALL CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars); - static void MIR_SYSCALL CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars); - static void MIR_SYSCALL CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars); - static void MIR_SYSCALL CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars); - static int MIR_SYSCALL StringLength(const char* psz); - static int MIR_SYSCALL StringLength(const wchar_t* psz); - static int MIR_SYSCALL StringLengthN(const char* psz, size_t sizeInXChar); - static int MIR_SYSCALL StringLengthN(const wchar_t* psz, size_t sizeInXChar); - static void MIR_SYSCALL Concatenate(CMSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2); - - // Implementation -private: - __forceinline CMStringData* GetData() const - { return (reinterpret_cast<CMStringData *>(m_pszData)-1); - } - - void Attach(CMStringData* pData); - void Fork(int nLength); - PXSTR PrepareWrite(int nLength); - void PrepareWrite2(int nLength); - void Reallocate(int nLength); - void SetLength(int nLength); - static CMStringData* MIR_SYSCALL CloneData(CMStringData* pData); - -private: - PXSTR m_pszData; -}; - - -template< typename _CharType = char > -class ChTraitsCRT : public ChTraitsBase < _CharType > -{ -public: - static char* MIR_SYSCALL CharNext(const char *p) - { - #ifdef _MSC_VER - return reinterpret_cast<char*>(_mbsinc(reinterpret_cast<const unsigned char*>(p))); - #else - return reinterpret_cast<char*>(p+1); - #endif - } - - static int MIR_SYSCALL IsDigit(char ch) - { - #ifdef _MSC_VER - return _ismbcdigit(ch); - #else - return isdigit(ch); - #endif - } - - static int MIR_SYSCALL IsSpace(char ch) - { - #ifdef _MSC_VER - return _ismbcspace(ch); - #else - return isspace(ch); - #endif - } - - static int MIR_SYSCALL StringCompare(const char *pszA, const char *pszB) - { - #ifdef _MSC_VER - return _mbscmp(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB)); - #else - return strcmp(pszA, pszB); - #endif - } - - static int MIR_SYSCALL StringCompareIgnore(const char *pszA, const char *pszB) - { - #ifdef _MSC_VER - return _mbsicmp(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB)); - #else - return strcasecmp(pszA, pszB); - #endif - } - - static int MIR_SYSCALL StringCollate(const char *pszA, const char *pszB) - { - #ifdef _MSC_VER - return _mbscoll(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB)); - #else - return strcoll(pszA, pszB); - #endif - } - - static int MIR_SYSCALL StringCollateIgnore(const char *pszA, const char *pszB) - { - #ifdef _MSC_VER - return _mbsicoll(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB)); - #else - return strcoll(pszA, pszB); - #endif - } - - static const char* MIR_SYSCALL StringFindString(const char *pszBlock, const char *pszMatch) - { - #ifdef _MSC_VER - return reinterpret_cast<const char*>(_mbsstr(reinterpret_cast<const unsigned char*>(pszBlock), - reinterpret_cast<const unsigned char*>(pszMatch))); - #else - return strstr(pszBlock, pszMatch); - #endif - } - - static char* MIR_SYSCALL StringFindString(char *pszBlock, const char *pszMatch) - { - return const_cast<char*>(StringFindString(const_cast<const char*>(pszBlock), pszMatch)); - } - - static const char* MIR_SYSCALL StringFindChar(const char *pszBlock, char chMatch) - { - #ifdef _MSC_VER - return reinterpret_cast<const char*>(_mbschr(reinterpret_cast<const unsigned char*>(pszBlock), (unsigned char)chMatch)); - #else - return strchr(pszBlock, chMatch); - #endif - } - - static const char* MIR_SYSCALL StringFindCharRev(const char *psz, char ch) - { - #ifdef _MSC_VER - return reinterpret_cast<const char*>(_mbsrchr(reinterpret_cast<const unsigned char*>(psz), (unsigned char)ch)); - #else - return strrchr(psz, ch); - #endif - } - - static const char* MIR_SYSCALL StringScanSet(const char *pszBlock, const char *pszMatch) - { - #ifdef _MSC_VER - return reinterpret_cast<const char*>(_mbspbrk(reinterpret_cast<const unsigned char*>(pszBlock), - reinterpret_cast<const unsigned char*>(pszMatch))); - #else - return strpbrk(pszBlock, pszMatch); - #endif - } - - static int MIR_SYSCALL StringSpanIncluding(const char *pszBlock, const char *pszSet) - { - #ifdef _MSC_VER - return (int)_mbsspn(reinterpret_cast<const unsigned char*>(pszBlock), reinterpret_cast<const unsigned char*>(pszSet)); - #else - return (int)strspn(pszBlock, pszSet); - #endif - } - - static int MIR_SYSCALL StringSpanExcluding(const char *pszBlock, const char *pszSet) - { - #ifdef _MSC_VER - return (int)_mbscspn(reinterpret_cast<const unsigned char*>(pszBlock), reinterpret_cast<const unsigned char*>(pszSet)); - #else - return (int)strcspn(pszBlock, pszSet); - #endif - } - - static char* MIR_SYSCALL StringUppercase(char *psz) - { - #ifdef _MSC_VER - CharUpperBuffA(psz, (uint32_t)strlen(psz)); - #else - strupr(psz); - #endif - return psz; - } - - static char* MIR_SYSCALL StringLowercase(char *psz) - { - #ifdef _MSC_VER - CharLowerBuffA(psz, (uint32_t)strlen(psz)); - #else - strlwr(psz); - #endif - return psz; - } - - static char* MIR_SYSCALL StringUppercase(char *psz, size_t size) - { - #ifdef _MSC_VER - CharUpperBuffA(psz, (uint32_t)size); - #else - - #endif - return psz; - } - - static char* MIR_SYSCALL StringLowercase(char *psz, size_t size) - { - #ifdef _MSC_VER - CharLowerBuffA(psz, (uint32_t)size); - #endif - return psz; - } - - static char* MIR_SYSCALL StringReverse(char *psz) - { - #ifdef _MSC_VER - return reinterpret_cast<LPSTR>(_mbsrev(reinterpret_cast<unsigned char*>(psz))); - #else - return strrev(psz); - #endif - } - - static int MIR_SYSCALL GetFormattedLength(_Printf_format_string_ const char *pszFormat, va_list args) - { - #ifdef _MSC_VER - return _vscprintf(pszFormat, args); - #else - return 0; // !!!!!!!!!! - #endif - } - - static int MIR_SYSCALL Format(char *pszBuffer, size_t nlength, _Printf_format_string_ const char *pszFormat, va_list args) - { - #ifdef _MSC_VER - return vsprintf_s(pszBuffer, nlength, pszFormat, args); - #else - return 0; // !!!!!!!!!! - #endif - } - - static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc) - { - // Returns required buffer length in XCHARs - return int(strlen(pszSrc)); - } - - static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc, int nLength) - { - (void)pszSrc; - // Returns required buffer length in XCHARs - return nLength; - } - - static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSource) - { - // Returns required buffer length in XCHARs - #ifdef _MSC_VER - return ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 0, pszSource, -1, NULL, 0, NULL, NULL) - 1; - #else - return 0; - #endif - } - - static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSource, int nLength) - { - // Returns required buffer length in XCHARs - #ifdef _MSC_VER - return ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 0, pszSource, nLength, NULL, 0, NULL, NULL); - #else - return 0; - #endif - } - - static void MIR_SYSCALL ConvertToBaseType(char*pszDest, int nDestLength, const char *pszSrc, int nSrcLength = -1) - { - if (nSrcLength == -1) { nSrcLength = 1 + GetBaseTypeLength(pszSrc); } - // nLen is in XCHARs - memcpy_s(pszDest, nDestLength*sizeof(char), pszSrc, nSrcLength*sizeof(char)); - } - - static void MIR_SYSCALL ConvertToBaseType(char*pszDest, int nDestLength, const wchar_t *pszSrc, int nSrcLength = -1) - { - // nLen is in XCHARs - #ifdef _MSC_VER - ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL); - #endif - } - - static void ConvertToOem(_CharType* pstrString) - { - #ifdef _MSC_VER - bool fSuccess = ::CharToOemA(pstrString, pstrString); - #endif // _MSC_VER - } - - static void ConvertToAnsi(_CharType* pstrString) - { - #ifdef _MSC_VER - bool fSuccess = ::OemToCharA(pstrString, pstrString); - #endif - } - - static void ConvertToOem(_CharType* pstrString, size_t size) - { - #ifdef _MSC_VER - uint32_t dwSize = static_cast<uint32_t>(size); - ::CharToOemBuffA(pstrString, pstrString, dwSize); - #endif - } - - static void ConvertToAnsi(_CharType* pstrString, size_t size) - { - #ifdef _MSC_VER - uint32_t dwSize = static_cast<uint32_t>(size); - ::OemToCharBuffA(pstrString, pstrString, dwSize); - #endif - } - - static void MIR_SYSCALL FloodCharacters(char ch, int nLength, char* pch) - { - // nLength is in XCHARs - memset(pch, ch, nLength); - } - - static int MIR_SYSCALL SafeStringLen(const char *psz) - { - // returns length in bytes - return (psz != NULL) ? int(strlen(psz)) : 0; - } - - static int MIR_SYSCALL SafeStringLen(const wchar_t *psz) - { - // returns length in wchar_ts - return (psz != NULL) ? int(wcslen(psz)) : 0; - } - - static int MIR_SYSCALL GetCharLen(const wchar_t *pch) - { - // returns char length - return 1; - } - - static int MIR_SYSCALL GetCharLen(const char *pch) - { - // returns char length - #ifdef _MSC_VER - return int(_mbclen(reinterpret_cast<const unsigned char*>(pch))); - #else - return mblen(pch, strlen(pch)); - #endif - } - - static uint32_t MIR_SYSCALL GetEnvironmentVariable(const char *pszVar, char*pszBuffer, uint32_t dwSize) - { - #ifdef _MSC_VER - return ::GetEnvironmentVariableA(pszVar, pszBuffer, dwSize); - #else - return 0; // !!!!!!!!!! - #endif - } - - static char* MirCopy(const char *pstrString, size_t size) - { - return mir_strndup(pstrString, size); - } -}; - -// specialization for wchar_t -template<> -class ChTraitsCRT< wchar_t > : public ChTraitsBase< wchar_t > -{ - static uint32_t MIR_SYSCALL _GetEnvironmentVariableW(const wchar_t *pszName, wchar_t *pszBuffer, uint32_t nSize) - { - #ifdef _MSC_VER - return ::GetEnvironmentVariableW(pszName, pszBuffer, nSize); - #else - return 0; // !!!!!!!!!! - #endif - } - -public: - static wchar_t* MIR_SYSCALL CharNext(const wchar_t *psz) - { - return const_cast<wchar_t*>(psz+1); - } - - static int MIR_SYSCALL IsDigit(wchar_t ch) - { - #ifdef _MSC_VER - return iswdigit(static_cast<unsigned short>(ch)); - #else - return 0; - #endif - } - - static int MIR_SYSCALL IsSpace(wchar_t ch) - { - #ifdef _MSC_VER - return iswspace(static_cast<unsigned short>(ch)); - #else - return 0; - #endif - } - - static int MIR_SYSCALL StringCompare(const wchar_t *pszA, const wchar_t *pszB) - { - return wcscmp(pszA, pszB); - } - - static int MIR_SYSCALL StringCompareIgnore(const wchar_t *pszA, const wchar_t *pszB) - { - #ifdef _MSC_VER - return _wcsicmp(pszA, pszB); - #else - return 0; - #endif - } - - static int MIR_SYSCALL StringCollate(const wchar_t *pszA, const wchar_t *pszB) - { - return wcscoll(pszA, pszB); - } - - static int MIR_SYSCALL StringCollateIgnore(const wchar_t *pszA, const wchar_t *pszB) - { - #ifdef _MSC_VER - return _wcsicoll(pszA, pszB); - #else - return 0; - #endif - } - - static const wchar_t* MIR_SYSCALL StringFindString(const wchar_t *pszBlock, const wchar_t *pszMatch) - { - return wcsstr(pszBlock, pszMatch); - } - - static wchar_t* MIR_SYSCALL StringFindString(wchar_t *pszBlock, const wchar_t *pszMatch) - { - return const_cast<wchar_t*>(wcsstr(pszBlock, pszMatch)); - } - - static const wchar_t* MIR_SYSCALL StringFindChar(const wchar_t *pszBlock, wchar_t chMatch) - { - return wcschr(pszBlock, chMatch); - } - - static const wchar_t* MIR_SYSCALL StringFindCharRev(const wchar_t *psz, wchar_t ch) - { - return wcsrchr(psz, ch); - } - - static const wchar_t* MIR_SYSCALL StringScanSet(const wchar_t *pszBlock, const wchar_t *pszMatch) - { - return wcspbrk(pszBlock, pszMatch); - } - - static int MIR_SYSCALL StringSpanIncluding(const wchar_t *pszBlock, const wchar_t *pszSet) - { - return (int)wcsspn(pszBlock, pszSet); - } - - static int MIR_SYSCALL StringSpanExcluding(const wchar_t *pszBlock, const wchar_t *pszSet) - { - return (int)wcscspn(pszBlock, pszSet); - } - - static wchar_t* MIR_SYSCALL StringUppercase(wchar_t *psz) - { - #ifdef _MSC_VER - CharUpperBuffW(psz, (uint32_t)wcslen(psz)); - #endif - return psz; - } - - static wchar_t* MIR_SYSCALL StringLowercase(wchar_t *psz) - { - #ifdef _MSC_VER - CharLowerBuffW(psz, (uint32_t)wcslen(psz)); - #endif - return psz; - } - - static wchar_t* MIR_SYSCALL StringUppercase(wchar_t *psz, size_t len) - { - #ifdef _MSC_VER - CharUpperBuffW(psz, (uint32_t)len); - #endif - return psz; - } - - static wchar_t* MIR_SYSCALL StringLowercase(wchar_t *psz, size_t len) - { - #ifdef _MSC_VER - CharLowerBuffW(psz, (uint32_t)len); - #endif - return psz; - } - - static wchar_t* MIR_SYSCALL StringReverse(wchar_t *psz) - { - #ifdef _MSC_VER - return _wcsrev(psz); - #else - return psz; - #endif - } - - static int MIR_SYSCALL GetFormattedLength(_Printf_format_string_ const wchar_t *pszFormat, va_list args) - { - #ifdef _MSC_VER - return _vscwprintf(pszFormat, args); - #else - return 0; - #endif - } - - static int MIR_SYSCALL Format(wchar_t *pszBuffer, _Printf_format_string_ const wchar_t *pszFormat, va_list args) - { - #pragma warning(push) - #pragma warning(disable : 4996) - - #ifdef _MSC_VER - return vswprintf(pszBuffer, pszFormat, args); - #else - return 0; - #endif - #pragma warning(pop) - } - - static int MIR_SYSCALL Format(wchar_t *pszBuffer, size_t nLength, _Printf_format_string_ const wchar_t *pszFormat, va_list args) - { - #pragma warning(push) - #pragma warning(disable : 4996) - - #ifdef _MSC_VER - return _vsnwprintf(pszBuffer, nLength, pszFormat, args); - #else - return 0; - #endif - - #pragma warning(pop) - } - - static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc) - { - // Returns required buffer size in wchar_ts - #ifdef _MSC_VER - return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, nullptr, 0)-1; - #else - return 0; - #endif - } - - static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc, int nLength) - { - // Returns required buffer size in wchar_ts - #ifdef _MSC_VER - return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nLength, nullptr, 0); - #else - return 0; - #endif - } - - static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSrc) - { - // Returns required buffer size in wchar_ts - return (int)wcslen(pszSrc); - } - - static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSrc, int nLength) - { - (void)pszSrc; - // Returns required buffer size in wchar_ts - return nLength; - } - - static void MIR_SYSCALL ConvertToBaseType(wchar_t *pszDest, int nDestLength, const char *pszSrc, int nSrcLength = -1) - { - // nLen is in wchar_ts - #ifdef _MSC_VER - ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength); - #endif - } - - static void MIR_SYSCALL ConvertToBaseType(wchar_t *pszDest, int nDestLength, const wchar_t *pszSrc, int nSrcLength = -1) - { - if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); } - // nLen is in wchar_ts - #if _MSC_VER >= 1400 - wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength); - #else - wmemcpy(pszDest, pszSrc, nDestLength); - #endif - } - - static void MIR_SYSCALL FloodCharacters(wchar_t ch, int nLength, wchar_t *psz) - { - // nLength is in XCHARs - for (int i = 0; i < nLength; i++) - { - psz[i] = ch; - } - } - - static int MIR_SYSCALL SafeStringLen(const char *psz) - { - // returns length in bytes - return (psz != nullptr) ? (int)strlen(psz) : 0; - } - - static int MIR_SYSCALL SafeStringLen(const wchar_t *psz) - { - // returns length in wchar_ts - return (psz != nullptr) ? (int)wcslen(psz) : 0; - } - - static int MIR_SYSCALL GetCharLen(const wchar_t* pch) - { - (void)pch; - // returns char length - return 1; - } - - static int MIR_SYSCALL GetCharLen(const char* pch) - { - // returns char length - #ifdef _MSC_VER - return (int)(_mbclen(reinterpret_cast< const unsigned char* >(pch))); - #else - return mblen(pch, strlen(pch)); - #endif - } - - static uint32_t MIR_SYSCALL GetEnvironmentVariable(const wchar_t *pszVar, wchar_t *pszBuffer, uint32_t dwSize) - { - return _GetEnvironmentVariableW(pszVar, pszBuffer, dwSize); - } - - static void MIR_SYSCALL ConvertToOem(wchar_t* /*psz*/) - { - } - - static void MIR_SYSCALL ConvertToAnsi(wchar_t* /*psz*/) - { - } - - static void MIR_SYSCALL ConvertToOem(wchar_t* /*psz*/, size_t) - { - } - - static void MIR_SYSCALL ConvertToAnsi(wchar_t* /*psz*/, size_t) - { - } - - static wchar_t* MirCopy(const wchar_t *pstrString, size_t size) - { - return mir_wstrndup(pstrString, size); - } -}; - -template< typename BaseType, class StringTraits > -class MIR_CORE_EXPORT CMStringT : public CMSimpleStringT< BaseType > -{ -public: - typedef CMSimpleStringT< BaseType> CThisSimpleString; - typedef typename CThisSimpleString::XCHAR XCHAR; - typedef typename CThisSimpleString::PXSTR PXSTR; - typedef typename CThisSimpleString::PCXSTR PCXSTR; - typedef typename CThisSimpleString::YCHAR YCHAR; - typedef typename CThisSimpleString::PYSTR PYSTR; - typedef typename CThisSimpleString::PCYSTR PCYSTR; - -public: - CMStringT(); - - // Copy constructor - CMStringT(const CMStringT& strSrc); - - CMStringT(const XCHAR* pszSrc); - CMStringT(CMStringDataFormat, _Printf_format_string_ const XCHAR* pszFormat, ...); - - CMStringT(const YCHAR* pszSrc); - CMStringT(const unsigned char* pszSrc); - - CMStringT(char ch, int nLength = 1); - CMStringT(wchar_t ch, int nLength = 1); - - CMStringT(const XCHAR* pch, int nLength); - CMStringT(const YCHAR* pch, int nLength); - - // Destructor - ~CMStringT(); - - // Assignment operators - CMStringT& operator=(const CMStringT& strSrc); - CMStringT& operator=(PCXSTR pszSrc); - CMStringT& operator=(PCYSTR pszSrc); - CMStringT& operator=(const unsigned char* pszSrc); - CMStringT& operator=(char ch); - CMStringT& operator=(wchar_t ch); - - CMStringT& operator+=(const CMStringT& str); - CMStringT& operator+=(const CThisSimpleString& str); - CMStringT& operator+=(PCXSTR pszSrc); - CMStringT& operator+=(PCYSTR pszSrc); - CMStringT& operator+=(char ch); - CMStringT& operator+=(unsigned char ch); - CMStringT& operator+=(wchar_t ch); - - // Comparison - - int Compare(PCXSTR psz) const; - int CompareNoCase(PCXSTR psz) const; - int Collate(PCXSTR psz) const; - int CollateNoCase(PCXSTR psz) const; - - // Advanced manipulation - - // Delete 'nCount' characters, starting at index 'iIndex' - int Delete(int iIndex, int nCount = 1); - - // Insert character 'ch' before index 'iIndex' - int Insert(int iIndex, XCHAR ch); - - // Insert string 'psz' before index 'iIndex' - int Insert(int iIndex, PCXSTR psz); - - // Replace all occurrences of character 'chOld' with character 'chNew' - int Replace(XCHAR chOld, XCHAR chNew); - - // Replace all occurrences of string 'pszOld' with string 'pszNew' - int Replace(PCXSTR pszOld, PCXSTR pszNew); - - // Remove all occurrences of character 'chRemove' - int Remove(XCHAR chRemove); - - CMStringT Tokenize(PCXSTR pszTokens, int& iStart) const; - - // find routines - - // Find the first occurrence of character 'ch', starting at index 'iStart' - int Find(XCHAR ch, int iStart = 0) const; - - // look for a specific sub-string - - // Find the first occurrence of string 'pszSub', starting at index 'iStart' - int Find(PCXSTR pszSub, int iStart = 0) const; - - // Find the first occurrence of any of the characters in string 'pszCharSet' - int FindOneOf(PCXSTR pszCharSet) const; - - // Find the last occurrence of character 'ch' - int ReverseFind(XCHAR ch) const; - - // manipulation - - // Convert the string to uppercase - CMStringT& MakeUpper(); - - // Convert the string to lowercase - CMStringT& MakeLower(); - - // Reverse the string - CMStringT& MakeReverse(); - - // trimming - - // Remove all trailing whitespace - CMStringT& TrimRight(); - - // Remove all leading whitespace - CMStringT& TrimLeft(); - - // Remove all leading and trailing whitespace - CMStringT& Trim(); - - // Remove all leading and trailing occurrences of character 'chTarget' - CMStringT& Trim(XCHAR chTarget); - - // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets' - CMStringT& Trim(PCXSTR pszTargets); - - // trimming anything (either side) - - // Remove all trailing occurrences of character 'chTarget' - CMStringT& TrimRight(XCHAR chTarget); - - // Remove all trailing occurrences of any of the characters in string 'pszTargets' - CMStringT& TrimRight(PCXSTR pszTargets); - - // Remove all leading occurrences of character 'chTarget' - CMStringT& TrimLeft(XCHAR chTarget); - - // Remove all leading occurrences of any of the characters in string 'pszTargets' - CMStringT& TrimLeft(PCXSTR pszTargets); - - // Convert the string to the OEM character set - void AnsiToOem(); - - // Convert the string to the ANSI character set - void OemToAnsi(); - - // Very simple sub-string extraction - - // Return the substring starting at index 'iFirst' - CMStringT Mid(int iFirst) const; - - // Return the substring starting at index 'iFirst', with length 'nCount' - CMStringT Mid(int iFirst, int nCount) const; - - // Return the substring consisting of the rightmost 'nCount' characters - CMStringT Right(int nCount) const; - - // Return the substring consisting of the leftmost 'nCount' characters - CMStringT Left(int nCount) const; - - // Return the substring consisting of the leftmost characters in the set 'pszCharSet' - CMStringT SpanIncluding(PCXSTR pszCharSet) const; - - // Return the substring consisting of the leftmost characters not in the set 'pszCharSet' - CMStringT SpanExcluding(PCXSTR pszCharSet) const; - - // Format data using format string 'pszFormat' - PCXSTR Format(PCXSTR _Printf_format_string_ pszFormat, ...); - PCXSTR FormatV(PCXSTR _Printf_format_string_ pszFormat, va_list args); - - // Append formatted data using format string 'pszFormat' - PCXSTR AppendFormat(PCXSTR _Printf_format_string_ pszFormat, ...); - void AppendFormatV(PCXSTR _Printf_format_string_ pszFormat, va_list args); - - // return a copy of string to be freed by mir_free() - PXSTR Detach() const; - - // Set the string to the value of environment variable 'pszVar' - BOOL GetEnvironmentVariable(PCXSTR pszVar); - - friend bool __forceinline operator==(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) == 0; } - friend bool __forceinline operator==(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) == 0; } - friend bool __forceinline operator==(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) == 0; } - friend bool __forceinline operator==(const CMStringT& str1, PCYSTR psz2) { return str1 == CMStringT(psz2); } - friend bool __forceinline operator==(PCYSTR psz1, const CMStringT& str2) { return CMStringT(psz1) == str2; } - - friend bool __forceinline operator!=(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) != 0; } - friend bool __forceinline operator!=(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) != 0; } - friend bool __forceinline operator!=(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) != 0; } - friend bool __forceinline operator!=(const CMStringT& str1, PCYSTR psz2) { return str1 != CMStringT(psz2); } - friend bool __forceinline operator!=(PCYSTR psz1, const CMStringT& str2) { return CMStringT(psz1) != str2; } - - friend bool __forceinline operator<(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) < 0; } - friend bool __forceinline operator<(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) < 0; } - friend bool __forceinline operator<(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) > 0; } - - friend bool __forceinline operator>(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) > 0; } - friend bool __forceinline operator>(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) > 0; } - friend bool __forceinline operator>(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) < 0; } - - friend bool __forceinline operator<=(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) <= 0; } - friend bool __forceinline operator<=(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) <= 0; } - friend bool __forceinline operator<=(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) >= 0; } - - friend bool __forceinline operator>=(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) >= 0; } - friend bool __forceinline operator>=(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) >= 0; } - friend bool __forceinline operator>=(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) <= 0; } - - friend bool __forceinline operator==(XCHAR ch1, const CMStringT& str2) { return (str2.GetLength() == 1) && (str2[0] == ch1); } - friend bool __forceinline operator==(const CMStringT& str1, XCHAR ch2) { return (str1.GetLength() == 1) && (str1[0] == ch2); } - - friend bool __forceinline operator!=(XCHAR ch1, const CMStringT& str2) { return (str2.GetLength() != 1) || (str2[0] != ch1); } - friend bool __forceinline operator!=(const CMStringT& str1, XCHAR ch2) { return (str1.GetLength() != 1) || (str1[0] != ch2); } -}; - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, const CMStringT<BaseType, StringTraits>& str2); - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, typename CMStringT<BaseType, StringTraits>::PCXSTR psz2); - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(typename CMStringT<BaseType, StringTraits>::PCXSTR psz1, const CMStringT<BaseType, StringTraits>& str2); - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, wchar_t ch2); - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, char ch2); - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator + (wchar_t ch1, const CMStringT<BaseType, StringTraits>& str2); - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(char ch1, const CMStringT<BaseType, StringTraits>& str2); - -typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW; -typedef CMStringT< char, ChTraitsCRT< char > > CMStringA; - -#endif // M_STRING_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+#ifndef M_STRING_H__
+#define M_STRING_H__
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+ #include <mbstring.h>
+#else
+ #include <ctype.h>
+#endif // _WINDOWS
+#include <wchar.h>
+
+#include <m_core.h>
+
+#ifdef __MINGW32__
+#include <limits.h>
+
+__inline size_t strnlen(const char *string, size_t maxlen)
+{
+ const char *end = (const char *)memchr ((const void *)string, '\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+__inline size_t wcsnlen(const wchar_t *string, size_t maxlen)
+{
+ const wchar_t *end = wmemchr (string, L'\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+
+/* FIXME: This may be wrong assumption about Langpack_GetDefaultCodePage */
+#define Langpack_GetDefaultCodePage() CP_THREAD_ACP
+/* FIXME: This is unsafe */
+#define memcpy_s(dest,size,src,count) memcpy(dest,src,count)
+/* FIXME: This is quite silly implementation of _mbsstr */
+#define _mbsstr(str,search) strstr((const char *)str,(const char *)search)
+#define __max(x,y) (((x)<(y))?(y):(x))
+#endif /* __MINGW32__ */
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct CMStringData;
+
+MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize);
+MIR_CORE_DLL(void) mirstr_free(CMStringData *pData);
+MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize);
+MIR_CORE_DLL(CMStringData*) mirstr_getNil();
+
+MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis);
+MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis);
+MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+enum CMStringDataFormat { FORMAT };
+
+struct CMStringData
+{
+ int nDataLength; // Length of currently used data in XCHARs (not including terminating null)
+ int nAllocLength; // Length of allocated data in XCHARs (not including terminating null)
+ long nRefs; // Reference count: negative == locked
+
+ __forceinline void* data() { return (this + 1); }
+ __forceinline void AddRef() { InterlockedIncrement(&nRefs); }
+ __forceinline bool IsLocked() const { return nRefs < 0; }
+ __forceinline bool IsShared() const { return nRefs > 1; }
+
+ __forceinline void Lock() { mirstr_lock(this); }
+ __forceinline void Release() { mirstr_release(this); }
+ __forceinline void Unlock() { mirstr_unlock(this); }
+};
+
+template< typename BaseType = char >
+class ChTraitsBase
+{
+public:
+ typedef char XCHAR;
+ typedef LPSTR PXSTR;
+ typedef LPCSTR PCXSTR;
+ typedef wchar_t YCHAR;
+ typedef LPWSTR PYSTR;
+ typedef LPCWSTR PCYSTR;
+};
+
+template<>
+class ChTraitsBase< wchar_t >
+{
+public:
+ typedef wchar_t XCHAR;
+ typedef LPWSTR PXSTR;
+ typedef LPCWSTR PCXSTR;
+ typedef char YCHAR;
+ typedef LPSTR PYSTR;
+ typedef LPCSTR PCYSTR;
+};
+
+template< typename BaseType >
+class CMSimpleStringT
+{
+public:
+ typedef typename ChTraitsBase< BaseType >::XCHAR XCHAR;
+ typedef typename ChTraitsBase< BaseType >::PXSTR PXSTR;
+ typedef typename ChTraitsBase< BaseType >::PCXSTR PCXSTR;
+ typedef typename ChTraitsBase< BaseType >::YCHAR YCHAR;
+ typedef typename ChTraitsBase< BaseType >::PYSTR PYSTR;
+ typedef typename ChTraitsBase< BaseType >::PCYSTR PCYSTR;
+
+public:
+ explicit CMSimpleStringT();
+
+ CMSimpleStringT(const CMSimpleStringT& strSrc);
+ CMSimpleStringT(PCXSTR pszSrc);
+ CMSimpleStringT(const XCHAR* pchSrc, int nLength);
+ ~CMSimpleStringT();
+
+ CMSimpleStringT& operator=(const CMSimpleStringT& strSrc);
+
+ __forceinline CMSimpleStringT& operator=(PCXSTR pszSrc)
+ { SetString(pszSrc);
+ return *this;
+ }
+
+ __forceinline CMSimpleStringT& operator+=(const CMSimpleStringT& strSrc)
+ { Append(strSrc);
+ return *this;
+ }
+
+ __forceinline CMSimpleStringT& operator+=(PCXSTR pszSrc)
+ { Append(pszSrc);
+ return *this;
+ }
+
+ __forceinline CMSimpleStringT& operator+=(char ch)
+ { AppendChar(XCHAR(ch));
+ return *this;
+ }
+
+ __forceinline CMSimpleStringT& operator+=(unsigned char ch)
+ { AppendChar(XCHAR(ch));
+ return *this;
+ }
+
+ __forceinline CMSimpleStringT& operator+=(wchar_t ch)
+ { AppendChar(XCHAR(ch));
+ return *this;
+ }
+
+ __forceinline XCHAR operator[](int iChar) const
+ { return m_pszData[iChar];
+ }
+
+ __forceinline operator PCXSTR() const
+ { return m_pszData;
+ }
+
+ __forceinline PCXSTR c_str() const
+ { return m_pszData;
+ }
+
+ __forceinline int GetAllocLength() const
+ { return GetData()->nAllocLength;
+ }
+
+ __forceinline XCHAR GetAt(int iChar) const
+ { return m_pszData[iChar];
+ }
+
+ __forceinline PXSTR GetBuffer(int nMinBufferLength)
+ { return PrepareWrite(nMinBufferLength);
+ }
+
+ __forceinline int GetLength() const
+ { return GetData()->nDataLength;
+ }
+
+ __forceinline PCXSTR GetString() const
+ { return m_pszData;
+ }
+
+ __forceinline PCXSTR GetTail() const
+ { return m_pszData + GetData()->nDataLength;
+ }
+
+ __forceinline bool IsEmpty() const
+ { return GetLength() == 0;
+ }
+
+ __forceinline void Preallocate(int nLength)
+ { PrepareWrite(nLength);
+ }
+
+ __forceinline void ReleaseBufferSetLength(int nNewLength)
+ { SetLength(nNewLength);
+ }
+
+ void Append(PCXSTR pszSrc);
+ void Append(PCXSTR pszSrc, int nLength);
+ void AppendChar(XCHAR ch);
+ void Append(const CMSimpleStringT& strSrc);
+
+ void Empty();
+ void FreeExtra();
+
+ PXSTR GetBuffer();
+ PXSTR GetBufferSetLength(int nLength);
+
+ PXSTR LockBuffer();
+ void UnlockBuffer();
+
+ void ReleaseBuffer(int nNewLength = -1);
+
+ void Truncate(int nNewLength);
+ void SetAt(int iChar, XCHAR ch);
+ void SetString(PCXSTR pszSrc);
+ void SetString(PCXSTR pszSrc, int nLength);
+
+public:
+ template <typename Basetype>
+ friend CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType> &str1, const CMSimpleStringT<BaseType> &str2);
+
+ template <typename Basetype>
+ friend CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType> &str1, PCXSTR psz2);
+
+ template <typename Basetype>
+ friend CMSimpleStringT<BaseType> operator+(PCXSTR psz1, const CMSimpleStringT<BaseType> &str2);
+
+ static void MIR_SYSCALL CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars);
+ static void MIR_SYSCALL CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars);
+ static void MIR_SYSCALL CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars);
+ static void MIR_SYSCALL CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars);
+ static int MIR_SYSCALL StringLength(const char* psz);
+ static int MIR_SYSCALL StringLength(const wchar_t* psz);
+ static int MIR_SYSCALL StringLengthN(const char* psz, size_t sizeInXChar);
+ static int MIR_SYSCALL StringLengthN(const wchar_t* psz, size_t sizeInXChar);
+ static void MIR_SYSCALL Concatenate(CMSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2);
+
+ // Implementation
+private:
+ __forceinline CMStringData* GetData() const
+ { return (reinterpret_cast<CMStringData *>(m_pszData)-1);
+ }
+
+ void Attach(CMStringData* pData);
+ void Fork(int nLength);
+ PXSTR PrepareWrite(int nLength);
+ void PrepareWrite2(int nLength);
+ void Reallocate(int nLength);
+ void SetLength(int nLength);
+ static CMStringData* MIR_SYSCALL CloneData(CMStringData* pData);
+
+private:
+ PXSTR m_pszData;
+};
+
+
+template< typename _CharType = char >
+class ChTraitsCRT : public ChTraitsBase < _CharType >
+{
+public:
+ static char* MIR_SYSCALL CharNext(const char *p)
+ {
+ #ifdef _MSC_VER
+ return reinterpret_cast<char*>(_mbsinc(reinterpret_cast<const unsigned char*>(p)));
+ #else
+ return reinterpret_cast<char*>(p+1);
+ #endif
+ }
+
+ static int MIR_SYSCALL IsDigit(char ch)
+ {
+ #ifdef _MSC_VER
+ return _ismbcdigit(ch);
+ #else
+ return isdigit(ch);
+ #endif
+ }
+
+ static int MIR_SYSCALL IsSpace(char ch)
+ {
+ #ifdef _MSC_VER
+ return _ismbcspace(ch);
+ #else
+ return isspace(ch);
+ #endif
+ }
+
+ static int MIR_SYSCALL StringCompare(const char *pszA, const char *pszB)
+ {
+ #ifdef _MSC_VER
+ return _mbscmp(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
+ #else
+ return strcmp(pszA, pszB);
+ #endif
+ }
+
+ static int MIR_SYSCALL StringCompareIgnore(const char *pszA, const char *pszB)
+ {
+ #ifdef _MSC_VER
+ return _mbsicmp(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
+ #else
+ return strcasecmp(pszA, pszB);
+ #endif
+ }
+
+ static int MIR_SYSCALL StringCollate(const char *pszA, const char *pszB)
+ {
+ #ifdef _MSC_VER
+ return _mbscoll(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
+ #else
+ return strcoll(pszA, pszB);
+ #endif
+ }
+
+ static int MIR_SYSCALL StringCollateIgnore(const char *pszA, const char *pszB)
+ {
+ #ifdef _MSC_VER
+ return _mbsicoll(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
+ #else
+ return strcoll(pszA, pszB);
+ #endif
+ }
+
+ static const char* MIR_SYSCALL StringFindString(const char *pszBlock, const char *pszMatch)
+ {
+ #ifdef _MSC_VER
+ return reinterpret_cast<const char*>(_mbsstr(reinterpret_cast<const unsigned char*>(pszBlock),
+ reinterpret_cast<const unsigned char*>(pszMatch)));
+ #else
+ return strstr(pszBlock, pszMatch);
+ #endif
+ }
+
+ static char* MIR_SYSCALL StringFindString(char *pszBlock, const char *pszMatch)
+ {
+ return const_cast<char*>(StringFindString(const_cast<const char*>(pszBlock), pszMatch));
+ }
+
+ static const char* MIR_SYSCALL StringFindChar(const char *pszBlock, char chMatch)
+ {
+ #ifdef _MSC_VER
+ return reinterpret_cast<const char*>(_mbschr(reinterpret_cast<const unsigned char*>(pszBlock), (unsigned char)chMatch));
+ #else
+ return strchr(pszBlock, chMatch);
+ #endif
+ }
+
+ static const char* MIR_SYSCALL StringFindCharRev(const char *psz, char ch)
+ {
+ #ifdef _MSC_VER
+ return reinterpret_cast<const char*>(_mbsrchr(reinterpret_cast<const unsigned char*>(psz), (unsigned char)ch));
+ #else
+ return strrchr(psz, ch);
+ #endif
+ }
+
+ static const char* MIR_SYSCALL StringScanSet(const char *pszBlock, const char *pszMatch)
+ {
+ #ifdef _MSC_VER
+ return reinterpret_cast<const char*>(_mbspbrk(reinterpret_cast<const unsigned char*>(pszBlock),
+ reinterpret_cast<const unsigned char*>(pszMatch)));
+ #else
+ return strpbrk(pszBlock, pszMatch);
+ #endif
+ }
+
+ static int MIR_SYSCALL StringSpanIncluding(const char *pszBlock, const char *pszSet)
+ {
+ #ifdef _MSC_VER
+ return (int)_mbsspn(reinterpret_cast<const unsigned char*>(pszBlock), reinterpret_cast<const unsigned char*>(pszSet));
+ #else
+ return (int)strspn(pszBlock, pszSet);
+ #endif
+ }
+
+ static int MIR_SYSCALL StringSpanExcluding(const char *pszBlock, const char *pszSet)
+ {
+ #ifdef _MSC_VER
+ return (int)_mbscspn(reinterpret_cast<const unsigned char*>(pszBlock), reinterpret_cast<const unsigned char*>(pszSet));
+ #else
+ return (int)strcspn(pszBlock, pszSet);
+ #endif
+ }
+
+ static char* MIR_SYSCALL StringUppercase(char *psz)
+ {
+ #ifdef _MSC_VER
+ CharUpperBuffA(psz, (uint32_t)strlen(psz));
+ #else
+ strupr(psz);
+ #endif
+ return psz;
+ }
+
+ static char* MIR_SYSCALL StringLowercase(char *psz)
+ {
+ #ifdef _MSC_VER
+ CharLowerBuffA(psz, (uint32_t)strlen(psz));
+ #else
+ strlwr(psz);
+ #endif
+ return psz;
+ }
+
+ static char* MIR_SYSCALL StringUppercase(char *psz, size_t size)
+ {
+ #ifdef _MSC_VER
+ CharUpperBuffA(psz, (uint32_t)size);
+ #else
+
+ #endif
+ return psz;
+ }
+
+ static char* MIR_SYSCALL StringLowercase(char *psz, size_t size)
+ {
+ #ifdef _MSC_VER
+ CharLowerBuffA(psz, (uint32_t)size);
+ #endif
+ return psz;
+ }
+
+ static char* MIR_SYSCALL StringReverse(char *psz)
+ {
+ #ifdef _MSC_VER
+ return reinterpret_cast<LPSTR>(_mbsrev(reinterpret_cast<unsigned char*>(psz)));
+ #else
+ return strrev(psz);
+ #endif
+ }
+
+ static int MIR_SYSCALL GetFormattedLength(_Printf_format_string_ const char *pszFormat, va_list args)
+ {
+ #ifdef _MSC_VER
+ return _vscprintf(pszFormat, args);
+ #else
+ return 0; // !!!!!!!!!!
+ #endif
+ }
+
+ static int MIR_SYSCALL Format(char *pszBuffer, size_t nlength, _Printf_format_string_ const char *pszFormat, va_list args)
+ {
+ #ifdef _MSC_VER
+ return vsprintf_s(pszBuffer, nlength, pszFormat, args);
+ #else
+ return 0; // !!!!!!!!!!
+ #endif
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc)
+ {
+ // Returns required buffer length in XCHARs
+ return int(strlen(pszSrc));
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc, int nLength)
+ {
+ (void)pszSrc;
+ // Returns required buffer length in XCHARs
+ return nLength;
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSource)
+ {
+ // Returns required buffer length in XCHARs
+ #ifdef _MSC_VER
+ return ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 0, pszSource, -1, NULL, 0, NULL, NULL) - 1;
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSource, int nLength)
+ {
+ // Returns required buffer length in XCHARs
+ #ifdef _MSC_VER
+ return ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 0, pszSource, nLength, NULL, 0, NULL, NULL);
+ #else
+ return 0;
+ #endif
+ }
+
+ static void MIR_SYSCALL ConvertToBaseType(char*pszDest, int nDestLength, const char *pszSrc, int nSrcLength = -1)
+ {
+ if (nSrcLength == -1) { nSrcLength = 1 + GetBaseTypeLength(pszSrc); }
+ // nLen is in XCHARs
+ memcpy_s(pszDest, nDestLength*sizeof(char), pszSrc, nSrcLength*sizeof(char));
+ }
+
+ static void MIR_SYSCALL ConvertToBaseType(char*pszDest, int nDestLength, const wchar_t *pszSrc, int nSrcLength = -1)
+ {
+ // nLen is in XCHARs
+ #ifdef _MSC_VER
+ ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL);
+ #endif
+ }
+
+ static void ConvertToOem(_CharType* pstrString)
+ {
+ #ifdef _MSC_VER
+ bool fSuccess = ::CharToOemA(pstrString, pstrString);
+ #endif // _MSC_VER
+ }
+
+ static void ConvertToAnsi(_CharType* pstrString)
+ {
+ #ifdef _MSC_VER
+ bool fSuccess = ::OemToCharA(pstrString, pstrString);
+ #endif
+ }
+
+ static void ConvertToOem(_CharType* pstrString, size_t size)
+ {
+ #ifdef _MSC_VER
+ uint32_t dwSize = static_cast<uint32_t>(size);
+ ::CharToOemBuffA(pstrString, pstrString, dwSize);
+ #endif
+ }
+
+ static void ConvertToAnsi(_CharType* pstrString, size_t size)
+ {
+ #ifdef _MSC_VER
+ uint32_t dwSize = static_cast<uint32_t>(size);
+ ::OemToCharBuffA(pstrString, pstrString, dwSize);
+ #endif
+ }
+
+ static void MIR_SYSCALL FloodCharacters(char ch, int nLength, char* pch)
+ {
+ // nLength is in XCHARs
+ memset(pch, ch, nLength);
+ }
+
+ static int MIR_SYSCALL SafeStringLen(const char *psz)
+ {
+ // returns length in bytes
+ return (psz != NULL) ? int(strlen(psz)) : 0;
+ }
+
+ static int MIR_SYSCALL SafeStringLen(const wchar_t *psz)
+ {
+ // returns length in wchar_ts
+ return (psz != NULL) ? int(wcslen(psz)) : 0;
+ }
+
+ static int MIR_SYSCALL GetCharLen(const wchar_t *pch)
+ {
+ // returns char length
+ return 1;
+ }
+
+ static int MIR_SYSCALL GetCharLen(const char *pch)
+ {
+ // returns char length
+ #ifdef _MSC_VER
+ return int(_mbclen(reinterpret_cast<const unsigned char*>(pch)));
+ #else
+ return mblen(pch, strlen(pch));
+ #endif
+ }
+
+ static uint32_t MIR_SYSCALL GetEnvironmentVariable(const char *pszVar, char*pszBuffer, uint32_t dwSize)
+ {
+ #ifdef _MSC_VER
+ return ::GetEnvironmentVariableA(pszVar, pszBuffer, dwSize);
+ #else
+ return 0; // !!!!!!!!!!
+ #endif
+ }
+
+ static char* MirCopy(const char *pstrString, size_t size)
+ {
+ return mir_strndup(pstrString, size);
+ }
+};
+
+// specialization for wchar_t
+template<>
+class ChTraitsCRT< wchar_t > : public ChTraitsBase< wchar_t >
+{
+ static uint32_t MIR_SYSCALL _GetEnvironmentVariableW(const wchar_t *pszName, wchar_t *pszBuffer, uint32_t nSize)
+ {
+ #ifdef _MSC_VER
+ return ::GetEnvironmentVariableW(pszName, pszBuffer, nSize);
+ #else
+ return 0; // !!!!!!!!!!
+ #endif
+ }
+
+public:
+ static wchar_t* MIR_SYSCALL CharNext(const wchar_t *psz)
+ {
+ return const_cast<wchar_t*>(psz+1);
+ }
+
+ static int MIR_SYSCALL IsDigit(wchar_t ch)
+ {
+ #ifdef _MSC_VER
+ return iswdigit(static_cast<unsigned short>(ch));
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL IsSpace(wchar_t ch)
+ {
+ #ifdef _MSC_VER
+ return iswspace(static_cast<unsigned short>(ch));
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL StringCompare(const wchar_t *pszA, const wchar_t *pszB)
+ {
+ return wcscmp(pszA, pszB);
+ }
+
+ static int MIR_SYSCALL StringCompareIgnore(const wchar_t *pszA, const wchar_t *pszB)
+ {
+ #ifdef _MSC_VER
+ return _wcsicmp(pszA, pszB);
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL StringCollate(const wchar_t *pszA, const wchar_t *pszB)
+ {
+ return wcscoll(pszA, pszB);
+ }
+
+ static int MIR_SYSCALL StringCollateIgnore(const wchar_t *pszA, const wchar_t *pszB)
+ {
+ #ifdef _MSC_VER
+ return _wcsicoll(pszA, pszB);
+ #else
+ return 0;
+ #endif
+ }
+
+ static const wchar_t* MIR_SYSCALL StringFindString(const wchar_t *pszBlock, const wchar_t *pszMatch)
+ {
+ return wcsstr(pszBlock, pszMatch);
+ }
+
+ static wchar_t* MIR_SYSCALL StringFindString(wchar_t *pszBlock, const wchar_t *pszMatch)
+ {
+ return const_cast<wchar_t*>(wcsstr(pszBlock, pszMatch));
+ }
+
+ static const wchar_t* MIR_SYSCALL StringFindChar(const wchar_t *pszBlock, wchar_t chMatch)
+ {
+ return wcschr(pszBlock, chMatch);
+ }
+
+ static const wchar_t* MIR_SYSCALL StringFindCharRev(const wchar_t *psz, wchar_t ch)
+ {
+ return wcsrchr(psz, ch);
+ }
+
+ static const wchar_t* MIR_SYSCALL StringScanSet(const wchar_t *pszBlock, const wchar_t *pszMatch)
+ {
+ return wcspbrk(pszBlock, pszMatch);
+ }
+
+ static int MIR_SYSCALL StringSpanIncluding(const wchar_t *pszBlock, const wchar_t *pszSet)
+ {
+ return (int)wcsspn(pszBlock, pszSet);
+ }
+
+ static int MIR_SYSCALL StringSpanExcluding(const wchar_t *pszBlock, const wchar_t *pszSet)
+ {
+ return (int)wcscspn(pszBlock, pszSet);
+ }
+
+ static wchar_t* MIR_SYSCALL StringUppercase(wchar_t *psz)
+ {
+ #ifdef _MSC_VER
+ CharUpperBuffW(psz, (uint32_t)wcslen(psz));
+ #endif
+ return psz;
+ }
+
+ static wchar_t* MIR_SYSCALL StringLowercase(wchar_t *psz)
+ {
+ #ifdef _MSC_VER
+ CharLowerBuffW(psz, (uint32_t)wcslen(psz));
+ #endif
+ return psz;
+ }
+
+ static wchar_t* MIR_SYSCALL StringUppercase(wchar_t *psz, size_t len)
+ {
+ #ifdef _MSC_VER
+ CharUpperBuffW(psz, (uint32_t)len);
+ #endif
+ return psz;
+ }
+
+ static wchar_t* MIR_SYSCALL StringLowercase(wchar_t *psz, size_t len)
+ {
+ #ifdef _MSC_VER
+ CharLowerBuffW(psz, (uint32_t)len);
+ #endif
+ return psz;
+ }
+
+ static wchar_t* MIR_SYSCALL StringReverse(wchar_t *psz)
+ {
+ #ifdef _MSC_VER
+ return _wcsrev(psz);
+ #else
+ return psz;
+ #endif
+ }
+
+ static int MIR_SYSCALL GetFormattedLength(_Printf_format_string_ const wchar_t *pszFormat, va_list args)
+ {
+ #ifdef _MSC_VER
+ return _vscwprintf(pszFormat, args);
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL Format(wchar_t *pszBuffer, _Printf_format_string_ const wchar_t *pszFormat, va_list args)
+ {
+ #pragma warning(push)
+ #pragma warning(disable : 4996)
+
+ #ifdef _MSC_VER
+ return vswprintf(pszBuffer, pszFormat, args);
+ #else
+ return 0;
+ #endif
+ #pragma warning(pop)
+ }
+
+ static int MIR_SYSCALL Format(wchar_t *pszBuffer, size_t nLength, _Printf_format_string_ const wchar_t *pszFormat, va_list args)
+ {
+ #pragma warning(push)
+ #pragma warning(disable : 4996)
+
+ #ifdef _MSC_VER
+ return _vsnwprintf(pszBuffer, nLength, pszFormat, args);
+ #else
+ return 0;
+ #endif
+
+ #pragma warning(pop)
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc)
+ {
+ // Returns required buffer size in wchar_ts
+ #ifdef _MSC_VER
+ return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, nullptr, 0)-1;
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const char *pszSrc, int nLength)
+ {
+ // Returns required buffer size in wchar_ts
+ #ifdef _MSC_VER
+ return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nLength, nullptr, 0);
+ #else
+ return 0;
+ #endif
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSrc)
+ {
+ // Returns required buffer size in wchar_ts
+ return (int)wcslen(pszSrc);
+ }
+
+ static int MIR_SYSCALL GetBaseTypeLength(const wchar_t *pszSrc, int nLength)
+ {
+ (void)pszSrc;
+ // Returns required buffer size in wchar_ts
+ return nLength;
+ }
+
+ static void MIR_SYSCALL ConvertToBaseType(wchar_t *pszDest, int nDestLength, const char *pszSrc, int nSrcLength = -1)
+ {
+ // nLen is in wchar_ts
+ #ifdef _MSC_VER
+ ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength);
+ #endif
+ }
+
+ static void MIR_SYSCALL ConvertToBaseType(wchar_t *pszDest, int nDestLength, const wchar_t *pszSrc, int nSrcLength = -1)
+ {
+ if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
+ // nLen is in wchar_ts
+ #if _MSC_VER >= 1400
+ wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength);
+ #else
+ wmemcpy(pszDest, pszSrc, nDestLength);
+ #endif
+ }
+
+ static void MIR_SYSCALL FloodCharacters(wchar_t ch, int nLength, wchar_t *psz)
+ {
+ // nLength is in XCHARs
+ for (int i = 0; i < nLength; i++)
+ {
+ psz[i] = ch;
+ }
+ }
+
+ static int MIR_SYSCALL SafeStringLen(const char *psz)
+ {
+ // returns length in bytes
+ return (psz != nullptr) ? (int)strlen(psz) : 0;
+ }
+
+ static int MIR_SYSCALL SafeStringLen(const wchar_t *psz)
+ {
+ // returns length in wchar_ts
+ return (psz != nullptr) ? (int)wcslen(psz) : 0;
+ }
+
+ static int MIR_SYSCALL GetCharLen(const wchar_t* pch)
+ {
+ (void)pch;
+ // returns char length
+ return 1;
+ }
+
+ static int MIR_SYSCALL GetCharLen(const char* pch)
+ {
+ // returns char length
+ #ifdef _MSC_VER
+ return (int)(_mbclen(reinterpret_cast< const unsigned char* >(pch)));
+ #else
+ return mblen(pch, strlen(pch));
+ #endif
+ }
+
+ static uint32_t MIR_SYSCALL GetEnvironmentVariable(const wchar_t *pszVar, wchar_t *pszBuffer, uint32_t dwSize)
+ {
+ return _GetEnvironmentVariableW(pszVar, pszBuffer, dwSize);
+ }
+
+ static void MIR_SYSCALL ConvertToOem(wchar_t* /*psz*/)
+ {
+ }
+
+ static void MIR_SYSCALL ConvertToAnsi(wchar_t* /*psz*/)
+ {
+ }
+
+ static void MIR_SYSCALL ConvertToOem(wchar_t* /*psz*/, size_t)
+ {
+ }
+
+ static void MIR_SYSCALL ConvertToAnsi(wchar_t* /*psz*/, size_t)
+ {
+ }
+
+ static wchar_t* MirCopy(const wchar_t *pstrString, size_t size)
+ {
+ return mir_wstrndup(pstrString, size);
+ }
+};
+
+template< typename BaseType, class StringTraits >
+class MIR_CORE_EXPORT CMStringT : public CMSimpleStringT< BaseType >
+{
+public:
+ typedef CMSimpleStringT< BaseType> CThisSimpleString;
+ typedef typename CThisSimpleString::XCHAR XCHAR;
+ typedef typename CThisSimpleString::PXSTR PXSTR;
+ typedef typename CThisSimpleString::PCXSTR PCXSTR;
+ typedef typename CThisSimpleString::YCHAR YCHAR;
+ typedef typename CThisSimpleString::PYSTR PYSTR;
+ typedef typename CThisSimpleString::PCYSTR PCYSTR;
+
+public:
+ CMStringT();
+
+ // Copy constructor
+ CMStringT(const CMStringT& strSrc);
+
+ CMStringT(const XCHAR* pszSrc);
+ CMStringT(CMStringDataFormat, _Printf_format_string_ const XCHAR* pszFormat, ...);
+
+ CMStringT(const YCHAR* pszSrc);
+ CMStringT(const unsigned char* pszSrc);
+
+ CMStringT(char ch, int nLength = 1);
+ CMStringT(wchar_t ch, int nLength = 1);
+
+ CMStringT(const XCHAR* pch, int nLength);
+ CMStringT(const YCHAR* pch, int nLength);
+
+ // Destructor
+ ~CMStringT();
+
+ // Assignment operators
+ CMStringT& operator=(const CMStringT& strSrc);
+ CMStringT& operator=(PCXSTR pszSrc);
+ CMStringT& operator=(PCYSTR pszSrc);
+ CMStringT& operator=(const unsigned char* pszSrc);
+ CMStringT& operator=(char ch);
+ CMStringT& operator=(wchar_t ch);
+
+ CMStringT& operator+=(const CMStringT& str);
+ CMStringT& operator+=(const CThisSimpleString& str);
+ CMStringT& operator+=(PCXSTR pszSrc);
+ CMStringT& operator+=(PCYSTR pszSrc);
+ CMStringT& operator+=(char ch);
+ CMStringT& operator+=(unsigned char ch);
+ CMStringT& operator+=(wchar_t ch);
+
+ // Comparison
+
+ int Compare(PCXSTR psz) const;
+ int CompareNoCase(PCXSTR psz) const;
+ int Collate(PCXSTR psz) const;
+ int CollateNoCase(PCXSTR psz) const;
+
+ // Advanced manipulation
+
+ // Delete 'nCount' characters, starting at index 'iIndex'
+ int Delete(int iIndex, int nCount = 1);
+
+ // Insert character 'ch' before index 'iIndex'
+ int Insert(int iIndex, XCHAR ch);
+
+ // Insert string 'psz' before index 'iIndex'
+ int Insert(int iIndex, PCXSTR psz);
+
+ // Replace all occurrences of character 'chOld' with character 'chNew'
+ int Replace(XCHAR chOld, XCHAR chNew);
+
+ // Replace all occurrences of string 'pszOld' with string 'pszNew'
+ int Replace(PCXSTR pszOld, PCXSTR pszNew);
+
+ // Remove all occurrences of character 'chRemove'
+ int Remove(XCHAR chRemove);
+
+ CMStringT Tokenize(PCXSTR pszTokens, int& iStart) const;
+
+ // find routines
+
+ // Find the first occurrence of character 'ch', starting at index 'iStart'
+ int Find(XCHAR ch, int iStart = 0) const;
+
+ // look for a specific sub-string
+
+ // Find the first occurrence of string 'pszSub', starting at index 'iStart'
+ int Find(PCXSTR pszSub, int iStart = 0) const;
+
+ // Find the first occurrence of any of the characters in string 'pszCharSet'
+ int FindOneOf(PCXSTR pszCharSet) const;
+
+ // Find the last occurrence of character 'ch'
+ int ReverseFind(XCHAR ch) const;
+
+ // manipulation
+
+ // Convert the string to uppercase
+ CMStringT& MakeUpper();
+
+ // Convert the string to lowercase
+ CMStringT& MakeLower();
+
+ // Reverse the string
+ CMStringT& MakeReverse();
+
+ // trimming
+
+ // Remove all trailing whitespace
+ CMStringT& TrimRight();
+
+ // Remove all leading whitespace
+ CMStringT& TrimLeft();
+
+ // Remove all leading and trailing whitespace
+ CMStringT& Trim();
+
+ // Remove all leading and trailing occurrences of character 'chTarget'
+ CMStringT& Trim(XCHAR chTarget);
+
+ // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
+ CMStringT& Trim(PCXSTR pszTargets);
+
+ // trimming anything (either side)
+
+ // Remove all trailing occurrences of character 'chTarget'
+ CMStringT& TrimRight(XCHAR chTarget);
+
+ // Remove all trailing occurrences of any of the characters in string 'pszTargets'
+ CMStringT& TrimRight(PCXSTR pszTargets);
+
+ // Remove all leading occurrences of character 'chTarget'
+ CMStringT& TrimLeft(XCHAR chTarget);
+
+ // Remove all leading occurrences of any of the characters in string 'pszTargets'
+ CMStringT& TrimLeft(PCXSTR pszTargets);
+
+ // Convert the string to the OEM character set
+ void AnsiToOem();
+
+ // Convert the string to the ANSI character set
+ void OemToAnsi();
+
+ // Very simple sub-string extraction
+
+ // Return the substring starting at index 'iFirst'
+ CMStringT Mid(int iFirst) const;
+
+ // Return the substring starting at index 'iFirst', with length 'nCount'
+ CMStringT Mid(int iFirst, int nCount) const;
+
+ // Return the substring consisting of the rightmost 'nCount' characters
+ CMStringT Right(int nCount) const;
+
+ // Return the substring consisting of the leftmost 'nCount' characters
+ CMStringT Left(int nCount) const;
+
+ // Return the substring consisting of the leftmost characters in the set 'pszCharSet'
+ CMStringT SpanIncluding(PCXSTR pszCharSet) const;
+
+ // Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
+ CMStringT SpanExcluding(PCXSTR pszCharSet) const;
+
+ // Format data using format string 'pszFormat'
+ PCXSTR Format(PCXSTR _Printf_format_string_ pszFormat, ...);
+ PCXSTR FormatV(PCXSTR _Printf_format_string_ pszFormat, va_list args);
+
+ // Append formatted data using format string 'pszFormat'
+ PCXSTR AppendFormat(PCXSTR _Printf_format_string_ pszFormat, ...);
+ void AppendFormatV(PCXSTR _Printf_format_string_ pszFormat, va_list args);
+
+ // return a copy of string to be freed by mir_free()
+ PXSTR Detach() const;
+
+ // Set the string to the value of environment variable 'pszVar'
+ BOOL GetEnvironmentVariable(PCXSTR pszVar);
+
+ friend bool __forceinline operator==(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) == 0; }
+ friend bool __forceinline operator==(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) == 0; }
+ friend bool __forceinline operator==(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) == 0; }
+ friend bool __forceinline operator==(const CMStringT& str1, PCYSTR psz2) { return str1 == CMStringT(psz2); }
+ friend bool __forceinline operator==(PCYSTR psz1, const CMStringT& str2) { return CMStringT(psz1) == str2; }
+
+ friend bool __forceinline operator!=(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) != 0; }
+ friend bool __forceinline operator!=(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) != 0; }
+ friend bool __forceinline operator!=(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) != 0; }
+ friend bool __forceinline operator!=(const CMStringT& str1, PCYSTR psz2) { return str1 != CMStringT(psz2); }
+ friend bool __forceinline operator!=(PCYSTR psz1, const CMStringT& str2) { return CMStringT(psz1) != str2; }
+
+ friend bool __forceinline operator<(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) < 0; }
+ friend bool __forceinline operator<(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) < 0; }
+ friend bool __forceinline operator<(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) > 0; }
+
+ friend bool __forceinline operator>(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) > 0; }
+ friend bool __forceinline operator>(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) > 0; }
+ friend bool __forceinline operator>(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) < 0; }
+
+ friend bool __forceinline operator<=(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) <= 0; }
+ friend bool __forceinline operator<=(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) <= 0; }
+ friend bool __forceinline operator<=(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) >= 0; }
+
+ friend bool __forceinline operator>=(const CMStringT& str1, const CMStringT& str2) { return str1.Compare(str2) >= 0; }
+ friend bool __forceinline operator>=(const CMStringT& str1, PCXSTR psz2) { return str1.Compare(psz2) >= 0; }
+ friend bool __forceinline operator>=(PCXSTR psz1, const CMStringT& str2) { return str2.Compare(psz1) <= 0; }
+
+ friend bool __forceinline operator==(XCHAR ch1, const CMStringT& str2) { return (str2.GetLength() == 1) && (str2[0] == ch1); }
+ friend bool __forceinline operator==(const CMStringT& str1, XCHAR ch2) { return (str1.GetLength() == 1) && (str1[0] == ch2); }
+
+ friend bool __forceinline operator!=(XCHAR ch1, const CMStringT& str2) { return (str2.GetLength() != 1) || (str2[0] != ch1); }
+ friend bool __forceinline operator!=(const CMStringT& str1, XCHAR ch2) { return (str1.GetLength() != 1) || (str1[0] != ch2); }
+};
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, const CMStringT<BaseType, StringTraits>& str2);
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, typename CMStringT<BaseType, StringTraits>::PCXSTR psz2);
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(typename CMStringT<BaseType, StringTraits>::PCXSTR psz1, const CMStringT<BaseType, StringTraits>& str2);
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, wchar_t ch2);
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, char ch2);
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator + (wchar_t ch1, const CMStringT<BaseType, StringTraits>& str2);
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(char ch1, const CMStringT<BaseType, StringTraits>& str2);
+
+typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW;
+typedef CMStringT< char, ChTraitsCRT< char > > CMStringA;
+
+#endif // M_STRING_H__
diff --git a/include/m_string.inl b/include/m_string.inl index 40413810ea..7834a573bb 100644 --- a/include/m_string.inl +++ b/include/m_string.inl @@ -1,1471 +1,1471 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#ifndef M_STRING_INL__ - -#ifndef _MSC_VER - #include <algorithm> - #define __max std::max -#endif - -template<typename BaseType> -CMSimpleStringT<BaseType>::CMSimpleStringT() -{ - CMStringData* pData = mirstr_getNil(); - Attach(pData); -} - -template<typename BaseType> -CMSimpleStringT<BaseType>::CMSimpleStringT(const CMSimpleStringT& strSrc) -{ - CMStringData* pSrcData = strSrc.GetData(); - CMStringData* pNewData = CloneData(pSrcData); - Attach(pNewData); -} - -template<typename BaseType> -CMSimpleStringT<BaseType>::CMSimpleStringT(PCXSTR pszSrc) -{ - int nLength = StringLength(pszSrc); - CMStringData* pData = mirstr_allocate(nLength, sizeof(XCHAR)); - if (pData != nullptr) { - Attach(pData); - SetLength(nLength); - CopyChars(m_pszData, nLength, pszSrc, nLength); - } -} - -template<typename BaseType> -CMSimpleStringT<BaseType>::CMSimpleStringT(const XCHAR* pchSrc, int nLength) -{ - CMStringData* pData = mirstr_allocate(nLength, sizeof(XCHAR)); - if (pData != nullptr) { - Attach(pData); - SetLength(nLength); - CopyChars(m_pszData, nLength, pchSrc, nLength); - } -} - -template<typename BaseType> -CMSimpleStringT<BaseType>::~CMSimpleStringT() -{ - CMStringData* pData = GetData(); - pData->Release(); -} - -template<typename BaseType> -CMSimpleStringT<BaseType>& CMSimpleStringT<BaseType>::operator=(const CMSimpleStringT& strSrc) -{ - CMStringData* pSrcData = strSrc.GetData(); - CMStringData* pOldData = GetData(); - if (pSrcData != pOldData) { - if (pOldData->IsLocked()) - SetString(strSrc.GetString(), strSrc.GetLength()); - else { - CMStringData* pNewData = CloneData(pSrcData); - pOldData->Release(); - Attach(pNewData); - } - } - - return *this; -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Append(PCXSTR pszSrc) -{ - Append(pszSrc, StringLength(pszSrc)); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Append(PCXSTR pszSrc, int nLength) -{ - // See comment in SetString() about why we do this - UINT_PTR nOffset = UINT_PTR(pszSrc - GetString()); - - int nOldLength = GetLength(); - if (nOldLength < 0) { - // protects from underflow - nOldLength = 0; - } - - //Make sure we don't read pass end of the terminating NULL - int nSrcLength = StringLength(pszSrc); - nLength = nLength > nSrcLength ? nSrcLength : nLength; - - int nNewLength = nOldLength + nLength; - PXSTR pszBuffer = GetBuffer(nNewLength); - if (nOffset <= UINT_PTR(nOldLength)) { - pszSrc = pszBuffer + nOffset; - // No need to call CopyCharsOverlapped, since the destination is - // beyond the end of the original buffer - } - CopyChars(pszBuffer + nOldLength, nLength, pszSrc, nLength); - ReleaseBufferSetLength(nNewLength); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::AppendChar(XCHAR ch) -{ - UINT nOldLength = GetLength(); - int nNewLength = nOldLength + 1; - PXSTR pszBuffer = GetBuffer(nNewLength); - pszBuffer[nOldLength] = ch; - ReleaseBufferSetLength(nNewLength); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Append(const CMSimpleStringT<BaseType>& strSrc) -{ - Append(strSrc.GetString(), strSrc.GetLength()); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Empty() -{ - CMStringData* pOldData = GetData(); - if (pOldData->nDataLength == 0) - return; - - if (pOldData->IsLocked()) { - // Don't reallocate a locked buffer that's shrinking - SetLength(0); - } - else { - pOldData->Release(); - CMStringData* pNewData = mirstr_getNil(); - Attach(pNewData); - } -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::FreeExtra() -{ - CMStringData* pOldData = GetData(); - int nLength = pOldData->nDataLength; - if (pOldData->nAllocLength == nLength) - return; - - if (!pOldData->IsLocked()) { // Don't reallocate a locked buffer that's shrinking - CMStringData* pNewData = mirstr_allocate(nLength, sizeof(XCHAR)); - if (pNewData == nullptr) { - SetLength(nLength); - return; - } - - CopyChars(PXSTR(pNewData->data()), nLength, PCXSTR(pOldData->data()), nLength); - - pOldData->Release(); - Attach(pNewData); - SetLength(nLength); - } -} - -template<typename BaseType> -typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::GetBuffer() -{ - CMStringData* pData = GetData(); - if (pData->IsShared()) - Fork(pData->nDataLength); - - return m_pszData; -} - -template<typename BaseType> -typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::GetBufferSetLength(int nLength) -{ - PXSTR pszBuffer = GetBuffer(nLength); - SetLength(nLength); - - return pszBuffer; -} - -template<typename BaseType> -typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::LockBuffer() -{ - CMStringData* pData = GetData(); - if (pData->IsShared()) { - Fork(pData->nDataLength); - pData = GetData(); // Do it again, because the fork might have changed it - } - pData->Lock(); - - return m_pszData; -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::UnlockBuffer() -{ - CMStringData* pData = GetData(); - pData->Unlock(); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::ReleaseBuffer(int nNewLength) -{ - if (nNewLength == -1) { - int nAlloc = GetData()->nAllocLength; - nNewLength = StringLengthN(m_pszData, nAlloc); - } - SetLength(nNewLength); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Truncate(int nNewLength) -{ - GetBuffer(nNewLength); - ReleaseBufferSetLength(nNewLength); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::SetAt(int iChar, XCHAR ch) -{ - int nLength = GetLength(); - PXSTR pszBuffer = GetBuffer(); - pszBuffer[iChar] = ch; - ReleaseBufferSetLength(nLength); - -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::SetString(PCXSTR pszSrc) -{ - SetString(pszSrc, StringLength(pszSrc)); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::SetString(PCXSTR pszSrc, int nLength) -{ - if (nLength == 0) - Empty(); - else { - UINT nOldLength = GetLength(); - UINT_PTR nOffset = pszSrc - GetString(); - - PXSTR pszBuffer = GetBuffer(nLength); - if (nOffset <= nOldLength) - CopyCharsOverlapped(pszBuffer, GetAllocLength(), pszBuffer + nOffset, nLength); - else - CopyChars(pszBuffer, GetAllocLength(), pszSrc, nLength); - - ReleaseBufferSetLength(nLength); - } -} - -template<typename BaseType> -class CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType>& str1, const CMSimpleStringT<BaseType>& str2) -{ - CMSimpleStringT<BaseType> s; - Concatenate(s, str1, str1.GetLength(), str2, str2.GetLength()); - return s; -} - -template<typename BaseType> -class CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType>& str1, typename CMSimpleStringT<BaseType>::PCXSTR psz2) -{ - CMSimpleStringT<BaseType> s; - Concatenate(s, str1, str1.GetLength(), psz2, StringLength(psz2)); - return s; -} - -template<typename BaseType> -CMSimpleStringT<BaseType> operator+(typename CMSimpleStringT<BaseType>::PCXSTR psz1, const CMSimpleStringT<BaseType>& str2) -{ - CMSimpleStringT<BaseType> s; - Concatenate(s, psz1, StringLength(psz1), str2, str2.GetLength()); - return s; -} - -template<typename BaseType> -void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars) -{ - #pragma warning (push) - #pragma warning(disable : 4996) - memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR)); - #pragma warning (pop) -} - -template<typename BaseType> -void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars) -{ - memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR)); -} - -template<typename BaseType> -void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars) -{ - #pragma warning (push) - #pragma warning(disable : 4996) - memmove(pchDest, pchSrc, nChars * sizeof(XCHAR)); - #pragma warning (pop) -} - -template<typename BaseType> -void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars) -{ - memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR)); -} - -template<typename BaseType> -int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLength(const char* psz) -{ - if (psz == nullptr) - return(0); - - return (int(strlen(psz))); -} - -template<typename BaseType> -int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLength(const wchar_t* psz) -{ - if (psz == nullptr) - return 0; - - return int(wcslen(psz)); -} - -template<typename BaseType> -int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLengthN(const char* psz, size_t sizeInXChar) -{ - if (psz == nullptr) - return 0; - - return int(strnlen(psz, sizeInXChar)); -} - -template<typename BaseType> -int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLengthN(const wchar_t* psz, size_t sizeInXChar) -{ - if (psz == nullptr) - return 0; - - return int(wcsnlen(psz, sizeInXChar)); -} - -template<typename BaseType> -void MIR_SYSCALL CMSimpleStringT<BaseType>::Concatenate(CMSimpleStringT<BaseType>& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2) -{ - int nNewLength = nLength1 + nLength2; - PXSTR pszBuffer = strResult.GetBuffer(nNewLength); - CopyChars(pszBuffer, nLength1, psz1, nLength1); - CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2); - strResult.ReleaseBufferSetLength(nNewLength); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Attach(CMStringData* pData) -{ - m_pszData = static_cast<PXSTR>(pData->data()); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Fork(int nLength) -{ - CMStringData* pOldData = GetData(); - int nOldLength = pOldData->nDataLength; - CMStringData* pNewData = mirstr_allocate(nLength, sizeof(XCHAR)); - if (pNewData != nullptr) { - int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength) + 1; // Copy '\0' - CopyChars(PXSTR(pNewData->data()), nCharsToCopy, PCXSTR(pOldData->data()), nCharsToCopy); - pNewData->nDataLength = nOldLength; - pOldData->Release(); - Attach(pNewData); - } -} - -template<typename BaseType> -typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::PrepareWrite(int nLength) -{ - CMStringData* pOldData = GetData(); - int nShared = 1 - pOldData->nRefs; // nShared < 0 means true, >= 0 means false - int nTooShort = pOldData->nAllocLength - nLength; // nTooShort < 0 means true, >= 0 means false - if ((nShared | nTooShort) < 0) // If either sign bit is set (i.e. either is less than zero), we need to copy data - PrepareWrite2(nLength); - - return m_pszData; -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::PrepareWrite2(int nLength) -{ - CMStringData* pOldData = GetData(); - if (pOldData->nDataLength > nLength) - nLength = pOldData->nDataLength; - - if (pOldData->IsShared()) { - Fork(nLength); - } - else if (pOldData->nAllocLength < nLength) { - // Grow exponentially, until we hit 1K. - int nNewLength = pOldData->nAllocLength; - if (nNewLength > 1024) - nNewLength += 1024; - else - nNewLength *= 2; - - if (nNewLength < nLength) - nNewLength = nLength; - - Reallocate(nNewLength); - } -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::Reallocate(int nLength) -{ - CMStringData* pOldData = GetData(); - if (pOldData->nAllocLength >= nLength || nLength <= 0) - return; - - CMStringData* pNewData = mirstr_realloc(pOldData, nLength, sizeof(XCHAR)); - if (pNewData != nullptr) - Attach(pNewData); -} - -template<typename BaseType> -void CMSimpleStringT<BaseType>::SetLength(int nLength) -{ - GetData()->nDataLength = nLength; - m_pszData[nLength] = 0; -} - -template<typename BaseType> -CMStringData* MIR_SYSCALL CMSimpleStringT<BaseType>::CloneData(CMStringData* pData) -{ - CMStringData* pNewData = nullptr; - - if (!pData->IsLocked()) { - pNewData = pData; - pNewData->AddRef(); - } - - return pNewData; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT() : - CThisSimpleString() -{ -} - -// Copy constructor -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(const CMStringT<BaseType, StringTraits>& strSrc) : - CThisSimpleString(strSrc) -{ -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(const XCHAR* pszSrc) : - CThisSimpleString() -{ - *this = pszSrc; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(CMStringDataFormat, const XCHAR* pszFormat, ...) : - CThisSimpleString() -{ - va_list args; - va_start(args, pszFormat); - FormatV(pszFormat, args); -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(const YCHAR* pszSrc) : - CThisSimpleString() -{ - *this = pszSrc; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(const unsigned char* pszSrc) : - CThisSimpleString() -{ - *this = reinterpret_cast<const char*>(pszSrc); -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(char ch, int nLength) : - CThisSimpleString() -{ - if (nLength > 0) { - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::FloodCharacters(XCHAR(ch), nLength, pszBuffer); - this->ReleaseBufferSetLength(nLength); - } -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(wchar_t ch, int nLength) : - CThisSimpleString() -{ - if (nLength > 0) { - //Convert ch to the BaseType - wchar_t pszCh[2] = { ch, 0 }; - int nBaseTypeCharLen = 1; - - if (ch != L'\0') - nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh); - - XCHAR *buffBaseTypeChar = new XCHAR[nBaseTypeCharLen + 1]; - StringTraits::ConvertToBaseType(buffBaseTypeChar, nBaseTypeCharLen + 1, pszCh, 1); - //allocate enough characters in String and flood (replicate) with the (converted character)*nLength - PXSTR pszBuffer = this->GetBuffer(nLength*nBaseTypeCharLen); - if (nBaseTypeCharLen == 1) //Optimization for a common case - wide char translates to 1 ansi/wide char. - StringTraits::FloodCharacters(buffBaseTypeChar[0], nLength, pszBuffer); - else { - XCHAR* p = pszBuffer; - for (int i = 0; i < nLength; i++) { - for (int j = 0; j < nBaseTypeCharLen; ++j) { - *p = buffBaseTypeChar[j]; - ++p; - } - } - } - this->ReleaseBufferSetLength(nLength*nBaseTypeCharLen); - delete[] buffBaseTypeChar; - } -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(const XCHAR* pch, int nLength) : - CThisSimpleString(pch, nLength) -{ -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::CMStringT(const YCHAR* pch, int nLength) : - CThisSimpleString() -{ - if (nLength > 0) { - int nDestLength = StringTraits::GetBaseTypeLength(pch, nLength); - PXSTR pszBuffer = this->GetBuffer(nDestLength); - StringTraits::ConvertToBaseType(pszBuffer, nDestLength, pch, nLength); - this->ReleaseBufferSetLength(nDestLength); - } -} - -// Destructor -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>::~CMStringT() -{ -} - -// Assignment operators -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(const CMStringT& strSrc) -{ - CThisSimpleString::operator=(strSrc); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(PCXSTR pszSrc) -{ - CThisSimpleString::operator=(pszSrc); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(PCYSTR pszSrc) -{ - // nDestLength is in XCHARs - int nDestLength = (pszSrc != nullptr) ? StringTraits::GetBaseTypeLength(pszSrc) : 0; - if (nDestLength > 0) { - PXSTR pszBuffer = this->GetBuffer(nDestLength); - StringTraits::ConvertToBaseType(pszBuffer, nDestLength, pszSrc); - this->ReleaseBufferSetLength(nDestLength); - } - else this->Empty(); - - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(const unsigned char* pszSrc) -{ - return operator=(reinterpret_cast<const char*>(pszSrc)); -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(char ch) -{ - char ach[2] = { ch, 0 }; - return operator=(ach); -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(wchar_t ch) -{ - wchar_t ach[2] = { ch, 0 }; - return operator=(ach); -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(const CMStringT& str) -{ - CThisSimpleString::operator+=(str); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(const CThisSimpleString& str) -{ - CThisSimpleString::operator+=(str); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(PCXSTR pszSrc) -{ - CThisSimpleString::operator+=(pszSrc); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(PCYSTR pszSrc) -{ - CMStringT str(pszSrc); - return operator+=(str); -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(char ch) -{ - CThisSimpleString::operator+=(ch); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(unsigned char ch) -{ - CThisSimpleString::operator+=(ch); - return *this; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(wchar_t ch) -{ - CThisSimpleString::operator+=(ch); - return *this; -} - -// Comparison - -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Compare(PCXSTR psz) const -{ - return StringTraits::StringCompare(this->GetString(), psz); -} - -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::CompareNoCase(PCXSTR psz) const -{ - return StringTraits::StringCompareIgnore(this->GetString(), psz); -} - -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Collate(PCXSTR psz) const -{ - return StringTraits::StringCollate(this->GetString(), psz); -} - -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::CollateNoCase(PCXSTR psz) const -{ - return StringTraits::StringCollateIgnore(this->GetString(), psz); -} - -// Advanced manipulation - -// Delete 'nCount' characters, starting at index 'iIndex' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Delete(int iIndex, int nCount) -{ - if (iIndex < 0) - iIndex = 0; - - if (nCount < 0) - nCount = 0; - - int nLength = this->GetLength(); - if (nCount + iIndex > nLength) - nCount = nLength - iIndex; - - if (nCount > 0) { - int nNewLength = nLength - nCount; - int nXCHARsToCopy = nLength - (iIndex + nCount) + 1; - PXSTR pszBuffer = this->GetBuffer(); -#if _MSC_VER >= 1400 - memmove_s(pszBuffer + iIndex, nXCHARsToCopy*sizeof(XCHAR), pszBuffer + iIndex + nCount, nXCHARsToCopy*sizeof(XCHAR)); -#else - memmove(pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof(XCHAR)); -#endif - this->ReleaseBufferSetLength(nNewLength); - } - - return this->GetLength(); -} - -// Insert character 'ch' before index 'iIndex' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Insert(int iIndex, XCHAR ch) -{ - if (iIndex < 0) - iIndex = 0; - - if (iIndex > this->GetLength()) - iIndex = this->GetLength(); - - int nNewLength = this->GetLength() + 1; - - PXSTR pszBuffer = this->GetBuffer(nNewLength); - - // move existing bytes down -#if _MSC_VER >= 1400 - memmove_s(pszBuffer + iIndex + 1, (nNewLength - iIndex)*sizeof(XCHAR), pszBuffer + iIndex, (nNewLength - iIndex)*sizeof(XCHAR)); -#else - memmove(pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof(XCHAR)); -#endif - pszBuffer[iIndex] = ch; - - this->ReleaseBufferSetLength(nNewLength); - return nNewLength; -} - -// Insert string 'psz' before index 'iIndex' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Insert(int iIndex, PCXSTR psz) -{ - if (iIndex < 0) - iIndex = 0; - - if (iIndex > this->GetLength()) - iIndex = this->GetLength(); - - // nInsertLength and nNewLength are in XCHARs - int nInsertLength = StringTraits::SafeStringLen(psz); - int nNewLength = this->GetLength(); - if (nInsertLength > 0) { - nNewLength += nInsertLength; - - PXSTR pszBuffer = this->GetBuffer(nNewLength); - // move existing bytes down -#if _MSC_VER >= 1400 - memmove_s(pszBuffer + iIndex + nInsertLength, (nNewLength - iIndex - nInsertLength + 1)*sizeof(XCHAR), pszBuffer + iIndex, (nNewLength - iIndex - nInsertLength + 1)*sizeof(XCHAR)); - memcpy_s(pszBuffer + iIndex, nInsertLength*sizeof(XCHAR), psz, nInsertLength*sizeof(XCHAR)); -#else - memmove(pszBuffer+iIndex+nInsertLength, pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof(XCHAR)); - memcpy(pszBuffer+iIndex, psz, nInsertLength*sizeof(XCHAR)); -#endif - this->ReleaseBufferSetLength(nNewLength); - } - - return nNewLength; -} - -// Replace all occurrences of character 'chOld' with character 'chNew' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Replace(XCHAR chOld, XCHAR chNew) -{ - int nCount = 0; - - // short-circuit the nop case - if (chOld != chNew) { - // otherwise modify each character that matches in the string - bool bCopied = false; - PXSTR pszBuffer = const_cast<PXSTR>(this->GetString()); // We don't actually write to pszBuffer until we've called GetBuffer(). - - int nLength = this->GetLength(); - int iChar = 0; - while (iChar < nLength) { - // replace instances of the specified character only - if (pszBuffer[iChar] == chOld) { - if (!bCopied) { - bCopied = true; - pszBuffer = this->GetBuffer(nLength); - } - pszBuffer[iChar] = chNew; - nCount++; - } - iChar = int(StringTraits::CharNext(pszBuffer + iChar) - pszBuffer); - } - - if (bCopied) - this->ReleaseBufferSetLength(nLength); - } - - return nCount; -} - -// Replace all occurrences of string 'pszOld' with string 'pszNew' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Replace(PCXSTR pszOld, PCXSTR pszNew) -{ - // can't have empty or NULL lpszOld - - // nSourceLen is in XCHARs - int nSourceLen = StringTraits::SafeStringLen(pszOld); - if (nSourceLen == 0) - return 0; - // nReplacementLen is in XCHARs - int nReplacementLen = StringTraits::SafeStringLen(pszNew); - - // loop once to figure out the size of the result string - int nCount = 0; - { - PCXSTR pszStart = this->GetString(); - PCXSTR pszEnd = pszStart + this->GetLength(); - while (pszStart < pszEnd) { - PCXSTR pszTarget; - while ((pszTarget = StringTraits::StringFindString(pszStart, pszOld)) != nullptr) { - nCount++; - pszStart = pszTarget + nSourceLen; - } - pszStart += StringTraits::SafeStringLen(pszStart) + 1; - } - } - - // if any changes were made, make them - if (nCount > 0) { - // if the buffer is too small, just - // allocate a new buffer (slow but sure) - int nOldLength = this->GetLength(); - int nNewLength = nOldLength + (nReplacementLen - nSourceLen)*nCount; - - PXSTR pszBuffer = this->GetBuffer(__max(nNewLength, nOldLength)); - - PXSTR pszStart = pszBuffer; - PXSTR pszEnd = pszStart + nOldLength; - - // loop again to actually do the work - while (pszStart < pszEnd) { - PXSTR pszTarget; - while ((pszTarget = StringTraits::StringFindString(pszStart, pszOld)) != nullptr) { - int nBalance = nOldLength - int(pszTarget - pszBuffer + nSourceLen); - memmove_s(pszTarget + nReplacementLen, nBalance*sizeof(XCHAR), - pszTarget + nSourceLen, nBalance*sizeof(XCHAR)); - memcpy_s(pszTarget, nReplacementLen*sizeof(XCHAR), - pszNew, nReplacementLen*sizeof(XCHAR)); - pszStart = pszTarget + nReplacementLen; - pszTarget[nReplacementLen + nBalance] = 0; - nOldLength += (nReplacementLen - nSourceLen); - } - pszStart += StringTraits::SafeStringLen(pszStart) + 1; - } - this->ReleaseBufferSetLength(nNewLength); - } - - return nCount; -} - -// Remove all occurrences of character 'chRemove' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Remove(XCHAR chRemove) -{ - int nLength = this->GetLength(); - PXSTR pszBuffer = this->GetBuffer(nLength); - - PXSTR pszSource = pszBuffer; - PXSTR pszDest = pszBuffer; - PXSTR pszEnd = pszBuffer + nLength; - - while (pszSource < pszEnd) { - PXSTR pszNewSource = StringTraits::CharNext(pszSource); - if (*pszSource != chRemove) { - // Copy the source to the destination. Remember to copy all bytes of an MBCS character - size_t NewSourceGap = (pszNewSource - pszSource); - PXSTR pszNewDest = pszDest + NewSourceGap; - for (size_t i = 0; pszDest != pszNewDest && i < NewSourceGap; i++) { - *pszDest = *pszSource; - pszSource++; - pszDest++; - } - } - pszSource = pszNewSource; - } - *pszDest = 0; - int nCount = int(pszSource - pszDest); - this->ReleaseBufferSetLength(nLength - nCount); - - return nCount; -} - -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Tokenize(PCXSTR pszTokens, int& iStart) const -{ - if ((pszTokens == nullptr) || (*pszTokens == (XCHAR)0)) { - if (iStart < this->GetLength()) - return CMStringT(this->GetString() + iStart); - } - else { - PCXSTR pszPlace = this->GetString() + iStart; - PCXSTR pszEnd = this->GetString() + this->GetLength(); - if (pszPlace < pszEnd) { - int nIncluding = StringTraits::StringSpanIncluding(pszPlace, pszTokens); - - if ((pszPlace + nIncluding) < pszEnd) { - pszPlace += nIncluding; - int nExcluding = StringTraits::StringSpanExcluding(pszPlace, pszTokens); - - int iFrom = iStart + nIncluding; - int nUntil = nExcluding; - iStart = iFrom + nUntil + 1; - - return Mid(iFrom, nUntil); - } - } - } - - // return empty string, done tokenizing - iStart = -1; - - return CMStringT(); -} - -// find routines - -// Find the first occurrence of character 'ch', starting at index 'iStart' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Find(XCHAR ch, int iStart) const -{ - // nLength is in XCHARs - int nLength = this->GetLength(); - if (iStart < 0 || iStart >= nLength) - return -1; - - // find first single character - PCXSTR psz = StringTraits::StringFindChar(this->GetString() + iStart, ch); - - // return -1 if not found and index otherwise - return (psz == nullptr) ? -1 : int(psz - this->GetString()); -} - -// look for a specific sub-string - -// Find the first occurrence of string 'pszSub', starting at index 'iStart' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::Find(PCXSTR pszSub, int iStart) const -{ - // iStart is in XCHARs - if (pszSub == nullptr) - return -1; - - // nLength is in XCHARs - int nLength = this->GetLength(); - if (iStart < 0 || iStart > nLength) - return -1; - - // find first matching substring - PCXSTR psz = StringTraits::StringFindString(this->GetString() + iStart, pszSub); - - // return -1 for not found, distance from beginning otherwise - return (psz == nullptr) ? -1 : int(psz - this->GetString()); -} - -// Find the first occurrence of any of the characters in string 'pszCharSet' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::FindOneOf(PCXSTR pszCharSet) const -{ - PCXSTR psz = StringTraits::StringScanSet(this->GetString(), pszCharSet); - return (psz == nullptr) ? -1 : int(psz - this->GetString()); -} - -// Find the last occurrence of character 'ch' -template< typename BaseType, class StringTraits > -int CMStringT<BaseType, StringTraits>::ReverseFind(XCHAR ch) const -{ - // find last single character - PCXSTR psz = StringTraits::StringFindCharRev(this->GetString(), ch); - - // return -1 if not found, distance from beginning otherwise - return (psz == nullptr) ? -1 : int(psz - this->GetString()); -} - -// manipulation - -// Convert the string to uppercase -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::MakeUpper() -{ - int nLength = this->GetLength(); - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::StringUppercase(pszBuffer, nLength + 1); - this->ReleaseBufferSetLength(nLength); - - return *this; -} - -// Convert the string to lowercase -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::MakeLower() -{ - int nLength = this->GetLength(); - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::StringLowercase(pszBuffer, nLength + 1); - this->ReleaseBufferSetLength(nLength); - - return *this; -} - -// Reverse the string -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::MakeReverse() -{ - int nLength = this->GetLength(); - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::StringReverse(pszBuffer); - this->ReleaseBufferSetLength(nLength); - - return *this; -} - -// trimming - -// Remove all trailing whitespace -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimRight() -{ - // find beginning of trailing spaces by starting - // at beginning (DBCS aware) - - PCXSTR psz = this->GetString(); - PCXSTR pszLast = nullptr; - - while (*psz != 0) { - if (StringTraits::IsSpace(*psz)) { - if (pszLast == nullptr) - pszLast = psz; - } - else pszLast = nullptr; - - psz = StringTraits::CharNext(psz); - } - - if (pszLast != nullptr) { - // truncate at trailing space start - int iLast = int(pszLast - this->GetString()); - - this->Truncate(iLast); - } - - return *this; -} - -// Remove all leading whitespace -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimLeft() -{ - // find first non-space character - - PCXSTR psz = this->GetString(); - while (StringTraits::IsSpace(*psz)) - psz = StringTraits::CharNext(psz); - - if (psz != this->GetString()) { - // fix up data and length - int iFirst = int(psz - this->GetString()); - PXSTR pszBuffer = this->GetBuffer(this->GetLength()); - psz = pszBuffer + iFirst; - int nDataLength = this->GetLength() - iFirst; - memmove_s(pszBuffer, (this->GetLength() + 1)*sizeof(XCHAR), - psz, (nDataLength + 1)*sizeof(XCHAR)); - this->ReleaseBufferSetLength(nDataLength); - } - - return *this; -} - -// Remove all leading and trailing whitespace -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::Trim() -{ - return TrimRight().TrimLeft(); -} - -// Remove all leading and trailing occurrences of character 'chTarget' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::Trim(XCHAR chTarget) -{ - return TrimRight(chTarget).TrimLeft(chTarget); -} - -// Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::Trim(PCXSTR pszTargets) -{ - return TrimRight(pszTargets).TrimLeft(pszTargets); -} - -// trimming anything (either side) - -// Remove all trailing occurrences of character 'chTarget' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimRight(XCHAR chTarget) -{ - // find beginning of trailing matches - // by starting at beginning (DBCS aware) - - PCXSTR psz = this->GetString(); - PCXSTR pszLast = nullptr; - - while (*psz != 0) { - if (*psz == chTarget) { - if (pszLast == nullptr) - pszLast = psz; - } - else pszLast = nullptr; - - psz = StringTraits::CharNext(psz); - } - - if (pszLast != nullptr) { - // truncate at left-most matching character - int iLast = int(pszLast - this->GetString()); - this->Truncate(iLast); - } - - return *this; -} - -// Remove all trailing occurrences of any of the characters in string 'pszTargets' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimRight(PCXSTR pszTargets) -{ - // if we're not trimming anything, we're not doing any work - if ((pszTargets == nullptr) || (*pszTargets == 0)) { - return *this; - } - - // find beginning of trailing matches - // by starting at beginning (DBCS aware) - - PCXSTR psz = this->GetString(); - PCXSTR pszLast = nullptr; - - while (*psz != 0) { - if (StringTraits::StringFindChar(pszTargets, *psz) != nullptr) { - if (pszLast == nullptr) { - pszLast = psz; - } - } - else { - pszLast = nullptr; - } - psz = StringTraits::CharNext(psz); - } - - if (pszLast != nullptr) { - // truncate at left-most matching character - int iLast = int(pszLast - this->GetString()); - this->Truncate(iLast); - } - - return *this; -} - -// Remove all leading occurrences of character 'chTarget' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimLeft(XCHAR chTarget) -{ - // find first non-matching character - PCXSTR psz = this->GetString(); - - while (chTarget == *psz) { - psz = StringTraits::CharNext(psz); - } - - if (psz != this->GetString()) { - // fix up data and length - int iFirst = int(psz - this->GetString()); - PXSTR pszBuffer = this->GetBuffer(this->GetLength()); - psz = pszBuffer + iFirst; - int nDataLength = this->GetLength() - iFirst; - memmove_s(pszBuffer, (this->GetLength() + 1)*sizeof(XCHAR), - psz, (nDataLength + 1)*sizeof(XCHAR)); - this->ReleaseBufferSetLength(nDataLength); - } - - return *this; -} - -// Remove all leading occurrences of any of the characters in string 'pszTargets' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimLeft(PCXSTR pszTargets) -{ - // if we're not trimming anything, we're not doing any work - if ((pszTargets == nullptr) || (*pszTargets == 0)) { - return *this; - } - - PCXSTR psz = this->GetString(); - while ((*psz != 0) && (StringTraits::StringFindChar(pszTargets, *psz) != nullptr)) { - psz = StringTraits::CharNext(psz); - } - - if (psz != this->GetString()) { - // fix up data and length - int iFirst = int(psz - this->GetString()); - PXSTR pszBuffer = this->GetBuffer(this->GetLength()); - psz = pszBuffer + iFirst; - int nDataLength = this->GetLength() - iFirst; - memmove_s(pszBuffer, (this->GetLength() + 1)*sizeof(XCHAR), - psz, (nDataLength + 1)*sizeof(XCHAR)); - this->ReleaseBufferSetLength(nDataLength); - } - - return *this; -} - -// Convert the string to the OEM character set -template< typename BaseType, class StringTraits > -void CMStringT<BaseType, StringTraits>::AnsiToOem() -{ - int nLength = this->GetLength(); - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::ConvertToOem(pszBuffer, nLength + 1); - this->ReleaseBufferSetLength(nLength); -} - -// Convert the string to the ANSI character set -template< typename BaseType, class StringTraits > -void CMStringT<BaseType, StringTraits>::OemToAnsi() -{ - int nLength = this->GetLength(); - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::ConvertToAnsi(pszBuffer, nLength + 1); - this->ReleaseBufferSetLength(nLength); -} - -// Very simple sub-string extraction - -// Return the substring starting at index 'iFirst' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Mid(int iFirst) const -{ - return Mid(iFirst, this->GetLength() - iFirst); -} - -// Return the substring starting at index 'iFirst', with length 'nCount' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Mid(int iFirst, int nCount) const -{ - // nCount is in XCHARs - - // out-of-bounds requests return sensible things - if (iFirst < 0) - iFirst = 0; - if (nCount < 0) - nCount = 0; - - if ((iFirst + nCount) > this->GetLength()) - nCount = this->GetLength() - iFirst; - - if (iFirst > this->GetLength()) - nCount = 0; - - // optimize case of returning entire string - if ((iFirst == 0) && ((iFirst + nCount) == this->GetLength())) - return *this; - - return CMStringT(this->GetString() + iFirst, nCount); -} - -// Return the substring consisting of the rightmost 'nCount' characters -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Right(int nCount) const -{ - // nCount is in XCHARs - if (nCount < 0) - nCount = 0; - - int nLength = this->GetLength(); - if (nCount >= nLength) - return *this; - - return CMStringT(this->GetString() + nLength - nCount, nCount); -} - -// Return the substring consisting of the leftmost 'nCount' characters -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Left(int nCount) const -{ - // nCount is in XCHARs - if (nCount < 0) - nCount = 0; - - int nLength = this->GetLength(); - if (nCount >= nLength) - return *this; - - return CMStringT(this->GetString(), nCount); -} - -// Return the substring consisting of the leftmost characters in the set 'pszCharSet' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::SpanIncluding(PCXSTR pszCharSet) const -{ - return Left(StringTraits::StringSpanIncluding(this->GetString(), pszCharSet)); -} - -// Return the substring consisting of the leftmost characters not in the set 'pszCharSet' -template< typename BaseType, class StringTraits > -CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::SpanExcluding(PCXSTR pszCharSet) const -{ - return Left(StringTraits::StringSpanExcluding(this->GetString(), pszCharSet)); -} - -// Format data using format string 'pszFormat' -template< typename BaseType, class StringTraits > -typename CMStringT<BaseType, StringTraits>::PCXSTR CMStringT<BaseType, StringTraits>::Format(PCXSTR pszFormat, ...) -{ - va_list argList; - va_start(argList, pszFormat); - FormatV(pszFormat, argList); - va_end(argList); - return this->GetString(); -} - -// Append formatted data using format string 'pszFormat' -template< typename BaseType, class StringTraits > -typename CMStringT<BaseType, StringTraits>::PCXSTR CMStringT<BaseType, StringTraits>::AppendFormat(PCXSTR pszFormat, ...) -{ - va_list argList; - va_start(argList, pszFormat); - AppendFormatV(pszFormat, argList); - va_end(argList); - return this->GetString(); -} - -template< typename BaseType, class StringTraits > -void CMStringT<BaseType, StringTraits>::AppendFormatV(PCXSTR pszFormat, va_list args) -{ - int nCurrentLength = this->GetLength(); - int nAppendLength = StringTraits::GetFormattedLength(pszFormat, args); - PXSTR pszBuffer = this->GetBuffer(nCurrentLength + nAppendLength); - StringTraits::Format(pszBuffer + nCurrentLength, nAppendLength + 1, pszFormat, args); - this->ReleaseBufferSetLength(nCurrentLength + nAppendLength); -} - -template< typename BaseType, class StringTraits > -typename CMStringT<BaseType, StringTraits>::PCXSTR CMStringT<BaseType, StringTraits>::FormatV(PCXSTR pszFormat, va_list args) -{ - int nLength = StringTraits::GetFormattedLength(pszFormat, args); - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::Format(pszBuffer, nLength + 1, pszFormat, args); - this->ReleaseBufferSetLength(nLength); - return this->GetString(); -} - -// Set the string to the value of environment variable 'pszVar' -template< typename BaseType, class StringTraits > -BOOL CMStringT<BaseType, StringTraits>::GetEnvironmentVariable(PCXSTR pszVar) -{ - int nLength = StringTraits::GetEnvironmentVariable(pszVar, nullptr, 0); - BOOL bRetVal = FALSE; - - if (nLength == 0) - this->Empty(); - else { - PXSTR pszBuffer = this->GetBuffer(nLength); - StringTraits::GetEnvironmentVariable(pszVar, pszBuffer, nLength); - this->ReleaseBuffer(); - bRetVal = TRUE; - } - - return bRetVal; -} - -// Set the string to the value of environment variable 'pszVar' -template< typename BaseType, class StringTraits > -typename CMStringT<BaseType, StringTraits>::PXSTR CMStringT<BaseType, StringTraits>::Detach() const -{ - return StringTraits::MirCopy(CMStringT<BaseType, StringTraits>::GetString(), this->GetLength()); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, const CMStringT<BaseType, StringTraits>& str2) -{ - CMStringT<BaseType, StringTraits> strResult; - CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), str2, str2.GetLength()); - return strResult; -} - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, typename CMStringT<BaseType, StringTraits>::PCXSTR psz2) -{ - CMStringT<BaseType, StringTraits> strResult; - CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), psz2, CMStringT<BaseType, StringTraits>::StringLength(psz2)); - return strResult; -} - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(typename CMStringT<BaseType, StringTraits>::PCXSTR psz1, const CMStringT<BaseType, StringTraits>& str2) -{ - CMStringT<BaseType, StringTraits> strResult; - CMStringT<BaseType, StringTraits>::Concatenate(strResult, psz1, CMStringT<BaseType, StringTraits>::StringLength(psz1), str2, str2.GetLength()); - return strResult; -} - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, wchar_t ch2) -{ - CMStringT<BaseType, StringTraits> strResult; - typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch2); - CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1); - return strResult; -} - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, char ch2) -{ - CMStringT<BaseType, StringTraits> strResult; - typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch2); - CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1); - return strResult; -} - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(wchar_t ch1, const CMStringT<BaseType, StringTraits>& str2) -{ - CMStringT<BaseType, StringTraits> strResult; - typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch1); - CMStringT<BaseType, StringTraits>::Concatenate(strResult, &chTemp, 1, str2, str2.GetLength()); - return strResult; -} - -template< typename BaseType, class StringTraits > -MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(char ch1, const CMStringT<BaseType, StringTraits>& str2) -{ - CMStringT<BaseType, StringTraits> strResult; - typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch1); - CMStringT<BaseType, StringTraits>::Concatenate(strResult, &chTemp, 1, str2, str2.GetLength()); - return strResult; -} - -#endif // M_STRING_INL__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+#ifndef M_STRING_INL__
+
+#ifndef _MSC_VER
+ #include <algorithm>
+ #define __max std::max
+#endif
+
+template<typename BaseType>
+CMSimpleStringT<BaseType>::CMSimpleStringT()
+{
+ CMStringData* pData = mirstr_getNil();
+ Attach(pData);
+}
+
+template<typename BaseType>
+CMSimpleStringT<BaseType>::CMSimpleStringT(const CMSimpleStringT& strSrc)
+{
+ CMStringData* pSrcData = strSrc.GetData();
+ CMStringData* pNewData = CloneData(pSrcData);
+ Attach(pNewData);
+}
+
+template<typename BaseType>
+CMSimpleStringT<BaseType>::CMSimpleStringT(PCXSTR pszSrc)
+{
+ int nLength = StringLength(pszSrc);
+ CMStringData* pData = mirstr_allocate(nLength, sizeof(XCHAR));
+ if (pData != nullptr) {
+ Attach(pData);
+ SetLength(nLength);
+ CopyChars(m_pszData, nLength, pszSrc, nLength);
+ }
+}
+
+template<typename BaseType>
+CMSimpleStringT<BaseType>::CMSimpleStringT(const XCHAR* pchSrc, int nLength)
+{
+ CMStringData* pData = mirstr_allocate(nLength, sizeof(XCHAR));
+ if (pData != nullptr) {
+ Attach(pData);
+ SetLength(nLength);
+ CopyChars(m_pszData, nLength, pchSrc, nLength);
+ }
+}
+
+template<typename BaseType>
+CMSimpleStringT<BaseType>::~CMSimpleStringT()
+{
+ CMStringData* pData = GetData();
+ pData->Release();
+}
+
+template<typename BaseType>
+CMSimpleStringT<BaseType>& CMSimpleStringT<BaseType>::operator=(const CMSimpleStringT& strSrc)
+{
+ CMStringData* pSrcData = strSrc.GetData();
+ CMStringData* pOldData = GetData();
+ if (pSrcData != pOldData) {
+ if (pOldData->IsLocked())
+ SetString(strSrc.GetString(), strSrc.GetLength());
+ else {
+ CMStringData* pNewData = CloneData(pSrcData);
+ pOldData->Release();
+ Attach(pNewData);
+ }
+ }
+
+ return *this;
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Append(PCXSTR pszSrc)
+{
+ Append(pszSrc, StringLength(pszSrc));
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Append(PCXSTR pszSrc, int nLength)
+{
+ // See comment in SetString() about why we do this
+ UINT_PTR nOffset = UINT_PTR(pszSrc - GetString());
+
+ int nOldLength = GetLength();
+ if (nOldLength < 0) {
+ // protects from underflow
+ nOldLength = 0;
+ }
+
+ //Make sure we don't read pass end of the terminating NULL
+ int nSrcLength = StringLength(pszSrc);
+ nLength = nLength > nSrcLength ? nSrcLength : nLength;
+
+ int nNewLength = nOldLength + nLength;
+ PXSTR pszBuffer = GetBuffer(nNewLength);
+ if (nOffset <= UINT_PTR(nOldLength)) {
+ pszSrc = pszBuffer + nOffset;
+ // No need to call CopyCharsOverlapped, since the destination is
+ // beyond the end of the original buffer
+ }
+ CopyChars(pszBuffer + nOldLength, nLength, pszSrc, nLength);
+ ReleaseBufferSetLength(nNewLength);
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::AppendChar(XCHAR ch)
+{
+ UINT nOldLength = GetLength();
+ int nNewLength = nOldLength + 1;
+ PXSTR pszBuffer = GetBuffer(nNewLength);
+ pszBuffer[nOldLength] = ch;
+ ReleaseBufferSetLength(nNewLength);
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Append(const CMSimpleStringT<BaseType>& strSrc)
+{
+ Append(strSrc.GetString(), strSrc.GetLength());
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Empty()
+{
+ CMStringData* pOldData = GetData();
+ if (pOldData->nDataLength == 0)
+ return;
+
+ if (pOldData->IsLocked()) {
+ // Don't reallocate a locked buffer that's shrinking
+ SetLength(0);
+ }
+ else {
+ pOldData->Release();
+ CMStringData* pNewData = mirstr_getNil();
+ Attach(pNewData);
+ }
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::FreeExtra()
+{
+ CMStringData* pOldData = GetData();
+ int nLength = pOldData->nDataLength;
+ if (pOldData->nAllocLength == nLength)
+ return;
+
+ if (!pOldData->IsLocked()) { // Don't reallocate a locked buffer that's shrinking
+ CMStringData* pNewData = mirstr_allocate(nLength, sizeof(XCHAR));
+ if (pNewData == nullptr) {
+ SetLength(nLength);
+ return;
+ }
+
+ CopyChars(PXSTR(pNewData->data()), nLength, PCXSTR(pOldData->data()), nLength);
+
+ pOldData->Release();
+ Attach(pNewData);
+ SetLength(nLength);
+ }
+}
+
+template<typename BaseType>
+typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::GetBuffer()
+{
+ CMStringData* pData = GetData();
+ if (pData->IsShared())
+ Fork(pData->nDataLength);
+
+ return m_pszData;
+}
+
+template<typename BaseType>
+typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::GetBufferSetLength(int nLength)
+{
+ PXSTR pszBuffer = GetBuffer(nLength);
+ SetLength(nLength);
+
+ return pszBuffer;
+}
+
+template<typename BaseType>
+typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::LockBuffer()
+{
+ CMStringData* pData = GetData();
+ if (pData->IsShared()) {
+ Fork(pData->nDataLength);
+ pData = GetData(); // Do it again, because the fork might have changed it
+ }
+ pData->Lock();
+
+ return m_pszData;
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::UnlockBuffer()
+{
+ CMStringData* pData = GetData();
+ pData->Unlock();
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::ReleaseBuffer(int nNewLength)
+{
+ if (nNewLength == -1) {
+ int nAlloc = GetData()->nAllocLength;
+ nNewLength = StringLengthN(m_pszData, nAlloc);
+ }
+ SetLength(nNewLength);
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Truncate(int nNewLength)
+{
+ GetBuffer(nNewLength);
+ ReleaseBufferSetLength(nNewLength);
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::SetAt(int iChar, XCHAR ch)
+{
+ int nLength = GetLength();
+ PXSTR pszBuffer = GetBuffer();
+ pszBuffer[iChar] = ch;
+ ReleaseBufferSetLength(nLength);
+
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::SetString(PCXSTR pszSrc)
+{
+ SetString(pszSrc, StringLength(pszSrc));
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::SetString(PCXSTR pszSrc, int nLength)
+{
+ if (nLength == 0)
+ Empty();
+ else {
+ UINT nOldLength = GetLength();
+ UINT_PTR nOffset = pszSrc - GetString();
+
+ PXSTR pszBuffer = GetBuffer(nLength);
+ if (nOffset <= nOldLength)
+ CopyCharsOverlapped(pszBuffer, GetAllocLength(), pszBuffer + nOffset, nLength);
+ else
+ CopyChars(pszBuffer, GetAllocLength(), pszSrc, nLength);
+
+ ReleaseBufferSetLength(nLength);
+ }
+}
+
+template<typename BaseType>
+class CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType>& str1, const CMSimpleStringT<BaseType>& str2)
+{
+ CMSimpleStringT<BaseType> s;
+ Concatenate(s, str1, str1.GetLength(), str2, str2.GetLength());
+ return s;
+}
+
+template<typename BaseType>
+class CMSimpleStringT<BaseType> operator+(const CMSimpleStringT<BaseType>& str1, typename CMSimpleStringT<BaseType>::PCXSTR psz2)
+{
+ CMSimpleStringT<BaseType> s;
+ Concatenate(s, str1, str1.GetLength(), psz2, StringLength(psz2));
+ return s;
+}
+
+template<typename BaseType>
+CMSimpleStringT<BaseType> operator+(typename CMSimpleStringT<BaseType>::PCXSTR psz1, const CMSimpleStringT<BaseType>& str2)
+{
+ CMSimpleStringT<BaseType> s;
+ Concatenate(s, psz1, StringLength(psz1), str2, str2.GetLength());
+ return s;
+}
+
+template<typename BaseType>
+void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars)
+{
+ #pragma warning (push)
+ #pragma warning(disable : 4996)
+ memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
+ #pragma warning (pop)
+}
+
+template<typename BaseType>
+void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
+{
+ memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
+}
+
+template<typename BaseType>
+void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars)
+{
+ #pragma warning (push)
+ #pragma warning(disable : 4996)
+ memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
+ #pragma warning (pop)
+}
+
+template<typename BaseType>
+void MIR_SYSCALL CMSimpleStringT<BaseType>::CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
+{
+ memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
+}
+
+template<typename BaseType>
+int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLength(const char* psz)
+{
+ if (psz == nullptr)
+ return(0);
+
+ return (int(strlen(psz)));
+}
+
+template<typename BaseType>
+int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLength(const wchar_t* psz)
+{
+ if (psz == nullptr)
+ return 0;
+
+ return int(wcslen(psz));
+}
+
+template<typename BaseType>
+int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLengthN(const char* psz, size_t sizeInXChar)
+{
+ if (psz == nullptr)
+ return 0;
+
+ return int(strnlen(psz, sizeInXChar));
+}
+
+template<typename BaseType>
+int MIR_SYSCALL CMSimpleStringT<BaseType>::StringLengthN(const wchar_t* psz, size_t sizeInXChar)
+{
+ if (psz == nullptr)
+ return 0;
+
+ return int(wcsnlen(psz, sizeInXChar));
+}
+
+template<typename BaseType>
+void MIR_SYSCALL CMSimpleStringT<BaseType>::Concatenate(CMSimpleStringT<BaseType>& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2)
+{
+ int nNewLength = nLength1 + nLength2;
+ PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
+ CopyChars(pszBuffer, nLength1, psz1, nLength1);
+ CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
+ strResult.ReleaseBufferSetLength(nNewLength);
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Attach(CMStringData* pData)
+{
+ m_pszData = static_cast<PXSTR>(pData->data());
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Fork(int nLength)
+{
+ CMStringData* pOldData = GetData();
+ int nOldLength = pOldData->nDataLength;
+ CMStringData* pNewData = mirstr_allocate(nLength, sizeof(XCHAR));
+ if (pNewData != nullptr) {
+ int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength) + 1; // Copy '\0'
+ CopyChars(PXSTR(pNewData->data()), nCharsToCopy, PCXSTR(pOldData->data()), nCharsToCopy);
+ pNewData->nDataLength = nOldLength;
+ pOldData->Release();
+ Attach(pNewData);
+ }
+}
+
+template<typename BaseType>
+typename CMSimpleStringT<BaseType>::PXSTR CMSimpleStringT<BaseType>::PrepareWrite(int nLength)
+{
+ CMStringData* pOldData = GetData();
+ int nShared = 1 - pOldData->nRefs; // nShared < 0 means true, >= 0 means false
+ int nTooShort = pOldData->nAllocLength - nLength; // nTooShort < 0 means true, >= 0 means false
+ if ((nShared | nTooShort) < 0) // If either sign bit is set (i.e. either is less than zero), we need to copy data
+ PrepareWrite2(nLength);
+
+ return m_pszData;
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::PrepareWrite2(int nLength)
+{
+ CMStringData* pOldData = GetData();
+ if (pOldData->nDataLength > nLength)
+ nLength = pOldData->nDataLength;
+
+ if (pOldData->IsShared()) {
+ Fork(nLength);
+ }
+ else if (pOldData->nAllocLength < nLength) {
+ // Grow exponentially, until we hit 1K.
+ int nNewLength = pOldData->nAllocLength;
+ if (nNewLength > 1024)
+ nNewLength += 1024;
+ else
+ nNewLength *= 2;
+
+ if (nNewLength < nLength)
+ nNewLength = nLength;
+
+ Reallocate(nNewLength);
+ }
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::Reallocate(int nLength)
+{
+ CMStringData* pOldData = GetData();
+ if (pOldData->nAllocLength >= nLength || nLength <= 0)
+ return;
+
+ CMStringData* pNewData = mirstr_realloc(pOldData, nLength, sizeof(XCHAR));
+ if (pNewData != nullptr)
+ Attach(pNewData);
+}
+
+template<typename BaseType>
+void CMSimpleStringT<BaseType>::SetLength(int nLength)
+{
+ GetData()->nDataLength = nLength;
+ m_pszData[nLength] = 0;
+}
+
+template<typename BaseType>
+CMStringData* MIR_SYSCALL CMSimpleStringT<BaseType>::CloneData(CMStringData* pData)
+{
+ CMStringData* pNewData = nullptr;
+
+ if (!pData->IsLocked()) {
+ pNewData = pData;
+ pNewData->AddRef();
+ }
+
+ return pNewData;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT() :
+ CThisSimpleString()
+{
+}
+
+// Copy constructor
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(const CMStringT<BaseType, StringTraits>& strSrc) :
+ CThisSimpleString(strSrc)
+{
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(const XCHAR* pszSrc) :
+ CThisSimpleString()
+{
+ *this = pszSrc;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(CMStringDataFormat, const XCHAR* pszFormat, ...) :
+ CThisSimpleString()
+{
+ va_list args;
+ va_start(args, pszFormat);
+ FormatV(pszFormat, args);
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(const YCHAR* pszSrc) :
+ CThisSimpleString()
+{
+ *this = pszSrc;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(const unsigned char* pszSrc) :
+ CThisSimpleString()
+{
+ *this = reinterpret_cast<const char*>(pszSrc);
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(char ch, int nLength) :
+ CThisSimpleString()
+{
+ if (nLength > 0) {
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::FloodCharacters(XCHAR(ch), nLength, pszBuffer);
+ this->ReleaseBufferSetLength(nLength);
+ }
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(wchar_t ch, int nLength) :
+ CThisSimpleString()
+{
+ if (nLength > 0) {
+ //Convert ch to the BaseType
+ wchar_t pszCh[2] = { ch, 0 };
+ int nBaseTypeCharLen = 1;
+
+ if (ch != L'\0')
+ nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh);
+
+ XCHAR *buffBaseTypeChar = new XCHAR[nBaseTypeCharLen + 1];
+ StringTraits::ConvertToBaseType(buffBaseTypeChar, nBaseTypeCharLen + 1, pszCh, 1);
+ //allocate enough characters in String and flood (replicate) with the (converted character)*nLength
+ PXSTR pszBuffer = this->GetBuffer(nLength*nBaseTypeCharLen);
+ if (nBaseTypeCharLen == 1) //Optimization for a common case - wide char translates to 1 ansi/wide char.
+ StringTraits::FloodCharacters(buffBaseTypeChar[0], nLength, pszBuffer);
+ else {
+ XCHAR* p = pszBuffer;
+ for (int i = 0; i < nLength; i++) {
+ for (int j = 0; j < nBaseTypeCharLen; ++j) {
+ *p = buffBaseTypeChar[j];
+ ++p;
+ }
+ }
+ }
+ this->ReleaseBufferSetLength(nLength*nBaseTypeCharLen);
+ delete[] buffBaseTypeChar;
+ }
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(const XCHAR* pch, int nLength) :
+ CThisSimpleString(pch, nLength)
+{
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::CMStringT(const YCHAR* pch, int nLength) :
+ CThisSimpleString()
+{
+ if (nLength > 0) {
+ int nDestLength = StringTraits::GetBaseTypeLength(pch, nLength);
+ PXSTR pszBuffer = this->GetBuffer(nDestLength);
+ StringTraits::ConvertToBaseType(pszBuffer, nDestLength, pch, nLength);
+ this->ReleaseBufferSetLength(nDestLength);
+ }
+}
+
+// Destructor
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>::~CMStringT()
+{
+}
+
+// Assignment operators
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(const CMStringT& strSrc)
+{
+ CThisSimpleString::operator=(strSrc);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(PCXSTR pszSrc)
+{
+ CThisSimpleString::operator=(pszSrc);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(PCYSTR pszSrc)
+{
+ // nDestLength is in XCHARs
+ int nDestLength = (pszSrc != nullptr) ? StringTraits::GetBaseTypeLength(pszSrc) : 0;
+ if (nDestLength > 0) {
+ PXSTR pszBuffer = this->GetBuffer(nDestLength);
+ StringTraits::ConvertToBaseType(pszBuffer, nDestLength, pszSrc);
+ this->ReleaseBufferSetLength(nDestLength);
+ }
+ else this->Empty();
+
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(const unsigned char* pszSrc)
+{
+ return operator=(reinterpret_cast<const char*>(pszSrc));
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(char ch)
+{
+ char ach[2] = { ch, 0 };
+ return operator=(ach);
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator=(wchar_t ch)
+{
+ wchar_t ach[2] = { ch, 0 };
+ return operator=(ach);
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(const CMStringT& str)
+{
+ CThisSimpleString::operator+=(str);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(const CThisSimpleString& str)
+{
+ CThisSimpleString::operator+=(str);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(PCXSTR pszSrc)
+{
+ CThisSimpleString::operator+=(pszSrc);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(PCYSTR pszSrc)
+{
+ CMStringT str(pszSrc);
+ return operator+=(str);
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(char ch)
+{
+ CThisSimpleString::operator+=(ch);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(unsigned char ch)
+{
+ CThisSimpleString::operator+=(ch);
+ return *this;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::operator+=(wchar_t ch)
+{
+ CThisSimpleString::operator+=(ch);
+ return *this;
+}
+
+// Comparison
+
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Compare(PCXSTR psz) const
+{
+ return StringTraits::StringCompare(this->GetString(), psz);
+}
+
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::CompareNoCase(PCXSTR psz) const
+{
+ return StringTraits::StringCompareIgnore(this->GetString(), psz);
+}
+
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Collate(PCXSTR psz) const
+{
+ return StringTraits::StringCollate(this->GetString(), psz);
+}
+
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::CollateNoCase(PCXSTR psz) const
+{
+ return StringTraits::StringCollateIgnore(this->GetString(), psz);
+}
+
+// Advanced manipulation
+
+// Delete 'nCount' characters, starting at index 'iIndex'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Delete(int iIndex, int nCount)
+{
+ if (iIndex < 0)
+ iIndex = 0;
+
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if (nCount + iIndex > nLength)
+ nCount = nLength - iIndex;
+
+ if (nCount > 0) {
+ int nNewLength = nLength - nCount;
+ int nXCHARsToCopy = nLength - (iIndex + nCount) + 1;
+ PXSTR pszBuffer = this->GetBuffer();
+#if _MSC_VER >= 1400
+ memmove_s(pszBuffer + iIndex, nXCHARsToCopy*sizeof(XCHAR), pszBuffer + iIndex + nCount, nXCHARsToCopy*sizeof(XCHAR));
+#else
+ memmove(pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof(XCHAR));
+#endif
+ this->ReleaseBufferSetLength(nNewLength);
+ }
+
+ return this->GetLength();
+}
+
+// Insert character 'ch' before index 'iIndex'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Insert(int iIndex, XCHAR ch)
+{
+ if (iIndex < 0)
+ iIndex = 0;
+
+ if (iIndex > this->GetLength())
+ iIndex = this->GetLength();
+
+ int nNewLength = this->GetLength() + 1;
+
+ PXSTR pszBuffer = this->GetBuffer(nNewLength);
+
+ // move existing bytes down
+#if _MSC_VER >= 1400
+ memmove_s(pszBuffer + iIndex + 1, (nNewLength - iIndex)*sizeof(XCHAR), pszBuffer + iIndex, (nNewLength - iIndex)*sizeof(XCHAR));
+#else
+ memmove(pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof(XCHAR));
+#endif
+ pszBuffer[iIndex] = ch;
+
+ this->ReleaseBufferSetLength(nNewLength);
+ return nNewLength;
+}
+
+// Insert string 'psz' before index 'iIndex'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Insert(int iIndex, PCXSTR psz)
+{
+ if (iIndex < 0)
+ iIndex = 0;
+
+ if (iIndex > this->GetLength())
+ iIndex = this->GetLength();
+
+ // nInsertLength and nNewLength are in XCHARs
+ int nInsertLength = StringTraits::SafeStringLen(psz);
+ int nNewLength = this->GetLength();
+ if (nInsertLength > 0) {
+ nNewLength += nInsertLength;
+
+ PXSTR pszBuffer = this->GetBuffer(nNewLength);
+ // move existing bytes down
+#if _MSC_VER >= 1400
+ memmove_s(pszBuffer + iIndex + nInsertLength, (nNewLength - iIndex - nInsertLength + 1)*sizeof(XCHAR), pszBuffer + iIndex, (nNewLength - iIndex - nInsertLength + 1)*sizeof(XCHAR));
+ memcpy_s(pszBuffer + iIndex, nInsertLength*sizeof(XCHAR), psz, nInsertLength*sizeof(XCHAR));
+#else
+ memmove(pszBuffer+iIndex+nInsertLength, pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof(XCHAR));
+ memcpy(pszBuffer+iIndex, psz, nInsertLength*sizeof(XCHAR));
+#endif
+ this->ReleaseBufferSetLength(nNewLength);
+ }
+
+ return nNewLength;
+}
+
+// Replace all occurrences of character 'chOld' with character 'chNew'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Replace(XCHAR chOld, XCHAR chNew)
+{
+ int nCount = 0;
+
+ // short-circuit the nop case
+ if (chOld != chNew) {
+ // otherwise modify each character that matches in the string
+ bool bCopied = false;
+ PXSTR pszBuffer = const_cast<PXSTR>(this->GetString()); // We don't actually write to pszBuffer until we've called GetBuffer().
+
+ int nLength = this->GetLength();
+ int iChar = 0;
+ while (iChar < nLength) {
+ // replace instances of the specified character only
+ if (pszBuffer[iChar] == chOld) {
+ if (!bCopied) {
+ bCopied = true;
+ pszBuffer = this->GetBuffer(nLength);
+ }
+ pszBuffer[iChar] = chNew;
+ nCount++;
+ }
+ iChar = int(StringTraits::CharNext(pszBuffer + iChar) - pszBuffer);
+ }
+
+ if (bCopied)
+ this->ReleaseBufferSetLength(nLength);
+ }
+
+ return nCount;
+}
+
+// Replace all occurrences of string 'pszOld' with string 'pszNew'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Replace(PCXSTR pszOld, PCXSTR pszNew)
+{
+ // can't have empty or NULL lpszOld
+
+ // nSourceLen is in XCHARs
+ int nSourceLen = StringTraits::SafeStringLen(pszOld);
+ if (nSourceLen == 0)
+ return 0;
+ // nReplacementLen is in XCHARs
+ int nReplacementLen = StringTraits::SafeStringLen(pszNew);
+
+ // loop once to figure out the size of the result string
+ int nCount = 0;
+ {
+ PCXSTR pszStart = this->GetString();
+ PCXSTR pszEnd = pszStart + this->GetLength();
+ while (pszStart < pszEnd) {
+ PCXSTR pszTarget;
+ while ((pszTarget = StringTraits::StringFindString(pszStart, pszOld)) != nullptr) {
+ nCount++;
+ pszStart = pszTarget + nSourceLen;
+ }
+ pszStart += StringTraits::SafeStringLen(pszStart) + 1;
+ }
+ }
+
+ // if any changes were made, make them
+ if (nCount > 0) {
+ // if the buffer is too small, just
+ // allocate a new buffer (slow but sure)
+ int nOldLength = this->GetLength();
+ int nNewLength = nOldLength + (nReplacementLen - nSourceLen)*nCount;
+
+ PXSTR pszBuffer = this->GetBuffer(__max(nNewLength, nOldLength));
+
+ PXSTR pszStart = pszBuffer;
+ PXSTR pszEnd = pszStart + nOldLength;
+
+ // loop again to actually do the work
+ while (pszStart < pszEnd) {
+ PXSTR pszTarget;
+ while ((pszTarget = StringTraits::StringFindString(pszStart, pszOld)) != nullptr) {
+ int nBalance = nOldLength - int(pszTarget - pszBuffer + nSourceLen);
+ memmove_s(pszTarget + nReplacementLen, nBalance*sizeof(XCHAR),
+ pszTarget + nSourceLen, nBalance*sizeof(XCHAR));
+ memcpy_s(pszTarget, nReplacementLen*sizeof(XCHAR),
+ pszNew, nReplacementLen*sizeof(XCHAR));
+ pszStart = pszTarget + nReplacementLen;
+ pszTarget[nReplacementLen + nBalance] = 0;
+ nOldLength += (nReplacementLen - nSourceLen);
+ }
+ pszStart += StringTraits::SafeStringLen(pszStart) + 1;
+ }
+ this->ReleaseBufferSetLength(nNewLength);
+ }
+
+ return nCount;
+}
+
+// Remove all occurrences of character 'chRemove'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Remove(XCHAR chRemove)
+{
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+
+ PXSTR pszSource = pszBuffer;
+ PXSTR pszDest = pszBuffer;
+ PXSTR pszEnd = pszBuffer + nLength;
+
+ while (pszSource < pszEnd) {
+ PXSTR pszNewSource = StringTraits::CharNext(pszSource);
+ if (*pszSource != chRemove) {
+ // Copy the source to the destination. Remember to copy all bytes of an MBCS character
+ size_t NewSourceGap = (pszNewSource - pszSource);
+ PXSTR pszNewDest = pszDest + NewSourceGap;
+ for (size_t i = 0; pszDest != pszNewDest && i < NewSourceGap; i++) {
+ *pszDest = *pszSource;
+ pszSource++;
+ pszDest++;
+ }
+ }
+ pszSource = pszNewSource;
+ }
+ *pszDest = 0;
+ int nCount = int(pszSource - pszDest);
+ this->ReleaseBufferSetLength(nLength - nCount);
+
+ return nCount;
+}
+
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Tokenize(PCXSTR pszTokens, int& iStart) const
+{
+ if ((pszTokens == nullptr) || (*pszTokens == (XCHAR)0)) {
+ if (iStart < this->GetLength())
+ return CMStringT(this->GetString() + iStart);
+ }
+ else {
+ PCXSTR pszPlace = this->GetString() + iStart;
+ PCXSTR pszEnd = this->GetString() + this->GetLength();
+ if (pszPlace < pszEnd) {
+ int nIncluding = StringTraits::StringSpanIncluding(pszPlace, pszTokens);
+
+ if ((pszPlace + nIncluding) < pszEnd) {
+ pszPlace += nIncluding;
+ int nExcluding = StringTraits::StringSpanExcluding(pszPlace, pszTokens);
+
+ int iFrom = iStart + nIncluding;
+ int nUntil = nExcluding;
+ iStart = iFrom + nUntil + 1;
+
+ return Mid(iFrom, nUntil);
+ }
+ }
+ }
+
+ // return empty string, done tokenizing
+ iStart = -1;
+
+ return CMStringT();
+}
+
+// find routines
+
+// Find the first occurrence of character 'ch', starting at index 'iStart'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Find(XCHAR ch, int iStart) const
+{
+ // nLength is in XCHARs
+ int nLength = this->GetLength();
+ if (iStart < 0 || iStart >= nLength)
+ return -1;
+
+ // find first single character
+ PCXSTR psz = StringTraits::StringFindChar(this->GetString() + iStart, ch);
+
+ // return -1 if not found and index otherwise
+ return (psz == nullptr) ? -1 : int(psz - this->GetString());
+}
+
+// look for a specific sub-string
+
+// Find the first occurrence of string 'pszSub', starting at index 'iStart'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::Find(PCXSTR pszSub, int iStart) const
+{
+ // iStart is in XCHARs
+ if (pszSub == nullptr)
+ return -1;
+
+ // nLength is in XCHARs
+ int nLength = this->GetLength();
+ if (iStart < 0 || iStart > nLength)
+ return -1;
+
+ // find first matching substring
+ PCXSTR psz = StringTraits::StringFindString(this->GetString() + iStart, pszSub);
+
+ // return -1 for not found, distance from beginning otherwise
+ return (psz == nullptr) ? -1 : int(psz - this->GetString());
+}
+
+// Find the first occurrence of any of the characters in string 'pszCharSet'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::FindOneOf(PCXSTR pszCharSet) const
+{
+ PCXSTR psz = StringTraits::StringScanSet(this->GetString(), pszCharSet);
+ return (psz == nullptr) ? -1 : int(psz - this->GetString());
+}
+
+// Find the last occurrence of character 'ch'
+template< typename BaseType, class StringTraits >
+int CMStringT<BaseType, StringTraits>::ReverseFind(XCHAR ch) const
+{
+ // find last single character
+ PCXSTR psz = StringTraits::StringFindCharRev(this->GetString(), ch);
+
+ // return -1 if not found, distance from beginning otherwise
+ return (psz == nullptr) ? -1 : int(psz - this->GetString());
+}
+
+// manipulation
+
+// Convert the string to uppercase
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::MakeUpper()
+{
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::StringUppercase(pszBuffer, nLength + 1);
+ this->ReleaseBufferSetLength(nLength);
+
+ return *this;
+}
+
+// Convert the string to lowercase
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::MakeLower()
+{
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::StringLowercase(pszBuffer, nLength + 1);
+ this->ReleaseBufferSetLength(nLength);
+
+ return *this;
+}
+
+// Reverse the string
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::MakeReverse()
+{
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::StringReverse(pszBuffer);
+ this->ReleaseBufferSetLength(nLength);
+
+ return *this;
+}
+
+// trimming
+
+// Remove all trailing whitespace
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimRight()
+{
+ // find beginning of trailing spaces by starting
+ // at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = nullptr;
+
+ while (*psz != 0) {
+ if (StringTraits::IsSpace(*psz)) {
+ if (pszLast == nullptr)
+ pszLast = psz;
+ }
+ else pszLast = nullptr;
+
+ psz = StringTraits::CharNext(psz);
+ }
+
+ if (pszLast != nullptr) {
+ // truncate at trailing space start
+ int iLast = int(pszLast - this->GetString());
+
+ this->Truncate(iLast);
+ }
+
+ return *this;
+}
+
+// Remove all leading whitespace
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimLeft()
+{
+ // find first non-space character
+
+ PCXSTR psz = this->GetString();
+ while (StringTraits::IsSpace(*psz))
+ psz = StringTraits::CharNext(psz);
+
+ if (psz != this->GetString()) {
+ // fix up data and length
+ int iFirst = int(psz - this->GetString());
+ PXSTR pszBuffer = this->GetBuffer(this->GetLength());
+ psz = pszBuffer + iFirst;
+ int nDataLength = this->GetLength() - iFirst;
+ memmove_s(pszBuffer, (this->GetLength() + 1)*sizeof(XCHAR),
+ psz, (nDataLength + 1)*sizeof(XCHAR));
+ this->ReleaseBufferSetLength(nDataLength);
+ }
+
+ return *this;
+}
+
+// Remove all leading and trailing whitespace
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::Trim()
+{
+ return TrimRight().TrimLeft();
+}
+
+// Remove all leading and trailing occurrences of character 'chTarget'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::Trim(XCHAR chTarget)
+{
+ return TrimRight(chTarget).TrimLeft(chTarget);
+}
+
+// Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::Trim(PCXSTR pszTargets)
+{
+ return TrimRight(pszTargets).TrimLeft(pszTargets);
+}
+
+// trimming anything (either side)
+
+// Remove all trailing occurrences of character 'chTarget'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimRight(XCHAR chTarget)
+{
+ // find beginning of trailing matches
+ // by starting at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = nullptr;
+
+ while (*psz != 0) {
+ if (*psz == chTarget) {
+ if (pszLast == nullptr)
+ pszLast = psz;
+ }
+ else pszLast = nullptr;
+
+ psz = StringTraits::CharNext(psz);
+ }
+
+ if (pszLast != nullptr) {
+ // truncate at left-most matching character
+ int iLast = int(pszLast - this->GetString());
+ this->Truncate(iLast);
+ }
+
+ return *this;
+}
+
+// Remove all trailing occurrences of any of the characters in string 'pszTargets'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimRight(PCXSTR pszTargets)
+{
+ // if we're not trimming anything, we're not doing any work
+ if ((pszTargets == nullptr) || (*pszTargets == 0)) {
+ return *this;
+ }
+
+ // find beginning of trailing matches
+ // by starting at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = nullptr;
+
+ while (*psz != 0) {
+ if (StringTraits::StringFindChar(pszTargets, *psz) != nullptr) {
+ if (pszLast == nullptr) {
+ pszLast = psz;
+ }
+ }
+ else {
+ pszLast = nullptr;
+ }
+ psz = StringTraits::CharNext(psz);
+ }
+
+ if (pszLast != nullptr) {
+ // truncate at left-most matching character
+ int iLast = int(pszLast - this->GetString());
+ this->Truncate(iLast);
+ }
+
+ return *this;
+}
+
+// Remove all leading occurrences of character 'chTarget'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimLeft(XCHAR chTarget)
+{
+ // find first non-matching character
+ PCXSTR psz = this->GetString();
+
+ while (chTarget == *psz) {
+ psz = StringTraits::CharNext(psz);
+ }
+
+ if (psz != this->GetString()) {
+ // fix up data and length
+ int iFirst = int(psz - this->GetString());
+ PXSTR pszBuffer = this->GetBuffer(this->GetLength());
+ psz = pszBuffer + iFirst;
+ int nDataLength = this->GetLength() - iFirst;
+ memmove_s(pszBuffer, (this->GetLength() + 1)*sizeof(XCHAR),
+ psz, (nDataLength + 1)*sizeof(XCHAR));
+ this->ReleaseBufferSetLength(nDataLength);
+ }
+
+ return *this;
+}
+
+// Remove all leading occurrences of any of the characters in string 'pszTargets'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits>& CMStringT<BaseType, StringTraits>::TrimLeft(PCXSTR pszTargets)
+{
+ // if we're not trimming anything, we're not doing any work
+ if ((pszTargets == nullptr) || (*pszTargets == 0)) {
+ return *this;
+ }
+
+ PCXSTR psz = this->GetString();
+ while ((*psz != 0) && (StringTraits::StringFindChar(pszTargets, *psz) != nullptr)) {
+ psz = StringTraits::CharNext(psz);
+ }
+
+ if (psz != this->GetString()) {
+ // fix up data and length
+ int iFirst = int(psz - this->GetString());
+ PXSTR pszBuffer = this->GetBuffer(this->GetLength());
+ psz = pszBuffer + iFirst;
+ int nDataLength = this->GetLength() - iFirst;
+ memmove_s(pszBuffer, (this->GetLength() + 1)*sizeof(XCHAR),
+ psz, (nDataLength + 1)*sizeof(XCHAR));
+ this->ReleaseBufferSetLength(nDataLength);
+ }
+
+ return *this;
+}
+
+// Convert the string to the OEM character set
+template< typename BaseType, class StringTraits >
+void CMStringT<BaseType, StringTraits>::AnsiToOem()
+{
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::ConvertToOem(pszBuffer, nLength + 1);
+ this->ReleaseBufferSetLength(nLength);
+}
+
+// Convert the string to the ANSI character set
+template< typename BaseType, class StringTraits >
+void CMStringT<BaseType, StringTraits>::OemToAnsi()
+{
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::ConvertToAnsi(pszBuffer, nLength + 1);
+ this->ReleaseBufferSetLength(nLength);
+}
+
+// Very simple sub-string extraction
+
+// Return the substring starting at index 'iFirst'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Mid(int iFirst) const
+{
+ return Mid(iFirst, this->GetLength() - iFirst);
+}
+
+// Return the substring starting at index 'iFirst', with length 'nCount'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Mid(int iFirst, int nCount) const
+{
+ // nCount is in XCHARs
+
+ // out-of-bounds requests return sensible things
+ if (iFirst < 0)
+ iFirst = 0;
+ if (nCount < 0)
+ nCount = 0;
+
+ if ((iFirst + nCount) > this->GetLength())
+ nCount = this->GetLength() - iFirst;
+
+ if (iFirst > this->GetLength())
+ nCount = 0;
+
+ // optimize case of returning entire string
+ if ((iFirst == 0) && ((iFirst + nCount) == this->GetLength()))
+ return *this;
+
+ return CMStringT(this->GetString() + iFirst, nCount);
+}
+
+// Return the substring consisting of the rightmost 'nCount' characters
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Right(int nCount) const
+{
+ // nCount is in XCHARs
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if (nCount >= nLength)
+ return *this;
+
+ return CMStringT(this->GetString() + nLength - nCount, nCount);
+}
+
+// Return the substring consisting of the leftmost 'nCount' characters
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::Left(int nCount) const
+{
+ // nCount is in XCHARs
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if (nCount >= nLength)
+ return *this;
+
+ return CMStringT(this->GetString(), nCount);
+}
+
+// Return the substring consisting of the leftmost characters in the set 'pszCharSet'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::SpanIncluding(PCXSTR pszCharSet) const
+{
+ return Left(StringTraits::StringSpanIncluding(this->GetString(), pszCharSet));
+}
+
+// Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
+template< typename BaseType, class StringTraits >
+CMStringT<BaseType, StringTraits> CMStringT<BaseType, StringTraits>::SpanExcluding(PCXSTR pszCharSet) const
+{
+ return Left(StringTraits::StringSpanExcluding(this->GetString(), pszCharSet));
+}
+
+// Format data using format string 'pszFormat'
+template< typename BaseType, class StringTraits >
+typename CMStringT<BaseType, StringTraits>::PCXSTR CMStringT<BaseType, StringTraits>::Format(PCXSTR pszFormat, ...)
+{
+ va_list argList;
+ va_start(argList, pszFormat);
+ FormatV(pszFormat, argList);
+ va_end(argList);
+ return this->GetString();
+}
+
+// Append formatted data using format string 'pszFormat'
+template< typename BaseType, class StringTraits >
+typename CMStringT<BaseType, StringTraits>::PCXSTR CMStringT<BaseType, StringTraits>::AppendFormat(PCXSTR pszFormat, ...)
+{
+ va_list argList;
+ va_start(argList, pszFormat);
+ AppendFormatV(pszFormat, argList);
+ va_end(argList);
+ return this->GetString();
+}
+
+template< typename BaseType, class StringTraits >
+void CMStringT<BaseType, StringTraits>::AppendFormatV(PCXSTR pszFormat, va_list args)
+{
+ int nCurrentLength = this->GetLength();
+ int nAppendLength = StringTraits::GetFormattedLength(pszFormat, args);
+ PXSTR pszBuffer = this->GetBuffer(nCurrentLength + nAppendLength);
+ StringTraits::Format(pszBuffer + nCurrentLength, nAppendLength + 1, pszFormat, args);
+ this->ReleaseBufferSetLength(nCurrentLength + nAppendLength);
+}
+
+template< typename BaseType, class StringTraits >
+typename CMStringT<BaseType, StringTraits>::PCXSTR CMStringT<BaseType, StringTraits>::FormatV(PCXSTR pszFormat, va_list args)
+{
+ int nLength = StringTraits::GetFormattedLength(pszFormat, args);
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::Format(pszBuffer, nLength + 1, pszFormat, args);
+ this->ReleaseBufferSetLength(nLength);
+ return this->GetString();
+}
+
+// Set the string to the value of environment variable 'pszVar'
+template< typename BaseType, class StringTraits >
+BOOL CMStringT<BaseType, StringTraits>::GetEnvironmentVariable(PCXSTR pszVar)
+{
+ int nLength = StringTraits::GetEnvironmentVariable(pszVar, nullptr, 0);
+ BOOL bRetVal = FALSE;
+
+ if (nLength == 0)
+ this->Empty();
+ else {
+ PXSTR pszBuffer = this->GetBuffer(nLength);
+ StringTraits::GetEnvironmentVariable(pszVar, pszBuffer, nLength);
+ this->ReleaseBuffer();
+ bRetVal = TRUE;
+ }
+
+ return bRetVal;
+}
+
+// Set the string to the value of environment variable 'pszVar'
+template< typename BaseType, class StringTraits >
+typename CMStringT<BaseType, StringTraits>::PXSTR CMStringT<BaseType, StringTraits>::Detach() const
+{
+ return StringTraits::MirCopy(CMStringT<BaseType, StringTraits>::GetString(), this->GetLength());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, const CMStringT<BaseType, StringTraits>& str2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), str2, str2.GetLength());
+ return strResult;
+}
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, typename CMStringT<BaseType, StringTraits>::PCXSTR psz2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), psz2, CMStringT<BaseType, StringTraits>::StringLength(psz2));
+ return strResult;
+}
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(typename CMStringT<BaseType, StringTraits>::PCXSTR psz1, const CMStringT<BaseType, StringTraits>& str2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, psz1, CMStringT<BaseType, StringTraits>::StringLength(psz1), str2, str2.GetLength());
+ return strResult;
+}
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, wchar_t ch2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch2);
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1);
+ return strResult;
+}
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(const CMStringT<BaseType, StringTraits>& str1, char ch2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch2);
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1);
+ return strResult;
+}
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(wchar_t ch1, const CMStringT<BaseType, StringTraits>& str2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch1);
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, &chTemp, 1, str2, str2.GetLength());
+ return strResult;
+}
+
+template< typename BaseType, class StringTraits >
+MIR_CORE_EXPORT CMStringT<BaseType, StringTraits> CALLBACK operator+(char ch1, const CMStringT<BaseType, StringTraits>& str2)
+{
+ CMStringT<BaseType, StringTraits> strResult;
+ typename CMStringT<BaseType, StringTraits>::XCHAR chTemp = typename CMStringT<BaseType, StringTraits>::XCHAR(ch1);
+ CMStringT<BaseType, StringTraits>::Concatenate(strResult, &chTemp, 1, str2, str2.GetLength());
+ return strResult;
+}
+
+#endif // M_STRING_INL__
diff --git a/include/m_system.h b/include/m_system.h index 706820f8fc..2c00e048f2 100644 --- a/include/m_system.h +++ b/include/m_system.h @@ -1,662 +1,662 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#ifndef M_SYSTEM_H__ -#define M_SYSTEM_H__ 1 - -#ifndef MIRANDANAME - #define MIRANDANAME "Miranda NG" -#endif -#ifndef MIRANDACLASS - #define MIRANDACLASS "Miranda" -#endif - -// set the default compatibility lever for Miranda 0.4.x -#ifndef MIRANDA_VER - #define MIRANDA_VER 0x0A00 -#endif - -#ifdef _MSC_VER - #pragma warning(disable:4244 4245) -#endif - -#define NEWSTR_ALLOCA(A) (A == NULL)?NULL:strcpy((char*)alloca(strlen(A)+1), A) -#define NEWWSTR_ALLOCA(A) ((A==NULL)?NULL:wcscpy((wchar_t*)alloca(sizeof(wchar_t)*(wcslen(A)+1)),A)) - -#include <m_core.h> - -// miranda/system/modulesloaded -// called after all modules have been successfully initialised -// wParam = lParam = 0 -// used to resolve double-dependencies in the module load order -#define ME_SYSTEM_MODULESLOADED "Miranda/System/ModulesLoaded" - -// miranda/system/shutdown event -// called just before the application terminates -// the database is still guaranteed to be running during this hook. -// wParam = lParam = 0 -#define ME_SYSTEM_SHUTDOWN "Miranda/System/Shutdown" - -// restarts miranda (0.8+) -// wParam = 0 or 1. 1 - restart with current profile, 0 - restart in default profile or profile manager -// lParam = (wchar_t*)path to a new miranda32.exe binary or NULL to use current -#define MS_SYSTEM_RESTART "Miranda/System/Restart" - -// miranda/system/oktoexit event -// called before the app goes into shutdown routine to make sure everyone is -// happy to exit -// wParam = lParam = 0 -// return nonzero to stop the exit cycle -#define ME_SYSTEM_OKTOEXIT "Miranda/System/OkToExitEvent" - -// gets the version number of Miranda encoded as a uint32_t -// returns the version number, encoded as one version per byte, therefore -// version 1.2.3.10 is 0x0102030a -EXTERN_C MIR_APP_DLL(uint32_t) Miranda_GetVersion(void); - -// gets the version number of Miranda encoded as four WORDs v0.92.2+ -// returns the version number, encoded as one version per word, therefore -// version 1.2.3.3210 is 0x0001, 0x0002, 0x0003, 0x0C8a -typedef uint16_t MFileVersion[4]; -EXTERN_C MIR_APP_DLL(void) Miranda_GetFileVersion(MFileVersion*); - -// gets the version of Miranda encoded as text -// cch is the size of the buffer pointed to by pszVersion, in bytes -// may return a build qualifier, such as "0.1.0.1 alpha" -// returns 0 on success, nonzero on failure -EXTERN_C MIR_APP_DLL(void) Miranda_GetVersionText(char *pDest, size_t cbSize); - -// returns a system window that can handle global timers -// a usual practice is to use a unique pointer as a timer id -EXTERN_C MIR_APP_DLL(class CDlgBase *) Miranda_GetSystemWindow(); - -// Adds an event to the list to be checked in the main message loop -// when a handle gets triggered, an appopriate stub gets called -typedef void (CALLBACK *MWaitableStub)(void); -typedef void (CALLBACK *MWaitableStubEx)(void*); - -EXTERN_C MIR_CORE_DLL(void) Miranda_WaitOnHandle(MWaitableStub pFunc, HANDLE hEvent = nullptr); -EXTERN_C MIR_CORE_DLL(void) Miranda_WaitOnHandleEx(MWaitableStubEx pFunc, void *pInfo); - -// wParam = 0 (ignored) -// lParam = 0 (ignored) -// -// This hook is fired just before the thread unwind stack is used, -// it allows MT plugins to shutdown threads if they have any special -// processing to do, etc. -#define ME_SYSTEM_PRESHUTDOWN "Miranda/System/PShutdown" - -// Returns true when Miranda has got WM_QUIT and is in the process of shutting down -EXTERN_C MIR_CORE_DLL(bool) Miranda_IsTerminated(void); - -// Enables termination flag -EXTERN_C MIR_CORE_DLL(void) Miranda_SetTerminated(void); - -// Check if everyone is happy to exit -// if everyone acknowleges OK to exit then returns true, otherwise false -EXTERN_C MIR_APP_DLL(bool) Miranda_OkToExit(void); - -// Used by contact lists inside CloseAction -// Waits for a permission to exit and destroys contact list -EXTERN_C MIR_APP_DLL(void) Miranda_Close(void); - -// returns the last window tick where a monitored event was seen, currently WM_CHAR/WM_MOUSEMOVE -EXTERN_C MIR_CORE_DLL(uint32_t) Miranda_GetIdle(void); - -/////////////////////////////////////////////////////////////////////////////// - -#if defined(__cplusplus) - -#ifndef M_STRING_H__ - #include <m_string.h> -#endif - -/////////////////////////////////////////////////////////////////////////////// -// general lists' templates - -struct MIR_CORE_EXPORT MNonCopyable -{ - __inline MNonCopyable() {} - - MNonCopyable(const MNonCopyable &) = delete; - MNonCopyable &operator=(const MNonCopyable &) = delete; -}; - -/////////////////////////////////////////////////////////////////////////////// -// mir_ptr - automatic pointer for buffers, allocated using mir_alloc/mir_calloc - -template<class T> class mir_ptr -{ -protected: - T *data; - -public: - __inline explicit mir_ptr() : data(nullptr) {} - __inline explicit mir_ptr(T *_p) : data(_p) {} - __inline ~mir_ptr() { mir_free(data); } - __inline T *get() const { return data; } - __inline T *operator=(T *_p) { if (data) mir_free(data); data = _p; return data; } - __inline T *operator->() const { return data; } - __inline operator T *() const { return data; } - __inline operator INT_PTR() const { return (INT_PTR)data; } - __inline T *detach() { T *res = data; data = nullptr; return res; } -}; - -typedef mir_ptr<char> ptrA; -typedef mir_ptr<wchar_t> ptrW; - -/////////////////////////////////////////////////////////////////////////////// -// mir_cs - simple wrapper for the critical sections - -class MIR_CORE_EXPORT mir_cs : public MNonCopyable -{ - #ifdef _MSC_VER - CRITICAL_SECTION m_cs; - #endif - -public: - mir_cs(); - ~mir_cs(); - - void Lock(); - void Unlock(); -}; - -/////////////////////////////////////////////////////////////////////////////// -// mir_cslock - simple locker for the critical sections - -class mir_cslock : public MNonCopyable -{ - mir_cs &cs; - -public: - __inline mir_cslock(mir_cs &_cs) : cs(_cs) { cs.Lock(); } - __inline ~mir_cslock() { cs.Unlock(); } -}; - -class mir_cslockfull : public MNonCopyable -{ - mir_cs &cs; - bool bIsLocked = false; - -public: - __inline void lock() { bIsLocked = true; cs.Lock(); } - __inline void unlock() { bIsLocked = false; cs.Unlock(); } - - __inline mir_cslockfull(mir_cs &_cs) : cs(_cs) { lock(); } - __inline ~mir_cslockfull() { if (bIsLocked) unlock(); } -}; - -////////////////////////////////////////////////////////////////////////////// -//pass_ptrA, pass_ptrW and pass_ptrT - automatic pointer for passwords - -class pass_ptrA : public mir_ptr<char> -{ -public: - __inline explicit pass_ptrA() : mir_ptr() {} - __inline explicit pass_ptrA(char *_p) : mir_ptr(_p) {} - __inline ~pass_ptrA() { zero(); } - __inline char *operator=(char *_p) { zero(); return mir_ptr::operator=(_p); } - __inline void zero() - { - if (data) SecureZeroMemory(data, mir_strlen(data)); - } -}; - -class pass_ptrW : public mir_ptr<wchar_t> -{ -public: - __inline explicit pass_ptrW() : mir_ptr() {} - __inline explicit pass_ptrW(wchar_t *_p) : mir_ptr(_p) {} - __inline ~pass_ptrW() { zero(); } - __inline wchar_t *operator=(wchar_t *_p) { zero(); return mir_ptr::operator=(_p); } - __inline void zero() - { - if (data) SecureZeroMemory(data, mir_wstrlen(data) * sizeof(wchar_t)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// basic class for classes that should be cleared inside new() - -class MZeroedObject -{ -public: - __inline void *operator new(size_t size) - { - return calloc(1, size); - } - - __inline void operator delete(void *p) - { - free(p); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// general lists' templates - -#define NumericKeySortT -1 -#define HandleKeySortT -2 -#define PtrKeySortT -3 - -template<class T> struct LIST -{ - typedef int (*FTSortFunc)(const T *p1, const T *p2); - - __inline LIST(int aincr, FTSortFunc afunc = nullptr) - { - memset(this, 0, sizeof(*this)); - increment = aincr; - sortFunc = afunc; - } - - __inline LIST(int aincr, INT_PTR id) - { - memset(this, 0, sizeof(*this)); - increment = aincr; - sortFunc = FTSortFunc(id); - } - - __inline LIST(const LIST &x) - { - items = nullptr; - List_Copy((SortedList *)&x, (SortedList *)this, sizeof(T)); - } - - __inline LIST &operator = (const LIST &x) - { - destroy(); - List_Copy((SortedList *)&x, (SortedList *)this, sizeof(T)); - return *this; - } - - __inline ~LIST() - { - destroy(); - } - - __inline T *operator[](int idx) const { return (idx >= 0 && idx < count) ? items[idx] : nullptr; } - __inline int getCount(void) const { return count; } - __inline T **getArray(void) const { return items; } - - __inline int getIndex(T *p) const - { - int idx; - return (!List_GetIndex((SortedList *)this, p, &idx)) ? -1 : idx; - } - - class reverse_iterator - { - int index; - T **base; - - public: - reverse_iterator(const LIST &_lst) : - index(_lst.getCount() - 1), - base(_lst.getArray()) - { - } - - class iterator - { - T **ptr; - - public: - iterator(T **_p) : ptr(_p) {} - iterator operator++() { --ptr; return *this; } - bool operator!=(const iterator &p) { return ptr != p.ptr; } - operator T **() const { return ptr; } - }; - - __inline iterator begin() const { return iterator(base + index); } - __inline iterator end() const { return iterator(base - 1); } - __inline int indexOf(T **p) const { return int(p - base); } - }; - - __inline void destroy(void) { List_Destroy((SortedList *)this); } - __inline T* find(T *p) const { return (T *)List_Find((SortedList *)this, p); } - __inline int indexOf(T *p) const { return List_IndexOf((SortedList *)this, p); } - __inline int insert(T *p, int idx) { return List_Insert((SortedList *)this, p, idx); } - __inline int remove(int idx) { return List_Remove((SortedList *)this, idx); } - - __inline int insert(T *p) { return List_InsertPtr((SortedList *)this, p); } - __inline int remove(T *p) { return List_RemovePtr((SortedList *)this, p); } - - __inline int indexOf(T **p) const { return int(p - items); } - - __inline T* removeItem(T **p) - { - T *savePtr = *p; - List_Remove((SortedList *)this, int(p - items)); - return savePtr; - } - - __inline void put(int idx, T *p) { items[idx] = p; } - - __inline T **begin() const { return items; } - __inline T **end() const { return items + count; } - - __inline reverse_iterator rev_iter() const { return reverse_iterator(*this); } - -protected: - T **items; - int count, limit, increment; - FTSortFunc sortFunc; -}; - -template<class T> struct OBJLIST : public LIST<T> -{ - typedef int (*FTSortFunc)(const T *p1, const T *p2); - - __inline OBJLIST(int aincr, FTSortFunc afunc = nullptr) : - LIST<T>(aincr, afunc) - { - } - - __inline OBJLIST(int aincr, INT_PTR id) : - LIST<T>(aincr, (FTSortFunc)id) - { - } - - __inline OBJLIST(const OBJLIST &x) : - LIST<T>(x.increment, x.sortFunc) - { - this->items = nullptr; - List_ObjCopy((SortedList *)&x, (SortedList *)this, sizeof(T)); - } - - __inline OBJLIST &operator = (const OBJLIST &x) - { - destroy(); - List_ObjCopy((SortedList *)&x, (SortedList *)this, sizeof(T)); - return *this; - } - - ~OBJLIST() - { - destroy(); - } - - __inline void destroy(void) - { - for (int i = 0; i < this->count; i++) - delete this->items[i]; - - List_Destroy((SortedList *)this); - } - - __inline int remove(int idx) - { - delete this->items[idx]; - return List_Remove((SortedList *)this, idx); - } - - __inline int remove(T *p) - { - int i = this->getIndex(p); - if (i != -1) { - remove(i); - return 1; - } - return 0; - } - - __inline T &operator[](int idx) const { return *this->items[idx]; } -}; - -#define __A2W(s) L ## s -#define _A2W(s) __A2W(s) - -class _A2T : public ptrW -{ -public: - __inline _A2T(const char *s) : ptrW(mir_a2u(s)) {} - __inline _A2T(const char *s, int cp) : ptrW(mir_a2u_cp(s, cp)) {} -}; - -class _T2A : public ptrA -{ -public: - __forceinline _T2A(const wchar_t *s) : ptrA(mir_u2a(s)) {} - __forceinline _T2A(const wchar_t *s, int cp) : ptrA(mir_u2a_cp(s, cp)) {} -}; - -class T2Utf : public ptrA -{ -public: - __forceinline T2Utf(const wchar_t *str) : ptrA(mir_utf8encodeW(str)) {} - __forceinline operator uint8_t*() const { return (uint8_t*)data; } -#ifdef _XSTRING_ - std::string str() const { return std::string(data); } -#endif -}; - -class Utf2T : public ptrW -{ -public: - __forceinline Utf2T(const char *str) : ptrW(mir_utf8decodeW(str)) {} - __forceinline operator wchar_t *() const { return data; } -#ifdef _XSTRING_ - std::wstring str() const { return std::wstring(data); } -#endif -}; - -/////////////////////////////////////////////////////////////////////////////// -// basic class for classes that should be cleared inside new() - -class MIR_CORE_EXPORT MBinBuffer -{ - uint8_t *m_buf = nullptr; - -public: - MBinBuffer(); - MBinBuffer(size_t preAlloc); - MBinBuffer(const MBinBuffer &orig); - ~MBinBuffer(); - MBinBuffer& operator=(MBinBuffer &&) noexcept; - - __forceinline uint8_t* data() const { return m_buf; } - __forceinline bool isEmpty() const { return m_buf == nullptr; } - size_t length() const; - - // adds a buffer to the end - void append(const void *pBuf, size_t bufLen); - - __forceinline void append(const MBinBuffer &buf) - { append(buf.data(), buf.length()); - } - - // adds a buffer to the beginning - void appendBefore(const void *pBuf, size_t bufLen); - - __forceinline void appendBefore(const MBinBuffer &buf) - { appendBefore(buf.data(), buf.length()); - } - - // replaces buffer contents - void assign(const void *pBuf, size_t bufLen); - - // drops a part of buffer - void remove(size_t sz); -}; - -/////////////////////////////////////////////////////////////////////////////// -// thread handle controller - -class MThreadLock -{ - HANDLE &m_pHandle; - -public: - __forceinline MThreadLock(HANDLE &pHandle) : - m_pHandle(pHandle) - { - } - - __forceinline ~MThreadLock() - { - m_pHandle = nullptr; - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// parameter classes for XML, JSON & HTTP requests - -struct PARAM -{ - const char *szName; - __forceinline PARAM(const char *_name) : szName(_name) - { - } -}; - -struct BOOL_PARAM : public PARAM -{ - bool bValue; - __forceinline BOOL_PARAM(const char *_name, bool _value) : - PARAM(_name), bValue(_value) - { - } -}; - -struct INT_PARAM : public PARAM -{ - int32_t iValue; - __forceinline INT_PARAM(const char *_name, int32_t _value) : - PARAM(_name), iValue(_value) - { - } -}; - -struct INT64_PARAM : public PARAM -{ - int64_t iValue; - __forceinline INT64_PARAM(const char *_name, int64_t _value) : - PARAM(_name), iValue(_value) - { - } -}; - -struct SINT64_PARAM : public PARAM -{ - int64_t iValue; - __forceinline SINT64_PARAM(const char *_name, int64_t _value) : - PARAM(_name), iValue(_value) - { - } -}; - -struct CHAR_PARAM : public PARAM -{ - const char *szValue; - __forceinline CHAR_PARAM(const char *_name, const char *_value) : - PARAM(_name), szValue(_value) - { - } -}; - -struct WCHAR_PARAM : public PARAM -{ - const wchar_t *wszValue; - __forceinline WCHAR_PARAM(const char *_name, const wchar_t *_value) : - PARAM(_name), wszValue(_value) - { - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Callbacks - -class CCallbackImp -{ - struct CDummy - { - int foo; - }; - - typedef void (CDummy:: *TFnCallback)(void *argument); - - CDummy *m_object; - TFnCallback m_func; - -protected: - template<typename TClass, typename TArgument> - __inline CCallbackImp(TClass *object, void (TClass:: *func)(TArgument *argument)) : - m_object((CDummy *)object), - m_func((TFnCallback)func) - { - } - - __inline void Invoke(void *argument) const { if (m_func && m_object) (m_object->*m_func)(argument); } - -public: - __inline CCallbackImp() : m_object(nullptr), m_func(nullptr) {} - - __inline CCallbackImp(const CCallbackImp &other) : m_object(other.m_object), m_func(other.m_func) {} - __inline CCallbackImp &operator=(const CCallbackImp &other) { m_object = other.m_object; m_func = other.m_func; return *this; } - - __inline bool operator==(const CCallbackImp &other) const { return (m_object == other.m_object) && (m_func == other.m_func); } - __inline bool operator!=(const CCallbackImp &other) const { return (m_object != other.m_object) || (m_func != other.m_func); } - - __inline operator bool() const { return m_object && m_func; } -}; - -template<typename TArgument> -struct CCallback : public CCallbackImp -{ - typedef CCallbackImp CSuper; - -public: - __inline CCallback() {} - - template<typename TClass> - __inline CCallback(TClass *object, void (TClass:: *func)(TArgument *argument)) : CCallbackImp(object, func) {} - - __inline CCallback &operator=(const CCallbackImp &x) { CSuper::operator =(x); return *this; } - - __inline void operator()(TArgument *argument) const { Invoke((void *)argument); } -}; - -template<typename TClass, typename TArgument> -__inline CCallback<TArgument> Callback(TClass *object, void (TClass:: *func)(TArgument *argument)) -{ - return CCallback<TArgument>(object, func); -} - -/////////////////////////////////////////////////////////////////////////////// -// http support - -// works inline, in the same buffer, thus destroying its contents -// returns the address of buffer passed - -MIR_CORE_DLL(char *) mir_urlDecode(char *szUrl); - -MIR_CORE_DLL(CMStringA) mir_urlEncode(const char *szUrl); - -#endif // __cpluscplus - -#endif // M_SYSTEM_H +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+#ifndef M_SYSTEM_H__
+#define M_SYSTEM_H__ 1
+
+#ifndef MIRANDANAME
+ #define MIRANDANAME "Miranda NG"
+#endif
+#ifndef MIRANDACLASS
+ #define MIRANDACLASS "Miranda"
+#endif
+
+// set the default compatibility lever for Miranda 0.4.x
+#ifndef MIRANDA_VER
+ #define MIRANDA_VER 0x0A00
+#endif
+
+#ifdef _MSC_VER
+ #pragma warning(disable:4244 4245)
+#endif
+
+#define NEWSTR_ALLOCA(A) (A == NULL)?NULL:strcpy((char*)alloca(strlen(A)+1), A)
+#define NEWWSTR_ALLOCA(A) ((A==NULL)?NULL:wcscpy((wchar_t*)alloca(sizeof(wchar_t)*(wcslen(A)+1)),A))
+
+#include <m_core.h>
+
+// miranda/system/modulesloaded
+// called after all modules have been successfully initialised
+// wParam = lParam = 0
+// used to resolve double-dependencies in the module load order
+#define ME_SYSTEM_MODULESLOADED "Miranda/System/ModulesLoaded"
+
+// miranda/system/shutdown event
+// called just before the application terminates
+// the database is still guaranteed to be running during this hook.
+// wParam = lParam = 0
+#define ME_SYSTEM_SHUTDOWN "Miranda/System/Shutdown"
+
+// restarts miranda (0.8+)
+// wParam = 0 or 1. 1 - restart with current profile, 0 - restart in default profile or profile manager
+// lParam = (wchar_t*)path to a new miranda32.exe binary or NULL to use current
+#define MS_SYSTEM_RESTART "Miranda/System/Restart"
+
+// miranda/system/oktoexit event
+// called before the app goes into shutdown routine to make sure everyone is
+// happy to exit
+// wParam = lParam = 0
+// return nonzero to stop the exit cycle
+#define ME_SYSTEM_OKTOEXIT "Miranda/System/OkToExitEvent"
+
+// gets the version number of Miranda encoded as a uint32_t
+// returns the version number, encoded as one version per byte, therefore
+// version 1.2.3.10 is 0x0102030a
+EXTERN_C MIR_APP_DLL(uint32_t) Miranda_GetVersion(void);
+
+// gets the version number of Miranda encoded as four WORDs v0.92.2+
+// returns the version number, encoded as one version per word, therefore
+// version 1.2.3.3210 is 0x0001, 0x0002, 0x0003, 0x0C8a
+typedef uint16_t MFileVersion[4];
+EXTERN_C MIR_APP_DLL(void) Miranda_GetFileVersion(MFileVersion*);
+
+// gets the version of Miranda encoded as text
+// cch is the size of the buffer pointed to by pszVersion, in bytes
+// may return a build qualifier, such as "0.1.0.1 alpha"
+// returns 0 on success, nonzero on failure
+EXTERN_C MIR_APP_DLL(void) Miranda_GetVersionText(char *pDest, size_t cbSize);
+
+// returns a system window that can handle global timers
+// a usual practice is to use a unique pointer as a timer id
+EXTERN_C MIR_APP_DLL(class CDlgBase *) Miranda_GetSystemWindow();
+
+// Adds an event to the list to be checked in the main message loop
+// when a handle gets triggered, an appopriate stub gets called
+typedef void (CALLBACK *MWaitableStub)(void);
+typedef void (CALLBACK *MWaitableStubEx)(void*);
+
+EXTERN_C MIR_CORE_DLL(void) Miranda_WaitOnHandle(MWaitableStub pFunc, HANDLE hEvent = nullptr);
+EXTERN_C MIR_CORE_DLL(void) Miranda_WaitOnHandleEx(MWaitableStubEx pFunc, void *pInfo);
+
+// wParam = 0 (ignored)
+// lParam = 0 (ignored)
+//
+// This hook is fired just before the thread unwind stack is used,
+// it allows MT plugins to shutdown threads if they have any special
+// processing to do, etc.
+#define ME_SYSTEM_PRESHUTDOWN "Miranda/System/PShutdown"
+
+// Returns true when Miranda has got WM_QUIT and is in the process of shutting down
+EXTERN_C MIR_CORE_DLL(bool) Miranda_IsTerminated(void);
+
+// Enables termination flag
+EXTERN_C MIR_CORE_DLL(void) Miranda_SetTerminated(void);
+
+// Check if everyone is happy to exit
+// if everyone acknowleges OK to exit then returns true, otherwise false
+EXTERN_C MIR_APP_DLL(bool) Miranda_OkToExit(void);
+
+// Used by contact lists inside CloseAction
+// Waits for a permission to exit and destroys contact list
+EXTERN_C MIR_APP_DLL(void) Miranda_Close(void);
+
+// returns the last window tick where a monitored event was seen, currently WM_CHAR/WM_MOUSEMOVE
+EXTERN_C MIR_CORE_DLL(uint32_t) Miranda_GetIdle(void);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(__cplusplus)
+
+#ifndef M_STRING_H__
+ #include <m_string.h>
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// general lists' templates
+
+struct MIR_CORE_EXPORT MNonCopyable
+{
+ __inline MNonCopyable() {}
+
+ MNonCopyable(const MNonCopyable &) = delete;
+ MNonCopyable &operator=(const MNonCopyable &) = delete;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// mir_ptr - automatic pointer for buffers, allocated using mir_alloc/mir_calloc
+
+template<class T> class mir_ptr
+{
+protected:
+ T *data;
+
+public:
+ __inline explicit mir_ptr() : data(nullptr) {}
+ __inline explicit mir_ptr(T *_p) : data(_p) {}
+ __inline ~mir_ptr() { mir_free(data); }
+ __inline T *get() const { return data; }
+ __inline T *operator=(T *_p) { if (data) mir_free(data); data = _p; return data; }
+ __inline T *operator->() const { return data; }
+ __inline operator T *() const { return data; }
+ __inline operator INT_PTR() const { return (INT_PTR)data; }
+ __inline T *detach() { T *res = data; data = nullptr; return res; }
+};
+
+typedef mir_ptr<char> ptrA;
+typedef mir_ptr<wchar_t> ptrW;
+
+///////////////////////////////////////////////////////////////////////////////
+// mir_cs - simple wrapper for the critical sections
+
+class MIR_CORE_EXPORT mir_cs : public MNonCopyable
+{
+ #ifdef _MSC_VER
+ CRITICAL_SECTION m_cs;
+ #endif
+
+public:
+ mir_cs();
+ ~mir_cs();
+
+ void Lock();
+ void Unlock();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// mir_cslock - simple locker for the critical sections
+
+class mir_cslock : public MNonCopyable
+{
+ mir_cs &cs;
+
+public:
+ __inline mir_cslock(mir_cs &_cs) : cs(_cs) { cs.Lock(); }
+ __inline ~mir_cslock() { cs.Unlock(); }
+};
+
+class mir_cslockfull : public MNonCopyable
+{
+ mir_cs &cs;
+ bool bIsLocked = false;
+
+public:
+ __inline void lock() { bIsLocked = true; cs.Lock(); }
+ __inline void unlock() { bIsLocked = false; cs.Unlock(); }
+
+ __inline mir_cslockfull(mir_cs &_cs) : cs(_cs) { lock(); }
+ __inline ~mir_cslockfull() { if (bIsLocked) unlock(); }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//pass_ptrA, pass_ptrW and pass_ptrT - automatic pointer for passwords
+
+class pass_ptrA : public mir_ptr<char>
+{
+public:
+ __inline explicit pass_ptrA() : mir_ptr() {}
+ __inline explicit pass_ptrA(char *_p) : mir_ptr(_p) {}
+ __inline ~pass_ptrA() { zero(); }
+ __inline char *operator=(char *_p) { zero(); return mir_ptr::operator=(_p); }
+ __inline void zero()
+ {
+ if (data) SecureZeroMemory(data, mir_strlen(data));
+ }
+};
+
+class pass_ptrW : public mir_ptr<wchar_t>
+{
+public:
+ __inline explicit pass_ptrW() : mir_ptr() {}
+ __inline explicit pass_ptrW(wchar_t *_p) : mir_ptr(_p) {}
+ __inline ~pass_ptrW() { zero(); }
+ __inline wchar_t *operator=(wchar_t *_p) { zero(); return mir_ptr::operator=(_p); }
+ __inline void zero()
+ {
+ if (data) SecureZeroMemory(data, mir_wstrlen(data) * sizeof(wchar_t));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// basic class for classes that should be cleared inside new()
+
+class MZeroedObject
+{
+public:
+ __inline void *operator new(size_t size)
+ {
+ return calloc(1, size);
+ }
+
+ __inline void operator delete(void *p)
+ {
+ free(p);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// general lists' templates
+
+#define NumericKeySortT -1
+#define HandleKeySortT -2
+#define PtrKeySortT -3
+
+template<class T> struct LIST
+{
+ typedef int (*FTSortFunc)(const T *p1, const T *p2);
+
+ __inline LIST(int aincr, FTSortFunc afunc = nullptr)
+ {
+ memset(this, 0, sizeof(*this));
+ increment = aincr;
+ sortFunc = afunc;
+ }
+
+ __inline LIST(int aincr, INT_PTR id)
+ {
+ memset(this, 0, sizeof(*this));
+ increment = aincr;
+ sortFunc = FTSortFunc(id);
+ }
+
+ __inline LIST(const LIST &x)
+ {
+ items = nullptr;
+ List_Copy((SortedList *)&x, (SortedList *)this, sizeof(T));
+ }
+
+ __inline LIST &operator = (const LIST &x)
+ {
+ destroy();
+ List_Copy((SortedList *)&x, (SortedList *)this, sizeof(T));
+ return *this;
+ }
+
+ __inline ~LIST()
+ {
+ destroy();
+ }
+
+ __inline T *operator[](int idx) const { return (idx >= 0 && idx < count) ? items[idx] : nullptr; }
+ __inline int getCount(void) const { return count; }
+ __inline T **getArray(void) const { return items; }
+
+ __inline int getIndex(T *p) const
+ {
+ int idx;
+ return (!List_GetIndex((SortedList *)this, p, &idx)) ? -1 : idx;
+ }
+
+ class reverse_iterator
+ {
+ int index;
+ T **base;
+
+ public:
+ reverse_iterator(const LIST &_lst) :
+ index(_lst.getCount() - 1),
+ base(_lst.getArray())
+ {
+ }
+
+ class iterator
+ {
+ T **ptr;
+
+ public:
+ iterator(T **_p) : ptr(_p) {}
+ iterator operator++() { --ptr; return *this; }
+ bool operator!=(const iterator &p) { return ptr != p.ptr; }
+ operator T **() const { return ptr; }
+ };
+
+ __inline iterator begin() const { return iterator(base + index); }
+ __inline iterator end() const { return iterator(base - 1); }
+ __inline int indexOf(T **p) const { return int(p - base); }
+ };
+
+ __inline void destroy(void) { List_Destroy((SortedList *)this); }
+ __inline T* find(T *p) const { return (T *)List_Find((SortedList *)this, p); }
+ __inline int indexOf(T *p) const { return List_IndexOf((SortedList *)this, p); }
+ __inline int insert(T *p, int idx) { return List_Insert((SortedList *)this, p, idx); }
+ __inline int remove(int idx) { return List_Remove((SortedList *)this, idx); }
+
+ __inline int insert(T *p) { return List_InsertPtr((SortedList *)this, p); }
+ __inline int remove(T *p) { return List_RemovePtr((SortedList *)this, p); }
+
+ __inline int indexOf(T **p) const { return int(p - items); }
+
+ __inline T* removeItem(T **p)
+ {
+ T *savePtr = *p;
+ List_Remove((SortedList *)this, int(p - items));
+ return savePtr;
+ }
+
+ __inline void put(int idx, T *p) { items[idx] = p; }
+
+ __inline T **begin() const { return items; }
+ __inline T **end() const { return items + count; }
+
+ __inline reverse_iterator rev_iter() const { return reverse_iterator(*this); }
+
+protected:
+ T **items;
+ int count, limit, increment;
+ FTSortFunc sortFunc;
+};
+
+template<class T> struct OBJLIST : public LIST<T>
+{
+ typedef int (*FTSortFunc)(const T *p1, const T *p2);
+
+ __inline OBJLIST(int aincr, FTSortFunc afunc = nullptr) :
+ LIST<T>(aincr, afunc)
+ {
+ }
+
+ __inline OBJLIST(int aincr, INT_PTR id) :
+ LIST<T>(aincr, (FTSortFunc)id)
+ {
+ }
+
+ __inline OBJLIST(const OBJLIST &x) :
+ LIST<T>(x.increment, x.sortFunc)
+ {
+ this->items = nullptr;
+ List_ObjCopy((SortedList *)&x, (SortedList *)this, sizeof(T));
+ }
+
+ __inline OBJLIST &operator = (const OBJLIST &x)
+ {
+ destroy();
+ List_ObjCopy((SortedList *)&x, (SortedList *)this, sizeof(T));
+ return *this;
+ }
+
+ ~OBJLIST()
+ {
+ destroy();
+ }
+
+ __inline void destroy(void)
+ {
+ for (int i = 0; i < this->count; i++)
+ delete this->items[i];
+
+ List_Destroy((SortedList *)this);
+ }
+
+ __inline int remove(int idx)
+ {
+ delete this->items[idx];
+ return List_Remove((SortedList *)this, idx);
+ }
+
+ __inline int remove(T *p)
+ {
+ int i = this->getIndex(p);
+ if (i != -1) {
+ remove(i);
+ return 1;
+ }
+ return 0;
+ }
+
+ __inline T &operator[](int idx) const { return *this->items[idx]; }
+};
+
+#define __A2W(s) L ## s
+#define _A2W(s) __A2W(s)
+
+class _A2T : public ptrW
+{
+public:
+ __inline _A2T(const char *s) : ptrW(mir_a2u(s)) {}
+ __inline _A2T(const char *s, int cp) : ptrW(mir_a2u_cp(s, cp)) {}
+};
+
+class _T2A : public ptrA
+{
+public:
+ __forceinline _T2A(const wchar_t *s) : ptrA(mir_u2a(s)) {}
+ __forceinline _T2A(const wchar_t *s, int cp) : ptrA(mir_u2a_cp(s, cp)) {}
+};
+
+class T2Utf : public ptrA
+{
+public:
+ __forceinline T2Utf(const wchar_t *str) : ptrA(mir_utf8encodeW(str)) {}
+ __forceinline operator uint8_t*() const { return (uint8_t*)data; }
+#ifdef _XSTRING_
+ std::string str() const { return std::string(data); }
+#endif
+};
+
+class Utf2T : public ptrW
+{
+public:
+ __forceinline Utf2T(const char *str) : ptrW(mir_utf8decodeW(str)) {}
+ __forceinline operator wchar_t *() const { return data; }
+#ifdef _XSTRING_
+ std::wstring str() const { return std::wstring(data); }
+#endif
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// basic class for classes that should be cleared inside new()
+
+class MIR_CORE_EXPORT MBinBuffer
+{
+ uint8_t *m_buf = nullptr;
+
+public:
+ MBinBuffer();
+ MBinBuffer(size_t preAlloc);
+ MBinBuffer(const MBinBuffer &orig);
+ ~MBinBuffer();
+ MBinBuffer& operator=(MBinBuffer &&) noexcept;
+
+ __forceinline uint8_t* data() const { return m_buf; }
+ __forceinline bool isEmpty() const { return m_buf == nullptr; }
+ size_t length() const;
+
+ // adds a buffer to the end
+ void append(const void *pBuf, size_t bufLen);
+
+ __forceinline void append(const MBinBuffer &buf)
+ { append(buf.data(), buf.length());
+ }
+
+ // adds a buffer to the beginning
+ void appendBefore(const void *pBuf, size_t bufLen);
+
+ __forceinline void appendBefore(const MBinBuffer &buf)
+ { appendBefore(buf.data(), buf.length());
+ }
+
+ // replaces buffer contents
+ void assign(const void *pBuf, size_t bufLen);
+
+ // drops a part of buffer
+ void remove(size_t sz);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// thread handle controller
+
+class MThreadLock
+{
+ HANDLE &m_pHandle;
+
+public:
+ __forceinline MThreadLock(HANDLE &pHandle) :
+ m_pHandle(pHandle)
+ {
+ }
+
+ __forceinline ~MThreadLock()
+ {
+ m_pHandle = nullptr;
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// parameter classes for XML, JSON & HTTP requests
+
+struct PARAM
+{
+ const char *szName;
+ __forceinline PARAM(const char *_name) : szName(_name)
+ {
+ }
+};
+
+struct BOOL_PARAM : public PARAM
+{
+ bool bValue;
+ __forceinline BOOL_PARAM(const char *_name, bool _value) :
+ PARAM(_name), bValue(_value)
+ {
+ }
+};
+
+struct INT_PARAM : public PARAM
+{
+ int32_t iValue;
+ __forceinline INT_PARAM(const char *_name, int32_t _value) :
+ PARAM(_name), iValue(_value)
+ {
+ }
+};
+
+struct INT64_PARAM : public PARAM
+{
+ int64_t iValue;
+ __forceinline INT64_PARAM(const char *_name, int64_t _value) :
+ PARAM(_name), iValue(_value)
+ {
+ }
+};
+
+struct SINT64_PARAM : public PARAM
+{
+ int64_t iValue;
+ __forceinline SINT64_PARAM(const char *_name, int64_t _value) :
+ PARAM(_name), iValue(_value)
+ {
+ }
+};
+
+struct CHAR_PARAM : public PARAM
+{
+ const char *szValue;
+ __forceinline CHAR_PARAM(const char *_name, const char *_value) :
+ PARAM(_name), szValue(_value)
+ {
+ }
+};
+
+struct WCHAR_PARAM : public PARAM
+{
+ const wchar_t *wszValue;
+ __forceinline WCHAR_PARAM(const char *_name, const wchar_t *_value) :
+ PARAM(_name), wszValue(_value)
+ {
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callbacks
+
+class CCallbackImp
+{
+ struct CDummy
+ {
+ int foo;
+ };
+
+ typedef void (CDummy:: *TFnCallback)(void *argument);
+
+ CDummy *m_object;
+ TFnCallback m_func;
+
+protected:
+ template<typename TClass, typename TArgument>
+ __inline CCallbackImp(TClass *object, void (TClass:: *func)(TArgument *argument)) :
+ m_object((CDummy *)object),
+ m_func((TFnCallback)func)
+ {
+ }
+
+ __inline void Invoke(void *argument) const { if (m_func && m_object) (m_object->*m_func)(argument); }
+
+public:
+ __inline CCallbackImp() : m_object(nullptr), m_func(nullptr) {}
+
+ __inline CCallbackImp(const CCallbackImp &other) : m_object(other.m_object), m_func(other.m_func) {}
+ __inline CCallbackImp &operator=(const CCallbackImp &other) { m_object = other.m_object; m_func = other.m_func; return *this; }
+
+ __inline bool operator==(const CCallbackImp &other) const { return (m_object == other.m_object) && (m_func == other.m_func); }
+ __inline bool operator!=(const CCallbackImp &other) const { return (m_object != other.m_object) || (m_func != other.m_func); }
+
+ __inline operator bool() const { return m_object && m_func; }
+};
+
+template<typename TArgument>
+struct CCallback : public CCallbackImp
+{
+ typedef CCallbackImp CSuper;
+
+public:
+ __inline CCallback() {}
+
+ template<typename TClass>
+ __inline CCallback(TClass *object, void (TClass:: *func)(TArgument *argument)) : CCallbackImp(object, func) {}
+
+ __inline CCallback &operator=(const CCallbackImp &x) { CSuper::operator =(x); return *this; }
+
+ __inline void operator()(TArgument *argument) const { Invoke((void *)argument); }
+};
+
+template<typename TClass, typename TArgument>
+__inline CCallback<TArgument> Callback(TClass *object, void (TClass:: *func)(TArgument *argument))
+{
+ return CCallback<TArgument>(object, func);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// http support
+
+// works inline, in the same buffer, thus destroying its contents
+// returns the address of buffer passed
+
+MIR_CORE_DLL(char *) mir_urlDecode(char *szUrl);
+
+MIR_CORE_DLL(CMStringA) mir_urlEncode(const char *szUrl);
+
+#endif // __cpluscplus
+
+#endif // M_SYSTEM_H
diff --git a/include/m_timezones.h b/include/m_timezones.h index 5fc78c41d0..3b0d44a61f 100644 --- a/include/m_timezones.h +++ b/include/m_timezones.h @@ -1,139 +1,139 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-10 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef __M_TIMEZONES_H -#define __M_TIMEZONES_H - -#ifndef M_CORE_H__ -#include <m_core.h> -#endif - -#define MIM_TZ_NAMELEN 64 - -#define TZF_PLF_CB 1 // UI element is assumed to be a combo box -#define TZF_PLF_LB 2 // UI element is assumed to be a list box -#define TZF_DIFONLY 4 -#define TZF_KNOWNONLY 8 - -#define LOCAL_TIME_HANDLE NULL -#define UTC_TIME_HANDLE ((void*)-1) - -typedef INT_PTR mir_time; - -EXTERN_C MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags); -EXTERN_C MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags); - -EXTERN_C MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ); -EXTERN_C MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags); - -EXTERN_C MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags); -EXTERN_C MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags); - -EXTERN_C MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags); -EXTERN_C MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags); - -#ifdef _MSC_VER -EXTERN_C MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st); -EXTERN_C MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time src, SYSTEMTIME *dest, uint32_t dwFlags); - -EXTERN_C MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ); -#endif -EXTERN_C MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts); - -EXTERN_C MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ); -EXTERN_C MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname); - -#ifdef __cplusplus - -__forceinline int printDateTimeByContact (MCONTACT hContact, LPCTSTR szFormat, LPTSTR szDest, int cbDest, uint32_t dwFlags) -{ - return TimeZone_PrintDateTime(TimeZone_CreateByContact(hContact, nullptr, dwFlags), szFormat, szDest, cbDest, dwFlags); -} - -__forceinline int printTimeStampByContact(MCONTACT hContact, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, int cbDest, uint32_t dwFlags) -{ - return TimeZone_PrintTimeStamp(TimeZone_CreateByContact(hContact, nullptr, dwFlags), ts, szFormat, szDest, cbDest, dwFlags); -} - -#ifdef _MSC_VER -__forceinline LPTIME_ZONE_INFORMATION getTziByContact(MCONTACT hContact, uint32_t dwFlags = 0) -{ - return TimeZone_GetInfo(TimeZone_CreateByContact(hContact, nullptr, dwFlags)); -} - -__forceinline int getTimeZoneTimeByContact(MCONTACT hContact, SYSTEMTIME *st, uint32_t dwFlags = 0) -{ - return TimeZone_GetTimeZoneTime(TimeZone_CreateByContact(hContact, nullptr, dwFlags), st); -} -#endif - -__forceinline mir_time timeStampToTimeZoneTimeStampByContact(MCONTACT hContact, mir_time ts, uint32_t dwFlags = 0) -{ - return TimeZone_UtcToLocal(TimeZone_CreateByContact(hContact, nullptr, dwFlags), ts); -} - -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// Time services -// -// Converts a GMT timestamp into local time -// Returns the converted value -// -// Timestamps have zero at midnight 1/1/1970 GMT, this service converts such a -// value to be based at midnight 1/1/1970 local time. -// This service does not use a simple conversion based on the current offset -// between GMT and local. Rather, it figures out whether daylight savings time -// would have been in place at the time of the stamp and gives the local time as -// it would have been at the time and date the stamp contains. -// This service isn't nearly as useful as db/time/TimestampToString below and I -// recommend avoiding its use when possible so that you don't get your timezones -// mixed up (like I did. Living at GMT makes things easier for me, but has certain -// disadvantages :-)). - -EXTERN_C MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t); - -///////////////////////////////////////////////////////////////////////////////////////// -// Converts a GMT timestamp into a customisable local time string -// Returns the destination buffer -// -// Uses db/time/timestamptolocal for the conversion so read that description to -// see what's going on. -// The string is formatted according to the current user's locale, language and -// preferences. -// szFormat can have the following special characters: -// t Time without seconds, eg hh:mm -// s Time with seconds, eg hh:mm:ss -// m Time without minutes, eg hh -// d Short date, eg dd/mm/yyyy -// D Long date, eg d mmmm yyyy -// I ISO 8061 Time yyyy-mm-ddThh:mm:ssZ -// All other characters are copied across to szDest as-is - -EXTERN_C MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest); -EXTERN_C MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest); - -#define TimeZone_ToStringT TimeZone_ToStringW - -#endif /* __M_TIMEZONES_H */ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-10 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __M_TIMEZONES_H
+#define __M_TIMEZONES_H
+
+#ifndef M_CORE_H__
+#include <m_core.h>
+#endif
+
+#define MIM_TZ_NAMELEN 64
+
+#define TZF_PLF_CB 1 // UI element is assumed to be a combo box
+#define TZF_PLF_LB 2 // UI element is assumed to be a list box
+#define TZF_DIFONLY 4
+#define TZF_KNOWNONLY 8
+
+#define LOCAL_TIME_HANDLE NULL
+#define UTC_TIME_HANDLE ((void*)-1)
+
+typedef INT_PTR mir_time;
+
+EXTERN_C MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags);
+EXTERN_C MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags);
+
+EXTERN_C MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ);
+EXTERN_C MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags);
+
+EXTERN_C MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags);
+EXTERN_C MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags);
+
+EXTERN_C MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags);
+EXTERN_C MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags);
+
+#ifdef _MSC_VER
+EXTERN_C MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st);
+EXTERN_C MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time src, SYSTEMTIME *dest, uint32_t dwFlags);
+
+EXTERN_C MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ);
+#endif
+EXTERN_C MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts);
+
+EXTERN_C MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ);
+EXTERN_C MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname);
+
+#ifdef __cplusplus
+
+__forceinline int printDateTimeByContact (MCONTACT hContact, LPCTSTR szFormat, LPTSTR szDest, int cbDest, uint32_t dwFlags)
+{
+ return TimeZone_PrintDateTime(TimeZone_CreateByContact(hContact, nullptr, dwFlags), szFormat, szDest, cbDest, dwFlags);
+}
+
+__forceinline int printTimeStampByContact(MCONTACT hContact, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, int cbDest, uint32_t dwFlags)
+{
+ return TimeZone_PrintTimeStamp(TimeZone_CreateByContact(hContact, nullptr, dwFlags), ts, szFormat, szDest, cbDest, dwFlags);
+}
+
+#ifdef _MSC_VER
+__forceinline LPTIME_ZONE_INFORMATION getTziByContact(MCONTACT hContact, uint32_t dwFlags = 0)
+{
+ return TimeZone_GetInfo(TimeZone_CreateByContact(hContact, nullptr, dwFlags));
+}
+
+__forceinline int getTimeZoneTimeByContact(MCONTACT hContact, SYSTEMTIME *st, uint32_t dwFlags = 0)
+{
+ return TimeZone_GetTimeZoneTime(TimeZone_CreateByContact(hContact, nullptr, dwFlags), st);
+}
+#endif
+
+__forceinline mir_time timeStampToTimeZoneTimeStampByContact(MCONTACT hContact, mir_time ts, uint32_t dwFlags = 0)
+{
+ return TimeZone_UtcToLocal(TimeZone_CreateByContact(hContact, nullptr, dwFlags), ts);
+}
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Time services
+//
+// Converts a GMT timestamp into local time
+// Returns the converted value
+//
+// Timestamps have zero at midnight 1/1/1970 GMT, this service converts such a
+// value to be based at midnight 1/1/1970 local time.
+// This service does not use a simple conversion based on the current offset
+// between GMT and local. Rather, it figures out whether daylight savings time
+// would have been in place at the time of the stamp and gives the local time as
+// it would have been at the time and date the stamp contains.
+// This service isn't nearly as useful as db/time/TimestampToString below and I
+// recommend avoiding its use when possible so that you don't get your timezones
+// mixed up (like I did. Living at GMT makes things easier for me, but has certain
+// disadvantages :-)).
+
+EXTERN_C MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Converts a GMT timestamp into a customisable local time string
+// Returns the destination buffer
+//
+// Uses db/time/timestamptolocal for the conversion so read that description to
+// see what's going on.
+// The string is formatted according to the current user's locale, language and
+// preferences.
+// szFormat can have the following special characters:
+// t Time without seconds, eg hh:mm
+// s Time with seconds, eg hh:mm:ss
+// m Time without minutes, eg hh
+// d Short date, eg dd/mm/yyyy
+// D Long date, eg d mmmm yyyy
+// I ISO 8061 Time yyyy-mm-ddThh:mm:ssZ
+// All other characters are copied across to szDest as-is
+
+EXTERN_C MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest);
+EXTERN_C MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest);
+
+#define TimeZone_ToStringT TimeZone_ToStringW
+
+#endif /* __M_TIMEZONES_H */
diff --git a/include/m_types.h b/include/m_types.h index 1b0bdeb3bf..5410b3476a 100644 --- a/include/m_types.h +++ b/include/m_types.h @@ -1,210 +1,210 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_TYPES_H__ -#define M_TYPES_H__ 1 - -/////////////////////////////////////////////////////////////////////////////// -// Linux - -#ifndef _MSC_VER - -#include <wchar.h> -#include <netinet/in.h> - -using namespace std; - -#define CALLBACK -#define EXTERN_C extern "C" - -#define PURE = 0 -#define STDMETHOD(method) virtual HRESULT method -#define STDMETHOD_(ret, method) virtual ret method -#define STDMETHODIMP_(ret) ret - -#define FALSE 0 -#define TRUE 1 -#define CP_ACP 0 -#define SW_HIDE 0 -#define SW_SHOW 5 -#define MAX_PATH 260 -#define LR_SHARED 0x8000 -#define LF_FACESIZE 32 -#define _TRUNCATE size_t(-1) -#define INVALID_HANDLE_VALUE HANDLE(-1) - -#define MB_OK 0x00000000L -#define MB_OKCANCEL 0x00000001L -#define MB_YESNOCANCEL 0x00000003L -#define MB_YESNO 0x00000004L -#define MB_RETRYCANCEL 0x00000005L - -#define MB_ICONSTOP 0x00000010L -#define MB_ICONERROR 0x00000010L -#define MB_ICONQUESTION 0x00000020L -#define MB_ICONEXCLAMATION 0x00000030L -#define MB_ICONWARNING 0x00000030L -#define MB_ICONINFORMATION 0x00000040L - -#define IDABORT 3 -#define IDCANCEL 2 -#define IDCONTINUE 11 -#define IDIGNORE 5 -#define IDNO 7 -#define IDOK 1 -#define IDRETRY 4 -#define IDTRYAGAIN 10 -#define IDYES 6 - -typedef void *HANDLE; -typedef int BOOL, SOCKET; -typedef uint32_t UINT, COLORREF; -typedef intptr_t WPARAM, LPARAM, INT_PTR; -typedef uintptr_t UINT_PTR, DWORD_PTR, LRESULT; -typedef char *LPSTR; -typedef const char *LPCSTR; -typedef wchar_t *LPWSTR, *LPTSTR; -typedef const wchar_t *LPCWSTR, *LPCTSTR; -typedef sockaddr_in SOCKADDR_IN; - -struct RECT { int left, top, right, bottom; }; -struct POINT { int x, y; }; -struct SIZE { int width, height; }; -struct MSG; -struct LOGFONTA; -struct LOGFONTW; -struct WIN32_FIND_DATA; - -#define GetCurrentThreadId pthread_self - -#define MIR_EXPORT __attribute__((__visibility__("default"))) -#define MIR_IMPORT -#define MIR_SYSCALL -#define MIR_CDECL -#define UNREFERENCED_PARAMETER(x) - -#define __try try -#define __except catch -#define EXCEPTION_EXECUTE_HANDLER ... - -#define _In_z_ -#define _Pre_notnull_ -#define _Always_(x) -#define _Printf_format_string_ -#define _countof(array) (sizeof(array) / sizeof(array[0])) -#define __forceinline inline __attribute__ ((always_inline)) -#define __fallthrough - -#define InterlockedIncrement(x) __sync_fetch_and_add(x, 1) -#define InterlockedDecrement(x) __sync_fetch_and_add(x, -1) - -#define SecureZeroMemory(x, y) memset(x, 0, y) -#define interface struct -#define memcpy_s(a,b,c,d) memcpy(a,c,(b)<(d)?(b):(d)) -#define memmove_s(a,b,c,d) memmove(a,c,(b)<(d)?(b):(d)) - -#define stricmp strcasecmp -#define strnicmp strncasecmp -#define wcsicmp wcscasecmp -#define wcsnicmp wcsncasecmp - -#define _vsnprintf vsnprintf -#define _vsnwprintf vswprintf - -#define DECLARE_HANDLE(name) struct _##name { int unused; }; typedef struct _##name *name -DECLARE_HANDLE(HDC); -DECLARE_HANDLE(HWND); -DECLARE_HANDLE(HFONT); -DECLARE_HANDLE(HICON); -DECLARE_HANDLE(HMENU); -DECLARE_HANDLE(HBRUSH); -DECLARE_HANDLE(HBITMAP); -DECLARE_HANDLE(HCURSOR); -DECLARE_HANDLE(HTREEITEM); -DECLARE_HANDLE(HINSTANCE); - -struct EXCEPTION_POINTERS { int unused; }; -struct LOGFONT { int unused; }; -struct SYSTEMTIME; -struct MEASUREITEMSTRUCT; -struct DRAWITEMSTRUCT; -struct DELETEITEMSTRUCT; - -struct NMHDR; -struct NMLISTVIEW; -struct NMLVDISPINFO; -struct NMLVSCROLL; -struct NMLVGETINFOTIP; -struct NMLVFINDITEM; -struct NMITEMACTIVATE; -struct NMLVKEYDOWN; -struct NMLVCUSTOMDRAW; -struct NMCLISTCONTROL; -struct NMTREEVIEW; -struct NMTVKEYDOWN; -struct NMTVDISPINFO; -struct NMTVGETINFOTIP; -struct NMTVCUSTOMDRAW; - -struct LVFINDINFO; -struct LVBKIMAGE; -struct LVCOLUMN; -struct LVGROUP; -struct LVGROUPMETRICS; -struct LVINSERTMARK; -struct LVTILEINFO; -struct LVTILEVIEWINFO; -struct LVITEM; -struct LVHITTESTINFO; -struct LVINSERTGROUPSORTED; -struct LVSETINFOTIP; - -struct TVITEMEX; -struct TVHITTESTINFO; -struct TVINSERTSTRUCT; -struct TVSORTCB; -struct _TREEITEM; - -#ifdef ELEMENTARY_H - typedef Evas_Object* MWindow; -#else - typedef void *MWindow; -#endif - -#else -/////////////////////////////////////////////////////////////////////////////// -// Windows - -#include <tchar.h> - -#define MIR_EXPORT __declspec(dllexport) -#define MIR_IMPORT __declspec(dllimport) - -#define MIR_SYSCALL __stdcall -#define MIR_CDECL __cdecl - -typedef HWND MWindow; - -#endif - -#endif // M_TYPES_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_TYPES_H__
+#define M_TYPES_H__ 1
+
+///////////////////////////////////////////////////////////////////////////////
+// Linux
+
+#ifndef _MSC_VER
+
+#include <wchar.h>
+#include <netinet/in.h>
+
+using namespace std;
+
+#define CALLBACK
+#define EXTERN_C extern "C"
+
+#define PURE = 0
+#define STDMETHOD(method) virtual HRESULT method
+#define STDMETHOD_(ret, method) virtual ret method
+#define STDMETHODIMP_(ret) ret
+
+#define FALSE 0
+#define TRUE 1
+#define CP_ACP 0
+#define SW_HIDE 0
+#define SW_SHOW 5
+#define MAX_PATH 260
+#define LR_SHARED 0x8000
+#define LF_FACESIZE 32
+#define _TRUNCATE size_t(-1)
+#define INVALID_HANDLE_VALUE HANDLE(-1)
+
+#define MB_OK 0x00000000L
+#define MB_OKCANCEL 0x00000001L
+#define MB_YESNOCANCEL 0x00000003L
+#define MB_YESNO 0x00000004L
+#define MB_RETRYCANCEL 0x00000005L
+
+#define MB_ICONSTOP 0x00000010L
+#define MB_ICONERROR 0x00000010L
+#define MB_ICONQUESTION 0x00000020L
+#define MB_ICONEXCLAMATION 0x00000030L
+#define MB_ICONWARNING 0x00000030L
+#define MB_ICONINFORMATION 0x00000040L
+
+#define IDABORT 3
+#define IDCANCEL 2
+#define IDCONTINUE 11
+#define IDIGNORE 5
+#define IDNO 7
+#define IDOK 1
+#define IDRETRY 4
+#define IDTRYAGAIN 10
+#define IDYES 6
+
+typedef void *HANDLE;
+typedef int BOOL, SOCKET;
+typedef uint32_t UINT, COLORREF;
+typedef intptr_t WPARAM, LPARAM, INT_PTR;
+typedef uintptr_t UINT_PTR, DWORD_PTR, LRESULT;
+typedef char *LPSTR;
+typedef const char *LPCSTR;
+typedef wchar_t *LPWSTR, *LPTSTR;
+typedef const wchar_t *LPCWSTR, *LPCTSTR;
+typedef sockaddr_in SOCKADDR_IN;
+
+struct RECT { int left, top, right, bottom; };
+struct POINT { int x, y; };
+struct SIZE { int width, height; };
+struct MSG;
+struct LOGFONTA;
+struct LOGFONTW;
+struct WIN32_FIND_DATA;
+
+#define GetCurrentThreadId pthread_self
+
+#define MIR_EXPORT __attribute__((__visibility__("default")))
+#define MIR_IMPORT
+#define MIR_SYSCALL
+#define MIR_CDECL
+#define UNREFERENCED_PARAMETER(x)
+
+#define __try try
+#define __except catch
+#define EXCEPTION_EXECUTE_HANDLER ...
+
+#define _In_z_
+#define _Pre_notnull_
+#define _Always_(x)
+#define _Printf_format_string_
+#define _countof(array) (sizeof(array) / sizeof(array[0]))
+#define __forceinline inline __attribute__ ((always_inline))
+#define __fallthrough
+
+#define InterlockedIncrement(x) __sync_fetch_and_add(x, 1)
+#define InterlockedDecrement(x) __sync_fetch_and_add(x, -1)
+
+#define SecureZeroMemory(x, y) memset(x, 0, y)
+#define interface struct
+#define memcpy_s(a,b,c,d) memcpy(a,c,(b)<(d)?(b):(d))
+#define memmove_s(a,b,c,d) memmove(a,c,(b)<(d)?(b):(d))
+
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#define wcsicmp wcscasecmp
+#define wcsnicmp wcsncasecmp
+
+#define _vsnprintf vsnprintf
+#define _vsnwprintf vswprintf
+
+#define DECLARE_HANDLE(name) struct _##name { int unused; }; typedef struct _##name *name
+DECLARE_HANDLE(HDC);
+DECLARE_HANDLE(HWND);
+DECLARE_HANDLE(HFONT);
+DECLARE_HANDLE(HICON);
+DECLARE_HANDLE(HMENU);
+DECLARE_HANDLE(HBRUSH);
+DECLARE_HANDLE(HBITMAP);
+DECLARE_HANDLE(HCURSOR);
+DECLARE_HANDLE(HTREEITEM);
+DECLARE_HANDLE(HINSTANCE);
+
+struct EXCEPTION_POINTERS { int unused; };
+struct LOGFONT { int unused; };
+struct SYSTEMTIME;
+struct MEASUREITEMSTRUCT;
+struct DRAWITEMSTRUCT;
+struct DELETEITEMSTRUCT;
+
+struct NMHDR;
+struct NMLISTVIEW;
+struct NMLVDISPINFO;
+struct NMLVSCROLL;
+struct NMLVGETINFOTIP;
+struct NMLVFINDITEM;
+struct NMITEMACTIVATE;
+struct NMLVKEYDOWN;
+struct NMLVCUSTOMDRAW;
+struct NMCLISTCONTROL;
+struct NMTREEVIEW;
+struct NMTVKEYDOWN;
+struct NMTVDISPINFO;
+struct NMTVGETINFOTIP;
+struct NMTVCUSTOMDRAW;
+
+struct LVFINDINFO;
+struct LVBKIMAGE;
+struct LVCOLUMN;
+struct LVGROUP;
+struct LVGROUPMETRICS;
+struct LVINSERTMARK;
+struct LVTILEINFO;
+struct LVTILEVIEWINFO;
+struct LVITEM;
+struct LVHITTESTINFO;
+struct LVINSERTGROUPSORTED;
+struct LVSETINFOTIP;
+
+struct TVITEMEX;
+struct TVHITTESTINFO;
+struct TVINSERTSTRUCT;
+struct TVSORTCB;
+struct _TREEITEM;
+
+#ifdef ELEMENTARY_H
+ typedef Evas_Object* MWindow;
+#else
+ typedef void *MWindow;
+#endif
+
+#else
+///////////////////////////////////////////////////////////////////////////////
+// Windows
+
+#include <tchar.h>
+
+#define MIR_EXPORT __declspec(dllexport)
+#define MIR_IMPORT __declspec(dllimport)
+
+#define MIR_SYSCALL __stdcall
+#define MIR_CDECL __cdecl
+
+typedef HWND MWindow;
+
+#endif
+
+#endif // M_TYPES_H__
diff --git a/include/m_userinfo.h b/include/m_userinfo.h index 9da71f1db5..880d224739 100644 --- a/include/m_userinfo.h +++ b/include/m_userinfo.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_utils.h b/include/m_utils.h index f9cb7e42cd..0d5b599ac6 100644 --- a/include/m_utils.h +++ b/include/m_utils.h @@ -3,7 +3,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-12 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/m_xml.h b/include/m_xml.h index e77db66a6f..de1231ec75 100644 --- a/include/m_xml.h +++ b/include/m_xml.h @@ -1,194 +1,194 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_XML_H__ -#define M_XML_H__ - -#include <m_core.h> - -///////////////////////////////////////////////////////////////////////////////////////// -// new API to replace the old one - -#ifdef MIR_CORE_EXPORTS -#define TINYXML2_EXPORT 1 -#else -#define TINYXML2_IMPORT 1 -#endif - -#include "../src/mir_core/src/tinyxml2.h" - -typedef tinyxml2::XMLNode TiXmlNode; -typedef tinyxml2::XMLText TiXmlText; -typedef tinyxml2::XMLElement TiXmlElement; -typedef tinyxml2::XMLDocument TiXmlDocument; - -typedef tinyxml2::XMLHandle TiXmlHandle; -typedef tinyxml2::XMLConstHandle TiXmlConst; - -///////////////////////////////////////////////////////////////////////////////////////// -// safe wrappers - -EXTERN_C MIR_CORE_DLL(int) XmlGetChildCount(const TiXmlElement*); - -EXTERN_C MIR_CORE_DLL(TiXmlElement*) XmlAddChild(TiXmlElement*, const char *pszName); -EXTERN_C MIR_CORE_DLL(TiXmlElement*) XmlAddChildA(TiXmlElement*, const char *pszName, const char *ptszValue); -EXTERN_C MIR_CORE_DLL(TiXmlElement*) XmlAddChildI(TiXmlElement*, const char *pszName, int iValue); - -EXTERN_C MIR_CORE_DLL(int) XmlGetChildInt(const TiXmlElement *hXml, const char *key); -EXTERN_C MIR_CORE_DLL(const char*) XmlGetChildText(const TiXmlElement *hXml, const char *key); -EXTERN_C MIR_CORE_DLL(const TiXmlElement*) XmlGetChildByTag(const TiXmlElement *hXml, const char *key, const char *attrName, const char *attrValue); -EXTERN_C MIR_CORE_DLL(const TiXmlElement*) XmlFirstChild(const TiXmlElement *hXml, const char *key = nullptr); - -EXTERN_C MIR_CORE_DLL(void) XmlAddAttr(TiXmlElement*, const char *pszName, const char *ptszValue); -EXTERN_C MIR_CORE_DLL(const char*) XmlGetAttr(const TiXmlElement*, const char *pszName); - -///////////////////////////////////////////////////////////////////////////////////////// -// simple element iterator -// -// allows traversing subnodes in a cycle like -// for (auto *pNode : TiXmlEnum(pRoot)) { - -class TiXmlIterator -{ - const TiXmlElement *m_pCurr; - -public: - TiXmlIterator(const TiXmlElement *pNode) : - m_pCurr(pNode) - { - } - - TiXmlIterator& operator=(const TiXmlElement *pNode) - { - m_pCurr = pNode; - return *this; - } - - // Prefix ++ overload - TiXmlIterator& operator++() - { - if (m_pCurr) - m_pCurr = m_pCurr->NextSiblingElement(); - return *this; - } - - const TiXmlElement* operator*() - { - return m_pCurr; - } - - bool operator!=(const TiXmlIterator &iterator) - { - return m_pCurr != iterator.m_pCurr; - } -}; - -class TiXmlEnum -{ - const TiXmlElement *m_pFirst; - -public: - TiXmlEnum(const TiXmlNode *pNode) - { - m_pFirst = (pNode) ? pNode->FirstChildElement() : nullptr; - } - - TiXmlIterator begin() - { - return TiXmlIterator(m_pFirst); - } - - TiXmlIterator end() - { - return TiXmlIterator(nullptr); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// filtered element iterator -// -// allows traversing subnodes of the specified name in a cycle like -// for (auto *pNode : TiXmlFilter(pRoot, "element")) { - -class TiXmlFilterIterator -{ - const TiXmlElement *m_pCurr; - const char *m_pszFilter; - -public: - TiXmlFilterIterator(const TiXmlElement *pNode, const char *pszNodeName) : - m_pszFilter(pszNodeName), - m_pCurr(pNode) - { - } - - TiXmlFilterIterator& operator=(const TiXmlElement *pNode) - { - m_pCurr = pNode; - return *this; - } - - // Prefix ++ overload - TiXmlFilterIterator& operator++() - { - if (m_pCurr) - m_pCurr = m_pCurr->NextSiblingElement(m_pszFilter); - return *this; - } - - const TiXmlElement* operator*() - { - return m_pCurr; - } - - bool operator!=(const TiXmlFilterIterator &iterator) - { - return m_pCurr != iterator.m_pCurr; - } -}; - -class TiXmlFilter -{ - const TiXmlElement *m_pFirst; - const char *m_pszFilter; - -public: - TiXmlFilter(const TiXmlNode *pNode, const char *pszNodeName) : - m_pszFilter(pszNodeName) - { - m_pFirst = (pNode) ? pNode->FirstChildElement(pszNodeName) : nullptr; - } - - TiXmlFilterIterator begin() - { - return TiXmlFilterIterator(m_pFirst, m_pszFilter); - } - - TiXmlFilterIterator end() - { - return TiXmlFilterIterator(nullptr, nullptr); - } -}; - -#endif // M_XML_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_XML_H__
+#define M_XML_H__
+
+#include <m_core.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// new API to replace the old one
+
+#ifdef MIR_CORE_EXPORTS
+#define TINYXML2_EXPORT 1
+#else
+#define TINYXML2_IMPORT 1
+#endif
+
+#include "../src/mir_core/src/tinyxml2.h"
+
+typedef tinyxml2::XMLNode TiXmlNode;
+typedef tinyxml2::XMLText TiXmlText;
+typedef tinyxml2::XMLElement TiXmlElement;
+typedef tinyxml2::XMLDocument TiXmlDocument;
+
+typedef tinyxml2::XMLHandle TiXmlHandle;
+typedef tinyxml2::XMLConstHandle TiXmlConst;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// safe wrappers
+
+EXTERN_C MIR_CORE_DLL(int) XmlGetChildCount(const TiXmlElement*);
+
+EXTERN_C MIR_CORE_DLL(TiXmlElement*) XmlAddChild(TiXmlElement*, const char *pszName);
+EXTERN_C MIR_CORE_DLL(TiXmlElement*) XmlAddChildA(TiXmlElement*, const char *pszName, const char *ptszValue);
+EXTERN_C MIR_CORE_DLL(TiXmlElement*) XmlAddChildI(TiXmlElement*, const char *pszName, int iValue);
+
+EXTERN_C MIR_CORE_DLL(int) XmlGetChildInt(const TiXmlElement *hXml, const char *key);
+EXTERN_C MIR_CORE_DLL(const char*) XmlGetChildText(const TiXmlElement *hXml, const char *key);
+EXTERN_C MIR_CORE_DLL(const TiXmlElement*) XmlGetChildByTag(const TiXmlElement *hXml, const char *key, const char *attrName, const char *attrValue);
+EXTERN_C MIR_CORE_DLL(const TiXmlElement*) XmlFirstChild(const TiXmlElement *hXml, const char *key = nullptr);
+
+EXTERN_C MIR_CORE_DLL(void) XmlAddAttr(TiXmlElement*, const char *pszName, const char *ptszValue);
+EXTERN_C MIR_CORE_DLL(const char*) XmlGetAttr(const TiXmlElement*, const char *pszName);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// simple element iterator
+//
+// allows traversing subnodes in a cycle like
+// for (auto *pNode : TiXmlEnum(pRoot)) {
+
+class TiXmlIterator
+{
+ const TiXmlElement *m_pCurr;
+
+public:
+ TiXmlIterator(const TiXmlElement *pNode) :
+ m_pCurr(pNode)
+ {
+ }
+
+ TiXmlIterator& operator=(const TiXmlElement *pNode)
+ {
+ m_pCurr = pNode;
+ return *this;
+ }
+
+ // Prefix ++ overload
+ TiXmlIterator& operator++()
+ {
+ if (m_pCurr)
+ m_pCurr = m_pCurr->NextSiblingElement();
+ return *this;
+ }
+
+ const TiXmlElement* operator*()
+ {
+ return m_pCurr;
+ }
+
+ bool operator!=(const TiXmlIterator &iterator)
+ {
+ return m_pCurr != iterator.m_pCurr;
+ }
+};
+
+class TiXmlEnum
+{
+ const TiXmlElement *m_pFirst;
+
+public:
+ TiXmlEnum(const TiXmlNode *pNode)
+ {
+ m_pFirst = (pNode) ? pNode->FirstChildElement() : nullptr;
+ }
+
+ TiXmlIterator begin()
+ {
+ return TiXmlIterator(m_pFirst);
+ }
+
+ TiXmlIterator end()
+ {
+ return TiXmlIterator(nullptr);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// filtered element iterator
+//
+// allows traversing subnodes of the specified name in a cycle like
+// for (auto *pNode : TiXmlFilter(pRoot, "element")) {
+
+class TiXmlFilterIterator
+{
+ const TiXmlElement *m_pCurr;
+ const char *m_pszFilter;
+
+public:
+ TiXmlFilterIterator(const TiXmlElement *pNode, const char *pszNodeName) :
+ m_pszFilter(pszNodeName),
+ m_pCurr(pNode)
+ {
+ }
+
+ TiXmlFilterIterator& operator=(const TiXmlElement *pNode)
+ {
+ m_pCurr = pNode;
+ return *this;
+ }
+
+ // Prefix ++ overload
+ TiXmlFilterIterator& operator++()
+ {
+ if (m_pCurr)
+ m_pCurr = m_pCurr->NextSiblingElement(m_pszFilter);
+ return *this;
+ }
+
+ const TiXmlElement* operator*()
+ {
+ return m_pCurr;
+ }
+
+ bool operator!=(const TiXmlFilterIterator &iterator)
+ {
+ return m_pCurr != iterator.m_pCurr;
+ }
+};
+
+class TiXmlFilter
+{
+ const TiXmlElement *m_pFirst;
+ const char *m_pszFilter;
+
+public:
+ TiXmlFilter(const TiXmlNode *pNode, const char *pszNodeName) :
+ m_pszFilter(pszNodeName)
+ {
+ m_pFirst = (pNode) ? pNode->FirstChildElement(pszNodeName) : nullptr;
+ }
+
+ TiXmlFilterIterator begin()
+ {
+ return TiXmlFilterIterator(m_pFirst, m_pszFilter);
+ }
+
+ TiXmlFilterIterator end()
+ {
+ return TiXmlFilterIterator(nullptr, nullptr);
+ }
+};
+
+#endif // M_XML_H__
diff --git a/include/m_xstatus.h b/include/m_xstatus.h index b26c03bdb3..afbac6890e 100644 --- a/include/m_xstatus.h +++ b/include/m_xstatus.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/include/newpluginapi.h b/include/newpluginapi.h index 88d72a47d0..a7e561461f 100644 --- a/include/newpluginapi.h +++ b/include/newpluginapi.h @@ -1,501 +1,501 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_NEWPLUGINAPI_H__ -#define M_NEWPLUGINAPI_H__ - -#if !defined(HIMAGELIST) -typedef struct _IMAGELIST* HIMAGELIST; -#endif - -#include <m_gui.h> -#include <m_database.h> -#include <m_protocols.h> - -#define PLUGIN_MAKE_VERSION(a, b, c, d) (((((DWORD)(a))&0xFF)<<24)|((((DWORD)(b))&0xFF)<<16)|((((DWORD)(c))&0xFF)<<8)|(((DWORD)(d))&0xFF)) -#define MAXMODULELABELLENGTH 64 - -#define UNICODE_AWARE 0x0001 -#define STATIC_PLUGIN 0x0002 - -MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int langId); -MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid); -MIR_APP_DLL(int) SetServiceModePlugin(const char *szPluginName, WPARAM = 0, LPARAM = 0); - -// manually get/set flag specified at Options - Plugins - Enabled -MIR_APP_DLL(bool) IsPluginOnWhiteList(const char *szPluginName); -MIR_APP_DLL(void) SetPluginOnWhiteList(const char *szPluginName, bool bAllow); - -///////////////////////////////////////////////////////////////////////////////////////// -// Used to define the end of the MirandaPluginInterface list - -#define MIID_LAST {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}} - -///////////////////////////////////////////////////////////////////////////////////////// -// Replaceable internal modules interface ids - -#define MIID_HISTORY {0x5ca0cbc1, 0x999a, 0x4ea2, {0x8b, 0x44, 0xf8, 0xf6, 0x7d, 0x7f, 0x8e, 0xbe}} -#define MIID_UIUSERINFO {0x570b931c, 0x9af8, 0x48f1, {0xad, 0x9f, 0xc4, 0x49, 0x8c, 0x61, 0x8a, 0x77}} -#define MIID_SRAWAY {0x5ab54c76, 0x1b4c, 0x4a00, {0xb4, 0x04, 0x48, 0xcb, 0xea, 0x5f, 0xef, 0xe7}} -#define MIID_SREMAIL {0xd005b5a6, 0x1b66, 0x445a, {0xb6, 0x03, 0x74, 0xd4, 0xd4, 0x55, 0x2d, 0xe2}} -#define MIID_SRFILE {0x989d104d, 0xacb7, 0x4ee0, {0xb9, 0x6d, 0x67, 0xce, 0x46, 0x53, 0xb6, 0x95}} -#define MIID_UIHISTORY {0x7f7e3d98, 0xce1f, 0x4962, {0x82, 0x84, 0x96, 0x85, 0x50, 0xf1, 0xd3, 0xd9}} -#define MIID_AUTOAWAY {0x9c87f7dc, 0x3bd7, 0x4983, {0xb7, 0xfb, 0xb8, 0x48, 0xfd, 0xbc, 0x91, 0xf0}} -#define MIID_USERONLINE {0x130829e0, 0x2463, 0x4ff8, {0xbb, 0xc8, 0xce, 0x73, 0xc0, 0x18, 0x84, 0x42}} -#define MIID_CRYPTO {0x415ca6e1, 0x895f, 0x40e6, {0x87, 0xbd, 0x9b, 0x39, 0x60, 0x16, 0xd0, 0xe5}} -#define MIID_POPUP {0xb275f4a4, 0xe347, 0x4515, {0xaf, 0x71, 0x77, 0xd0, 0x1e, 0xef, 0x54, 0x41}} - -///////////////////////////////////////////////////////////////////////////////////////// -// Common plugin interfaces (core plugins) - -#define MIID_DATABASE {0xae77fd33, 0xe484, 0x4dc7, {0x8c, 0xbc, 0x09, 0x9f, 0xed, 0xcc, 0xcf, 0xdd}} -#define MIID_CLIST {0x9d8da8bf, 0x665b, 0x4908, {0x9e, 0x61, 0x9f, 0x75, 0x98, 0xae, 0x33, 0x0e}} -#define MIID_SRMM {0x58c7eea6, 0xf9db, 0x4dd9, {0x80, 0x36, 0xae, 0x80, 0x2b, 0xc0, 0x41, 0x4c}} -#define MIID_TESTPLUGIN {0x53b974f4, 0x3c74, 0x4dba, {0x8f, 0xc2, 0x6f, 0x92, 0xfe, 0x01, 0x3b, 0x8c}} - -///////////////////////////////////////////////////////////////////////////////////////// -// Special exception interface for protocols. -// This interface allows more than one plugin to implement it at the same time - -#define MIID_PROTOCOL {0x2a3c815e, 0xa7d9, 0x424b, {0xba, 0x30, 0x2, 0xd0, 0x83, 0x22, 0x90, 0x85}} - -#define MIID_SERVICEMODE {0x8a92c026, 0x953a, 0x4f5f, { 0x99, 0x21, 0xf2, 0xc2, 0xdc, 0x19, 0x5e, 0xc5}} - -///////////////////////////////////////////////////////////////////////////////////////// -// Each service mode plugin must implement MS_SERVICEMODE_LAUNCH -// This service might return one of the following values: -// SERVICE_CONTINUE - load Miranda normally, like there's no service plugins at all -// SERVICE_ONLYDB - load database and then execute service plugin only -// SERVICE_MONOPOLY - execute only service plugin, even without database -// SERVICE_FAILED - terminate Miranda execution - -#define SERVICE_CONTINUE 0 -#define SERVICE_ONLYDB 1 -#define SERVICE_MONOPOLY 2 -#define SERVICE_FAILED (-1) - -#define MS_SERVICEMODE_LAUNCH "ServiceMode/Launch" - -struct PLUGININFOEX -{ - int cbSize; - const char *shortName; - uint32_t version; - const char *description; - const char *author; - const char *copyright; - const char *homepage; - uint8_t flags; // right now the only flag, UNICODE_AWARE, is recognized here - MUUID uuid; // plugin's unique identifier -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda/System/LoadModule event -// called when a plugin is being loaded dynamically -// wParam = CMPluginBase* -// lParam = HINSTANCE of the loaded plugin - -#define ME_SYSTEM_MODULELOAD "Miranda/System/LoadModule" - -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda/System/UnloadModule event -// called when a plugin is being unloaded dynamically -// wParam = CMPluginBase* -// lParam = HINSTANCE of the plugin to be unloaded - -#define ME_SYSTEM_MODULEUNLOAD "Miranda/System/UnloadModule" - -///////////////////////////////////////////////////////////////////////////////////////// -// plugin's class - -// initializes an empty account -typedef struct PROTO_INTERFACE* (*pfnInitProto)(const char* szModuleName, const wchar_t* szUserName); - -// deallocates an account instance -typedef int(*pfnUninitProto)(PROTO_INTERFACE*); - -#pragma warning(push) -#pragma warning(disable:4275) - -struct IcolibItem; - -class MIR_APP_EXPORT CMPluginBase : public MNonCopyable -{ - void tryOpenLog(); - -protected: - HINSTANCE m_hInst; - const char *m_szModuleName; - const PLUGININFOEX &m_pInfo; - HANDLE m_hLogger = nullptr; - LIST<IcolibItem> m_arIcons; - - CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo); - ~CMPluginBase(); - - // pass one of PROTOTYPE_* constants as type - void RegisterProtocol(int type, pfnInitProto = nullptr, pfnUninitProto = nullptr); - void SetUniqueId(const char *pszUniqueId); - -public: - void debugLogA(LPCSTR szFormat, ...); - void debugLogW(LPCWSTR wszFormat, ...); - - __forceinline void addIcolib(HANDLE hIcolib) { m_arIcons.insert((IcolibItem*)hIcolib); } - int addImgListIcon(HIMAGELIST himl, int iconId); - HICON getIcon(int iconId, bool big = false); - HANDLE getIconHandle(int iconId); - void releaseIcon(int iconId, bool big = false); - - __forceinline const PLUGININFOEX& getInfo() const { return m_pInfo; } - __forceinline const char* getModule() const { return m_szModuleName; } - - __forceinline HINSTANCE getInst() const { return m_hInst; } - __forceinline void setInst(HINSTANCE hInst) { m_hInst = hInst; } - - virtual int Load(); - virtual int Unload(); - - //////////////////////////////////////////////////////////////////////////////////////// - // registering module's resources - - template <size_t _Size> - __forceinline void registerIcon(const char *szSection, IconItem(&pIcons)[_Size], const char *prefix = nullptr) - { - Icon_Register(m_hInst, szSection, pIcons, _Size, prefix, this); - } - - template <size_t _Size> - __forceinline void registerIconW(const wchar_t *szSection, IconItemT(&pIcons)[_Size], const char *prefix = nullptr) - { - Icon_RegisterT(m_hInst, szSection, pIcons, _Size, prefix, this); - } - - int addOptions(WPARAM wParam, struct OPTIONSDIALOGPAGE *odp); - void openOptions(const wchar_t *pszGroup, const wchar_t *pszPage = 0, const wchar_t *pszTab = 0); - void openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage = 0, const wchar_t *pszTab = 0); - - HANDLE addIcon(const struct SKINICONDESC*); - HANDLE addTTB(const struct TTBButton*); - - HGENMENU addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib = nullptr); - - int addFont(struct FontID *pFont); - int addFont(struct FontIDW *pFont); - - int addColor(struct ColourID *pColor); - int addColor(struct ColourIDW *pColor); - - int addEffect(struct EffectID *pEffect); - int addEffect(struct EffectIDW *pEffect); - - int addPopupOption(const char *pszDescr, CMOption<bool> &pVar); - int addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &pVal); - - int addFrame(const struct CLISTFrame*); - int addHotkey(const struct HOTKEYDESC*); - int addSound(const char *name, const wchar_t *section, const wchar_t *description, const wchar_t *defaultFile = nullptr); - int addUserInfo(WPARAM wParam, struct USERINFOPAGE *odp); - - //////////////////////////////////////////////////////////////////////////////////////// - - __forceinline INT_PTR delSetting(const char *name) - { - return db_unset(0, m_szModuleName, name); - } - __forceinline INT_PTR delSetting(MCONTACT hContact, const char *name) - { - return db_unset(hContact, m_szModuleName, name); - } - - __forceinline bool getBool(const char *name, bool defaultValue = false) - { - return db_get_b(0, m_szModuleName, name, defaultValue) != 0; - } - __forceinline bool getBool(MCONTACT hContact, const char *name, bool defaultValue = false) - { - return db_get_b(hContact, m_szModuleName, name, defaultValue) != 0; - } - - __forceinline int getByte(const char *name, int defaultValue = 0) - { - return db_get_b(0, m_szModuleName, name, defaultValue); - } - __forceinline int getByte(MCONTACT hContact, const char *name, int defaultValue = 0) - { - return db_get_b(hContact, m_szModuleName, name, defaultValue); - } - - __forceinline int getWord(const char *name, int defaultValue = 0) - { - return db_get_w(0, m_szModuleName, name, defaultValue); - } - __forceinline int getWord(MCONTACT hContact, const char *name, int defaultValue = 0) - { - return db_get_w(hContact, m_szModuleName, name, defaultValue); - } - - __forceinline uint32_t getDword(const char *name, int defaultValue = 0) - { - return db_get_dw(0, m_szModuleName, name, defaultValue); - } - __forceinline uint32_t getDword(MCONTACT hContact, const char *name, int defaultValue = 0) - { - return db_get_dw(hContact, m_szModuleName, name, defaultValue); - } - - __forceinline INT_PTR getString(const char *name, DBVARIANT *result) - { - return db_get_s(0, m_szModuleName, name, result); - } - __forceinline INT_PTR getString(MCONTACT hContact, const char *name, DBVARIANT *result) - { - return db_get_s(hContact, m_szModuleName, name, result); - } - - __forceinline INT_PTR getUString(const char *name, DBVARIANT *result) - { - return db_get_utf(0, m_szModuleName, name, result); - } - __forceinline INT_PTR getUString(MCONTACT hContact, const char *name, DBVARIANT *result) - { - return db_get_utf(hContact, m_szModuleName, name, result); - } - - __forceinline INT_PTR getWString(const char *name, DBVARIANT *result) - { - return db_get_ws(0, m_szModuleName, name, result); - } - __forceinline INT_PTR getWString(MCONTACT hContact, const char *name, DBVARIANT *result) - { - return db_get_ws(hContact, m_szModuleName, name, result); - } - - __forceinline CMStringA getMStringA(const char *name, const char *szValue = nullptr) - { - return db_get_sm(0, m_szModuleName, name, szValue); - } - __forceinline CMStringA getMStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) - { - return db_get_sm(hContact, m_szModuleName, name, szValue); - } - - __forceinline char* getStringA(const char *name, const char *szValue = nullptr) - { - return db_get_sa(0, m_szModuleName, name, szValue); - } - __forceinline char* getStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) - { - return db_get_sa(hContact, m_szModuleName, name, szValue); - } - - __forceinline char* getUStringA(const char *name, const char *szValue = nullptr) - { - return db_get_utfa(0, m_szModuleName, name, szValue); - } - __forceinline char* getUStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr) - { - return db_get_utfa(hContact, m_szModuleName, name, szValue); - } - - __forceinline wchar_t* getWStringA(const char *name, const wchar_t *szValue = nullptr) - { - return db_get_wsa(0, m_szModuleName, name, szValue); - } - __forceinline wchar_t* getWStringA(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr) - { - return db_get_wsa(hContact, m_szModuleName, name, szValue); - } - - __forceinline CMStringW getMStringW(const char *name, const wchar_t *szValue = nullptr) - { - return db_get_wsm(0, m_szModuleName, name, szValue); - } - __forceinline CMStringW getMStringW(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr) - { - return db_get_wsm(hContact, m_szModuleName, name, szValue); - } - - __forceinline void setByte(const char *name, uint8_t value) - { - db_set_b(0, m_szModuleName, name, value); - } - __forceinline void setByte(MCONTACT hContact, const char *name, uint8_t value) - { - db_set_b(hContact, m_szModuleName, name, value); - } - - __forceinline void setWord(const char *name, uint16_t value) - { - db_set_w(0, m_szModuleName, name, value); - } - __forceinline void setWord(MCONTACT hContact, const char *name, uint16_t value) - { - db_set_w(hContact, m_szModuleName, name, value); - } - - __forceinline void setDword(const char *name, uint32_t value) - { - db_set_dw(0, m_szModuleName, name, value); - } - __forceinline void setDword(MCONTACT hContact, const char *name, uint32_t value) - { - db_set_dw(hContact, m_szModuleName, name, value); - } - - __forceinline void setString(const char *name, const char* value) - { - db_set_s(0, m_szModuleName, name, value); - } - __forceinline void setString(MCONTACT hContact, const char *name, const char* value) - { - db_set_s(hContact, m_szModuleName, name, value); - } - - __forceinline void setUString(const char *name, const char* value) - { - db_set_utf(0, m_szModuleName, name, value); - } - __forceinline void setUString(MCONTACT hContact, const char *name, const char* value) - { - db_set_utf(hContact, m_szModuleName, name, value); - } - - __forceinline void setWString(const char *name, const wchar_t* value) - { - db_set_ws(0, m_szModuleName, name, value); - } - __forceinline void setWString(MCONTACT hContact, const char *name, const wchar_t* value) - { - db_set_ws(hContact, m_szModuleName, name, value); - } -}; - -#pragma warning(pop) - -extern struct CMPlugin g_plugin; - -///////////////////////////////////////////////////////////////////////////////////////// -// Basic class for plugins (not protocols) written in C++ - -typedef BOOL(MIR_SYSCALL* const _pfnCrtInit)(HINSTANCE, uint32_t, void*); - -template<class T> class PLUGIN : public CMPluginBase -{ - typedef CMPluginBase CSuper; - -protected: - __forceinline PLUGIN(const char *moduleName, const PLUGININFOEX &pInfo) - : CSuper(moduleName, pInfo) - {} - - __forceinline HANDLE CreatePluginEvent(const char *name) - { - CMStringA str(FORMAT, "%s\\%s", m_szModuleName, name); - return CreateHookableEvent(str); - } - - typedef int(MIR_CDECL T::*MyEventFunc)(WPARAM, LPARAM); - __forceinline void HookPluginEvent(const char *name, MyEventFunc pFunc) - { - HookEventObj(name, (MIRANDAHOOKOBJ)*(void**)&pFunc, this); - } - - typedef INT_PTR(MIR_CDECL T::*MyServiceFunc)(WPARAM, LPARAM); - __forceinline void CreatePluginService(const char *name, MyServiceFunc pFunc) - { - CMStringA str(FORMAT, "%s\\%s", m_szModuleName, name); - CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)*(void**)&pFunc, this); - } - - typedef INT_PTR(MIR_CDECL T::*MyServiceFuncParam)(WPARAM, LPARAM, LPARAM); - __forceinline void CreatePluginServiceParam(const char *name, MyServiceFuncParam pFunc, LPARAM param) - { - CMStringA str(FORMAT, "%s\\%s", m_szModuleName, name); - CreateServiceFunctionObjParam(str, (MIRANDASERVICEOBJPARAM)*(void**)&pFunc, this, param); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Basic class for protocols with accounts - -struct CMPlugin; - -template<class P> class ACCPROTOPLUGIN : public PLUGIN<CMPlugin> -{ - typedef PLUGIN<CMPlugin> CSuper; - -protected: - ACCPROTOPLUGIN(const char *moduleName, const PLUGININFOEX &pInfo) : - CSuper(moduleName, pInfo) - { - CMPluginBase::RegisterProtocol(1002, &fnInit, &fnUninit); - } - - static PROTO_INTERFACE* fnInit(const char *szModuleName, const wchar_t *wszAccountName) - { - P *ppro = new P(szModuleName, wszAccountName); - g_arInstances.insert(ppro); - return ppro; - } - - static int fnUninit(PROTO_INTERFACE *ppro) - { - g_arInstances.remove((P*)ppro); - return 0; - } - -public: - static OBJLIST<P> g_arInstances; - - static P* getInstance(const char *szProto) - { - for (auto &it : g_arInstances) - if (mir_strcmp(szProto, it->m_szModuleName) == 0) - return it; - - return nullptr; - } - - static P* getInstance(MCONTACT hContact) - { - return getInstance(::Proto_GetBaseAccountName(hContact)); - } -}; - -template<class P> -OBJLIST<P> ACCPROTOPLUGIN<P>::g_arInstances(1, PtrKeySortT); - -#ifndef __NO_CMPLUGIN_NEEDED -#ifdef _DEBUG -#pragma comment(lib, "cmstubd.lib") -#else -#pragma comment(lib, "cmstub.lib") -#endif -#endif - -EXTERN_C MIR_APP_DLL(HINSTANCE) GetInstByAddress(void* codePtr); -EXTERN_C MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst); - -#endif // M_NEWPLUGINAPI_H__ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_NEWPLUGINAPI_H__
+#define M_NEWPLUGINAPI_H__
+
+#if !defined(HIMAGELIST)
+typedef struct _IMAGELIST* HIMAGELIST;
+#endif
+
+#include <m_gui.h>
+#include <m_database.h>
+#include <m_protocols.h>
+
+#define PLUGIN_MAKE_VERSION(a, b, c, d) (((((DWORD)(a))&0xFF)<<24)|((((DWORD)(b))&0xFF)<<16)|((((DWORD)(c))&0xFF)<<8)|(((DWORD)(d))&0xFF))
+#define MAXMODULELABELLENGTH 64
+
+#define UNICODE_AWARE 0x0001
+#define STATIC_PLUGIN 0x0002
+
+MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int langId);
+MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid);
+MIR_APP_DLL(int) SetServiceModePlugin(const char *szPluginName, WPARAM = 0, LPARAM = 0);
+
+// manually get/set flag specified at Options - Plugins - Enabled
+MIR_APP_DLL(bool) IsPluginOnWhiteList(const char *szPluginName);
+MIR_APP_DLL(void) SetPluginOnWhiteList(const char *szPluginName, bool bAllow);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Used to define the end of the MirandaPluginInterface list
+
+#define MIID_LAST {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Replaceable internal modules interface ids
+
+#define MIID_HISTORY {0x5ca0cbc1, 0x999a, 0x4ea2, {0x8b, 0x44, 0xf8, 0xf6, 0x7d, 0x7f, 0x8e, 0xbe}}
+#define MIID_UIUSERINFO {0x570b931c, 0x9af8, 0x48f1, {0xad, 0x9f, 0xc4, 0x49, 0x8c, 0x61, 0x8a, 0x77}}
+#define MIID_SRAWAY {0x5ab54c76, 0x1b4c, 0x4a00, {0xb4, 0x04, 0x48, 0xcb, 0xea, 0x5f, 0xef, 0xe7}}
+#define MIID_SREMAIL {0xd005b5a6, 0x1b66, 0x445a, {0xb6, 0x03, 0x74, 0xd4, 0xd4, 0x55, 0x2d, 0xe2}}
+#define MIID_SRFILE {0x989d104d, 0xacb7, 0x4ee0, {0xb9, 0x6d, 0x67, 0xce, 0x46, 0x53, 0xb6, 0x95}}
+#define MIID_UIHISTORY {0x7f7e3d98, 0xce1f, 0x4962, {0x82, 0x84, 0x96, 0x85, 0x50, 0xf1, 0xd3, 0xd9}}
+#define MIID_AUTOAWAY {0x9c87f7dc, 0x3bd7, 0x4983, {0xb7, 0xfb, 0xb8, 0x48, 0xfd, 0xbc, 0x91, 0xf0}}
+#define MIID_USERONLINE {0x130829e0, 0x2463, 0x4ff8, {0xbb, 0xc8, 0xce, 0x73, 0xc0, 0x18, 0x84, 0x42}}
+#define MIID_CRYPTO {0x415ca6e1, 0x895f, 0x40e6, {0x87, 0xbd, 0x9b, 0x39, 0x60, 0x16, 0xd0, 0xe5}}
+#define MIID_POPUP {0xb275f4a4, 0xe347, 0x4515, {0xaf, 0x71, 0x77, 0xd0, 0x1e, 0xef, 0x54, 0x41}}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Common plugin interfaces (core plugins)
+
+#define MIID_DATABASE {0xae77fd33, 0xe484, 0x4dc7, {0x8c, 0xbc, 0x09, 0x9f, 0xed, 0xcc, 0xcf, 0xdd}}
+#define MIID_CLIST {0x9d8da8bf, 0x665b, 0x4908, {0x9e, 0x61, 0x9f, 0x75, 0x98, 0xae, 0x33, 0x0e}}
+#define MIID_SRMM {0x58c7eea6, 0xf9db, 0x4dd9, {0x80, 0x36, 0xae, 0x80, 0x2b, 0xc0, 0x41, 0x4c}}
+#define MIID_TESTPLUGIN {0x53b974f4, 0x3c74, 0x4dba, {0x8f, 0xc2, 0x6f, 0x92, 0xfe, 0x01, 0x3b, 0x8c}}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Special exception interface for protocols.
+// This interface allows more than one plugin to implement it at the same time
+
+#define MIID_PROTOCOL {0x2a3c815e, 0xa7d9, 0x424b, {0xba, 0x30, 0x2, 0xd0, 0x83, 0x22, 0x90, 0x85}}
+
+#define MIID_SERVICEMODE {0x8a92c026, 0x953a, 0x4f5f, { 0x99, 0x21, 0xf2, 0xc2, 0xdc, 0x19, 0x5e, 0xc5}}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Each service mode plugin must implement MS_SERVICEMODE_LAUNCH
+// This service might return one of the following values:
+// SERVICE_CONTINUE - load Miranda normally, like there's no service plugins at all
+// SERVICE_ONLYDB - load database and then execute service plugin only
+// SERVICE_MONOPOLY - execute only service plugin, even without database
+// SERVICE_FAILED - terminate Miranda execution
+
+#define SERVICE_CONTINUE 0
+#define SERVICE_ONLYDB 1
+#define SERVICE_MONOPOLY 2
+#define SERVICE_FAILED (-1)
+
+#define MS_SERVICEMODE_LAUNCH "ServiceMode/Launch"
+
+struct PLUGININFOEX
+{
+ int cbSize;
+ const char *shortName;
+ uint32_t version;
+ const char *description;
+ const char *author;
+ const char *copyright;
+ const char *homepage;
+ uint8_t flags; // right now the only flag, UNICODE_AWARE, is recognized here
+ MUUID uuid; // plugin's unique identifier
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda/System/LoadModule event
+// called when a plugin is being loaded dynamically
+// wParam = CMPluginBase*
+// lParam = HINSTANCE of the loaded plugin
+
+#define ME_SYSTEM_MODULELOAD "Miranda/System/LoadModule"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda/System/UnloadModule event
+// called when a plugin is being unloaded dynamically
+// wParam = CMPluginBase*
+// lParam = HINSTANCE of the plugin to be unloaded
+
+#define ME_SYSTEM_MODULEUNLOAD "Miranda/System/UnloadModule"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// plugin's class
+
+// initializes an empty account
+typedef struct PROTO_INTERFACE* (*pfnInitProto)(const char* szModuleName, const wchar_t* szUserName);
+
+// deallocates an account instance
+typedef int(*pfnUninitProto)(PROTO_INTERFACE*);
+
+#pragma warning(push)
+#pragma warning(disable:4275)
+
+struct IcolibItem;
+
+class MIR_APP_EXPORT CMPluginBase : public MNonCopyable
+{
+ void tryOpenLog();
+
+protected:
+ HINSTANCE m_hInst;
+ const char *m_szModuleName;
+ const PLUGININFOEX &m_pInfo;
+ HANDLE m_hLogger = nullptr;
+ LIST<IcolibItem> m_arIcons;
+
+ CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo);
+ ~CMPluginBase();
+
+ // pass one of PROTOTYPE_* constants as type
+ void RegisterProtocol(int type, pfnInitProto = nullptr, pfnUninitProto = nullptr);
+ void SetUniqueId(const char *pszUniqueId);
+
+public:
+ void debugLogA(LPCSTR szFormat, ...);
+ void debugLogW(LPCWSTR wszFormat, ...);
+
+ __forceinline void addIcolib(HANDLE hIcolib) { m_arIcons.insert((IcolibItem*)hIcolib); }
+ int addImgListIcon(HIMAGELIST himl, int iconId);
+ HICON getIcon(int iconId, bool big = false);
+ HANDLE getIconHandle(int iconId);
+ void releaseIcon(int iconId, bool big = false);
+
+ __forceinline const PLUGININFOEX& getInfo() const { return m_pInfo; }
+ __forceinline const char* getModule() const { return m_szModuleName; }
+
+ __forceinline HINSTANCE getInst() const { return m_hInst; }
+ __forceinline void setInst(HINSTANCE hInst) { m_hInst = hInst; }
+
+ virtual int Load();
+ virtual int Unload();
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // registering module's resources
+
+ template <size_t _Size>
+ __forceinline void registerIcon(const char *szSection, IconItem(&pIcons)[_Size], const char *prefix = nullptr)
+ {
+ Icon_Register(m_hInst, szSection, pIcons, _Size, prefix, this);
+ }
+
+ template <size_t _Size>
+ __forceinline void registerIconW(const wchar_t *szSection, IconItemT(&pIcons)[_Size], const char *prefix = nullptr)
+ {
+ Icon_RegisterT(m_hInst, szSection, pIcons, _Size, prefix, this);
+ }
+
+ int addOptions(WPARAM wParam, struct OPTIONSDIALOGPAGE *odp);
+ void openOptions(const wchar_t *pszGroup, const wchar_t *pszPage = 0, const wchar_t *pszTab = 0);
+ void openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage = 0, const wchar_t *pszTab = 0);
+
+ HANDLE addIcon(const struct SKINICONDESC*);
+ HANDLE addTTB(const struct TTBButton*);
+
+ HGENMENU addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib = nullptr);
+
+ int addFont(struct FontID *pFont);
+ int addFont(struct FontIDW *pFont);
+
+ int addColor(struct ColourID *pColor);
+ int addColor(struct ColourIDW *pColor);
+
+ int addEffect(struct EffectID *pEffect);
+ int addEffect(struct EffectIDW *pEffect);
+
+ int addPopupOption(const char *pszDescr, CMOption<bool> &pVar);
+ int addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &pVal);
+
+ int addFrame(const struct CLISTFrame*);
+ int addHotkey(const struct HOTKEYDESC*);
+ int addSound(const char *name, const wchar_t *section, const wchar_t *description, const wchar_t *defaultFile = nullptr);
+ int addUserInfo(WPARAM wParam, struct USERINFOPAGE *odp);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline INT_PTR delSetting(const char *name)
+ {
+ return db_unset(0, m_szModuleName, name);
+ }
+ __forceinline INT_PTR delSetting(MCONTACT hContact, const char *name)
+ {
+ return db_unset(hContact, m_szModuleName, name);
+ }
+
+ __forceinline bool getBool(const char *name, bool defaultValue = false)
+ {
+ return db_get_b(0, m_szModuleName, name, defaultValue) != 0;
+ }
+ __forceinline bool getBool(MCONTACT hContact, const char *name, bool defaultValue = false)
+ {
+ return db_get_b(hContact, m_szModuleName, name, defaultValue) != 0;
+ }
+
+ __forceinline int getByte(const char *name, int defaultValue = 0)
+ {
+ return db_get_b(0, m_szModuleName, name, defaultValue);
+ }
+ __forceinline int getByte(MCONTACT hContact, const char *name, int defaultValue = 0)
+ {
+ return db_get_b(hContact, m_szModuleName, name, defaultValue);
+ }
+
+ __forceinline int getWord(const char *name, int defaultValue = 0)
+ {
+ return db_get_w(0, m_szModuleName, name, defaultValue);
+ }
+ __forceinline int getWord(MCONTACT hContact, const char *name, int defaultValue = 0)
+ {
+ return db_get_w(hContact, m_szModuleName, name, defaultValue);
+ }
+
+ __forceinline uint32_t getDword(const char *name, int defaultValue = 0)
+ {
+ return db_get_dw(0, m_szModuleName, name, defaultValue);
+ }
+ __forceinline uint32_t getDword(MCONTACT hContact, const char *name, int defaultValue = 0)
+ {
+ return db_get_dw(hContact, m_szModuleName, name, defaultValue);
+ }
+
+ __forceinline INT_PTR getString(const char *name, DBVARIANT *result)
+ {
+ return db_get_s(0, m_szModuleName, name, result);
+ }
+ __forceinline INT_PTR getString(MCONTACT hContact, const char *name, DBVARIANT *result)
+ {
+ return db_get_s(hContact, m_szModuleName, name, result);
+ }
+
+ __forceinline INT_PTR getUString(const char *name, DBVARIANT *result)
+ {
+ return db_get_utf(0, m_szModuleName, name, result);
+ }
+ __forceinline INT_PTR getUString(MCONTACT hContact, const char *name, DBVARIANT *result)
+ {
+ return db_get_utf(hContact, m_szModuleName, name, result);
+ }
+
+ __forceinline INT_PTR getWString(const char *name, DBVARIANT *result)
+ {
+ return db_get_ws(0, m_szModuleName, name, result);
+ }
+ __forceinline INT_PTR getWString(MCONTACT hContact, const char *name, DBVARIANT *result)
+ {
+ return db_get_ws(hContact, m_szModuleName, name, result);
+ }
+
+ __forceinline CMStringA getMStringA(const char *name, const char *szValue = nullptr)
+ {
+ return db_get_sm(0, m_szModuleName, name, szValue);
+ }
+ __forceinline CMStringA getMStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr)
+ {
+ return db_get_sm(hContact, m_szModuleName, name, szValue);
+ }
+
+ __forceinline char* getStringA(const char *name, const char *szValue = nullptr)
+ {
+ return db_get_sa(0, m_szModuleName, name, szValue);
+ }
+ __forceinline char* getStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr)
+ {
+ return db_get_sa(hContact, m_szModuleName, name, szValue);
+ }
+
+ __forceinline char* getUStringA(const char *name, const char *szValue = nullptr)
+ {
+ return db_get_utfa(0, m_szModuleName, name, szValue);
+ }
+ __forceinline char* getUStringA(MCONTACT hContact, const char *name, const char *szValue = nullptr)
+ {
+ return db_get_utfa(hContact, m_szModuleName, name, szValue);
+ }
+
+ __forceinline wchar_t* getWStringA(const char *name, const wchar_t *szValue = nullptr)
+ {
+ return db_get_wsa(0, m_szModuleName, name, szValue);
+ }
+ __forceinline wchar_t* getWStringA(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr)
+ {
+ return db_get_wsa(hContact, m_szModuleName, name, szValue);
+ }
+
+ __forceinline CMStringW getMStringW(const char *name, const wchar_t *szValue = nullptr)
+ {
+ return db_get_wsm(0, m_szModuleName, name, szValue);
+ }
+ __forceinline CMStringW getMStringW(MCONTACT hContact, const char *name, const wchar_t *szValue = nullptr)
+ {
+ return db_get_wsm(hContact, m_szModuleName, name, szValue);
+ }
+
+ __forceinline void setByte(const char *name, uint8_t value)
+ {
+ db_set_b(0, m_szModuleName, name, value);
+ }
+ __forceinline void setByte(MCONTACT hContact, const char *name, uint8_t value)
+ {
+ db_set_b(hContact, m_szModuleName, name, value);
+ }
+
+ __forceinline void setWord(const char *name, uint16_t value)
+ {
+ db_set_w(0, m_szModuleName, name, value);
+ }
+ __forceinline void setWord(MCONTACT hContact, const char *name, uint16_t value)
+ {
+ db_set_w(hContact, m_szModuleName, name, value);
+ }
+
+ __forceinline void setDword(const char *name, uint32_t value)
+ {
+ db_set_dw(0, m_szModuleName, name, value);
+ }
+ __forceinline void setDword(MCONTACT hContact, const char *name, uint32_t value)
+ {
+ db_set_dw(hContact, m_szModuleName, name, value);
+ }
+
+ __forceinline void setString(const char *name, const char* value)
+ {
+ db_set_s(0, m_szModuleName, name, value);
+ }
+ __forceinline void setString(MCONTACT hContact, const char *name, const char* value)
+ {
+ db_set_s(hContact, m_szModuleName, name, value);
+ }
+
+ __forceinline void setUString(const char *name, const char* value)
+ {
+ db_set_utf(0, m_szModuleName, name, value);
+ }
+ __forceinline void setUString(MCONTACT hContact, const char *name, const char* value)
+ {
+ db_set_utf(hContact, m_szModuleName, name, value);
+ }
+
+ __forceinline void setWString(const char *name, const wchar_t* value)
+ {
+ db_set_ws(0, m_szModuleName, name, value);
+ }
+ __forceinline void setWString(MCONTACT hContact, const char *name, const wchar_t* value)
+ {
+ db_set_ws(hContact, m_szModuleName, name, value);
+ }
+};
+
+#pragma warning(pop)
+
+extern struct CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Basic class for plugins (not protocols) written in C++
+
+typedef BOOL(MIR_SYSCALL* const _pfnCrtInit)(HINSTANCE, uint32_t, void*);
+
+template<class T> class PLUGIN : public CMPluginBase
+{
+ typedef CMPluginBase CSuper;
+
+protected:
+ __forceinline PLUGIN(const char *moduleName, const PLUGININFOEX &pInfo)
+ : CSuper(moduleName, pInfo)
+ {}
+
+ __forceinline HANDLE CreatePluginEvent(const char *name)
+ {
+ CMStringA str(FORMAT, "%s\\%s", m_szModuleName, name);
+ return CreateHookableEvent(str);
+ }
+
+ typedef int(MIR_CDECL T::*MyEventFunc)(WPARAM, LPARAM);
+ __forceinline void HookPluginEvent(const char *name, MyEventFunc pFunc)
+ {
+ HookEventObj(name, (MIRANDAHOOKOBJ)*(void**)&pFunc, this);
+ }
+
+ typedef INT_PTR(MIR_CDECL T::*MyServiceFunc)(WPARAM, LPARAM);
+ __forceinline void CreatePluginService(const char *name, MyServiceFunc pFunc)
+ {
+ CMStringA str(FORMAT, "%s\\%s", m_szModuleName, name);
+ CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)*(void**)&pFunc, this);
+ }
+
+ typedef INT_PTR(MIR_CDECL T::*MyServiceFuncParam)(WPARAM, LPARAM, LPARAM);
+ __forceinline void CreatePluginServiceParam(const char *name, MyServiceFuncParam pFunc, LPARAM param)
+ {
+ CMStringA str(FORMAT, "%s\\%s", m_szModuleName, name);
+ CreateServiceFunctionObjParam(str, (MIRANDASERVICEOBJPARAM)*(void**)&pFunc, this, param);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Basic class for protocols with accounts
+
+struct CMPlugin;
+
+template<class P> class ACCPROTOPLUGIN : public PLUGIN<CMPlugin>
+{
+ typedef PLUGIN<CMPlugin> CSuper;
+
+protected:
+ ACCPROTOPLUGIN(const char *moduleName, const PLUGININFOEX &pInfo) :
+ CSuper(moduleName, pInfo)
+ {
+ CMPluginBase::RegisterProtocol(1002, &fnInit, &fnUninit);
+ }
+
+ static PROTO_INTERFACE* fnInit(const char *szModuleName, const wchar_t *wszAccountName)
+ {
+ P *ppro = new P(szModuleName, wszAccountName);
+ g_arInstances.insert(ppro);
+ return ppro;
+ }
+
+ static int fnUninit(PROTO_INTERFACE *ppro)
+ {
+ g_arInstances.remove((P*)ppro);
+ return 0;
+ }
+
+public:
+ static OBJLIST<P> g_arInstances;
+
+ static P* getInstance(const char *szProto)
+ {
+ for (auto &it : g_arInstances)
+ if (mir_strcmp(szProto, it->m_szModuleName) == 0)
+ return it;
+
+ return nullptr;
+ }
+
+ static P* getInstance(MCONTACT hContact)
+ {
+ return getInstance(::Proto_GetBaseAccountName(hContact));
+ }
+};
+
+template<class P>
+OBJLIST<P> ACCPROTOPLUGIN<P>::g_arInstances(1, PtrKeySortT);
+
+#ifndef __NO_CMPLUGIN_NEEDED
+#ifdef _DEBUG
+#pragma comment(lib, "cmstubd.lib")
+#else
+#pragma comment(lib, "cmstub.lib")
+#endif
+#endif
+
+EXTERN_C MIR_APP_DLL(HINSTANCE) GetInstByAddress(void* codePtr);
+EXTERN_C MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst);
+
+#endif // M_NEWPLUGINAPI_H__
diff --git a/include/statusmodes.h b/include/statusmodes.h index ddeedcb895..78559de1e1 100644 --- a/include/statusmodes.h +++ b/include/statusmodes.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONChildren.cpp b/libs/libjson/src/JSONChildren.cpp index be5494dffe..cd6fa5ad48 100644 --- a/libs/libjson/src/JSONChildren.cpp +++ b/libs/libjson/src/JSONChildren.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONDebug.cpp b/libs/libjson/src/JSONDebug.cpp index d44f015633..5d3ee4d30a 100644 --- a/libs/libjson/src/JSONDebug.cpp +++ b/libs/libjson/src/JSONDebug.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONIterators.cpp b/libs/libjson/src/JSONIterators.cpp index c1168bf462..75466c4d03 100644 --- a/libs/libjson/src/JSONIterators.cpp +++ b/libs/libjson/src/JSONIterators.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONMemory.cpp b/libs/libjson/src/JSONMemory.cpp index fdad06b114..140a69a458 100644 --- a/libs/libjson/src/JSONMemory.cpp +++ b/libs/libjson/src/JSONMemory.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONNode.cpp b/libs/libjson/src/JSONNode.cpp index a0bcabb525..1340254b02 100644 --- a/libs/libjson/src/JSONNode.cpp +++ b/libs/libjson/src/JSONNode.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONNode_Mutex.cpp b/libs/libjson/src/JSONNode_Mutex.cpp index 6a515b08cc..83e9238b5f 100644 --- a/libs/libjson/src/JSONNode_Mutex.cpp +++ b/libs/libjson/src/JSONNode_Mutex.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONWorker.cpp b/libs/libjson/src/JSONWorker.cpp index c44f74abd4..a37daa6a27 100644 --- a/libs/libjson/src/JSONWorker.cpp +++ b/libs/libjson/src/JSONWorker.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONWriter.cpp b/libs/libjson/src/JSONWriter.cpp index eec43d2aee..926dc951d4 100644 --- a/libs/libjson/src/JSONWriter.cpp +++ b/libs/libjson/src/JSONWriter.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/internalJSONNode.cpp b/libs/libjson/src/internalJSONNode.cpp index 6eca8f4dfa..756b7340ff 100644 --- a/libs/libjson/src/internalJSONNode.cpp +++ b/libs/libjson/src/internalJSONNode.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/libJSON.cpp b/libs/libjson/src/libJSON.cpp index 3df3619f0a..592de76dbe 100644 --- a/libs/libjson/src/libJSON.cpp +++ b/libs/libjson/src/libJSON.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/stdafx.cxx b/libs/libjson/src/stdafx.cxx index 888794dff6..9ba7cc9a76 100644 --- a/libs/libjson/src/stdafx.cxx +++ b/libs/libjson/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/libs/libjson/src/stdafx.h b/libs/libjson/src/stdafx.h index 4ffbc32144..658248f9be 100644 --- a/libs/libjson/src/stdafx.h +++ b/libs/libjson/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libsignal/src/stdafx.cxx b/libs/libsignal/src/stdafx.cxx index 98a006e921..e23069a5b8 100644 --- a/libs/libsignal/src/stdafx.cxx +++ b/libs/libsignal/src/stdafx.cxx @@ -1,19 +1,19 @@ -/* - -Copyright (C) 2012-22 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/>. -*/ - +/*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/libs/libsignal/src/stdafx.h b/libs/libsignal/src/stdafx.h index a772837321..eeb510e840 100644 --- a/libs/libsignal/src/stdafx.h +++ b/libs/libsignal/src/stdafx.h @@ -1,26 +1,26 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#include <stdio.h> +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+#include <stdio.h>
diff --git a/libs/mTextControl/src/stdafx.cxx b/libs/mTextControl/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/libs/mTextControl/src/stdafx.cxx +++ b/libs/mTextControl/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/plugins/AVS/src/acc.cpp b/plugins/AVS/src/acc.cpp index 359a5660c3..e637b83d23 100644 --- a/plugins/AVS/src/acc.cpp +++ b/plugins/AVS/src/acc.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/cache.cpp b/plugins/AVS/src/cache.cpp index c60c8cd05b..318834644d 100644 --- a/plugins/AVS/src/cache.cpp +++ b/plugins/AVS/src/cache.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/contact_ava.cpp b/plugins/AVS/src/contact_ava.cpp index d2d8c08399..2f67afc693 100644 --- a/plugins/AVS/src/contact_ava.cpp +++ b/plugins/AVS/src/contact_ava.cpp @@ -1,263 +1,263 @@ -/* -Copyright (C) 2012-22 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" - -class CContactAvatarDlg : public CDlgBase -{ - MCONTACT m_hContact; - HANDLE m_hHook = 0; - - UI_MESSAGE_MAP(CContactAvatarDlg, CDlgBase); - UI_MESSAGE(WM_DRAWITEM, OnDrawItem); - UI_MESSAGE(DM_AVATARCHANGED, OnAvatarChanged); - UI_MESSAGE_MAP_END(); - - INT_PTR OnAvatarChanged(UINT, WPARAM, LPARAM) - { - InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE); - return 0; - } - - INT_PTR OnDrawItem(UINT, WPARAM, LPARAM lParam) - { - LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; - if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) { - AVATARDRAWREQUEST avdrq = { 0 }; - GetClientRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), &avdrq.rcDraw); - - FillRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNFACE)); - - avdrq.hContact = m_hContact; - avdrq.hTargetDC = dis->hDC; - avdrq.dwFlags |= AVDRQ_DRAWBORDER; - avdrq.clrBorder = GetSysColor(COLOR_BTNTEXT); - avdrq.radius = 6; - if (!CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq)) { - // Get text rectangle - RECT rc = avdrq.rcDraw; - rc.top += 10; - rc.bottom -= 10; - rc.left += 10; - rc.right -= 10; - - // Calc text size - RECT rc_ret = rc; - DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc_ret, - DT_WORDBREAK | DT_NOPREFIX | DT_CENTER | DT_CALCRECT); - - // Calc needed size - rc.top += ((rc.bottom - rc.top) - (rc_ret.bottom - rc_ret.top)) / 2; - rc.bottom = rc.top + (rc_ret.bottom - rc_ret.top); - DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc, - DT_WORDBREAK | DT_NOPREFIX | DT_CENTER); - } - - FrameRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNSHADOW)); - } - return TRUE; - } - - void ReloadAvatar() - { - SaveTransparentData(m_hwnd, m_hContact, chkProtect.IsChecked()); - ChangeAvatar(m_hContact, true); - OnAvatarChanged(0, 0, 0); - } - - void SetAvatarName() - { - uint8_t is_locked = db_get_b(m_hContact, "ContactPhoto", "Locked", 0); - - wchar_t szFinalName[MAX_PATH]; - szFinalName[0] = 0; - - DBVARIANT dbv = {}; - if (is_locked && !db_get_ws(m_hContact, "ContactPhoto", "Backup", &dbv)) { - MyPathToAbsolute(dbv.pwszVal, szFinalName); - db_free(&dbv); - } - else if (!db_get_ws(m_hContact, "ContactPhoto", "RFile", &dbv)) { - MyPathToAbsolute(dbv.pwszVal, szFinalName); - db_free(&dbv); - } - else if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { - MyPathToAbsolute(dbv.pwszVal, szFinalName); - db_free(&dbv); - } - szFinalName[MAX_PATH - 1] = 0; - SetDlgItemText(m_hwnd, IDC_AVATARNAME, szFinalName); - } - - CCtrlSpin spin1, spin2; - CCtrlCheck chkProtect, chkMakeTrans, chkHide; - CCtrlButton btnUseDefault, btnChange, btnReset, btnDefault; - -public: - CContactAvatarDlg(MCONTACT hContact) : - CDlgBase(g_plugin, IDD_AVATAROPTIONS), - m_hContact(hContact), - spin1(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2), - spin2(this, IDC_BKG_COLOR_DIFFERENCE_SPIN, 100), - btnReset(this, IDC_RESET), - btnChange(this, IDC_CHANGE), - btnDefault(this, IDC_DELETE), - btnUseDefault(this, ID_USE_DEFAULTS), - chkHide(this, IDC_HIDEAVATAR), - chkProtect(this, IDC_PROTECTAVATAR), - chkMakeTrans(this, IDC_MAKETRANSPBKG) - { - btnReset.OnClick = Callback(this, &CContactAvatarDlg::onClick_Reset); - btnChange.OnClick = Callback(this, &CContactAvatarDlg::onClick_Change); - btnDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_Default); - btnUseDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_UseDefault); - - chkProtect.OnChange = Callback(this, &CContactAvatarDlg::onChange_Protect); - chkMakeTrans.OnChange = Callback(this, &CContactAvatarDlg::onChange_MakeTrans); - } - - bool OnInitDialog() override - { - m_hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED); - - if (m_hContact) { - wchar_t szTitle[512]; - mir_snwprintf(szTitle, TranslateT("Set avatar options for %s"), Clist_GetContactDisplayName(m_hContact)); - SetWindowText(m_hwnd, szTitle); - } - - SetAvatarName(); - ShowWindow(m_hwnd, SW_SHOWNORMAL); - OnAvatarChanged(0, 0, 0); - chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked", 0)); - chkHide.SetState(Contact::IsHidden(m_hContact)); - - LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact)); - SendMessage(m_hwnd, WM_SETICON, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_AVATAR)); - return true; - } - - bool OnApply() override - { - bool locked = chkProtect.IsChecked(); - bool hidden = chkHide.IsChecked(); - - SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden); - if (hidden != Contact::IsHidden(m_hContact)) - Contact::Hide(m_hContact, hidden); - - if (!locked && db_get_b(m_hContact, "ContactPhoto", "NeedUpdate", 0)) - QueueAdd(m_hContact); - return true; - } - - void OnDestroy() override - { - UnhookEvent(m_hHook); - } - - void onClick_UseDefault(CCtrlButton *) - { - MCONTACT hContact = GetContactThatHaveTheAvatar(m_hContact); - - db_unset(hContact, "ContactPhoto", "MakeTransparentBkg"); - db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints"); - db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff"); - - LoadTransparentData(m_hwnd, hContact); - ReloadAvatar(); - } - - void onClick_Change(CCtrlButton *) - { - SetAvatar(m_hContact, 0); - SetAvatarName(); - chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked")); - } - - void onClick_Reset(CCtrlButton *) - { - ProtectAvatar(m_hContact, 0); - if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) { - DBVARIANT dbv = { 0 }; - if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { - DeleteFileW(dbv.pwszVal); - db_free(&dbv); - } - } - db_unset(m_hContact, "ContactPhoto", "Locked"); - db_unset(m_hContact, "ContactPhoto", "Backup"); - db_unset(m_hContact, "ContactPhoto", "RFile"); - db_unset(m_hContact, "ContactPhoto", "File"); - db_unset(m_hContact, "ContactPhoto", "Format"); - - db_unset(m_hContact, Proto_GetBaseAccountName(m_hContact), "AvatarHash"); - DeleteAvatarFromCache(m_hContact, FALSE); - - QueueAdd(m_hContact); - DestroyWindow(m_hwnd); - } - - void onClick_Default(CCtrlButton *) - { - if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) { - DBVARIANT dbv = { 0 }; - ProtectAvatar(m_hContact, 0); - if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { - DeleteFileW(dbv.pwszVal); - db_free(&dbv); - } - } - db_unset(m_hContact, "ContactPhoto", "Locked"); - db_unset(m_hContact, "ContactPhoto", "Backup"); - db_unset(m_hContact, "ContactPhoto", "RFile"); - db_unset(m_hContact, "ContactPhoto", "File"); - db_unset(m_hContact, "ContactPhoto", "Format"); - DeleteAvatarFromCache(m_hContact, FALSE); - SetAvatarName(); - OnAvatarChanged(0, 0, 0); - } - - void onChange_Protect(CCtrlCheck *) - { - ProtectAvatar(m_hContact, chkProtect.IsChecked()); - } - - void onChange_MakeTrans(CCtrlCheck *) - { - bool enable = chkMakeTrans.IsChecked(); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable); - - ReloadAvatar(); - } -}; - -INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam) -{ - if (wParam) { - auto *pDlg = new CContactAvatarDlg(wParam); - if (lParam) - pDlg->SetParent((HWND)lParam); - pDlg->Create(); - } - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+class CContactAvatarDlg : public CDlgBase
+{
+ MCONTACT m_hContact;
+ HANDLE m_hHook = 0;
+
+ UI_MESSAGE_MAP(CContactAvatarDlg, CDlgBase);
+ UI_MESSAGE(WM_DRAWITEM, OnDrawItem);
+ UI_MESSAGE(DM_AVATARCHANGED, OnAvatarChanged);
+ UI_MESSAGE_MAP_END();
+
+ INT_PTR OnAvatarChanged(UINT, WPARAM, LPARAM)
+ {
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE);
+ return 0;
+ }
+
+ INT_PTR OnDrawItem(UINT, WPARAM, LPARAM lParam)
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
+ AVATARDRAWREQUEST avdrq = { 0 };
+ GetClientRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), &avdrq.rcDraw);
+
+ FillRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNFACE));
+
+ avdrq.hContact = m_hContact;
+ avdrq.hTargetDC = dis->hDC;
+ avdrq.dwFlags |= AVDRQ_DRAWBORDER;
+ avdrq.clrBorder = GetSysColor(COLOR_BTNTEXT);
+ avdrq.radius = 6;
+ if (!CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq)) {
+ // Get text rectangle
+ RECT rc = avdrq.rcDraw;
+ rc.top += 10;
+ rc.bottom -= 10;
+ rc.left += 10;
+ rc.right -= 10;
+
+ // Calc text size
+ RECT rc_ret = rc;
+ DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc_ret,
+ DT_WORDBREAK | DT_NOPREFIX | DT_CENTER | DT_CALCRECT);
+
+ // Calc needed size
+ rc.top += ((rc.bottom - rc.top) - (rc_ret.bottom - rc_ret.top)) / 2;
+ rc.bottom = rc.top + (rc_ret.bottom - rc_ret.top);
+ DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc,
+ DT_WORDBREAK | DT_NOPREFIX | DT_CENTER);
+ }
+
+ FrameRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNSHADOW));
+ }
+ return TRUE;
+ }
+
+ void ReloadAvatar()
+ {
+ SaveTransparentData(m_hwnd, m_hContact, chkProtect.IsChecked());
+ ChangeAvatar(m_hContact, true);
+ OnAvatarChanged(0, 0, 0);
+ }
+
+ void SetAvatarName()
+ {
+ uint8_t is_locked = db_get_b(m_hContact, "ContactPhoto", "Locked", 0);
+
+ wchar_t szFinalName[MAX_PATH];
+ szFinalName[0] = 0;
+
+ DBVARIANT dbv = {};
+ if (is_locked && !db_get_ws(m_hContact, "ContactPhoto", "Backup", &dbv)) {
+ MyPathToAbsolute(dbv.pwszVal, szFinalName);
+ db_free(&dbv);
+ }
+ else if (!db_get_ws(m_hContact, "ContactPhoto", "RFile", &dbv)) {
+ MyPathToAbsolute(dbv.pwszVal, szFinalName);
+ db_free(&dbv);
+ }
+ else if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ MyPathToAbsolute(dbv.pwszVal, szFinalName);
+ db_free(&dbv);
+ }
+ szFinalName[MAX_PATH - 1] = 0;
+ SetDlgItemText(m_hwnd, IDC_AVATARNAME, szFinalName);
+ }
+
+ CCtrlSpin spin1, spin2;
+ CCtrlCheck chkProtect, chkMakeTrans, chkHide;
+ CCtrlButton btnUseDefault, btnChange, btnReset, btnDefault;
+
+public:
+ CContactAvatarDlg(MCONTACT hContact) :
+ CDlgBase(g_plugin, IDD_AVATAROPTIONS),
+ m_hContact(hContact),
+ spin1(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2),
+ spin2(this, IDC_BKG_COLOR_DIFFERENCE_SPIN, 100),
+ btnReset(this, IDC_RESET),
+ btnChange(this, IDC_CHANGE),
+ btnDefault(this, IDC_DELETE),
+ btnUseDefault(this, ID_USE_DEFAULTS),
+ chkHide(this, IDC_HIDEAVATAR),
+ chkProtect(this, IDC_PROTECTAVATAR),
+ chkMakeTrans(this, IDC_MAKETRANSPBKG)
+ {
+ btnReset.OnClick = Callback(this, &CContactAvatarDlg::onClick_Reset);
+ btnChange.OnClick = Callback(this, &CContactAvatarDlg::onClick_Change);
+ btnDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_Default);
+ btnUseDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_UseDefault);
+
+ chkProtect.OnChange = Callback(this, &CContactAvatarDlg::onChange_Protect);
+ chkMakeTrans.OnChange = Callback(this, &CContactAvatarDlg::onChange_MakeTrans);
+ }
+
+ bool OnInitDialog() override
+ {
+ m_hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED);
+
+ if (m_hContact) {
+ wchar_t szTitle[512];
+ mir_snwprintf(szTitle, TranslateT("Set avatar options for %s"), Clist_GetContactDisplayName(m_hContact));
+ SetWindowText(m_hwnd, szTitle);
+ }
+
+ SetAvatarName();
+ ShowWindow(m_hwnd, SW_SHOWNORMAL);
+ OnAvatarChanged(0, 0, 0);
+ chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked", 0));
+ chkHide.SetState(Contact::IsHidden(m_hContact));
+
+ LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact));
+ SendMessage(m_hwnd, WM_SETICON, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_AVATAR));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ bool locked = chkProtect.IsChecked();
+ bool hidden = chkHide.IsChecked();
+
+ SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden);
+ if (hidden != Contact::IsHidden(m_hContact))
+ Contact::Hide(m_hContact, hidden);
+
+ if (!locked && db_get_b(m_hContact, "ContactPhoto", "NeedUpdate", 0))
+ QueueAdd(m_hContact);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ UnhookEvent(m_hHook);
+ }
+
+ void onClick_UseDefault(CCtrlButton *)
+ {
+ MCONTACT hContact = GetContactThatHaveTheAvatar(m_hContact);
+
+ db_unset(hContact, "ContactPhoto", "MakeTransparentBkg");
+ db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints");
+ db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff");
+
+ LoadTransparentData(m_hwnd, hContact);
+ ReloadAvatar();
+ }
+
+ void onClick_Change(CCtrlButton *)
+ {
+ SetAvatar(m_hContact, 0);
+ SetAvatarName();
+ chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked"));
+ }
+
+ void onClick_Reset(CCtrlButton *)
+ {
+ ProtectAvatar(m_hContact, 0);
+ if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFileW(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+
+ db_unset(m_hContact, Proto_GetBaseAccountName(m_hContact), "AvatarHash");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+
+ QueueAdd(m_hContact);
+ DestroyWindow(m_hwnd);
+ }
+
+ void onClick_Default(CCtrlButton *)
+ {
+ if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ ProtectAvatar(m_hContact, 0);
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFileW(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+ SetAvatarName();
+ OnAvatarChanged(0, 0, 0);
+ }
+
+ void onChange_Protect(CCtrlCheck *)
+ {
+ ProtectAvatar(m_hContact, chkProtect.IsChecked());
+ }
+
+ void onChange_MakeTrans(CCtrlCheck *)
+ {
+ bool enable = chkMakeTrans.IsChecked();
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable);
+
+ ReloadAvatar();
+ }
+};
+
+INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam) {
+ auto *pDlg = new CContactAvatarDlg(wParam);
+ if (lParam)
+ pDlg->SetParent((HWND)lParam);
+ pDlg->Create();
+ }
+ return 0;
+}
diff --git a/plugins/AVS/src/main.cpp b/plugins/AVS/src/main.cpp index 2ad7a85047..bab379ba4d 100644 --- a/plugins/AVS/src/main.cpp +++ b/plugins/AVS/src/main.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/options.cpp b/plugins/AVS/src/options.cpp index 83348a3f7f..f281c686ed 100644 --- a/plugins/AVS/src/options.cpp +++ b/plugins/AVS/src/options.cpp @@ -1,441 +1,441 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (c) 2000-04 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -extern OBJLIST<protoPicCacheEntry> g_ProtoPictures; -extern HANDLE hEventChanged; - -static BOOL dialoginit = TRUE; - -static void RemoveProtoPic(protoPicCacheEntry *pce) -{ - if (pce == nullptr) - return; - - db_unset(0, PPICT_MODULE, pce->szProtoname); - - // common for all accounts - if (pce->cacheType == PCE_TYPE_GLOBAL) { - for (auto &p : g_ProtoPictures) { - if (p->szProtoname == nullptr) - continue; - - p->clear(); - CreateAvatarInCache(0, p, p->szProtoname); - NotifyEventHooks(hEventChanged, 0, (LPARAM)p); - } - return; - } - - // common for all accounts of this proto - if (pce->cacheType == PCE_TYPE_PROTO) { - for (auto &p : g_ProtoPictures) { - if (p->szProtoname == nullptr) - continue; - - PROTOACCOUNT *pdescr = Proto_GetAccount(p->szProtoname); - if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname)) - continue; - - if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) { - p->clear(); - CreateAvatarInCache(0, p, p->szProtoname); - NotifyEventHooks(hEventChanged, 0, (LPARAM)p); - } - } - return; - } - - for (auto &p : g_ProtoPictures) { - if (!mir_strcmp(p->szProtoname, pce->szProtoname)) { - p->clear(); - NotifyEventHooks(hEventChanged, 0, (LPARAM)p); - } - } -} - -static void SetProtoPic(protoPicCacheEntry *pce) -{ - wchar_t FileName[MAX_PATH], filter[256]; - Bitmap_GetFilter(filter, _countof(filter)); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.lpstrFilter = filter; - ofn.hwndOwner = nullptr; - ofn.lpstrFile = FileName; - ofn.nMaxFile = MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - ofn.Flags = OFN_HIDEREADONLY; - ofn.lpstrInitialDir = L"."; - *FileName = '\0'; - ofn.lpstrDefExt = L""; - if (!GetOpenFileName(&ofn)) - return; - - if (_waccess(FileName, 4) == -1) - return; - - wchar_t szNewPath[MAX_PATH]; - PathToRelativeW(FileName, szNewPath, g_szDataPath); - db_set_ws(0, PPICT_MODULE, pce->szProtoname, szNewPath); - - switch(pce->cacheType) { - case PCE_TYPE_GLOBAL: - for (auto &p : g_ProtoPictures) { - if (mir_strlen(p->szProtoname) == 0) - continue; - - if (p->hbmPic == nullptr || !mir_strcmp(p->szProtoname, AVS_DEFAULT)) { - CreateAvatarInCache(0, p, pce->szProtoname); - NotifyEventHooks(hEventChanged, 0, (LPARAM)p); - } - } - break; - - case PCE_TYPE_PROTO: - for (auto &p : g_ProtoPictures) { - PROTOACCOUNT* pdescr = Proto_GetAccount(p->szProtoname); - if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname)) - continue; - - if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) { - if (mir_strlen(p->szProtoname) != 0) { - if (p->hbmPic == nullptr) { - CreateAvatarInCache(0, p, pce->szProtoname); - NotifyEventHooks(hEventChanged, 0, (LPARAM)p); - } - } - } - } - break; - - default: - for (auto &p : g_ProtoPictures) { - if (mir_strlen(p->szProtoname) == 0) - break; - - if (!mir_strcmp(p->szProtoname, pce->szProtoname) && mir_strlen(p->szProtoname) == mir_strlen(pce->szProtoname)) { - if (p->hbmPic != nullptr) - DeleteObject(p->hbmPic); - memset(p, 0, sizeof(AVATARCACHEENTRY)); - CreateAvatarInCache(0, p, pce->szProtoname); - NotifyEventHooks(hEventChanged, 0, (LPARAM)p); - break; - } - } - } -} - -static protoPicCacheEntry *g_selectedProto; - -static INT_PTR CALLBACK DlgProcOptionsAvatars(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - - CheckDlgButton(hwndDlg, IDC_SHOWWARNINGS, g_plugin.getByte("warnings", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_MAKE_GRAYSCALE, g_plugin.getByte("MakeGrayscale", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_MAKE_TRANSPARENT_BKG, g_plugin.getByte("MakeTransparentBkg", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL, g_plugin.getByte("MakeTransparencyProportionalToColorDiff", 0) ? BST_CHECKED : BST_UNCHECKED); - - SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2)); - SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgNumPoints", 5)); - - SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0)); - SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgColorDiff", 10)); - { - BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), enabled); - } - - return TRUE; - - case WM_COMMAND: - if ((LOWORD(wParam) == IDC_BKG_NUM_POINTS || LOWORD(wParam) == IDC_BKG_COLOR_DIFFERENCE) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())) - return FALSE; - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case IDC_MAKE_TRANSPARENT_BKG: - { - BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), transp_enabled); - break; - } - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - g_plugin.setByte("warnings", IsDlgButtonChecked(hwndDlg, IDC_SHOWWARNINGS) ? 1 : 0); - g_plugin.setByte("MakeGrayscale", IsDlgButtonChecked(hwndDlg, IDC_MAKE_GRAYSCALE) ? 1 : 0); - g_plugin.setByte("MakeTransparentBkg", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG) ? 1 : 0); - g_plugin.setByte("MakeTransparencyProportionalToColorDiff", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL) ? 1 : 0); - g_plugin.setWord("TranspBkgNumPoints", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0)); - g_plugin.setWord("TranspBkgColorDiff", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0)); - } - } - break; - } - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK DlgProcOptionsOwn(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - - CheckDlgButton(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP, g_plugin.getByte("MakeMyAvatarsTransparent", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_SET_MAKE_SQUARE, g_plugin.getByte("SetAllwaysMakeSquare", 0) ? BST_CHECKED : BST_UNCHECKED); - - return TRUE; - - case WM_COMMAND: - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - g_plugin.setByte("MakeMyAvatarsTransparent", IsDlgButtonChecked(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP) ? 1 : 0); - g_plugin.setByte("SetAllwaysMakeSquare", IsDlgButtonChecked(hwndDlg, IDC_SET_MAKE_SQUARE) ? 1 : 0); - } - } - break; - } - return FALSE; -} - -static protoPicCacheEntry* GetProtoFromList(HWND hwndDlg, int iItem) -{ - LVITEM item; - item.mask = LVIF_PARAM; - item.iItem = iItem; - if (!ListView_GetItem(GetDlgItem(hwndDlg, IDC_PROTOCOLS), &item)) - return nullptr; - - return (protoPicCacheEntry*)item.lParam; -} - -static INT_PTR CALLBACK DlgProcOptionsProtos(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS); - HWND hwndChoosePic = GetDlgItem(hwndDlg, IDC_SETPROTOPIC); - HWND hwndRemovePic = GetDlgItem(hwndDlg, IDC_REMOVEPROTOPIC); - - switch (msg) { - case WM_INITDIALOG: - dialoginit = TRUE; - TranslateDialogDefault(hwndDlg); - - ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES); - { - LVCOLUMN lvc = { 0 }; - lvc.mask = LVCF_FMT; - lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT; - ListView_InsertColumn(hwndList, 0, &lvc); - - CMStringW tmp; - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_PARAM; - item.iItem = 1000; - for (auto &p : g_ProtoPictures) { - switch (p->cacheType) { - case PCE_TYPE_ACCOUNT: - item.pszText = p->pa->tszAccountName; - break; - case PCE_TYPE_PROTO: - tmp.Format(TranslateT("Global avatar for %s accounts"), _A2T(p->pd->szName).get()); - item.pszText = tmp.GetBuffer(); - break; - default: - item.pszText = TranslateT("Global avatar"); - break; - } - item.lParam = (LPARAM)p; - int newItem = ListView_InsertItem(hwndList, &item); - if (newItem >= 0) - ListView_SetCheckState(hwndList, newItem, g_plugin.getByte(p->szProtoname, 1) ? TRUE : FALSE); - } - ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE); - ListView_Arrange(hwndList, LVA_ALIGNLEFT | LVA_ALIGNTOP); - EnableWindow(hwndChoosePic, FALSE); - EnableWindow(hwndRemovePic, FALSE); - } - dialoginit = FALSE; - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_SETPROTOPIC: - case IDC_REMOVEPROTOPIC: - int iItem = ListView_GetSelectionMark(hwndList); - auto *pce = GetProtoFromList(hwndDlg, iItem); - if (pce) { - if (LOWORD(wParam) == IDC_SETPROTOPIC) - SetProtoPic(pce); - else - RemoveProtoPic(pce); - - NMHDR nm = { hwndList, IDC_PROTOCOLS, NM_CLICK }; - SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nm); - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; - if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) { - AVATARDRAWREQUEST avdrq = { 0 }; - avdrq.hTargetDC = dis->hDC; - avdrq.dwFlags |= AVDRQ_PROTOPICT; - avdrq.szProto = (g_selectedProto) ? g_selectedProto->szProtoname : nullptr; - GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), &avdrq.rcDraw); - CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq); - } - } - return TRUE; - - case WM_NOTIFY: - if (dialoginit) - break; - - switch (((LPNMHDR)lParam)->idFrom) { - case IDC_PROTOCOLS: - switch (((LPNMHDR)lParam)->code) { - case LVN_KEYDOWN: - { - NMLVKEYDOWN* ptkd = (NMLVKEYDOWN*)lParam; - if (ptkd&&ptkd->wVKey == VK_SPACE && ListView_GetSelectedCount(ptkd->hdr.hwndFrom) == 1) - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - break; - - case LVN_ITEMCHANGED: - { - NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam; - if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_PROTOCOLS)) && ((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK)) - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - break; - - case NM_CLICK: - EnableWindow(hwndChoosePic, TRUE); - EnableWindow(hwndRemovePic, TRUE); - - int iItem = ListView_GetSelectionMark(hwndList); - g_selectedProto = GetProtoFromList(hwndDlg, iItem); - if (g_selectedProto) { - DBVARIANT dbv; - if (!db_get_ws(0, PPICT_MODULE, g_selectedProto->szProtoname, &dbv)) { - if (!PathIsAbsoluteW(VARSW(dbv.pwszVal))) { - wchar_t szFinalPath[MAX_PATH]; - mir_snwprintf(szFinalPath, L"%%miranda_path%%\\%s", dbv.pwszVal); - SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, szFinalPath); - } - else SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, dbv.pwszVal); - - InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE); - db_free(&dbv); - } - else { - SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, L""); - InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE); - } - } - } - break; - - case 0: - if (((LPNMHDR)lParam)->code == PSN_APPLY) { - for (int i = 0; i < ListView_GetItemCount(hwndList); i++) { - auto *pce = GetProtoFromList(hwndDlg, i); - - BOOL oldVal = g_plugin.getByte(pce->szProtoname, 1); - BOOL newVal = ListView_GetCheckState(hwndList, i); - - if (oldVal && !newVal) - for (auto &hContact : Contacts(pce->szProtoname)) - DeleteAvatarFromCache(hContact, TRUE); - - if (newVal) - g_plugin.setByte(pce->szProtoname, 1); - else - g_plugin.setByte(pce->szProtoname, 0); - } - } - } - break; - } - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Module entry point - -int OptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.szGroup.a = LPGEN("Contacts"); - odp.szTitle.a = LPGEN("Avatars"); - - odp.szTab.a = LPGEN("Protocols"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PICTS); - odp.pfnDlgProc = DlgProcOptionsProtos; - g_plugin.addOptions(wParam, &odp); - - odp.szTab.a = LPGEN("Contact avatars"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_AVATARS); - odp.pfnDlgProc = DlgProcOptionsAvatars; - g_plugin.addOptions(wParam, &odp); - - odp.szTab.a = LPGEN("Own avatars"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_OWN); - odp.pfnDlgProc = DlgProcOptionsOwn; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2000-04 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+extern OBJLIST<protoPicCacheEntry> g_ProtoPictures;
+extern HANDLE hEventChanged;
+
+static BOOL dialoginit = TRUE;
+
+static void RemoveProtoPic(protoPicCacheEntry *pce)
+{
+ if (pce == nullptr)
+ return;
+
+ db_unset(0, PPICT_MODULE, pce->szProtoname);
+
+ // common for all accounts
+ if (pce->cacheType == PCE_TYPE_GLOBAL) {
+ for (auto &p : g_ProtoPictures) {
+ if (p->szProtoname == nullptr)
+ continue;
+
+ p->clear();
+ CreateAvatarInCache(0, p, p->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ return;
+ }
+
+ // common for all accounts of this proto
+ if (pce->cacheType == PCE_TYPE_PROTO) {
+ for (auto &p : g_ProtoPictures) {
+ if (p->szProtoname == nullptr)
+ continue;
+
+ PROTOACCOUNT *pdescr = Proto_GetAccount(p->szProtoname);
+ if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname))
+ continue;
+
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) {
+ p->clear();
+ CreateAvatarInCache(0, p, p->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+ return;
+ }
+
+ for (auto &p : g_ProtoPictures) {
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname)) {
+ p->clear();
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+}
+
+static void SetProtoPic(protoPicCacheEntry *pce)
+{
+ wchar_t FileName[MAX_PATH], filter[256];
+ Bitmap_GetFilter(filter, _countof(filter));
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.lpstrFilter = filter;
+ ofn.hwndOwner = nullptr;
+ ofn.lpstrFile = FileName;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.lpstrInitialDir = L".";
+ *FileName = '\0';
+ ofn.lpstrDefExt = L"";
+ if (!GetOpenFileName(&ofn))
+ return;
+
+ if (_waccess(FileName, 4) == -1)
+ return;
+
+ wchar_t szNewPath[MAX_PATH];
+ PathToRelativeW(FileName, szNewPath, g_szDataPath);
+ db_set_ws(0, PPICT_MODULE, pce->szProtoname, szNewPath);
+
+ switch(pce->cacheType) {
+ case PCE_TYPE_GLOBAL:
+ for (auto &p : g_ProtoPictures) {
+ if (mir_strlen(p->szProtoname) == 0)
+ continue;
+
+ if (p->hbmPic == nullptr || !mir_strcmp(p->szProtoname, AVS_DEFAULT)) {
+ CreateAvatarInCache(0, p, pce->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+ break;
+
+ case PCE_TYPE_PROTO:
+ for (auto &p : g_ProtoPictures) {
+ PROTOACCOUNT* pdescr = Proto_GetAccount(p->szProtoname);
+ if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname))
+ continue;
+
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) {
+ if (mir_strlen(p->szProtoname) != 0) {
+ if (p->hbmPic == nullptr) {
+ CreateAvatarInCache(0, p, pce->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ for (auto &p : g_ProtoPictures) {
+ if (mir_strlen(p->szProtoname) == 0)
+ break;
+
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname) && mir_strlen(p->szProtoname) == mir_strlen(pce->szProtoname)) {
+ if (p->hbmPic != nullptr)
+ DeleteObject(p->hbmPic);
+ memset(p, 0, sizeof(AVATARCACHEENTRY));
+ CreateAvatarInCache(0, p, pce->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ break;
+ }
+ }
+ }
+}
+
+static protoPicCacheEntry *g_selectedProto;
+
+static INT_PTR CALLBACK DlgProcOptionsAvatars(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_SHOWWARNINGS, g_plugin.getByte("warnings", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MAKE_GRAYSCALE, g_plugin.getByte("MakeGrayscale", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MAKE_TRANSPARENT_BKG, g_plugin.getByte("MakeTransparentBkg", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL, g_plugin.getByte("MakeTransparencyProportionalToColorDiff", 0) ? BST_CHECKED : BST_UNCHECKED);
+
+ SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2));
+ SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgNumPoints", 5));
+
+ SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgColorDiff", 10));
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), enabled);
+ }
+
+ return TRUE;
+
+ case WM_COMMAND:
+ if ((LOWORD(wParam) == IDC_BKG_NUM_POINTS || LOWORD(wParam) == IDC_BKG_COLOR_DIFFERENCE) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
+ return FALSE;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_MAKE_TRANSPARENT_BKG:
+ {
+ BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), transp_enabled);
+ break;
+ }
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ g_plugin.setByte("warnings", IsDlgButtonChecked(hwndDlg, IDC_SHOWWARNINGS) ? 1 : 0);
+ g_plugin.setByte("MakeGrayscale", IsDlgButtonChecked(hwndDlg, IDC_MAKE_GRAYSCALE) ? 1 : 0);
+ g_plugin.setByte("MakeTransparentBkg", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG) ? 1 : 0);
+ g_plugin.setByte("MakeTransparencyProportionalToColorDiff", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL) ? 1 : 0);
+ g_plugin.setWord("TranspBkgNumPoints", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0));
+ g_plugin.setWord("TranspBkgColorDiff", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0));
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK DlgProcOptionsOwn(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP, g_plugin.getByte("MakeMyAvatarsTransparent", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SET_MAKE_SQUARE, g_plugin.getByte("SetAllwaysMakeSquare", 0) ? BST_CHECKED : BST_UNCHECKED);
+
+ return TRUE;
+
+ case WM_COMMAND:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ g_plugin.setByte("MakeMyAvatarsTransparent", IsDlgButtonChecked(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP) ? 1 : 0);
+ g_plugin.setByte("SetAllwaysMakeSquare", IsDlgButtonChecked(hwndDlg, IDC_SET_MAKE_SQUARE) ? 1 : 0);
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static protoPicCacheEntry* GetProtoFromList(HWND hwndDlg, int iItem)
+{
+ LVITEM item;
+ item.mask = LVIF_PARAM;
+ item.iItem = iItem;
+ if (!ListView_GetItem(GetDlgItem(hwndDlg, IDC_PROTOCOLS), &item))
+ return nullptr;
+
+ return (protoPicCacheEntry*)item.lParam;
+}
+
+static INT_PTR CALLBACK DlgProcOptionsProtos(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
+ HWND hwndChoosePic = GetDlgItem(hwndDlg, IDC_SETPROTOPIC);
+ HWND hwndRemovePic = GetDlgItem(hwndDlg, IDC_REMOVEPROTOPIC);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ dialoginit = TRUE;
+ TranslateDialogDefault(hwndDlg);
+
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES);
+ {
+ LVCOLUMN lvc = { 0 };
+ lvc.mask = LVCF_FMT;
+ lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
+ ListView_InsertColumn(hwndList, 0, &lvc);
+
+ CMStringW tmp;
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_PARAM;
+ item.iItem = 1000;
+ for (auto &p : g_ProtoPictures) {
+ switch (p->cacheType) {
+ case PCE_TYPE_ACCOUNT:
+ item.pszText = p->pa->tszAccountName;
+ break;
+ case PCE_TYPE_PROTO:
+ tmp.Format(TranslateT("Global avatar for %s accounts"), _A2T(p->pd->szName).get());
+ item.pszText = tmp.GetBuffer();
+ break;
+ default:
+ item.pszText = TranslateT("Global avatar");
+ break;
+ }
+ item.lParam = (LPARAM)p;
+ int newItem = ListView_InsertItem(hwndList, &item);
+ if (newItem >= 0)
+ ListView_SetCheckState(hwndList, newItem, g_plugin.getByte(p->szProtoname, 1) ? TRUE : FALSE);
+ }
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
+ ListView_Arrange(hwndList, LVA_ALIGNLEFT | LVA_ALIGNTOP);
+ EnableWindow(hwndChoosePic, FALSE);
+ EnableWindow(hwndRemovePic, FALSE);
+ }
+ dialoginit = FALSE;
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_SETPROTOPIC:
+ case IDC_REMOVEPROTOPIC:
+ int iItem = ListView_GetSelectionMark(hwndList);
+ auto *pce = GetProtoFromList(hwndDlg, iItem);
+ if (pce) {
+ if (LOWORD(wParam) == IDC_SETPROTOPIC)
+ SetProtoPic(pce);
+ else
+ RemoveProtoPic(pce);
+
+ NMHDR nm = { hwndList, IDC_PROTOCOLS, NM_CLICK };
+ SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nm);
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
+ AVATARDRAWREQUEST avdrq = { 0 };
+ avdrq.hTargetDC = dis->hDC;
+ avdrq.dwFlags |= AVDRQ_PROTOPICT;
+ avdrq.szProto = (g_selectedProto) ? g_selectedProto->szProtoname : nullptr;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), &avdrq.rcDraw);
+ CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq);
+ }
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ if (dialoginit)
+ break;
+
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_PROTOCOLS:
+ switch (((LPNMHDR)lParam)->code) {
+ case LVN_KEYDOWN:
+ {
+ NMLVKEYDOWN* ptkd = (NMLVKEYDOWN*)lParam;
+ if (ptkd&&ptkd->wVKey == VK_SPACE && ListView_GetSelectedCount(ptkd->hdr.hwndFrom) == 1)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+
+ case LVN_ITEMCHANGED:
+ {
+ NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam;
+ if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_PROTOCOLS)) && ((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK))
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+
+ case NM_CLICK:
+ EnableWindow(hwndChoosePic, TRUE);
+ EnableWindow(hwndRemovePic, TRUE);
+
+ int iItem = ListView_GetSelectionMark(hwndList);
+ g_selectedProto = GetProtoFromList(hwndDlg, iItem);
+ if (g_selectedProto) {
+ DBVARIANT dbv;
+ if (!db_get_ws(0, PPICT_MODULE, g_selectedProto->szProtoname, &dbv)) {
+ if (!PathIsAbsoluteW(VARSW(dbv.pwszVal))) {
+ wchar_t szFinalPath[MAX_PATH];
+ mir_snwprintf(szFinalPath, L"%%miranda_path%%\\%s", dbv.pwszVal);
+ SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, szFinalPath);
+ }
+ else SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, dbv.pwszVal);
+
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE);
+ db_free(&dbv);
+ }
+ else {
+ SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, L"");
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE);
+ }
+ }
+ }
+ break;
+
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY) {
+ for (int i = 0; i < ListView_GetItemCount(hwndList); i++) {
+ auto *pce = GetProtoFromList(hwndDlg, i);
+
+ BOOL oldVal = g_plugin.getByte(pce->szProtoname, 1);
+ BOOL newVal = ListView_GetCheckState(hwndList, i);
+
+ if (oldVal && !newVal)
+ for (auto &hContact : Contacts(pce->szProtoname))
+ DeleteAvatarFromCache(hContact, TRUE);
+
+ if (newVal)
+ g_plugin.setByte(pce->szProtoname, 1);
+ else
+ g_plugin.setByte(pce->szProtoname, 0);
+ }
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module entry point
+
+int OptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.szGroup.a = LPGEN("Contacts");
+ odp.szTitle.a = LPGEN("Avatars");
+
+ odp.szTab.a = LPGEN("Protocols");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PICTS);
+ odp.pfnDlgProc = DlgProcOptionsProtos;
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.a = LPGEN("Contact avatars");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_AVATARS);
+ odp.pfnDlgProc = DlgProcOptionsAvatars;
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.a = LPGEN("Own avatars");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_OWN);
+ odp.pfnDlgProc = DlgProcOptionsOwn;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/AVS/src/poll.cpp b/plugins/AVS/src/poll.cpp index 16b16c65dc..80b1e46ae3 100644 --- a/plugins/AVS/src/poll.cpp +++ b/plugins/AVS/src/poll.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/poll.h b/plugins/AVS/src/poll.h index 3a9c049485..70eaed598d 100644 --- a/plugins/AVS/src/poll.h +++ b/plugins/AVS/src/poll.h @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/services.cpp b/plugins/AVS/src/services.cpp index 544e521b4e..0c11763074 100644 --- a/plugins/AVS/src/services.cpp +++ b/plugins/AVS/src/services.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/stdafx.cxx b/plugins/AVS/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AVS/src/stdafx.cxx +++ b/plugins/AVS/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AVS/src/stdafx.h b/plugins/AVS/src/stdafx.h index c43d83e32a..d8eb699e64 100644 --- a/plugins/AVS/src/stdafx.h +++ b/plugins/AVS/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/userInfo.cpp b/plugins/AVS/src/userInfo.cpp index 36eadc75d6..d5e69533d0 100644 --- a/plugins/AVS/src/userInfo.cpp +++ b/plugins/AVS/src/userInfo.cpp @@ -1,485 +1,485 @@ -/* -Copyright (C) 2012-22 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" - -BOOL ScreenToClient(HWND hWnd, LPRECT lpRect); - -///////////////////////////////////////////////////////////////////////////////////////// - -void LoadTransparentData(HWND hwndDlg, MCONTACT hContact) -{ - CheckDlgButton(hwndDlg, IDC_MAKETRANSPBKG, db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", g_plugin.getByte("MakeTransparentBkg", 0)) ? BST_CHECKED : BST_UNCHECKED); - SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", g_plugin.getWord("TranspBkgNumPoints", 5))); - SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgColorDiff", g_plugin.getWord("TranspBkgColorDiff", 10))); - - BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled); -} - -static void SaveTransparentData(HWND hwndDlg, MCONTACT hContact) -{ - BOOL transp = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG); - if (g_plugin.getByte("MakeTransparentBkg", 0) == transp) - db_unset(hContact, "ContactPhoto", "MakeTransparentBkg"); - else - db_set_b(hContact, "ContactPhoto", "MakeTransparentBkg", transp); - - uint16_t tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0); - if (g_plugin.getWord("TranspBkgNumPoints", 5) == tmp) - db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints"); - else - db_set_w(hContact, "ContactPhoto", "TranspBkgNumPoints", tmp); - - tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0); - if (g_plugin.getWord("TranspBkgColorDiff", 10) == tmp) - db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff"); - else - db_set_w(hContact, "ContactPhoto", "TranspBkgColorDiff", tmp); -} - -void SaveTransparentData(HWND hwndDlg, MCONTACT hContact, BOOL locked) -{ - SaveTransparentData(hwndDlg, hContact); - - MCONTACT tmp = GetContactThatHaveTheAvatar(hContact, locked); - if (tmp != hContact) - SaveTransparentData(hwndDlg, tmp); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// User info dialog - -class AvatarUserInfoDlg : public CUserInfoPageDlg -{ - HANDLE hHook = nullptr; - - CCtrlSpin spinPoints, spinDifference; - CCtrlCheck chkTransparent; - CCtrlButton btnHide, btnChange, btnDefaults, btnProtect, btnReset, btnDelete; - - void ReloadAvatar() - { - SaveTransparentData(m_hwnd, m_hContact, IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR)); - ChangeAvatar(m_hContact, true); - } - - INT_PTR OnChangeAvatar(UINT, WPARAM, LPARAM) - { - InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE); - return 0; - } - - UI_MESSAGE_MAP(AvatarUserInfoDlg, CUserInfoPageDlg); - UI_MESSAGE(DM_AVATARCHANGED, OnChangeAvatar); - UI_MESSAGE_MAP_END(); - -public: - AvatarUserInfoDlg() : - CUserInfoPageDlg(g_plugin, IDD_USER_AVATAR), - btnHide(this, IDC_HIDEAVATAR), - btnReset(this, IDC_RESET), - btnChange(this, IDC_CHANGE), - btnDelete(this, IDC_DELETE), - btnProtect(this, IDC_PROTECTAVATAR), - btnDefaults(this, ID_USE_DEFAULTS), - chkTransparent(this, IDC_MAKETRANSPBKG), - spinPoints(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2), - spinDifference(this, IDC_BKG_NUM_POINTS_SPIN, 100) - { - btnHide.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Hide); - btnReset.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Reset); - btnChange.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Change); - btnDelete.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Delete); - btnProtect.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Protect); - btnDefaults.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_UseDefaults); - - chkTransparent.OnChange = Callback(this, &AvatarUserInfoDlg::onChange_Transparent); - } - - bool OnInitDialog() override - { - hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED); - - if (m_hContact != 0) - btnDelete.Hide(); - - LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact)); - return true; - } - - bool OnRefresh() override - { - HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC); - SendMessage(protopic, AVATAR_SETCONTACT, 0, m_hContact); - SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, GetSysColor(COLOR_BTNSHADOW)); - SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("Contact has no avatar")); - SendMessage(protopic, AVATAR_RESPECTHIDDEN, 0, FALSE); - SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, FALSE); - - CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_HIDEAVATAR, Contact::IsHidden(m_hContact) ? BST_CHECKED : BST_UNCHECKED); - return false; - } - - bool IsEmpty() const override - { - auto wszFileName(db_get_wsm(m_hContact, "ContactPhoto", "File")); - return (wszFileName.IsEmpty() || _waccess(wszFileName, 0) != 0); - } - - void OnDestroy() override - { - UnhookEvent(hHook); - } - - void onClick_UseDefaults(CCtrlButton *) - { - m_hContact = GetContactThatHaveTheAvatar(m_hContact); - - db_unset(m_hContact, "ContactPhoto", "MakeTransparentBkg"); - db_unset(m_hContact, "ContactPhoto", "TranspBkgNumPoints"); - db_unset(m_hContact, "ContactPhoto", "TranspBkgColorDiff"); - - LoadTransparentData(m_hwnd, m_hContact); - ReloadAvatar(); - } - - void onClick_Change(CCtrlButton *) - { - SetAvatar(m_hContact, 0); - CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED); - } - - void onClick_Hide(CCtrlButton *) - { - bool hidden = IsDlgButtonChecked(m_hwnd, IDC_HIDEAVATAR) != 0; - SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden); - if (hidden != Contact::IsHidden(m_hContact)) - Contact::Hide(m_hContact, hidden); - } - - void onClick_Protect(CCtrlButton *) - { - BOOL locked = IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR); - SaveTransparentData(m_hwnd, m_hContact, locked); - ProtectAvatar(m_hContact, locked ? 1 : 0); - } - - void onChange_Transparent(CCtrlCheck *) - { - BOOL enable = IsDlgButtonChecked(m_hwnd, IDC_MAKETRANSPBKG); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable); - EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable); - ReloadAvatar(); - } - - void onClick_Reset(CCtrlButton *) - { - ProtectAvatar(m_hContact, 0); - if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) { - DBVARIANT dbv = { 0 }; - if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { - DeleteFile(dbv.pwszVal); - db_free(&dbv); - } - } - db_unset(m_hContact, "ContactPhoto", "Locked"); - db_unset(m_hContact, "ContactPhoto", "Backup"); - db_unset(m_hContact, "ContactPhoto", "RFile"); - db_unset(m_hContact, "ContactPhoto", "File"); - db_unset(m_hContact, "ContactPhoto", "Format"); - - char *szProto = Proto_GetBaseAccountName(m_hContact); - db_unset(m_hContact, szProto, "AvatarHash"); - DeleteAvatarFromCache(m_hContact, FALSE); - - QueueAdd(m_hContact); - } - - void onClick_Delete(CCtrlButton *) - { - ProtectAvatar(m_hContact, 0); - if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) { - DBVARIANT dbv = { 0 }; - if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { - DeleteFile(dbv.pwszVal); - db_free(&dbv); - } - } - db_unset(m_hContact, "ContactPhoto", "Locked"); - db_unset(m_hContact, "ContactPhoto", "Backup"); - db_unset(m_hContact, "ContactPhoto", "RFile"); - db_unset(m_hContact, "ContactPhoto", "File"); - db_unset(m_hContact, "ContactPhoto", "Format"); - DeleteAvatarFromCache(m_hContact, FALSE); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Protocol avatar dialog - -class AvatarProtoInfoDlg : public CUserInfoPageDlg -{ - char* GetSelectedProtocol() - { - // Get selection - int iItem = protocols.GetSelectionMark(); - if (iItem < 0) - return nullptr; - - // Get protocol name - LVITEM item = {0}; - item.mask = LVIF_PARAM; - item.iItem = iItem; - SendMessage(protocols.GetHwnd(), LVM_GETITEMA, 0, (LPARAM)&item); - return (char *)item.lParam; - } - - void OffsetWindow(int iCtrlId, int dx) - { - HWND hwnd = GetDlgItem(m_hwnd, iCtrlId); - - RECT rc; - GetWindowRect(hwnd, &rc); - ScreenToClient(m_hwnd, &rc); - OffsetRect(&rc, dx, 0); - MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); - } - - void EnableDisableControls(char *proto) - { - if (chkPerProto.IsChecked()) { - if (proto == nullptr) { - EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE); - EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE); - } - else { - if (!ProtoServiceExists(proto, PS_SETMYAVATAR)) { - EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE); - EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE); - } - else { - EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE); - - int width, height; - SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height); - EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0); - } - } - } - else { - EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE); - - if (g_plugin.getByte("GlobalUserAvatarNotConsistent", 1)) - EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), TRUE); - else { - int width, height; - SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height); - EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0); - } - } - } - - CCtrlCheck chkPerProto; - CCtrlButton btnChange, btnDelete; - CCtrlListView protocols; - -public: - AvatarProtoInfoDlg() : - CUserInfoPageDlg(g_plugin, IDD_PROTO_AVATARS), - protocols(this, IDC_PROTOCOLS), - btnChange(this, IDC_CHANGE), - btnDelete(this, IDC_DELETE), - chkPerProto(this, IDC_PER_PROTO) - { - protocols.OnItemChanged = Callback(this, &AvatarProtoInfoDlg::onItemChanged_List); - - btnChange.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Change); - btnDelete.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Delete); - - CreateLink(chkPerProto, g_plugin.bPerProto); - chkPerProto.OnChange = Callback(this, &AvatarProtoInfoDlg::onChange_PerProto); - } - - bool OnInitDialog() override - { - HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC); - SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNSHADOW)); - SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("No avatar")); - SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM)FALSE); - - protocols.SetExtendedListViewStyleEx(0, LVS_EX_SUBITEMIMAGES); - - HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 4, 0); - protocols.SetImageList(hIml, LVSIL_SMALL); - - LVCOLUMN lvc = { 0 }; - lvc.mask = LVCF_FMT; - lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT; - protocols.InsertColumn(0, &lvc); - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; - item.iItem = 1000; - - // List protocols - int num = 0; - for (auto &it : Accounts()) { - if (!ProtoServiceExists(it->szModuleName, PS_GETMYAVATAR)) - continue; - - if (!Proto_IsAvatarsEnabled(it->szModuleName)) - continue; - - ImageList_AddIcon(hIml, Skin_LoadProtoIcon(it->szModuleName, ID_STATUS_ONLINE)); - item.pszText = it->tszAccountName; - item.iImage = num; - item.lParam = (LPARAM)it->szModuleName; - protocols.InsertItem(&item); - num++; - } - - protocols.SetCurSel(0); - protocols.SetColumnWidth(0, LVSCW_AUTOSIZE); - protocols.Arrange(LVA_ALIGNLEFT | LVA_ALIGNTOP); - return true; - } - - void onItemChanged_List(CCtrlListView::TEventInfo *ev) - { - LPNMLISTVIEW li = ev->nmlv; - if (li->uNewState & LVIS_SELECTED) { - SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, li->lParam); - EnableDisableControls((char*)li->lParam); - } - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - if (msg == WM_NOTIFY) { - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->idFrom == IDC_PROTOPIC && hdr->code == NM_AVATAR_CHANGED) - EnableDisableControls(GetSelectedProtocol()); - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - - void onClick_Change(CCtrlButton *) - { - if (!chkPerProto.IsChecked()) - SetMyAvatar(NULL, NULL); - else { - char *proto = GetSelectedProtocol(); - if (proto != nullptr) - SetMyAvatar((WPARAM)proto, NULL); - } - } - - void onClick_Delete(CCtrlButton *) - { - if (!chkPerProto.IsChecked()) { - if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), TranslateT("Global avatar"), MB_YESNO) == IDYES) - SetMyAvatar(NULL, (LPARAM)L""); - } - else { - if (char *proto = GetSelectedProtocol()) { - char description[256]; - CallProtoService(proto, PS_GETNAME, _countof(description), (LPARAM)description); - wchar_t *descr = mir_a2u(description); - if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), descr, MB_YESNO) == IDYES) - SetMyAvatar((WPARAM)proto, (LPARAM)L""); - mir_free(descr); - } - } - } - - void onChange_PerProto(CCtrlCheck *pCheck) - { - int diff = 147; // Pre-calc - - if (chkPerProto.IsChecked()) { - if (!IsWindowVisible(protocols.GetHwnd())) { - // Show list of protocols - protocols.Show(); - - // Move controls - OffsetWindow(IDC_PROTOPIC, diff); - OffsetWindow(IDC_CHANGE, diff); - OffsetWindow(IDC_DELETE, diff); - } - - char *proto = GetSelectedProtocol(); - if (proto == nullptr) { - protocols.SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, 0x0F); - } - else { - SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, (LPARAM)proto); - EnableDisableControls(proto); - } - } - else { - if (!m_bInitialized) { - PostMessage(m_hwnd, WM_COMMAND, IDC_PER_PROTO, 0); - } - else if (!pCheck || IsWindowVisible(protocols.GetHwnd())) { - // Hide list of protocols - protocols.Hide(); - - // Move controls - OffsetWindow(IDC_PROTOPIC, -diff); - OffsetWindow(IDC_CHANGE, -diff); - OffsetWindow(IDC_DELETE, -diff); - } - - SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, NULL); - } - } -}; - -int OnDetailsInit(WPARAM wParam, LPARAM hContact) -{ - USERINFOPAGE uip = {}; - uip.szTitle.a = LPGEN("Avatar"); - uip.flags = ODPF_ICON; - uip.dwInitParam = (LPARAM)g_plugin.getIconHandle(IDI_AVATAR); - - if (hContact == NULL) { - // User dialog - uip.pDialog = new AvatarProtoInfoDlg(); - g_plugin.addUserInfo(wParam, &uip); - } - else { - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto == nullptr || g_plugin.getByte(szProto, 1)) { - // Contact dialog - uip.position = -2000000000; - uip.pDialog = new AvatarUserInfoDlg(); - g_plugin.addUserInfo(wParam, &uip); - } - } - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void LoadTransparentData(HWND hwndDlg, MCONTACT hContact)
+{
+ CheckDlgButton(hwndDlg, IDC_MAKETRANSPBKG, db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", g_plugin.getByte("MakeTransparentBkg", 0)) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", g_plugin.getWord("TranspBkgNumPoints", 5)));
+ SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgColorDiff", g_plugin.getWord("TranspBkgColorDiff", 10)));
+
+ BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
+}
+
+static void SaveTransparentData(HWND hwndDlg, MCONTACT hContact)
+{
+ BOOL transp = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
+ if (g_plugin.getByte("MakeTransparentBkg", 0) == transp)
+ db_unset(hContact, "ContactPhoto", "MakeTransparentBkg");
+ else
+ db_set_b(hContact, "ContactPhoto", "MakeTransparentBkg", transp);
+
+ uint16_t tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0);
+ if (g_plugin.getWord("TranspBkgNumPoints", 5) == tmp)
+ db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints");
+ else
+ db_set_w(hContact, "ContactPhoto", "TranspBkgNumPoints", tmp);
+
+ tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0);
+ if (g_plugin.getWord("TranspBkgColorDiff", 10) == tmp)
+ db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff");
+ else
+ db_set_w(hContact, "ContactPhoto", "TranspBkgColorDiff", tmp);
+}
+
+void SaveTransparentData(HWND hwndDlg, MCONTACT hContact, BOOL locked)
+{
+ SaveTransparentData(hwndDlg, hContact);
+
+ MCONTACT tmp = GetContactThatHaveTheAvatar(hContact, locked);
+ if (tmp != hContact)
+ SaveTransparentData(hwndDlg, tmp);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// User info dialog
+
+class AvatarUserInfoDlg : public CUserInfoPageDlg
+{
+ HANDLE hHook = nullptr;
+
+ CCtrlSpin spinPoints, spinDifference;
+ CCtrlCheck chkTransparent;
+ CCtrlButton btnHide, btnChange, btnDefaults, btnProtect, btnReset, btnDelete;
+
+ void ReloadAvatar()
+ {
+ SaveTransparentData(m_hwnd, m_hContact, IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR));
+ ChangeAvatar(m_hContact, true);
+ }
+
+ INT_PTR OnChangeAvatar(UINT, WPARAM, LPARAM)
+ {
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE);
+ return 0;
+ }
+
+ UI_MESSAGE_MAP(AvatarUserInfoDlg, CUserInfoPageDlg);
+ UI_MESSAGE(DM_AVATARCHANGED, OnChangeAvatar);
+ UI_MESSAGE_MAP_END();
+
+public:
+ AvatarUserInfoDlg() :
+ CUserInfoPageDlg(g_plugin, IDD_USER_AVATAR),
+ btnHide(this, IDC_HIDEAVATAR),
+ btnReset(this, IDC_RESET),
+ btnChange(this, IDC_CHANGE),
+ btnDelete(this, IDC_DELETE),
+ btnProtect(this, IDC_PROTECTAVATAR),
+ btnDefaults(this, ID_USE_DEFAULTS),
+ chkTransparent(this, IDC_MAKETRANSPBKG),
+ spinPoints(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2),
+ spinDifference(this, IDC_BKG_NUM_POINTS_SPIN, 100)
+ {
+ btnHide.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Hide);
+ btnReset.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Reset);
+ btnChange.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Change);
+ btnDelete.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Delete);
+ btnProtect.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Protect);
+ btnDefaults.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_UseDefaults);
+
+ chkTransparent.OnChange = Callback(this, &AvatarUserInfoDlg::onChange_Transparent);
+ }
+
+ bool OnInitDialog() override
+ {
+ hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED);
+
+ if (m_hContact != 0)
+ btnDelete.Hide();
+
+ LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact));
+ return true;
+ }
+
+ bool OnRefresh() override
+ {
+ HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC);
+ SendMessage(protopic, AVATAR_SETCONTACT, 0, m_hContact);
+ SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, GetSysColor(COLOR_BTNSHADOW));
+ SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("Contact has no avatar"));
+ SendMessage(protopic, AVATAR_RESPECTHIDDEN, 0, FALSE);
+ SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, FALSE);
+
+ CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_HIDEAVATAR, Contact::IsHidden(m_hContact) ? BST_CHECKED : BST_UNCHECKED);
+ return false;
+ }
+
+ bool IsEmpty() const override
+ {
+ auto wszFileName(db_get_wsm(m_hContact, "ContactPhoto", "File"));
+ return (wszFileName.IsEmpty() || _waccess(wszFileName, 0) != 0);
+ }
+
+ void OnDestroy() override
+ {
+ UnhookEvent(hHook);
+ }
+
+ void onClick_UseDefaults(CCtrlButton *)
+ {
+ m_hContact = GetContactThatHaveTheAvatar(m_hContact);
+
+ db_unset(m_hContact, "ContactPhoto", "MakeTransparentBkg");
+ db_unset(m_hContact, "ContactPhoto", "TranspBkgNumPoints");
+ db_unset(m_hContact, "ContactPhoto", "TranspBkgColorDiff");
+
+ LoadTransparentData(m_hwnd, m_hContact);
+ ReloadAvatar();
+ }
+
+ void onClick_Change(CCtrlButton *)
+ {
+ SetAvatar(m_hContact, 0);
+ CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED);
+ }
+
+ void onClick_Hide(CCtrlButton *)
+ {
+ bool hidden = IsDlgButtonChecked(m_hwnd, IDC_HIDEAVATAR) != 0;
+ SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden);
+ if (hidden != Contact::IsHidden(m_hContact))
+ Contact::Hide(m_hContact, hidden);
+ }
+
+ void onClick_Protect(CCtrlButton *)
+ {
+ BOOL locked = IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR);
+ SaveTransparentData(m_hwnd, m_hContact, locked);
+ ProtectAvatar(m_hContact, locked ? 1 : 0);
+ }
+
+ void onChange_Transparent(CCtrlCheck *)
+ {
+ BOOL enable = IsDlgButtonChecked(m_hwnd, IDC_MAKETRANSPBKG);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable);
+ ReloadAvatar();
+ }
+
+ void onClick_Reset(CCtrlButton *)
+ {
+ ProtectAvatar(m_hContact, 0);
+ if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFile(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+
+ char *szProto = Proto_GetBaseAccountName(m_hContact);
+ db_unset(m_hContact, szProto, "AvatarHash");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+
+ QueueAdd(m_hContact);
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ ProtectAvatar(m_hContact, 0);
+ if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFile(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Protocol avatar dialog
+
+class AvatarProtoInfoDlg : public CUserInfoPageDlg
+{
+ char* GetSelectedProtocol()
+ {
+ // Get selection
+ int iItem = protocols.GetSelectionMark();
+ if (iItem < 0)
+ return nullptr;
+
+ // Get protocol name
+ LVITEM item = {0};
+ item.mask = LVIF_PARAM;
+ item.iItem = iItem;
+ SendMessage(protocols.GetHwnd(), LVM_GETITEMA, 0, (LPARAM)&item);
+ return (char *)item.lParam;
+ }
+
+ void OffsetWindow(int iCtrlId, int dx)
+ {
+ HWND hwnd = GetDlgItem(m_hwnd, iCtrlId);
+
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+ ScreenToClient(m_hwnd, &rc);
+ OffsetRect(&rc, dx, 0);
+ MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ }
+
+ void EnableDisableControls(char *proto)
+ {
+ if (chkPerProto.IsChecked()) {
+ if (proto == nullptr) {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE);
+ }
+ else {
+ if (!ProtoServiceExists(proto, PS_SETMYAVATAR)) {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE);
+ }
+ else {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE);
+
+ int width, height;
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0);
+ }
+ }
+ }
+ else {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE);
+
+ if (g_plugin.getByte("GlobalUserAvatarNotConsistent", 1))
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), TRUE);
+ else {
+ int width, height;
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0);
+ }
+ }
+ }
+
+ CCtrlCheck chkPerProto;
+ CCtrlButton btnChange, btnDelete;
+ CCtrlListView protocols;
+
+public:
+ AvatarProtoInfoDlg() :
+ CUserInfoPageDlg(g_plugin, IDD_PROTO_AVATARS),
+ protocols(this, IDC_PROTOCOLS),
+ btnChange(this, IDC_CHANGE),
+ btnDelete(this, IDC_DELETE),
+ chkPerProto(this, IDC_PER_PROTO)
+ {
+ protocols.OnItemChanged = Callback(this, &AvatarProtoInfoDlg::onItemChanged_List);
+
+ btnChange.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Change);
+ btnDelete.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Delete);
+
+ CreateLink(chkPerProto, g_plugin.bPerProto);
+ chkPerProto.OnChange = Callback(this, &AvatarProtoInfoDlg::onChange_PerProto);
+ }
+
+ bool OnInitDialog() override
+ {
+ HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC);
+ SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNSHADOW));
+ SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("No avatar"));
+ SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM)FALSE);
+
+ protocols.SetExtendedListViewStyleEx(0, LVS_EX_SUBITEMIMAGES);
+
+ HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 4, 0);
+ protocols.SetImageList(hIml, LVSIL_SMALL);
+
+ LVCOLUMN lvc = { 0 };
+ lvc.mask = LVCF_FMT;
+ lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
+ protocols.InsertColumn(0, &lvc);
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
+ item.iItem = 1000;
+
+ // List protocols
+ int num = 0;
+ for (auto &it : Accounts()) {
+ if (!ProtoServiceExists(it->szModuleName, PS_GETMYAVATAR))
+ continue;
+
+ if (!Proto_IsAvatarsEnabled(it->szModuleName))
+ continue;
+
+ ImageList_AddIcon(hIml, Skin_LoadProtoIcon(it->szModuleName, ID_STATUS_ONLINE));
+ item.pszText = it->tszAccountName;
+ item.iImage = num;
+ item.lParam = (LPARAM)it->szModuleName;
+ protocols.InsertItem(&item);
+ num++;
+ }
+
+ protocols.SetCurSel(0);
+ protocols.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ protocols.Arrange(LVA_ALIGNLEFT | LVA_ALIGNTOP);
+ return true;
+ }
+
+ void onItemChanged_List(CCtrlListView::TEventInfo *ev)
+ {
+ LPNMLISTVIEW li = ev->nmlv;
+ if (li->uNewState & LVIS_SELECTED) {
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, li->lParam);
+ EnableDisableControls((char*)li->lParam);
+ }
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ if (msg == WM_NOTIFY) {
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if (hdr->idFrom == IDC_PROTOPIC && hdr->code == NM_AVATAR_CHANGED)
+ EnableDisableControls(GetSelectedProtocol());
+ }
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+ void onClick_Change(CCtrlButton *)
+ {
+ if (!chkPerProto.IsChecked())
+ SetMyAvatar(NULL, NULL);
+ else {
+ char *proto = GetSelectedProtocol();
+ if (proto != nullptr)
+ SetMyAvatar((WPARAM)proto, NULL);
+ }
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ if (!chkPerProto.IsChecked()) {
+ if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), TranslateT("Global avatar"), MB_YESNO) == IDYES)
+ SetMyAvatar(NULL, (LPARAM)L"");
+ }
+ else {
+ if (char *proto = GetSelectedProtocol()) {
+ char description[256];
+ CallProtoService(proto, PS_GETNAME, _countof(description), (LPARAM)description);
+ wchar_t *descr = mir_a2u(description);
+ if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), descr, MB_YESNO) == IDYES)
+ SetMyAvatar((WPARAM)proto, (LPARAM)L"");
+ mir_free(descr);
+ }
+ }
+ }
+
+ void onChange_PerProto(CCtrlCheck *pCheck)
+ {
+ int diff = 147; // Pre-calc
+
+ if (chkPerProto.IsChecked()) {
+ if (!IsWindowVisible(protocols.GetHwnd())) {
+ // Show list of protocols
+ protocols.Show();
+
+ // Move controls
+ OffsetWindow(IDC_PROTOPIC, diff);
+ OffsetWindow(IDC_CHANGE, diff);
+ OffsetWindow(IDC_DELETE, diff);
+ }
+
+ char *proto = GetSelectedProtocol();
+ if (proto == nullptr) {
+ protocols.SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, 0x0F);
+ }
+ else {
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, (LPARAM)proto);
+ EnableDisableControls(proto);
+ }
+ }
+ else {
+ if (!m_bInitialized) {
+ PostMessage(m_hwnd, WM_COMMAND, IDC_PER_PROTO, 0);
+ }
+ else if (!pCheck || IsWindowVisible(protocols.GetHwnd())) {
+ // Hide list of protocols
+ protocols.Hide();
+
+ // Move controls
+ OffsetWindow(IDC_PROTOPIC, -diff);
+ OffsetWindow(IDC_CHANGE, -diff);
+ OffsetWindow(IDC_DELETE, -diff);
+ }
+
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, NULL);
+ }
+ }
+};
+
+int OnDetailsInit(WPARAM wParam, LPARAM hContact)
+{
+ USERINFOPAGE uip = {};
+ uip.szTitle.a = LPGEN("Avatar");
+ uip.flags = ODPF_ICON;
+ uip.dwInitParam = (LPARAM)g_plugin.getIconHandle(IDI_AVATAR);
+
+ if (hContact == NULL) {
+ // User dialog
+ uip.pDialog = new AvatarProtoInfoDlg();
+ g_plugin.addUserInfo(wParam, &uip);
+ }
+ else {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr || g_plugin.getByte(szProto, 1)) {
+ // Contact dialog
+ uip.position = -2000000000;
+ uip.pDialog = new AvatarUserInfoDlg();
+ g_plugin.addUserInfo(wParam, &uip);
+ }
+ }
+ return 0;
+}
diff --git a/plugins/AVS/src/utils.cpp b/plugins/AVS/src/utils.cpp index f820d870a1..975e3808b3 100644 --- a/plugins/AVS/src/utils.cpp +++ b/plugins/AVS/src/utils.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/version.h b/plugins/AVS/src/version.h index d0bae7638f..b58daf865c 100644 --- a/plugins/AVS/src/version.h +++ b/plugins/AVS/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Loads and manages contact pictures for other plugins."
#define __AUTHOR "Nightwish, Pescuma"
#define __AUTHORWEB "https://miranda-ng.org/p/AVS"
-#define __COPYRIGHT "© 2000-2012 Miranda-IM project, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2000-2012 Miranda-IM project, 2012-23 Miranda NG team"
diff --git a/plugins/AddContactPlus/src/addcontact.cpp b/plugins/AddContactPlus/src/addcontact.cpp index 11770d4945..a80224650f 100644 --- a/plugins/AddContactPlus/src/addcontact.cpp +++ b/plugins/AddContactPlus/src/addcontact.cpp @@ -2,7 +2,7 @@ AddContact+ plugin for Miranda NG
Copyright (C) 2007-11 Bartosz 'Dezeath' Białek
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/plugins/AddContactPlus/src/main.cpp b/plugins/AddContactPlus/src/main.cpp index 8a0ee33db6..44fcf1e81d 100644 --- a/plugins/AddContactPlus/src/main.cpp +++ b/plugins/AddContactPlus/src/main.cpp @@ -2,7 +2,7 @@ AddContact+ plugin for Miranda NG
Copyright (C) 2007-11 Bartosz 'Dezeath' Białek
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/plugins/AddContactPlus/src/stdafx.cxx b/plugins/AddContactPlus/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AddContactPlus/src/stdafx.cxx +++ b/plugins/AddContactPlus/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AddContactPlus/src/stdafx.h b/plugins/AddContactPlus/src/stdafx.h index f17f161d82..e90008d0a6 100644 --- a/plugins/AddContactPlus/src/stdafx.h +++ b/plugins/AddContactPlus/src/stdafx.h @@ -2,7 +2,7 @@ AddContact+ plugin for Miranda NG
Copyright (C) 2007-11 Bartosz 'Dezeath' Białek
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/plugins/AddContactPlus/src/version.h b/plugins/AddContactPlus/src/version.h index 3e206c907d..436a635cc9 100644 --- a/plugins/AddContactPlus/src/version.h +++ b/plugins/AddContactPlus/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Provides the ability to quickly add new contacts."
#define __AUTHOR "Bartosz 'Dezeath' Białek"
#define __AUTHORWEB "https://miranda-ng.org/p/AddContactPlus"
-#define __COPYRIGHT "© 2007-22 Bartosz 'Dezeath' Białek, Miranda NG team"
+#define __COPYRIGHT "© 2007-23 Bartosz 'Dezeath' Białek, Miranda NG team"
diff --git a/plugins/Alarms/src/stdafx.cxx b/plugins/Alarms/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Alarms/src/stdafx.cxx +++ b/plugins/Alarms/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AsSingleWindow/src/stdafx.cxx b/plugins/AsSingleWindow/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/plugins/AsSingleWindow/src/stdafx.cxx +++ b/plugins/AsSingleWindow/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/plugins/AsSingleWindow/src/version.h b/plugins/AsSingleWindow/src/version.h index 52c99a0cb1..452e1b1883 100644 --- a/plugins/AsSingleWindow/src/version.h +++ b/plugins/AsSingleWindow/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Allows you to move, minimize and activate Miranda's windows as if it were a single window."
#define __AUTHOR "Aleksey Smyrnov aka Soar"
#define __AUTHORWEB "https://miranda-ng.org/p/AsSingleWindow"
-#define __COPYRIGHT "© 2010-2011 Soar, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2010-2011 Soar, 2017-23 Miranda NG team"
diff --git a/plugins/AssocMgr/src/stdafx.cxx b/plugins/AssocMgr/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AssocMgr/src/stdafx.cxx +++ b/plugins/AssocMgr/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AuthState/src/stdafx.cxx b/plugins/AuthState/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AuthState/src/stdafx.cxx +++ b/plugins/AuthState/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AutoRun/src/stdafx.cxx b/plugins/AutoRun/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AutoRun/src/stdafx.cxx +++ b/plugins/AutoRun/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AutoShutdown/src/stdafx.cxx b/plugins/AutoShutdown/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AutoShutdown/src/stdafx.cxx +++ b/plugins/AutoShutdown/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/AvatarHistory/src/stdafx.cxx b/plugins/AvatarHistory/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/AvatarHistory/src/stdafx.cxx +++ b/plugins/AvatarHistory/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/BASS_interface/src/stdafx.cxx b/plugins/BASS_interface/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/BASS_interface/src/stdafx.cxx +++ b/plugins/BASS_interface/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/BasicHistory/src/stdafx.cxx b/plugins/BasicHistory/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/BasicHistory/src/stdafx.cxx +++ b/plugins/BasicHistory/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Boltun/src/stdafx.cxx b/plugins/Boltun/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Boltun/src/stdafx.cxx +++ b/plugins/Boltun/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/BossKeyPlus/src/BossKeyIdle.cpp b/plugins/BossKeyPlus/src/BossKeyIdle.cpp index 5a83a2fa11..962d9ff6fd 100644 --- a/plugins/BossKeyPlus/src/BossKeyIdle.cpp +++ b/plugins/BossKeyPlus/src/BossKeyIdle.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/BossKeyPlus/src/stdafx.cxx b/plugins/BossKeyPlus/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/BossKeyPlus/src/stdafx.cxx +++ b/plugins/BossKeyPlus/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/BuddyExpectator/src/stdafx.cxx b/plugins/BuddyExpectator/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/BuddyExpectator/src/stdafx.cxx +++ b/plugins/BuddyExpectator/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/BuddyPounce/src/options.cpp b/plugins/BuddyPounce/src/options.cpp index b77dd1263e..aeedbc9b63 100644 --- a/plugins/BuddyPounce/src/options.cpp +++ b/plugins/BuddyPounce/src/options.cpp @@ -1,245 +1,245 @@ -/* -Copyright (C) 2012-22 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" - -COptionsDlg::COptionsDlg(int dlgId) : - CSuper(dlgId), - m_settings(this, IDC_SETTINGS), - spin(this, IDC_SPIN, 1024), - msg1(this, IDC_SETTINGMSG), - msg2(this, IDC_SETTINGMSG2), - edtNumber(this, IDC_SETTINGNUMBER), - chkAdvanced(this, IDC_USEADVANCED), - chkShowDelivery(this, IDC_SHOWDELIVERYMSGS) -{ - CreateLink(chkAdvanced, g_plugin.bUseAdvanced); - CreateLink(chkShowDelivery, g_plugin.bShowDelivery); - - m_settings.OnSelChange = Callback(this, &COptionsDlg::onSelChange_Settings); -} - -bool COptionsDlg::OnInitDialog() -{ - CSuper::OnInitDialog(); - - m_settings.AddString(TranslateT("Send If My Status Is...")); - m_settings.AddString(TranslateT("Send If They Change Status to...")); - m_settings.AddString(L"----------------------------"); - m_settings.AddString(TranslateT("Reuse Pounce")); - m_settings.AddString(TranslateT("Give Up delay")); - m_settings.AddString(TranslateT("Confirmation Window")); - m_settings.SetCurSel(g_plugin.getByte(hContact, "LastSetting")); - onSelChange_Settings(0); - return true; -} - -bool COptionsDlg::OnApply() -{ - CSuper::OnApply(); - - saveLastSetting(); - return true; -} - -void COptionsDlg::OnDestroy() -{ - if (SendIfMy) - DestroyWindow(SendIfMy); - if (SendWhenThey) - DestroyWindow(SendWhenThey); -} - -void COptionsDlg::onSelChange_Settings(CCtrlListBox*) -{ - if (m_bInitialized) - saveLastSetting(); - - int item = m_settings.GetCurSel(); - switch (item) { - case 0: // Send If My Status Is... - showAll(false); - if (m_bInitialized) - statusModes(true); - break; - - case 1: // Send If They Change status to - showAll(false); - if (m_bInitialized) - statusModes(false); - break; - - case 3: // Reuse Pounce - showAll(true); - msg1.SetText(TranslateT("Reuse this message? (0 to use it once)")); - msg2.SetText(TranslateT("Times")); - edtNumber.SetInt(g_plugin.getByte(hContact, "Reuse")); - break; - - case 4: // Give Up delay - showAll(true); - msg1.SetText(TranslateT("Give up after... (0 to not give up)")); - msg2.SetText(TranslateT("Days")); - edtNumber.SetInt(g_plugin.getByte(hContact, "GiveUpDays")); - break; - - case 5: // confirm window - showAll(true); - msg1.SetText(TranslateT("Show confirmation window? (0 to not Show)")); - msg2.SetText(TranslateT("Seconds to wait before sending")); - edtNumber.SetInt(g_plugin.getWord(hContact, "ConfirmTimeout")); - break; - } - g_plugin.setByte(hContact, "LastSetting", (uint8_t)item); - NotifyChange(); -} - -void COptionsDlg::saveLastSetting() -{ - switch (g_plugin.getByte(hContact, "LastSetting", 2)) { - case 3: // Reuse Pounce - g_plugin.setByte(hContact, "Reuse", (uint8_t)edtNumber.GetInt()); - break; - case 4: // Give Up delay - g_plugin.setByte(hContact, "GiveUpDays", (uint8_t)edtNumber.GetInt()); - g_plugin.setDword(hContact, "GiveUpDate", (uint32_t)edtNumber.GetInt() * SECONDSINADAY); - break; - case 5: // confirm window - g_plugin.setWord(hContact, "ConfirmTimeout", (uint16_t)edtNumber.GetInt()); - break; - } -} - -void COptionsDlg::showAll(bool bShow) -{ - msg1.Show(bShow); - msg2.Show(bShow); - spin.Show(bShow); - edtNumber.Show(bShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Status modes dialog wrapper - -class CStatusModesDlg : public CDlgBase -{ - COptionsDlg *pDlg; - bool isMe; - - CCtrlCheck chk1, chk2, chk3, chk4, chk5, chk6, chk7, chk8; - -public: - CStatusModesDlg(COptionsDlg *_1, bool _2) : - CDlgBase(g_plugin, IDD_STATUSMODES), - pDlg(_1), - isMe(_2), - chk1(this, IDC_CHECK1), - chk2(this, IDC_CHECK2), - chk3(this, IDC_CHECK3), - chk4(this, IDC_CHECK4), - chk5(this, IDC_CHECK5), - chk6(this, IDC_CHECK6), - chk7(this, IDC_CHECK7), - chk8(this, IDC_CHECK8) - { - SetParent(pDlg->GetHwnd()); - } - - bool OnInitDialog() override - { - int statusFlag; - - if (isMe) { - pDlg->SendIfMy = m_hwnd; - statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfMyStatusIsFLAG", 0); - SetCaption(TranslateT("Send If My Status Is")); - chk1.SetText(TranslateT("Any")); - chk2.SetText(TranslateT("Online")); - chk3.SetText(TranslateT("Away")); - chk4.SetText(TranslateT("Not available")); - chk5.SetText(TranslateT("Occupied")); - chk6.SetText(TranslateT("Do not disturb")); - chk7.SetText(TranslateT("Free for chat")); - chk8.SetText(TranslateT("Invisible")); - } - else { - pDlg->SendWhenThey = m_hwnd; - statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", 0); - SetCaption(TranslateT("Send If Their Status changes")); - chk1.SetText(TranslateT("From Offline")); - chk2.SetText(TranslateT("To Online")); - chk3.SetText(TranslateT("To Away")); - chk4.SetText(TranslateT("To Not available")); - chk5.SetText(TranslateT("To Occupied")); - chk6.SetText(TranslateT("To Do not disturb")); - chk7.SetText(TranslateT("To Free for chat")); - chk8.SetText(TranslateT("To Invisible")); - } - - chk1.SetState((statusFlag & ANY) != 0); - chk2.SetState((statusFlag & ONLINE) != 0); - chk3.SetState((statusFlag & AWAY) != 0); - chk4.SetState((statusFlag & NA) != 0); - chk5.SetState((statusFlag & OCCUPIED) != 0); - chk6.SetState((statusFlag & DND) != 0); - chk7.SetState((statusFlag & FFC) != 0); - chk8.SetState((statusFlag & INVISIBLE) != 0); - return true; - } - - bool OnApply() override - { - int flag = chk1.GetState() - | (chk2.GetState() << 1) - | (chk3.GetState() << 2) - | (chk4.GetState() << 3) - | (chk5.GetState() << 4) - | (chk6.GetState() << 5) - | (chk7.GetState() << 6) - | (chk8.GetState() << 7); - - if (isMe) - g_plugin.setWord(pDlg->hContact, "SendIfMyStatusIsFLAG", flag); - else - g_plugin.setWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", flag); - return true; - } - - void OnDestroy() override - { - if (isMe) - pDlg->SendIfMy = nullptr; - else - pDlg->SendWhenThey = nullptr; - } -}; - -void COptionsDlg::statusModes(bool isMe) -{ - if (isMe) { - if (SendIfMy) - SetForegroundWindow(SendIfMy); - else - (new CStatusModesDlg(this, true))->Create(); - } - else { - if (SendWhenThey) - SetForegroundWindow(SendWhenThey); - else - (new CStatusModesDlg(this, false))->Create(); - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+COptionsDlg::COptionsDlg(int dlgId) :
+ CSuper(dlgId),
+ m_settings(this, IDC_SETTINGS),
+ spin(this, IDC_SPIN, 1024),
+ msg1(this, IDC_SETTINGMSG),
+ msg2(this, IDC_SETTINGMSG2),
+ edtNumber(this, IDC_SETTINGNUMBER),
+ chkAdvanced(this, IDC_USEADVANCED),
+ chkShowDelivery(this, IDC_SHOWDELIVERYMSGS)
+{
+ CreateLink(chkAdvanced, g_plugin.bUseAdvanced);
+ CreateLink(chkShowDelivery, g_plugin.bShowDelivery);
+
+ m_settings.OnSelChange = Callback(this, &COptionsDlg::onSelChange_Settings);
+}
+
+bool COptionsDlg::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ m_settings.AddString(TranslateT("Send If My Status Is..."));
+ m_settings.AddString(TranslateT("Send If They Change Status to..."));
+ m_settings.AddString(L"----------------------------");
+ m_settings.AddString(TranslateT("Reuse Pounce"));
+ m_settings.AddString(TranslateT("Give Up delay"));
+ m_settings.AddString(TranslateT("Confirmation Window"));
+ m_settings.SetCurSel(g_plugin.getByte(hContact, "LastSetting"));
+ onSelChange_Settings(0);
+ return true;
+}
+
+bool COptionsDlg::OnApply()
+{
+ CSuper::OnApply();
+
+ saveLastSetting();
+ return true;
+}
+
+void COptionsDlg::OnDestroy()
+{
+ if (SendIfMy)
+ DestroyWindow(SendIfMy);
+ if (SendWhenThey)
+ DestroyWindow(SendWhenThey);
+}
+
+void COptionsDlg::onSelChange_Settings(CCtrlListBox*)
+{
+ if (m_bInitialized)
+ saveLastSetting();
+
+ int item = m_settings.GetCurSel();
+ switch (item) {
+ case 0: // Send If My Status Is...
+ showAll(false);
+ if (m_bInitialized)
+ statusModes(true);
+ break;
+
+ case 1: // Send If They Change status to
+ showAll(false);
+ if (m_bInitialized)
+ statusModes(false);
+ break;
+
+ case 3: // Reuse Pounce
+ showAll(true);
+ msg1.SetText(TranslateT("Reuse this message? (0 to use it once)"));
+ msg2.SetText(TranslateT("Times"));
+ edtNumber.SetInt(g_plugin.getByte(hContact, "Reuse"));
+ break;
+
+ case 4: // Give Up delay
+ showAll(true);
+ msg1.SetText(TranslateT("Give up after... (0 to not give up)"));
+ msg2.SetText(TranslateT("Days"));
+ edtNumber.SetInt(g_plugin.getByte(hContact, "GiveUpDays"));
+ break;
+
+ case 5: // confirm window
+ showAll(true);
+ msg1.SetText(TranslateT("Show confirmation window? (0 to not Show)"));
+ msg2.SetText(TranslateT("Seconds to wait before sending"));
+ edtNumber.SetInt(g_plugin.getWord(hContact, "ConfirmTimeout"));
+ break;
+ }
+ g_plugin.setByte(hContact, "LastSetting", (uint8_t)item);
+ NotifyChange();
+}
+
+void COptionsDlg::saveLastSetting()
+{
+ switch (g_plugin.getByte(hContact, "LastSetting", 2)) {
+ case 3: // Reuse Pounce
+ g_plugin.setByte(hContact, "Reuse", (uint8_t)edtNumber.GetInt());
+ break;
+ case 4: // Give Up delay
+ g_plugin.setByte(hContact, "GiveUpDays", (uint8_t)edtNumber.GetInt());
+ g_plugin.setDword(hContact, "GiveUpDate", (uint32_t)edtNumber.GetInt() * SECONDSINADAY);
+ break;
+ case 5: // confirm window
+ g_plugin.setWord(hContact, "ConfirmTimeout", (uint16_t)edtNumber.GetInt());
+ break;
+ }
+}
+
+void COptionsDlg::showAll(bool bShow)
+{
+ msg1.Show(bShow);
+ msg2.Show(bShow);
+ spin.Show(bShow);
+ edtNumber.Show(bShow);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Status modes dialog wrapper
+
+class CStatusModesDlg : public CDlgBase
+{
+ COptionsDlg *pDlg;
+ bool isMe;
+
+ CCtrlCheck chk1, chk2, chk3, chk4, chk5, chk6, chk7, chk8;
+
+public:
+ CStatusModesDlg(COptionsDlg *_1, bool _2) :
+ CDlgBase(g_plugin, IDD_STATUSMODES),
+ pDlg(_1),
+ isMe(_2),
+ chk1(this, IDC_CHECK1),
+ chk2(this, IDC_CHECK2),
+ chk3(this, IDC_CHECK3),
+ chk4(this, IDC_CHECK4),
+ chk5(this, IDC_CHECK5),
+ chk6(this, IDC_CHECK6),
+ chk7(this, IDC_CHECK7),
+ chk8(this, IDC_CHECK8)
+ {
+ SetParent(pDlg->GetHwnd());
+ }
+
+ bool OnInitDialog() override
+ {
+ int statusFlag;
+
+ if (isMe) {
+ pDlg->SendIfMy = m_hwnd;
+ statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfMyStatusIsFLAG", 0);
+ SetCaption(TranslateT("Send If My Status Is"));
+ chk1.SetText(TranslateT("Any"));
+ chk2.SetText(TranslateT("Online"));
+ chk3.SetText(TranslateT("Away"));
+ chk4.SetText(TranslateT("Not available"));
+ chk5.SetText(TranslateT("Occupied"));
+ chk6.SetText(TranslateT("Do not disturb"));
+ chk7.SetText(TranslateT("Free for chat"));
+ chk8.SetText(TranslateT("Invisible"));
+ }
+ else {
+ pDlg->SendWhenThey = m_hwnd;
+ statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", 0);
+ SetCaption(TranslateT("Send If Their Status changes"));
+ chk1.SetText(TranslateT("From Offline"));
+ chk2.SetText(TranslateT("To Online"));
+ chk3.SetText(TranslateT("To Away"));
+ chk4.SetText(TranslateT("To Not available"));
+ chk5.SetText(TranslateT("To Occupied"));
+ chk6.SetText(TranslateT("To Do not disturb"));
+ chk7.SetText(TranslateT("To Free for chat"));
+ chk8.SetText(TranslateT("To Invisible"));
+ }
+
+ chk1.SetState((statusFlag & ANY) != 0);
+ chk2.SetState((statusFlag & ONLINE) != 0);
+ chk3.SetState((statusFlag & AWAY) != 0);
+ chk4.SetState((statusFlag & NA) != 0);
+ chk5.SetState((statusFlag & OCCUPIED) != 0);
+ chk6.SetState((statusFlag & DND) != 0);
+ chk7.SetState((statusFlag & FFC) != 0);
+ chk8.SetState((statusFlag & INVISIBLE) != 0);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int flag = chk1.GetState()
+ | (chk2.GetState() << 1)
+ | (chk3.GetState() << 2)
+ | (chk4.GetState() << 3)
+ | (chk5.GetState() << 4)
+ | (chk6.GetState() << 5)
+ | (chk7.GetState() << 6)
+ | (chk8.GetState() << 7);
+
+ if (isMe)
+ g_plugin.setWord(pDlg->hContact, "SendIfMyStatusIsFLAG", flag);
+ else
+ g_plugin.setWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", flag);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ if (isMe)
+ pDlg->SendIfMy = nullptr;
+ else
+ pDlg->SendWhenThey = nullptr;
+ }
+};
+
+void COptionsDlg::statusModes(bool isMe)
+{
+ if (isMe) {
+ if (SendIfMy)
+ SetForegroundWindow(SendIfMy);
+ else
+ (new CStatusModesDlg(this, true))->Create();
+ }
+ else {
+ if (SendWhenThey)
+ SetForegroundWindow(SendWhenThey);
+ else
+ (new CStatusModesDlg(this, false))->Create();
+ }
+}
diff --git a/plugins/BuddyPounce/src/stdafx.cxx b/plugins/BuddyPounce/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/BuddyPounce/src/stdafx.cxx +++ b/plugins/BuddyPounce/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/CSList/src/stdafx.cxx b/plugins/CSList/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/CSList/src/stdafx.cxx +++ b/plugins/CSList/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/CSList/src/version.h b/plugins/CSList/src/version.h index d85a555d5d..c4330c6960 100644 --- a/plugins/CSList/src/version.h +++ b/plugins/CSList/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "This plugin offers simple management functions to keep your extra statuses on one place."
#define __AUTHOR "Mataes, jarvis"
#define __AUTHORWEB "https://miranda-ng.org/p/CSList"
-#define __COPYRIGHT "© 2010-22 Mataes, 2007-09 jarvis"
+#define __COPYRIGHT "© 2010-23 Mataes, 2007-09 jarvis"
diff --git a/plugins/ChangeKeyboardLayout/src/stdafx.cxx b/plugins/ChangeKeyboardLayout/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ChangeKeyboardLayout/src/stdafx.cxx +++ b/plugins/ChangeKeyboardLayout/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ClientChangeNotify/src/stdafx.cxx b/plugins/ClientChangeNotify/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ClientChangeNotify/src/stdafx.cxx +++ b/plugins/ClientChangeNotify/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Clist_blind/src/clc.cpp b/plugins/Clist_blind/src/clc.cpp index ca86f428cc..dbdc1fec5a 100644 --- a/plugins/Clist_blind/src/clc.cpp +++ b/plugins/Clist_blind/src/clc.cpp @@ -1,368 +1,368 @@ -/* -Copyright (C) 2012-22 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" - -static void ScrollTo(HWND, ClcData *dat, int, int) -{ - ListView_SetSelectionMark(dat->hwnd_list, dat->selection); - ListView_SetItemState(dat->hwnd_list, dat->selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -wchar_t status_name[128]; -static wchar_t* GetStatusName(ClcContact *item) -{ - status_name[0] = '\0'; - if (item->hContact == NULL || item->pce->szProto == nullptr) - return status_name; - - // Get XStatusName - MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusName", status_name, _countof(status_name), nullptr); - if (status_name[0] != '\0') - return status_name; - - // Get status name - int status = db_get_w(item->hContact, item->pce->szProto, "Status", ID_STATUS_OFFLINE); - mir_wstrncpy(status_name, Clist_GetStatusModeDescription(status, 0), _countof(status_name)); - return status_name; -} - -wchar_t status_message[256]; -static wchar_t* GetStatusMessage(ClcContact *item) -{ - status_message[0] = '\0'; - if (item->hContact == NULL || item->pce->szProto == nullptr) - return status_message; - - // Get XStatusMsg - MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusMsg", status_message, _countof(status_message), nullptr); - if (status_message[0] != '\0') - return status_message; - - // Get status message - MyDBGetContactSettingTString(item->hContact, "CList", "StatusMsg", status_message, _countof(status_message), nullptr); - return status_message; -} - -wchar_t proto_name[128]; -static wchar_t* GetProtoName(ClcContact *item) -{ - proto_name[0] = '\0'; - if (item->hContact == NULL || item->pce->szProto == nullptr) { - mir_wstrncpy(proto_name, TranslateT("Unknown protocol"), _countof(proto_name)); - return proto_name; - } - - PROTOACCOUNT *acc = Proto_GetAccount(item->pce->szProto); - if (acc == nullptr) { - char description[128]; - CallProtoService(item->pce->szProto, PS_GETNAME, sizeof(description), (LPARAM)description); - mir_snwprintf(proto_name, L"%S", description); - return proto_name; - } - - mir_wstrncpy(proto_name, acc->tszAccountName, _countof(proto_name)); - return proto_name; -} - -static void RebuildEntireListInternal(HWND hwnd, ClcData *tmp_dat, bool call_orig) -{ - ClcData *dat = tmp_dat; - int level = 0, iItem = 0; - - wchar_t tmp[1024]; - wchar_t template_contact[1024]; - wchar_t template_group[1024]; - wchar_t template_divider[1024]; - wchar_t template_info[1024]; - int selection = dat->selection; - BOOL has_focus = (GetFocus() == dat->hwnd_list || GetFocus() == hwnd); - - if (call_orig) - coreCli.pfnRebuildEntireList(hwnd, dat); - - MyDBGetContactSettingTString(NULL, "CLC", "TemplateContact", template_contact, 1024, TranslateT("%name% [%status% %protocol%] %status_message%")); - MyDBGetContactSettingTString(NULL, "CLC", "TemplateGroup", template_group, 1024, TranslateT("Group: %name% %count% [%mode%]")); - MyDBGetContactSettingTString(NULL, "CLC", "TemplateDivider", template_divider, 1024, TranslateT("Divider: %s")); - MyDBGetContactSettingTString(NULL, "CLC", "TemplateInfo", template_info, 1024, TranslateT("Info: %s")); - - SendMessage(dat->hwnd_list, WM_SETREDRAW, FALSE, 0); - - // Reset content - ListView_DeleteAllItems(dat->hwnd_list); - - // Set font - SendMessage(dat->hwnd_list, WM_SETFONT, (WPARAM)dat->fontInfo[FONTID_CONTACTS].hFont, 0); - - // Add all items to the list - ClcGroup *group = &dat->list; - group->scanIndex = 0; - - wchar_t *text = tmp; - size_t size = _countof(tmp); - while (true) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - - level--; - group->scanIndex++; - continue; - } - - ClcContact *item = group->cl[group->scanIndex]; - text[0] = '\0'; - switch (item->type) { - case CLCIT_GROUP: - { - CMStringW wszText(template_group); - wszText.Replace(L"%name%", item->szText); - wszText.Replace(L"%mode%", item->group->expanded ? TranslateT("Expanded") : TranslateT("Collapsed")); - - wchar_t *szCounts = Clist_GetGroupCountsText(dat, item); - wchar_t count[128]; - if (szCounts[0] != '\0') - mir_snwprintf(count, L"%s ", szCounts); - else - count[0] = '\0'; - wszText.Replace(L"%count%", count); - - if (!wszText.IsEmpty()) - mir_wstrncpy(text, wszText, size); - } - break; - - case CLCIT_CONTACT: - { - CMStringW wszText(template_contact); - wszText.Replace(L"%name%", item->szText); - wszText.Replace(L"%status%", GetStatusName(item)); - wszText.Replace(L"%protocol%", GetProtoName(item)); - wszText.Replace(L"%status_message%", GetStatusMessage(item)); - - if (!wszText.IsEmpty()) - mir_wstrncpy(text, wszText, size); - } - break; - - case CLCIT_DIVIDER: - mir_snwprintf(text, size, template_divider, item->szText); - break; - - case CLCIT_INFO: - mir_snwprintf(text, size, template_info, item->szText); - break; - } - - LVITEMW lvi = {}; - lvi.iItem = iItem++; - lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_INDENT; - lvi.pszText = tmp; - lvi.iIndent = level; - lvi.lParam = LPARAM(item); - ListView_InsertItem(dat->hwnd_list, &lvi); - - if (item->type == CLCIT_GROUP && item->group->expanded) { - group = item->group; - level++; - group->scanIndex = 0; - continue; - } - group->scanIndex++; - } - - SendMessage(dat->hwnd_list, WM_SETREDRAW, TRUE, 0); - InvalidateRect(dat->hwnd_list, nullptr, TRUE); - - if (dat->list.cl.getCount()) { - RECT rc; - ListView_GetItemRect(dat->hwnd_list, 0, &rc, LVIR_SELECTBOUNDS); - if (rc.bottom > rc.top) - dat->rowHeight = rc.bottom - rc.top; - } - - dat->selection = selection; - ScrollTo(dat->hwnd_list, dat, 0, 0); - if (has_focus) - SetFocus(dat->hwnd_list); - - dat->bNeedsRebuild = false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCCREATE: - return DefWindowProc(hwnd, msg, wParam, lParam); - - case WM_CREATE: - break; - - case WM_DRAWITEM: - return Menu_DrawItem(lParam); - } - return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam); -} - -static LRESULT CALLBACK ContactListControlSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_KEYDOWN: - if (wParam == VK_LEFT || wParam == VK_RIGHT || wParam == VK_RETURN || wParam == VK_DELETE || wParam == VK_F2) - coreCli.pfnContactListControlWndProc(GetParent(hwnd), WM_KEYDOWN, wParam, 0); - break; - - case WM_CHAR: - HWND hwndParent = GetParent(hwnd); - ClcData *dat = (ClcData *)GetWindowLongPtr(hwndParent, 0); - int iSaveSelection = dat->selection; - - coreCli.pfnContactListControlWndProc(hwndParent, msg, wParam, lParam); - - if (iSaveSelection != dat->selection) - ScrollTo(dat->hwnd_list, dat, 0, 0); - - return 0; - } - - return mir_callNextSubclass(hwnd, ContactListControlSubclass, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - ClcData *dat = (ClcData *)GetWindowLongPtr(hwnd, 0); - RECT r; - - switch (msg) { - case WM_CREATE: - dat = new ClcData(); - SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); - - dat->hwnd_list = CreateWindowW(WC_LISTVIEWW, L"", - WS_VISIBLE | WS_CHILD | WS_VSCROLL | LVS_NOCOLUMNHEADER | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_REPORT, - 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr); - dat->bNeedsRebuild = false; - mir_subclassWindow(dat->hwnd_list, ContactListControlSubclass); - - ListView_SetExtendedListViewStyle(dat->hwnd_list, LVS_EX_FULLROWSELECT); - - GetClientRect(hwnd, &r); - SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE); - { - LVCOLUMN lvc = {}; - lvc.mask = LVCF_FMT | LVCF_WIDTH; - lvc.fmt = LVCFMT_LEFT; - lvc.cx = r.right - r.left; - ListView_InsertColumn(dat->hwnd_list, 0, &lvc); - - HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 10, 1); - ListView_SetImageList(dat->hwnd_list, hImgList, LVSIL_SMALL); - } - break; - - case WM_SIZE: - GetClientRect(hwnd, &r); - SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE); - ListView_SetColumnWidth(dat->hwnd_list, 0, r.right - r.left); - break; - - case WM_PRINTCLIENT: - case WM_PAINT: - if (dat->bNeedsRebuild) - RebuildEntireListInternal(hwnd, dat, false); - __fallthrough; - - case WM_VSCROLL: - case WM_MOUSEWHEEL: - return DefWindowProc(hwnd, msg, wParam, lParam); - - case INTM_SCROLLBARCHANGED: - return TRUE; - - case WM_NOTIFY: - if (LPNMHDR pnmh = (LPNMHDR)lParam) - if (pnmh->code == LVN_ITEMCHANGED || pnmh->code == LVN_ITEMACTIVATE) - dat->selection = ListView_GetSelectionMark(dat->hwnd_list); - break; - - case WM_SETFOCUS: - case WM_ENABLE: - SetFocus(dat->hwnd_list); - break; - } - - return coreCli.pfnContactListControlWndProc(hwnd, msg, wParam, lParam); -} - -static void RebuildEntireList(HWND hwnd, ClcData *dat) -{ - RebuildEntireListInternal(hwnd, dat, true); -} - -static void SetGroupExpand(HWND hwnd, ClcData *dat, ClcGroup *group, int newState) -{ - coreCli.pfnSetGroupExpand(hwnd, dat, group, newState); - dat->bNeedsRebuild = true; -} - -static void RecalcScrollBar(HWND, ClcData *) -{ -} - -static int GetRowHeight(ClcData *dat, int) -{ - return dat->rowHeight; -} - -static void LoadClcOptions(HWND hwnd, ClcData *dat, BOOL bFirst) -{ - coreCli.pfnLoadClcOptions(hwnd, dat, bFirst); - - dat->rowHeight = 16; -} - -static void SortCLC(HWND hwnd, ClcData *dat, int useInsertionSort) -{ - if (dat->bNeedsResort) { - coreCli.pfnSortCLC(hwnd, dat, useInsertionSort); - dat->bNeedsRebuild = true; - } -} - -void InitClc() -{ - Clist_GetInterface(); - coreCli = g_clistApi; - g_clistApi.bOwnerDrawMenu = false; - g_clistApi.hInst = g_plugin.getInst(); - g_clistApi.pfnContactListWndProc = ContactListWndProc; - g_clistApi.pfnContactListControlWndProc = ContactListControlWndProc; - g_clistApi.pfnRebuildEntireList = RebuildEntireList; - g_clistApi.pfnSetGroupExpand = SetGroupExpand; - g_clistApi.pfnRecalcScrollBar = RecalcScrollBar; - g_clistApi.pfnScrollTo = ScrollTo; - g_clistApi.pfnLoadClcOptions = LoadClcOptions; - g_clistApi.pfnGetRowHeight = GetRowHeight; - g_clistApi.pfnSortCLC = SortCLC; - g_clistApi.pfnCompareContacts = CompareContacts; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+static void ScrollTo(HWND, ClcData *dat, int, int)
+{
+ ListView_SetSelectionMark(dat->hwnd_list, dat->selection);
+ ListView_SetItemState(dat->hwnd_list, dat->selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+wchar_t status_name[128];
+static wchar_t* GetStatusName(ClcContact *item)
+{
+ status_name[0] = '\0';
+ if (item->hContact == NULL || item->pce->szProto == nullptr)
+ return status_name;
+
+ // Get XStatusName
+ MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusName", status_name, _countof(status_name), nullptr);
+ if (status_name[0] != '\0')
+ return status_name;
+
+ // Get status name
+ int status = db_get_w(item->hContact, item->pce->szProto, "Status", ID_STATUS_OFFLINE);
+ mir_wstrncpy(status_name, Clist_GetStatusModeDescription(status, 0), _countof(status_name));
+ return status_name;
+}
+
+wchar_t status_message[256];
+static wchar_t* GetStatusMessage(ClcContact *item)
+{
+ status_message[0] = '\0';
+ if (item->hContact == NULL || item->pce->szProto == nullptr)
+ return status_message;
+
+ // Get XStatusMsg
+ MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusMsg", status_message, _countof(status_message), nullptr);
+ if (status_message[0] != '\0')
+ return status_message;
+
+ // Get status message
+ MyDBGetContactSettingTString(item->hContact, "CList", "StatusMsg", status_message, _countof(status_message), nullptr);
+ return status_message;
+}
+
+wchar_t proto_name[128];
+static wchar_t* GetProtoName(ClcContact *item)
+{
+ proto_name[0] = '\0';
+ if (item->hContact == NULL || item->pce->szProto == nullptr) {
+ mir_wstrncpy(proto_name, TranslateT("Unknown protocol"), _countof(proto_name));
+ return proto_name;
+ }
+
+ PROTOACCOUNT *acc = Proto_GetAccount(item->pce->szProto);
+ if (acc == nullptr) {
+ char description[128];
+ CallProtoService(item->pce->szProto, PS_GETNAME, sizeof(description), (LPARAM)description);
+ mir_snwprintf(proto_name, L"%S", description);
+ return proto_name;
+ }
+
+ mir_wstrncpy(proto_name, acc->tszAccountName, _countof(proto_name));
+ return proto_name;
+}
+
+static void RebuildEntireListInternal(HWND hwnd, ClcData *tmp_dat, bool call_orig)
+{
+ ClcData *dat = tmp_dat;
+ int level = 0, iItem = 0;
+
+ wchar_t tmp[1024];
+ wchar_t template_contact[1024];
+ wchar_t template_group[1024];
+ wchar_t template_divider[1024];
+ wchar_t template_info[1024];
+ int selection = dat->selection;
+ BOOL has_focus = (GetFocus() == dat->hwnd_list || GetFocus() == hwnd);
+
+ if (call_orig)
+ coreCli.pfnRebuildEntireList(hwnd, dat);
+
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateContact", template_contact, 1024, TranslateT("%name% [%status% %protocol%] %status_message%"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateGroup", template_group, 1024, TranslateT("Group: %name% %count% [%mode%]"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateDivider", template_divider, 1024, TranslateT("Divider: %s"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateInfo", template_info, 1024, TranslateT("Info: %s"));
+
+ SendMessage(dat->hwnd_list, WM_SETREDRAW, FALSE, 0);
+
+ // Reset content
+ ListView_DeleteAllItems(dat->hwnd_list);
+
+ // Set font
+ SendMessage(dat->hwnd_list, WM_SETFONT, (WPARAM)dat->fontInfo[FONTID_CONTACTS].hFont, 0);
+
+ // Add all items to the list
+ ClcGroup *group = &dat->list;
+ group->scanIndex = 0;
+
+ wchar_t *text = tmp;
+ size_t size = _countof(tmp);
+ while (true) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+
+ level--;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *item = group->cl[group->scanIndex];
+ text[0] = '\0';
+ switch (item->type) {
+ case CLCIT_GROUP:
+ {
+ CMStringW wszText(template_group);
+ wszText.Replace(L"%name%", item->szText);
+ wszText.Replace(L"%mode%", item->group->expanded ? TranslateT("Expanded") : TranslateT("Collapsed"));
+
+ wchar_t *szCounts = Clist_GetGroupCountsText(dat, item);
+ wchar_t count[128];
+ if (szCounts[0] != '\0')
+ mir_snwprintf(count, L"%s ", szCounts);
+ else
+ count[0] = '\0';
+ wszText.Replace(L"%count%", count);
+
+ if (!wszText.IsEmpty())
+ mir_wstrncpy(text, wszText, size);
+ }
+ break;
+
+ case CLCIT_CONTACT:
+ {
+ CMStringW wszText(template_contact);
+ wszText.Replace(L"%name%", item->szText);
+ wszText.Replace(L"%status%", GetStatusName(item));
+ wszText.Replace(L"%protocol%", GetProtoName(item));
+ wszText.Replace(L"%status_message%", GetStatusMessage(item));
+
+ if (!wszText.IsEmpty())
+ mir_wstrncpy(text, wszText, size);
+ }
+ break;
+
+ case CLCIT_DIVIDER:
+ mir_snwprintf(text, size, template_divider, item->szText);
+ break;
+
+ case CLCIT_INFO:
+ mir_snwprintf(text, size, template_info, item->szText);
+ break;
+ }
+
+ LVITEMW lvi = {};
+ lvi.iItem = iItem++;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_INDENT;
+ lvi.pszText = tmp;
+ lvi.iIndent = level;
+ lvi.lParam = LPARAM(item);
+ ListView_InsertItem(dat->hwnd_list, &lvi);
+
+ if (item->type == CLCIT_GROUP && item->group->expanded) {
+ group = item->group;
+ level++;
+ group->scanIndex = 0;
+ continue;
+ }
+ group->scanIndex++;
+ }
+
+ SendMessage(dat->hwnd_list, WM_SETREDRAW, TRUE, 0);
+ InvalidateRect(dat->hwnd_list, nullptr, TRUE);
+
+ if (dat->list.cl.getCount()) {
+ RECT rc;
+ ListView_GetItemRect(dat->hwnd_list, 0, &rc, LVIR_SELECTBOUNDS);
+ if (rc.bottom > rc.top)
+ dat->rowHeight = rc.bottom - rc.top;
+ }
+
+ dat->selection = selection;
+ ScrollTo(dat->hwnd_list, dat, 0, 0);
+ if (has_focus)
+ SetFocus(dat->hwnd_list);
+
+ dat->bNeedsRebuild = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCCREATE:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_CREATE:
+ break;
+
+ case WM_DRAWITEM:
+ return Menu_DrawItem(lParam);
+ }
+ return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK ContactListControlSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ if (wParam == VK_LEFT || wParam == VK_RIGHT || wParam == VK_RETURN || wParam == VK_DELETE || wParam == VK_F2)
+ coreCli.pfnContactListControlWndProc(GetParent(hwnd), WM_KEYDOWN, wParam, 0);
+ break;
+
+ case WM_CHAR:
+ HWND hwndParent = GetParent(hwnd);
+ ClcData *dat = (ClcData *)GetWindowLongPtr(hwndParent, 0);
+ int iSaveSelection = dat->selection;
+
+ coreCli.pfnContactListControlWndProc(hwndParent, msg, wParam, lParam);
+
+ if (iSaveSelection != dat->selection)
+ ScrollTo(dat->hwnd_list, dat, 0, 0);
+
+ return 0;
+ }
+
+ return mir_callNextSubclass(hwnd, ContactListControlSubclass, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ ClcData *dat = (ClcData *)GetWindowLongPtr(hwnd, 0);
+ RECT r;
+
+ switch (msg) {
+ case WM_CREATE:
+ dat = new ClcData();
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat);
+
+ dat->hwnd_list = CreateWindowW(WC_LISTVIEWW, L"",
+ WS_VISIBLE | WS_CHILD | WS_VSCROLL | LVS_NOCOLUMNHEADER | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_REPORT,
+ 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr);
+ dat->bNeedsRebuild = false;
+ mir_subclassWindow(dat->hwnd_list, ContactListControlSubclass);
+
+ ListView_SetExtendedListViewStyle(dat->hwnd_list, LVS_EX_FULLROWSELECT);
+
+ GetClientRect(hwnd, &r);
+ SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+ {
+ LVCOLUMN lvc = {};
+ lvc.mask = LVCF_FMT | LVCF_WIDTH;
+ lvc.fmt = LVCFMT_LEFT;
+ lvc.cx = r.right - r.left;
+ ListView_InsertColumn(dat->hwnd_list, 0, &lvc);
+
+ HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 10, 1);
+ ListView_SetImageList(dat->hwnd_list, hImgList, LVSIL_SMALL);
+ }
+ break;
+
+ case WM_SIZE:
+ GetClientRect(hwnd, &r);
+ SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+ ListView_SetColumnWidth(dat->hwnd_list, 0, r.right - r.left);
+ break;
+
+ case WM_PRINTCLIENT:
+ case WM_PAINT:
+ if (dat->bNeedsRebuild)
+ RebuildEntireListInternal(hwnd, dat, false);
+ __fallthrough;
+
+ case WM_VSCROLL:
+ case WM_MOUSEWHEEL:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case INTM_SCROLLBARCHANGED:
+ return TRUE;
+
+ case WM_NOTIFY:
+ if (LPNMHDR pnmh = (LPNMHDR)lParam)
+ if (pnmh->code == LVN_ITEMCHANGED || pnmh->code == LVN_ITEMACTIVATE)
+ dat->selection = ListView_GetSelectionMark(dat->hwnd_list);
+ break;
+
+ case WM_SETFOCUS:
+ case WM_ENABLE:
+ SetFocus(dat->hwnd_list);
+ break;
+ }
+
+ return coreCli.pfnContactListControlWndProc(hwnd, msg, wParam, lParam);
+}
+
+static void RebuildEntireList(HWND hwnd, ClcData *dat)
+{
+ RebuildEntireListInternal(hwnd, dat, true);
+}
+
+static void SetGroupExpand(HWND hwnd, ClcData *dat, ClcGroup *group, int newState)
+{
+ coreCli.pfnSetGroupExpand(hwnd, dat, group, newState);
+ dat->bNeedsRebuild = true;
+}
+
+static void RecalcScrollBar(HWND, ClcData *)
+{
+}
+
+static int GetRowHeight(ClcData *dat, int)
+{
+ return dat->rowHeight;
+}
+
+static void LoadClcOptions(HWND hwnd, ClcData *dat, BOOL bFirst)
+{
+ coreCli.pfnLoadClcOptions(hwnd, dat, bFirst);
+
+ dat->rowHeight = 16;
+}
+
+static void SortCLC(HWND hwnd, ClcData *dat, int useInsertionSort)
+{
+ if (dat->bNeedsResort) {
+ coreCli.pfnSortCLC(hwnd, dat, useInsertionSort);
+ dat->bNeedsRebuild = true;
+ }
+}
+
+void InitClc()
+{
+ Clist_GetInterface();
+ coreCli = g_clistApi;
+ g_clistApi.bOwnerDrawMenu = false;
+ g_clistApi.hInst = g_plugin.getInst();
+ g_clistApi.pfnContactListWndProc = ContactListWndProc;
+ g_clistApi.pfnContactListControlWndProc = ContactListControlWndProc;
+ g_clistApi.pfnRebuildEntireList = RebuildEntireList;
+ g_clistApi.pfnSetGroupExpand = SetGroupExpand;
+ g_clistApi.pfnRecalcScrollBar = RecalcScrollBar;
+ g_clistApi.pfnScrollTo = ScrollTo;
+ g_clistApi.pfnLoadClcOptions = LoadClcOptions;
+ g_clistApi.pfnGetRowHeight = GetRowHeight;
+ g_clistApi.pfnSortCLC = SortCLC;
+ g_clistApi.pfnCompareContacts = CompareContacts;
+}
diff --git a/plugins/Clist_blind/src/clc.h b/plugins/Clist_blind/src/clc.h index 32ea4b9fbb..828cd5793d 100644 --- a/plugins/Clist_blind/src/clc.h +++ b/plugins/Clist_blind/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/clcopts.cpp b/plugins/Clist_blind/src/clcopts.cpp index 023d115fd7..1c540bc780 100644 --- a/plugins/Clist_blind/src/clcopts.cpp +++ b/plugins/Clist_blind/src/clcopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/clistmenus.cpp b/plugins/Clist_blind/src/clistmenus.cpp index e9700f7bca..718e878b96 100644 --- a/plugins/Clist_blind/src/clistmenus.cpp +++ b/plugins/Clist_blind/src/clistmenus.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/clistopts.cpp b/plugins/Clist_blind/src/clistopts.cpp index 5ab790bb57..28352cce7c 100644 --- a/plugins/Clist_blind/src/clistopts.cpp +++ b/plugins/Clist_blind/src/clistopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/cluiopts.cpp b/plugins/Clist_blind/src/cluiopts.cpp index 4b7a805a08..f785534c7e 100644 --- a/plugins/Clist_blind/src/cluiopts.cpp +++ b/plugins/Clist_blind/src/cluiopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/contact.cpp b/plugins/Clist_blind/src/contact.cpp index 96d106b544..2e024404db 100644 --- a/plugins/Clist_blind/src/contact.cpp +++ b/plugins/Clist_blind/src/contact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/init.cpp b/plugins/Clist_blind/src/init.cpp index b9da691500..2e05c537f4 100644 --- a/plugins/Clist_blind/src/init.cpp +++ b/plugins/Clist_blind/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/stdafx.cxx b/plugins/Clist_blind/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Clist_blind/src/stdafx.cxx +++ b/plugins/Clist_blind/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Clist_blind/src/stdafx.h b/plugins/Clist_blind/src/stdafx.h index abb668e1da..b896fe42ff 100644 --- a/plugins/Clist_blind/src/stdafx.h +++ b/plugins/Clist_blind/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/utils.cpp b/plugins/Clist_blind/src/utils.cpp index b53d78a47c..211139a1cc 100644 --- a/plugins/Clist_blind/src/utils.cpp +++ b/plugins/Clist_blind/src/utils.cpp @@ -1,41 +1,41 @@ -/* -Copyright (C) 2012-22 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" - -wchar_t* MyDBGetContactSettingTString(MCONTACT hContact, char *module, char *setting, wchar_t *out, size_t len, wchar_t *def) -{ - out[0] = '\0'; - - DBVARIANT dbv; - if (!db_get_ws(hContact, module, setting, &dbv)) { - if (dbv.type == DBVT_ASCIIZ) - MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, out, (int)len); - else if (dbv.type == DBVT_UTF8) - MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, out, (int)len); - else if (dbv.type == DBVT_WCHAR) - mir_wstrncpy(out, dbv.pwszVal, len); - else if (def != nullptr) - mir_wstrncpy(out, def, len); - - db_free(&dbv); - } - else if (def != nullptr) - mir_wstrncpy(out, def, len); - - return out; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+wchar_t* MyDBGetContactSettingTString(MCONTACT hContact, char *module, char *setting, wchar_t *out, size_t len, wchar_t *def)
+{
+ out[0] = '\0';
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, module, setting, &dbv)) {
+ if (dbv.type == DBVT_ASCIIZ)
+ MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, out, (int)len);
+ else if (dbv.type == DBVT_UTF8)
+ MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, out, (int)len);
+ else if (dbv.type == DBVT_WCHAR)
+ mir_wstrncpy(out, dbv.pwszVal, len);
+ else if (def != nullptr)
+ mir_wstrncpy(out, def, len);
+
+ db_free(&dbv);
+ }
+ else if (def != nullptr)
+ mir_wstrncpy(out, def, len);
+
+ return out;
+}
diff --git a/plugins/Clist_blind/src/version.h b/plugins/Clist_blind/src/version.h index c641efe7ce..d7b227fa66 100644 --- a/plugins/Clist_blind/src/version.h +++ b/plugins/Clist_blind/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "A contact list for blind folks."
#define __AUTHOR "Ricardo Pescuma Domenecci, based on previous work from Miranda IM project"
#define __AUTHORWEB "https://miranda-ng.org/p/Clist_blind"
-#define __COPYRIGHT "© 2000-2009 Miranda IM project, Ricardo Pescuma Domenecci, 2013-22 Miranda NG team"
+#define __COPYRIGHT "© 2000-2009 Miranda IM project, Ricardo Pescuma Domenecci, 2013-23 Miranda NG team"
diff --git a/plugins/Clist_modern/src/cluiframes.cpp b/plugins/Clist_modern/src/cluiframes.cpp index c2cb8b459c..250c221327 100644 --- a/plugins/Clist_modern/src/cluiframes.cpp +++ b/plugins/Clist_modern/src/cluiframes.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/groupmenu.cpp b/plugins/Clist_modern/src/groupmenu.cpp index 82e2873a2f..c3d6d99d0b 100644 --- a/plugins/Clist_modern/src/groupmenu.cpp +++ b/plugins/Clist_modern/src/groupmenu.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/init.cpp b/plugins/Clist_modern/src/init.cpp index cfddf29267..3026d17ba2 100644 --- a/plugins/Clist_modern/src/init.cpp +++ b/plugins/Clist_modern/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_aniavatars.cpp b/plugins/Clist_modern/src/modern_aniavatars.cpp index 327ed73f7f..babff44fa8 100644 --- a/plugins/Clist_modern/src/modern_aniavatars.cpp +++ b/plugins/Clist_modern/src/modern_aniavatars.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_awaymsg.cpp b/plugins/Clist_modern/src/modern_awaymsg.cpp index 31a9bcabed..cf8897d329 100644 --- a/plugins/Clist_modern/src/modern_awaymsg.cpp +++ b/plugins/Clist_modern/src/modern_awaymsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_awaymsg.h b/plugins/Clist_modern/src/modern_awaymsg.h index e8b3e1c3cb..d5c0cf6337 100644 --- a/plugins/Clist_modern/src/modern_awaymsg.h +++ b/plugins/Clist_modern/src/modern_awaymsg.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_cache_funcs.h b/plugins/Clist_modern/src/modern_cache_funcs.h index 5adc1db25b..03f8d5ab7d 100644 --- a/plugins/Clist_modern/src/modern_cache_funcs.h +++ b/plugins/Clist_modern/src/modern_cache_funcs.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_cachefuncs.cpp b/plugins/Clist_modern/src/modern_cachefuncs.cpp index cb977bea6e..1fff523f92 100644 --- a/plugins/Clist_modern/src/modern_cachefuncs.cpp +++ b/plugins/Clist_modern/src/modern_cachefuncs.cpp @@ -1,745 +1,745 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -Created by Pescuma -Modified by FYR -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// Module for working with lines text and avatars - -#include "stdafx.h" -#include "modern_sync.h" - -typedef BOOL(*ExecuteOnAllContactsFuncPtr) (ClcContact *contact, BOOL subcontact, void *param); - -///////////////////////////////////////////////////////////////////////////////////////// -// Module static declarations - -static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size); - -static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param); -static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param); - -///////////////////////////////////////////////////////////////////////////////////////// -// Get time zone for contact -// -void Cache_GetTimezone(ClcData *dat, MCONTACT hContact) -{ - ClcCacheEntry *pdnce = Clist_GetCacheEntry(hContact); - if (dat == nullptr && g_clistApi.hwndContactTree) - dat = (ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0); - - if (dat && dat->hWnd == g_clistApi.hwndContactTree) { - uint32_t flags = dat->contact_time_show_only_if_different ? TZF_DIFONLY : 0; - pdnce->hTimeZone = TimeZone_CreateByContact(hContact, nullptr, flags); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Get all lines of text -// -void Cache_GetText(ClcData *dat, ClcContact *contact) -{ - Cache_GetFirstLineText(dat, contact); - - if (!dat->bForceInDialog) { - if (g_plugin.secondLine.bActive) - Cache_GetNthLineText(dat, contact->pce, 2); - if (g_plugin.thirdLine.bActive) - Cache_GetNthLineText(dat, contact->pce, 3); - } -} - -void CSmileyString::AddListeningToIcon(ClcData *dat, wchar_t *szText) -{ - iMaxSmileyHeight = 0; - DestroySmileyList(); - - if (szText == nullptr) - return; - - int text_size = (int)mir_wstrlen(szText); - - plText = List_Create(0, 1); - - // Add Icon - { - ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece)); - piece->type = TEXT_PIECE_TYPE_SMILEY; - piece->len = 0; - piece->smiley = g_hListeningToIcon; - piece->smiley_width = 16; - piece->smiley_height = 16; - - ICONINFO icon; - if (GetIconInfo(piece->smiley, &icon)) { - BITMAP bm; - if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) { - piece->smiley_width = bm.bmWidth; - piece->smiley_height = bm.bmHeight; - } - - DeleteObject(icon.hbmMask); - DeleteObject(icon.hbmColor); - } - - dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height); - iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight); - - List_Insert(plText, piece, plText->realCount); - } - - // Add text - { - ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece)); - piece->type = TEXT_PIECE_TYPE_TEXT; - piece->start_pos = 0; - piece->len = text_size; - List_Insert(plText, piece, plText->realCount); - } -} - -void CSmileyString::_CopySmileyList(SortedList *plInput) -{ - if (!plInput || plInput->realCount == 0) - return; - - plText = List_Create(0, 1); - for (int i = 0; i < plInput->realCount; i++) { - ClcContactTextPiece *pieceFrom = (ClcContactTextPiece *)plInput->items[i]; - if (pieceFrom != nullptr) { - ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece)); - *piece = *pieceFrom; - if (pieceFrom->type == TEXT_PIECE_TYPE_SMILEY) - piece->smiley = CopyIcon(pieceFrom->smiley); - List_Insert(plText, piece, plText->realCount); - } - } -} - -void CSmileyString::DestroySmileyList() -{ - if (plText == nullptr) - return; - - if (IsBadReadPtr(plText, sizeof(SortedList))) { - plText = nullptr; - return; - } - - if (plText->realCount != 0) { - for (int i = 0; i < plText->realCount; i++) { - if (plText->items[i] != nullptr) { - ClcContactTextPiece *piece = (ClcContactTextPiece *)plText->items[i]; - - if (!IsBadWritePtr(piece, sizeof(ClcContactTextPiece))) { - if (piece->type == TEXT_PIECE_TYPE_SMILEY && piece->smiley != g_hListeningToIcon) - DestroyIcon_protect(piece->smiley); - mir_free(piece); - } - } - } - List_Destroy(plText); - } - mir_free(plText); - - plText = nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Parsing of text for smiley -// -void CSmileyString::ReplaceSmileys(ClcData *dat, ClcCacheEntry *pdnce, wchar_t *szText, BOOL replace_smileys) -{ - int last_pos = 0; - iMaxSmileyHeight = 0; - - DestroySmileyList(); - - if (!dat->text_replace_smileys || !replace_smileys || szText == nullptr) - return; - - int text_size = (int)mir_wstrlen(szText); - - // Call service for the first time to see if needs to be used... - SMADD_BATCHPARSE2 sp = {}; - sp.cbSize = sizeof(sp); - sp.hContact = pdnce->hContact; - - if (dat->text_use_protocol_smileys) { - sp.Protocolname = pdnce->szProto; - - if (db_get_b(0, "CLC", "Meta", SETTING_USEMETAICON_DEFAULT) != 1 && pdnce->szProto != nullptr && mir_strcmp(pdnce->szProto, META_PROTO) == 0) { - MCONTACT hContact = db_mc_getMostOnline(pdnce->hContact); - if (hContact != 0) - sp.Protocolname = Proto_GetBaseAccountName(hContact); - } - } - else sp.Protocolname = "clist"; - - sp.str = szText; - sp.flag = SAFL_TCHAR; - - SMADD_BATCHPARSERES *spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp); - - // Did not find a simley - if (spr == nullptr || (INT_PTR)spr == CALLSERVICE_NOTFOUND) - return; - - // Lets add smileys - plText = List_Create(0, 1); - - for (unsigned i = 0; i < sp.numSmileys; ++i) { - if (spr[i].hIcon != nullptr) { // For deffective smileypacks - // Add text - if (spr[i].startChar - last_pos > 0) { - ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece)); - - piece->type = TEXT_PIECE_TYPE_TEXT; - piece->start_pos = last_pos;//sp.str - text; - piece->len = spr[i].startChar - last_pos; - List_Insert(plText, piece, plText->realCount); - } - - // Add smiley - { - BITMAP bm; - ICONINFO icon; - ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece)); - - piece->type = TEXT_PIECE_TYPE_SMILEY; - piece->len = spr[i].size; - piece->smiley = spr[i].hIcon; - - piece->smiley_width = 16; - piece->smiley_height = 16; - if (GetIconInfo(piece->smiley, &icon)) { - if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) { - piece->smiley_width = bm.bmWidth; - piece->smiley_height = bm.bmHeight; - } - - DeleteObject(icon.hbmMask); - DeleteObject(icon.hbmColor); - } - - dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height); - iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight); - - List_Insert(plText, piece, plText->realCount); - } - } - // Get next - last_pos = spr[i].startChar + spr[i].size; - } - CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr); - - // Add rest of text - if (last_pos < text_size) { - ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece)); - - piece->type = TEXT_PIECE_TYPE_TEXT; - piece->start_pos = last_pos; - piece->len = text_size - last_pos; - - List_Insert(plText, piece, plText->realCount); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Getting Status name -// returns -1 for XStatus, 1 for Status -// -int GetStatusName(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority) -{ - BOOL noAwayMsg = FALSE; - BOOL noXstatus = FALSE; - // Hide status text if Offline /// no offline - uint16_t nStatus = pdnce->getStatus(); - if ((nStatus == ID_STATUS_OFFLINE || nStatus == 0) && g_CluiData.bRemoveAwayMessageForOffline) noAwayMsg = TRUE; - if (nStatus == ID_STATUS_OFFLINE || nStatus == 0) noXstatus = TRUE; - text[0] = '\0'; - // Get XStatusName - if (!noAwayMsg && !noXstatus && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) { - DBVARIANT dbv = { 0 }; - if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) { - CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1); - db_free(&dbv); - - if (text[0] != '\0') - return -1; - } - } - - // Get Status name - wchar_t *tmp = Clist_GetStatusModeDescription(nStatus, 0); - if (tmp && *tmp) { - mir_wstrncpy(text, tmp, text_size); - return 1; - } - - // Get XStatusName - if (!noAwayMsg && !noXstatus && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto) { - DBVARIANT dbv = { 0 }; - if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) { - CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1); - db_free(&dbv); - - if (text[0] != '\0') - return -1; - } - } - - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Get Listening to information -// -void GetListeningTo(wchar_t *text, int text_size, ClcCacheEntry *pdnce) -{ - *text = '\0'; - - if (pdnce->m_iStatus == ID_STATUS_OFFLINE || pdnce->m_iStatus == 0) - return; - - ptrW tszValue(db_get_wsa(pdnce->hContact, pdnce->szProto, "ListeningTo")); - if (tszValue != nullptr) - CopySkipUnprintableChars(text, tszValue, text_size - 1); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Getting Status message(Away message) -// returns -1 for XStatus, 1 for Status -// -int GetStatusMessage(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority) -{ - BOOL noAwayMsg = FALSE; - uint16_t wStatus = pdnce->getStatus(); - *text = '\0'; - - // Hide status text if Offline /// no offline - if (wStatus == ID_STATUS_OFFLINE || wStatus == 0) - noAwayMsg = TRUE; - - // Get XStatusMsg - if (!noAwayMsg && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) { - ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg")); - if (tszXStatusMsg != nullptr) { - CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1); - if (text[0] != '\0') - return -1; - } - } - - // Get StatusMsg - if (pdnce->hContact && text[0] == '\0') { - if (noAwayMsg && ServiceExists(MS_LASTSEEN_GET)) { - ptrW pwszLastSeen((LPWSTR)CallService(MS_LASTSEEN_GET, (WPARAM)pdnce->hContact)); - if (pwszLastSeen) { - CMStringW wszLastSeen(FORMAT, L"%s: %s", TranslateT("Last seen"), pwszLastSeen); - CopySkipUnprintableChars(text, (wchar_t*)wszLastSeen.c_str(), text_size - 1); - if (text[0] != '\0') - return 1; - } - } - - ptrW tszStatusMsg(g_plugin.getWStringA(pdnce->hContact, "StatusMsg")); - if (tszStatusMsg != nullptr) { - CopySkipUnprintableChars(text, tszStatusMsg, text_size - 1); - if (text[0] != '\0') - return 1; - } - - } - - // Get XStatusMsg - if (!noAwayMsg && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto && text[0] == '\0') { - // Try to get XStatusMsg - ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg")); - if (tszXStatusMsg != nullptr) { - CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1); - if (text[0] != '\0') - return -1; - } - } - - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Get the text for specified lines - -int Cache_GetLineText(ClcCacheEntry *pdnce, int type, LPTSTR text, int text_size, ClcLineInfo &line) -{ - if (text == nullptr) - return TEXT_EMPTY; - text[0] = '\0'; - - switch (type) { - case TEXT_STATUS: -LBL_Status: - if (GetStatusName(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) { - // Try to get XStatusMsg - ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg")); - if (tszXStatusMsg != nullptr && tszXStatusMsg[0] != 0) { - wchar_t *tmp = NEWWSTR_ALLOCA(text); - mir_snwprintf(text, text_size, L"%s: %s", tmp, tszXStatusMsg.get()); - CopySkipUnprintableChars(text, text, text_size - 1); - } - } - return TEXT_STATUS; - - case TEXT_NICKNAME: - if (pdnce->hContact && pdnce->szProto) { - ptrW tszNick(db_get_wsa(pdnce->hContact, pdnce->szProto, "Nick")); - if (tszNick != nullptr) { - mir_wstrncpy(text, tszNick, text_size); - CopySkipUnprintableChars(text, text, text_size - 1); - } - } - return TEXT_NICKNAME; - - case TEXT_STATUS_MESSAGE: - if (GetStatusMessage(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) { - // Try to get XStatusName - ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName")); - if (tszXStatusName != nullptr && tszXStatusName[0] != 0) { - wchar_t *tmp = NEWWSTR_ALLOCA(text); - mir_snwprintf(text, text_size, L"%s: %s", tszXStatusName.get(), tmp); - CopySkipUnprintableChars(text, text, text_size - 1); - } - } - else if (line.bUseNameAndMessageForXstatus && line.bXstatusHasPriority) { - // Try to get XStatusName - ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName")); - if (tszXStatusName != nullptr && tszXStatusName[0] != 0) { - mir_wstrncpy(text, tszXStatusName, text_size); - CopySkipUnprintableChars(text, text, text_size - 1); - } - } - - if (text[0] == '\0') { - if (line.bShowListeningIfNoAway) { - GetListeningTo(text, text_size, pdnce); - if (text[0] != '\0') - return TEXT_LISTENING_TO; - } - - if (line.bShowStatusIfNoAway) // re-request status if no away - goto LBL_Status; - } - return TEXT_STATUS_MESSAGE; - - case TEXT_LISTENING_TO: - GetListeningTo(text, text_size, pdnce); - return TEXT_LISTENING_TO; - - case TEXT_TEXT: - { - ptrW tmp(variables_parsedup(line.text, pdnce->tszName, pdnce->hContact)); - mir_wstrncpy(text, tmp, text_size); - CopySkipUnprintableChars(text, text, text_size - 1); - } - return TEXT_TEXT; - - case TEXT_CONTACT_TIME: - if (pdnce->hTimeZone) { - // Get pdnce time - text[0] = 0; - TimeZone_PrintDateTime(pdnce->hTimeZone, L"t", text, text_size, 0); - } - return TEXT_CONTACT_TIME; - } - - return TEXT_EMPTY; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Get the text for First Line - -void Cache_GetFirstLineText(ClcData *dat, ClcContact *contact) -{ - if (GetCurrentThreadId() != g_dwMainThreadID) - return; - - ClcCacheEntry *pdnce = contact->pce; - wchar_t *name = Clist_GetContactDisplayName(contact->hContact); - if (dat->first_line_append_nick && !dat->bForceInDialog) { - DBVARIANT dbv = { 0 }; - if (!db_get_ws(pdnce->hContact, pdnce->szProto, "Nick", &dbv)) { - wchar_t nick[_countof(contact->szText)]; - wcsncpy_s(nick, dbv.pwszVal, _TRUNCATE); - db_free(&dbv); - - // They are the same -> use the name to keep the case - if (mir_wstrcmpi(name, nick) == 0) - wcsncpy_s(contact->szText, name, _TRUNCATE); - else // Append then - mir_snwprintf(contact->szText, L"%s - %s", name, nick); - } - else wcsncpy_s(contact->szText, name, _TRUNCATE); - } - else wcsncpy_s(contact->szText, name, _TRUNCATE); - - if (!dat->bForceInDialog) - contact->ssText.ReplaceSmileys(dat, pdnce, contact->szText, dat->first_line_draw_smileys); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Get the text for Second Line - -void Cache_GetNthLineText(ClcData *dat, ClcCacheEntry *pdnce, int n) -{ - if (pdnce == nullptr) - return; - - wchar_t Text[240 - EXTRA_ICON_COUNT]; Text[0] = 0; - ClcLineInfo &line = (n == 2) ? g_plugin.secondLine : g_plugin.thirdLine; - wchar_t* &szText = (n == 2) ? pdnce->szSecondLineText : pdnce->szThirdLineText; - - // in most cases replaceStrW does nothing - if (!line.bActive) { - replaceStrW(szText, nullptr); - return; - } - - int type = Cache_GetLineText(pdnce, line.iType, Text, _countof(Text), line); - if (Text[0] == 0) { - replaceStrW(szText, nullptr); - return; - } - - Text[_countof(Text) - 1] = 0; //to be sure that it is null terminated string - replaceStrW(szText, Text); - - CSmileyString &ss = (n == 2) ? pdnce->ssSecondLine : pdnce->ssThirdLine; - if (type == TEXT_LISTENING_TO && szText[0] != '\0') - ss.AddListeningToIcon(dat, szText); - else - ss.ReplaceSmileys(dat, pdnce, szText, line.bDrawSmilies); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void RemoveTag(wchar_t *to, wchar_t *tag) -{ - wchar_t *st = to; - int len = (int)mir_wstrlen(tag); - int lastsize = (int)mir_wstrlen(to) + 1; - while (st = wcsstr(st, tag)) { - lastsize -= len; - memmove((void*)st, (void*)(st + len), (lastsize)*sizeof(wchar_t)); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Copy string with removing Escape chars from text and BBcodes - -static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size) -{ - uint32_t i; - BOOL keep = 0; - wchar_t * cp = to; - if (!to) return 0; - if (!buf) { - to[0] = '\0'; - return 0; - } - - for (i = 0; i < size; i++) { - if (buf[i] == 0) break; - if (buf[i] > 0 && buf[i] < ' ') { - *cp = ' '; - if (!keep) cp++; - keep = 1; - } - else { - keep = 0; - *cp = buf[i]; - cp++; - } - } - *cp = 0; - - //remove bbcodes: [b] [i] [u] <b> <i> <u> - RemoveTag(to, L"[b]"); RemoveTag(to, L"[/b]"); - RemoveTag(to, L"[u]"); RemoveTag(to, L"[/u]"); - RemoveTag(to, L"[i]"); RemoveTag(to, L"[/i]"); - - RemoveTag(to, L"<b>"); RemoveTag(to, L"</b>"); - RemoveTag(to, L"<u>"); RemoveTag(to, L"</u>"); - RemoveTag(to, L"<i>"); RemoveTag(to, L"</i>"); - - RemoveTag(to, L"[B]"); RemoveTag(to, L"[/b]"); - RemoveTag(to, L"[U]"); RemoveTag(to, L"[/u]"); - RemoveTag(to, L"[I]"); RemoveTag(to, L"[/i]"); - - RemoveTag(to, L"<B>"); RemoveTag(to, L"</B>"); - RemoveTag(to, L"<U>"); RemoveTag(to, L"</U>"); - RemoveTag(to, L"<I>"); RemoveTag(to, L"</I>"); - return i; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// If ExecuteOnAllContactsFuncPtr returns FALSE, stop loop -// Return TRUE if finished, FALSE if was stoped -// -static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param) -{ - return ExecuteOnAllContactsOfGroup(&dat->list, func, param); -} - -static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param) -{ - if (!group) - return TRUE; - - for (auto &it : group->cl) { - if (it->type == CLCIT_CONTACT) { - if (!func(it, FALSE, param)) - return FALSE; - - if (it->iSubAllocated > 0) { - for (int i = 0; i < it->iSubAllocated; i++) - if (!func(&it->subcontacts[i], TRUE, param)) - return FALSE; - } - } - else if (it->type == CLCIT_GROUP) - if (!ExecuteOnAllContactsOfGroup(it->group, func, param)) - return FALSE; - } - - return TRUE; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Avatar working routines -// -BOOL UpdateAllAvatarsProxy(ClcContact *contact, BOOL, void *param) -{ - Cache_GetAvatar((ClcData *)param, contact); - return TRUE; -} - -void UpdateAllAvatars(ClcData *dat) -{ - ExecuteOnAllContacts(dat, UpdateAllAvatarsProxy, dat); -} - -BOOL ReduceAvatarPosition(ClcContact *contact, BOOL, void *param) -{ - if (contact->avatar_pos >= *((int *)param)) - contact->avatar_pos--; - - return TRUE; -} - -void Cache_ProceedAvatarInList(ClcData *dat, ClcContact *contact) -{ - AVATARCACHEENTRY *ace = contact->avatar_data; - int old_pos = contact->avatar_pos; - - if (ace == nullptr || ace->dwFlags == AVS_BITMAP_EXPIRED || ace->hbmPic == nullptr) { - // Avatar was not ready or removed - need to remove it from cache - if (old_pos >= 0) { - ImageArray_RemoveImage(&dat->avatar_cache, old_pos); - - // Update all items - ExecuteOnAllContacts(dat, ReduceAvatarPosition, (void *)&old_pos); - contact->avatar_pos = AVATAR_POS_DONT_HAVE; - return; - } - } - else if (contact->avatar_data->hbmPic != nullptr) { // let's add it - // Clipping width and height - LONG width_clip = dat->avatars_maxwidth_size ? dat->avatars_maxwidth_size : dat->avatars_maxheight_size; - LONG height_clip = dat->avatars_maxheight_size; - - if (height_clip * ace->bmWidth / ace->bmHeight <= width_clip) - width_clip = height_clip * ace->bmWidth / ace->bmHeight; - else - height_clip = width_clip * ace->bmHeight / ace->bmWidth; - - if (wildcmpiw(contact->avatar_data->szFilename, L"*.gif")) { - if (old_pos == AVATAR_POS_ANIMATED) - AniAva_RemoveAvatar(contact->hContact); - - int res = AniAva_AddAvatar(contact->hContact, contact->avatar_data->szFilename, width_clip, height_clip); - if (res) { - contact->avatar_pos = AVATAR_POS_ANIMATED; - contact->avatar_size.cy = HIWORD(res); - contact->avatar_size.cx = LOWORD(res); - return; - } - } - - // Create objs - HDC hdc = CreateCompatibleDC(dat->avatar_cache.hdc); - - void *pt; - HBITMAP hDrawBmp = ske_CreateDIB32Point(width_clip, height_clip, &pt); - HBITMAP oldBmp = (HBITMAP)SelectObject(hdc, hDrawBmp); - - // need to draw avatar bitmap here - DrawAvatarImageWithGDIp(hdc, 0, 0, width_clip, height_clip, ace->hbmPic, 0, 0, ace->bmWidth, ace->bmHeight, ace->dwFlags, 255); - SelectObject(hdc, oldBmp); - DeleteDC(hdc); - - // Add to list - if (old_pos >= 0) { - ImageArray_ChangeImage(&dat->avatar_cache, hDrawBmp, old_pos); - contact->avatar_pos = old_pos; - } - else contact->avatar_pos = ImageArray_AddImage(&dat->avatar_cache, hDrawBmp, -1); - - if (old_pos == AVATAR_POS_ANIMATED && contact->avatar_pos != AVATAR_POS_ANIMATED) - AniAva_RemoveAvatar(contact->hContact); - - DeleteObject(hDrawBmp); - } -} - -void Cache_GetAvatar(ClcData *dat, ClcContact *contact) -{ - // workaround for avatar service - if (g_CluiData.bSTATE != STATE_NORMAL) { - contact->avatar_pos = AVATAR_POS_DONT_HAVE; - contact->avatar_data = nullptr; - return; - } - - if (dat->avatars_show && !g_plugin.getByte(contact->hContact, "HideContactAvatar", 0)) { - contact->avatar_data = (AVATARCACHEENTRY*)CallService(MS_AV_GETAVATARBITMAP, contact->hContact, 0); - if (contact->avatar_data == nullptr || contact->avatar_data->dwFlags == AVS_BITMAP_EXPIRED) - contact->avatar_data = nullptr; - - if (contact->avatar_data != nullptr) - contact->avatar_data->t_lastAccess = (uint32_t)time(0); - } - else contact->avatar_data = nullptr; - - Cache_ProceedAvatarInList(dat, contact); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Created by Pescuma
+Modified by FYR
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module for working with lines text and avatars
+
+#include "stdafx.h"
+#include "modern_sync.h"
+
+typedef BOOL(*ExecuteOnAllContactsFuncPtr) (ClcContact *contact, BOOL subcontact, void *param);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module static declarations
+
+static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size);
+
+static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param);
+static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get time zone for contact
+//
+void Cache_GetTimezone(ClcData *dat, MCONTACT hContact)
+{
+ ClcCacheEntry *pdnce = Clist_GetCacheEntry(hContact);
+ if (dat == nullptr && g_clistApi.hwndContactTree)
+ dat = (ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
+
+ if (dat && dat->hWnd == g_clistApi.hwndContactTree) {
+ uint32_t flags = dat->contact_time_show_only_if_different ? TZF_DIFONLY : 0;
+ pdnce->hTimeZone = TimeZone_CreateByContact(hContact, nullptr, flags);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get all lines of text
+//
+void Cache_GetText(ClcData *dat, ClcContact *contact)
+{
+ Cache_GetFirstLineText(dat, contact);
+
+ if (!dat->bForceInDialog) {
+ if (g_plugin.secondLine.bActive)
+ Cache_GetNthLineText(dat, contact->pce, 2);
+ if (g_plugin.thirdLine.bActive)
+ Cache_GetNthLineText(dat, contact->pce, 3);
+ }
+}
+
+void CSmileyString::AddListeningToIcon(ClcData *dat, wchar_t *szText)
+{
+ iMaxSmileyHeight = 0;
+ DestroySmileyList();
+
+ if (szText == nullptr)
+ return;
+
+ int text_size = (int)mir_wstrlen(szText);
+
+ plText = List_Create(0, 1);
+
+ // Add Icon
+ {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+ piece->type = TEXT_PIECE_TYPE_SMILEY;
+ piece->len = 0;
+ piece->smiley = g_hListeningToIcon;
+ piece->smiley_width = 16;
+ piece->smiley_height = 16;
+
+ ICONINFO icon;
+ if (GetIconInfo(piece->smiley, &icon)) {
+ BITMAP bm;
+ if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) {
+ piece->smiley_width = bm.bmWidth;
+ piece->smiley_height = bm.bmHeight;
+ }
+
+ DeleteObject(icon.hbmMask);
+ DeleteObject(icon.hbmColor);
+ }
+
+ dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height);
+ iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight);
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+
+ // Add text
+ {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = 0;
+ piece->len = text_size;
+ List_Insert(plText, piece, plText->realCount);
+ }
+}
+
+void CSmileyString::_CopySmileyList(SortedList *plInput)
+{
+ if (!plInput || plInput->realCount == 0)
+ return;
+
+ plText = List_Create(0, 1);
+ for (int i = 0; i < plInput->realCount; i++) {
+ ClcContactTextPiece *pieceFrom = (ClcContactTextPiece *)plInput->items[i];
+ if (pieceFrom != nullptr) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+ *piece = *pieceFrom;
+ if (pieceFrom->type == TEXT_PIECE_TYPE_SMILEY)
+ piece->smiley = CopyIcon(pieceFrom->smiley);
+ List_Insert(plText, piece, plText->realCount);
+ }
+ }
+}
+
+void CSmileyString::DestroySmileyList()
+{
+ if (plText == nullptr)
+ return;
+
+ if (IsBadReadPtr(plText, sizeof(SortedList))) {
+ plText = nullptr;
+ return;
+ }
+
+ if (plText->realCount != 0) {
+ for (int i = 0; i < plText->realCount; i++) {
+ if (plText->items[i] != nullptr) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)plText->items[i];
+
+ if (!IsBadWritePtr(piece, sizeof(ClcContactTextPiece))) {
+ if (piece->type == TEXT_PIECE_TYPE_SMILEY && piece->smiley != g_hListeningToIcon)
+ DestroyIcon_protect(piece->smiley);
+ mir_free(piece);
+ }
+ }
+ }
+ List_Destroy(plText);
+ }
+ mir_free(plText);
+
+ plText = nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Parsing of text for smiley
+//
+void CSmileyString::ReplaceSmileys(ClcData *dat, ClcCacheEntry *pdnce, wchar_t *szText, BOOL replace_smileys)
+{
+ int last_pos = 0;
+ iMaxSmileyHeight = 0;
+
+ DestroySmileyList();
+
+ if (!dat->text_replace_smileys || !replace_smileys || szText == nullptr)
+ return;
+
+ int text_size = (int)mir_wstrlen(szText);
+
+ // Call service for the first time to see if needs to be used...
+ SMADD_BATCHPARSE2 sp = {};
+ sp.cbSize = sizeof(sp);
+ sp.hContact = pdnce->hContact;
+
+ if (dat->text_use_protocol_smileys) {
+ sp.Protocolname = pdnce->szProto;
+
+ if (db_get_b(0, "CLC", "Meta", SETTING_USEMETAICON_DEFAULT) != 1 && pdnce->szProto != nullptr && mir_strcmp(pdnce->szProto, META_PROTO) == 0) {
+ MCONTACT hContact = db_mc_getMostOnline(pdnce->hContact);
+ if (hContact != 0)
+ sp.Protocolname = Proto_GetBaseAccountName(hContact);
+ }
+ }
+ else sp.Protocolname = "clist";
+
+ sp.str = szText;
+ sp.flag = SAFL_TCHAR;
+
+ SMADD_BATCHPARSERES *spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp);
+
+ // Did not find a simley
+ if (spr == nullptr || (INT_PTR)spr == CALLSERVICE_NOTFOUND)
+ return;
+
+ // Lets add smileys
+ plText = List_Create(0, 1);
+
+ for (unsigned i = 0; i < sp.numSmileys; ++i) {
+ if (spr[i].hIcon != nullptr) { // For deffective smileypacks
+ // Add text
+ if (spr[i].startChar - last_pos > 0) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = last_pos;//sp.str - text;
+ piece->len = spr[i].startChar - last_pos;
+ List_Insert(plText, piece, plText->realCount);
+ }
+
+ // Add smiley
+ {
+ BITMAP bm;
+ ICONINFO icon;
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_SMILEY;
+ piece->len = spr[i].size;
+ piece->smiley = spr[i].hIcon;
+
+ piece->smiley_width = 16;
+ piece->smiley_height = 16;
+ if (GetIconInfo(piece->smiley, &icon)) {
+ if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) {
+ piece->smiley_width = bm.bmWidth;
+ piece->smiley_height = bm.bmHeight;
+ }
+
+ DeleteObject(icon.hbmMask);
+ DeleteObject(icon.hbmColor);
+ }
+
+ dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height);
+ iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight);
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+ }
+ // Get next
+ last_pos = spr[i].startChar + spr[i].size;
+ }
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr);
+
+ // Add rest of text
+ if (last_pos < text_size) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = last_pos;
+ piece->len = text_size - last_pos;
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Getting Status name
+// returns -1 for XStatus, 1 for Status
+//
+int GetStatusName(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority)
+{
+ BOOL noAwayMsg = FALSE;
+ BOOL noXstatus = FALSE;
+ // Hide status text if Offline /// no offline
+ uint16_t nStatus = pdnce->getStatus();
+ if ((nStatus == ID_STATUS_OFFLINE || nStatus == 0) && g_CluiData.bRemoveAwayMessageForOffline) noAwayMsg = TRUE;
+ if (nStatus == ID_STATUS_OFFLINE || nStatus == 0) noXstatus = TRUE;
+ text[0] = '\0';
+ // Get XStatusName
+ if (!noAwayMsg && !noXstatus && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) {
+ CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1);
+ db_free(&dbv);
+
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ // Get Status name
+ wchar_t *tmp = Clist_GetStatusModeDescription(nStatus, 0);
+ if (tmp && *tmp) {
+ mir_wstrncpy(text, tmp, text_size);
+ return 1;
+ }
+
+ // Get XStatusName
+ if (!noAwayMsg && !noXstatus && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) {
+ CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1);
+ db_free(&dbv);
+
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get Listening to information
+//
+void GetListeningTo(wchar_t *text, int text_size, ClcCacheEntry *pdnce)
+{
+ *text = '\0';
+
+ if (pdnce->m_iStatus == ID_STATUS_OFFLINE || pdnce->m_iStatus == 0)
+ return;
+
+ ptrW tszValue(db_get_wsa(pdnce->hContact, pdnce->szProto, "ListeningTo"));
+ if (tszValue != nullptr)
+ CopySkipUnprintableChars(text, tszValue, text_size - 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Getting Status message(Away message)
+// returns -1 for XStatus, 1 for Status
+//
+int GetStatusMessage(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority)
+{
+ BOOL noAwayMsg = FALSE;
+ uint16_t wStatus = pdnce->getStatus();
+ *text = '\0';
+
+ // Hide status text if Offline /// no offline
+ if (wStatus == ID_STATUS_OFFLINE || wStatus == 0)
+ noAwayMsg = TRUE;
+
+ // Get XStatusMsg
+ if (!noAwayMsg && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
+ ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
+ if (tszXStatusMsg != nullptr) {
+ CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1);
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ // Get StatusMsg
+ if (pdnce->hContact && text[0] == '\0') {
+ if (noAwayMsg && ServiceExists(MS_LASTSEEN_GET)) {
+ ptrW pwszLastSeen((LPWSTR)CallService(MS_LASTSEEN_GET, (WPARAM)pdnce->hContact));
+ if (pwszLastSeen) {
+ CMStringW wszLastSeen(FORMAT, L"%s: %s", TranslateT("Last seen"), pwszLastSeen);
+ CopySkipUnprintableChars(text, (wchar_t*)wszLastSeen.c_str(), text_size - 1);
+ if (text[0] != '\0')
+ return 1;
+ }
+ }
+
+ ptrW tszStatusMsg(g_plugin.getWStringA(pdnce->hContact, "StatusMsg"));
+ if (tszStatusMsg != nullptr) {
+ CopySkipUnprintableChars(text, tszStatusMsg, text_size - 1);
+ if (text[0] != '\0')
+ return 1;
+ }
+
+ }
+
+ // Get XStatusMsg
+ if (!noAwayMsg && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto && text[0] == '\0') {
+ // Try to get XStatusMsg
+ ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
+ if (tszXStatusMsg != nullptr) {
+ CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1);
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the text for specified lines
+
+int Cache_GetLineText(ClcCacheEntry *pdnce, int type, LPTSTR text, int text_size, ClcLineInfo &line)
+{
+ if (text == nullptr)
+ return TEXT_EMPTY;
+ text[0] = '\0';
+
+ switch (type) {
+ case TEXT_STATUS:
+LBL_Status:
+ if (GetStatusName(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) {
+ // Try to get XStatusMsg
+ ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
+ if (tszXStatusMsg != nullptr && tszXStatusMsg[0] != 0) {
+ wchar_t *tmp = NEWWSTR_ALLOCA(text);
+ mir_snwprintf(text, text_size, L"%s: %s", tmp, tszXStatusMsg.get());
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+ return TEXT_STATUS;
+
+ case TEXT_NICKNAME:
+ if (pdnce->hContact && pdnce->szProto) {
+ ptrW tszNick(db_get_wsa(pdnce->hContact, pdnce->szProto, "Nick"));
+ if (tszNick != nullptr) {
+ mir_wstrncpy(text, tszNick, text_size);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+ return TEXT_NICKNAME;
+
+ case TEXT_STATUS_MESSAGE:
+ if (GetStatusMessage(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) {
+ // Try to get XStatusName
+ ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName"));
+ if (tszXStatusName != nullptr && tszXStatusName[0] != 0) {
+ wchar_t *tmp = NEWWSTR_ALLOCA(text);
+ mir_snwprintf(text, text_size, L"%s: %s", tszXStatusName.get(), tmp);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+ else if (line.bUseNameAndMessageForXstatus && line.bXstatusHasPriority) {
+ // Try to get XStatusName
+ ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName"));
+ if (tszXStatusName != nullptr && tszXStatusName[0] != 0) {
+ mir_wstrncpy(text, tszXStatusName, text_size);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+
+ if (text[0] == '\0') {
+ if (line.bShowListeningIfNoAway) {
+ GetListeningTo(text, text_size, pdnce);
+ if (text[0] != '\0')
+ return TEXT_LISTENING_TO;
+ }
+
+ if (line.bShowStatusIfNoAway) // re-request status if no away
+ goto LBL_Status;
+ }
+ return TEXT_STATUS_MESSAGE;
+
+ case TEXT_LISTENING_TO:
+ GetListeningTo(text, text_size, pdnce);
+ return TEXT_LISTENING_TO;
+
+ case TEXT_TEXT:
+ {
+ ptrW tmp(variables_parsedup(line.text, pdnce->tszName, pdnce->hContact));
+ mir_wstrncpy(text, tmp, text_size);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ return TEXT_TEXT;
+
+ case TEXT_CONTACT_TIME:
+ if (pdnce->hTimeZone) {
+ // Get pdnce time
+ text[0] = 0;
+ TimeZone_PrintDateTime(pdnce->hTimeZone, L"t", text, text_size, 0);
+ }
+ return TEXT_CONTACT_TIME;
+ }
+
+ return TEXT_EMPTY;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the text for First Line
+
+void Cache_GetFirstLineText(ClcData *dat, ClcContact *contact)
+{
+ if (GetCurrentThreadId() != g_dwMainThreadID)
+ return;
+
+ ClcCacheEntry *pdnce = contact->pce;
+ wchar_t *name = Clist_GetContactDisplayName(contact->hContact);
+ if (dat->first_line_append_nick && !dat->bForceInDialog) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(pdnce->hContact, pdnce->szProto, "Nick", &dbv)) {
+ wchar_t nick[_countof(contact->szText)];
+ wcsncpy_s(nick, dbv.pwszVal, _TRUNCATE);
+ db_free(&dbv);
+
+ // They are the same -> use the name to keep the case
+ if (mir_wstrcmpi(name, nick) == 0)
+ wcsncpy_s(contact->szText, name, _TRUNCATE);
+ else // Append then
+ mir_snwprintf(contact->szText, L"%s - %s", name, nick);
+ }
+ else wcsncpy_s(contact->szText, name, _TRUNCATE);
+ }
+ else wcsncpy_s(contact->szText, name, _TRUNCATE);
+
+ if (!dat->bForceInDialog)
+ contact->ssText.ReplaceSmileys(dat, pdnce, contact->szText, dat->first_line_draw_smileys);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the text for Second Line
+
+void Cache_GetNthLineText(ClcData *dat, ClcCacheEntry *pdnce, int n)
+{
+ if (pdnce == nullptr)
+ return;
+
+ wchar_t Text[240 - EXTRA_ICON_COUNT]; Text[0] = 0;
+ ClcLineInfo &line = (n == 2) ? g_plugin.secondLine : g_plugin.thirdLine;
+ wchar_t* &szText = (n == 2) ? pdnce->szSecondLineText : pdnce->szThirdLineText;
+
+ // in most cases replaceStrW does nothing
+ if (!line.bActive) {
+ replaceStrW(szText, nullptr);
+ return;
+ }
+
+ int type = Cache_GetLineText(pdnce, line.iType, Text, _countof(Text), line);
+ if (Text[0] == 0) {
+ replaceStrW(szText, nullptr);
+ return;
+ }
+
+ Text[_countof(Text) - 1] = 0; //to be sure that it is null terminated string
+ replaceStrW(szText, Text);
+
+ CSmileyString &ss = (n == 2) ? pdnce->ssSecondLine : pdnce->ssThirdLine;
+ if (type == TEXT_LISTENING_TO && szText[0] != '\0')
+ ss.AddListeningToIcon(dat, szText);
+ else
+ ss.ReplaceSmileys(dat, pdnce, szText, line.bDrawSmilies);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void RemoveTag(wchar_t *to, wchar_t *tag)
+{
+ wchar_t *st = to;
+ int len = (int)mir_wstrlen(tag);
+ int lastsize = (int)mir_wstrlen(to) + 1;
+ while (st = wcsstr(st, tag)) {
+ lastsize -= len;
+ memmove((void*)st, (void*)(st + len), (lastsize)*sizeof(wchar_t));
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copy string with removing Escape chars from text and BBcodes
+
+static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size)
+{
+ uint32_t i;
+ BOOL keep = 0;
+ wchar_t * cp = to;
+ if (!to) return 0;
+ if (!buf) {
+ to[0] = '\0';
+ return 0;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (buf[i] == 0) break;
+ if (buf[i] > 0 && buf[i] < ' ') {
+ *cp = ' ';
+ if (!keep) cp++;
+ keep = 1;
+ }
+ else {
+ keep = 0;
+ *cp = buf[i];
+ cp++;
+ }
+ }
+ *cp = 0;
+
+ //remove bbcodes: [b] [i] [u] <b> <i> <u>
+ RemoveTag(to, L"[b]"); RemoveTag(to, L"[/b]");
+ RemoveTag(to, L"[u]"); RemoveTag(to, L"[/u]");
+ RemoveTag(to, L"[i]"); RemoveTag(to, L"[/i]");
+
+ RemoveTag(to, L"<b>"); RemoveTag(to, L"</b>");
+ RemoveTag(to, L"<u>"); RemoveTag(to, L"</u>");
+ RemoveTag(to, L"<i>"); RemoveTag(to, L"</i>");
+
+ RemoveTag(to, L"[B]"); RemoveTag(to, L"[/b]");
+ RemoveTag(to, L"[U]"); RemoveTag(to, L"[/u]");
+ RemoveTag(to, L"[I]"); RemoveTag(to, L"[/i]");
+
+ RemoveTag(to, L"<B>"); RemoveTag(to, L"</B>");
+ RemoveTag(to, L"<U>"); RemoveTag(to, L"</U>");
+ RemoveTag(to, L"<I>"); RemoveTag(to, L"</I>");
+ return i;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// If ExecuteOnAllContactsFuncPtr returns FALSE, stop loop
+// Return TRUE if finished, FALSE if was stoped
+//
+static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param)
+{
+ return ExecuteOnAllContactsOfGroup(&dat->list, func, param);
+}
+
+static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param)
+{
+ if (!group)
+ return TRUE;
+
+ for (auto &it : group->cl) {
+ if (it->type == CLCIT_CONTACT) {
+ if (!func(it, FALSE, param))
+ return FALSE;
+
+ if (it->iSubAllocated > 0) {
+ for (int i = 0; i < it->iSubAllocated; i++)
+ if (!func(&it->subcontacts[i], TRUE, param))
+ return FALSE;
+ }
+ }
+ else if (it->type == CLCIT_GROUP)
+ if (!ExecuteOnAllContactsOfGroup(it->group, func, param))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Avatar working routines
+//
+BOOL UpdateAllAvatarsProxy(ClcContact *contact, BOOL, void *param)
+{
+ Cache_GetAvatar((ClcData *)param, contact);
+ return TRUE;
+}
+
+void UpdateAllAvatars(ClcData *dat)
+{
+ ExecuteOnAllContacts(dat, UpdateAllAvatarsProxy, dat);
+}
+
+BOOL ReduceAvatarPosition(ClcContact *contact, BOOL, void *param)
+{
+ if (contact->avatar_pos >= *((int *)param))
+ contact->avatar_pos--;
+
+ return TRUE;
+}
+
+void Cache_ProceedAvatarInList(ClcData *dat, ClcContact *contact)
+{
+ AVATARCACHEENTRY *ace = contact->avatar_data;
+ int old_pos = contact->avatar_pos;
+
+ if (ace == nullptr || ace->dwFlags == AVS_BITMAP_EXPIRED || ace->hbmPic == nullptr) {
+ // Avatar was not ready or removed - need to remove it from cache
+ if (old_pos >= 0) {
+ ImageArray_RemoveImage(&dat->avatar_cache, old_pos);
+
+ // Update all items
+ ExecuteOnAllContacts(dat, ReduceAvatarPosition, (void *)&old_pos);
+ contact->avatar_pos = AVATAR_POS_DONT_HAVE;
+ return;
+ }
+ }
+ else if (contact->avatar_data->hbmPic != nullptr) { // let's add it
+ // Clipping width and height
+ LONG width_clip = dat->avatars_maxwidth_size ? dat->avatars_maxwidth_size : dat->avatars_maxheight_size;
+ LONG height_clip = dat->avatars_maxheight_size;
+
+ if (height_clip * ace->bmWidth / ace->bmHeight <= width_clip)
+ width_clip = height_clip * ace->bmWidth / ace->bmHeight;
+ else
+ height_clip = width_clip * ace->bmHeight / ace->bmWidth;
+
+ if (wildcmpiw(contact->avatar_data->szFilename, L"*.gif")) {
+ if (old_pos == AVATAR_POS_ANIMATED)
+ AniAva_RemoveAvatar(contact->hContact);
+
+ int res = AniAva_AddAvatar(contact->hContact, contact->avatar_data->szFilename, width_clip, height_clip);
+ if (res) {
+ contact->avatar_pos = AVATAR_POS_ANIMATED;
+ contact->avatar_size.cy = HIWORD(res);
+ contact->avatar_size.cx = LOWORD(res);
+ return;
+ }
+ }
+
+ // Create objs
+ HDC hdc = CreateCompatibleDC(dat->avatar_cache.hdc);
+
+ void *pt;
+ HBITMAP hDrawBmp = ske_CreateDIB32Point(width_clip, height_clip, &pt);
+ HBITMAP oldBmp = (HBITMAP)SelectObject(hdc, hDrawBmp);
+
+ // need to draw avatar bitmap here
+ DrawAvatarImageWithGDIp(hdc, 0, 0, width_clip, height_clip, ace->hbmPic, 0, 0, ace->bmWidth, ace->bmHeight, ace->dwFlags, 255);
+ SelectObject(hdc, oldBmp);
+ DeleteDC(hdc);
+
+ // Add to list
+ if (old_pos >= 0) {
+ ImageArray_ChangeImage(&dat->avatar_cache, hDrawBmp, old_pos);
+ contact->avatar_pos = old_pos;
+ }
+ else contact->avatar_pos = ImageArray_AddImage(&dat->avatar_cache, hDrawBmp, -1);
+
+ if (old_pos == AVATAR_POS_ANIMATED && contact->avatar_pos != AVATAR_POS_ANIMATED)
+ AniAva_RemoveAvatar(contact->hContact);
+
+ DeleteObject(hDrawBmp);
+ }
+}
+
+void Cache_GetAvatar(ClcData *dat, ClcContact *contact)
+{
+ // workaround for avatar service
+ if (g_CluiData.bSTATE != STATE_NORMAL) {
+ contact->avatar_pos = AVATAR_POS_DONT_HAVE;
+ contact->avatar_data = nullptr;
+ return;
+ }
+
+ if (dat->avatars_show && !g_plugin.getByte(contact->hContact, "HideContactAvatar", 0)) {
+ contact->avatar_data = (AVATARCACHEENTRY*)CallService(MS_AV_GETAVATARBITMAP, contact->hContact, 0);
+ if (contact->avatar_data == nullptr || contact->avatar_data->dwFlags == AVS_BITMAP_EXPIRED)
+ contact->avatar_data = nullptr;
+
+ if (contact->avatar_data != nullptr)
+ contact->avatar_data->t_lastAccess = (uint32_t)time(0);
+ }
+ else contact->avatar_data = nullptr;
+
+ Cache_ProceedAvatarInList(dat, contact);
+}
diff --git a/plugins/Clist_modern/src/modern_clc.cpp b/plugins/Clist_modern/src/modern_clc.cpp index e549d98f88..d89b70b541 100644 --- a/plugins/Clist_modern/src/modern_clc.cpp +++ b/plugins/Clist_modern/src/modern_clc.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clc.h b/plugins/Clist_modern/src/modern_clc.h index 1d0d2328e4..c0956ee8cd 100644 --- a/plugins/Clist_modern/src/modern_clc.h +++ b/plugins/Clist_modern/src/modern_clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcidents.cpp b/plugins/Clist_modern/src/modern_clcidents.cpp index 36ac3c3291..29be4b6442 100644 --- a/plugins/Clist_modern/src/modern_clcidents.cpp +++ b/plugins/Clist_modern/src/modern_clcidents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcitems.cpp b/plugins/Clist_modern/src/modern_clcitems.cpp index 61b6d6eca5..a2472b99d8 100644 --- a/plugins/Clist_modern/src/modern_clcitems.cpp +++ b/plugins/Clist_modern/src/modern_clcitems.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcmsgs.cpp b/plugins/Clist_modern/src/modern_clcmsgs.cpp index 7c64c944ff..663872a25c 100644 --- a/plugins/Clist_modern/src/modern_clcmsgs.cpp +++ b/plugins/Clist_modern/src/modern_clcmsgs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcopts.cpp b/plugins/Clist_modern/src/modern_clcopts.cpp index d5d25caf10..52e16a6d53 100644 --- a/plugins/Clist_modern/src/modern_clcopts.cpp +++ b/plugins/Clist_modern/src/modern_clcopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcpaint.cpp b/plugins/Clist_modern/src/modern_clcpaint.cpp index 1bd6370e1d..aeb4dd1ade 100644 --- a/plugins/Clist_modern/src/modern_clcpaint.cpp +++ b/plugins/Clist_modern/src/modern_clcpaint.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcutils.cpp b/plugins/Clist_modern/src/modern_clcutils.cpp index 1fa39e4c78..29e438a846 100644 --- a/plugins/Clist_modern/src/modern_clcutils.cpp +++ b/plugins/Clist_modern/src/modern_clcutils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clist.h b/plugins/Clist_modern/src/modern_clist.h index 0aa53268d4..79d9ca48cf 100644 --- a/plugins/Clist_modern/src/modern_clist.h +++ b/plugins/Clist_modern/src/modern_clist.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistevents.cpp b/plugins/Clist_modern/src/modern_clistevents.cpp index 264f15f57e..e918bcee65 100644 --- a/plugins/Clist_modern/src/modern_clistevents.cpp +++ b/plugins/Clist_modern/src/modern_clistevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistmenus.cpp b/plugins/Clist_modern/src/modern_clistmenus.cpp index caae8ff8d1..802cad6cd7 100644 --- a/plugins/Clist_modern/src/modern_clistmenus.cpp +++ b/plugins/Clist_modern/src/modern_clistmenus.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistmod.cpp b/plugins/Clist_modern/src/modern_clistmod.cpp index 0f06f872d0..08373b20d4 100644 --- a/plugins/Clist_modern/src/modern_clistmod.cpp +++ b/plugins/Clist_modern/src/modern_clistmod.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistopts.cpp b/plugins/Clist_modern/src/modern_clistopts.cpp index cef88490dd..4862068c16 100644 --- a/plugins/Clist_modern/src/modern_clistopts.cpp +++ b/plugins/Clist_modern/src/modern_clistopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistsettings.cpp b/plugins/Clist_modern/src/modern_clistsettings.cpp index ac35bad94a..c786522386 100644 --- a/plugins/Clist_modern/src/modern_clistsettings.cpp +++ b/plugins/Clist_modern/src/modern_clistsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clisttray.cpp b/plugins/Clist_modern/src/modern_clisttray.cpp index fde9a29921..c79aff2f00 100644 --- a/plugins/Clist_modern/src/modern_clisttray.cpp +++ b/plugins/Clist_modern/src/modern_clisttray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clui.cpp b/plugins/Clist_modern/src/modern_clui.cpp index 7406276759..8eecc76542 100644 --- a/plugins/Clist_modern/src/modern_clui.cpp +++ b/plugins/Clist_modern/src/modern_clui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clui.h b/plugins/Clist_modern/src/modern_clui.h index c27ea5bb95..c6267ccabc 100644 --- a/plugins/Clist_modern/src/modern_clui.h +++ b/plugins/Clist_modern/src/modern_clui.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_cluiservices.cpp b/plugins/Clist_modern/src/modern_cluiservices.cpp index 3aa536ed2b..ba1011b445 100644 --- a/plugins/Clist_modern/src/modern_cluiservices.cpp +++ b/plugins/Clist_modern/src/modern_cluiservices.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_contact.cpp b/plugins/Clist_modern/src/modern_contact.cpp index c39b2504aa..4a8420752a 100644 --- a/plugins/Clist_modern/src/modern_contact.cpp +++ b/plugins/Clist_modern/src/modern_contact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_defsettings.h b/plugins/Clist_modern/src/modern_defsettings.h index 966f33e47c..04bfb6f73b 100644 --- a/plugins/Clist_modern/src/modern_defsettings.h +++ b/plugins/Clist_modern/src/modern_defsettings.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
Copyright 2007 Artem Shpynov
diff --git a/plugins/Clist_modern/src/modern_docking.cpp b/plugins/Clist_modern/src/modern_docking.cpp index 5820132744..ee9a5d9807 100644 --- a/plugins/Clist_modern/src/modern_docking.cpp +++ b/plugins/Clist_modern/src/modern_docking.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_global.cpp b/plugins/Clist_modern/src/modern_global.cpp index fc24f84c09..d5d3c5d765 100644 --- a/plugins/Clist_modern/src/modern_global.cpp +++ b/plugins/Clist_modern/src/modern_global.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_image_array.cpp b/plugins/Clist_modern/src/modern_image_array.cpp index de65d5b9fa..230cbd119a 100644 --- a/plugins/Clist_modern/src/modern_image_array.cpp +++ b/plugins/Clist_modern/src/modern_image_array.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_image_array.h b/plugins/Clist_modern/src/modern_image_array.h index c817883b5a..369535e687 100644 --- a/plugins/Clist_modern/src/modern_image_array.h +++ b/plugins/Clist_modern/src/modern_image_array.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_keyboard.cpp b/plugins/Clist_modern/src/modern_keyboard.cpp index d1c956982d..075b8b9cab 100644 --- a/plugins/Clist_modern/src/modern_keyboard.cpp +++ b/plugins/Clist_modern/src/modern_keyboard.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_rowheight_funcs.cpp b/plugins/Clist_modern/src/modern_rowheight_funcs.cpp index c35cb7fcf7..bd54fbd582 100644 --- a/plugins/Clist_modern/src/modern_rowheight_funcs.cpp +++ b/plugins/Clist_modern/src/modern_rowheight_funcs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_rowheight_funcs.h b/plugins/Clist_modern/src/modern_rowheight_funcs.h index 8699035b3c..ba99a67cf8 100644 --- a/plugins/Clist_modern/src/modern_rowheight_funcs.h +++ b/plugins/Clist_modern/src/modern_rowheight_funcs.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_skinbutton.cpp b/plugins/Clist_modern/src/modern_skinbutton.cpp index aa517a5ef2..f04d68d3b7 100644 --- a/plugins/Clist_modern/src/modern_skinbutton.cpp +++ b/plugins/Clist_modern/src/modern_skinbutton.cpp @@ -1,716 +1,716 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -/* -This file contains code related to new modern free positioned skinned buttons -*/ - -#include "stdafx.h" -#include "modern_clcpaint.h" - -#define MODERNSKINBUTTONCLASS "MirandaModernSkinButtonClass" -BOOL ModernSkinButtonModuleIsLoaded = FALSE; -static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); -int ModernSkinButtonUnloadModule(WPARAM wParam, LPARAM lParam); -HWND SetToolTip(HWND hwnd, wchar_t * tip); - -typedef struct _ModernSkinButtonCtrl -{ - HWND hwnd; - uint8_t down; // button state - uint8_t focus; // has focus (1 or 0) - uint8_t hover; - uint8_t IsSwitcher; - BOOL fCallOnPress; - char * ID; - char * CommandService; - char * StateService; - char * HandleService; - char * ValueDBSection; - char * ValueTypeDef; - int Left, Top, Bottom, Right; - HMENU hMenu; - wchar_t * Hint; - -} ModernSkinButtonCtrl; -typedef struct _HandleServiceParams -{ - HWND hwnd; - uint32_t msg; - WPARAM wParam; - LPARAM lParam; - BOOL handled; -} HandleServiceParams; - -static mir_cs csTips; -static HWND hwndToolTips = nullptr; - -int ModernSkinButtonLoadModule() -{ - WNDCLASSEX wc; - memset(&wc, 0, sizeof(wc)); - wc.cbSize = sizeof(wc); - wc.lpszClassName = _A2W(MODERNSKINBUTTONCLASS); - wc.lpfnWndProc = ModernSkinButtonWndProc; - wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.cbWndExtra = sizeof(ModernSkinButtonCtrl*); - wc.hbrBackground = nullptr; - wc.style = CS_GLOBALCLASS; - RegisterClassEx(&wc); - ModernSkinButtonModuleIsLoaded = TRUE; - return 0; -} - -int ModernSkinButtonUnloadModule(WPARAM, LPARAM) -{ - return 0; -} - -static int ModernSkinButtonPaintWorker(HWND hwnd, HDC whdc) -{ - HDC hdc; - RECT rc; - ModernSkinButtonCtrl* bct = (ModernSkinButtonCtrl *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (!bct) return 0; - if (!IsWindowVisible(hwnd)) return 0; - if (!whdc && !g_CluiData.fLayered) InvalidateRect(hwnd, nullptr, FALSE); - - if (whdc && g_CluiData.fLayered) hdc = whdc; - else { - //sdc = GetWindowDC(GetParent(hwnd)); - hdc = CreateCompatibleDC(nullptr); - } - GetClientRect(hwnd, &rc); - HBITMAP bmp = ske_CreateDIB32(rc.right, rc.bottom); - HBITMAP oldbmp = (HBITMAP)SelectObject(hdc, bmp); - if (!g_CluiData.fLayered) - ske_BltBackImage(bct->hwnd, hdc, nullptr); - { - MODERNMASK Request = {}; - // int res; - //HBRUSH br = CreateSolidBrush(RGB(255,255,255)); - char * Value = nullptr; - { - if (bct->ValueDBSection && bct->ValueTypeDef) { - char * key; - char * section; - uint32_t defval = 0; - char buf[20]; - key = mir_strdup(bct->ValueDBSection); - section = key; - if (bct->ValueTypeDef[0] != 's') - defval = (uint32_t)atol(bct->ValueTypeDef + 1); - do { - if (key[0] == '/') { key[0] = '\0'; key++; break; } - key++; - } while (key[0] != '\0'); - switch (bct->ValueTypeDef[0]) { - case 's': - Value = db_get_sa(0, section, key, bct->ValueTypeDef + 1); - break; - case 'd': - defval = db_get_dw(0, section, key, defval); - Value = mir_strdup(_ltoa(defval, buf, _countof(buf))); - break; - case 'w': - defval = db_get_w(0, section, key, defval); - Value = mir_strdup(_ltoa(defval, buf, _countof(buf))); - break; - case 'b': - defval = db_get_b(0, section, key, defval); - Value = mir_strdup(_ltoa(defval, buf, _countof(buf))); - break; - } - mir_free(section); - } - - } - g_clcPainter.AddParam(&Request, mod_CalcHash("Module"), "MButton", 0); - g_clcPainter.AddParam(&Request, mod_CalcHash("ID"), bct->ID, 0); - g_clcPainter.AddParam(&Request, mod_CalcHash("Down"), bct->down ? "1" : "0", 0); - g_clcPainter.AddParam(&Request, mod_CalcHash("Focused"), bct->focus ? "1" : "0", 0); - g_clcPainter.AddParam(&Request, mod_CalcHash("Hovered"), bct->hover ? "1" : "0", 0); - if (Value) { - g_clcPainter.AddParam(&Request, mod_CalcHash("Value"), Value, 0); - mir_free(Value); - } - SkinDrawGlyphMask(hdc, &rc, &rc, &Request); - SkinSelector_DeleteMask(&Request); - } - - if (!whdc && g_CluiData.fLayered) { - RECT r; - SetRect(&r, bct->Left, bct->Top, bct->Right, bct->Bottom); - ske_DrawImageAt(hdc, &r); - //CallingService to immeadeately update window with new image. - } - if (whdc && !g_CluiData.fLayered) { - RECT r = { 0 }; - GetClientRect(bct->hwnd, &r); - BitBlt(whdc, 0, 0, r.right, r.bottom, hdc, 0, 0, SRCCOPY); - } - SelectObject(hdc, oldbmp); - DeleteObject(bmp); - if (!whdc || !g_CluiData.fLayered) { - SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); - DeleteDC(hdc); - } - // if (sdc) - // ReleaseDC(GetParent(hwnd),sdc); - return 0; -} - -static int ModernSkinButtonToggleDBValue(char * ValueDBSection, char *ValueTypeDef) -{ - if (ValueDBSection && ValueTypeDef) { - char * key; - char * section; - char * val; - char * val2; - char * Value; - long l1 = 0, l2 = 0, curval; - // char buf[20]; - key = mir_strdup(ValueDBSection); - section = key; - do { - if (key[0] == '/') { key[0] = '\0'; key++; break; } - key++; - } while (key[0] != '\0'); - - val = mir_strdup(ValueTypeDef + 1); - val2 = val; - do { - if (val2[0] == '/') { val2[0] = '\0'; val2++; break; } - val2++; - } while (val2[0] != '\0'); - - if (ValueTypeDef[0] != 's') { - l1 = (uint32_t)atol(val); - l2 = (uint32_t)atol(val2); - } - - switch (ValueTypeDef[0]) { - case 's': - Value = db_get_sa(0, section, key); - if (!Value || (Value && !mir_strcmpi(Value, val2))) - Value = mir_strdup(val); - else - Value = mir_strdup(val2); - db_set_s(0, section, key, Value); - mir_free(Value); - break; - - case 'd': - curval = db_get_dw(0, section, key, l2); - curval = (curval == l2) ? l1 : l2; - db_set_dw(0, section, key, (uint32_t)curval); - break; - - case 'w': - curval = db_get_w(0, section, key, l2); - curval = (curval == l2) ? l1 : l2; - db_set_w(0, section, key, (uint16_t)curval); - break; - - case 'b': - curval = db_get_b(0, section, key, l2); - curval = (curval == l2) ? l1 : l2; - db_set_b(0, section, key, (uint8_t)curval); - break; - } - mir_free(section); - mir_free(val); - } - return 0; -} - -static char *_skipblank(char * str) //str will be modified; -{ - char * endstr = str + mir_strlen(str); - while ((*str == ' ' || *str == '\t') && *str != '\0') str++; - while ((*endstr == ' ' || *endstr == '\t') && *endstr != '\0' && endstr < str) endstr--; - if (*endstr != '\0') { - endstr++; - *endstr = '\0'; - } - return str; -} - -static int _CallServiceStrParams(IN char * toParce, OUT int *Return) -{ - int paramCount = 0; - int result = 0; - - char *pszService = mir_strdup(toParce); - if (!pszService) - return 0; - if (mir_strlen(pszService) == 0) { - mir_free(pszService); - return 0; - } - char *param2 = strrchr(pszService, '%'); - if (param2) { - paramCount++; - *param2 = '\0'; param2++; - _skipblank(param2); - if (mir_strlen(param2) == 0) - param2 = nullptr; - } - char *param1 = strrchr(pszService, '%'); - if (param1) { - paramCount++; - *param1 = '\0'; param1++; - _skipblank(param1); - if (mir_strlen(param1) == 0) - param1 = nullptr; - } - if (param1 && *param1 == '\"') { - param1++; - *(param1 + mir_strlen(param1)) = '\0'; - } - else if (param1) { - param1 = (char*)atoi(param1); - } - if (param2 && *param2 == '\"') { - param2++; - *(param2 + mir_strlen(param2)) = '\0'; - } - else if (param2) - param2 = (char*)atoi(param2); - - if (paramCount == 1) { - param1 = param2; - param2 = nullptr; - } - if (!ServiceExists(pszService)) { - result = 0; - } - else { - result = 1; - int ret = CallService(pszService, (WPARAM)param1, (WPARAM)param2); - if (Return) *Return = ret; - } - mir_free(pszService); - return result; -} - - -static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - ModernSkinButtonCtrl* bct = (msg != WM_NCCREATE) ? (ModernSkinButtonCtrl *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA) : nullptr; - if (bct) { - if (bct->HandleService && IsBadStringPtrA(bct->HandleService, 255)) - bct->HandleService = nullptr; - else if (bct->HandleService && ServiceExists(bct->HandleService)) { - HandleServiceParams MSG = {}; - MSG.hwnd = hwndDlg; - MSG.msg = msg; - MSG.wParam = wParam; - MSG.lParam = lParam; - int t = CallService(bct->HandleService, (WPARAM)&MSG, 0); - if (MSG.handled) return t; - } - } - - switch (msg) { - case WM_NCCREATE: - SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - if (((CREATESTRUCT *)lParam)->lpszName) SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName); - return TRUE; - - case WM_DESTROY: - if (bct == nullptr) - break; - - if (hwndToolTips) { - mir_cslock lck(csTips); - TOOLINFO ti = { 0 }; - ti.cbSize = sizeof(ti); - ti.uFlags = TTF_IDISHWND; - ti.hwnd = bct->hwnd; - ti.uId = (UINT_PTR)bct->hwnd; - if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) - SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti); - - if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) { - DestroyWindow(hwndToolTips); - hwndToolTips = nullptr; - } - } - mir_free(bct->ID); - mir_free(bct->CommandService); - mir_free(bct->StateService); - mir_free(bct->HandleService); - mir_free(bct->Hint); - mir_free(bct->ValueDBSection); - mir_free(bct->ValueTypeDef); - mir_free(bct); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; // DONT! fall thru - - case WM_SETCURSOR: - { - HCURSOR hCurs1 = LoadCursor(nullptr, IDC_ARROW); - if (hCurs1) SetCursor(hCurs1); - if (bct) SetToolTip(hwndDlg, bct->Hint); - } - return 1; - - case WM_PRINT: - if (IsWindowVisible(hwndDlg)) - ModernSkinButtonPaintWorker(hwndDlg, (HDC)wParam); - break; - - case WM_PAINT: - if (IsWindowVisible(hwndDlg) && !g_CluiData.fLayered) { - PAINTSTRUCT ps = {}; - BeginPaint(hwndDlg, &ps); - ModernSkinButtonPaintWorker(hwndDlg, (HDC)ps.hdc); - EndPaint(hwndDlg, &ps); - } - return DefWindowProc(hwndDlg, msg, wParam, lParam); - - case WM_CAPTURECHANGED: - if (bct) { - bct->hover = 0; - bct->down = 0; - ModernSkinButtonPaintWorker(bct->hwnd, nullptr); - } - break; - - case WM_MOUSEMOVE: - if (bct) { - if (!bct->hover) { - SetCapture(bct->hwnd); - bct->hover = 1; - ModernSkinButtonPaintWorker(bct->hwnd, nullptr); - } - else { - POINT t = UNPACK_POINT(lParam); - ClientToScreen(bct->hwnd, &t); - if (WindowFromPoint(t) != bct->hwnd) - ReleaseCapture(); - } - } - return 0; - - case WM_LBUTTONDOWN: - if (bct) { - bct->down = 1; - SetForegroundWindow(GetParent(bct->hwnd)); - ModernSkinButtonPaintWorker(bct->hwnd, nullptr); - if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255)) - bct->CommandService = nullptr; - if (bct->fCallOnPress) { - if (bct->CommandService) { - if (!_CallServiceStrParams(bct->CommandService, nullptr) && (bct->ValueDBSection && bct->ValueTypeDef)) - ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef); - } - bct->down = 0; - - ModernSkinButtonPaintWorker(bct->hwnd, nullptr); - } - } - return 0; - - case WM_LBUTTONUP: - if (bct && bct->down) { - ReleaseCapture(); - bct->hover = 0; - bct->down = 0; - ModernSkinButtonPaintWorker(bct->hwnd, nullptr); - if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255)) - bct->CommandService = nullptr; - if (bct->CommandService) - if (_CallServiceStrParams(bct->CommandService, nullptr)) { - } - else if (bct->ValueDBSection && bct->ValueTypeDef) - ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef); - } - } - return DefWindowProc(hwndDlg, msg, wParam, lParam); -} - -HWND SetToolTip(HWND hwnd, wchar_t * tip) -{ - TOOLINFO ti; - if (!tip) return nullptr; - mir_cslock lck(csTips); - if (!hwndToolTips) { - hwndToolTips = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, - WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, - CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, - hwnd, nullptr, g_hMirApp, nullptr); - - SetWindowPos(hwndToolTips, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - } - - memset(&ti, 0, sizeof(ti)); - ti.cbSize = sizeof(ti); - ti.uFlags = TTF_IDISHWND; - ti.hwnd = hwnd; - ti.uId = (UINT_PTR)hwnd; - if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) { - SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti); - } - ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; - ti.uId = (UINT_PTR)hwnd; - ti.lpszText = (wchar_t*)tip; - SendMessage(hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti); - - return hwndToolTips; -} - - - -typedef struct _MButton -{ - HWND hwnd; - uint8_t ConstrainPositionFrom; //(BBRRTTLL) L = 0 - from left, L = 1 from right, L = 2 from center - int OrL, OrR, OrT, OrB; - int minW, minH; - ModernSkinButtonCtrl * bct; - -} MButton; -MButton * Buttons = nullptr; -uint32_t ButtonsCount = 0; - -#define _center_h( rc ) (((rc)->right + (rc)->left ) >> 1) -#define _center_v( rc ) (((rc)->bottom + (rc)->top ) >> 1) - -int ModernSkinButton_AddButton(HWND parent, - char * ID, - char * CommandService, - char * StateDefService, - char * HandeService, - int Left, - int Top, - int Right, - int Bottom, - uint32_t sbFlags, - wchar_t * Hint, - char * DBkey, - char * TypeDef, - int MinWidth, int MinHeight) -{ - // if (!parent) return 0; - if (!ModernSkinButtonModuleIsLoaded) return 0; - if (!Buttons) - Buttons = (MButton*)mir_alloc(sizeof(MButton)); - else - Buttons = (MButton*)mir_realloc(Buttons, sizeof(MButton)*(ButtonsCount + 1)); - { - //HWND hwnd; - RECT rc = { 0 }; - ModernSkinButtonCtrl* bct; - int l, r, b, t; - if (parent) GetClientRect(parent, &rc); - l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Left) : - (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Left) : - (rc.left + Left); - - t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Top) : - (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Top) : - (rc.top + Top); - - r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Right) : - (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Right) : - (rc.left + Right); - - b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Bottom) : - (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Bottom) : - (rc.top + Bottom); - bct = (ModernSkinButtonCtrl *)mir_alloc(sizeof(ModernSkinButtonCtrl)); - memset(bct, 0, sizeof(ModernSkinButtonCtrl)); - bct->Left = l; - bct->Right = r; - bct->Top = t; - bct->Bottom = b; - bct->fCallOnPress = (sbFlags & SBF_CALL_ON_PRESS) != 0; - bct->HandleService = mir_strdup(HandeService); - bct->CommandService = mir_strdup(CommandService); - bct->StateService = mir_strdup(StateDefService); - if (DBkey && *DBkey != '\0') bct->ValueDBSection = mir_strdup(DBkey); else bct->ValueDBSection = nullptr; - if (TypeDef && *TypeDef != '\0') bct->ValueTypeDef = mir_strdup(TypeDef); else bct->ValueTypeDef = mir_strdup("sDefault"); - bct->ID = mir_strdup(ID); - bct->Hint = mir_wstrdup(Hint); - Buttons[ButtonsCount].bct = bct; - Buttons[ButtonsCount].hwnd = nullptr; - Buttons[ButtonsCount].OrL = Left; - Buttons[ButtonsCount].OrT = Top; - Buttons[ButtonsCount].OrR = Right; - Buttons[ButtonsCount].OrB = Bottom; - Buttons[ButtonsCount].ConstrainPositionFrom = (uint8_t)sbFlags; - Buttons[ButtonsCount].minH = MinHeight; - Buttons[ButtonsCount].minW = MinWidth; - ButtonsCount++; - // CLUI_ShowWindowMod(hwnd,SW_SHOW); - } - return 0; -} - - - -static int ModernSkinButtonErase(int l, int t, int r, int b) -{ - uint32_t i; - if (!ModernSkinButtonModuleIsLoaded) return 0; - if (!g_CluiData.fLayered) return 0; - if (!g_pCachedWindow) return 0; - if (!g_pCachedWindow->hImageDC || !g_pCachedWindow->hBackDC) return 0; - if (!(l || r || t || b)) { - for (i = 0; i < ButtonsCount; i++) { - if (g_clistApi.hwndContactList && Buttons[i].hwnd != nullptr) { - //TODO: Erase button - BitBlt(g_pCachedWindow->hImageDC, Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right - Buttons[i].bct->Left, Buttons[i].bct->Bottom - Buttons[i].bct->Top, - g_pCachedWindow->hBackDC, Buttons[i].bct->Left, Buttons[i].bct->Top, SRCCOPY); - } - } - } - else { - BitBlt(g_pCachedWindow->hImageDC, l, t, r - l, b - t, g_pCachedWindow->hBackDC, l, t, SRCCOPY); - } - return 0; -} - -static HWND ModernSkinButtonCreateWindow(ModernSkinButtonCtrl * bct, HWND parent) -{ - HWND hwnd; - - if (bct == nullptr) return FALSE; - { - wchar_t *UnicodeID = mir_a2u(bct->ID); - hwnd = CreateWindow(_A2W(MODERNSKINBUTTONCLASS), UnicodeID, WS_VISIBLE | WS_CHILD, bct->Left, bct->Top, bct->Right - bct->Left, bct->Bottom - bct->Top, parent, nullptr, g_plugin.getInst(), nullptr); - mir_free(UnicodeID); - } - - bct->hwnd = hwnd; - bct->focus = 0; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)bct); - return hwnd; -} - -int ModernSkinButtonRedrawAll() -{ - if (!ModernSkinButtonModuleIsLoaded) return 0; - g_mutex_bLockUpdating++; - for (uint32_t i = 0; i < ButtonsCount; i++) { - if (g_clistApi.hwndContactList && Buttons[i].hwnd == nullptr) - Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, g_clistApi.hwndContactList); - ModernSkinButtonPaintWorker(Buttons[i].hwnd, nullptr); - } - g_mutex_bLockUpdating--; - return 0; -} - -int ModernSkinButtonDeleteAll() -{ - if (!ModernSkinButtonModuleIsLoaded) - return 0; - - for (size_t i = 0; i < ButtonsCount; i++) - if (Buttons[i].hwnd) - DestroyWindow(Buttons[i].hwnd); - - mir_free_and_nil(Buttons); - ButtonsCount = 0; - return 0; -} - -int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *pRect) -{ - RECT rc, clr, rd; - BOOL altDraw = FALSE; - static SIZE oldWndSize = { 0 }; - if (!ModernSkinButtonModuleIsLoaded) return 0; - GetWindowRect(parent, &rd); - GetClientRect(parent, &clr); - if (!pRect) - GetWindowRect(parent, &rc); - else - rc = *pRect; - - if (g_CluiData.fLayered && (draw & SBRF_DO_ALT_DRAW)) { - int sx, sy; - sx = rd.right - rd.left; - sy = rd.bottom - rd.top; - if (sx != oldWndSize.cx || sy != oldWndSize.cy) - altDraw = TRUE;//EraseButtons(); - oldWndSize.cx = sx; - oldWndSize.cy = sy; - } - - OffsetRect(&rc, -rc.left, -rc.top); - rc.right = rc.left + (clr.right - clr.left); - rc.bottom = rc.top + (clr.bottom - clr.top); - for (uint32_t i = 0; i < ButtonsCount; i++) { - int sbFlags = Buttons[i].ConstrainPositionFrom; - if (parent && Buttons[i].hwnd == nullptr) { - Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, parent); - altDraw = FALSE; - } - - int l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Buttons[i].OrL) : - (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Buttons[i].OrL) : - (rc.left + Buttons[i].OrL); - - int t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Buttons[i].OrT) : - (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Buttons[i].OrT) : - (rc.top + Buttons[i].OrT); - - int r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Buttons[i].OrR) : - (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Buttons[i].OrR) : - (rc.left + Buttons[i].OrR); - - int b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Buttons[i].OrB) : - (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Buttons[i].OrB) : - (rc.top + Buttons[i].OrB); - - SetWindowPos(Buttons[i].hwnd, HWND_TOP, l, t, r - l, b - t, 0); - if (rc.right - rc.left < Buttons[i].minW || rc.bottom - rc.top < Buttons[i].minH) - CLUI_ShowWindowMod(Buttons[i].hwnd, SW_HIDE); - else - CLUI_ShowWindowMod(Buttons[i].hwnd, SW_SHOW); - if ((1 || altDraw) && - (Buttons[i].bct->Left != l || - Buttons[i].bct->Top != t || - Buttons[i].bct->Right != r || - Buttons[i].bct->Bottom != b)) { - //Need to erase in old location - ModernSkinButtonErase(Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right, Buttons[i].bct->Bottom); - } - - Buttons[i].bct->Left = l; - Buttons[i].bct->Top = t; - Buttons[i].bct->Right = r; - Buttons[i].bct->Bottom = b; - } - - if (draw & SBRF_DO_REDRAW_ALL) - ModernSkinButtonRedrawAll(); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+This file contains code related to new modern free positioned skinned buttons
+*/
+
+#include "stdafx.h"
+#include "modern_clcpaint.h"
+
+#define MODERNSKINBUTTONCLASS "MirandaModernSkinButtonClass"
+BOOL ModernSkinButtonModuleIsLoaded = FALSE;
+static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+int ModernSkinButtonUnloadModule(WPARAM wParam, LPARAM lParam);
+HWND SetToolTip(HWND hwnd, wchar_t * tip);
+
+typedef struct _ModernSkinButtonCtrl
+{
+ HWND hwnd;
+ uint8_t down; // button state
+ uint8_t focus; // has focus (1 or 0)
+ uint8_t hover;
+ uint8_t IsSwitcher;
+ BOOL fCallOnPress;
+ char * ID;
+ char * CommandService;
+ char * StateService;
+ char * HandleService;
+ char * ValueDBSection;
+ char * ValueTypeDef;
+ int Left, Top, Bottom, Right;
+ HMENU hMenu;
+ wchar_t * Hint;
+
+} ModernSkinButtonCtrl;
+typedef struct _HandleServiceParams
+{
+ HWND hwnd;
+ uint32_t msg;
+ WPARAM wParam;
+ LPARAM lParam;
+ BOOL handled;
+} HandleServiceParams;
+
+static mir_cs csTips;
+static HWND hwndToolTips = nullptr;
+
+int ModernSkinButtonLoadModule()
+{
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = _A2W(MODERNSKINBUTTONCLASS);
+ wc.lpfnWndProc = ModernSkinButtonWndProc;
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wc.cbWndExtra = sizeof(ModernSkinButtonCtrl*);
+ wc.hbrBackground = nullptr;
+ wc.style = CS_GLOBALCLASS;
+ RegisterClassEx(&wc);
+ ModernSkinButtonModuleIsLoaded = TRUE;
+ return 0;
+}
+
+int ModernSkinButtonUnloadModule(WPARAM, LPARAM)
+{
+ return 0;
+}
+
+static int ModernSkinButtonPaintWorker(HWND hwnd, HDC whdc)
+{
+ HDC hdc;
+ RECT rc;
+ ModernSkinButtonCtrl* bct = (ModernSkinButtonCtrl *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!bct) return 0;
+ if (!IsWindowVisible(hwnd)) return 0;
+ if (!whdc && !g_CluiData.fLayered) InvalidateRect(hwnd, nullptr, FALSE);
+
+ if (whdc && g_CluiData.fLayered) hdc = whdc;
+ else {
+ //sdc = GetWindowDC(GetParent(hwnd));
+ hdc = CreateCompatibleDC(nullptr);
+ }
+ GetClientRect(hwnd, &rc);
+ HBITMAP bmp = ske_CreateDIB32(rc.right, rc.bottom);
+ HBITMAP oldbmp = (HBITMAP)SelectObject(hdc, bmp);
+ if (!g_CluiData.fLayered)
+ ske_BltBackImage(bct->hwnd, hdc, nullptr);
+ {
+ MODERNMASK Request = {};
+ // int res;
+ //HBRUSH br = CreateSolidBrush(RGB(255,255,255));
+ char * Value = nullptr;
+ {
+ if (bct->ValueDBSection && bct->ValueTypeDef) {
+ char * key;
+ char * section;
+ uint32_t defval = 0;
+ char buf[20];
+ key = mir_strdup(bct->ValueDBSection);
+ section = key;
+ if (bct->ValueTypeDef[0] != 's')
+ defval = (uint32_t)atol(bct->ValueTypeDef + 1);
+ do {
+ if (key[0] == '/') { key[0] = '\0'; key++; break; }
+ key++;
+ } while (key[0] != '\0');
+ switch (bct->ValueTypeDef[0]) {
+ case 's':
+ Value = db_get_sa(0, section, key, bct->ValueTypeDef + 1);
+ break;
+ case 'd':
+ defval = db_get_dw(0, section, key, defval);
+ Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
+ break;
+ case 'w':
+ defval = db_get_w(0, section, key, defval);
+ Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
+ break;
+ case 'b':
+ defval = db_get_b(0, section, key, defval);
+ Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
+ break;
+ }
+ mir_free(section);
+ }
+
+ }
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Module"), "MButton", 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("ID"), bct->ID, 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Down"), bct->down ? "1" : "0", 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Focused"), bct->focus ? "1" : "0", 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Hovered"), bct->hover ? "1" : "0", 0);
+ if (Value) {
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Value"), Value, 0);
+ mir_free(Value);
+ }
+ SkinDrawGlyphMask(hdc, &rc, &rc, &Request);
+ SkinSelector_DeleteMask(&Request);
+ }
+
+ if (!whdc && g_CluiData.fLayered) {
+ RECT r;
+ SetRect(&r, bct->Left, bct->Top, bct->Right, bct->Bottom);
+ ske_DrawImageAt(hdc, &r);
+ //CallingService to immeadeately update window with new image.
+ }
+ if (whdc && !g_CluiData.fLayered) {
+ RECT r = { 0 };
+ GetClientRect(bct->hwnd, &r);
+ BitBlt(whdc, 0, 0, r.right, r.bottom, hdc, 0, 0, SRCCOPY);
+ }
+ SelectObject(hdc, oldbmp);
+ DeleteObject(bmp);
+ if (!whdc || !g_CluiData.fLayered) {
+ SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+ DeleteDC(hdc);
+ }
+ // if (sdc)
+ // ReleaseDC(GetParent(hwnd),sdc);
+ return 0;
+}
+
+static int ModernSkinButtonToggleDBValue(char * ValueDBSection, char *ValueTypeDef)
+{
+ if (ValueDBSection && ValueTypeDef) {
+ char * key;
+ char * section;
+ char * val;
+ char * val2;
+ char * Value;
+ long l1 = 0, l2 = 0, curval;
+ // char buf[20];
+ key = mir_strdup(ValueDBSection);
+ section = key;
+ do {
+ if (key[0] == '/') { key[0] = '\0'; key++; break; }
+ key++;
+ } while (key[0] != '\0');
+
+ val = mir_strdup(ValueTypeDef + 1);
+ val2 = val;
+ do {
+ if (val2[0] == '/') { val2[0] = '\0'; val2++; break; }
+ val2++;
+ } while (val2[0] != '\0');
+
+ if (ValueTypeDef[0] != 's') {
+ l1 = (uint32_t)atol(val);
+ l2 = (uint32_t)atol(val2);
+ }
+
+ switch (ValueTypeDef[0]) {
+ case 's':
+ Value = db_get_sa(0, section, key);
+ if (!Value || (Value && !mir_strcmpi(Value, val2)))
+ Value = mir_strdup(val);
+ else
+ Value = mir_strdup(val2);
+ db_set_s(0, section, key, Value);
+ mir_free(Value);
+ break;
+
+ case 'd':
+ curval = db_get_dw(0, section, key, l2);
+ curval = (curval == l2) ? l1 : l2;
+ db_set_dw(0, section, key, (uint32_t)curval);
+ break;
+
+ case 'w':
+ curval = db_get_w(0, section, key, l2);
+ curval = (curval == l2) ? l1 : l2;
+ db_set_w(0, section, key, (uint16_t)curval);
+ break;
+
+ case 'b':
+ curval = db_get_b(0, section, key, l2);
+ curval = (curval == l2) ? l1 : l2;
+ db_set_b(0, section, key, (uint8_t)curval);
+ break;
+ }
+ mir_free(section);
+ mir_free(val);
+ }
+ return 0;
+}
+
+static char *_skipblank(char * str) //str will be modified;
+{
+ char * endstr = str + mir_strlen(str);
+ while ((*str == ' ' || *str == '\t') && *str != '\0') str++;
+ while ((*endstr == ' ' || *endstr == '\t') && *endstr != '\0' && endstr < str) endstr--;
+ if (*endstr != '\0') {
+ endstr++;
+ *endstr = '\0';
+ }
+ return str;
+}
+
+static int _CallServiceStrParams(IN char * toParce, OUT int *Return)
+{
+ int paramCount = 0;
+ int result = 0;
+
+ char *pszService = mir_strdup(toParce);
+ if (!pszService)
+ return 0;
+ if (mir_strlen(pszService) == 0) {
+ mir_free(pszService);
+ return 0;
+ }
+ char *param2 = strrchr(pszService, '%');
+ if (param2) {
+ paramCount++;
+ *param2 = '\0'; param2++;
+ _skipblank(param2);
+ if (mir_strlen(param2) == 0)
+ param2 = nullptr;
+ }
+ char *param1 = strrchr(pszService, '%');
+ if (param1) {
+ paramCount++;
+ *param1 = '\0'; param1++;
+ _skipblank(param1);
+ if (mir_strlen(param1) == 0)
+ param1 = nullptr;
+ }
+ if (param1 && *param1 == '\"') {
+ param1++;
+ *(param1 + mir_strlen(param1)) = '\0';
+ }
+ else if (param1) {
+ param1 = (char*)atoi(param1);
+ }
+ if (param2 && *param2 == '\"') {
+ param2++;
+ *(param2 + mir_strlen(param2)) = '\0';
+ }
+ else if (param2)
+ param2 = (char*)atoi(param2);
+
+ if (paramCount == 1) {
+ param1 = param2;
+ param2 = nullptr;
+ }
+ if (!ServiceExists(pszService)) {
+ result = 0;
+ }
+ else {
+ result = 1;
+ int ret = CallService(pszService, (WPARAM)param1, (WPARAM)param2);
+ if (Return) *Return = ret;
+ }
+ mir_free(pszService);
+ return result;
+}
+
+
+static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ ModernSkinButtonCtrl* bct = (msg != WM_NCCREATE) ? (ModernSkinButtonCtrl *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA) : nullptr;
+ if (bct) {
+ if (bct->HandleService && IsBadStringPtrA(bct->HandleService, 255))
+ bct->HandleService = nullptr;
+ else if (bct->HandleService && ServiceExists(bct->HandleService)) {
+ HandleServiceParams MSG = {};
+ MSG.hwnd = hwndDlg;
+ MSG.msg = msg;
+ MSG.wParam = wParam;
+ MSG.lParam = lParam;
+ int t = CallService(bct->HandleService, (WPARAM)&MSG, 0);
+ if (MSG.handled) return t;
+ }
+ }
+
+ switch (msg) {
+ case WM_NCCREATE:
+ SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ if (((CREATESTRUCT *)lParam)->lpszName) SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName);
+ return TRUE;
+
+ case WM_DESTROY:
+ if (bct == nullptr)
+ break;
+
+ if (hwndToolTips) {
+ mir_cslock lck(csTips);
+ TOOLINFO ti = { 0 };
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+
+ if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
+ DestroyWindow(hwndToolTips);
+ hwndToolTips = nullptr;
+ }
+ }
+ mir_free(bct->ID);
+ mir_free(bct->CommandService);
+ mir_free(bct->StateService);
+ mir_free(bct->HandleService);
+ mir_free(bct->Hint);
+ mir_free(bct->ValueDBSection);
+ mir_free(bct->ValueTypeDef);
+ mir_free(bct);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break; // DONT! fall thru
+
+ case WM_SETCURSOR:
+ {
+ HCURSOR hCurs1 = LoadCursor(nullptr, IDC_ARROW);
+ if (hCurs1) SetCursor(hCurs1);
+ if (bct) SetToolTip(hwndDlg, bct->Hint);
+ }
+ return 1;
+
+ case WM_PRINT:
+ if (IsWindowVisible(hwndDlg))
+ ModernSkinButtonPaintWorker(hwndDlg, (HDC)wParam);
+ break;
+
+ case WM_PAINT:
+ if (IsWindowVisible(hwndDlg) && !g_CluiData.fLayered) {
+ PAINTSTRUCT ps = {};
+ BeginPaint(hwndDlg, &ps);
+ ModernSkinButtonPaintWorker(hwndDlg, (HDC)ps.hdc);
+ EndPaint(hwndDlg, &ps);
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+
+ case WM_CAPTURECHANGED:
+ if (bct) {
+ bct->hover = 0;
+ bct->down = 0;
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (bct) {
+ if (!bct->hover) {
+ SetCapture(bct->hwnd);
+ bct->hover = 1;
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ }
+ else {
+ POINT t = UNPACK_POINT(lParam);
+ ClientToScreen(bct->hwnd, &t);
+ if (WindowFromPoint(t) != bct->hwnd)
+ ReleaseCapture();
+ }
+ }
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ if (bct) {
+ bct->down = 1;
+ SetForegroundWindow(GetParent(bct->hwnd));
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
+ bct->CommandService = nullptr;
+ if (bct->fCallOnPress) {
+ if (bct->CommandService) {
+ if (!_CallServiceStrParams(bct->CommandService, nullptr) && (bct->ValueDBSection && bct->ValueTypeDef))
+ ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
+ }
+ bct->down = 0;
+
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ }
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ if (bct && bct->down) {
+ ReleaseCapture();
+ bct->hover = 0;
+ bct->down = 0;
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
+ bct->CommandService = nullptr;
+ if (bct->CommandService)
+ if (_CallServiceStrParams(bct->CommandService, nullptr)) {
+ }
+ else if (bct->ValueDBSection && bct->ValueTypeDef)
+ ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
+ }
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
+
+HWND SetToolTip(HWND hwnd, wchar_t * tip)
+{
+ TOOLINFO ti;
+ if (!tip) return nullptr;
+ mir_cslock lck(csTips);
+ if (!hwndToolTips) {
+ hwndToolTips = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr,
+ WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, nullptr, g_hMirApp, nullptr);
+
+ SetWindowPos(hwndToolTips, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+
+ memset(&ti, 0, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = hwnd;
+ ti.uId = (UINT_PTR)hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ }
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)hwnd;
+ ti.lpszText = (wchar_t*)tip;
+ SendMessage(hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ return hwndToolTips;
+}
+
+
+
+typedef struct _MButton
+{
+ HWND hwnd;
+ uint8_t ConstrainPositionFrom; //(BBRRTTLL) L = 0 - from left, L = 1 from right, L = 2 from center
+ int OrL, OrR, OrT, OrB;
+ int minW, minH;
+ ModernSkinButtonCtrl * bct;
+
+} MButton;
+MButton * Buttons = nullptr;
+uint32_t ButtonsCount = 0;
+
+#define _center_h( rc ) (((rc)->right + (rc)->left ) >> 1)
+#define _center_v( rc ) (((rc)->bottom + (rc)->top ) >> 1)
+
+int ModernSkinButton_AddButton(HWND parent,
+ char * ID,
+ char * CommandService,
+ char * StateDefService,
+ char * HandeService,
+ int Left,
+ int Top,
+ int Right,
+ int Bottom,
+ uint32_t sbFlags,
+ wchar_t * Hint,
+ char * DBkey,
+ char * TypeDef,
+ int MinWidth, int MinHeight)
+{
+ // if (!parent) return 0;
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ if (!Buttons)
+ Buttons = (MButton*)mir_alloc(sizeof(MButton));
+ else
+ Buttons = (MButton*)mir_realloc(Buttons, sizeof(MButton)*(ButtonsCount + 1));
+ {
+ //HWND hwnd;
+ RECT rc = { 0 };
+ ModernSkinButtonCtrl* bct;
+ int l, r, b, t;
+ if (parent) GetClientRect(parent, &rc);
+ l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Left) :
+ (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Left) :
+ (rc.left + Left);
+
+ t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Top) :
+ (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Top) :
+ (rc.top + Top);
+
+ r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Right) :
+ (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Right) :
+ (rc.left + Right);
+
+ b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Bottom) :
+ (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Bottom) :
+ (rc.top + Bottom);
+ bct = (ModernSkinButtonCtrl *)mir_alloc(sizeof(ModernSkinButtonCtrl));
+ memset(bct, 0, sizeof(ModernSkinButtonCtrl));
+ bct->Left = l;
+ bct->Right = r;
+ bct->Top = t;
+ bct->Bottom = b;
+ bct->fCallOnPress = (sbFlags & SBF_CALL_ON_PRESS) != 0;
+ bct->HandleService = mir_strdup(HandeService);
+ bct->CommandService = mir_strdup(CommandService);
+ bct->StateService = mir_strdup(StateDefService);
+ if (DBkey && *DBkey != '\0') bct->ValueDBSection = mir_strdup(DBkey); else bct->ValueDBSection = nullptr;
+ if (TypeDef && *TypeDef != '\0') bct->ValueTypeDef = mir_strdup(TypeDef); else bct->ValueTypeDef = mir_strdup("sDefault");
+ bct->ID = mir_strdup(ID);
+ bct->Hint = mir_wstrdup(Hint);
+ Buttons[ButtonsCount].bct = bct;
+ Buttons[ButtonsCount].hwnd = nullptr;
+ Buttons[ButtonsCount].OrL = Left;
+ Buttons[ButtonsCount].OrT = Top;
+ Buttons[ButtonsCount].OrR = Right;
+ Buttons[ButtonsCount].OrB = Bottom;
+ Buttons[ButtonsCount].ConstrainPositionFrom = (uint8_t)sbFlags;
+ Buttons[ButtonsCount].minH = MinHeight;
+ Buttons[ButtonsCount].minW = MinWidth;
+ ButtonsCount++;
+ // CLUI_ShowWindowMod(hwnd,SW_SHOW);
+ }
+ return 0;
+}
+
+
+
+static int ModernSkinButtonErase(int l, int t, int r, int b)
+{
+ uint32_t i;
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ if (!g_CluiData.fLayered) return 0;
+ if (!g_pCachedWindow) return 0;
+ if (!g_pCachedWindow->hImageDC || !g_pCachedWindow->hBackDC) return 0;
+ if (!(l || r || t || b)) {
+ for (i = 0; i < ButtonsCount; i++) {
+ if (g_clistApi.hwndContactList && Buttons[i].hwnd != nullptr) {
+ //TODO: Erase button
+ BitBlt(g_pCachedWindow->hImageDC, Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right - Buttons[i].bct->Left, Buttons[i].bct->Bottom - Buttons[i].bct->Top,
+ g_pCachedWindow->hBackDC, Buttons[i].bct->Left, Buttons[i].bct->Top, SRCCOPY);
+ }
+ }
+ }
+ else {
+ BitBlt(g_pCachedWindow->hImageDC, l, t, r - l, b - t, g_pCachedWindow->hBackDC, l, t, SRCCOPY);
+ }
+ return 0;
+}
+
+static HWND ModernSkinButtonCreateWindow(ModernSkinButtonCtrl * bct, HWND parent)
+{
+ HWND hwnd;
+
+ if (bct == nullptr) return FALSE;
+ {
+ wchar_t *UnicodeID = mir_a2u(bct->ID);
+ hwnd = CreateWindow(_A2W(MODERNSKINBUTTONCLASS), UnicodeID, WS_VISIBLE | WS_CHILD, bct->Left, bct->Top, bct->Right - bct->Left, bct->Bottom - bct->Top, parent, nullptr, g_plugin.getInst(), nullptr);
+ mir_free(UnicodeID);
+ }
+
+ bct->hwnd = hwnd;
+ bct->focus = 0;
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)bct);
+ return hwnd;
+}
+
+int ModernSkinButtonRedrawAll()
+{
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ g_mutex_bLockUpdating++;
+ for (uint32_t i = 0; i < ButtonsCount; i++) {
+ if (g_clistApi.hwndContactList && Buttons[i].hwnd == nullptr)
+ Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, g_clistApi.hwndContactList);
+ ModernSkinButtonPaintWorker(Buttons[i].hwnd, nullptr);
+ }
+ g_mutex_bLockUpdating--;
+ return 0;
+}
+
+int ModernSkinButtonDeleteAll()
+{
+ if (!ModernSkinButtonModuleIsLoaded)
+ return 0;
+
+ for (size_t i = 0; i < ButtonsCount; i++)
+ if (Buttons[i].hwnd)
+ DestroyWindow(Buttons[i].hwnd);
+
+ mir_free_and_nil(Buttons);
+ ButtonsCount = 0;
+ return 0;
+}
+
+int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *pRect)
+{
+ RECT rc, clr, rd;
+ BOOL altDraw = FALSE;
+ static SIZE oldWndSize = { 0 };
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ GetWindowRect(parent, &rd);
+ GetClientRect(parent, &clr);
+ if (!pRect)
+ GetWindowRect(parent, &rc);
+ else
+ rc = *pRect;
+
+ if (g_CluiData.fLayered && (draw & SBRF_DO_ALT_DRAW)) {
+ int sx, sy;
+ sx = rd.right - rd.left;
+ sy = rd.bottom - rd.top;
+ if (sx != oldWndSize.cx || sy != oldWndSize.cy)
+ altDraw = TRUE;//EraseButtons();
+ oldWndSize.cx = sx;
+ oldWndSize.cy = sy;
+ }
+
+ OffsetRect(&rc, -rc.left, -rc.top);
+ rc.right = rc.left + (clr.right - clr.left);
+ rc.bottom = rc.top + (clr.bottom - clr.top);
+ for (uint32_t i = 0; i < ButtonsCount; i++) {
+ int sbFlags = Buttons[i].ConstrainPositionFrom;
+ if (parent && Buttons[i].hwnd == nullptr) {
+ Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, parent);
+ altDraw = FALSE;
+ }
+
+ int l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Buttons[i].OrL) :
+ (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Buttons[i].OrL) :
+ (rc.left + Buttons[i].OrL);
+
+ int t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Buttons[i].OrT) :
+ (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Buttons[i].OrT) :
+ (rc.top + Buttons[i].OrT);
+
+ int r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Buttons[i].OrR) :
+ (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Buttons[i].OrR) :
+ (rc.left + Buttons[i].OrR);
+
+ int b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Buttons[i].OrB) :
+ (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Buttons[i].OrB) :
+ (rc.top + Buttons[i].OrB);
+
+ SetWindowPos(Buttons[i].hwnd, HWND_TOP, l, t, r - l, b - t, 0);
+ if (rc.right - rc.left < Buttons[i].minW || rc.bottom - rc.top < Buttons[i].minH)
+ CLUI_ShowWindowMod(Buttons[i].hwnd, SW_HIDE);
+ else
+ CLUI_ShowWindowMod(Buttons[i].hwnd, SW_SHOW);
+ if ((1 || altDraw) &&
+ (Buttons[i].bct->Left != l ||
+ Buttons[i].bct->Top != t ||
+ Buttons[i].bct->Right != r ||
+ Buttons[i].bct->Bottom != b)) {
+ //Need to erase in old location
+ ModernSkinButtonErase(Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right, Buttons[i].bct->Bottom);
+ }
+
+ Buttons[i].bct->Left = l;
+ Buttons[i].bct->Top = t;
+ Buttons[i].bct->Right = r;
+ Buttons[i].bct->Bottom = b;
+ }
+
+ if (draw & SBRF_DO_REDRAW_ALL)
+ ModernSkinButtonRedrawAll();
+ return 0;
+}
diff --git a/plugins/Clist_modern/src/modern_skinengine.cpp b/plugins/Clist_modern/src/modern_skinengine.cpp index cda79568be..faf1616152 100644 --- a/plugins/Clist_modern/src/modern_skinengine.cpp +++ b/plugins/Clist_modern/src/modern_skinengine.cpp @@ -1,3757 +1,3757 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -//Include -#include "stdafx.h" - -#define _EFFECTENUM_FULL_H -#include "modern_effectenum.h" -#undef _EFFECTENUM_FULL_H - -#include "modern_sync.h" - -//Implementation - -#pragma pack(push, 1) -/* tga header */ -struct tga_header_t -{ - uint8_t id_lenght; /* size of image id */ - uint8_t colormap_type; /* 1 is has a colormap */ - uint8_t image_type; /* compression type */ - - short cm_first_entry; /* colormap origin */ - short cm_length; /* colormap length */ - uint8_t cm_size; /* colormap size */ - - short x_origin; /* bottom left x coord origin */ - short y_origin; /* bottom left y coord origin */ - - short width; /* picture width (in pixels) */ - short height; /* picture height (in pixels) */ - - uint8_t pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */ - uint8_t image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */ -}; -#pragma pack(pop) - -/* Global variables */ - -SKINOBJECTSLIST g_SkinObjectList = { 0 }; -CURRWNDIMAGEDATA *g_pCachedWindow = nullptr; - -bool g_bPostWasCanceled = false; -bool g_bFullRepaint = false; - -int g_mutex_bLockUpdating = 0; - -SortedList *gl_plGlyphTexts = nullptr; -SortedList *gl_plSkinFonts = nullptr; - -/* Private module variables */ - -static HANDLE hSkinLoadedEvent; - -static GLYPHIMAGE *pLoadedImages = nullptr; -static uint32_t dwLoadedImagesCount = 0; -static uint32_t dwLoadedImagesAlocated = 0; - -static BOOL flag_bUpdateQueued = FALSE; -static BOOL flag_bJustDrawNonFramedObjects = FALSE; -static BOOL mutex_bLockUpdate = FALSE; - -static LIST<EFFECTSSTACKITEM> arEffectStack(10, HandleKeySortT); -static SKINOBJECTSLIST *pCurrentSkin = nullptr; -static char **pszSettingName = nullptr; -static int nArrayLen = 0; - -static uint8_t pbGammaWeight[256] = { 0 }; -static uint8_t pbGammaWeightAdv[256] = { 0 }; -static BOOL bGammaWeightFilled = FALSE; - -static mir_cs cs_SkinChanging; - -static LISTMODERNMASK *MainModernMaskList = nullptr; - -/* Private module procedures */ -static BOOL ske_GetMaskBit(uint8_t *line, int x); -static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM lParam); -static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM lParam); - -static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor); -static void ske_AddParseTextGlyphObject(char * szGlyphTextID, char * szDefineString, SKINOBJECTSLIST *Skin); -static void ske_AddParseSkinFont(char * szFontID, char * szDefineString); -static int ske_GetSkinFromDB(char * szSection, SKINOBJECTSLIST * Skin); -static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin); -static int ske_LoadSkinFromResource(BOOL bOnlyObjects); -static void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult); -static int ske_ValidateSingleFrameImage(FRAMEWND * Frame, BOOL SkipBkgBlitting); -static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM lParam); -static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam); -static INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM lParam); - -static MODERNEFFECT meCurrentEffect = { 0xFF, { 0 }, 0, 0 }; - -////////////////////////////////////////////////////////////////////////// -// Ini file parser - -IniParser::IniParser(wchar_t * tcsFileName, uint8_t flags) : _Flags(flags) -{ - _DoInit(); - if (!tcsFileName) return; - - if (tcsFileName[0] == '%') { - //TODO: Add parser of resource filename here - _LoadResourceIni(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF"); - return; - } - - _hFile = _wfopen(tcsFileName, L"r"); - if (_hFile != nullptr) { - _eType = IT_FILE; - _isValid = true; - } -} - -IniParser::IniParser(HINSTANCE hInst, const char * resourceName, const char * resourceType, uint8_t flags) : _Flags(flags) -{ - _DoInit(); - _LoadResourceIni(hInst, resourceName, resourceType); -} - -IniParser::~IniParser() -{ - mir_free(_szSection); - if (_hFile) fclose(_hFile); - if (_hGlobalRes) { - UnlockResource(_hGlobalRes); - FreeResource(_hGlobalRes); - } - - _szSection = nullptr; - _hGlobalRes = nullptr; - _hFile = nullptr; - _isValid = false; - _eType = IT_UNKNOWN; -} - -HRESULT IniParser::Parse(ParserCallback_t pLineCallBackProc, LPARAM SecCheck) -{ - if (_isValid && pLineCallBackProc) { - _pLineCallBackProc = pLineCallBackProc; - _SecCheck = SecCheck; - switch (_eType) { - case IT_FILE: - return _DoParseFile(); - case IT_RESOURCE: - return _DoParseResource(); - } - } - return E_FAIL; -} - -HRESULT IniParser::WriteStrToDb(const char * szSection, const char * szName, const char * szValue, IniParser * This) -{ - if (This->_SecCheck) { - //TODO check security here - if (wildcmp(szSection, "Skin_Description_Section")) - return S_OK; - } - if ((This->_Flags == IniParser::FLAG_ONLY_OBJECTS) && !wildcmp(szSection, DEFAULTSKINSECTION)) - return S_OK; // skip not objects - - switch (szValue[0]) { - case 'b': - db_set_b(0, szSection, szName, (uint8_t)atoi(szValue + 1)); - break; - - case 'w': - db_set_w(0, szSection, szName, (uint16_t)atoi(szValue + 1)); - break; - - case 'd': - db_set_dw(0, szSection, szName, (uint32_t)atoi(szValue + 1)); - break; - - case 's': - db_set_s(0, szSection, szName, szValue + 1); - break; - } - return S_OK; -} - -int IniParser::GetSkinFolder(IN const wchar_t * szFileName, OUT wchar_t * pszFolderName) -{ - wchar_t *szBuff = mir_wstrdup(szFileName); - wchar_t *pszPos = szBuff + mir_wstrlen(szBuff); - while (pszPos > szBuff && *pszPos != '.') { pszPos--; } - *pszPos = '\0'; - mir_wstrcpy(pszFolderName, szBuff); - - wchar_t custom_folder[MAX_PATH], cus[MAX_PATH]; - wchar_t *b3; - mir_wstrncpy(custom_folder, pszFolderName, _countof(custom_folder)); - b3 = custom_folder + mir_wstrlen(custom_folder); - while (b3 > custom_folder && *b3 != '\\') { b3--; } - *b3 = '\0'; - - GetPrivateProfileString(L"Skin_Description_Section", L"SkinFolder", L"", cus, _countof(custom_folder), szFileName); - if (cus[0] != 0) - mir_snwprintf(pszFolderName, MAX_PATH, L"%s\\%s", custom_folder, cus); - - mir_free(szBuff); - PathToRelativeW(pszFolderName, pszFolderName); - return 0; -} - -void IniParser::_DoInit() -{ - _isValid = false; - _eType = IT_UNKNOWN; - _szSection = nullptr; - _hFile = nullptr; - _hGlobalRes = nullptr; - _dwSizeOfRes = 0; - _pPosition = nullptr; - _pLineCallBackProc = nullptr; - _SecCheck = 0; -} - -void IniParser::_LoadResourceIni(HINSTANCE hInst, const char * resourceName, const char * resourceType) -{ - if (_eType != IT_UNKNOWN) - return; - - HRSRC hRSrc = FindResourceA(hInst, resourceName, resourceType); - if (!hRSrc) - return; - - _hGlobalRes = LoadResource(hInst, hRSrc); - if (!_hGlobalRes) - return; - - _dwSizeOfRes = SizeofResource(hInst, hRSrc); - _pPosition = (char*)LockResource(_hGlobalRes); - - _isValid = true; - _eType = IT_RESOURCE; -} - -HRESULT IniParser::_DoParseFile() -{ - char szLine[MAX_LINE_LEN]; - _nLine = 0; - while (fgets(szLine, _countof(szLine), _hFile) != nullptr) { - size_t len = 0; - char *pLine = (char *)_RemoveTailings(szLine, len); - if (len > 0) { - pLine[len] = '\0'; - if (!_DoParseLine(pLine)) - return E_FAIL; - } - else _nLine++; - }; - - return S_OK; -} - -HRESULT IniParser::_DoParseResource() -{ - _nLine = 0; - char szLine[MAX_LINE_LEN]; - char *pos = (char *)_pPosition; - - while (pos < _pPosition + _dwSizeOfRes) { - int i = 0; - while (pos < _pPosition + _dwSizeOfRes && *pos != '\n' && *pos != '\0' && i < MAX_LINE_LEN - 1) { - if ((*pos) != '\r') - szLine[i++] = *pos; - pos++; - } - szLine[i] = '\0'; - pos++; - - size_t len = 0; - char *pLine = (char *)_RemoveTailings(szLine, len); - if (len > 0) { - pLine[len] = '\0'; - if (!_DoParseLine(pLine)) - return E_FAIL; - } - else _nLine++; - } - return S_OK; -} - -const char *IniParser::_RemoveTailings(const char *szLine, size_t &len) -{ - const char *pStart = szLine; - while (*pStart == ' ' || *pStart == '\t') - pStart++; //skip spaces at begin - const char *pEnd = pStart + mir_strlen(pStart); - while (pEnd > pStart && (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n' || *pEnd == '\r')) - pEnd--; - - len = pEnd - pStart; - return pStart; -} - -BOOL IniParser::_DoParseLine(char *szLine) -{ - _nLine++; - size_t len = mir_strlen(szLine); - if (len == 0) - return TRUE; - - switch (szLine[0]) { - case ';': - return TRUE; // start of comment is found - - case '[': - //New section start here - mir_free(_szSection); - _szSection = nullptr; - { - char *tbuf = szLine + 1; // skip [ - - char *ebuf = tbuf; - - while (*ebuf != ']' && *ebuf != '\0') ebuf++; - if (*ebuf == '\0') - return FALSE; // no close bracket - - uint32_t sectionLen = ebuf - tbuf + 1; - _szSection = (char*)mir_alloc(sectionLen + 1); - mir_strncpy(_szSection, tbuf, sectionLen); - _szSection[sectionLen] = '\0'; - } - return TRUE; - - default: - if (!_szSection) - return TRUE; //param found out of section - - char *keyName = szLine; - char *keyValue = szLine; - - size_t eqPlace = 0, len2 = mir_strlen(keyName); - while (eqPlace < len2 && keyName[eqPlace] != '=') - eqPlace++; //find '=' - - if (eqPlace == 0 || eqPlace == len2) - return TRUE; // = not found or no key name //say false - - keyName[eqPlace] = '\0'; - - keyValue = keyName + eqPlace + 1; - - //remove tail spaces in Name - rtrim(keyName); - while (*keyValue) { - if (!isspace(*keyValue)) - break; - keyValue++; - } - rtrim(keyValue); - _pLineCallBackProc(_szSection, keyName, keyValue, this); - } - return TRUE; -} -////////////////////////////////////////////////////////////////////////// -// End of IniParser -////////////////////////////////////////////////////////////////////////// - -HRESULT SkinEngineLoadModule() -{ - ModernSkinButtonLoadModule(); - MainModernMaskList = (LISTMODERNMASK*)mir_calloc(sizeof(LISTMODERNMASK)); - //init variables - g_SkinObjectList.dwObjLPAlocated = 0; - g_SkinObjectList.dwObjLPReserved = 0; - g_SkinObjectList.pObjects = nullptr; - // Initialize GDI+ - InitGdiPlus(); - AniAva_InitModule(); - //create services - CreateServiceFunction(MS_SKIN_DRAWGLYPH, ske_Service_DrawGlyph); - CreateServiceFunction(MS_SKINENG_UPTATEFRAMEIMAGE, ske_Service_UpdateFrameImage); - CreateServiceFunction(MS_SKINENG_INVALIDATEFRAMEIMAGE, ske_Service_InvalidateFrameImage); - CreateServiceFunction(MS_SKINENG_ALPHATEXTOUT, ske_Service_AlphaTextOut); - CreateServiceFunction(MS_SKINENG_DRAWICONEXFIX, ske_Service_DrawIconEx); - - CreateServiceFunction(MS_DRAW_TEXT_WITH_EFFECT, ske_Service_DrawTextWithEffect); - - //create event handle - hSkinLoadedEvent = HookEvent(ME_SKIN_SERVICESCREATED, CLUI_OnSkinLoad); - NotifyEventHooks(g_CluiData.hEventSkinServicesCreated, 0, 0); - return S_OK; -} - -int SkinEngineUnloadModule() -{ - //unload services - ModernSkinButtonUnloadModule(0, 0); - ske_UnloadSkin(&g_SkinObjectList); - - mir_free_and_nil(g_SkinObjectList.pObjects); - mir_free_and_nil(g_SkinObjectList.pMaskList); - mir_free_and_nil(MainModernMaskList); - - for (auto &it : arEffectStack) - mir_free(it); - arEffectStack.destroy(); - - if (g_pCachedWindow) { - SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld); - SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld); - DeleteObject(g_pCachedWindow->hBackDIB); - DeleteObject(g_pCachedWindow->hImageDIB); - DeleteDC(g_pCachedWindow->hBackDC); - DeleteDC(g_pCachedWindow->hImageDC); - ReleaseDC(nullptr, g_pCachedWindow->hScreenDC); - mir_free_and_nil(g_pCachedWindow); - } - GdiFlush(); - DestroyHookableEvent(g_CluiData.hEventSkinServicesCreated); - AniAva_UnloadModule(); - ShutdownGdiPlus(); - //free variables - return 1; -} - -BOOL ske_AlphaBlend(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction) -{ - if (g_CluiData.fDisableSkinEngine && !(blendFunction.BlendFlags & 128)) { - if (nWidthDest != nWidthSrc || nHeightDest != nHeightSrc) - return StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, SRCCOPY); - return BitBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY); - } - - if (blendFunction.BlendFlags & 128) // Use gdi+ engine - return GDIPlus_AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, - hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, - &blendFunction); - - blendFunction.BlendFlags &= ~128; - return AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction); -} - -struct DCBUFFER -{ - HDC hdcOwnedBy; - int nUsageID; - int width; - int height; - void *pImage; - HDC hDC; - HBITMAP oldBitmap; - HBITMAP hBitmap; - uint32_t dwDestroyAfterTime; -}; - -static int SortBufferList(const DCBUFFER *buf1, const DCBUFFER *buf2) -{ - if (buf1->hdcOwnedBy != buf2->hdcOwnedBy) return (int)(buf1->hdcOwnedBy < buf2->hdcOwnedBy); - else if (buf1->nUsageID != buf2->nUsageID) return (int)(buf1->nUsageID < buf2->nUsageID); - else return (int)(buf1->hDC < buf2->hDC); -} - -LIST<DCBUFFER> BufferList(2, SortBufferList); -mir_cs BufferListCS; - -enum -{ - BUFFER_DRAWICON = 0, - BUFFER_DRAWIMAGE -}; - -HDC ske_RequestBufferDC(HDC hdcOwner, int dcID, int width, int height, BOOL fClear) -{ - mir_cslock lck(BufferListCS); - //Try to find DC in buffer list - DCBUFFER buf; - buf.hdcOwnedBy = hdcOwner; - buf.nUsageID = dcID; - buf.hDC = nullptr; - DCBUFFER *pBuf = BufferList.find(&buf); - if (!pBuf) { - //if not found - allocate it - pBuf = (DCBUFFER *)mir_alloc(sizeof(DCBUFFER)); - *pBuf = buf; - pBuf->width = width; - pBuf->height = height; - pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage)); - pBuf->hDC = CreateCompatibleDC(hdcOwner); - pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap); - pBuf->dwDestroyAfterTime = 0; - BufferList.insert(pBuf); - } - else { - if (pBuf->width != width || pBuf->height != height) { - //resize - SelectObject(pBuf->hDC, pBuf->oldBitmap); - DeleteObject(pBuf->hBitmap); - pBuf->width = width; - pBuf->height = height; - pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage)); - pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap); - } - else if (fClear) - memset(pBuf->pImage, 0, width*height*sizeof(uint32_t)); - } - pBuf->dwDestroyAfterTime = 0; - return pBuf->hDC; -} - -int ske_ReleaseBufferDC(HDC hDC, int keepTime) -{ - uint32_t dwCurrentTime = GetTickCount(); - - // Try to find DC in buffer list - set flag to be release after time; - mir_cslock lck(BufferListCS); - for (auto &it : BufferList.rev_iter()) { - if (it) { - if (hDC != nullptr && it->hDC == hDC) { - it->dwDestroyAfterTime = dwCurrentTime + keepTime; - break; - } - - if ((it->dwDestroyAfterTime && it->dwDestroyAfterTime < dwCurrentTime) || keepTime == -1) { - SelectObject(it->hDC, it->oldBitmap); - DeleteObject(it->hBitmap); - DeleteDC(it->hDC); - mir_free(BufferList.removeItem(&it)); - } - } - } - return 0; -} - -BOOL ske_SetRgnOpaque(HDC memdc, HRGN hrgn, BOOL force) -{ - if (g_CluiData.fDisableSkinEngine && !force) return TRUE; - uint32_t rgnsz = GetRegionData(hrgn, 0, nullptr); - RGNDATA *rdata = (RGNDATA *)mir_alloc(rgnsz); - GetRegionData(hrgn, rgnsz, rdata); - RECT *rect = (RECT *)rdata->Buffer; - for (uint32_t d = 0; d < rdata->rdh.nCount; d++) { - ske_SetRectOpaque(memdc, &rect[d], force); - } - mir_free(rdata); - return TRUE; -} - - -BOOL ske_SetRectOpaque(HDC memdc, RECT *fr, BOOL force) -{ - int f = 0; - uint8_t *bits; - BITMAP bmp; - - if (g_CluiData.fDisableSkinEngine && !force) - return TRUE; - - HBITMAP hbmp = (HBITMAP)GetCurrentObject(memdc, OBJ_BITMAP); - GetObject(hbmp, sizeof(bmp), &bmp); - - if (bmp.bmPlanes != 1) - return FALSE; - - if (!bmp.bmBits) { - f = 1; - bits = (uint8_t*)mir_alloc(bmp.bmWidthBytes*bmp.bmHeight); - GetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits); - } - else - bits = (uint8_t*)bmp.bmBits; - - int sx = (fr->left > 0) ? fr->left : 0; - int sy = (fr->top > 0) ? fr->top : 0; - int ex = (fr->right < bmp.bmWidth) ? fr->right : bmp.bmWidth; - int ey = (fr->bottom < bmp.bmHeight) ? fr->bottom : bmp.bmHeight; - - int width = ex - sx; - - uint8_t *pLine = ((uint8_t *)bits) + (bmp.bmHeight - sy - 1) * bmp.bmWidthBytes + (sx << 2) + 3; - for (int y = 0; y < (ey - sy); y++) { - uint8_t *pColumn = pLine; - for (int x = 0; x < width; x++) { - *pColumn = 255; - pColumn += 4; - } - pLine -= bmp.bmWidthBytes; - } - if (f) { - SetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits); - mir_free(bits); - } - // DeleteObject(hbmp); - return 1; -} - -static BOOL ske_SkinFillRectByGlyph(HDC hDest, HDC hSource, RECT *rFill, RECT *rGlyph, RECT *rClip, uint8_t mode, uint8_t drawMode) -{ - BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - - //initializations - if (mode == FM_STRETCH) { - HDC mem2dc = nullptr; - HBITMAP mem2bmp = nullptr, oldbmp = nullptr; - RECT wr; - IntersectRect(&wr, rClip, rFill); - if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0; - if (drawMode != 2) { - mem2dc = CreateCompatibleDC(hDest); - mem2bmp = ske_CreateDIB32(wr.right - wr.left, wr.bottom - wr.top); - oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp); - } - - if (drawMode == 0 || drawMode == 2) { - if (drawMode == 0) { - ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top, - hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf); - ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf); - } - else { - ske_AlphaBlend(hDest, rFill->left, rFill->top, rFill->right - rFill->left, rFill->bottom - rFill->top, - hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf); - - } - } - else { - // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 }; - ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top, - hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf); - ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf); - } - if (drawMode != 2) { - SelectObject(mem2dc, oldbmp); - DeleteObject(mem2bmp); - DeleteDC(mem2dc); - } - return 1; - } - else if (mode == FM_TILE_VERT && (rGlyph->bottom - rGlyph->top > 0) && (rGlyph->right - rGlyph->left > 0)) { - RECT wr; - IntersectRect(&wr, rClip, rFill); - if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0; - HDC mem2dc = CreateCompatibleDC(hDest); - //SetStretchBltMode(mem2dc, HALFTONE); - HBITMAP mem2bmp = ske_CreateDIB32(wr.right - wr.left, rGlyph->bottom - rGlyph->top); - HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp); - if (!oldbmp) - return 0; - - /// draw here - { - int y = 0; - int w = rFill->right - rFill->left; - int h = rGlyph->bottom - rGlyph->top; - if (h > 0 && (wr.bottom - wr.top)*(wr.right - wr.left) != 0) { - w = wr.right - wr.left; - { - // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 }; - ske_AlphaBlend(mem2dc, -(wr.left - rFill->left), 0, rFill->right - rFill->left, h, hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, h, bf); - //StretchBlt(mem2dc,-(wr.left-rFill->left), 0, rFill->right-rFill->left,h,hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,h,SRCCOPY); - } - if (drawMode == 0 || drawMode == 2) { - if (drawMode == 0) { - int dy = (wr.top - rFill->top) % h; - if (dy >= 0) { - y = wr.top; - int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top); - BitBlt(hDest, wr.left, y, w, ht, mem2dc, 0, dy, SRCCOPY); - } - - y = wr.top + h - dy; - while (y < wr.bottom - h) { - BitBlt(hDest, wr.left, y, w, h, mem2dc, 0, 0, SRCCOPY); - y += h; - } - if (y <= wr.bottom) - BitBlt(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, SRCCOPY); - - } - else { - y = wr.top; - while (y < wr.bottom - h) { - // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 }; - ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf); - y += h; - } - if (y <= wr.bottom) { - // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 }; - ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf); - } - } - - } - else { - BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - int dy = (wr.top - rFill->top) % h; - if (dy >= 0) { - y = wr.top; - int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top); - ske_AlphaBlend(hDest, wr.left, y, w, ht, mem2dc, 0, dy, w, ht, bf2); - } - - y = wr.top + h - dy; - while (y < wr.bottom - h) { - ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf2); - y += h; - } - if (y <= wr.bottom) - ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf2); - } - } - } - SelectObject(mem2dc, oldbmp); - DeleteObject(mem2bmp); - DeleteDC(mem2dc); - } - else if (mode == FM_TILE_HORZ && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0) && (rFill->bottom - rFill->top) > 0 && (rFill->right - rFill->left) > 0) { - RECT wr; - int w = rGlyph->right - rGlyph->left; - int h = rFill->bottom - rFill->top; - IntersectRect(&wr, rClip, rFill); - if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0; - h = wr.bottom - wr.top; - HDC mem2dc = CreateCompatibleDC(hDest); - - HBITMAP mem2bmp = ske_CreateDIB32(w, h); - HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp); - - if (!oldbmp) - return 0; - /// draw here - { - int x = 0; - { - //SetStretchBltMode(mem2dc, HALFTONE); - //StretchBlt(mem2dc, 0, 0, w,h,hSource,rGlyph->left+(wr.left-rFill->left),rGlyph->top,w,h,SRCCOPY); - - // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 }; - ske_AlphaBlend(mem2dc, 0, -(wr.top - rFill->top), w, rFill->bottom - rFill->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf); - if (drawMode == 0 || drawMode == 2) { - if (drawMode == 0) { - int dx = (wr.left - rFill->left) % w; - if (dx >= 0) { - x = wr.left; - int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left); - BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY); - } - x = wr.left + w - dx; - while (x < wr.right - w) { - BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY); - x += w; - } - if (x <= wr.right) - BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY); - } - else { - int dx = (wr.left - rFill->left) % w; - x = wr.left - dx; - while (x < wr.right - w) { - ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf); - x += w; - } - if (x <= wr.right) - ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf); - } - - } - else { - BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - int dx = (wr.left - rFill->left) % w; - if (dx >= 0) { - x = wr.left; - int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left); - ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2); - } - x = wr.left + w - dx; - while (x < wr.right - w) { - ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2); - x += w; - } - if (x <= wr.right) - ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2); - } - } - } - SelectObject(mem2dc, oldbmp); - DeleteObject(mem2bmp); - DeleteDC(mem2dc); - } - else if (mode == FM_TILE_BOTH && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0)) { - int w = rGlyph->right - rGlyph->left; - int x = 0; - int h = rFill->bottom - rFill->top; - RECT wr; - IntersectRect(&wr, rClip, rFill); - if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0; - HDC mem2dc = CreateCompatibleDC(hDest); - HBITMAP mem2bmp = ske_CreateDIB32(w, wr.bottom - wr.top); - h = wr.bottom - wr.top; - HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp); -#ifdef _DEBUG - if (!oldbmp) - (nullptr, "Tile bitmap not selected", "ERROR", MB_OK); -#endif - /// draw here - { - //fill temp bitmap - { - int dy = (wr.top - rFill->top) % (rGlyph->bottom - rGlyph->top); - int y = -dy; - while (y < wr.bottom - wr.top) { - - ske_AlphaBlend(mem2dc, 0, y, w, rGlyph->bottom - rGlyph->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf); - y += rGlyph->bottom - rGlyph->top; - } - - //-- - //end temp bitmap - if (drawMode == 0 || drawMode == 2) { - if (drawMode == 0) { - int dx = (wr.left - rFill->left) % w; - if (dx >= 0) { - x = wr.left; - int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left); - BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY); - } - x = wr.left + w - dx; - while (x < wr.right - w) { - BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY); - x += w; - } - if (x <= wr.right) - BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY); - } - else { - int dx = (wr.left - rFill->left) % w; - x = wr.left - dx; - while (x < wr.right - w) { - ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf); - x += w; - } - if (x <= wr.right) - ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf); - } - - } - else { - BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - int dx = (wr.left - rFill->left) % w; - if (dx >= 0) { - x = wr.left; - int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left); - ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2); - } - x = wr.left + w - dx; - while (x < wr.right - w) { - ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2); - x += w; - } - if (x <= wr.right) - ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2); - } - } - } - SelectObject(mem2dc, oldbmp); - DeleteObject(mem2bmp); - DeleteDC(mem2dc); - } - return 1; - -} - -HBITMAP ske_CreateDIB32(int cx, int cy) -{ - return ske_CreateDIB32Point(cx, cy, nullptr); -} - -HBITMAP ske_CreateDIB32Point(int cx, int cy, void **bits) -{ - if (cx < 0 || cy < 0) - return nullptr; - - BITMAPINFO RGB32BitsBITMAPINFO = { 0 }; - RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - RGB32BitsBITMAPINFO.bmiHeader.biWidth = cx;//bm.bmWidth; - RGB32BitsBITMAPINFO.bmiHeader.biHeight = cy;//bm.bmHeight; - RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1; - RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32; - // pointer used for direct Bitmap pixels access - - UINT *ptPixels; - HBITMAP DirectBitmap = CreateDIBSection(nullptr, - &RGB32BitsBITMAPINFO, - DIB_RGB_COLORS, - (void **)&ptPixels, - nullptr, 0); - if ((DirectBitmap == nullptr || ptPixels == nullptr) && cx != 0 && cy != 0) { -#ifdef _DEBUG - MessageBoxA(nullptr, "Object not allocated. Check GDI object count", "ERROR", MB_OK | MB_ICONERROR); - DebugBreak(); -#endif - ; - } - else memset(ptPixels, 0, cx*cy * 4); - if (bits != nullptr) *bits = ptPixels; - return DirectBitmap; -} - -HRGN ske_CreateOpaqueRgn(uint8_t Level, bool Opaque) -{ - if (!g_pCachedWindow) - return nullptr; - - RGBQUAD *buf = (RGBQUAD *)g_pCachedWindow->hImageDIBByte; - if (buf == nullptr) - return nullptr; - - unsigned int cRect = 64; - PRGNDATA pRgnData = (PRGNDATA)mir_alloc(sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT)); - memset(pRgnData, 0, sizeof(RGNDATAHEADER)); - pRgnData->rdh.dwSize = sizeof(RGNDATAHEADER); - pRgnData->rdh.iType = RDH_RECTANGLES; - - for (int y = 0; y < g_pCachedWindow->Height; ++y) { - bool inside = false; - bool lastin = false; - unsigned int entry = 0; - - for (int x = 0; x < g_pCachedWindow->Width; ++x) { - inside = Opaque ? (buf->rgbReserved > Level) : (buf->rgbReserved < Level); - ++buf; - - if (inside != lastin) { - if (inside) { - lastin = true; - entry = x; - } - else { - if (pRgnData->rdh.nCount == cRect) { - cRect = cRect + 64; - pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT)); - } - SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, x, g_pCachedWindow->Height - y + 1); - - pRgnData->rdh.nCount++; - lastin = false; - } - } - } - - if (lastin) { - if (pRgnData->rdh.nCount == cRect) { - cRect = cRect + 64; - pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT)); - } - SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, g_pCachedWindow->Width, g_pCachedWindow->Height - y + 1); - - pRgnData->rdh.nCount++; - } - } - - HRGN hRgn = ExtCreateRegion(nullptr, sizeof(RGNDATAHEADER) + pRgnData->rdh.nCount*sizeof(RECT), (LPRGNDATA)pRgnData); - mir_free(pRgnData); - return hRgn; -} - -static int ske_DrawSkinObject(SKINDRAWREQUEST *preq, GLYPHOBJECT *pobj) -{ - HDC memdc = nullptr, glyphdc = nullptr; - int k = 0; - //BITMAP bmp = {0}; - HBITMAP membmp = nullptr, oldbmp = nullptr, oldglyph = nullptr; - uint8_t Is32Bit = 0; - RECT PRect; - POINT mode2offset = { 0 }; - int depth = 0; - int mode = 0; //0-FastDraw, 1-DirectAlphaDraw, 2-BufferedAlphaDraw - - if (!(preq && pobj)) return -1; - if ((!pobj->hGlyph || pobj->hGlyph == (HBITMAP)-1) && ((pobj->Style & 7) == ST_IMAGE || (pobj->Style & 7) == ST_FRAGMENT || (pobj->Style & 7) == ST_SOLARIZE)) return 0; - // Determine painting mode - depth = GetDeviceCaps(preq->hDC, BITSPIXEL); - depth = depth < 16 ? 16 : depth; - Is32Bit = pobj->bmBitsPixel == 32; - if ((!Is32Bit && pobj->dwAlpha == 255) && pobj->Style != ST_BRUSH) mode = 0; - else if (pobj->dwAlpha == 255 && pobj->Style != ST_BRUSH) mode = 1; - else mode = 2; - // End painting mode - - //force mode - - if (preq->rcClipRect.bottom - preq->rcClipRect.top*preq->rcClipRect.right - preq->rcClipRect.left == 0) - preq->rcClipRect = preq->rcDestRect; - IntersectRect(&PRect, &preq->rcDestRect, &preq->rcClipRect); - if (IsRectEmpty(&PRect)) { - return 0; - } - if (mode == 2) { - memdc = CreateCompatibleDC(preq->hDC); - membmp = ske_CreateDIB32(PRect.right - PRect.left, PRect.bottom - PRect.top); - oldbmp = (HBITMAP)SelectObject(memdc, membmp); - if (oldbmp == nullptr) { - SelectObject(memdc, oldbmp); - DeleteDC(memdc); - DeleteObject(membmp); - return 0; - } - } - - if (mode != 2) memdc = preq->hDC; - { - if (pobj->hGlyph && pobj->hGlyph != (HBITMAP)-1) { - glyphdc = CreateCompatibleDC(preq->hDC); - oldglyph = (HBITMAP)SelectObject(glyphdc, pobj->hGlyph); - } - // Drawing - { - RECT rFill, rGlyph, rClip; - if ((pobj->Style & 7) == ST_BRUSH) { - HBRUSH br = CreateSolidBrush(pobj->dwColor); - RECT fr; - if (mode == 2) { - SetRect(&fr, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top); - FillRect(memdc, &fr, br); - ske_SetRectOpaque(memdc, &fr); - // FillRectAlpha(memdc,&fr,pobj->dwColor|0xFF000000); - } - else { - fr = PRect; - // SetRect(&fr, 0, 0, PRect.right-PRect.left,PRect.bottom-PRect.top); - FillRect(preq->hDC, &fr, br); - } - DeleteObject(br); - k = -1; - } - else { - if (mode == 2) { - mode2offset.x = PRect.left; - mode2offset.y = PRect.top; - OffsetRect(&PRect, -mode2offset.x, -mode2offset.y); - } - rClip = (preq->rcClipRect); - - { - int lft = 0; - int top = 0; - int rgh = pobj->bmWidth; - int btm = pobj->bmHeight; - if ((pobj->Style & 7) == ST_FRAGMENT) { - lft = pobj->clipArea.x; - top = pobj->clipArea.y; - rgh = min(rgh, lft + pobj->szclipArea.cx); - btm = min(btm, top + pobj->szclipArea.cy); - } - - // Draw center... - if (1) { - rFill.top = preq->rcDestRect.top + pobj->dwTop; - rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom; - rFill.left = preq->rcDestRect.left + pobj->dwLeft; - rFill.right = preq->rcDestRect.right - pobj->dwRight; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - rGlyph.top = top + pobj->dwTop; - rGlyph.left = lft + pobj->dwLeft; - rGlyph.right = rgh - pobj->dwRight; - rGlyph.bottom = btm - pobj->dwBottom; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode, mode); - } - - // Draw top side... - if (1) { - rFill.top = preq->rcDestRect.top; - rFill.bottom = preq->rcDestRect.top + pobj->dwTop; - rFill.left = preq->rcDestRect.left + pobj->dwLeft; - rFill.right = preq->rcDestRect.right - pobj->dwRight; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - rGlyph.top = top + 0; - rGlyph.left = lft + pobj->dwLeft; - rGlyph.right = rgh - pobj->dwRight; - rGlyph.bottom = top + pobj->dwTop; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode); - } - // Draw bottom side... - if (1) { - rFill.top = preq->rcDestRect.bottom - pobj->dwBottom; - rFill.bottom = preq->rcDestRect.bottom; - rFill.left = preq->rcDestRect.left + pobj->dwLeft; - rFill.right = preq->rcDestRect.right - pobj->dwRight; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - - rGlyph.top = btm - pobj->dwBottom; - rGlyph.left = lft + pobj->dwLeft; - rGlyph.right = rgh - pobj->dwRight; - rGlyph.bottom = btm; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode); - } - // Draw left side... - if (1) { - rFill.top = preq->rcDestRect.top + pobj->dwTop; - rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom; - rFill.left = preq->rcDestRect.left; - rFill.right = preq->rcDestRect.left + pobj->dwLeft; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - - rGlyph.top = top + pobj->dwTop; - rGlyph.left = lft; - rGlyph.right = lft + pobj->dwLeft; - rGlyph.bottom = btm - pobj->dwBottom; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode); - } - - // Draw right side... - if (1) { - rFill.top = preq->rcDestRect.top + pobj->dwTop; - rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom; - rFill.left = preq->rcDestRect.right - pobj->dwRight; - rFill.right = preq->rcDestRect.right; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - - rGlyph.top = top + pobj->dwTop; - rGlyph.left = rgh - pobj->dwRight; - rGlyph.right = rgh; - rGlyph.bottom = btm - pobj->dwBottom; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode); - } - - - // Draw Top-Left corner... - if (1) { - rFill.top = preq->rcDestRect.top; - rFill.bottom = preq->rcDestRect.top + pobj->dwTop; - rFill.left = preq->rcDestRect.left; - rFill.right = preq->rcDestRect.left + pobj->dwLeft; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - - rGlyph.top = top; - rGlyph.left = lft; - rGlyph.right = lft + pobj->dwLeft; - rGlyph.bottom = top + pobj->dwTop; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode); - } - // Draw Top-Right corner... - if (1) { - rFill.top = preq->rcDestRect.top; - rFill.bottom = preq->rcDestRect.top + pobj->dwTop; - rFill.left = preq->rcDestRect.right - pobj->dwRight; - rFill.right = preq->rcDestRect.right; - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - - rGlyph.top = top; - rGlyph.left = rgh - pobj->dwRight; - rGlyph.right = rgh; - rGlyph.bottom = top + pobj->dwTop; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode); - } - - // Draw Bottom-Left corner... - if (1) { - rFill.top = preq->rcDestRect.bottom - pobj->dwBottom; - rFill.bottom = preq->rcDestRect.bottom; - rFill.left = preq->rcDestRect.left; - rFill.right = preq->rcDestRect.left + pobj->dwLeft; - - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - - rGlyph.left = lft; - rGlyph.right = lft + pobj->dwLeft; - rGlyph.top = btm - pobj->dwBottom; - rGlyph.bottom = btm; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode); - } - // Draw Bottom-Right corner... - if (1) { - rFill.top = preq->rcDestRect.bottom - pobj->dwBottom; - rFill.bottom = preq->rcDestRect.bottom; - rFill.left = preq->rcDestRect.right - pobj->dwRight; - rFill.right = preq->rcDestRect.right; - - - if (mode == 2) - OffsetRect(&rFill, -mode2offset.x, -mode2offset.y); - - rGlyph.left = rgh - pobj->dwRight; - rGlyph.right = rgh; - rGlyph.top = btm - pobj->dwBottom; - rGlyph.bottom = btm; - - k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode); - } - } - - } - - if ((k > 0 || k == -1) && mode == 2) { - { - BLENDFUNCTION bf = { AC_SRC_OVER, 0, pobj->dwAlpha, uint8_t(pobj->bmBitsPixel == 32 && pobj->Style != ST_BRUSH ? AC_SRC_ALPHA : 0) }; - OffsetRect(&PRect, mode2offset.x, mode2offset.y); - ske_AlphaBlend(preq->hDC, PRect.left, PRect.top, PRect.right - PRect.left, PRect.bottom - PRect.top, - memdc, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top, bf); - } - } - } - //free GDI resources - //--++-- - - //free GDI resources - { - - if (oldglyph) SelectObject(glyphdc, oldglyph); - if (glyphdc) DeleteDC(glyphdc); - } - if (mode == 2) { - SelectObject(memdc, oldbmp); - DeleteDC(memdc); - DeleteObject(membmp); - } - - } - if (pobj->plTextList && pobj->plTextList->realCount > 0) { - HFONT hOldFont; - for (int i = 0; i < pobj->plTextList->realCount; i++) { - GLYPHTEXT *gt = (GLYPHTEXT *)pobj->plTextList->items[i]; - if (!gt->hFont) { - if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) { - int j = 0; - for (j = 0; j < gl_plSkinFonts->realCount; j++) { - SKINFONT *sf = (SKINFONT*)gl_plSkinFonts->items[j]; - if (sf->szFontID && !mir_strcmp(sf->szFontID, gt->szFontID)) { - gt->hFont = sf->hFont; - break; - } - } - } - if (!gt->hFont) gt->hFont = (HFONT)-1; - } - if (gt->hFont != (HFONT)-1) { - RECT rc = { 0 }; - hOldFont = (HFONT)SelectObject(preq->hDC, gt->hFont); - - - - if (gt->RelativeFlags & 2) rc.left = preq->rcDestRect.right + gt->iLeft; - else if (gt->RelativeFlags & 1) rc.left = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iLeft; - else rc.left = preq->rcDestRect.left + gt->iLeft; - - if (gt->RelativeFlags & 8) rc.top = preq->rcDestRect.bottom + gt->iTop; - else if (gt->RelativeFlags & 4) rc.top = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iTop; - else rc.top = preq->rcDestRect.top + gt->iTop; - - if (gt->RelativeFlags & 32) rc.right = preq->rcDestRect.right + gt->iRight; - else if (gt->RelativeFlags & 16) rc.right = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iRight; - else rc.right = preq->rcDestRect.left + gt->iRight; - - if (gt->RelativeFlags & 128) rc.bottom = preq->rcDestRect.bottom + gt->iBottom; - else if (gt->RelativeFlags & 64) rc.bottom = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iBottom; - else rc.bottom = preq->rcDestRect.top + gt->iBottom; - - ske_AlphaTextOut(preq->hDC, gt->stText, -1, &rc, gt->dwFlags, gt->dwColor); - SelectObject(preq->hDC, hOldFont); - } - } - } - - return 0; -} - - - -int ske_AddDescriptorToSkinObjectList(SKINOBJECTDESCRIPTOR *lpDescr, SKINOBJECTSLIST *Skin) -{ - SKINOBJECTSLIST *sk = (Skin ? Skin : &g_SkinObjectList); - if (!mir_strcmpi(lpDescr->szObjectID, "_HEADER_")) - return 0; - //check if new object allready presents. - for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++) - if (!mir_strcmp(sk->pObjects[i].szObjectID, lpDescr->szObjectID)) - return 0; - // Realocated list to add space for new object - if (sk->dwObjLPAlocated + 1 > sk->dwObjLPReserved) { - sk->pObjects = (SKINOBJECTDESCRIPTOR*)mir_realloc(sk->pObjects, sizeof(SKINOBJECTDESCRIPTOR)*(sk->dwObjLPReserved + 1)/*alloc step*/); - sk->dwObjLPReserved++; - } - { //filling new objects field - sk->pObjects[sk->dwObjLPAlocated].bType = lpDescr->bType; - sk->pObjects[sk->dwObjLPAlocated].Data = nullptr; - sk->pObjects[sk->dwObjLPAlocated].szObjectID = mir_strdup(lpDescr->szObjectID); - // sk->Objects[sk->dwObjLPAlocated].szObjectName = mir_strdup(lpDescr->szObjectName); - if (lpDescr->Data != nullptr) { //Copy defaults values - switch (lpDescr->bType) { - case OT_GLYPHOBJECT: - { - GLYPHOBJECT *gl = (GLYPHOBJECT *)lpDescr->Data; - sk->pObjects[sk->dwObjLPAlocated].Data = mir_alloc(sizeof(GLYPHOBJECT)); - GLYPHOBJECT *obdat = (GLYPHOBJECT *)sk->pObjects[sk->dwObjLPAlocated].Data; - memcpy(obdat, gl, sizeof(GLYPHOBJECT)); - if (gl->szFileName != nullptr) { - obdat->szFileName = mir_strdup(gl->szFileName); - replaceStr(gl->szFileName, nullptr); - } - else obdat->szFileName = nullptr; - - obdat->hGlyph = nullptr; - break; - } - } - - } - } - sk->dwObjLPAlocated++; - return 1; -} - -static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin) -{ - SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin; - return skin_FindObjectByRequest((char *)szName, sk->pMaskList); -} - -static SKINOBJECTDESCRIPTOR* ske_FindObjectByMask(MODERNMASK *pModernMask, SKINOBJECTSLIST *Skin) -{ - SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin; - return sk->pMaskList ? skin_FindObjectByMask(pModernMask, sk->pMaskList) : nullptr; -} - -SKINOBJECTDESCRIPTOR* ske_FindObjectByName(const char *szName, uint8_t objType, SKINOBJECTSLIST *Skin) -{ - SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin; - for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++) { - if (sk->pObjects[i].bType == objType || objType == OT_ANY) { - if (!mir_strcmp(sk->pObjects[i].szObjectID, szName)) - return &(sk->pObjects[i]); - } - } - return nullptr; -} - -////////////////////////////////////////////////////////////////////////// -// Paint glyph -// wParam - LPSKINDRAWREQUEST -// lParam - possible direct pointer to modern mask -////////////////////////////////////////////////////////////////////////// - -INT_PTR ske_Service_DrawGlyph(WPARAM wParam, LPARAM lParam) -{ - auto *preq = (SKINDRAWREQUEST *)wParam; - if (preq == nullptr) - return -1; - - mir_cslock lck(cs_SkinChanging); - - SKINOBJECTDESCRIPTOR *pgl = (lParam ? ske_FindObjectByMask((MODERNMASK*)lParam, nullptr) : ske_FindObject(preq->szObjectID, nullptr)); - if (pgl == nullptr) return -1; - if (pgl->Data == nullptr) return -1; - - GLYPHOBJECT *gl = (GLYPHOBJECT*)pgl->Data; - int iStyle = gl->Style & 7; - if (iStyle == ST_SKIP) - return ST_SKIP; - - if (gl->hGlyph == nullptr && gl->hGlyph != (HBITMAP)-1 && (iStyle == ST_IMAGE || iStyle == ST_FRAGMENT || iStyle == ST_SOLARIZE)) { - if (gl->szFileName) { - gl->hGlyph = ske_LoadGlyphImage(_A2T(gl->szFileName)); - if (gl->hGlyph) { - BITMAP bmp = { 0 }; - GetObject(gl->hGlyph, sizeof(BITMAP), &bmp); - gl->bmBitsPixel = (uint8_t)bmp.bmBitsPixel; - gl->bmHeight = bmp.bmHeight; - gl->bmWidth = bmp.bmWidth; - } - else gl->hGlyph = (HBITMAP)-1; //invalid - } - } - return ske_DrawSkinObject(preq, gl); -} - - -void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult) -{ - BITMAP bmp; - BOOL flag = FALSE; - uint8_t *pBitmapBits; - uint32_t Len; - int bh, bw, y, x; - - GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp); - bh = bmp.bmHeight; - bw = bmp.bmWidth; - Len = bh * bw * 4; - flag = (bmp.bmBits == nullptr); - if (flag) { - pBitmapBits = (LPBYTE)mir_alloc(Len); - GetBitmapBits(hbmp, Len, pBitmapBits); - } - else - pBitmapBits = (uint8_t *)bmp.bmBits; - for (y = 0; y < bh; ++y) { - uint8_t *pPixel = pBitmapBits + bw * 4 * y; - - for (x = 0; x < bw; ++x) { - if (Mult) { - pPixel[0] = pPixel[0] * pPixel[3] / 255; - pPixel[1] = pPixel[1] * pPixel[3] / 255; - pPixel[2] = pPixel[2] * pPixel[3] / 255; - } - else { - pPixel[3] = 255; - } - pPixel += 4; - } - } - if (flag) { - Len = SetBitmapBits(hbmp, Len, pBitmapBits); - mir_free(pBitmapBits); - } - return; -} - -int ske_GetFullFilename(wchar_t *buf, const wchar_t *file, wchar_t *skinfolder, BOOL madeAbsolute) -{ - wchar_t *SkinPlace = db_get_wsa(0, SKIN, "SkinFolder"); - if (SkinPlace == nullptr) - SkinPlace = mir_wstrdup(L"\\Skin\\default"); - - wchar_t b2[MAX_PATH]; - if (file[0] != '\\' && file[1] != ':') - mir_snwprintf(b2, L"%s\\%s", (skinfolder == nullptr) ? SkinPlace : ((INT_PTR)skinfolder != -1) ? skinfolder : L"", file); - else - mir_wstrncpy(b2, file, _countof(b2)); - - if (madeAbsolute) { - if (b2[0] == '\\' && b2[1] != '\\') - PathToAbsoluteW(b2 + 1, buf); - else - PathToAbsoluteW(b2, buf); - } - else mir_wstrncpy(buf, b2, MAX_PATH); - - mir_free(SkinPlace); - return 0; -} - -/* -This function is required to load TGA to dib buffer myself -Major part of routines is from http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga.c -*/ - -static BOOL ske_ReadTGAImageData(void *From, uint32_t fromSize, uint8_t *destBuf, uint32_t bufSize, BOOL RLE) -{ - uint8_t *pos = destBuf; - uint8_t *from = fromSize ? (uint8_t *)From : nullptr; - FILE *fp = !fromSize ? (FILE *)From : nullptr; - uint32_t destCount = 0; - uint32_t fromCount = 0; - if (!RLE) { - while (((from && fromCount < fromSize) || (fp && fromCount < bufSize)) - && (destCount < bufSize)) { - uint8_t r = from ? from[fromCount++] : (uint8_t)fgetc(fp); - uint8_t g = from ? from[fromCount++] : (uint8_t)fgetc(fp); - uint8_t b = from ? from[fromCount++] : (uint8_t)fgetc(fp); - uint8_t a = from ? from[fromCount++] : (uint8_t)fgetc(fp); - pos[destCount++] = r; - pos[destCount++] = g; - pos[destCount++] = b; - pos[destCount++] = a; - - if (destCount > bufSize) break; - if (from) if (fromCount < fromSize) break; - } - } - else { - uint8_t rgba[4]; - uint8_t packet_header; - uint8_t *ptr = pos; - uint8_t size; - int i; - while (ptr < pos + bufSize) { - /* read first byte */ - packet_header = from ? from[fromCount] : (uint8_t)fgetc(fp); - if (from) from++; - size = 1 + (packet_header & 0x7f); - if (packet_header & 0x80) { - /* run-length packet */ - if (from) { - *((uint32_t*)rgba) = *((uint32_t*)(from + fromCount)); - fromCount += 4; - } - else fread(rgba, sizeof(uint8_t), 4, fp); - for (i = 0; i < size; ++i, ptr += 4) { - ptr[2] = rgba[2]; - ptr[1] = rgba[1]; - ptr[0] = rgba[0]; - ptr[3] = rgba[3]; - } - } - else { /* not run-length packet */ - for (i = 0; i < size; ++i, ptr += 4) { - ptr[0] = from ? from[fromCount++] : (uint8_t)fgetc(fp); - ptr[1] = from ? from[fromCount++] : (uint8_t)fgetc(fp); - ptr[2] = from ? from[fromCount++] : (uint8_t)fgetc(fp); - ptr[3] = from ? from[fromCount++] : (uint8_t)fgetc(fp); - } - } - } - } - return TRUE; -} - -static HBITMAP ske_LoadGlyphImage_TGA(const wchar_t *szFilename) -{ - uint8_t *colormap = nullptr; - int cx = 0, cy = 0; - BOOL err = FALSE; - tga_header_t header; - if (!szFilename) return nullptr; - if (!wildcmpiw(szFilename, L"*\\*%.tga")) { - //Loading TGA image from file - FILE *fp = _wfopen(szFilename, L"rb"); - if (!fp) { - TRACEVAR("error: couldn't open \"%s\"!\n", szFilename); - return nullptr; - } - /* read header */ - fread(&header, sizeof(tga_header_t), 1, fp); - if ((header.pixel_depth != 32) || ((header.image_type != 10) && (header.image_type != 2))) { - fclose(fp); - return nullptr; - } - - /*memory allocation */ - colormap = (uint8_t*)mir_alloc(header.width*header.height * 4); - cx = header.width; - cy = header.height; - fseek(fp, header.id_lenght, SEEK_CUR); - fseek(fp, header.cm_length, SEEK_CUR); - err = !ske_ReadTGAImageData((void*)fp, 0, colormap, header.width*header.height * 4, header.image_type == 10); - fclose(fp); - } - else { - /* reading from resources IDR_TGA_DEFAULT_SKIN */ - HRSRC hRSrc = FindResourceA(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_TGA_DEFAULT_SKIN), "TGA"); - if (!hRSrc) return nullptr; - HGLOBAL hRes = LoadResource(g_plugin.getInst(), hRSrc); - if (!hRes) return nullptr; - uint32_t size = SizeofResource(g_plugin.getInst(), hRSrc); - uint8_t *mem = (uint8_t*)LockResource(hRes); - if (size > sizeof(header)) { - tga_header_t *tgahdr = (tga_header_t*)mem; - if (tgahdr->pixel_depth == 32 && (tgahdr->image_type == 2 || tgahdr->image_type == 10)) { - colormap = (uint8_t*)mir_alloc(tgahdr->width*tgahdr->height * 4); - cx = tgahdr->width; - cy = tgahdr->height; - ske_ReadTGAImageData((void*)(mem + sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), size - (sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), colormap, cx*cy * 4, tgahdr->image_type == 10); - } - } - FreeResource(hRes); - } - - if (colormap == nullptr) - return nullptr; - - // create dib section - uint8_t *pt; - HBITMAP hbmp = ske_CreateDIB32Point(cx, cy, (void**)&pt); - if (hbmp) - memcpy(pt, colormap, cx*cy * 4); - mir_free(colormap); - return hbmp; -} - -static HBITMAP ske_LoadGlyphImageByDecoders(const wchar_t *tszFileName) -{ - if (!wcschr(tszFileName, '%') && !PathFileExists(tszFileName)) - return nullptr; - - const wchar_t *ext = wcsrchr(tszFileName, '.'); - if (ext == nullptr) - return nullptr; - - BITMAP bmpInfo; - HBITMAP hBitmap; - bool f = false; - - if (!mir_wstrcmpi(ext, L".tga")) { - hBitmap = ske_LoadGlyphImage_TGA(tszFileName); - f = true; - } - else hBitmap = Bitmap_Load(tszFileName); - - if (hBitmap == nullptr) - return nullptr; - - GetObject(hBitmap, sizeof(BITMAP), &bmpInfo); - if (bmpInfo.bmBitsPixel == 32) - ske_PreMultiplyChannels(hBitmap, f); - else { - HDC dc32 = CreateCompatibleDC(nullptr); - HDC dc24 = CreateCompatibleDC(nullptr); - HBITMAP hBitmap32 = ske_CreateDIB32(bmpInfo.bmWidth, bmpInfo.bmHeight); - HBITMAP obmp24 = (HBITMAP)SelectObject(dc24, hBitmap); - HBITMAP obmp32 = (HBITMAP)SelectObject(dc32, hBitmap32); - BitBlt(dc32, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, dc24, 0, 0, SRCCOPY); - SelectObject(dc24, obmp24); - SelectObject(dc32, obmp32); - DeleteDC(dc24); - DeleteDC(dc32); - DeleteObject(hBitmap); - hBitmap = hBitmap32; - ske_PreMultiplyChannels(hBitmap, 0); - } - return hBitmap; -} - -static HBITMAP ske_skinLoadGlyphImage(const wchar_t *tszFileName) -{ - if (!wildcmpiw(tszFileName, L"*.tga")) - return GDIPlus_LoadGlyphImage(tszFileName); - - return ske_LoadGlyphImageByDecoders(tszFileName); -} - -HBITMAP ske_LoadGlyphImage(const wchar_t *tszFileName) -{ - // try to find image in loaded - wchar_t szFile[MAX_PATH] = { 0 }; - ske_GetFullFilename(szFile, tszFileName, g_SkinObjectList.szSkinPlace, TRUE); - - mir_cslock lck(cs_SkinChanging); - - if (pLoadedImages) { - for (uint32_t i = 0; i < dwLoadedImagesCount; i++) { - if (!mir_wstrcmpi(pLoadedImages[i].szFileName, szFile)) { - pLoadedImages[i].dwLoadedTimes++; - return pLoadedImages[i].hGlyph; - } - } - } - - // load new image - HBITMAP hbmp = ske_skinLoadGlyphImage(szFile); - if (hbmp == nullptr) - return nullptr; - - // add to loaded list - if (dwLoadedImagesCount + 1 > dwLoadedImagesAlocated) { - pLoadedImages = (GLYPHIMAGE*)mir_realloc(pLoadedImages, sizeof(GLYPHIMAGE)*(dwLoadedImagesCount + 1)); - if (!pLoadedImages) - return nullptr; - dwLoadedImagesAlocated++; - } - - pLoadedImages[dwLoadedImagesCount].dwLoadedTimes = 1; - pLoadedImages[dwLoadedImagesCount].hGlyph = hbmp; - pLoadedImages[dwLoadedImagesCount].szFileName = mir_wstrdup(szFile); - dwLoadedImagesCount++; - return hbmp; -} - -int ske_UnloadGlyphImage(HBITMAP hbmp) -{ - for (uint32_t i = 0; i < dwLoadedImagesCount && pLoadedImages; i++) { - if (hbmp != pLoadedImages[i].hGlyph) - continue; - - pLoadedImages[i].dwLoadedTimes--; - if (pLoadedImages[i].dwLoadedTimes == 0) { - LPGLYPHIMAGE gl = &(pLoadedImages[i]); - replaceStrW(gl->szFileName, nullptr); - memmove(&(pLoadedImages[i]), &(pLoadedImages[i + 1]), sizeof(GLYPHIMAGE) * (dwLoadedImagesCount - i - 1)); - dwLoadedImagesCount--; - DeleteObject(hbmp); - if (dwLoadedImagesCount == 0) { - dwLoadedImagesAlocated = 0; - mir_free_and_nil(pLoadedImages); - } - } - return 0; - } - DeleteObject(hbmp); - return 0; -} - -int ske_UnloadSkin(SKINOBJECTSLIST *Skin) -{ - ClearMaskList(Skin->pMaskList); - - //clear font list - if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) { - for (int i = 0; i < gl_plSkinFonts->realCount; i++) { - SKINFONT *sf = (SKINFONT *)gl_plSkinFonts->items[i]; - if (sf) { - mir_free(sf->szFontID); - DeleteObject(sf->hFont); - mir_free(sf); - } - } - List_Destroy(gl_plSkinFonts); - mir_free_and_nil(gl_plSkinFonts); - } - - replaceStrW(Skin->szSkinPlace, nullptr); - if (Skin->pTextList) List_Destroy(Skin->pTextList); - mir_free_and_nil(Skin->pTextList); - ModernSkinButtonDeleteAll(); - if (Skin->dwObjLPAlocated == 0) - return 0; - - for (uint32_t i = 0; i < Skin->dwObjLPAlocated; i++) { - switch (Skin->pObjects[i].bType) { - case OT_GLYPHOBJECT: - GLYPHOBJECT *dt = (GLYPHOBJECT*)Skin->pObjects[i].Data; - if (dt->hGlyph && dt->hGlyph != (HBITMAP)-1) - ske_UnloadGlyphImage(dt->hGlyph); - dt->hGlyph = nullptr; - replaceStr(dt->szFileName, nullptr); - - if (dt->plTextList && dt->plTextList->realCount > 0) { - for (int k = 0; k < dt->plTextList->realCount; k++) { - GLYPHTEXT *gt = (GLYPHTEXT *)dt->plTextList->items[k]; - if (gt) { - mir_free(gt->stText); - mir_free(gt->stValueText); - mir_free(gt->szFontID); - mir_free(gt->szGlyphTextID); - mir_free(gt); - } - } - List_Destroy(dt->plTextList); - mir_free(dt->plTextList); - } - mir_free(dt); - break; - } - replaceStr(Skin->pObjects[i].szObjectID, nullptr); - } - mir_free_and_nil(Skin->pObjects); - Skin->pTextList = nullptr; - Skin->dwObjLPAlocated = 0; - Skin->dwObjLPReserved = 0; - return 0; -} - -static void RegisterMaskByParce(const char *szSetting, char *szValue, SKINOBJECTSLIST *pSkin) -{ - size_t i, val_len = mir_strlen(szValue); - - for (i = 0; i < val_len; i++) - if (szValue[i] == ':') - break; - - if (i < val_len) { - char *Obj, *Mask; - int res; - uint32_t ID = atoi(szSetting + 1); - Mask = szValue + i + 1; - Obj = (char*)mir_alloc(i + 2); - mir_strncpy(Obj, szValue, i + 1); - Obj[i + 1] = '\0'; - res = AddStrModernMaskToList(ID, Mask, Obj, pSkin->pMaskList); - mir_free(Obj); - } -} - -static int ske_ProcessLoadindString(const char *szSetting, char *szValue) -{ - if (!pCurrentSkin) return 0; - if (szSetting[0] == '$') - RegisterObjectByParce((char *)szSetting, szValue); - else if (szSetting[0] == '#') - RegisterButtonByParce((char *)szSetting, szValue); - else if (szSetting[0] == '@') - RegisterMaskByParce((char *)szSetting, szValue, pCurrentSkin); /// - else if (szSetting[0] == 't') - ske_AddParseTextGlyphObject((char*)szSetting, szValue, pCurrentSkin); - else if (szSetting[0] == 'f') - ske_AddParseSkinFont((char*)szSetting, szValue); - else return 0; - return 1; -} - -static int ske_enumdb_SkinObjectsProc(const char *szSetting, void *) -{ - ptrA value(db_get_sa(0, SKIN, szSetting)); - ske_ProcessLoadindString(szSetting, value); - return 0; -} - -static int ske_SortTextGlyphObjectFunc(void *first, void *second) -{ - GLYPHTEXT *p1 = *(GLYPHTEXT**)first, *p2 = *(GLYPHTEXT**)second; - return mir_strcmp(p1->szGlyphTextID, p2->szGlyphTextID); -} - -static void ske_LinkSkinObjects(SKINOBJECTSLIST *pObjectList) -{ - // LINK Mask with objects - for (uint32_t i = 0; i < pObjectList->pMaskList->dwMaskCnt; i++) { - MODERNMASK *mm = &(pObjectList->pMaskList->pl_Masks[i]); - void *pObject = (void *)ske_FindObjectByName(mm->szObjectName, OT_ANY, (SKINOBJECTSLIST *)pObjectList); - replaceStr(mm->szObjectName, nullptr); - mm->bObjectFound = TRUE; - mm->pObject = pObject; - } - - if (pObjectList->pTextList) { - // LINK Text with objects - for (int i = 0; i < pObjectList->pTextList->realCount; i++) { - GLYPHTEXT *glText = (GLYPHTEXT *)pObjectList->pTextList->items[i]; - SKINOBJECTDESCRIPTOR *lpobj = ske_FindObjectByName(glText->szObjectName, OT_GLYPHOBJECT, pObjectList); - replaceStr(glText->szObjectName, nullptr); - GLYPHOBJECT *globj = nullptr; - if (lpobj) - globj = (GLYPHOBJECT*)lpobj->Data; - if (globj) { - if (!globj->plTextList) { - globj->plTextList = List_Create(0, 1); - globj->plTextList->sortFunc = ske_SortTextGlyphObjectFunc; - } - List_Insert(globj->plTextList, (void*)glText, globj->plTextList->realCount); - qsort(globj->plTextList->items, globj->plTextList->realCount, sizeof(GLYPHTEXT*), (int(*)(const void*, const void*))globj->plTextList->sortFunc); - pObjectList->pTextList->items[i] = nullptr; - } - else { - GLYPHTEXT *gt = glText; - if (gt) { - mir_free(gt->stText); - mir_free(gt->stValueText); - mir_free(gt->szFontID); - mir_free(gt->szGlyphTextID); - mir_free(gt); - } - } - } - List_Destroy(pObjectList->pTextList); - mir_free_and_nil(pObjectList->pTextList); - } -} - -// Getting skin objects and masks from DB - -static int ske_GetSkinFromDB(char *, SKINOBJECTSLIST *Skin) -{ - if (Skin == nullptr) return 0; - ske_UnloadSkin(Skin); - g_CluiData.fDisableSkinEngine = db_get_b(0, "ModernData", "DisableEngine", SETTING_DISABLESKIN_DEFAULT); - // window borders - if (g_CluiData.fDisableSkinEngine) { - g_CluiData.LeftClientMargin = 0; - g_CluiData.RightClientMargin = 0; - g_CluiData.TopClientMargin = 0; - g_CluiData.BottomClientMargin = 0; - return 0; - } - - // window borders - g_CluiData.LeftClientMargin = (int)db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT); - g_CluiData.RightClientMargin = (int)db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT); - g_CluiData.TopClientMargin = (int)db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT); - g_CluiData.BottomClientMargin = (int)db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT); - - Skin->pMaskList = (LISTMODERNMASK*)mir_alloc(sizeof(LISTMODERNMASK)); - memset(Skin->pMaskList, 0, sizeof(LISTMODERNMASK)); - Skin->szSkinPlace = db_get_wsa(0, SKIN, "SkinFolder"); - if (!Skin->szSkinPlace || (wcschr(Skin->szSkinPlace, '%') && !db_get_b(0, SKIN, "Modified", 0))) { - BOOL bOnlyObjects = FALSE; - if (Skin->szSkinPlace && wcschr(Skin->szSkinPlace, '%')) - bOnlyObjects = TRUE; - mir_free(Skin->szSkinPlace); - Skin->szSkinPlace = mir_wstrdup(L"%Default%"); - ske_LoadSkinFromResource(bOnlyObjects); - } - - // Load objects - pCurrentSkin = Skin; - db_enum_settings(0, ske_enumdb_SkinObjectsProc, SKIN); - - SortMaskList(pCurrentSkin->pMaskList); - ske_LinkSkinObjects(pCurrentSkin); - - // Load Masks - return 0; -} - -// surrogate to be called from outside -void ske_LoadSkinFromDB(void) -{ - ske_GetSkinFromDB(SKIN, &g_SkinObjectList); - g_CluiData.dwKeyColor = db_get_dw(0, "ModernSettings", "KeyColor", (uint32_t)SETTING_KEYCOLOR_DEFAULT); -} - -static int ske_LoadSkinFromResource(BOOL bOnlyObjects) -{ - IniParser parser(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF", bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS); - if (parser.CheckOK()) { - db_delete_module(0, "ModernSkin"); - db_set_s(0, SKIN, "SkinFolder", "%Default%"); - db_set_s(0, SKIN, "SkinFile", "%Default%"); - parser.Parse(IniParser::WriteStrToDb, 0); - } - return 0; -} - -// Load data from ini file -int ske_LoadSkinFromIniFile(wchar_t *szFileName, BOOL bOnlyObjects) -{ - if (wcschr(szFileName, '%')) - return ske_LoadSkinFromResource(bOnlyObjects); - - IniParser parser(szFileName, bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS); - if (!parser.CheckOK()) - return 0; - - db_delete_module(0, "ModernSkin"); - - wchar_t skinFolder[MAX_PATH], skinFile[MAX_PATH]; - IniParser::GetSkinFolder(szFileName, skinFolder); - PathToRelativeW(szFileName, skinFile); - - db_set_ws(0, SKIN, "SkinFolder", skinFolder); - db_set_ws(0, SKIN, "SkinFile", skinFile); - - parser.Parse(IniParser::WriteStrToDb, 1); - return 0; -} - -BOOL ske_TextOut(HDC hdc, int x, int y, LPCTSTR lpString, int nCount) -{ - SIZE sz; - GetTextExtentPoint32(hdc, lpString, nCount, &sz); - - RECT rc = { 0 }; - SetRect(&rc, x, y, x + sz.cx, y + sz.cy); - ske_DrawText(hdc, lpString, nCount, &rc, DT_NOCLIP | DT_SINGLELINE | DT_LEFT); - return 1; -} - -static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM) -{ - if (!wParam) return 0; - - AlphaTextOutParams ap = *(AlphaTextOutParams*)wParam; - return ske_AlphaTextOut(ap.hDC, ap.lpString, ap.nCount, ap.lpRect, ap.format, ap.ARGBcolor); -} - -static __inline void ske_SetMatrix(sbyte *matrix, - sbyte a, sbyte b, sbyte c, - sbyte d, sbyte e, sbyte f, - sbyte g, sbyte h, sbyte i) -{ - matrix[0] = a; matrix[1] = b; matrix[2] = c; - matrix[3] = d; matrix[4] = e; matrix[5] = f; - matrix[6] = g; matrix[7] = h; matrix[8] = i; -} - -bool ske_ResetTextEffect(HDC hdc) -{ - int idx = arEffectStack.getIndex((EFFECTSSTACKITEM*)&hdc); - if (idx == -1) - return false; - - mir_free(arEffectStack[idx]); - arEffectStack.remove(idx); - return true; -} - -bool ske_SelectTextEffect(HDC hdc, uint8_t EffectID, uint32_t FirstColor, uint32_t SecondColor) -{ - if (EffectID > MAXPREDEFINEDEFFECTS) - return false; - - if (EffectID == -1) - return ske_ResetTextEffect(hdc); - - EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc); - if (effect == nullptr) { - effect = (EFFECTSSTACKITEM *)mir_alloc(sizeof(EFFECTSSTACKITEM)); - effect->hdc = hdc; - arEffectStack.insert(effect); - } - - effect->EffectID = EffectID; - effect->FirstColor = FirstColor; - effect->SecondColor = SecondColor; - return true; -} - -static bool ske_GetTextEffect(HDC hdc, MODERNEFFECT *modernEffect) -{ - if (!modernEffect) - return false; - - EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc); - if (effect == nullptr) - return false; - - modernEffect->EffectID = effect->EffectID; - modernEffect->EffectColor1 = effect->FirstColor; - modernEffect->EffectColor2 = effect->SecondColor; - modernEffect->EffectMatrix = ModernEffectsEnum[effect->EffectID]; - return true; -} - -static bool ske_DrawTextEffect(uint8_t *destPt, uint8_t *maskPt, uint32_t width, uint32_t height, MODERNEFFECT *effect) -{ - sbyte *buf; - sbyte *outbuf; - sbyte *bufline, *buflineTop, *buflineMid; - int sign = 0; - uint8_t *maskline, *destline; - uint8_t al, rl, gl, bl, ad, rd, gd, bd; - int k = 0; - uint32_t x, y; - sbyte *matrix; - uint8_t mcTopStart; - uint8_t mcBottomEnd; - uint8_t mcLeftStart; - uint8_t mcRightEnd; - uint8_t effectCount; - int minX = width; - int maxX = 0; - int minY = height; - int maxY = 0; - if (effect->EffectID == 0xFF) return false; - if (!width || !height) return false; - if (!destPt) return false; - buf = (sbyte*)mir_alloc(width*height*sizeof(uint8_t)); - { - matrix = effect->EffectMatrix.matrix; - mcTopStart = 2 - effect->EffectMatrix.topEffect; - mcBottomEnd = 3 + effect->EffectMatrix.bottomEffect; - mcLeftStart = 2 - effect->EffectMatrix.leftEffect; - mcRightEnd = 3 + effect->EffectMatrix.rightEffect; - effectCount = effect->EffectMatrix.cycleCount; - } - al = 255 - ((uint8_t)(effect->EffectColor1 >> 24)); - rl = GetRValue(effect->EffectColor1); - gl = GetGValue(effect->EffectColor1); - bl = GetBValue(effect->EffectColor1); - ad = 255 - ((uint8_t)(effect->EffectColor2 >> 24)); - rd = GetRValue(effect->EffectColor2); - gd = GetGValue(effect->EffectColor2); - bd = GetBValue(effect->EffectColor2); - - // Fill buffer by mid values of image - for (y = 0; y < height; y++) { - bufline = buf + y*width; - maskline = maskPt + ((y*width) << 2); - for (x = 0; x < width; x++) { - uint8_t a = (sbyte)(uint32_t)((maskline[0] + maskline[2] + maskline[1] + maskline[1]) >> 4); - *bufline = a; - if (a != 0) { - minX = min((int)x, minX); - minY = min((int)y, minY); - maxX = max((int)x, maxX); - maxY = max((int)y, maxY); - } - bufline++; - maskline += 4; - } - } - // Here perform effect on buffer and place results to outbuf - for (k = 0; k < (effectCount & 0x7F); k++) { - minX = max(0, minX + mcLeftStart - 2); - minY = max(0, minY + mcTopStart - 2); - maxX = min((int)width, maxX + mcRightEnd - 1); - maxY = min((int)height, maxY + mcBottomEnd - 1); - - outbuf = (sbyte*)mir_alloc(width*height*sizeof(sbyte)); - memset(outbuf, 0, width*height*sizeof(sbyte)); - for (y = (uint32_t)minY; y < (uint32_t)maxY; y++) { - int val; - bufline = outbuf + y*width + minX; - buflineMid = buf + y*width + minX; - for (x = (uint32_t)minX; x < (uint32_t)maxX; x++) { - int matrixHor, matrixVer; - val = 0; - for (matrixVer = mcTopStart; matrixVer < mcBottomEnd; matrixVer++) { - int buflineStep = width*(matrixVer - 2); - int as = y + matrixVer - 2; - sbyte *buflineTopS = nullptr; - if (as >= 0 && (uint32_t)as < height) buflineTopS = buflineMid + buflineStep; - - for (matrixHor = mcLeftStart; matrixHor < mcRightEnd; matrixHor++) { - buflineTop = buflineTopS; - int a = x + matrixHor - 2; - if (buflineTop && a >= 0 && (uint32_t)a < width) buflineTop += matrixHor - 2; - else buflineTop = nullptr; - if (buflineTop) - val += ((*buflineTop)*matrix[matrixVer * 5 + matrixHor]); - } - } - val = (val + 1) >> 5; - *bufline = (sbyte)((val>127) ? 127 : (val < -125) ? -125 : val); - bufline++; - buflineMid++; - } - } - mir_free(buf); - buf = outbuf; - } - { - uint8_t r1, b1, g1, a1; - b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1; - //perform out to dest - for (y = 0; y < height; y++) { - bufline = buf + y*width; - destline = destPt + ((y*width) << 2); - for (x = 0; x < width; x++) { - sbyte val = *bufline; - uint8_t absVal = ((val < 0) ? -val : val); - - if (val != 0) { - if (val > 0 && sign < 0) { - b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1; - } - else if (val < 0 && sign>0) { - b1 = bd; r1 = rd; g1 = gd; a1 = ad; sign = -1; - } - - absVal = absVal*a1 / 255; - - destline[0] = ((destline[0] * (128 - absVal)) + absVal*b1) >> 7; - destline[1] = ((destline[1] * (128 - absVal)) + absVal*g1) >> 7; - destline[2] = ((destline[2] * (128 - absVal)) + absVal*r1) >> 7; - destline[3] += ((255 - destline[3])*(a1*absVal)) / 32640; - } - bufline++; - destline += 4; - } - } - mir_free(buf); - } - return false; -} - -static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor) -{ - if (!(lpString && lpRect)) - return 0; - - // Step first fill fast calc correction tables: - static bool _tables_empty = true; - static uint8_t gammaTbl[256]; // Gamma correction table - static uint16_t blueMulTbl[256]; // blue coefficient multiplication table - static uint16_t greenMulTbl[256]; // green coefficient multiplication table - static uint16_t redMulTbl[256]; // red coefficient multiplication table - if (_tables_empty) { - // fill tables - double gammaCfPw = 1000 / (double)DBGetContactSettingRangedWord(0, "ModernData", "AlphaTextOutGamma", 700, 1, 5000); - uint8_t blueCf = db_get_b(0, "ModernData", "AlphaTextOutBlueCorrection", 28); - uint8_t redCf = db_get_b(0, "ModernData", "AlphaTextOutRed Correction", 77); - uint8_t greenCf = db_get_b(0, "ModernData", "AlphaTextOutGreen Correction", 151); - - for (int i = 0; i < 256; i++) { - gammaTbl[i] = (uint8_t)(255 * pow((double)i / 255, gammaCfPw)); - blueMulTbl[i] = i * blueCf; - redMulTbl[i] = i * redCf; - greenMulTbl[i] = i * greenCf; - } - } - - // Calc len of input string - if (nCount == -1) - nCount = (int)mir_wstrlen(lpString); - - // retrieve destination bitmap bits - HBITMAP hDestBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP); - BITMAP bmpDest; - GetObject(hDestBitmap, sizeof(BITMAP), &bmpDest); - - bool destHasNotDIB = (bmpDest.bmBits == nullptr); - uint8_t *pDestBits; - if (destHasNotDIB) { - pDestBits = (uint8_t*)mir_alloc(bmpDest.bmHeight * bmpDest.bmWidthBytes); - GetBitmapBits(hDestBitmap, bmpDest.bmHeight*bmpDest.bmWidthBytes, pDestBits); - } - else - pDestBits = (uint8_t*)bmpDest.bmBits; - - // Creating offscreen buffer - HDC hOffscreenDC = CreateCompatibleDC(hDC); - - // Font to be used to draw text - HFONT hFont = (HFONT)GetCurrentObject(hDC, OBJ_FONT); - HFONT hOldOffscreenFont = (HFONT)SelectObject(hOffscreenDC, hFont); - - // Calculating text geometric size - RECT workRect = *lpRect; - int workRectWidth = workRect.right - workRect.left; - int workRectHeight = workRect.bottom - workRect.top; - if (workRectWidth <= 0 || workRectHeight <= 0) { - if (destHasNotDIB) - mir_free(pDestBits); - return 0; - } - - SIZE textSize; - GetTextExtentPoint32(hOffscreenDC, lpString, nCount, &textSize); - - LPCTSTR lpWorkString = lpString; - BOOL bNeedFreeWorkString = FALSE; - - // if we need to cut the text with ellipsis - if ((format & DT_END_ELLIPSIS) && textSize.cx > workRectWidth) { - // Calc geometric width of ellipsis - SIZE szEllipsis; - GetTextExtentPoint32A(hOffscreenDC, "...", 3, &szEllipsis); - szEllipsis.cx++; // CORRECTION: some width correction - - // Calc count of visible chars - int visibleCharCount = nCount; - if (workRectWidth > szEllipsis.cx) - GetTextExtentExPoint(hOffscreenDC, lpString, nCount, workRectWidth - szEllipsis.cx, &visibleCharCount, nullptr, &textSize); - else - GetTextExtentExPoint(hOffscreenDC, lpString, nCount, 0, &visibleCharCount, nullptr, &textSize); - - // replace end of string by elipsis - bNeedFreeWorkString = TRUE; - lpWorkString = (wchar_t*)mir_alloc((visibleCharCount + 4) * sizeof(wchar_t)); - - memcpy((void*)lpWorkString, lpString, visibleCharCount * sizeof(wchar_t)); - memcpy((void*)(lpWorkString + visibleCharCount), L"...", 4 * sizeof(wchar_t)); // 3 + 1 - - nCount = visibleCharCount + 3; - } - - // Calc sizes and offsets - - textSize.cx += 2; // CORRECTION: for italic - - int drx = 0; // x-axis offset of draw point - - if (workRectWidth > textSize.cx) { - if (format & (DT_RIGHT | DT_RTLREADING)) - drx = workRectWidth - textSize.cx; - else if (format & DT_CENTER) - drx = (workRectWidth - textSize.cx) >> 1; - } - else textSize.cx = workRectWidth; - - int dry = 0; // y-axis offset of draw point - - if (workRectHeight > textSize.cy) { - if (format & DT_BOTTOM) - dry = workRectHeight - textSize.cy; - else if (format & DT_VCENTER) - dry = (workRectHeight - textSize.cy) >> 1; - } - else textSize.cy = workRectHeight; - - textSize.cx += 4; // CORRECTION: for effects ??? - textSize.cy += 4; // CORRECTION: for effects ??? - - if (textSize.cx > 0 && textSize.cy > 0) { // Ok we need to paint - // probably here are mess ofscreen and temp buff dc - - //Create bitmap image for offscreen - uint8_t *bits = nullptr; - HBITMAP hbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bits); - if (bits != nullptr) { - HBITMAP holdbmp = (HBITMAP)SelectObject(hOffscreenDC, hbmp); - - //Create buffer bitmap image for temp text - uint8_t *bufbits = nullptr; - HBITMAP bufbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bufbits); - if (bufbits != nullptr) { - HDC bufDC = CreateCompatibleDC(hDC); - HBITMAP bufoldbmp = (HBITMAP)SelectObject(bufDC, bufbmp); - HFONT hOldBufFont = (HFONT)SelectObject(bufDC, hFont); - SetBkColor(bufDC, RGB(0, 0, 0)); - SetTextColor(bufDC, RGB(255, 255, 255)); - - // Copy from destination to temp buffer - BitBlt(hOffscreenDC, 0, 0, textSize.cx, textSize.cy, hDC, workRect.left + drx - 2, workRect.top + dry - 2, SRCCOPY); - - //Draw text on offscreen bitmap - TextOut(bufDC, 2, 2, lpWorkString, nCount); - - MODERNEFFECT effect; - if (ske_GetTextEffect(hDC, &effect)) - ske_DrawTextEffect(bits, bufbits, textSize.cx, textSize.cy, &effect); - - // RenderText - RECT drawRect; - drawRect.left = 0; drawRect.top = 0; - drawRect.right = textSize.cx; - drawRect.bottom = textSize.cy; - - uint32_t width = textSize.cx; - uint32_t heigh = textSize.cy; - - uint8_t *pDestScanLine, *pBufScanLine, *pix, *bufpix; - - uint8_t al = 255 - ((uint8_t)(ARGBcolor >> 24)); - uint8_t r = GetRValue(ARGBcolor); - uint8_t g = GetGValue(ARGBcolor); - uint8_t b = GetBValue(ARGBcolor); - - for (uint32_t y = 2; y < heigh - 2; y++) { - int lineBytes = y * (width << 2); - - pDestScanLine = bits + lineBytes; - pBufScanLine = bufbits + lineBytes; - - for (uint32_t x = 2; x < width - 2; x++) { - pix = pDestScanLine + (x << 2); - bufpix = pBufScanLine + (x << 2); - - // Monochromatic - uint8_t bx = gammaTbl[bufpix[0]]; - uint8_t gx = gammaTbl[bufpix[1]]; - uint8_t rx = gammaTbl[bufpix[2]]; - - if (al != 255) { - bx *= al / 255; - gx *= al / 255; - rx *= al / 255; - } - - uint8_t ax = (uint8_t)(((uint32_t)rx * 77 + (uint32_t)gx * 151 + (uint32_t)bx * 28 + 128) / 256); - if (ax) { - //Normalize components to gray - uint8_t axx = 255 - ((r + g + b) >> 2); // Coefficient of grayance, more white font - more gray edges - uint16_t atx = ax * (255 - axx); - bx = (atx + bx * axx) / 255; - gx = (atx + gx * axx) / 255; - rx = (atx + rx * axx) / 255; - - short brx = (short)((b - pix[0])*bx / 255); - short grx = (short)((g - pix[1])*gx / 255); - short rrx = (short)((r - pix[2])*rx / 255); - - pix[0] += brx; - pix[1] += grx; - pix[2] += rrx; - pix[3] = (uint8_t)(ax + (uint8_t)(255 - ax)*pix[3] / 255); - } - } - } - - // Blit to destination - BitBlt(hDC, workRect.left + drx - 2, workRect.top + dry - 2, textSize.cx, textSize.cy, hOffscreenDC, 0, 0, SRCCOPY); - - //free resources - SelectObject(bufDC, bufoldbmp); - DeleteObject(bufbmp); - SelectObject(bufDC, hOldBufFont); - DeleteDC(bufDC); - } - SelectObject(hOffscreenDC, holdbmp); - DeleteObject(hbmp); - } - } - - // Final cleanup - SelectObject(hOffscreenDC, hOldOffscreenFont); - DeleteDC(hOffscreenDC); - - if (destHasNotDIB) - mir_free(pDestBits); - - if (bNeedFreeWorkString) - mir_free((void*)lpWorkString); - - return 0; -} - -static int ske_DrawTextWithEffectWorker(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, FONTEFFECT *effect) -{ - if (format & DT_CALCRECT) - return DrawText(hdc, lpString, nCount, lpRect, format); - - if (format & DT_RTLREADING) - SetTextAlign(hdc, TA_RTLREADING); - - uint32_t color = GetTextColor(hdc); - RECT r = *lpRect; - OffsetRect(&r, 1, 1); - uint32_t form = format; - if (effect && effect->effectIndex) - ske_SelectTextEffect(hdc, effect->effectIndex - 1, effect->baseColour, effect->secondaryColour); - - int res = ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color); - - if (effect && effect->effectIndex) - ske_ResetTextEffect(hdc); - - return res; -} - -INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM) -{ - DrawTextWithEffectParam *p = (DrawTextWithEffectParam *)wParam; - return ske_DrawTextWithEffectWorker(p->hdc, p->lpchText, p->cchText, p->lprc, p->dwDTFormat, p->pEffect); -} - -BOOL ske_DrawText(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format) -{ - RECT r = *lpRect; - OffsetRect(&r, 1, 1); - if (format & DT_RTLREADING) - SetTextAlign(hdc, TA_RTLREADING); - if (format & DT_CALCRECT) - return DrawText(hdc, lpString, nCount, lpRect, format); - if (format & DT_FORCENATIVERENDER || g_CluiData.fDisableSkinEngine) - return DrawText(hdc, lpString, nCount, lpRect, format & ~DT_FORCENATIVERENDER); - - uint32_t form = format; - uint32_t color = GetTextColor(hdc); - return ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color); -} - -HICON ske_ImageList_GetIcon(HIMAGELIST himl, int i) -{ - IMAGEINFO imi = {}; - BITMAP bm = { 0 }; - if (i != -1) { - ImageList_GetImageInfo(himl, i, &imi); - GetObject(imi.hbmImage, sizeof(bm), &bm); - // stupid bug of Microsoft - // Icons bitmaps are not premultiplied - // So Imagelist_AddIcon - premultiply alpha - // But incorrect - it is possible that alpha will - // be less than color and - // ImageList_GetIcon will return overflowed colors - // TODO: Direct draw Icon from imagelist without - // extracting of icon - if (bm.bmBitsPixel == 32) { - uint8_t *bits = (uint8_t*)bm.bmBits; - if (!bits) { - bits = (uint8_t*)mir_alloc(bm.bmWidthBytes*bm.bmHeight); - GetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits); - } - - uint8_t *bcbits = bits + (bm.bmHeight - imi.rcImage.bottom)*bm.bmWidthBytes + (imi.rcImage.left*bm.bmBitsPixel >> 3); - for (int iy = 0; iy < imi.rcImage.bottom - imi.rcImage.top; iy++) { - int x; - // Dummy microsoft fix - alpha can be less than r,g or b - // Looks like color channels in icons should be non-premultiplied with alpha - // But AddIcon store it premultiplied (incorrectly cause can be Alpha == 7F, but R,G or B == 80 - // So i check that alpha is 0x7F and set it to 0x80 - uint32_t *c = ((uint32_t*)bcbits); - for (x = 0; x < imi.rcImage.right - imi.rcImage.left; x++) { - uint32_t val = *c; - uint8_t a = (uint8_t)((val) >> 24); - if (a != 0) { - uint8_t r = (uint8_t)((val & 0xFF0000) >> 16); - uint8_t g = (uint8_t)((val & 0xFF00) >> 8); - uint8_t b = (uint8_t)(val & 0xFF); - if (a < r || a < g || a < b) { - a = max(max(r, g), b); - val = a << 24 | r << 16 | g << 8 | b; - *c = val; - } - } - c++; - } - bcbits += bm.bmWidthBytes; - } - - if (!bm.bmBits) { - SetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits); - mir_free(bits); - } - } - } - return ImageList_GetIcon(himl, i, ILD_NORMAL); -} - -BOOL ske_ImageList_DrawEx(HIMAGELIST himl, int i, HDC hdcDst, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) -{ - // the routine to directly draw icon from image list without creating icon from there - should be some faster - if (i < 0) - return FALSE; - - if (g_CluiData.fDisableSkinEngine) - return ImageList_DrawEx(himl, i, hdcDst, x, y, dx, dy, rgbBk, rgbFg, fStyle); - - uint8_t alpha; - if (fStyle & ILD_BLEND25) - alpha = 64; - else if (fStyle & ILD_BLEND50) - alpha = 128; - else - alpha = 255; - - HICON hIcon = ske_ImageList_GetIcon(himl, i); - if (hIcon == nullptr) - return FALSE; - - ske_DrawIconEx(hdcDst, x, y, hIcon, dx ? dx : GetSystemMetrics(SM_CXSMICON), dy ? dy : GetSystemMetrics(SM_CYSMICON), 0, nullptr, DI_NORMAL | (alpha << 24)); - DestroyIcon(hIcon); - return TRUE; -} - -static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM) -{ - DrawIconFixParam *p = (DrawIconFixParam*)wParam; - if (!p) - return 0; - - return ske_DrawIconEx(p->hdc, p->xLeft, p->yTop, p->hIcon, p->cxWidth, p->cyWidth, p->istepIfAniCur, p->hbrFlickerFreeDraw, p->diFlags); -} - - -BOOL ske_DrawIconEx(HDC hdcDst, int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags) -{ - ICONINFO ici; - uint8_t alpha = (uint8_t)((diFlags & 0xFF000000) >> 24); - - HBITMAP tBmp = nullptr; - uint8_t *imbits, *imimagbits, *immaskbits; - uint8_t *t1, *t2, *t3; - - //lockimagelist - uint8_t hasmask = FALSE, no32bit = FALSE, noMirrorMask = FALSE, hasalpha = FALSE; - alpha = alpha ? alpha : 255; - - if (g_CluiData.fDisableSkinEngine && !(diFlags & 0x80)) - return DrawIconEx(hdcDst, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags & 0xFFFF7F); - - if (!GetIconInfo(hIcon, &ici)) - return 0; - - BITMAP imbt; - GetObject(ici.hbmColor, sizeof(BITMAP), &imbt); - if (imbt.bmWidth*imbt.bmHeight == 0) { - DeleteObject(ici.hbmColor); - DeleteObject(ici.hbmMask); - return 0; - } - - BITMAP immaskbt; - GetObject(ici.hbmMask, sizeof(BITMAP), &immaskbt); - uint32_t cy = imbt.bmHeight; - - if (imbt.bmBitsPixel != 32) { - no32bit = TRUE; - HDC tempDC1 = CreateCompatibleDC(hdcDst); - tBmp = ske_CreateDIB32(imbt.bmWidth, imbt.bmHeight); - if (tBmp) { - GetObject(tBmp, sizeof(BITMAP), &imbt); - HBITMAP otBmp = (HBITMAP)SelectObject(tempDC1, tBmp); - DrawIconEx(tempDC1, 0, 0, hIcon, imbt.bmWidth, imbt.bmHeight, istepIfAniCur, hbrFlickerFreeDraw, DI_IMAGE); - noMirrorMask = TRUE; - SelectObject(tempDC1, otBmp); - } - DeleteDC(tempDC1); - } - - bool NoDIBImage = (imbt.bmBits == nullptr); - if (NoDIBImage) { - imimagbits = (uint8_t*)mir_alloc(cy*imbt.bmWidthBytes); - GetBitmapBits(ici.hbmColor, cy*imbt.bmWidthBytes, (void*)imimagbits); - } - else imimagbits = (uint8_t*)imbt.bmBits; - - if (immaskbt.bmBits == nullptr) { - immaskbits = (uint8_t*)mir_alloc(cy*immaskbt.bmWidthBytes); - GetBitmapBits(ici.hbmMask, cy*immaskbt.bmWidthBytes, (void*)immaskbits); - } - else immaskbits = (uint8_t*)immaskbt.bmBits; - - HDC imDC = CreateCompatibleDC(hdcDst); - uint32_t icy = imbt.bmHeight; - uint32_t cx = imbt.bmWidth; - HBITMAP imBmp = ske_CreateDIB32Point(cx, icy, (void**)&imbits); - HBITMAP oldBmp = (HBITMAP)SelectObject(imDC, imBmp); - if (imbits != nullptr && imimagbits != nullptr && immaskbits != nullptr) { - int x, y; - int mwb = immaskbt.bmWidthBytes; - int mwb2 = imbt.bmWidthBytes; - int bottom = icy; - int right = cx; - int top = 0; - int h = icy; - for (y = top; (y < bottom) && !hasmask; y++) { - t1 = immaskbits + y*mwb; - for (x = 0; (x < mwb) && !hasmask; x++) - hasmask |= (*(t1 + x) != 0); - } - - for (y = top; (y < bottom) && !hasalpha; y++) { - t1 = imimagbits + (cy - y - 1)*mwb2; - for (x = 0; (x < right) && !hasalpha; x++) - hasalpha |= (*(t1 + (x << 2) + 3) != 0); - } - - for (y = 0; y < (int)icy; y++) { - t1 = imimagbits + (h - y - 1 - top)*mwb2; - t2 = imbits + (!no32bit ? y : (icy - y - 1))*mwb2; - t3 = immaskbits + (noMirrorMask ? y : (h - y - 1 - top))*mwb; - for (x = 0; x < right; x++) { - uint8_t mask = 0; - uint8_t a = 0; - uint32_t *src = (uint32_t*)(t1 + (x << 2)); - uint32_t *dest = (uint32_t*)(t2 + (x << 2)); - if (hasalpha && !hasmask) - a = ((uint8_t*)src)[3]; - else { - mask = ((1 << (7 - x % 8))&(*(t3 + (x >> 3)))) != 0; - if (mask) { - if (!hasalpha) { - *dest = 0; - continue; - } - - if (((uint8_t*)src)[3]>0) - a = ((uint8_t*)src)[3]; - else - a = 0; - } - else if (hasalpha || hasmask) - a = (((uint8_t*)src)[3] > 0 ? ((uint8_t*)src)[3] : 255); - else if (!hasalpha && !hasmask) - a = 255; - else { *dest = 0; continue; } - } - if (a > 0) { - ((uint8_t*)dest)[3] = a; - ((uint8_t*)dest)[0] = ((uint8_t*)src)[0] * a / 255; - ((uint8_t*)dest)[1] = ((uint8_t*)src)[1] * a / 255; - ((uint8_t*)dest)[2] = ((uint8_t*)src)[2] * a / 255; - } - else *dest = 0; - } - } - } - - BLENDFUNCTION bf = { AC_SRC_OVER, diFlags & 128, alpha, AC_SRC_ALPHA }; - ske_AlphaBlend(hdcDst, xLeft, yTop, cxWidth, cyWidth, imDC, 0, 0, cx, icy, bf); - - if (immaskbt.bmBits == nullptr) mir_free(immaskbits); - if (imbt.bmBits == nullptr) mir_free(imimagbits); - SelectObject(imDC, oldBmp); - DeleteObject(imBmp); - if (no32bit)DeleteObject(tBmp); - DeleteObject(ici.hbmColor); - DeleteObject(ici.hbmMask); - SelectObject(imDC, GetStockObject(DEFAULT_GUI_FONT)); - DeleteDC(imDC); - return 1; -} - -int ske_PrepareImageButDontUpdateIt(RECT *r) -{ - if (!g_CluiData.fLayered) - return ske_ReCreateBackImage(FALSE, r); - - mutex_bLockUpdate = 1; - ske_DrawNonFramedObjects(TRUE, r); - ske_ValidateFrameImageProc(r); - mutex_bLockUpdate = 0; - return 0; -} - -int ske_RedrawCompleteWindow() -{ - if (g_CluiData.fLayered) { - ske_DrawNonFramedObjects(TRUE, nullptr); - CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE, 0, 0); - } - else RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); - - return 0; -} - -// Request to repaint frame or change/drop callback data -// wParam = hWnd of called frame -// lParam = pointer to sPaintRequest (or nullptr to redraw all) -// return 2 - already queued, data updated, 1-have been queued, 0 - failure - -static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM) // Immideately recall paint routines for frame and refresh image -{ - if (MirandaLoading()) - return 0; - - RECT wnd; - bool NoCancelPost = false; - bool IsAnyQueued = false; - if (!g_CluiData.mutexOnEdgeSizing) - GetWindowRect(g_clistApi.hwndContactList, &wnd); - else - wnd = g_rcEdgeSizingRect; - - if (!g_CluiData.fLayered) { - RedrawWindow((HWND)wParam, nullptr, nullptr, RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); - return 0; - } - - if (g_pCachedWindow == nullptr) ske_ValidateFrameImageProc(&wnd); - else if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) ske_ValidateFrameImageProc(&wnd); - else if (wParam == 0) ske_ValidateFrameImageProc(&wnd); - else { // all Ok Update Single Frame - // TO BE LOCKED OR PROXIED - FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam); - if (!frm) - ske_ValidateFrameImageProc(&wnd); - // Validate frame, update window image and remove it from queue - else { - if (frm->UpdateRgn) { - DeleteObject(frm->UpdateRgn); - frm->UpdateRgn = nullptr; - } - ske_ValidateSingleFrameImage(frm, 0); - ske_UpdateWindowImage(); - NoCancelPost = 1; - //-- Remove frame from queue - if (flag_bUpdateQueued) { - frm->bQueued = 0; - for (int i = 0; i < g_nFramesCount; i++) - if (IsAnyQueued |= g_pfwFrames[i].bQueued) - break; - } - } - } - - if ((!NoCancelPost || !IsAnyQueued) && flag_bUpdateQueued) { // no any queued updating cancel post or need to cancel post - flag_bUpdateQueued = 0; - g_bPostWasCanceled = true; - } - return 1; -} - -static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam) // Post request for updating -{ - if (MirandaLoading()) return 0; - - if (wParam) { - FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam); - sPaintRequest *pr = (sPaintRequest*)lParam; - if (!g_CluiData.fLayered || (frm && frm->floating)) - return InvalidateRect((HWND)wParam, pr ? (RECT*)&(pr->rcUpdate) : nullptr, FALSE); - - if (frm) { - if (frm->PaintCallbackProc != nullptr) { - frm->PaintData = (sPaintRequest *)pr; - frm->bQueued = 1; - if (pr) { - HRGN r2; - if (!IsRectEmpty(&pr->rcUpdate)) { - RECT rcClient; - RECT rcUpdate; - GetClientRect(frm->hWnd, &rcClient); - IntersectRect(&rcUpdate, &rcClient, &pr->rcUpdate); - if (IsRectEmpty(&rcUpdate)) - return 0; - r2 = CreateRectRgn(rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom); - } - else { - RECT r; - GetClientRect(frm->hWnd, &r); - r2 = CreateRectRgn(r.left, r.top, r.right, r.bottom); - } - - if (!frm->UpdateRgn) { - frm->UpdateRgn = CreateRectRgn(0, 0, 1, 1); - CombineRgn(frm->UpdateRgn, r2, nullptr, RGN_COPY); - } - else CombineRgn(frm->UpdateRgn, frm->UpdateRgn, r2, RGN_OR); - DeleteObject(r2); - } - } - } - else Sync(QueueAllFramesUpdating, true); - } - else Sync(QueueAllFramesUpdating, true); - - if (!flag_bUpdateQueued || g_bPostWasCanceled) - if (PostMessage(g_clistApi.hwndContactList, UM_UPDATE, 0, 0)) { - flag_bUpdateQueued = 1; - g_bPostWasCanceled = false; - } - return 1; -} - -static int ske_ValidateSingleFrameImage(FRAMEWND *Frame, BOOL SkipBkgBlitting) // Calling frame paint proc -{ - if (!g_pCachedWindow) { TRACE("ske_ValidateSingleFrameImage calling without cached\n"); return 0; } - if (Frame->hWnd == (HWND)-1 && !Frame->PaintCallbackProc) { TRACE("ske_ValidateSingleFrameImage calling without FrameProc\n"); return 0; } - - // if ok update image - RECT rcPaint, wnd; - RECT ru = { 0 }; - int w1, h1, x1, y1; - - CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &wnd); - rcPaint = Frame->wndSize; - { - int dx, dy, bx, by; - if (g_CluiData.mutexOnEdgeSizing) { - dx = rcPaint.left - wnd.left; - dy = rcPaint.top - wnd.top; - bx = rcPaint.right - wnd.right; - by = rcPaint.bottom - wnd.bottom; - wnd = g_rcEdgeSizingRect; - rcPaint.left = wnd.left + dx; - rcPaint.top = wnd.top + dy; - rcPaint.right = wnd.right + bx; - rcPaint.bottom = wnd.bottom + by; - } - } - - int w = rcPaint.right - rcPaint.left; - int h = rcPaint.bottom - rcPaint.top; - if (w <= 0 || h <= 0) { - TRACE("Frame size smaller than 0\n"); - return 0; - } - int x = rcPaint.left; - int y = rcPaint.top; - HDC hdc = CreateCompatibleDC(g_pCachedWindow->hImageDC); - HBITMAP n = ske_CreateDIB32(w, h); - HBITMAP o = (HBITMAP)SelectObject(hdc, n); - - if (Frame->UpdateRgn && !SkipBkgBlitting) { - GetRgnBox(Frame->UpdateRgn, &ru); - { - RECT rc; - GetClientRect(Frame->hWnd, &rc); - if (ru.top < 0) ru.top = 0; - if (ru.left < 0) ru.left = 0; - if (ru.right > rc.right) ru.right = rc.right; - if (ru.bottom > rc.bottom) ru.bottom = rc.bottom; - } - if (!IsRectEmpty(&ru)) { - x1 = ru.left; - y1 = ru.top; - w1 = ru.right - ru.left; - h1 = ru.bottom - ru.top; - } - else { - x1 = 0; y1 = 0; w1 = w; h1 = h; - } - - // copy image at hdc - BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY); - - Frame->PaintCallbackProc(Frame->hWnd, hdc, &ru, Frame->UpdateRgn, Frame->dwFlags, Frame->PaintData); - } - else { - RECT r; - GetClientRect(Frame->hWnd, &r); - HRGN rgnUpdate = CreateRectRgn(r.left, r.top, r.right, r.bottom); - ru = r; - if (!IsRectEmpty(&ru)) { - x1 = ru.left; - y1 = ru.top; - w1 = ru.right - ru.left; - h1 = ru.bottom - ru.top; - } - else { - x1 = 0; y1 = 0; w1 = w; h1 = h; - } - - // copy image at hdc - if (SkipBkgBlitting) //image already at foreground - BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hImageDC, x + x1, y + y1, SRCCOPY); - else - BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY); - - Frame->PaintCallbackProc(Frame->hWnd, hdc, &r, rgnUpdate, Frame->dwFlags, Frame->PaintData); - ru = r; - DeleteObject(rgnUpdate); - } - DeleteObject(Frame->UpdateRgn); - Frame->UpdateRgn = nullptr; - - if (!IsRectEmpty(&ru)) { - x1 = ru.left; - y1 = ru.top; - w1 = ru.right - ru.left; - h1 = ru.bottom - ru.top; - } - else { - x1 = 0; y1 = 0; w1 = w; h1 = h; - } - - BitBlt(g_pCachedWindow->hImageDC, x + x1, y + y1, w1, h1, hdc, x1, y1, SRCCOPY); - - if (GetWindowLongPtr(Frame->hWnd, GWL_STYLE) & WS_VSCROLL) { - //Draw vertical scroll bar - // - SCROLLBARINFO si = { 0 }; - si.cbSize = sizeof(SCROLLBARINFO); - GetScrollBarInfo(Frame->hWnd, OBJID_VSCROLL, &si); - - RECT rLine = (si.rcScrollBar); - RECT rUpBtn = rLine; - RECT rDnBtn = rLine; - RECT rThumb = rLine; - - rUpBtn.bottom = rUpBtn.top + si.dxyLineButton; - rDnBtn.top = rDnBtn.bottom - si.dxyLineButton; - rThumb.top = rLine.top + si.xyThumbTop; - rThumb.bottom = rLine.top + si.xyThumbBottom; - - int dx = Frame->wndSize.right - rLine.right; - int dy = -rLine.top + Frame->wndSize.top; - - OffsetRect(&rLine, dx, dy); - OffsetRect(&rUpBtn, dx, dy); - OffsetRect(&rDnBtn, dx, dy); - OffsetRect(&rThumb, dx, dy); - BitBlt(g_pCachedWindow->hImageDC, rLine.left, rLine.top, rLine.right - rLine.left, rLine.bottom - rLine.top, g_pCachedWindow->hBackDC, rLine.left, rLine.top, SRCCOPY); - - char req[255]; - mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Back", Frame->name); - SkinDrawGlyph(g_pCachedWindow->hImageDC, &rLine, &rLine, req); - mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Thumb", Frame->name); - SkinDrawGlyph(g_pCachedWindow->hImageDC, &rThumb, &rThumb, req); - mir_snprintf(req, "Main,ID=ScrollBar, Frame=%S,Part=UpLineButton", Frame->name); - SkinDrawGlyph(g_pCachedWindow->hImageDC, &rUpBtn, &rUpBtn, req); - mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=DownLineButton", Frame->name); - SkinDrawGlyph(g_pCachedWindow->hImageDC, &rDnBtn, &rDnBtn, req); - } - - SelectObject(hdc, o); - DeleteObject(n); - DeleteDC(hdc); - return 1; -} - -int ske_BltBackImage(HWND destHWND, HDC destDC, RECT *BltClientRect) -{ - POINT ptMainWnd = { 0 }; - POINT ptChildWnd = { 0 }; - RECT w = { 0 }; - if (g_CluiData.fDisableSkinEngine) { - FillRect(destDC, BltClientRect, GetSysColorBrush(COLOR_3DFACE)); - return 0; - } - ske_ReCreateBackImage(FALSE, nullptr); - if (BltClientRect) w = *BltClientRect; - else GetClientRect(destHWND, &w); - ptChildWnd.x = w.left; - ptChildWnd.y = w.top; - ClientToScreen(destHWND, &ptChildWnd); - ClientToScreen(g_clistApi.hwndContactList, &ptMainWnd); - //TODO if main not relative to client area - return BitBlt(destDC, w.left, w.top, (w.right - w.left), (w.bottom - w.top), g_pCachedWindow->hBackDC, (ptChildWnd.x - ptMainWnd.x), (ptChildWnd.y - ptMainWnd.y), SRCCOPY); - -} - -int ske_ReCreateBackImage(BOOL Erase, RECT *w) -{ - RECT wnd = { 0 }; - BOOL IsNewCache = 0; - if (g_CluiData.fDisableSkinEngine) return 0; - GetClientRect(g_clistApi.hwndContactList, &wnd); - if (w) wnd = *w; - //-- Check cached. - if (g_pCachedWindow == nullptr) { - //-- Create New Cache - g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA)); - g_pCachedWindow->hScreenDC = GetDC(nullptr); - g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC); - g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC); - g_pCachedWindow->Width = wnd.right - wnd.left; - g_pCachedWindow->Height = wnd.bottom - wnd.top; - if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) { - g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte)); - g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte)); - g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB); - g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB); - } - IsNewCache = 1; - } - - if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) { - HBITMAP hb1 = nullptr, hb2 = nullptr; - g_pCachedWindow->Width = wnd.right - wnd.left; - g_pCachedWindow->Height = wnd.bottom - wnd.top; - if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) { - hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte)); - hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte)); - SelectObject(g_pCachedWindow->hImageDC, hb1); - SelectObject(g_pCachedWindow->hBackDC, hb2); - } - else { - SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld); - SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld); - } - if (g_pCachedWindow->hImageDIB) DeleteObject(g_pCachedWindow->hImageDIB); - if (g_pCachedWindow->hBackDIB) DeleteObject(g_pCachedWindow->hBackDIB); - g_pCachedWindow->hImageDIB = hb1; - g_pCachedWindow->hBackDIB = hb2; - IsNewCache = 1; - } - - if ((Erase || IsNewCache) && (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0)) { - HBITMAP hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height); - SelectObject(g_pCachedWindow->hBackDC, hb2); - DeleteObject(g_pCachedWindow->hBackDIB); - g_pCachedWindow->hBackDIB = hb2; - FillRect(g_pCachedWindow->hBackDC, &wnd, GetSysColorBrush(COLOR_BTNFACE)); - SkinDrawGlyph(g_pCachedWindow->hBackDC, &wnd, &wnd, "Main,ID=Background,Opt=Non-Layered"); - ske_SetRectOpaque(g_pCachedWindow->hBackDC, &wnd); - } - return 1; -} - -int ske_DrawNonFramedObjects(BOOL Erase, RECT *r) -{ - RECT w, wnd; - if (r) w = *r; - else CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &w); - if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr); - if (g_pCachedWindow == nullptr) - return ske_ValidateFrameImageProc(&w); - - wnd = w; - OffsetRect(&w, -w.left, -w.top); - if (Erase) { - HBITMAP hb2; - hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height); - SelectObject(g_pCachedWindow->hBackDC, hb2); - DeleteObject(g_pCachedWindow->hBackDIB); - g_pCachedWindow->hBackDIB = hb2; - } - - SkinDrawGlyph(g_pCachedWindow->hBackDC, &w, &w, "Main,ID=Background"); - - //--Draw frames captions - for (int i = 0; i < g_nFramesCount; i++) { - if (g_pfwFrames[i].TitleBar.ShowTitleBar && g_pfwFrames[i].visible && !g_pfwFrames[i].floating) { - RECT rc; - SetRect(&rc, g_pfwFrames[i].wndSize.left, g_pfwFrames[i].wndSize.top - g_nTitleBarHeight - g_CluiData.nGapBetweenTitlebar, g_pfwFrames[i].wndSize.right, g_pfwFrames[i].wndSize.top - g_CluiData.nGapBetweenTitlebar); - Sync(DrawTitleBar, g_pCachedWindow->hBackDC, &rc, g_pfwFrames[i].id); - } - } - g_mutex_bLockUpdating = 1; - - flag_bJustDrawNonFramedObjects = 1; - return 0; -} - -// Calling queued frame paint procs and refresh image -int ske_ValidateFrameImageProc(RECT *r) -{ - RECT wnd = { 0 }; - BOOL IsNewCache = 0; - BOOL IsForceAllPainting = 0; - if (r) wnd = *r; - else GetWindowRect(g_clistApi.hwndContactList, &wnd); - if (wnd.right - wnd.left == 0 || wnd.bottom - wnd.top == 0) - return 0; - - g_mutex_bLockUpdating = 1; - - //-- Check cached. - if (g_pCachedWindow == nullptr) { - //-- Create New Cache - g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA)); - g_pCachedWindow->hScreenDC = GetDC(nullptr); - g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC); - g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC); - g_pCachedWindow->Width = wnd.right - wnd.left; - g_pCachedWindow->Height = wnd.bottom - wnd.top; - g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte)); - g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte)); - g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB); - g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB); - IsNewCache = 1; - } - if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) { - HBITMAP hb1, hb2; - g_pCachedWindow->Width = wnd.right - wnd.left; - g_pCachedWindow->Height = wnd.bottom - wnd.top; - hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte)); - hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte)); - SelectObject(g_pCachedWindow->hImageDC, hb1); - SelectObject(g_pCachedWindow->hBackDC, hb2); - DeleteObject(g_pCachedWindow->hImageDIB); - DeleteObject(g_pCachedWindow->hBackDIB); - g_pCachedWindow->hImageDIB = hb1; - g_pCachedWindow->hBackDIB = hb2; - IsNewCache = 1; - } - if (IsNewCache) { - ske_DrawNonFramedObjects(0, &wnd); - IsForceAllPainting = 1; - } - if (flag_bJustDrawNonFramedObjects) { - IsForceAllPainting = 1; - flag_bJustDrawNonFramedObjects = 0; - } - if (IsForceAllPainting) { - BitBlt(g_pCachedWindow->hImageDC, 0, 0, g_pCachedWindow->Width, g_pCachedWindow->Height, g_pCachedWindow->hBackDC, 0, 0, SRCCOPY); - Sync(QueueAllFramesUpdating, true); - } - //-- Validating frames - for (int i = 0; i < g_nFramesCount; i++) - if (g_pfwFrames[i].PaintCallbackProc && g_pfwFrames[i].visible && !g_pfwFrames[i].floating) - if (g_pfwFrames[i].bQueued || IsForceAllPainting) - ske_ValidateSingleFrameImage(&g_pfwFrames[i], IsForceAllPainting); - - g_mutex_bLockUpdating = 1; - ModernSkinButtonRedrawAll(); - g_mutex_bLockUpdating = 0; - if (!mutex_bLockUpdate) - ske_UpdateWindowImageRect(&wnd); - - //-- Clear queue - Sync(QueueAllFramesUpdating, false); - flag_bUpdateQueued = 0; - g_bPostWasCanceled = false; - return 1; -} - -int ske_UpdateWindowImage() -{ - if (MirandaExiting()) - return 0; - - if (g_CluiData.fLayered) { - RECT r; - GetWindowRect(g_clistApi.hwndContactList, &r); - return ske_UpdateWindowImageRect(&r); - } - else ske_ReCreateBackImage(FALSE, nullptr); - ske_ApplyTranslucency(); - return 0; -} - -int ske_UpdateWindowImageRect(RECT *r) // Update window with current image and -{ - //if not validity -> ValidateImageProc - //else Update using current alpha - RECT wnd = *r; - - if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr); - if (g_pCachedWindow == nullptr) return ske_ValidateFrameImageProc(&wnd); - if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) return ske_ValidateFrameImageProc(&wnd); - if (g_bFullRepaint) { - g_bFullRepaint = false; - return ske_ValidateFrameImageProc(&wnd); - } - ske_JustUpdateWindowImageRect(&wnd); - return 0; -} - -void ske_ApplyTranslucency() -{ - int IsTransparancy; - HWND hwnd = g_clistApi.hwndContactList; - BOOL layered = (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) ? TRUE : FALSE; - - IsTransparancy = g_CluiData.fSmoothAnimation || g_bTransparentFlag; - if (!g_bTransparentFlag && !g_CluiData.fSmoothAnimation && g_CluiData.bCurrentAlpha != 0) - g_CluiData.bCurrentAlpha = 255; - - if (!g_CluiData.fLayered && IsTransparancy) { - if (!layered) - SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); - SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (uint8_t)g_CluiData.bCurrentAlpha, LWA_ALPHA); - } - - AniAva_RedrawAllAvatars(FALSE); - return; -} - -int ske_JustUpdateWindowImage() -{ - RECT r; - if (!g_CluiData.fLayered) { - ske_ApplyTranslucency(); - return 0; - } - GetWindowRect(g_clistApi.hwndContactList, &r); - return ske_JustUpdateWindowImageRect(&r); -} - -// Update window image -int ske_JustUpdateWindowImageRect(RECT *rty) -{ - if (!g_CluiData.fLayered) { - ske_ApplyTranslucency(); - return 0; - } - if (!g_clistApi.hwndContactList) - return 0; - - RECT wnd = *rty; - RECT rect = wnd; - POINT dest = { 0 }, src = { 0 }; - dest.x = rect.left; - dest.y = rect.top; - SIZE sz = { rect.right - rect.left, rect.bottom - rect.top }; - if (g_CluiData.fLayered) { - if (!(GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) & WS_EX_LAYERED)) - SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED); - Sync(SetAlpha, g_CluiData.bCurrentAlpha); - - BLENDFUNCTION bf = { AC_SRC_OVER, 0, g_CluiData.bCurrentAlpha, AC_SRC_ALPHA }; - UpdateLayeredWindow(g_clistApi.hwndContactList, g_pCachedWindow->hScreenDC, &dest, &sz, g_pCachedWindow->hImageDC, &src, RGB(1, 1, 1), &bf, ULW_ALPHA); - g_CluiData.fAeroGlass = false; - CLUI_UpdateAeroGlass(); - } - else InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE); - return 0; -} - -int ske_DrawImageAt(HDC hdc, RECT *rc) -{ - BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - BitBlt(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, g_pCachedWindow->hBackDC, rc->left, rc->top, SRCCOPY); - ske_AlphaBlend(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, hdc, 0, 0, rc->right - rc->left, rc->bottom - rc->top, bf); - if (!g_mutex_bLockUpdating) - ske_UpdateWindowImage(); - return 0; -} - -HBITMAP ske_GetCurrentWindowImage() -{ - return g_pCachedWindow->hImageDIB; -} - -/* -* Glyph text routine -*/ - -static uint32_t ske_HexToARGB(char *Hex) -{ - char buf[10] = { 0 }; - char buf2[11] = { 0 }; - mir_snprintf(buf, "%s\n", Hex); - if (buf[1] == 'x' || buf[1] == 'X') - mir_snprintf(buf2, "0x%s\n", buf + 2); - else - mir_snprintf(buf2, "0x%s\n", buf); - buf2[10] = '\0'; - - char *st; - uint32_t AARRGGBB = strtoul(buf2, &st, 16); - uint8_t alpha = (uint8_t)((AARRGGBB & 0xFF000000) >> 24); - alpha = 255 - ((alpha == 0) ? 255 : alpha); - AARRGGBB = (alpha << 24) + ((AARRGGBB & 0x00FF0000) >> 16) + ((AARRGGBB & 0x000000FF) << 16) + (AARRGGBB & 0x0000FF00); - return AARRGGBB; -} - -static wchar_t *ske_ReAppend(wchar_t *lfirst, wchar_t *lsecond, int len) -{ - size_t l1 = lfirst ? mir_wstrlen(lfirst) : 0; - size_t l2 = (len ? len : (mir_wstrlen(lsecond) + 1)); - wchar_t *buf = (wchar_t *)mir_alloc((l1 + l2 + 1)*sizeof(wchar_t)); - if (lfirst) memmove(buf, lfirst, l1*sizeof(wchar_t)); - memmove(buf + l1, lsecond, l2*sizeof(wchar_t)); - mir_free(lfirst); - if (len) buf[l1 + l2] = '\0'; - return buf; -} - -wchar_t* ske_ReplaceVar(wchar_t *var) -{ - if (!var) return mir_wstrdup(L""); - if (!mir_wstrcmpi(var, L"Profile")) { - char buf[MAX_PATH] = { 0 }; - Profile_GetNameA(MAX_PATH, buf); - - char *p = strrchr(buf, '.'); - if (p) *p = 0; - - mir_free(var); - return mir_a2u(buf); - } - - mir_free(var); - return mir_wstrdup(L""); -} - -wchar_t *ske_ParseText(wchar_t *stzText) -{ - size_t len = mir_wstrlen(stzText); - wchar_t *result = nullptr; - size_t stpos = 0, curpos = 0; - - while (curpos < len) { - //1 find first % - while (curpos < len && stzText[curpos] != (wchar_t)'%') - curpos++; - if (curpos < len) { //% found - if (curpos - stpos > 0) - result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos)); - stpos = curpos + 1; - curpos++; - //3 find second % - while (curpos < len && stzText[curpos] != (wchar_t)'%') - curpos++; - if (curpos >= len) - break; - if (curpos - stpos > 0) { - wchar_t *var = (wchar_t *)mir_alloc((curpos - stpos + 1)*sizeof(wchar_t)); - memcpy(var, stzText + stpos, (curpos - stpos)*sizeof(wchar_t)); - var[curpos - stpos] = (wchar_t)'\0'; - var = ske_ReplaceVar(var); - result = ske_ReAppend(result, var, 0); - mir_free(var); - } - else result = ske_ReAppend(result, L"%", 0); - - curpos++; - stpos = curpos; - } - else { - if (curpos - stpos > 0) - result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos)); - break; - } - } - return result; -} -/* -* Parse text object string, find glyph object and add text to it. -* szGlyphTextID and Define string is: -* t[szGlyphTextID] = s[HostObjectID],[Left],[Top],[Right],[Bottom],[LTRBHV],[FontID],[Color1],[reservedforColor2],[Text] -*/ - -static void ske_AddParseTextGlyphObject(char *szGlyphTextID, char *szDefineString, SKINOBJECTSLIST *Skin) -{ - char buf[255] = { 0 }; - GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE); - if (buf[0] == 0) - return; - - GLYPHTEXT *glText = (GLYPHTEXT*)mir_calloc(sizeof(GLYPHTEXT)); - glText->szGlyphTextID = mir_strdup(szGlyphTextID); - glText->szObjectName = mir_strdup(buf); - glText->iLeft = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE)); - glText->iTop = atoi(GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE)); - glText->iRight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 3, ',', TRUE)); - glText->iBottom = atoi(GetParamN(szDefineString, buf, sizeof(buf), 4, ',', TRUE)); - { - memset(buf, 0, 6); - GetParamN(szDefineString, buf, sizeof(buf), 5, ',', TRUE); - buf[0] &= 95; buf[1] &= 95; buf[2] &= 95; buf[3] &= 95; buf[4] &= 95; buf[5] &= 95; //to uppercase: &01011111 (0-95) - glText->RelativeFlags = - (buf[0] == 'C' ? 1 : ((buf[0] == 'R') ? 2 : 0)) //[BC][RC][BC][RC] --- Left relative - | (buf[1] == 'C' ? 4 : ((buf[1] == 'B') ? 8 : 0)) // | | |--------- Top relative - | (buf[2] == 'C' ? 16 : ((buf[2] == 'R') ? 32 : 0)) // | |--------------Right relative - | (buf[3] == 'C' ? 64 : ((buf[3] == 'B') ? 128 : 0)); // |------------------Bottom relative - glText->dwFlags = (buf[4] == 'C' ? DT_CENTER : ((buf[4] == 'R') ? DT_RIGHT : DT_LEFT)) - | (buf[5] == 'C' ? DT_VCENTER : ((buf[5] == 'B') ? DT_BOTTOM : DT_TOP)); - } - glText->szFontID = mir_strdup(GetParamN(szDefineString, buf, sizeof(buf), 6, ',', TRUE)); - - glText->dwColor = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 7, ',', TRUE)); - glText->dwShadow = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 8, ',', TRUE)); - glText->stValueText = mir_a2u(GetParamN(szDefineString, buf, sizeof(buf), 9, ',', TRUE)); - glText->stText = ske_ParseText(glText->stValueText); - - if (!Skin->pTextList) - Skin->pTextList = List_Create(0, 1); - List_InsertPtr(Skin->pTextList, glText); -} - - -/* -* Parse font definition string. -* szGlyphTextID and Define string is: -* f[szFontID] = s[FontTypefaceName],[size],[BIU] -*/ -static void ske_AddParseSkinFont(char *szFontID, char *szDefineString) -{ - SKINFONT *sf = (SKINFONT*)mir_calloc(sizeof(SKINFONT)); - if (!sf) - return; - - LOGFONTA logfont = { 0 }; - logfont.lfCharSet = DEFAULT_CHARSET; - logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; - logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - logfont.lfQuality = DEFAULT_QUALITY; - logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - - char buf[255]; - strncpy_s(logfont.lfFaceName, GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE), _TRUNCATE); - logfont.lfHeight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE)); - if (logfont.lfHeight < 0) { - HDC hdc = CreateCompatibleDC(nullptr); - logfont.lfHeight = (long)-MulDiv(logfont.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72); - DeleteDC(hdc); - } - logfont.lfHeight = -logfont.lfHeight; - GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE); - buf[0] &= 95; buf[1] &= 95; buf[2] &= 95; - logfont.lfWeight = (buf[0] == 'B') ? FW_BOLD : FW_NORMAL; - logfont.lfItalic = (buf[1] == 'I') ? 1 : 0; - logfont.lfUnderline = (buf[2] == 'U') ? 1 : 0; - - sf->hFont = CreateFontIndirectA(&logfont); - if (sf->hFont) { - sf->szFontID = mir_strdup(szFontID); - if (!gl_plSkinFonts) - gl_plSkinFonts = List_Create(0, 1); - if (gl_plSkinFonts) - List_Insert(gl_plSkinFonts, sf, gl_plSkinFonts->realCount); - else - mir_free(sf); - } - else mir_free(sf); -} - -/* - * ske_CheckHasAlfaChannel - checks if image has at least one uint8_t in alpha chennel - * that is not a 0. (is image real 32 bit or just 24 bit) - */ -static BOOL ske_CheckHasAlfaChannel(uint8_t *from, int widthByte, int height) -{ - uint32_t *pt = (uint32_t*)from; - for (int j = 0; j < height; j++) { - uint8_t *add = (uint8_t*)pt + widthByte; - while (pt < (uint32_t*)add) { - if ((*pt & 0xFF000000) != 0) - return TRUE; - pt++; - } - pt = (uint32_t*)(from + widthByte*j); - } - return FALSE; -} - -/* - * ske_CheckIconHasMask - checks if mask image has at least one that is not a 0. - * Not sure is ir required or not - */ -static BOOL ske_CheckIconHasMask(uint8_t *from) -{ - for (int i = 0; i < 16 * 16 / 8; i++) - if (from[i] != 0) - return TRUE; - - return FALSE; -} - -/* - * ske_GetMaskBit - return value of apropriate mask bit in line at x position - */ -static BOOL ske_GetMaskBit(uint8_t *line, int x) -{ - return ((*(line + (x >> 3)))&(0x01 << (7 - (x & 0x07)))) != 0; -} - -/* - * ske_Blend - alpha ske_Blend ARGB values of 2 pixels. X1 - underlaying, - * X2 - overlaying points. - */ - -static uint32_t ske_Blend(uint32_t X1, uint32_t X2, uint8_t alpha) -{ - uint8_t a1 = (uint8_t)(X1 >> 24); - uint8_t a2 = (uint8_t)(((X2 >> 24)*alpha) >> 8); - uint8_t r1 = (uint8_t)(X1 >> 16); - uint8_t r2 = (uint8_t)(X2 >> 16); - uint8_t g1 = (uint8_t)(X1 >> 8); - uint8_t g2 = (uint8_t)(X2 >> 8); - uint8_t b1 = (uint8_t)(X1); - uint8_t b2 = (uint8_t)(X2); - - uint8_t a_1 = ~a1; - uint8_t a_2 = ~a2; - uint16_t am = (uint16_t)a1*a_2; - - /* it is possible to use >>8 instead of /255 but it is require additional - * checking of alphavalues - */ - uint16_t ar = a1 + (((uint16_t)a_1*a2) / 255); - // if a2 more than 0 than result should be more - // or equal (if a1 == 0) to a2, else in combination - // with mask we can get here black points - - ar = (a2 > ar) ? a2 : ar; - - if (ar == 0) return 0; - - uint16_t arm = ar * 255; - uint16_t rr = (((uint16_t)r1*am + (uint16_t)r2*a2 * 255)) / arm; - uint16_t gr = (((uint16_t)g1*am + (uint16_t)g2*a2 * 255)) / arm; - uint16_t br = (((uint16_t)b1*am + (uint16_t)b2*a2 * 255)) / arm; - return (ar << 24) | (rr << 16) | (gr << 8) | br; -} - -/* - * CreateJoinedIcon - creates new icon by drawing hTop over hBottom. - */ - -HICON ske_CreateJoinedIcon(HICON hBottom, HICON hTop, uint8_t alpha) -{ - ICONINFO iNew = { 0 }; - ICONINFO iciBottom = { 0 }; - ICONINFO iciTop = { 0 }; - - BITMAP bmp_top = { 0 }; - BITMAP bmp_top_mask = { 0 }; - - BITMAP bmp_bottom = { 0 }; - BITMAP bmp_bottom_mask = { 0 }; - - HDC tempDC = CreateCompatibleDC(nullptr); - - uint8_t *ptPixels; - HBITMAP nImage = ske_CreateDIB32Point(16, 16, (void**)&ptPixels); - HBITMAP oImage = (HBITMAP)SelectObject(tempDC, nImage); - - GetIconInfo(hBottom, &iciBottom); - GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom); - GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask); - - GetIconInfo(hTop, &iciTop); - GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top); - GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask); - - if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32) { - uint8_t *BottomBuffer, *TopBuffer, *BottomMaskBuffer, *TopMaskBuffer; - uint8_t *bb, *tb, *bmb, *tmb; - uint8_t *db = ptPixels; - int vstep_d = 16 * 4; - int vstep_b = bmp_bottom.bmWidthBytes; - int vstep_t = bmp_top.bmWidthBytes; - int vstep_bm = bmp_bottom_mask.bmWidthBytes; - int vstep_tm = bmp_top_mask.bmWidthBytes; - alpha = alpha ? alpha : 255; - if (bmp_bottom.bmBits) bb = BottomBuffer = (uint8_t*)bmp_bottom.bmBits; - else { - BottomBuffer = (uint8_t*)mir_alloc(bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes); - GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes, BottomBuffer); - bb = BottomBuffer + vstep_b*(bmp_bottom.bmHeight - 1); - vstep_b = -vstep_b; - } - - if (bmp_top.bmBits) tb = TopBuffer = (uint8_t*)bmp_top.bmBits; - else { - TopBuffer = (uint8_t*)mir_alloc(bmp_top.bmHeight*bmp_top.bmWidthBytes); - GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight*bmp_top.bmWidthBytes, TopBuffer); - tb = TopBuffer + vstep_t*(bmp_top.bmHeight - 1); - vstep_t = -vstep_t; - } - - if (bmp_bottom_mask.bmBits) { - BottomMaskBuffer = (uint8_t*)bmp_bottom_mask.bmBits; - bmb = BottomMaskBuffer; - } - else { - BottomMaskBuffer = (uint8_t*)mir_alloc(bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes); - GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer); - bmb = BottomMaskBuffer + vstep_bm*(bmp_bottom_mask.bmHeight - 1); - vstep_bm = -vstep_bm; - - } - - if (bmp_top_mask.bmBits) { - TopMaskBuffer = (uint8_t*)bmp_top_mask.bmBits; - tmb = TopMaskBuffer; - } - else { - TopMaskBuffer = (uint8_t*)mir_alloc(bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes); - GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes, TopMaskBuffer); - tmb = TopMaskBuffer + vstep_tm*(bmp_top_mask.bmHeight - 1); - vstep_tm = -vstep_tm; - } - - BOOL topHasAlpha = ske_CheckHasAlfaChannel(TopBuffer, bmp_top.bmWidthBytes, bmp_top.bmHeight); - BOOL bottomHasAlpha = ske_CheckHasAlfaChannel(BottomBuffer, bmp_bottom.bmWidthBytes, bmp_bottom.bmHeight); - BOOL topHasMask = ske_CheckIconHasMask(TopMaskBuffer); - BOOL bottomHasMask = ske_CheckIconHasMask(BottomMaskBuffer); - for (int y = 0; y < 16; y++) { - for (int x = 0; x < 16; x++) { - BOOL mask_b = ske_GetMaskBit(bmb, x); - BOOL mask_t = ske_GetMaskBit(tmb, x); - uint32_t bottom_d = ((uint32_t*)bb)[x]; - uint32_t top_d = ((uint32_t*)tb)[x]; - if (topHasMask) { - if (mask_t == 1 && !topHasAlpha) top_d &= 0xFFFFFF; - else if (!topHasAlpha) top_d |= 0xFF000000; - } - if (bottomHasMask) { - if (mask_b == 1 && !bottomHasAlpha) bottom_d &= 0xFFFFFF; - else if (!bottomHasAlpha) bottom_d |= 0xFF000000; - } - ((uint32_t*)db)[x] = ske_Blend(bottom_d, top_d, alpha); - } - bb += vstep_b; - tb += vstep_t; - bmb += vstep_bm; - tmb += vstep_tm; - db += vstep_d; - } - - if (!bmp_bottom.bmBits) mir_free(BottomBuffer); - if (!bmp_top.bmBits) mir_free(TopBuffer); - if (!bmp_bottom_mask.bmBits) mir_free(BottomMaskBuffer); - if (!bmp_top_mask.bmBits) mir_free(TopMaskBuffer); - } - else { - ske_DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL); - ske_DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL | (alpha << 24)); - } - - DeleteObject(iciBottom.hbmColor); - DeleteObject(iciTop.hbmColor); - DeleteObject(iciBottom.hbmMask); - DeleteObject(iciTop.hbmMask); - - SelectObject(tempDC, oImage); - DeleteDC(tempDC); - - uint8_t p[32] = { 0 }; - HBITMAP nMask = CreateBitmap(16, 16, 1, 1, (void*)&p); - { - HDC tempDC2 = CreateCompatibleDC(nullptr); - HDC tempDC3 = CreateCompatibleDC(nullptr); - HBITMAP hbm = CreateCompatibleBitmap(tempDC3, 16, 16); - HBITMAP obmp = (HBITMAP)SelectObject(tempDC2, nMask); - HBITMAP obmp2 = (HBITMAP)SelectObject(tempDC3, hbm); - DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK); - DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK); - BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND); - SelectObject(tempDC2, obmp); - SelectObject(tempDC3, obmp2); - DeleteObject(hbm); - DeleteDC(tempDC2); - DeleteDC(tempDC3); - } - iNew.fIcon = TRUE; - iNew.hbmColor = nImage; - iNew.hbmMask = nMask; - HICON res = CreateIconIndirect(&iNew); - DeleteObject(nImage); - DeleteObject(nMask); - return res; -} - -#define NEWJOINEDSTR(destination, first, separator, last) \ - destination = (char*)alloca(mir_strlen(first)+mir_strlen(separator)+mir_strlen(last)+1); \ - if (destination) { \ - *destination = '\0'; \ - mir_strcat(destination,first); \ - mir_strcat(destination,separator); \ - mir_strcat(destination,last); \ - } - -#define SKINSETSECTION "SkinnedSettings" - -BOOL SkinDBGetContactSetting(MCONTACT hContact, const char *szSection, const char *szKey, DBVARIANT *retdbv, BOOL *bSkinned) -{ - if (!hContact) { //only for not contact settings - char *szSkinKey; - NEWJOINEDSTR(szSkinKey, szSection, "@", szKey); - if (!db_get(hContact, SKINSETSECTION, szSkinKey, retdbv)) { - if (bSkinned) *bSkinned = TRUE; - return FALSE; - } - } - // not skinned - if (bSkinned) bSkinned = FALSE; - return db_get(hContact, szSection, szKey, retdbv); -} - -uint8_t SkinDBGetContactSettingByte(MCONTACT hContact, const char *szSection, const char *szKey, uint8_t bDefault) -{ - DBVARIANT dbv = { 0 }; - BOOL bSkinned = FALSE; - if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) { - if (dbv.type == DBVT_BYTE) { - uint8_t retVal = dbv.bVal; - db_free(&dbv); - return retVal; - } - else { - db_free(&dbv); - if (!bSkinned) - return db_get_b(hContact, szSection, szKey, bDefault); - } - } - return bDefault; -} - -uint16_t SkinDBGetContactSettingWord(MCONTACT hContact, const char *szSection, const char *szKey, uint16_t wDefault) -{ - BOOL bSkinned = FALSE; - DBVARIANT dbv = { 0 }; - if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) { - if (dbv.type == DBVT_WORD) { - uint16_t retVal = dbv.wVal; - db_free(&dbv); - return retVal; - } - db_free(&dbv); - if (!bSkinned) - return db_get_w(hContact, szSection, szKey, wDefault); - } - return wDefault; -} - -uint32_t SkinDBGetContactSettingDword(MCONTACT hContact, const char *szSection, const char *szKey, uint32_t dwDefault) -{ - DBVARIANT dbv = { 0 }; - BOOL bSkinned = FALSE; - if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) { - if (dbv.type == DBVT_DWORD) { - uint32_t retVal = dbv.dVal; - db_free(&dbv); - return retVal; - } - db_free(&dbv); - if (!bSkinned) - return db_get_dw(hContact, szSection, szKey, dwDefault); - } - return dwDefault; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//Include
+#include "stdafx.h"
+
+#define _EFFECTENUM_FULL_H
+#include "modern_effectenum.h"
+#undef _EFFECTENUM_FULL_H
+
+#include "modern_sync.h"
+
+//Implementation
+
+#pragma pack(push, 1)
+/* tga header */
+struct tga_header_t
+{
+ uint8_t id_lenght; /* size of image id */
+ uint8_t colormap_type; /* 1 is has a colormap */
+ uint8_t image_type; /* compression type */
+
+ short cm_first_entry; /* colormap origin */
+ short cm_length; /* colormap length */
+ uint8_t cm_size; /* colormap size */
+
+ short x_origin; /* bottom left x coord origin */
+ short y_origin; /* bottom left y coord origin */
+
+ short width; /* picture width (in pixels) */
+ short height; /* picture height (in pixels) */
+
+ uint8_t pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */
+ uint8_t image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */
+};
+#pragma pack(pop)
+
+/* Global variables */
+
+SKINOBJECTSLIST g_SkinObjectList = { 0 };
+CURRWNDIMAGEDATA *g_pCachedWindow = nullptr;
+
+bool g_bPostWasCanceled = false;
+bool g_bFullRepaint = false;
+
+int g_mutex_bLockUpdating = 0;
+
+SortedList *gl_plGlyphTexts = nullptr;
+SortedList *gl_plSkinFonts = nullptr;
+
+/* Private module variables */
+
+static HANDLE hSkinLoadedEvent;
+
+static GLYPHIMAGE *pLoadedImages = nullptr;
+static uint32_t dwLoadedImagesCount = 0;
+static uint32_t dwLoadedImagesAlocated = 0;
+
+static BOOL flag_bUpdateQueued = FALSE;
+static BOOL flag_bJustDrawNonFramedObjects = FALSE;
+static BOOL mutex_bLockUpdate = FALSE;
+
+static LIST<EFFECTSSTACKITEM> arEffectStack(10, HandleKeySortT);
+static SKINOBJECTSLIST *pCurrentSkin = nullptr;
+static char **pszSettingName = nullptr;
+static int nArrayLen = 0;
+
+static uint8_t pbGammaWeight[256] = { 0 };
+static uint8_t pbGammaWeightAdv[256] = { 0 };
+static BOOL bGammaWeightFilled = FALSE;
+
+static mir_cs cs_SkinChanging;
+
+static LISTMODERNMASK *MainModernMaskList = nullptr;
+
+/* Private module procedures */
+static BOOL ske_GetMaskBit(uint8_t *line, int x);
+static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM lParam);
+static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM lParam);
+
+static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor);
+static void ske_AddParseTextGlyphObject(char * szGlyphTextID, char * szDefineString, SKINOBJECTSLIST *Skin);
+static void ske_AddParseSkinFont(char * szFontID, char * szDefineString);
+static int ske_GetSkinFromDB(char * szSection, SKINOBJECTSLIST * Skin);
+static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin);
+static int ske_LoadSkinFromResource(BOOL bOnlyObjects);
+static void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult);
+static int ske_ValidateSingleFrameImage(FRAMEWND * Frame, BOOL SkipBkgBlitting);
+static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM lParam);
+static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam);
+static INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM lParam);
+
+static MODERNEFFECT meCurrentEffect = { 0xFF, { 0 }, 0, 0 };
+
+//////////////////////////////////////////////////////////////////////////
+// Ini file parser
+
+IniParser::IniParser(wchar_t * tcsFileName, uint8_t flags) : _Flags(flags)
+{
+ _DoInit();
+ if (!tcsFileName) return;
+
+ if (tcsFileName[0] == '%') {
+ //TODO: Add parser of resource filename here
+ _LoadResourceIni(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF");
+ return;
+ }
+
+ _hFile = _wfopen(tcsFileName, L"r");
+ if (_hFile != nullptr) {
+ _eType = IT_FILE;
+ _isValid = true;
+ }
+}
+
+IniParser::IniParser(HINSTANCE hInst, const char * resourceName, const char * resourceType, uint8_t flags) : _Flags(flags)
+{
+ _DoInit();
+ _LoadResourceIni(hInst, resourceName, resourceType);
+}
+
+IniParser::~IniParser()
+{
+ mir_free(_szSection);
+ if (_hFile) fclose(_hFile);
+ if (_hGlobalRes) {
+ UnlockResource(_hGlobalRes);
+ FreeResource(_hGlobalRes);
+ }
+
+ _szSection = nullptr;
+ _hGlobalRes = nullptr;
+ _hFile = nullptr;
+ _isValid = false;
+ _eType = IT_UNKNOWN;
+}
+
+HRESULT IniParser::Parse(ParserCallback_t pLineCallBackProc, LPARAM SecCheck)
+{
+ if (_isValid && pLineCallBackProc) {
+ _pLineCallBackProc = pLineCallBackProc;
+ _SecCheck = SecCheck;
+ switch (_eType) {
+ case IT_FILE:
+ return _DoParseFile();
+ case IT_RESOURCE:
+ return _DoParseResource();
+ }
+ }
+ return E_FAIL;
+}
+
+HRESULT IniParser::WriteStrToDb(const char * szSection, const char * szName, const char * szValue, IniParser * This)
+{
+ if (This->_SecCheck) {
+ //TODO check security here
+ if (wildcmp(szSection, "Skin_Description_Section"))
+ return S_OK;
+ }
+ if ((This->_Flags == IniParser::FLAG_ONLY_OBJECTS) && !wildcmp(szSection, DEFAULTSKINSECTION))
+ return S_OK; // skip not objects
+
+ switch (szValue[0]) {
+ case 'b':
+ db_set_b(0, szSection, szName, (uint8_t)atoi(szValue + 1));
+ break;
+
+ case 'w':
+ db_set_w(0, szSection, szName, (uint16_t)atoi(szValue + 1));
+ break;
+
+ case 'd':
+ db_set_dw(0, szSection, szName, (uint32_t)atoi(szValue + 1));
+ break;
+
+ case 's':
+ db_set_s(0, szSection, szName, szValue + 1);
+ break;
+ }
+ return S_OK;
+}
+
+int IniParser::GetSkinFolder(IN const wchar_t * szFileName, OUT wchar_t * pszFolderName)
+{
+ wchar_t *szBuff = mir_wstrdup(szFileName);
+ wchar_t *pszPos = szBuff + mir_wstrlen(szBuff);
+ while (pszPos > szBuff && *pszPos != '.') { pszPos--; }
+ *pszPos = '\0';
+ mir_wstrcpy(pszFolderName, szBuff);
+
+ wchar_t custom_folder[MAX_PATH], cus[MAX_PATH];
+ wchar_t *b3;
+ mir_wstrncpy(custom_folder, pszFolderName, _countof(custom_folder));
+ b3 = custom_folder + mir_wstrlen(custom_folder);
+ while (b3 > custom_folder && *b3 != '\\') { b3--; }
+ *b3 = '\0';
+
+ GetPrivateProfileString(L"Skin_Description_Section", L"SkinFolder", L"", cus, _countof(custom_folder), szFileName);
+ if (cus[0] != 0)
+ mir_snwprintf(pszFolderName, MAX_PATH, L"%s\\%s", custom_folder, cus);
+
+ mir_free(szBuff);
+ PathToRelativeW(pszFolderName, pszFolderName);
+ return 0;
+}
+
+void IniParser::_DoInit()
+{
+ _isValid = false;
+ _eType = IT_UNKNOWN;
+ _szSection = nullptr;
+ _hFile = nullptr;
+ _hGlobalRes = nullptr;
+ _dwSizeOfRes = 0;
+ _pPosition = nullptr;
+ _pLineCallBackProc = nullptr;
+ _SecCheck = 0;
+}
+
+void IniParser::_LoadResourceIni(HINSTANCE hInst, const char * resourceName, const char * resourceType)
+{
+ if (_eType != IT_UNKNOWN)
+ return;
+
+ HRSRC hRSrc = FindResourceA(hInst, resourceName, resourceType);
+ if (!hRSrc)
+ return;
+
+ _hGlobalRes = LoadResource(hInst, hRSrc);
+ if (!_hGlobalRes)
+ return;
+
+ _dwSizeOfRes = SizeofResource(hInst, hRSrc);
+ _pPosition = (char*)LockResource(_hGlobalRes);
+
+ _isValid = true;
+ _eType = IT_RESOURCE;
+}
+
+HRESULT IniParser::_DoParseFile()
+{
+ char szLine[MAX_LINE_LEN];
+ _nLine = 0;
+ while (fgets(szLine, _countof(szLine), _hFile) != nullptr) {
+ size_t len = 0;
+ char *pLine = (char *)_RemoveTailings(szLine, len);
+ if (len > 0) {
+ pLine[len] = '\0';
+ if (!_DoParseLine(pLine))
+ return E_FAIL;
+ }
+ else _nLine++;
+ };
+
+ return S_OK;
+}
+
+HRESULT IniParser::_DoParseResource()
+{
+ _nLine = 0;
+ char szLine[MAX_LINE_LEN];
+ char *pos = (char *)_pPosition;
+
+ while (pos < _pPosition + _dwSizeOfRes) {
+ int i = 0;
+ while (pos < _pPosition + _dwSizeOfRes && *pos != '\n' && *pos != '\0' && i < MAX_LINE_LEN - 1) {
+ if ((*pos) != '\r')
+ szLine[i++] = *pos;
+ pos++;
+ }
+ szLine[i] = '\0';
+ pos++;
+
+ size_t len = 0;
+ char *pLine = (char *)_RemoveTailings(szLine, len);
+ if (len > 0) {
+ pLine[len] = '\0';
+ if (!_DoParseLine(pLine))
+ return E_FAIL;
+ }
+ else _nLine++;
+ }
+ return S_OK;
+}
+
+const char *IniParser::_RemoveTailings(const char *szLine, size_t &len)
+{
+ const char *pStart = szLine;
+ while (*pStart == ' ' || *pStart == '\t')
+ pStart++; //skip spaces at begin
+ const char *pEnd = pStart + mir_strlen(pStart);
+ while (pEnd > pStart && (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n' || *pEnd == '\r'))
+ pEnd--;
+
+ len = pEnd - pStart;
+ return pStart;
+}
+
+BOOL IniParser::_DoParseLine(char *szLine)
+{
+ _nLine++;
+ size_t len = mir_strlen(szLine);
+ if (len == 0)
+ return TRUE;
+
+ switch (szLine[0]) {
+ case ';':
+ return TRUE; // start of comment is found
+
+ case '[':
+ //New section start here
+ mir_free(_szSection);
+ _szSection = nullptr;
+ {
+ char *tbuf = szLine + 1; // skip [
+
+ char *ebuf = tbuf;
+
+ while (*ebuf != ']' && *ebuf != '\0') ebuf++;
+ if (*ebuf == '\0')
+ return FALSE; // no close bracket
+
+ uint32_t sectionLen = ebuf - tbuf + 1;
+ _szSection = (char*)mir_alloc(sectionLen + 1);
+ mir_strncpy(_szSection, tbuf, sectionLen);
+ _szSection[sectionLen] = '\0';
+ }
+ return TRUE;
+
+ default:
+ if (!_szSection)
+ return TRUE; //param found out of section
+
+ char *keyName = szLine;
+ char *keyValue = szLine;
+
+ size_t eqPlace = 0, len2 = mir_strlen(keyName);
+ while (eqPlace < len2 && keyName[eqPlace] != '=')
+ eqPlace++; //find '='
+
+ if (eqPlace == 0 || eqPlace == len2)
+ return TRUE; // = not found or no key name //say false
+
+ keyName[eqPlace] = '\0';
+
+ keyValue = keyName + eqPlace + 1;
+
+ //remove tail spaces in Name
+ rtrim(keyName);
+ while (*keyValue) {
+ if (!isspace(*keyValue))
+ break;
+ keyValue++;
+ }
+ rtrim(keyValue);
+ _pLineCallBackProc(_szSection, keyName, keyValue, this);
+ }
+ return TRUE;
+}
+//////////////////////////////////////////////////////////////////////////
+// End of IniParser
+//////////////////////////////////////////////////////////////////////////
+
+HRESULT SkinEngineLoadModule()
+{
+ ModernSkinButtonLoadModule();
+ MainModernMaskList = (LISTMODERNMASK*)mir_calloc(sizeof(LISTMODERNMASK));
+ //init variables
+ g_SkinObjectList.dwObjLPAlocated = 0;
+ g_SkinObjectList.dwObjLPReserved = 0;
+ g_SkinObjectList.pObjects = nullptr;
+ // Initialize GDI+
+ InitGdiPlus();
+ AniAva_InitModule();
+ //create services
+ CreateServiceFunction(MS_SKIN_DRAWGLYPH, ske_Service_DrawGlyph);
+ CreateServiceFunction(MS_SKINENG_UPTATEFRAMEIMAGE, ske_Service_UpdateFrameImage);
+ CreateServiceFunction(MS_SKINENG_INVALIDATEFRAMEIMAGE, ske_Service_InvalidateFrameImage);
+ CreateServiceFunction(MS_SKINENG_ALPHATEXTOUT, ske_Service_AlphaTextOut);
+ CreateServiceFunction(MS_SKINENG_DRAWICONEXFIX, ske_Service_DrawIconEx);
+
+ CreateServiceFunction(MS_DRAW_TEXT_WITH_EFFECT, ske_Service_DrawTextWithEffect);
+
+ //create event handle
+ hSkinLoadedEvent = HookEvent(ME_SKIN_SERVICESCREATED, CLUI_OnSkinLoad);
+ NotifyEventHooks(g_CluiData.hEventSkinServicesCreated, 0, 0);
+ return S_OK;
+}
+
+int SkinEngineUnloadModule()
+{
+ //unload services
+ ModernSkinButtonUnloadModule(0, 0);
+ ske_UnloadSkin(&g_SkinObjectList);
+
+ mir_free_and_nil(g_SkinObjectList.pObjects);
+ mir_free_and_nil(g_SkinObjectList.pMaskList);
+ mir_free_and_nil(MainModernMaskList);
+
+ for (auto &it : arEffectStack)
+ mir_free(it);
+ arEffectStack.destroy();
+
+ if (g_pCachedWindow) {
+ SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld);
+ SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ DeleteObject(g_pCachedWindow->hImageDIB);
+ DeleteDC(g_pCachedWindow->hBackDC);
+ DeleteDC(g_pCachedWindow->hImageDC);
+ ReleaseDC(nullptr, g_pCachedWindow->hScreenDC);
+ mir_free_and_nil(g_pCachedWindow);
+ }
+ GdiFlush();
+ DestroyHookableEvent(g_CluiData.hEventSkinServicesCreated);
+ AniAva_UnloadModule();
+ ShutdownGdiPlus();
+ //free variables
+ return 1;
+}
+
+BOOL ske_AlphaBlend(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction)
+{
+ if (g_CluiData.fDisableSkinEngine && !(blendFunction.BlendFlags & 128)) {
+ if (nWidthDest != nWidthSrc || nHeightDest != nHeightSrc)
+ return StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, SRCCOPY);
+ return BitBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
+ }
+
+ if (blendFunction.BlendFlags & 128) // Use gdi+ engine
+ return GDIPlus_AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
+ hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
+ &blendFunction);
+
+ blendFunction.BlendFlags &= ~128;
+ return AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction);
+}
+
+struct DCBUFFER
+{
+ HDC hdcOwnedBy;
+ int nUsageID;
+ int width;
+ int height;
+ void *pImage;
+ HDC hDC;
+ HBITMAP oldBitmap;
+ HBITMAP hBitmap;
+ uint32_t dwDestroyAfterTime;
+};
+
+static int SortBufferList(const DCBUFFER *buf1, const DCBUFFER *buf2)
+{
+ if (buf1->hdcOwnedBy != buf2->hdcOwnedBy) return (int)(buf1->hdcOwnedBy < buf2->hdcOwnedBy);
+ else if (buf1->nUsageID != buf2->nUsageID) return (int)(buf1->nUsageID < buf2->nUsageID);
+ else return (int)(buf1->hDC < buf2->hDC);
+}
+
+LIST<DCBUFFER> BufferList(2, SortBufferList);
+mir_cs BufferListCS;
+
+enum
+{
+ BUFFER_DRAWICON = 0,
+ BUFFER_DRAWIMAGE
+};
+
+HDC ske_RequestBufferDC(HDC hdcOwner, int dcID, int width, int height, BOOL fClear)
+{
+ mir_cslock lck(BufferListCS);
+ //Try to find DC in buffer list
+ DCBUFFER buf;
+ buf.hdcOwnedBy = hdcOwner;
+ buf.nUsageID = dcID;
+ buf.hDC = nullptr;
+ DCBUFFER *pBuf = BufferList.find(&buf);
+ if (!pBuf) {
+ //if not found - allocate it
+ pBuf = (DCBUFFER *)mir_alloc(sizeof(DCBUFFER));
+ *pBuf = buf;
+ pBuf->width = width;
+ pBuf->height = height;
+ pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage));
+ pBuf->hDC = CreateCompatibleDC(hdcOwner);
+ pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap);
+ pBuf->dwDestroyAfterTime = 0;
+ BufferList.insert(pBuf);
+ }
+ else {
+ if (pBuf->width != width || pBuf->height != height) {
+ //resize
+ SelectObject(pBuf->hDC, pBuf->oldBitmap);
+ DeleteObject(pBuf->hBitmap);
+ pBuf->width = width;
+ pBuf->height = height;
+ pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage));
+ pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap);
+ }
+ else if (fClear)
+ memset(pBuf->pImage, 0, width*height*sizeof(uint32_t));
+ }
+ pBuf->dwDestroyAfterTime = 0;
+ return pBuf->hDC;
+}
+
+int ske_ReleaseBufferDC(HDC hDC, int keepTime)
+{
+ uint32_t dwCurrentTime = GetTickCount();
+
+ // Try to find DC in buffer list - set flag to be release after time;
+ mir_cslock lck(BufferListCS);
+ for (auto &it : BufferList.rev_iter()) {
+ if (it) {
+ if (hDC != nullptr && it->hDC == hDC) {
+ it->dwDestroyAfterTime = dwCurrentTime + keepTime;
+ break;
+ }
+
+ if ((it->dwDestroyAfterTime && it->dwDestroyAfterTime < dwCurrentTime) || keepTime == -1) {
+ SelectObject(it->hDC, it->oldBitmap);
+ DeleteObject(it->hBitmap);
+ DeleteDC(it->hDC);
+ mir_free(BufferList.removeItem(&it));
+ }
+ }
+ }
+ return 0;
+}
+
+BOOL ske_SetRgnOpaque(HDC memdc, HRGN hrgn, BOOL force)
+{
+ if (g_CluiData.fDisableSkinEngine && !force) return TRUE;
+ uint32_t rgnsz = GetRegionData(hrgn, 0, nullptr);
+ RGNDATA *rdata = (RGNDATA *)mir_alloc(rgnsz);
+ GetRegionData(hrgn, rgnsz, rdata);
+ RECT *rect = (RECT *)rdata->Buffer;
+ for (uint32_t d = 0; d < rdata->rdh.nCount; d++) {
+ ske_SetRectOpaque(memdc, &rect[d], force);
+ }
+ mir_free(rdata);
+ return TRUE;
+}
+
+
+BOOL ske_SetRectOpaque(HDC memdc, RECT *fr, BOOL force)
+{
+ int f = 0;
+ uint8_t *bits;
+ BITMAP bmp;
+
+ if (g_CluiData.fDisableSkinEngine && !force)
+ return TRUE;
+
+ HBITMAP hbmp = (HBITMAP)GetCurrentObject(memdc, OBJ_BITMAP);
+ GetObject(hbmp, sizeof(bmp), &bmp);
+
+ if (bmp.bmPlanes != 1)
+ return FALSE;
+
+ if (!bmp.bmBits) {
+ f = 1;
+ bits = (uint8_t*)mir_alloc(bmp.bmWidthBytes*bmp.bmHeight);
+ GetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits);
+ }
+ else
+ bits = (uint8_t*)bmp.bmBits;
+
+ int sx = (fr->left > 0) ? fr->left : 0;
+ int sy = (fr->top > 0) ? fr->top : 0;
+ int ex = (fr->right < bmp.bmWidth) ? fr->right : bmp.bmWidth;
+ int ey = (fr->bottom < bmp.bmHeight) ? fr->bottom : bmp.bmHeight;
+
+ int width = ex - sx;
+
+ uint8_t *pLine = ((uint8_t *)bits) + (bmp.bmHeight - sy - 1) * bmp.bmWidthBytes + (sx << 2) + 3;
+ for (int y = 0; y < (ey - sy); y++) {
+ uint8_t *pColumn = pLine;
+ for (int x = 0; x < width; x++) {
+ *pColumn = 255;
+ pColumn += 4;
+ }
+ pLine -= bmp.bmWidthBytes;
+ }
+ if (f) {
+ SetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits);
+ mir_free(bits);
+ }
+ // DeleteObject(hbmp);
+ return 1;
+}
+
+static BOOL ske_SkinFillRectByGlyph(HDC hDest, HDC hSource, RECT *rFill, RECT *rGlyph, RECT *rClip, uint8_t mode, uint8_t drawMode)
+{
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+
+ //initializations
+ if (mode == FM_STRETCH) {
+ HDC mem2dc = nullptr;
+ HBITMAP mem2bmp = nullptr, oldbmp = nullptr;
+ RECT wr;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ if (drawMode != 2) {
+ mem2dc = CreateCompatibleDC(hDest);
+ mem2bmp = ske_CreateDIB32(wr.right - wr.left, wr.bottom - wr.top);
+ oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+ }
+
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top,
+ hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
+ ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf);
+ }
+ else {
+ ske_AlphaBlend(hDest, rFill->left, rFill->top, rFill->right - rFill->left, rFill->bottom - rFill->top,
+ hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
+
+ }
+ }
+ else {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top,
+ hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
+ ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf);
+ }
+ if (drawMode != 2) {
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ return 1;
+ }
+ else if (mode == FM_TILE_VERT && (rGlyph->bottom - rGlyph->top > 0) && (rGlyph->right - rGlyph->left > 0)) {
+ RECT wr;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ HDC mem2dc = CreateCompatibleDC(hDest);
+ //SetStretchBltMode(mem2dc, HALFTONE);
+ HBITMAP mem2bmp = ske_CreateDIB32(wr.right - wr.left, rGlyph->bottom - rGlyph->top);
+ HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+ if (!oldbmp)
+ return 0;
+
+ /// draw here
+ {
+ int y = 0;
+ int w = rFill->right - rFill->left;
+ int h = rGlyph->bottom - rGlyph->top;
+ if (h > 0 && (wr.bottom - wr.top)*(wr.right - wr.left) != 0) {
+ w = wr.right - wr.left;
+ {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(mem2dc, -(wr.left - rFill->left), 0, rFill->right - rFill->left, h, hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, h, bf);
+ //StretchBlt(mem2dc,-(wr.left-rFill->left), 0, rFill->right-rFill->left,h,hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,h,SRCCOPY);
+ }
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ int dy = (wr.top - rFill->top) % h;
+ if (dy >= 0) {
+ y = wr.top;
+ int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top);
+ BitBlt(hDest, wr.left, y, w, ht, mem2dc, 0, dy, SRCCOPY);
+ }
+
+ y = wr.top + h - dy;
+ while (y < wr.bottom - h) {
+ BitBlt(hDest, wr.left, y, w, h, mem2dc, 0, 0, SRCCOPY);
+ y += h;
+ }
+ if (y <= wr.bottom)
+ BitBlt(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, SRCCOPY);
+
+ }
+ else {
+ y = wr.top;
+ while (y < wr.bottom - h) {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf);
+ y += h;
+ }
+ if (y <= wr.bottom) {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf);
+ }
+ }
+
+ }
+ else {
+ BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ int dy = (wr.top - rFill->top) % h;
+ if (dy >= 0) {
+ y = wr.top;
+ int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top);
+ ske_AlphaBlend(hDest, wr.left, y, w, ht, mem2dc, 0, dy, w, ht, bf2);
+ }
+
+ y = wr.top + h - dy;
+ while (y < wr.bottom - h) {
+ ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf2);
+ y += h;
+ }
+ if (y <= wr.bottom)
+ ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf2);
+ }
+ }
+ }
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ else if (mode == FM_TILE_HORZ && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0) && (rFill->bottom - rFill->top) > 0 && (rFill->right - rFill->left) > 0) {
+ RECT wr;
+ int w = rGlyph->right - rGlyph->left;
+ int h = rFill->bottom - rFill->top;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ h = wr.bottom - wr.top;
+ HDC mem2dc = CreateCompatibleDC(hDest);
+
+ HBITMAP mem2bmp = ske_CreateDIB32(w, h);
+ HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+
+ if (!oldbmp)
+ return 0;
+ /// draw here
+ {
+ int x = 0;
+ {
+ //SetStretchBltMode(mem2dc, HALFTONE);
+ //StretchBlt(mem2dc, 0, 0, w,h,hSource,rGlyph->left+(wr.left-rFill->left),rGlyph->top,w,h,SRCCOPY);
+
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(mem2dc, 0, -(wr.top - rFill->top), w, rFill->bottom - rFill->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf);
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY);
+ x += w;
+ }
+ if (x <= wr.right)
+ BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY);
+ }
+ else {
+ int dx = (wr.left - rFill->left) % w;
+ x = wr.left - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf);
+ }
+
+ }
+ else {
+ BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2);
+ }
+ }
+ }
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ else if (mode == FM_TILE_BOTH && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0)) {
+ int w = rGlyph->right - rGlyph->left;
+ int x = 0;
+ int h = rFill->bottom - rFill->top;
+ RECT wr;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ HDC mem2dc = CreateCompatibleDC(hDest);
+ HBITMAP mem2bmp = ske_CreateDIB32(w, wr.bottom - wr.top);
+ h = wr.bottom - wr.top;
+ HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+#ifdef _DEBUG
+ if (!oldbmp)
+ (nullptr, "Tile bitmap not selected", "ERROR", MB_OK);
+#endif
+ /// draw here
+ {
+ //fill temp bitmap
+ {
+ int dy = (wr.top - rFill->top) % (rGlyph->bottom - rGlyph->top);
+ int y = -dy;
+ while (y < wr.bottom - wr.top) {
+
+ ske_AlphaBlend(mem2dc, 0, y, w, rGlyph->bottom - rGlyph->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf);
+ y += rGlyph->bottom - rGlyph->top;
+ }
+
+ //--
+ //end temp bitmap
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY);
+ x += w;
+ }
+ if (x <= wr.right)
+ BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY);
+ }
+ else {
+ int dx = (wr.left - rFill->left) % w;
+ x = wr.left - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf);
+ }
+
+ }
+ else {
+ BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2);
+ }
+ }
+ }
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ return 1;
+
+}
+
+HBITMAP ske_CreateDIB32(int cx, int cy)
+{
+ return ske_CreateDIB32Point(cx, cy, nullptr);
+}
+
+HBITMAP ske_CreateDIB32Point(int cx, int cy, void **bits)
+{
+ if (cx < 0 || cy < 0)
+ return nullptr;
+
+ BITMAPINFO RGB32BitsBITMAPINFO = { 0 };
+ RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ RGB32BitsBITMAPINFO.bmiHeader.biWidth = cx;//bm.bmWidth;
+ RGB32BitsBITMAPINFO.bmiHeader.biHeight = cy;//bm.bmHeight;
+ RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1;
+ RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32;
+ // pointer used for direct Bitmap pixels access
+
+ UINT *ptPixels;
+ HBITMAP DirectBitmap = CreateDIBSection(nullptr,
+ &RGB32BitsBITMAPINFO,
+ DIB_RGB_COLORS,
+ (void **)&ptPixels,
+ nullptr, 0);
+ if ((DirectBitmap == nullptr || ptPixels == nullptr) && cx != 0 && cy != 0) {
+#ifdef _DEBUG
+ MessageBoxA(nullptr, "Object not allocated. Check GDI object count", "ERROR", MB_OK | MB_ICONERROR);
+ DebugBreak();
+#endif
+ ;
+ }
+ else memset(ptPixels, 0, cx*cy * 4);
+ if (bits != nullptr) *bits = ptPixels;
+ return DirectBitmap;
+}
+
+HRGN ske_CreateOpaqueRgn(uint8_t Level, bool Opaque)
+{
+ if (!g_pCachedWindow)
+ return nullptr;
+
+ RGBQUAD *buf = (RGBQUAD *)g_pCachedWindow->hImageDIBByte;
+ if (buf == nullptr)
+ return nullptr;
+
+ unsigned int cRect = 64;
+ PRGNDATA pRgnData = (PRGNDATA)mir_alloc(sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
+ memset(pRgnData, 0, sizeof(RGNDATAHEADER));
+ pRgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
+ pRgnData->rdh.iType = RDH_RECTANGLES;
+
+ for (int y = 0; y < g_pCachedWindow->Height; ++y) {
+ bool inside = false;
+ bool lastin = false;
+ unsigned int entry = 0;
+
+ for (int x = 0; x < g_pCachedWindow->Width; ++x) {
+ inside = Opaque ? (buf->rgbReserved > Level) : (buf->rgbReserved < Level);
+ ++buf;
+
+ if (inside != lastin) {
+ if (inside) {
+ lastin = true;
+ entry = x;
+ }
+ else {
+ if (pRgnData->rdh.nCount == cRect) {
+ cRect = cRect + 64;
+ pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
+ }
+ SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, x, g_pCachedWindow->Height - y + 1);
+
+ pRgnData->rdh.nCount++;
+ lastin = false;
+ }
+ }
+ }
+
+ if (lastin) {
+ if (pRgnData->rdh.nCount == cRect) {
+ cRect = cRect + 64;
+ pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
+ }
+ SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, g_pCachedWindow->Width, g_pCachedWindow->Height - y + 1);
+
+ pRgnData->rdh.nCount++;
+ }
+ }
+
+ HRGN hRgn = ExtCreateRegion(nullptr, sizeof(RGNDATAHEADER) + pRgnData->rdh.nCount*sizeof(RECT), (LPRGNDATA)pRgnData);
+ mir_free(pRgnData);
+ return hRgn;
+}
+
+static int ske_DrawSkinObject(SKINDRAWREQUEST *preq, GLYPHOBJECT *pobj)
+{
+ HDC memdc = nullptr, glyphdc = nullptr;
+ int k = 0;
+ //BITMAP bmp = {0};
+ HBITMAP membmp = nullptr, oldbmp = nullptr, oldglyph = nullptr;
+ uint8_t Is32Bit = 0;
+ RECT PRect;
+ POINT mode2offset = { 0 };
+ int depth = 0;
+ int mode = 0; //0-FastDraw, 1-DirectAlphaDraw, 2-BufferedAlphaDraw
+
+ if (!(preq && pobj)) return -1;
+ if ((!pobj->hGlyph || pobj->hGlyph == (HBITMAP)-1) && ((pobj->Style & 7) == ST_IMAGE || (pobj->Style & 7) == ST_FRAGMENT || (pobj->Style & 7) == ST_SOLARIZE)) return 0;
+ // Determine painting mode
+ depth = GetDeviceCaps(preq->hDC, BITSPIXEL);
+ depth = depth < 16 ? 16 : depth;
+ Is32Bit = pobj->bmBitsPixel == 32;
+ if ((!Is32Bit && pobj->dwAlpha == 255) && pobj->Style != ST_BRUSH) mode = 0;
+ else if (pobj->dwAlpha == 255 && pobj->Style != ST_BRUSH) mode = 1;
+ else mode = 2;
+ // End painting mode
+
+ //force mode
+
+ if (preq->rcClipRect.bottom - preq->rcClipRect.top*preq->rcClipRect.right - preq->rcClipRect.left == 0)
+ preq->rcClipRect = preq->rcDestRect;
+ IntersectRect(&PRect, &preq->rcDestRect, &preq->rcClipRect);
+ if (IsRectEmpty(&PRect)) {
+ return 0;
+ }
+ if (mode == 2) {
+ memdc = CreateCompatibleDC(preq->hDC);
+ membmp = ske_CreateDIB32(PRect.right - PRect.left, PRect.bottom - PRect.top);
+ oldbmp = (HBITMAP)SelectObject(memdc, membmp);
+ if (oldbmp == nullptr) {
+ SelectObject(memdc, oldbmp);
+ DeleteDC(memdc);
+ DeleteObject(membmp);
+ return 0;
+ }
+ }
+
+ if (mode != 2) memdc = preq->hDC;
+ {
+ if (pobj->hGlyph && pobj->hGlyph != (HBITMAP)-1) {
+ glyphdc = CreateCompatibleDC(preq->hDC);
+ oldglyph = (HBITMAP)SelectObject(glyphdc, pobj->hGlyph);
+ }
+ // Drawing
+ {
+ RECT rFill, rGlyph, rClip;
+ if ((pobj->Style & 7) == ST_BRUSH) {
+ HBRUSH br = CreateSolidBrush(pobj->dwColor);
+ RECT fr;
+ if (mode == 2) {
+ SetRect(&fr, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top);
+ FillRect(memdc, &fr, br);
+ ske_SetRectOpaque(memdc, &fr);
+ // FillRectAlpha(memdc,&fr,pobj->dwColor|0xFF000000);
+ }
+ else {
+ fr = PRect;
+ // SetRect(&fr, 0, 0, PRect.right-PRect.left,PRect.bottom-PRect.top);
+ FillRect(preq->hDC, &fr, br);
+ }
+ DeleteObject(br);
+ k = -1;
+ }
+ else {
+ if (mode == 2) {
+ mode2offset.x = PRect.left;
+ mode2offset.y = PRect.top;
+ OffsetRect(&PRect, -mode2offset.x, -mode2offset.y);
+ }
+ rClip = (preq->rcClipRect);
+
+ {
+ int lft = 0;
+ int top = 0;
+ int rgh = pobj->bmWidth;
+ int btm = pobj->bmHeight;
+ if ((pobj->Style & 7) == ST_FRAGMENT) {
+ lft = pobj->clipArea.x;
+ top = pobj->clipArea.y;
+ rgh = min(rgh, lft + pobj->szclipArea.cx);
+ btm = min(btm, top + pobj->szclipArea.cy);
+ }
+
+ // Draw center...
+ if (1) {
+ rFill.top = preq->rcDestRect.top + pobj->dwTop;
+ rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.left = preq->rcDestRect.left + pobj->dwLeft;
+ rFill.right = preq->rcDestRect.right - pobj->dwRight;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+ rGlyph.top = top + pobj->dwTop;
+ rGlyph.left = lft + pobj->dwLeft;
+ rGlyph.right = rgh - pobj->dwRight;
+ rGlyph.bottom = btm - pobj->dwBottom;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode, mode);
+ }
+
+ // Draw top side...
+ if (1) {
+ rFill.top = preq->rcDestRect.top;
+ rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
+ rFill.left = preq->rcDestRect.left + pobj->dwLeft;
+ rFill.right = preq->rcDestRect.right - pobj->dwRight;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+ rGlyph.top = top + 0;
+ rGlyph.left = lft + pobj->dwLeft;
+ rGlyph.right = rgh - pobj->dwRight;
+ rGlyph.bottom = top + pobj->dwTop;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode);
+ }
+ // Draw bottom side...
+ if (1) {
+ rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.bottom = preq->rcDestRect.bottom;
+ rFill.left = preq->rcDestRect.left + pobj->dwLeft;
+ rFill.right = preq->rcDestRect.right - pobj->dwRight;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = btm - pobj->dwBottom;
+ rGlyph.left = lft + pobj->dwLeft;
+ rGlyph.right = rgh - pobj->dwRight;
+ rGlyph.bottom = btm;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode);
+ }
+ // Draw left side...
+ if (1) {
+ rFill.top = preq->rcDestRect.top + pobj->dwTop;
+ rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.left = preq->rcDestRect.left;
+ rFill.right = preq->rcDestRect.left + pobj->dwLeft;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top + pobj->dwTop;
+ rGlyph.left = lft;
+ rGlyph.right = lft + pobj->dwLeft;
+ rGlyph.bottom = btm - pobj->dwBottom;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode);
+ }
+
+ // Draw right side...
+ if (1) {
+ rFill.top = preq->rcDestRect.top + pobj->dwTop;
+ rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.left = preq->rcDestRect.right - pobj->dwRight;
+ rFill.right = preq->rcDestRect.right;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top + pobj->dwTop;
+ rGlyph.left = rgh - pobj->dwRight;
+ rGlyph.right = rgh;
+ rGlyph.bottom = btm - pobj->dwBottom;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode);
+ }
+
+
+ // Draw Top-Left corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.top;
+ rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
+ rFill.left = preq->rcDestRect.left;
+ rFill.right = preq->rcDestRect.left + pobj->dwLeft;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top;
+ rGlyph.left = lft;
+ rGlyph.right = lft + pobj->dwLeft;
+ rGlyph.bottom = top + pobj->dwTop;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+ // Draw Top-Right corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.top;
+ rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
+ rFill.left = preq->rcDestRect.right - pobj->dwRight;
+ rFill.right = preq->rcDestRect.right;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top;
+ rGlyph.left = rgh - pobj->dwRight;
+ rGlyph.right = rgh;
+ rGlyph.bottom = top + pobj->dwTop;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+
+ // Draw Bottom-Left corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.bottom = preq->rcDestRect.bottom;
+ rFill.left = preq->rcDestRect.left;
+ rFill.right = preq->rcDestRect.left + pobj->dwLeft;
+
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.left = lft;
+ rGlyph.right = lft + pobj->dwLeft;
+ rGlyph.top = btm - pobj->dwBottom;
+ rGlyph.bottom = btm;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+ // Draw Bottom-Right corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.bottom = preq->rcDestRect.bottom;
+ rFill.left = preq->rcDestRect.right - pobj->dwRight;
+ rFill.right = preq->rcDestRect.right;
+
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+ rGlyph.left = rgh - pobj->dwRight;
+ rGlyph.right = rgh;
+ rGlyph.top = btm - pobj->dwBottom;
+ rGlyph.bottom = btm;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+ }
+
+ }
+
+ if ((k > 0 || k == -1) && mode == 2) {
+ {
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, pobj->dwAlpha, uint8_t(pobj->bmBitsPixel == 32 && pobj->Style != ST_BRUSH ? AC_SRC_ALPHA : 0) };
+ OffsetRect(&PRect, mode2offset.x, mode2offset.y);
+ ske_AlphaBlend(preq->hDC, PRect.left, PRect.top, PRect.right - PRect.left, PRect.bottom - PRect.top,
+ memdc, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top, bf);
+ }
+ }
+ }
+ //free GDI resources
+ //--++--
+
+ //free GDI resources
+ {
+
+ if (oldglyph) SelectObject(glyphdc, oldglyph);
+ if (glyphdc) DeleteDC(glyphdc);
+ }
+ if (mode == 2) {
+ SelectObject(memdc, oldbmp);
+ DeleteDC(memdc);
+ DeleteObject(membmp);
+ }
+
+ }
+ if (pobj->plTextList && pobj->plTextList->realCount > 0) {
+ HFONT hOldFont;
+ for (int i = 0; i < pobj->plTextList->realCount; i++) {
+ GLYPHTEXT *gt = (GLYPHTEXT *)pobj->plTextList->items[i];
+ if (!gt->hFont) {
+ if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) {
+ int j = 0;
+ for (j = 0; j < gl_plSkinFonts->realCount; j++) {
+ SKINFONT *sf = (SKINFONT*)gl_plSkinFonts->items[j];
+ if (sf->szFontID && !mir_strcmp(sf->szFontID, gt->szFontID)) {
+ gt->hFont = sf->hFont;
+ break;
+ }
+ }
+ }
+ if (!gt->hFont) gt->hFont = (HFONT)-1;
+ }
+ if (gt->hFont != (HFONT)-1) {
+ RECT rc = { 0 };
+ hOldFont = (HFONT)SelectObject(preq->hDC, gt->hFont);
+
+
+
+ if (gt->RelativeFlags & 2) rc.left = preq->rcDestRect.right + gt->iLeft;
+ else if (gt->RelativeFlags & 1) rc.left = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iLeft;
+ else rc.left = preq->rcDestRect.left + gt->iLeft;
+
+ if (gt->RelativeFlags & 8) rc.top = preq->rcDestRect.bottom + gt->iTop;
+ else if (gt->RelativeFlags & 4) rc.top = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iTop;
+ else rc.top = preq->rcDestRect.top + gt->iTop;
+
+ if (gt->RelativeFlags & 32) rc.right = preq->rcDestRect.right + gt->iRight;
+ else if (gt->RelativeFlags & 16) rc.right = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iRight;
+ else rc.right = preq->rcDestRect.left + gt->iRight;
+
+ if (gt->RelativeFlags & 128) rc.bottom = preq->rcDestRect.bottom + gt->iBottom;
+ else if (gt->RelativeFlags & 64) rc.bottom = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iBottom;
+ else rc.bottom = preq->rcDestRect.top + gt->iBottom;
+
+ ske_AlphaTextOut(preq->hDC, gt->stText, -1, &rc, gt->dwFlags, gt->dwColor);
+ SelectObject(preq->hDC, hOldFont);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+int ske_AddDescriptorToSkinObjectList(SKINOBJECTDESCRIPTOR *lpDescr, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin ? Skin : &g_SkinObjectList);
+ if (!mir_strcmpi(lpDescr->szObjectID, "_HEADER_"))
+ return 0;
+ //check if new object allready presents.
+ for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++)
+ if (!mir_strcmp(sk->pObjects[i].szObjectID, lpDescr->szObjectID))
+ return 0;
+ // Realocated list to add space for new object
+ if (sk->dwObjLPAlocated + 1 > sk->dwObjLPReserved) {
+ sk->pObjects = (SKINOBJECTDESCRIPTOR*)mir_realloc(sk->pObjects, sizeof(SKINOBJECTDESCRIPTOR)*(sk->dwObjLPReserved + 1)/*alloc step*/);
+ sk->dwObjLPReserved++;
+ }
+ { //filling new objects field
+ sk->pObjects[sk->dwObjLPAlocated].bType = lpDescr->bType;
+ sk->pObjects[sk->dwObjLPAlocated].Data = nullptr;
+ sk->pObjects[sk->dwObjLPAlocated].szObjectID = mir_strdup(lpDescr->szObjectID);
+ // sk->Objects[sk->dwObjLPAlocated].szObjectName = mir_strdup(lpDescr->szObjectName);
+ if (lpDescr->Data != nullptr) { //Copy defaults values
+ switch (lpDescr->bType) {
+ case OT_GLYPHOBJECT:
+ {
+ GLYPHOBJECT *gl = (GLYPHOBJECT *)lpDescr->Data;
+ sk->pObjects[sk->dwObjLPAlocated].Data = mir_alloc(sizeof(GLYPHOBJECT));
+ GLYPHOBJECT *obdat = (GLYPHOBJECT *)sk->pObjects[sk->dwObjLPAlocated].Data;
+ memcpy(obdat, gl, sizeof(GLYPHOBJECT));
+ if (gl->szFileName != nullptr) {
+ obdat->szFileName = mir_strdup(gl->szFileName);
+ replaceStr(gl->szFileName, nullptr);
+ }
+ else obdat->szFileName = nullptr;
+
+ obdat->hGlyph = nullptr;
+ break;
+ }
+ }
+
+ }
+ }
+ sk->dwObjLPAlocated++;
+ return 1;
+}
+
+static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
+ return skin_FindObjectByRequest((char *)szName, sk->pMaskList);
+}
+
+static SKINOBJECTDESCRIPTOR* ske_FindObjectByMask(MODERNMASK *pModernMask, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
+ return sk->pMaskList ? skin_FindObjectByMask(pModernMask, sk->pMaskList) : nullptr;
+}
+
+SKINOBJECTDESCRIPTOR* ske_FindObjectByName(const char *szName, uint8_t objType, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
+ for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++) {
+ if (sk->pObjects[i].bType == objType || objType == OT_ANY) {
+ if (!mir_strcmp(sk->pObjects[i].szObjectID, szName))
+ return &(sk->pObjects[i]);
+ }
+ }
+ return nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Paint glyph
+// wParam - LPSKINDRAWREQUEST
+// lParam - possible direct pointer to modern mask
+//////////////////////////////////////////////////////////////////////////
+
+INT_PTR ske_Service_DrawGlyph(WPARAM wParam, LPARAM lParam)
+{
+ auto *preq = (SKINDRAWREQUEST *)wParam;
+ if (preq == nullptr)
+ return -1;
+
+ mir_cslock lck(cs_SkinChanging);
+
+ SKINOBJECTDESCRIPTOR *pgl = (lParam ? ske_FindObjectByMask((MODERNMASK*)lParam, nullptr) : ske_FindObject(preq->szObjectID, nullptr));
+ if (pgl == nullptr) return -1;
+ if (pgl->Data == nullptr) return -1;
+
+ GLYPHOBJECT *gl = (GLYPHOBJECT*)pgl->Data;
+ int iStyle = gl->Style & 7;
+ if (iStyle == ST_SKIP)
+ return ST_SKIP;
+
+ if (gl->hGlyph == nullptr && gl->hGlyph != (HBITMAP)-1 && (iStyle == ST_IMAGE || iStyle == ST_FRAGMENT || iStyle == ST_SOLARIZE)) {
+ if (gl->szFileName) {
+ gl->hGlyph = ske_LoadGlyphImage(_A2T(gl->szFileName));
+ if (gl->hGlyph) {
+ BITMAP bmp = { 0 };
+ GetObject(gl->hGlyph, sizeof(BITMAP), &bmp);
+ gl->bmBitsPixel = (uint8_t)bmp.bmBitsPixel;
+ gl->bmHeight = bmp.bmHeight;
+ gl->bmWidth = bmp.bmWidth;
+ }
+ else gl->hGlyph = (HBITMAP)-1; //invalid
+ }
+ }
+ return ske_DrawSkinObject(preq, gl);
+}
+
+
+void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult)
+{
+ BITMAP bmp;
+ BOOL flag = FALSE;
+ uint8_t *pBitmapBits;
+ uint32_t Len;
+ int bh, bw, y, x;
+
+ GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp);
+ bh = bmp.bmHeight;
+ bw = bmp.bmWidth;
+ Len = bh * bw * 4;
+ flag = (bmp.bmBits == nullptr);
+ if (flag) {
+ pBitmapBits = (LPBYTE)mir_alloc(Len);
+ GetBitmapBits(hbmp, Len, pBitmapBits);
+ }
+ else
+ pBitmapBits = (uint8_t *)bmp.bmBits;
+ for (y = 0; y < bh; ++y) {
+ uint8_t *pPixel = pBitmapBits + bw * 4 * y;
+
+ for (x = 0; x < bw; ++x) {
+ if (Mult) {
+ pPixel[0] = pPixel[0] * pPixel[3] / 255;
+ pPixel[1] = pPixel[1] * pPixel[3] / 255;
+ pPixel[2] = pPixel[2] * pPixel[3] / 255;
+ }
+ else {
+ pPixel[3] = 255;
+ }
+ pPixel += 4;
+ }
+ }
+ if (flag) {
+ Len = SetBitmapBits(hbmp, Len, pBitmapBits);
+ mir_free(pBitmapBits);
+ }
+ return;
+}
+
+int ske_GetFullFilename(wchar_t *buf, const wchar_t *file, wchar_t *skinfolder, BOOL madeAbsolute)
+{
+ wchar_t *SkinPlace = db_get_wsa(0, SKIN, "SkinFolder");
+ if (SkinPlace == nullptr)
+ SkinPlace = mir_wstrdup(L"\\Skin\\default");
+
+ wchar_t b2[MAX_PATH];
+ if (file[0] != '\\' && file[1] != ':')
+ mir_snwprintf(b2, L"%s\\%s", (skinfolder == nullptr) ? SkinPlace : ((INT_PTR)skinfolder != -1) ? skinfolder : L"", file);
+ else
+ mir_wstrncpy(b2, file, _countof(b2));
+
+ if (madeAbsolute) {
+ if (b2[0] == '\\' && b2[1] != '\\')
+ PathToAbsoluteW(b2 + 1, buf);
+ else
+ PathToAbsoluteW(b2, buf);
+ }
+ else mir_wstrncpy(buf, b2, MAX_PATH);
+
+ mir_free(SkinPlace);
+ return 0;
+}
+
+/*
+This function is required to load TGA to dib buffer myself
+Major part of routines is from http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga.c
+*/
+
+static BOOL ske_ReadTGAImageData(void *From, uint32_t fromSize, uint8_t *destBuf, uint32_t bufSize, BOOL RLE)
+{
+ uint8_t *pos = destBuf;
+ uint8_t *from = fromSize ? (uint8_t *)From : nullptr;
+ FILE *fp = !fromSize ? (FILE *)From : nullptr;
+ uint32_t destCount = 0;
+ uint32_t fromCount = 0;
+ if (!RLE) {
+ while (((from && fromCount < fromSize) || (fp && fromCount < bufSize))
+ && (destCount < bufSize)) {
+ uint8_t r = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ uint8_t g = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ uint8_t b = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ uint8_t a = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ pos[destCount++] = r;
+ pos[destCount++] = g;
+ pos[destCount++] = b;
+ pos[destCount++] = a;
+
+ if (destCount > bufSize) break;
+ if (from) if (fromCount < fromSize) break;
+ }
+ }
+ else {
+ uint8_t rgba[4];
+ uint8_t packet_header;
+ uint8_t *ptr = pos;
+ uint8_t size;
+ int i;
+ while (ptr < pos + bufSize) {
+ /* read first byte */
+ packet_header = from ? from[fromCount] : (uint8_t)fgetc(fp);
+ if (from) from++;
+ size = 1 + (packet_header & 0x7f);
+ if (packet_header & 0x80) {
+ /* run-length packet */
+ if (from) {
+ *((uint32_t*)rgba) = *((uint32_t*)(from + fromCount));
+ fromCount += 4;
+ }
+ else fread(rgba, sizeof(uint8_t), 4, fp);
+ for (i = 0; i < size; ++i, ptr += 4) {
+ ptr[2] = rgba[2];
+ ptr[1] = rgba[1];
+ ptr[0] = rgba[0];
+ ptr[3] = rgba[3];
+ }
+ }
+ else { /* not run-length packet */
+ for (i = 0; i < size; ++i, ptr += 4) {
+ ptr[0] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ ptr[1] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ ptr[2] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ ptr[3] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+static HBITMAP ske_LoadGlyphImage_TGA(const wchar_t *szFilename)
+{
+ uint8_t *colormap = nullptr;
+ int cx = 0, cy = 0;
+ BOOL err = FALSE;
+ tga_header_t header;
+ if (!szFilename) return nullptr;
+ if (!wildcmpiw(szFilename, L"*\\*%.tga")) {
+ //Loading TGA image from file
+ FILE *fp = _wfopen(szFilename, L"rb");
+ if (!fp) {
+ TRACEVAR("error: couldn't open \"%s\"!\n", szFilename);
+ return nullptr;
+ }
+ /* read header */
+ fread(&header, sizeof(tga_header_t), 1, fp);
+ if ((header.pixel_depth != 32) || ((header.image_type != 10) && (header.image_type != 2))) {
+ fclose(fp);
+ return nullptr;
+ }
+
+ /*memory allocation */
+ colormap = (uint8_t*)mir_alloc(header.width*header.height * 4);
+ cx = header.width;
+ cy = header.height;
+ fseek(fp, header.id_lenght, SEEK_CUR);
+ fseek(fp, header.cm_length, SEEK_CUR);
+ err = !ske_ReadTGAImageData((void*)fp, 0, colormap, header.width*header.height * 4, header.image_type == 10);
+ fclose(fp);
+ }
+ else {
+ /* reading from resources IDR_TGA_DEFAULT_SKIN */
+ HRSRC hRSrc = FindResourceA(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_TGA_DEFAULT_SKIN), "TGA");
+ if (!hRSrc) return nullptr;
+ HGLOBAL hRes = LoadResource(g_plugin.getInst(), hRSrc);
+ if (!hRes) return nullptr;
+ uint32_t size = SizeofResource(g_plugin.getInst(), hRSrc);
+ uint8_t *mem = (uint8_t*)LockResource(hRes);
+ if (size > sizeof(header)) {
+ tga_header_t *tgahdr = (tga_header_t*)mem;
+ if (tgahdr->pixel_depth == 32 && (tgahdr->image_type == 2 || tgahdr->image_type == 10)) {
+ colormap = (uint8_t*)mir_alloc(tgahdr->width*tgahdr->height * 4);
+ cx = tgahdr->width;
+ cy = tgahdr->height;
+ ske_ReadTGAImageData((void*)(mem + sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), size - (sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), colormap, cx*cy * 4, tgahdr->image_type == 10);
+ }
+ }
+ FreeResource(hRes);
+ }
+
+ if (colormap == nullptr)
+ return nullptr;
+
+ // create dib section
+ uint8_t *pt;
+ HBITMAP hbmp = ske_CreateDIB32Point(cx, cy, (void**)&pt);
+ if (hbmp)
+ memcpy(pt, colormap, cx*cy * 4);
+ mir_free(colormap);
+ return hbmp;
+}
+
+static HBITMAP ske_LoadGlyphImageByDecoders(const wchar_t *tszFileName)
+{
+ if (!wcschr(tszFileName, '%') && !PathFileExists(tszFileName))
+ return nullptr;
+
+ const wchar_t *ext = wcsrchr(tszFileName, '.');
+ if (ext == nullptr)
+ return nullptr;
+
+ BITMAP bmpInfo;
+ HBITMAP hBitmap;
+ bool f = false;
+
+ if (!mir_wstrcmpi(ext, L".tga")) {
+ hBitmap = ske_LoadGlyphImage_TGA(tszFileName);
+ f = true;
+ }
+ else hBitmap = Bitmap_Load(tszFileName);
+
+ if (hBitmap == nullptr)
+ return nullptr;
+
+ GetObject(hBitmap, sizeof(BITMAP), &bmpInfo);
+ if (bmpInfo.bmBitsPixel == 32)
+ ske_PreMultiplyChannels(hBitmap, f);
+ else {
+ HDC dc32 = CreateCompatibleDC(nullptr);
+ HDC dc24 = CreateCompatibleDC(nullptr);
+ HBITMAP hBitmap32 = ske_CreateDIB32(bmpInfo.bmWidth, bmpInfo.bmHeight);
+ HBITMAP obmp24 = (HBITMAP)SelectObject(dc24, hBitmap);
+ HBITMAP obmp32 = (HBITMAP)SelectObject(dc32, hBitmap32);
+ BitBlt(dc32, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, dc24, 0, 0, SRCCOPY);
+ SelectObject(dc24, obmp24);
+ SelectObject(dc32, obmp32);
+ DeleteDC(dc24);
+ DeleteDC(dc32);
+ DeleteObject(hBitmap);
+ hBitmap = hBitmap32;
+ ske_PreMultiplyChannels(hBitmap, 0);
+ }
+ return hBitmap;
+}
+
+static HBITMAP ske_skinLoadGlyphImage(const wchar_t *tszFileName)
+{
+ if (!wildcmpiw(tszFileName, L"*.tga"))
+ return GDIPlus_LoadGlyphImage(tszFileName);
+
+ return ske_LoadGlyphImageByDecoders(tszFileName);
+}
+
+HBITMAP ske_LoadGlyphImage(const wchar_t *tszFileName)
+{
+ // try to find image in loaded
+ wchar_t szFile[MAX_PATH] = { 0 };
+ ske_GetFullFilename(szFile, tszFileName, g_SkinObjectList.szSkinPlace, TRUE);
+
+ mir_cslock lck(cs_SkinChanging);
+
+ if (pLoadedImages) {
+ for (uint32_t i = 0; i < dwLoadedImagesCount; i++) {
+ if (!mir_wstrcmpi(pLoadedImages[i].szFileName, szFile)) {
+ pLoadedImages[i].dwLoadedTimes++;
+ return pLoadedImages[i].hGlyph;
+ }
+ }
+ }
+
+ // load new image
+ HBITMAP hbmp = ske_skinLoadGlyphImage(szFile);
+ if (hbmp == nullptr)
+ return nullptr;
+
+ // add to loaded list
+ if (dwLoadedImagesCount + 1 > dwLoadedImagesAlocated) {
+ pLoadedImages = (GLYPHIMAGE*)mir_realloc(pLoadedImages, sizeof(GLYPHIMAGE)*(dwLoadedImagesCount + 1));
+ if (!pLoadedImages)
+ return nullptr;
+ dwLoadedImagesAlocated++;
+ }
+
+ pLoadedImages[dwLoadedImagesCount].dwLoadedTimes = 1;
+ pLoadedImages[dwLoadedImagesCount].hGlyph = hbmp;
+ pLoadedImages[dwLoadedImagesCount].szFileName = mir_wstrdup(szFile);
+ dwLoadedImagesCount++;
+ return hbmp;
+}
+
+int ske_UnloadGlyphImage(HBITMAP hbmp)
+{
+ for (uint32_t i = 0; i < dwLoadedImagesCount && pLoadedImages; i++) {
+ if (hbmp != pLoadedImages[i].hGlyph)
+ continue;
+
+ pLoadedImages[i].dwLoadedTimes--;
+ if (pLoadedImages[i].dwLoadedTimes == 0) {
+ LPGLYPHIMAGE gl = &(pLoadedImages[i]);
+ replaceStrW(gl->szFileName, nullptr);
+ memmove(&(pLoadedImages[i]), &(pLoadedImages[i + 1]), sizeof(GLYPHIMAGE) * (dwLoadedImagesCount - i - 1));
+ dwLoadedImagesCount--;
+ DeleteObject(hbmp);
+ if (dwLoadedImagesCount == 0) {
+ dwLoadedImagesAlocated = 0;
+ mir_free_and_nil(pLoadedImages);
+ }
+ }
+ return 0;
+ }
+ DeleteObject(hbmp);
+ return 0;
+}
+
+int ske_UnloadSkin(SKINOBJECTSLIST *Skin)
+{
+ ClearMaskList(Skin->pMaskList);
+
+ //clear font list
+ if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) {
+ for (int i = 0; i < gl_plSkinFonts->realCount; i++) {
+ SKINFONT *sf = (SKINFONT *)gl_plSkinFonts->items[i];
+ if (sf) {
+ mir_free(sf->szFontID);
+ DeleteObject(sf->hFont);
+ mir_free(sf);
+ }
+ }
+ List_Destroy(gl_plSkinFonts);
+ mir_free_and_nil(gl_plSkinFonts);
+ }
+
+ replaceStrW(Skin->szSkinPlace, nullptr);
+ if (Skin->pTextList) List_Destroy(Skin->pTextList);
+ mir_free_and_nil(Skin->pTextList);
+ ModernSkinButtonDeleteAll();
+ if (Skin->dwObjLPAlocated == 0)
+ return 0;
+
+ for (uint32_t i = 0; i < Skin->dwObjLPAlocated; i++) {
+ switch (Skin->pObjects[i].bType) {
+ case OT_GLYPHOBJECT:
+ GLYPHOBJECT *dt = (GLYPHOBJECT*)Skin->pObjects[i].Data;
+ if (dt->hGlyph && dt->hGlyph != (HBITMAP)-1)
+ ske_UnloadGlyphImage(dt->hGlyph);
+ dt->hGlyph = nullptr;
+ replaceStr(dt->szFileName, nullptr);
+
+ if (dt->plTextList && dt->plTextList->realCount > 0) {
+ for (int k = 0; k < dt->plTextList->realCount; k++) {
+ GLYPHTEXT *gt = (GLYPHTEXT *)dt->plTextList->items[k];
+ if (gt) {
+ mir_free(gt->stText);
+ mir_free(gt->stValueText);
+ mir_free(gt->szFontID);
+ mir_free(gt->szGlyphTextID);
+ mir_free(gt);
+ }
+ }
+ List_Destroy(dt->plTextList);
+ mir_free(dt->plTextList);
+ }
+ mir_free(dt);
+ break;
+ }
+ replaceStr(Skin->pObjects[i].szObjectID, nullptr);
+ }
+ mir_free_and_nil(Skin->pObjects);
+ Skin->pTextList = nullptr;
+ Skin->dwObjLPAlocated = 0;
+ Skin->dwObjLPReserved = 0;
+ return 0;
+}
+
+static void RegisterMaskByParce(const char *szSetting, char *szValue, SKINOBJECTSLIST *pSkin)
+{
+ size_t i, val_len = mir_strlen(szValue);
+
+ for (i = 0; i < val_len; i++)
+ if (szValue[i] == ':')
+ break;
+
+ if (i < val_len) {
+ char *Obj, *Mask;
+ int res;
+ uint32_t ID = atoi(szSetting + 1);
+ Mask = szValue + i + 1;
+ Obj = (char*)mir_alloc(i + 2);
+ mir_strncpy(Obj, szValue, i + 1);
+ Obj[i + 1] = '\0';
+ res = AddStrModernMaskToList(ID, Mask, Obj, pSkin->pMaskList);
+ mir_free(Obj);
+ }
+}
+
+static int ske_ProcessLoadindString(const char *szSetting, char *szValue)
+{
+ if (!pCurrentSkin) return 0;
+ if (szSetting[0] == '$')
+ RegisterObjectByParce((char *)szSetting, szValue);
+ else if (szSetting[0] == '#')
+ RegisterButtonByParce((char *)szSetting, szValue);
+ else if (szSetting[0] == '@')
+ RegisterMaskByParce((char *)szSetting, szValue, pCurrentSkin); ///
+ else if (szSetting[0] == 't')
+ ske_AddParseTextGlyphObject((char*)szSetting, szValue, pCurrentSkin);
+ else if (szSetting[0] == 'f')
+ ske_AddParseSkinFont((char*)szSetting, szValue);
+ else return 0;
+ return 1;
+}
+
+static int ske_enumdb_SkinObjectsProc(const char *szSetting, void *)
+{
+ ptrA value(db_get_sa(0, SKIN, szSetting));
+ ske_ProcessLoadindString(szSetting, value);
+ return 0;
+}
+
+static int ske_SortTextGlyphObjectFunc(void *first, void *second)
+{
+ GLYPHTEXT *p1 = *(GLYPHTEXT**)first, *p2 = *(GLYPHTEXT**)second;
+ return mir_strcmp(p1->szGlyphTextID, p2->szGlyphTextID);
+}
+
+static void ske_LinkSkinObjects(SKINOBJECTSLIST *pObjectList)
+{
+ // LINK Mask with objects
+ for (uint32_t i = 0; i < pObjectList->pMaskList->dwMaskCnt; i++) {
+ MODERNMASK *mm = &(pObjectList->pMaskList->pl_Masks[i]);
+ void *pObject = (void *)ske_FindObjectByName(mm->szObjectName, OT_ANY, (SKINOBJECTSLIST *)pObjectList);
+ replaceStr(mm->szObjectName, nullptr);
+ mm->bObjectFound = TRUE;
+ mm->pObject = pObject;
+ }
+
+ if (pObjectList->pTextList) {
+ // LINK Text with objects
+ for (int i = 0; i < pObjectList->pTextList->realCount; i++) {
+ GLYPHTEXT *glText = (GLYPHTEXT *)pObjectList->pTextList->items[i];
+ SKINOBJECTDESCRIPTOR *lpobj = ske_FindObjectByName(glText->szObjectName, OT_GLYPHOBJECT, pObjectList);
+ replaceStr(glText->szObjectName, nullptr);
+ GLYPHOBJECT *globj = nullptr;
+ if (lpobj)
+ globj = (GLYPHOBJECT*)lpobj->Data;
+ if (globj) {
+ if (!globj->plTextList) {
+ globj->plTextList = List_Create(0, 1);
+ globj->plTextList->sortFunc = ske_SortTextGlyphObjectFunc;
+ }
+ List_Insert(globj->plTextList, (void*)glText, globj->plTextList->realCount);
+ qsort(globj->plTextList->items, globj->plTextList->realCount, sizeof(GLYPHTEXT*), (int(*)(const void*, const void*))globj->plTextList->sortFunc);
+ pObjectList->pTextList->items[i] = nullptr;
+ }
+ else {
+ GLYPHTEXT *gt = glText;
+ if (gt) {
+ mir_free(gt->stText);
+ mir_free(gt->stValueText);
+ mir_free(gt->szFontID);
+ mir_free(gt->szGlyphTextID);
+ mir_free(gt);
+ }
+ }
+ }
+ List_Destroy(pObjectList->pTextList);
+ mir_free_and_nil(pObjectList->pTextList);
+ }
+}
+
+// Getting skin objects and masks from DB
+
+static int ske_GetSkinFromDB(char *, SKINOBJECTSLIST *Skin)
+{
+ if (Skin == nullptr) return 0;
+ ske_UnloadSkin(Skin);
+ g_CluiData.fDisableSkinEngine = db_get_b(0, "ModernData", "DisableEngine", SETTING_DISABLESKIN_DEFAULT);
+ // window borders
+ if (g_CluiData.fDisableSkinEngine) {
+ g_CluiData.LeftClientMargin = 0;
+ g_CluiData.RightClientMargin = 0;
+ g_CluiData.TopClientMargin = 0;
+ g_CluiData.BottomClientMargin = 0;
+ return 0;
+ }
+
+ // window borders
+ g_CluiData.LeftClientMargin = (int)db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT);
+ g_CluiData.RightClientMargin = (int)db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT);
+ g_CluiData.TopClientMargin = (int)db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT);
+ g_CluiData.BottomClientMargin = (int)db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT);
+
+ Skin->pMaskList = (LISTMODERNMASK*)mir_alloc(sizeof(LISTMODERNMASK));
+ memset(Skin->pMaskList, 0, sizeof(LISTMODERNMASK));
+ Skin->szSkinPlace = db_get_wsa(0, SKIN, "SkinFolder");
+ if (!Skin->szSkinPlace || (wcschr(Skin->szSkinPlace, '%') && !db_get_b(0, SKIN, "Modified", 0))) {
+ BOOL bOnlyObjects = FALSE;
+ if (Skin->szSkinPlace && wcschr(Skin->szSkinPlace, '%'))
+ bOnlyObjects = TRUE;
+ mir_free(Skin->szSkinPlace);
+ Skin->szSkinPlace = mir_wstrdup(L"%Default%");
+ ske_LoadSkinFromResource(bOnlyObjects);
+ }
+
+ // Load objects
+ pCurrentSkin = Skin;
+ db_enum_settings(0, ske_enumdb_SkinObjectsProc, SKIN);
+
+ SortMaskList(pCurrentSkin->pMaskList);
+ ske_LinkSkinObjects(pCurrentSkin);
+
+ // Load Masks
+ return 0;
+}
+
+// surrogate to be called from outside
+void ske_LoadSkinFromDB(void)
+{
+ ske_GetSkinFromDB(SKIN, &g_SkinObjectList);
+ g_CluiData.dwKeyColor = db_get_dw(0, "ModernSettings", "KeyColor", (uint32_t)SETTING_KEYCOLOR_DEFAULT);
+}
+
+static int ske_LoadSkinFromResource(BOOL bOnlyObjects)
+{
+ IniParser parser(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF", bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS);
+ if (parser.CheckOK()) {
+ db_delete_module(0, "ModernSkin");
+ db_set_s(0, SKIN, "SkinFolder", "%Default%");
+ db_set_s(0, SKIN, "SkinFile", "%Default%");
+ parser.Parse(IniParser::WriteStrToDb, 0);
+ }
+ return 0;
+}
+
+// Load data from ini file
+int ske_LoadSkinFromIniFile(wchar_t *szFileName, BOOL bOnlyObjects)
+{
+ if (wcschr(szFileName, '%'))
+ return ske_LoadSkinFromResource(bOnlyObjects);
+
+ IniParser parser(szFileName, bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS);
+ if (!parser.CheckOK())
+ return 0;
+
+ db_delete_module(0, "ModernSkin");
+
+ wchar_t skinFolder[MAX_PATH], skinFile[MAX_PATH];
+ IniParser::GetSkinFolder(szFileName, skinFolder);
+ PathToRelativeW(szFileName, skinFile);
+
+ db_set_ws(0, SKIN, "SkinFolder", skinFolder);
+ db_set_ws(0, SKIN, "SkinFile", skinFile);
+
+ parser.Parse(IniParser::WriteStrToDb, 1);
+ return 0;
+}
+
+BOOL ske_TextOut(HDC hdc, int x, int y, LPCTSTR lpString, int nCount)
+{
+ SIZE sz;
+ GetTextExtentPoint32(hdc, lpString, nCount, &sz);
+
+ RECT rc = { 0 };
+ SetRect(&rc, x, y, x + sz.cx, y + sz.cy);
+ ske_DrawText(hdc, lpString, nCount, &rc, DT_NOCLIP | DT_SINGLELINE | DT_LEFT);
+ return 1;
+}
+
+static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM)
+{
+ if (!wParam) return 0;
+
+ AlphaTextOutParams ap = *(AlphaTextOutParams*)wParam;
+ return ske_AlphaTextOut(ap.hDC, ap.lpString, ap.nCount, ap.lpRect, ap.format, ap.ARGBcolor);
+}
+
+static __inline void ske_SetMatrix(sbyte *matrix,
+ sbyte a, sbyte b, sbyte c,
+ sbyte d, sbyte e, sbyte f,
+ sbyte g, sbyte h, sbyte i)
+{
+ matrix[0] = a; matrix[1] = b; matrix[2] = c;
+ matrix[3] = d; matrix[4] = e; matrix[5] = f;
+ matrix[6] = g; matrix[7] = h; matrix[8] = i;
+}
+
+bool ske_ResetTextEffect(HDC hdc)
+{
+ int idx = arEffectStack.getIndex((EFFECTSSTACKITEM*)&hdc);
+ if (idx == -1)
+ return false;
+
+ mir_free(arEffectStack[idx]);
+ arEffectStack.remove(idx);
+ return true;
+}
+
+bool ske_SelectTextEffect(HDC hdc, uint8_t EffectID, uint32_t FirstColor, uint32_t SecondColor)
+{
+ if (EffectID > MAXPREDEFINEDEFFECTS)
+ return false;
+
+ if (EffectID == -1)
+ return ske_ResetTextEffect(hdc);
+
+ EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc);
+ if (effect == nullptr) {
+ effect = (EFFECTSSTACKITEM *)mir_alloc(sizeof(EFFECTSSTACKITEM));
+ effect->hdc = hdc;
+ arEffectStack.insert(effect);
+ }
+
+ effect->EffectID = EffectID;
+ effect->FirstColor = FirstColor;
+ effect->SecondColor = SecondColor;
+ return true;
+}
+
+static bool ske_GetTextEffect(HDC hdc, MODERNEFFECT *modernEffect)
+{
+ if (!modernEffect)
+ return false;
+
+ EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc);
+ if (effect == nullptr)
+ return false;
+
+ modernEffect->EffectID = effect->EffectID;
+ modernEffect->EffectColor1 = effect->FirstColor;
+ modernEffect->EffectColor2 = effect->SecondColor;
+ modernEffect->EffectMatrix = ModernEffectsEnum[effect->EffectID];
+ return true;
+}
+
+static bool ske_DrawTextEffect(uint8_t *destPt, uint8_t *maskPt, uint32_t width, uint32_t height, MODERNEFFECT *effect)
+{
+ sbyte *buf;
+ sbyte *outbuf;
+ sbyte *bufline, *buflineTop, *buflineMid;
+ int sign = 0;
+ uint8_t *maskline, *destline;
+ uint8_t al, rl, gl, bl, ad, rd, gd, bd;
+ int k = 0;
+ uint32_t x, y;
+ sbyte *matrix;
+ uint8_t mcTopStart;
+ uint8_t mcBottomEnd;
+ uint8_t mcLeftStart;
+ uint8_t mcRightEnd;
+ uint8_t effectCount;
+ int minX = width;
+ int maxX = 0;
+ int minY = height;
+ int maxY = 0;
+ if (effect->EffectID == 0xFF) return false;
+ if (!width || !height) return false;
+ if (!destPt) return false;
+ buf = (sbyte*)mir_alloc(width*height*sizeof(uint8_t));
+ {
+ matrix = effect->EffectMatrix.matrix;
+ mcTopStart = 2 - effect->EffectMatrix.topEffect;
+ mcBottomEnd = 3 + effect->EffectMatrix.bottomEffect;
+ mcLeftStart = 2 - effect->EffectMatrix.leftEffect;
+ mcRightEnd = 3 + effect->EffectMatrix.rightEffect;
+ effectCount = effect->EffectMatrix.cycleCount;
+ }
+ al = 255 - ((uint8_t)(effect->EffectColor1 >> 24));
+ rl = GetRValue(effect->EffectColor1);
+ gl = GetGValue(effect->EffectColor1);
+ bl = GetBValue(effect->EffectColor1);
+ ad = 255 - ((uint8_t)(effect->EffectColor2 >> 24));
+ rd = GetRValue(effect->EffectColor2);
+ gd = GetGValue(effect->EffectColor2);
+ bd = GetBValue(effect->EffectColor2);
+
+ // Fill buffer by mid values of image
+ for (y = 0; y < height; y++) {
+ bufline = buf + y*width;
+ maskline = maskPt + ((y*width) << 2);
+ for (x = 0; x < width; x++) {
+ uint8_t a = (sbyte)(uint32_t)((maskline[0] + maskline[2] + maskline[1] + maskline[1]) >> 4);
+ *bufline = a;
+ if (a != 0) {
+ minX = min((int)x, minX);
+ minY = min((int)y, minY);
+ maxX = max((int)x, maxX);
+ maxY = max((int)y, maxY);
+ }
+ bufline++;
+ maskline += 4;
+ }
+ }
+ // Here perform effect on buffer and place results to outbuf
+ for (k = 0; k < (effectCount & 0x7F); k++) {
+ minX = max(0, minX + mcLeftStart - 2);
+ minY = max(0, minY + mcTopStart - 2);
+ maxX = min((int)width, maxX + mcRightEnd - 1);
+ maxY = min((int)height, maxY + mcBottomEnd - 1);
+
+ outbuf = (sbyte*)mir_alloc(width*height*sizeof(sbyte));
+ memset(outbuf, 0, width*height*sizeof(sbyte));
+ for (y = (uint32_t)minY; y < (uint32_t)maxY; y++) {
+ int val;
+ bufline = outbuf + y*width + minX;
+ buflineMid = buf + y*width + minX;
+ for (x = (uint32_t)minX; x < (uint32_t)maxX; x++) {
+ int matrixHor, matrixVer;
+ val = 0;
+ for (matrixVer = mcTopStart; matrixVer < mcBottomEnd; matrixVer++) {
+ int buflineStep = width*(matrixVer - 2);
+ int as = y + matrixVer - 2;
+ sbyte *buflineTopS = nullptr;
+ if (as >= 0 && (uint32_t)as < height) buflineTopS = buflineMid + buflineStep;
+
+ for (matrixHor = mcLeftStart; matrixHor < mcRightEnd; matrixHor++) {
+ buflineTop = buflineTopS;
+ int a = x + matrixHor - 2;
+ if (buflineTop && a >= 0 && (uint32_t)a < width) buflineTop += matrixHor - 2;
+ else buflineTop = nullptr;
+ if (buflineTop)
+ val += ((*buflineTop)*matrix[matrixVer * 5 + matrixHor]);
+ }
+ }
+ val = (val + 1) >> 5;
+ *bufline = (sbyte)((val>127) ? 127 : (val < -125) ? -125 : val);
+ bufline++;
+ buflineMid++;
+ }
+ }
+ mir_free(buf);
+ buf = outbuf;
+ }
+ {
+ uint8_t r1, b1, g1, a1;
+ b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1;
+ //perform out to dest
+ for (y = 0; y < height; y++) {
+ bufline = buf + y*width;
+ destline = destPt + ((y*width) << 2);
+ for (x = 0; x < width; x++) {
+ sbyte val = *bufline;
+ uint8_t absVal = ((val < 0) ? -val : val);
+
+ if (val != 0) {
+ if (val > 0 && sign < 0) {
+ b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1;
+ }
+ else if (val < 0 && sign>0) {
+ b1 = bd; r1 = rd; g1 = gd; a1 = ad; sign = -1;
+ }
+
+ absVal = absVal*a1 / 255;
+
+ destline[0] = ((destline[0] * (128 - absVal)) + absVal*b1) >> 7;
+ destline[1] = ((destline[1] * (128 - absVal)) + absVal*g1) >> 7;
+ destline[2] = ((destline[2] * (128 - absVal)) + absVal*r1) >> 7;
+ destline[3] += ((255 - destline[3])*(a1*absVal)) / 32640;
+ }
+ bufline++;
+ destline += 4;
+ }
+ }
+ mir_free(buf);
+ }
+ return false;
+}
+
+static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor)
+{
+ if (!(lpString && lpRect))
+ return 0;
+
+ // Step first fill fast calc correction tables:
+ static bool _tables_empty = true;
+ static uint8_t gammaTbl[256]; // Gamma correction table
+ static uint16_t blueMulTbl[256]; // blue coefficient multiplication table
+ static uint16_t greenMulTbl[256]; // green coefficient multiplication table
+ static uint16_t redMulTbl[256]; // red coefficient multiplication table
+ if (_tables_empty) {
+ // fill tables
+ double gammaCfPw = 1000 / (double)DBGetContactSettingRangedWord(0, "ModernData", "AlphaTextOutGamma", 700, 1, 5000);
+ uint8_t blueCf = db_get_b(0, "ModernData", "AlphaTextOutBlueCorrection", 28);
+ uint8_t redCf = db_get_b(0, "ModernData", "AlphaTextOutRed Correction", 77);
+ uint8_t greenCf = db_get_b(0, "ModernData", "AlphaTextOutGreen Correction", 151);
+
+ for (int i = 0; i < 256; i++) {
+ gammaTbl[i] = (uint8_t)(255 * pow((double)i / 255, gammaCfPw));
+ blueMulTbl[i] = i * blueCf;
+ redMulTbl[i] = i * redCf;
+ greenMulTbl[i] = i * greenCf;
+ }
+ }
+
+ // Calc len of input string
+ if (nCount == -1)
+ nCount = (int)mir_wstrlen(lpString);
+
+ // retrieve destination bitmap bits
+ HBITMAP hDestBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP);
+ BITMAP bmpDest;
+ GetObject(hDestBitmap, sizeof(BITMAP), &bmpDest);
+
+ bool destHasNotDIB = (bmpDest.bmBits == nullptr);
+ uint8_t *pDestBits;
+ if (destHasNotDIB) {
+ pDestBits = (uint8_t*)mir_alloc(bmpDest.bmHeight * bmpDest.bmWidthBytes);
+ GetBitmapBits(hDestBitmap, bmpDest.bmHeight*bmpDest.bmWidthBytes, pDestBits);
+ }
+ else
+ pDestBits = (uint8_t*)bmpDest.bmBits;
+
+ // Creating offscreen buffer
+ HDC hOffscreenDC = CreateCompatibleDC(hDC);
+
+ // Font to be used to draw text
+ HFONT hFont = (HFONT)GetCurrentObject(hDC, OBJ_FONT);
+ HFONT hOldOffscreenFont = (HFONT)SelectObject(hOffscreenDC, hFont);
+
+ // Calculating text geometric size
+ RECT workRect = *lpRect;
+ int workRectWidth = workRect.right - workRect.left;
+ int workRectHeight = workRect.bottom - workRect.top;
+ if (workRectWidth <= 0 || workRectHeight <= 0) {
+ if (destHasNotDIB)
+ mir_free(pDestBits);
+ return 0;
+ }
+
+ SIZE textSize;
+ GetTextExtentPoint32(hOffscreenDC, lpString, nCount, &textSize);
+
+ LPCTSTR lpWorkString = lpString;
+ BOOL bNeedFreeWorkString = FALSE;
+
+ // if we need to cut the text with ellipsis
+ if ((format & DT_END_ELLIPSIS) && textSize.cx > workRectWidth) {
+ // Calc geometric width of ellipsis
+ SIZE szEllipsis;
+ GetTextExtentPoint32A(hOffscreenDC, "...", 3, &szEllipsis);
+ szEllipsis.cx++; // CORRECTION: some width correction
+
+ // Calc count of visible chars
+ int visibleCharCount = nCount;
+ if (workRectWidth > szEllipsis.cx)
+ GetTextExtentExPoint(hOffscreenDC, lpString, nCount, workRectWidth - szEllipsis.cx, &visibleCharCount, nullptr, &textSize);
+ else
+ GetTextExtentExPoint(hOffscreenDC, lpString, nCount, 0, &visibleCharCount, nullptr, &textSize);
+
+ // replace end of string by elipsis
+ bNeedFreeWorkString = TRUE;
+ lpWorkString = (wchar_t*)mir_alloc((visibleCharCount + 4) * sizeof(wchar_t));
+
+ memcpy((void*)lpWorkString, lpString, visibleCharCount * sizeof(wchar_t));
+ memcpy((void*)(lpWorkString + visibleCharCount), L"...", 4 * sizeof(wchar_t)); // 3 + 1
+
+ nCount = visibleCharCount + 3;
+ }
+
+ // Calc sizes and offsets
+
+ textSize.cx += 2; // CORRECTION: for italic
+
+ int drx = 0; // x-axis offset of draw point
+
+ if (workRectWidth > textSize.cx) {
+ if (format & (DT_RIGHT | DT_RTLREADING))
+ drx = workRectWidth - textSize.cx;
+ else if (format & DT_CENTER)
+ drx = (workRectWidth - textSize.cx) >> 1;
+ }
+ else textSize.cx = workRectWidth;
+
+ int dry = 0; // y-axis offset of draw point
+
+ if (workRectHeight > textSize.cy) {
+ if (format & DT_BOTTOM)
+ dry = workRectHeight - textSize.cy;
+ else if (format & DT_VCENTER)
+ dry = (workRectHeight - textSize.cy) >> 1;
+ }
+ else textSize.cy = workRectHeight;
+
+ textSize.cx += 4; // CORRECTION: for effects ???
+ textSize.cy += 4; // CORRECTION: for effects ???
+
+ if (textSize.cx > 0 && textSize.cy > 0) { // Ok we need to paint
+ // probably here are mess ofscreen and temp buff dc
+
+ //Create bitmap image for offscreen
+ uint8_t *bits = nullptr;
+ HBITMAP hbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bits);
+ if (bits != nullptr) {
+ HBITMAP holdbmp = (HBITMAP)SelectObject(hOffscreenDC, hbmp);
+
+ //Create buffer bitmap image for temp text
+ uint8_t *bufbits = nullptr;
+ HBITMAP bufbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bufbits);
+ if (bufbits != nullptr) {
+ HDC bufDC = CreateCompatibleDC(hDC);
+ HBITMAP bufoldbmp = (HBITMAP)SelectObject(bufDC, bufbmp);
+ HFONT hOldBufFont = (HFONT)SelectObject(bufDC, hFont);
+ SetBkColor(bufDC, RGB(0, 0, 0));
+ SetTextColor(bufDC, RGB(255, 255, 255));
+
+ // Copy from destination to temp buffer
+ BitBlt(hOffscreenDC, 0, 0, textSize.cx, textSize.cy, hDC, workRect.left + drx - 2, workRect.top + dry - 2, SRCCOPY);
+
+ //Draw text on offscreen bitmap
+ TextOut(bufDC, 2, 2, lpWorkString, nCount);
+
+ MODERNEFFECT effect;
+ if (ske_GetTextEffect(hDC, &effect))
+ ske_DrawTextEffect(bits, bufbits, textSize.cx, textSize.cy, &effect);
+
+ // RenderText
+ RECT drawRect;
+ drawRect.left = 0; drawRect.top = 0;
+ drawRect.right = textSize.cx;
+ drawRect.bottom = textSize.cy;
+
+ uint32_t width = textSize.cx;
+ uint32_t heigh = textSize.cy;
+
+ uint8_t *pDestScanLine, *pBufScanLine, *pix, *bufpix;
+
+ uint8_t al = 255 - ((uint8_t)(ARGBcolor >> 24));
+ uint8_t r = GetRValue(ARGBcolor);
+ uint8_t g = GetGValue(ARGBcolor);
+ uint8_t b = GetBValue(ARGBcolor);
+
+ for (uint32_t y = 2; y < heigh - 2; y++) {
+ int lineBytes = y * (width << 2);
+
+ pDestScanLine = bits + lineBytes;
+ pBufScanLine = bufbits + lineBytes;
+
+ for (uint32_t x = 2; x < width - 2; x++) {
+ pix = pDestScanLine + (x << 2);
+ bufpix = pBufScanLine + (x << 2);
+
+ // Monochromatic
+ uint8_t bx = gammaTbl[bufpix[0]];
+ uint8_t gx = gammaTbl[bufpix[1]];
+ uint8_t rx = gammaTbl[bufpix[2]];
+
+ if (al != 255) {
+ bx *= al / 255;
+ gx *= al / 255;
+ rx *= al / 255;
+ }
+
+ uint8_t ax = (uint8_t)(((uint32_t)rx * 77 + (uint32_t)gx * 151 + (uint32_t)bx * 28 + 128) / 256);
+ if (ax) {
+ //Normalize components to gray
+ uint8_t axx = 255 - ((r + g + b) >> 2); // Coefficient of grayance, more white font - more gray edges
+ uint16_t atx = ax * (255 - axx);
+ bx = (atx + bx * axx) / 255;
+ gx = (atx + gx * axx) / 255;
+ rx = (atx + rx * axx) / 255;
+
+ short brx = (short)((b - pix[0])*bx / 255);
+ short grx = (short)((g - pix[1])*gx / 255);
+ short rrx = (short)((r - pix[2])*rx / 255);
+
+ pix[0] += brx;
+ pix[1] += grx;
+ pix[2] += rrx;
+ pix[3] = (uint8_t)(ax + (uint8_t)(255 - ax)*pix[3] / 255);
+ }
+ }
+ }
+
+ // Blit to destination
+ BitBlt(hDC, workRect.left + drx - 2, workRect.top + dry - 2, textSize.cx, textSize.cy, hOffscreenDC, 0, 0, SRCCOPY);
+
+ //free resources
+ SelectObject(bufDC, bufoldbmp);
+ DeleteObject(bufbmp);
+ SelectObject(bufDC, hOldBufFont);
+ DeleteDC(bufDC);
+ }
+ SelectObject(hOffscreenDC, holdbmp);
+ DeleteObject(hbmp);
+ }
+ }
+
+ // Final cleanup
+ SelectObject(hOffscreenDC, hOldOffscreenFont);
+ DeleteDC(hOffscreenDC);
+
+ if (destHasNotDIB)
+ mir_free(pDestBits);
+
+ if (bNeedFreeWorkString)
+ mir_free((void*)lpWorkString);
+
+ return 0;
+}
+
+static int ske_DrawTextWithEffectWorker(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, FONTEFFECT *effect)
+{
+ if (format & DT_CALCRECT)
+ return DrawText(hdc, lpString, nCount, lpRect, format);
+
+ if (format & DT_RTLREADING)
+ SetTextAlign(hdc, TA_RTLREADING);
+
+ uint32_t color = GetTextColor(hdc);
+ RECT r = *lpRect;
+ OffsetRect(&r, 1, 1);
+ uint32_t form = format;
+ if (effect && effect->effectIndex)
+ ske_SelectTextEffect(hdc, effect->effectIndex - 1, effect->baseColour, effect->secondaryColour);
+
+ int res = ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color);
+
+ if (effect && effect->effectIndex)
+ ske_ResetTextEffect(hdc);
+
+ return res;
+}
+
+INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM)
+{
+ DrawTextWithEffectParam *p = (DrawTextWithEffectParam *)wParam;
+ return ske_DrawTextWithEffectWorker(p->hdc, p->lpchText, p->cchText, p->lprc, p->dwDTFormat, p->pEffect);
+}
+
+BOOL ske_DrawText(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format)
+{
+ RECT r = *lpRect;
+ OffsetRect(&r, 1, 1);
+ if (format & DT_RTLREADING)
+ SetTextAlign(hdc, TA_RTLREADING);
+ if (format & DT_CALCRECT)
+ return DrawText(hdc, lpString, nCount, lpRect, format);
+ if (format & DT_FORCENATIVERENDER || g_CluiData.fDisableSkinEngine)
+ return DrawText(hdc, lpString, nCount, lpRect, format & ~DT_FORCENATIVERENDER);
+
+ uint32_t form = format;
+ uint32_t color = GetTextColor(hdc);
+ return ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color);
+}
+
+HICON ske_ImageList_GetIcon(HIMAGELIST himl, int i)
+{
+ IMAGEINFO imi = {};
+ BITMAP bm = { 0 };
+ if (i != -1) {
+ ImageList_GetImageInfo(himl, i, &imi);
+ GetObject(imi.hbmImage, sizeof(bm), &bm);
+ // stupid bug of Microsoft
+ // Icons bitmaps are not premultiplied
+ // So Imagelist_AddIcon - premultiply alpha
+ // But incorrect - it is possible that alpha will
+ // be less than color and
+ // ImageList_GetIcon will return overflowed colors
+ // TODO: Direct draw Icon from imagelist without
+ // extracting of icon
+ if (bm.bmBitsPixel == 32) {
+ uint8_t *bits = (uint8_t*)bm.bmBits;
+ if (!bits) {
+ bits = (uint8_t*)mir_alloc(bm.bmWidthBytes*bm.bmHeight);
+ GetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits);
+ }
+
+ uint8_t *bcbits = bits + (bm.bmHeight - imi.rcImage.bottom)*bm.bmWidthBytes + (imi.rcImage.left*bm.bmBitsPixel >> 3);
+ for (int iy = 0; iy < imi.rcImage.bottom - imi.rcImage.top; iy++) {
+ int x;
+ // Dummy microsoft fix - alpha can be less than r,g or b
+ // Looks like color channels in icons should be non-premultiplied with alpha
+ // But AddIcon store it premultiplied (incorrectly cause can be Alpha == 7F, but R,G or B == 80
+ // So i check that alpha is 0x7F and set it to 0x80
+ uint32_t *c = ((uint32_t*)bcbits);
+ for (x = 0; x < imi.rcImage.right - imi.rcImage.left; x++) {
+ uint32_t val = *c;
+ uint8_t a = (uint8_t)((val) >> 24);
+ if (a != 0) {
+ uint8_t r = (uint8_t)((val & 0xFF0000) >> 16);
+ uint8_t g = (uint8_t)((val & 0xFF00) >> 8);
+ uint8_t b = (uint8_t)(val & 0xFF);
+ if (a < r || a < g || a < b) {
+ a = max(max(r, g), b);
+ val = a << 24 | r << 16 | g << 8 | b;
+ *c = val;
+ }
+ }
+ c++;
+ }
+ bcbits += bm.bmWidthBytes;
+ }
+
+ if (!bm.bmBits) {
+ SetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits);
+ mir_free(bits);
+ }
+ }
+ }
+ return ImageList_GetIcon(himl, i, ILD_NORMAL);
+}
+
+BOOL ske_ImageList_DrawEx(HIMAGELIST himl, int i, HDC hdcDst, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle)
+{
+ // the routine to directly draw icon from image list without creating icon from there - should be some faster
+ if (i < 0)
+ return FALSE;
+
+ if (g_CluiData.fDisableSkinEngine)
+ return ImageList_DrawEx(himl, i, hdcDst, x, y, dx, dy, rgbBk, rgbFg, fStyle);
+
+ uint8_t alpha;
+ if (fStyle & ILD_BLEND25)
+ alpha = 64;
+ else if (fStyle & ILD_BLEND50)
+ alpha = 128;
+ else
+ alpha = 255;
+
+ HICON hIcon = ske_ImageList_GetIcon(himl, i);
+ if (hIcon == nullptr)
+ return FALSE;
+
+ ske_DrawIconEx(hdcDst, x, y, hIcon, dx ? dx : GetSystemMetrics(SM_CXSMICON), dy ? dy : GetSystemMetrics(SM_CYSMICON), 0, nullptr, DI_NORMAL | (alpha << 24));
+ DestroyIcon(hIcon);
+ return TRUE;
+}
+
+static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM)
+{
+ DrawIconFixParam *p = (DrawIconFixParam*)wParam;
+ if (!p)
+ return 0;
+
+ return ske_DrawIconEx(p->hdc, p->xLeft, p->yTop, p->hIcon, p->cxWidth, p->cyWidth, p->istepIfAniCur, p->hbrFlickerFreeDraw, p->diFlags);
+}
+
+
+BOOL ske_DrawIconEx(HDC hdcDst, int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags)
+{
+ ICONINFO ici;
+ uint8_t alpha = (uint8_t)((diFlags & 0xFF000000) >> 24);
+
+ HBITMAP tBmp = nullptr;
+ uint8_t *imbits, *imimagbits, *immaskbits;
+ uint8_t *t1, *t2, *t3;
+
+ //lockimagelist
+ uint8_t hasmask = FALSE, no32bit = FALSE, noMirrorMask = FALSE, hasalpha = FALSE;
+ alpha = alpha ? alpha : 255;
+
+ if (g_CluiData.fDisableSkinEngine && !(diFlags & 0x80))
+ return DrawIconEx(hdcDst, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags & 0xFFFF7F);
+
+ if (!GetIconInfo(hIcon, &ici))
+ return 0;
+
+ BITMAP imbt;
+ GetObject(ici.hbmColor, sizeof(BITMAP), &imbt);
+ if (imbt.bmWidth*imbt.bmHeight == 0) {
+ DeleteObject(ici.hbmColor);
+ DeleteObject(ici.hbmMask);
+ return 0;
+ }
+
+ BITMAP immaskbt;
+ GetObject(ici.hbmMask, sizeof(BITMAP), &immaskbt);
+ uint32_t cy = imbt.bmHeight;
+
+ if (imbt.bmBitsPixel != 32) {
+ no32bit = TRUE;
+ HDC tempDC1 = CreateCompatibleDC(hdcDst);
+ tBmp = ske_CreateDIB32(imbt.bmWidth, imbt.bmHeight);
+ if (tBmp) {
+ GetObject(tBmp, sizeof(BITMAP), &imbt);
+ HBITMAP otBmp = (HBITMAP)SelectObject(tempDC1, tBmp);
+ DrawIconEx(tempDC1, 0, 0, hIcon, imbt.bmWidth, imbt.bmHeight, istepIfAniCur, hbrFlickerFreeDraw, DI_IMAGE);
+ noMirrorMask = TRUE;
+ SelectObject(tempDC1, otBmp);
+ }
+ DeleteDC(tempDC1);
+ }
+
+ bool NoDIBImage = (imbt.bmBits == nullptr);
+ if (NoDIBImage) {
+ imimagbits = (uint8_t*)mir_alloc(cy*imbt.bmWidthBytes);
+ GetBitmapBits(ici.hbmColor, cy*imbt.bmWidthBytes, (void*)imimagbits);
+ }
+ else imimagbits = (uint8_t*)imbt.bmBits;
+
+ if (immaskbt.bmBits == nullptr) {
+ immaskbits = (uint8_t*)mir_alloc(cy*immaskbt.bmWidthBytes);
+ GetBitmapBits(ici.hbmMask, cy*immaskbt.bmWidthBytes, (void*)immaskbits);
+ }
+ else immaskbits = (uint8_t*)immaskbt.bmBits;
+
+ HDC imDC = CreateCompatibleDC(hdcDst);
+ uint32_t icy = imbt.bmHeight;
+ uint32_t cx = imbt.bmWidth;
+ HBITMAP imBmp = ske_CreateDIB32Point(cx, icy, (void**)&imbits);
+ HBITMAP oldBmp = (HBITMAP)SelectObject(imDC, imBmp);
+ if (imbits != nullptr && imimagbits != nullptr && immaskbits != nullptr) {
+ int x, y;
+ int mwb = immaskbt.bmWidthBytes;
+ int mwb2 = imbt.bmWidthBytes;
+ int bottom = icy;
+ int right = cx;
+ int top = 0;
+ int h = icy;
+ for (y = top; (y < bottom) && !hasmask; y++) {
+ t1 = immaskbits + y*mwb;
+ for (x = 0; (x < mwb) && !hasmask; x++)
+ hasmask |= (*(t1 + x) != 0);
+ }
+
+ for (y = top; (y < bottom) && !hasalpha; y++) {
+ t1 = imimagbits + (cy - y - 1)*mwb2;
+ for (x = 0; (x < right) && !hasalpha; x++)
+ hasalpha |= (*(t1 + (x << 2) + 3) != 0);
+ }
+
+ for (y = 0; y < (int)icy; y++) {
+ t1 = imimagbits + (h - y - 1 - top)*mwb2;
+ t2 = imbits + (!no32bit ? y : (icy - y - 1))*mwb2;
+ t3 = immaskbits + (noMirrorMask ? y : (h - y - 1 - top))*mwb;
+ for (x = 0; x < right; x++) {
+ uint8_t mask = 0;
+ uint8_t a = 0;
+ uint32_t *src = (uint32_t*)(t1 + (x << 2));
+ uint32_t *dest = (uint32_t*)(t2 + (x << 2));
+ if (hasalpha && !hasmask)
+ a = ((uint8_t*)src)[3];
+ else {
+ mask = ((1 << (7 - x % 8))&(*(t3 + (x >> 3)))) != 0;
+ if (mask) {
+ if (!hasalpha) {
+ *dest = 0;
+ continue;
+ }
+
+ if (((uint8_t*)src)[3]>0)
+ a = ((uint8_t*)src)[3];
+ else
+ a = 0;
+ }
+ else if (hasalpha || hasmask)
+ a = (((uint8_t*)src)[3] > 0 ? ((uint8_t*)src)[3] : 255);
+ else if (!hasalpha && !hasmask)
+ a = 255;
+ else { *dest = 0; continue; }
+ }
+ if (a > 0) {
+ ((uint8_t*)dest)[3] = a;
+ ((uint8_t*)dest)[0] = ((uint8_t*)src)[0] * a / 255;
+ ((uint8_t*)dest)[1] = ((uint8_t*)src)[1] * a / 255;
+ ((uint8_t*)dest)[2] = ((uint8_t*)src)[2] * a / 255;
+ }
+ else *dest = 0;
+ }
+ }
+ }
+
+ BLENDFUNCTION bf = { AC_SRC_OVER, diFlags & 128, alpha, AC_SRC_ALPHA };
+ ske_AlphaBlend(hdcDst, xLeft, yTop, cxWidth, cyWidth, imDC, 0, 0, cx, icy, bf);
+
+ if (immaskbt.bmBits == nullptr) mir_free(immaskbits);
+ if (imbt.bmBits == nullptr) mir_free(imimagbits);
+ SelectObject(imDC, oldBmp);
+ DeleteObject(imBmp);
+ if (no32bit)DeleteObject(tBmp);
+ DeleteObject(ici.hbmColor);
+ DeleteObject(ici.hbmMask);
+ SelectObject(imDC, GetStockObject(DEFAULT_GUI_FONT));
+ DeleteDC(imDC);
+ return 1;
+}
+
+int ske_PrepareImageButDontUpdateIt(RECT *r)
+{
+ if (!g_CluiData.fLayered)
+ return ske_ReCreateBackImage(FALSE, r);
+
+ mutex_bLockUpdate = 1;
+ ske_DrawNonFramedObjects(TRUE, r);
+ ske_ValidateFrameImageProc(r);
+ mutex_bLockUpdate = 0;
+ return 0;
+}
+
+int ske_RedrawCompleteWindow()
+{
+ if (g_CluiData.fLayered) {
+ ske_DrawNonFramedObjects(TRUE, nullptr);
+ CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE, 0, 0);
+ }
+ else RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+
+ return 0;
+}
+
+// Request to repaint frame or change/drop callback data
+// wParam = hWnd of called frame
+// lParam = pointer to sPaintRequest (or nullptr to redraw all)
+// return 2 - already queued, data updated, 1-have been queued, 0 - failure
+
+static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM) // Immideately recall paint routines for frame and refresh image
+{
+ if (MirandaLoading())
+ return 0;
+
+ RECT wnd;
+ bool NoCancelPost = false;
+ bool IsAnyQueued = false;
+ if (!g_CluiData.mutexOnEdgeSizing)
+ GetWindowRect(g_clistApi.hwndContactList, &wnd);
+ else
+ wnd = g_rcEdgeSizingRect;
+
+ if (!g_CluiData.fLayered) {
+ RedrawWindow((HWND)wParam, nullptr, nullptr, RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+ return 0;
+ }
+
+ if (g_pCachedWindow == nullptr) ske_ValidateFrameImageProc(&wnd);
+ else if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) ske_ValidateFrameImageProc(&wnd);
+ else if (wParam == 0) ske_ValidateFrameImageProc(&wnd);
+ else { // all Ok Update Single Frame
+ // TO BE LOCKED OR PROXIED
+ FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam);
+ if (!frm)
+ ske_ValidateFrameImageProc(&wnd);
+ // Validate frame, update window image and remove it from queue
+ else {
+ if (frm->UpdateRgn) {
+ DeleteObject(frm->UpdateRgn);
+ frm->UpdateRgn = nullptr;
+ }
+ ske_ValidateSingleFrameImage(frm, 0);
+ ske_UpdateWindowImage();
+ NoCancelPost = 1;
+ //-- Remove frame from queue
+ if (flag_bUpdateQueued) {
+ frm->bQueued = 0;
+ for (int i = 0; i < g_nFramesCount; i++)
+ if (IsAnyQueued |= g_pfwFrames[i].bQueued)
+ break;
+ }
+ }
+ }
+
+ if ((!NoCancelPost || !IsAnyQueued) && flag_bUpdateQueued) { // no any queued updating cancel post or need to cancel post
+ flag_bUpdateQueued = 0;
+ g_bPostWasCanceled = true;
+ }
+ return 1;
+}
+
+static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam) // Post request for updating
+{
+ if (MirandaLoading()) return 0;
+
+ if (wParam) {
+ FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam);
+ sPaintRequest *pr = (sPaintRequest*)lParam;
+ if (!g_CluiData.fLayered || (frm && frm->floating))
+ return InvalidateRect((HWND)wParam, pr ? (RECT*)&(pr->rcUpdate) : nullptr, FALSE);
+
+ if (frm) {
+ if (frm->PaintCallbackProc != nullptr) {
+ frm->PaintData = (sPaintRequest *)pr;
+ frm->bQueued = 1;
+ if (pr) {
+ HRGN r2;
+ if (!IsRectEmpty(&pr->rcUpdate)) {
+ RECT rcClient;
+ RECT rcUpdate;
+ GetClientRect(frm->hWnd, &rcClient);
+ IntersectRect(&rcUpdate, &rcClient, &pr->rcUpdate);
+ if (IsRectEmpty(&rcUpdate))
+ return 0;
+ r2 = CreateRectRgn(rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom);
+ }
+ else {
+ RECT r;
+ GetClientRect(frm->hWnd, &r);
+ r2 = CreateRectRgn(r.left, r.top, r.right, r.bottom);
+ }
+
+ if (!frm->UpdateRgn) {
+ frm->UpdateRgn = CreateRectRgn(0, 0, 1, 1);
+ CombineRgn(frm->UpdateRgn, r2, nullptr, RGN_COPY);
+ }
+ else CombineRgn(frm->UpdateRgn, frm->UpdateRgn, r2, RGN_OR);
+ DeleteObject(r2);
+ }
+ }
+ }
+ else Sync(QueueAllFramesUpdating, true);
+ }
+ else Sync(QueueAllFramesUpdating, true);
+
+ if (!flag_bUpdateQueued || g_bPostWasCanceled)
+ if (PostMessage(g_clistApi.hwndContactList, UM_UPDATE, 0, 0)) {
+ flag_bUpdateQueued = 1;
+ g_bPostWasCanceled = false;
+ }
+ return 1;
+}
+
+static int ske_ValidateSingleFrameImage(FRAMEWND *Frame, BOOL SkipBkgBlitting) // Calling frame paint proc
+{
+ if (!g_pCachedWindow) { TRACE("ske_ValidateSingleFrameImage calling without cached\n"); return 0; }
+ if (Frame->hWnd == (HWND)-1 && !Frame->PaintCallbackProc) { TRACE("ske_ValidateSingleFrameImage calling without FrameProc\n"); return 0; }
+
+ // if ok update image
+ RECT rcPaint, wnd;
+ RECT ru = { 0 };
+ int w1, h1, x1, y1;
+
+ CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &wnd);
+ rcPaint = Frame->wndSize;
+ {
+ int dx, dy, bx, by;
+ if (g_CluiData.mutexOnEdgeSizing) {
+ dx = rcPaint.left - wnd.left;
+ dy = rcPaint.top - wnd.top;
+ bx = rcPaint.right - wnd.right;
+ by = rcPaint.bottom - wnd.bottom;
+ wnd = g_rcEdgeSizingRect;
+ rcPaint.left = wnd.left + dx;
+ rcPaint.top = wnd.top + dy;
+ rcPaint.right = wnd.right + bx;
+ rcPaint.bottom = wnd.bottom + by;
+ }
+ }
+
+ int w = rcPaint.right - rcPaint.left;
+ int h = rcPaint.bottom - rcPaint.top;
+ if (w <= 0 || h <= 0) {
+ TRACE("Frame size smaller than 0\n");
+ return 0;
+ }
+ int x = rcPaint.left;
+ int y = rcPaint.top;
+ HDC hdc = CreateCompatibleDC(g_pCachedWindow->hImageDC);
+ HBITMAP n = ske_CreateDIB32(w, h);
+ HBITMAP o = (HBITMAP)SelectObject(hdc, n);
+
+ if (Frame->UpdateRgn && !SkipBkgBlitting) {
+ GetRgnBox(Frame->UpdateRgn, &ru);
+ {
+ RECT rc;
+ GetClientRect(Frame->hWnd, &rc);
+ if (ru.top < 0) ru.top = 0;
+ if (ru.left < 0) ru.left = 0;
+ if (ru.right > rc.right) ru.right = rc.right;
+ if (ru.bottom > rc.bottom) ru.bottom = rc.bottom;
+ }
+ if (!IsRectEmpty(&ru)) {
+ x1 = ru.left;
+ y1 = ru.top;
+ w1 = ru.right - ru.left;
+ h1 = ru.bottom - ru.top;
+ }
+ else {
+ x1 = 0; y1 = 0; w1 = w; h1 = h;
+ }
+
+ // copy image at hdc
+ BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY);
+
+ Frame->PaintCallbackProc(Frame->hWnd, hdc, &ru, Frame->UpdateRgn, Frame->dwFlags, Frame->PaintData);
+ }
+ else {
+ RECT r;
+ GetClientRect(Frame->hWnd, &r);
+ HRGN rgnUpdate = CreateRectRgn(r.left, r.top, r.right, r.bottom);
+ ru = r;
+ if (!IsRectEmpty(&ru)) {
+ x1 = ru.left;
+ y1 = ru.top;
+ w1 = ru.right - ru.left;
+ h1 = ru.bottom - ru.top;
+ }
+ else {
+ x1 = 0; y1 = 0; w1 = w; h1 = h;
+ }
+
+ // copy image at hdc
+ if (SkipBkgBlitting) //image already at foreground
+ BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hImageDC, x + x1, y + y1, SRCCOPY);
+ else
+ BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY);
+
+ Frame->PaintCallbackProc(Frame->hWnd, hdc, &r, rgnUpdate, Frame->dwFlags, Frame->PaintData);
+ ru = r;
+ DeleteObject(rgnUpdate);
+ }
+ DeleteObject(Frame->UpdateRgn);
+ Frame->UpdateRgn = nullptr;
+
+ if (!IsRectEmpty(&ru)) {
+ x1 = ru.left;
+ y1 = ru.top;
+ w1 = ru.right - ru.left;
+ h1 = ru.bottom - ru.top;
+ }
+ else {
+ x1 = 0; y1 = 0; w1 = w; h1 = h;
+ }
+
+ BitBlt(g_pCachedWindow->hImageDC, x + x1, y + y1, w1, h1, hdc, x1, y1, SRCCOPY);
+
+ if (GetWindowLongPtr(Frame->hWnd, GWL_STYLE) & WS_VSCROLL) {
+ //Draw vertical scroll bar
+ //
+ SCROLLBARINFO si = { 0 };
+ si.cbSize = sizeof(SCROLLBARINFO);
+ GetScrollBarInfo(Frame->hWnd, OBJID_VSCROLL, &si);
+
+ RECT rLine = (si.rcScrollBar);
+ RECT rUpBtn = rLine;
+ RECT rDnBtn = rLine;
+ RECT rThumb = rLine;
+
+ rUpBtn.bottom = rUpBtn.top + si.dxyLineButton;
+ rDnBtn.top = rDnBtn.bottom - si.dxyLineButton;
+ rThumb.top = rLine.top + si.xyThumbTop;
+ rThumb.bottom = rLine.top + si.xyThumbBottom;
+
+ int dx = Frame->wndSize.right - rLine.right;
+ int dy = -rLine.top + Frame->wndSize.top;
+
+ OffsetRect(&rLine, dx, dy);
+ OffsetRect(&rUpBtn, dx, dy);
+ OffsetRect(&rDnBtn, dx, dy);
+ OffsetRect(&rThumb, dx, dy);
+ BitBlt(g_pCachedWindow->hImageDC, rLine.left, rLine.top, rLine.right - rLine.left, rLine.bottom - rLine.top, g_pCachedWindow->hBackDC, rLine.left, rLine.top, SRCCOPY);
+
+ char req[255];
+ mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Back", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rLine, &rLine, req);
+ mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Thumb", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rThumb, &rThumb, req);
+ mir_snprintf(req, "Main,ID=ScrollBar, Frame=%S,Part=UpLineButton", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rUpBtn, &rUpBtn, req);
+ mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=DownLineButton", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rDnBtn, &rDnBtn, req);
+ }
+
+ SelectObject(hdc, o);
+ DeleteObject(n);
+ DeleteDC(hdc);
+ return 1;
+}
+
+int ske_BltBackImage(HWND destHWND, HDC destDC, RECT *BltClientRect)
+{
+ POINT ptMainWnd = { 0 };
+ POINT ptChildWnd = { 0 };
+ RECT w = { 0 };
+ if (g_CluiData.fDisableSkinEngine) {
+ FillRect(destDC, BltClientRect, GetSysColorBrush(COLOR_3DFACE));
+ return 0;
+ }
+ ske_ReCreateBackImage(FALSE, nullptr);
+ if (BltClientRect) w = *BltClientRect;
+ else GetClientRect(destHWND, &w);
+ ptChildWnd.x = w.left;
+ ptChildWnd.y = w.top;
+ ClientToScreen(destHWND, &ptChildWnd);
+ ClientToScreen(g_clistApi.hwndContactList, &ptMainWnd);
+ //TODO if main not relative to client area
+ return BitBlt(destDC, w.left, w.top, (w.right - w.left), (w.bottom - w.top), g_pCachedWindow->hBackDC, (ptChildWnd.x - ptMainWnd.x), (ptChildWnd.y - ptMainWnd.y), SRCCOPY);
+
+}
+
+int ske_ReCreateBackImage(BOOL Erase, RECT *w)
+{
+ RECT wnd = { 0 };
+ BOOL IsNewCache = 0;
+ if (g_CluiData.fDisableSkinEngine) return 0;
+ GetClientRect(g_clistApi.hwndContactList, &wnd);
+ if (w) wnd = *w;
+ //-- Check cached.
+ if (g_pCachedWindow == nullptr) {
+ //-- Create New Cache
+ g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
+ g_pCachedWindow->hScreenDC = GetDC(nullptr);
+ g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) {
+ g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB);
+ g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB);
+ }
+ IsNewCache = 1;
+ }
+
+ if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) {
+ HBITMAP hb1 = nullptr, hb2 = nullptr;
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) {
+ hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ SelectObject(g_pCachedWindow->hImageDC, hb1);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ }
+ else {
+ SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld);
+ SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld);
+ }
+ if (g_pCachedWindow->hImageDIB) DeleteObject(g_pCachedWindow->hImageDIB);
+ if (g_pCachedWindow->hBackDIB) DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hImageDIB = hb1;
+ g_pCachedWindow->hBackDIB = hb2;
+ IsNewCache = 1;
+ }
+
+ if ((Erase || IsNewCache) && (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0)) {
+ HBITMAP hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hBackDIB = hb2;
+ FillRect(g_pCachedWindow->hBackDC, &wnd, GetSysColorBrush(COLOR_BTNFACE));
+ SkinDrawGlyph(g_pCachedWindow->hBackDC, &wnd, &wnd, "Main,ID=Background,Opt=Non-Layered");
+ ske_SetRectOpaque(g_pCachedWindow->hBackDC, &wnd);
+ }
+ return 1;
+}
+
+int ske_DrawNonFramedObjects(BOOL Erase, RECT *r)
+{
+ RECT w, wnd;
+ if (r) w = *r;
+ else CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &w);
+ if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr);
+ if (g_pCachedWindow == nullptr)
+ return ske_ValidateFrameImageProc(&w);
+
+ wnd = w;
+ OffsetRect(&w, -w.left, -w.top);
+ if (Erase) {
+ HBITMAP hb2;
+ hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hBackDIB = hb2;
+ }
+
+ SkinDrawGlyph(g_pCachedWindow->hBackDC, &w, &w, "Main,ID=Background");
+
+ //--Draw frames captions
+ for (int i = 0; i < g_nFramesCount; i++) {
+ if (g_pfwFrames[i].TitleBar.ShowTitleBar && g_pfwFrames[i].visible && !g_pfwFrames[i].floating) {
+ RECT rc;
+ SetRect(&rc, g_pfwFrames[i].wndSize.left, g_pfwFrames[i].wndSize.top - g_nTitleBarHeight - g_CluiData.nGapBetweenTitlebar, g_pfwFrames[i].wndSize.right, g_pfwFrames[i].wndSize.top - g_CluiData.nGapBetweenTitlebar);
+ Sync(DrawTitleBar, g_pCachedWindow->hBackDC, &rc, g_pfwFrames[i].id);
+ }
+ }
+ g_mutex_bLockUpdating = 1;
+
+ flag_bJustDrawNonFramedObjects = 1;
+ return 0;
+}
+
+// Calling queued frame paint procs and refresh image
+int ske_ValidateFrameImageProc(RECT *r)
+{
+ RECT wnd = { 0 };
+ BOOL IsNewCache = 0;
+ BOOL IsForceAllPainting = 0;
+ if (r) wnd = *r;
+ else GetWindowRect(g_clistApi.hwndContactList, &wnd);
+ if (wnd.right - wnd.left == 0 || wnd.bottom - wnd.top == 0)
+ return 0;
+
+ g_mutex_bLockUpdating = 1;
+
+ //-- Check cached.
+ if (g_pCachedWindow == nullptr) {
+ //-- Create New Cache
+ g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
+ g_pCachedWindow->hScreenDC = GetDC(nullptr);
+ g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB);
+ g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB);
+ IsNewCache = 1;
+ }
+ if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) {
+ HBITMAP hb1, hb2;
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ SelectObject(g_pCachedWindow->hImageDC, hb1);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ DeleteObject(g_pCachedWindow->hImageDIB);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hImageDIB = hb1;
+ g_pCachedWindow->hBackDIB = hb2;
+ IsNewCache = 1;
+ }
+ if (IsNewCache) {
+ ske_DrawNonFramedObjects(0, &wnd);
+ IsForceAllPainting = 1;
+ }
+ if (flag_bJustDrawNonFramedObjects) {
+ IsForceAllPainting = 1;
+ flag_bJustDrawNonFramedObjects = 0;
+ }
+ if (IsForceAllPainting) {
+ BitBlt(g_pCachedWindow->hImageDC, 0, 0, g_pCachedWindow->Width, g_pCachedWindow->Height, g_pCachedWindow->hBackDC, 0, 0, SRCCOPY);
+ Sync(QueueAllFramesUpdating, true);
+ }
+ //-- Validating frames
+ for (int i = 0; i < g_nFramesCount; i++)
+ if (g_pfwFrames[i].PaintCallbackProc && g_pfwFrames[i].visible && !g_pfwFrames[i].floating)
+ if (g_pfwFrames[i].bQueued || IsForceAllPainting)
+ ske_ValidateSingleFrameImage(&g_pfwFrames[i], IsForceAllPainting);
+
+ g_mutex_bLockUpdating = 1;
+ ModernSkinButtonRedrawAll();
+ g_mutex_bLockUpdating = 0;
+ if (!mutex_bLockUpdate)
+ ske_UpdateWindowImageRect(&wnd);
+
+ //-- Clear queue
+ Sync(QueueAllFramesUpdating, false);
+ flag_bUpdateQueued = 0;
+ g_bPostWasCanceled = false;
+ return 1;
+}
+
+int ske_UpdateWindowImage()
+{
+ if (MirandaExiting())
+ return 0;
+
+ if (g_CluiData.fLayered) {
+ RECT r;
+ GetWindowRect(g_clistApi.hwndContactList, &r);
+ return ske_UpdateWindowImageRect(&r);
+ }
+ else ske_ReCreateBackImage(FALSE, nullptr);
+ ske_ApplyTranslucency();
+ return 0;
+}
+
+int ske_UpdateWindowImageRect(RECT *r) // Update window with current image and
+{
+ //if not validity -> ValidateImageProc
+ //else Update using current alpha
+ RECT wnd = *r;
+
+ if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr);
+ if (g_pCachedWindow == nullptr) return ske_ValidateFrameImageProc(&wnd);
+ if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) return ske_ValidateFrameImageProc(&wnd);
+ if (g_bFullRepaint) {
+ g_bFullRepaint = false;
+ return ske_ValidateFrameImageProc(&wnd);
+ }
+ ske_JustUpdateWindowImageRect(&wnd);
+ return 0;
+}
+
+void ske_ApplyTranslucency()
+{
+ int IsTransparancy;
+ HWND hwnd = g_clistApi.hwndContactList;
+ BOOL layered = (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) ? TRUE : FALSE;
+
+ IsTransparancy = g_CluiData.fSmoothAnimation || g_bTransparentFlag;
+ if (!g_bTransparentFlag && !g_CluiData.fSmoothAnimation && g_CluiData.bCurrentAlpha != 0)
+ g_CluiData.bCurrentAlpha = 255;
+
+ if (!g_CluiData.fLayered && IsTransparancy) {
+ if (!layered)
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (uint8_t)g_CluiData.bCurrentAlpha, LWA_ALPHA);
+ }
+
+ AniAva_RedrawAllAvatars(FALSE);
+ return;
+}
+
+int ske_JustUpdateWindowImage()
+{
+ RECT r;
+ if (!g_CluiData.fLayered) {
+ ske_ApplyTranslucency();
+ return 0;
+ }
+ GetWindowRect(g_clistApi.hwndContactList, &r);
+ return ske_JustUpdateWindowImageRect(&r);
+}
+
+// Update window image
+int ske_JustUpdateWindowImageRect(RECT *rty)
+{
+ if (!g_CluiData.fLayered) {
+ ske_ApplyTranslucency();
+ return 0;
+ }
+ if (!g_clistApi.hwndContactList)
+ return 0;
+
+ RECT wnd = *rty;
+ RECT rect = wnd;
+ POINT dest = { 0 }, src = { 0 };
+ dest.x = rect.left;
+ dest.y = rect.top;
+ SIZE sz = { rect.right - rect.left, rect.bottom - rect.top };
+ if (g_CluiData.fLayered) {
+ if (!(GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) & WS_EX_LAYERED))
+ SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
+ Sync(SetAlpha, g_CluiData.bCurrentAlpha);
+
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, g_CluiData.bCurrentAlpha, AC_SRC_ALPHA };
+ UpdateLayeredWindow(g_clistApi.hwndContactList, g_pCachedWindow->hScreenDC, &dest, &sz, g_pCachedWindow->hImageDC, &src, RGB(1, 1, 1), &bf, ULW_ALPHA);
+ g_CluiData.fAeroGlass = false;
+ CLUI_UpdateAeroGlass();
+ }
+ else InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ return 0;
+}
+
+int ske_DrawImageAt(HDC hdc, RECT *rc)
+{
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ BitBlt(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, g_pCachedWindow->hBackDC, rc->left, rc->top, SRCCOPY);
+ ske_AlphaBlend(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, hdc, 0, 0, rc->right - rc->left, rc->bottom - rc->top, bf);
+ if (!g_mutex_bLockUpdating)
+ ske_UpdateWindowImage();
+ return 0;
+}
+
+HBITMAP ske_GetCurrentWindowImage()
+{
+ return g_pCachedWindow->hImageDIB;
+}
+
+/*
+* Glyph text routine
+*/
+
+static uint32_t ske_HexToARGB(char *Hex)
+{
+ char buf[10] = { 0 };
+ char buf2[11] = { 0 };
+ mir_snprintf(buf, "%s\n", Hex);
+ if (buf[1] == 'x' || buf[1] == 'X')
+ mir_snprintf(buf2, "0x%s\n", buf + 2);
+ else
+ mir_snprintf(buf2, "0x%s\n", buf);
+ buf2[10] = '\0';
+
+ char *st;
+ uint32_t AARRGGBB = strtoul(buf2, &st, 16);
+ uint8_t alpha = (uint8_t)((AARRGGBB & 0xFF000000) >> 24);
+ alpha = 255 - ((alpha == 0) ? 255 : alpha);
+ AARRGGBB = (alpha << 24) + ((AARRGGBB & 0x00FF0000) >> 16) + ((AARRGGBB & 0x000000FF) << 16) + (AARRGGBB & 0x0000FF00);
+ return AARRGGBB;
+}
+
+static wchar_t *ske_ReAppend(wchar_t *lfirst, wchar_t *lsecond, int len)
+{
+ size_t l1 = lfirst ? mir_wstrlen(lfirst) : 0;
+ size_t l2 = (len ? len : (mir_wstrlen(lsecond) + 1));
+ wchar_t *buf = (wchar_t *)mir_alloc((l1 + l2 + 1)*sizeof(wchar_t));
+ if (lfirst) memmove(buf, lfirst, l1*sizeof(wchar_t));
+ memmove(buf + l1, lsecond, l2*sizeof(wchar_t));
+ mir_free(lfirst);
+ if (len) buf[l1 + l2] = '\0';
+ return buf;
+}
+
+wchar_t* ske_ReplaceVar(wchar_t *var)
+{
+ if (!var) return mir_wstrdup(L"");
+ if (!mir_wstrcmpi(var, L"Profile")) {
+ char buf[MAX_PATH] = { 0 };
+ Profile_GetNameA(MAX_PATH, buf);
+
+ char *p = strrchr(buf, '.');
+ if (p) *p = 0;
+
+ mir_free(var);
+ return mir_a2u(buf);
+ }
+
+ mir_free(var);
+ return mir_wstrdup(L"");
+}
+
+wchar_t *ske_ParseText(wchar_t *stzText)
+{
+ size_t len = mir_wstrlen(stzText);
+ wchar_t *result = nullptr;
+ size_t stpos = 0, curpos = 0;
+
+ while (curpos < len) {
+ //1 find first %
+ while (curpos < len && stzText[curpos] != (wchar_t)'%')
+ curpos++;
+ if (curpos < len) { //% found
+ if (curpos - stpos > 0)
+ result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos));
+ stpos = curpos + 1;
+ curpos++;
+ //3 find second %
+ while (curpos < len && stzText[curpos] != (wchar_t)'%')
+ curpos++;
+ if (curpos >= len)
+ break;
+ if (curpos - stpos > 0) {
+ wchar_t *var = (wchar_t *)mir_alloc((curpos - stpos + 1)*sizeof(wchar_t));
+ memcpy(var, stzText + stpos, (curpos - stpos)*sizeof(wchar_t));
+ var[curpos - stpos] = (wchar_t)'\0';
+ var = ske_ReplaceVar(var);
+ result = ske_ReAppend(result, var, 0);
+ mir_free(var);
+ }
+ else result = ske_ReAppend(result, L"%", 0);
+
+ curpos++;
+ stpos = curpos;
+ }
+ else {
+ if (curpos - stpos > 0)
+ result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos));
+ break;
+ }
+ }
+ return result;
+}
+/*
+* Parse text object string, find glyph object and add text to it.
+* szGlyphTextID and Define string is:
+* t[szGlyphTextID] = s[HostObjectID],[Left],[Top],[Right],[Bottom],[LTRBHV],[FontID],[Color1],[reservedforColor2],[Text]
+*/
+
+static void ske_AddParseTextGlyphObject(char *szGlyphTextID, char *szDefineString, SKINOBJECTSLIST *Skin)
+{
+ char buf[255] = { 0 };
+ GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE);
+ if (buf[0] == 0)
+ return;
+
+ GLYPHTEXT *glText = (GLYPHTEXT*)mir_calloc(sizeof(GLYPHTEXT));
+ glText->szGlyphTextID = mir_strdup(szGlyphTextID);
+ glText->szObjectName = mir_strdup(buf);
+ glText->iLeft = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE));
+ glText->iTop = atoi(GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE));
+ glText->iRight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 3, ',', TRUE));
+ glText->iBottom = atoi(GetParamN(szDefineString, buf, sizeof(buf), 4, ',', TRUE));
+ {
+ memset(buf, 0, 6);
+ GetParamN(szDefineString, buf, sizeof(buf), 5, ',', TRUE);
+ buf[0] &= 95; buf[1] &= 95; buf[2] &= 95; buf[3] &= 95; buf[4] &= 95; buf[5] &= 95; //to uppercase: &01011111 (0-95)
+ glText->RelativeFlags =
+ (buf[0] == 'C' ? 1 : ((buf[0] == 'R') ? 2 : 0)) //[BC][RC][BC][RC] --- Left relative
+ | (buf[1] == 'C' ? 4 : ((buf[1] == 'B') ? 8 : 0)) // | | |--------- Top relative
+ | (buf[2] == 'C' ? 16 : ((buf[2] == 'R') ? 32 : 0)) // | |--------------Right relative
+ | (buf[3] == 'C' ? 64 : ((buf[3] == 'B') ? 128 : 0)); // |------------------Bottom relative
+ glText->dwFlags = (buf[4] == 'C' ? DT_CENTER : ((buf[4] == 'R') ? DT_RIGHT : DT_LEFT))
+ | (buf[5] == 'C' ? DT_VCENTER : ((buf[5] == 'B') ? DT_BOTTOM : DT_TOP));
+ }
+ glText->szFontID = mir_strdup(GetParamN(szDefineString, buf, sizeof(buf), 6, ',', TRUE));
+
+ glText->dwColor = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 7, ',', TRUE));
+ glText->dwShadow = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 8, ',', TRUE));
+ glText->stValueText = mir_a2u(GetParamN(szDefineString, buf, sizeof(buf), 9, ',', TRUE));
+ glText->stText = ske_ParseText(glText->stValueText);
+
+ if (!Skin->pTextList)
+ Skin->pTextList = List_Create(0, 1);
+ List_InsertPtr(Skin->pTextList, glText);
+}
+
+
+/*
+* Parse font definition string.
+* szGlyphTextID and Define string is:
+* f[szFontID] = s[FontTypefaceName],[size],[BIU]
+*/
+static void ske_AddParseSkinFont(char *szFontID, char *szDefineString)
+{
+ SKINFONT *sf = (SKINFONT*)mir_calloc(sizeof(SKINFONT));
+ if (!sf)
+ return;
+
+ LOGFONTA logfont = { 0 };
+ logfont.lfCharSet = DEFAULT_CHARSET;
+ logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ logfont.lfQuality = DEFAULT_QUALITY;
+ logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ char buf[255];
+ strncpy_s(logfont.lfFaceName, GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE), _TRUNCATE);
+ logfont.lfHeight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE));
+ if (logfont.lfHeight < 0) {
+ HDC hdc = CreateCompatibleDC(nullptr);
+ logfont.lfHeight = (long)-MulDiv(logfont.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ DeleteDC(hdc);
+ }
+ logfont.lfHeight = -logfont.lfHeight;
+ GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE);
+ buf[0] &= 95; buf[1] &= 95; buf[2] &= 95;
+ logfont.lfWeight = (buf[0] == 'B') ? FW_BOLD : FW_NORMAL;
+ logfont.lfItalic = (buf[1] == 'I') ? 1 : 0;
+ logfont.lfUnderline = (buf[2] == 'U') ? 1 : 0;
+
+ sf->hFont = CreateFontIndirectA(&logfont);
+ if (sf->hFont) {
+ sf->szFontID = mir_strdup(szFontID);
+ if (!gl_plSkinFonts)
+ gl_plSkinFonts = List_Create(0, 1);
+ if (gl_plSkinFonts)
+ List_Insert(gl_plSkinFonts, sf, gl_plSkinFonts->realCount);
+ else
+ mir_free(sf);
+ }
+ else mir_free(sf);
+}
+
+/*
+ * ske_CheckHasAlfaChannel - checks if image has at least one uint8_t in alpha chennel
+ * that is not a 0. (is image real 32 bit or just 24 bit)
+ */
+static BOOL ske_CheckHasAlfaChannel(uint8_t *from, int widthByte, int height)
+{
+ uint32_t *pt = (uint32_t*)from;
+ for (int j = 0; j < height; j++) {
+ uint8_t *add = (uint8_t*)pt + widthByte;
+ while (pt < (uint32_t*)add) {
+ if ((*pt & 0xFF000000) != 0)
+ return TRUE;
+ pt++;
+ }
+ pt = (uint32_t*)(from + widthByte*j);
+ }
+ return FALSE;
+}
+
+/*
+ * ske_CheckIconHasMask - checks if mask image has at least one that is not a 0.
+ * Not sure is ir required or not
+ */
+static BOOL ske_CheckIconHasMask(uint8_t *from)
+{
+ for (int i = 0; i < 16 * 16 / 8; i++)
+ if (from[i] != 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * ske_GetMaskBit - return value of apropriate mask bit in line at x position
+ */
+static BOOL ske_GetMaskBit(uint8_t *line, int x)
+{
+ return ((*(line + (x >> 3)))&(0x01 << (7 - (x & 0x07)))) != 0;
+}
+
+/*
+ * ske_Blend - alpha ske_Blend ARGB values of 2 pixels. X1 - underlaying,
+ * X2 - overlaying points.
+ */
+
+static uint32_t ske_Blend(uint32_t X1, uint32_t X2, uint8_t alpha)
+{
+ uint8_t a1 = (uint8_t)(X1 >> 24);
+ uint8_t a2 = (uint8_t)(((X2 >> 24)*alpha) >> 8);
+ uint8_t r1 = (uint8_t)(X1 >> 16);
+ uint8_t r2 = (uint8_t)(X2 >> 16);
+ uint8_t g1 = (uint8_t)(X1 >> 8);
+ uint8_t g2 = (uint8_t)(X2 >> 8);
+ uint8_t b1 = (uint8_t)(X1);
+ uint8_t b2 = (uint8_t)(X2);
+
+ uint8_t a_1 = ~a1;
+ uint8_t a_2 = ~a2;
+ uint16_t am = (uint16_t)a1*a_2;
+
+ /* it is possible to use >>8 instead of /255 but it is require additional
+ * checking of alphavalues
+ */
+ uint16_t ar = a1 + (((uint16_t)a_1*a2) / 255);
+ // if a2 more than 0 than result should be more
+ // or equal (if a1 == 0) to a2, else in combination
+ // with mask we can get here black points
+
+ ar = (a2 > ar) ? a2 : ar;
+
+ if (ar == 0) return 0;
+
+ uint16_t arm = ar * 255;
+ uint16_t rr = (((uint16_t)r1*am + (uint16_t)r2*a2 * 255)) / arm;
+ uint16_t gr = (((uint16_t)g1*am + (uint16_t)g2*a2 * 255)) / arm;
+ uint16_t br = (((uint16_t)b1*am + (uint16_t)b2*a2 * 255)) / arm;
+ return (ar << 24) | (rr << 16) | (gr << 8) | br;
+}
+
+/*
+ * CreateJoinedIcon - creates new icon by drawing hTop over hBottom.
+ */
+
+HICON ske_CreateJoinedIcon(HICON hBottom, HICON hTop, uint8_t alpha)
+{
+ ICONINFO iNew = { 0 };
+ ICONINFO iciBottom = { 0 };
+ ICONINFO iciTop = { 0 };
+
+ BITMAP bmp_top = { 0 };
+ BITMAP bmp_top_mask = { 0 };
+
+ BITMAP bmp_bottom = { 0 };
+ BITMAP bmp_bottom_mask = { 0 };
+
+ HDC tempDC = CreateCompatibleDC(nullptr);
+
+ uint8_t *ptPixels;
+ HBITMAP nImage = ske_CreateDIB32Point(16, 16, (void**)&ptPixels);
+ HBITMAP oImage = (HBITMAP)SelectObject(tempDC, nImage);
+
+ GetIconInfo(hBottom, &iciBottom);
+ GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
+ GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);
+
+ GetIconInfo(hTop, &iciTop);
+ GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
+ GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);
+
+ if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32) {
+ uint8_t *BottomBuffer, *TopBuffer, *BottomMaskBuffer, *TopMaskBuffer;
+ uint8_t *bb, *tb, *bmb, *tmb;
+ uint8_t *db = ptPixels;
+ int vstep_d = 16 * 4;
+ int vstep_b = bmp_bottom.bmWidthBytes;
+ int vstep_t = bmp_top.bmWidthBytes;
+ int vstep_bm = bmp_bottom_mask.bmWidthBytes;
+ int vstep_tm = bmp_top_mask.bmWidthBytes;
+ alpha = alpha ? alpha : 255;
+ if (bmp_bottom.bmBits) bb = BottomBuffer = (uint8_t*)bmp_bottom.bmBits;
+ else {
+ BottomBuffer = (uint8_t*)mir_alloc(bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes, BottomBuffer);
+ bb = BottomBuffer + vstep_b*(bmp_bottom.bmHeight - 1);
+ vstep_b = -vstep_b;
+ }
+
+ if (bmp_top.bmBits) tb = TopBuffer = (uint8_t*)bmp_top.bmBits;
+ else {
+ TopBuffer = (uint8_t*)mir_alloc(bmp_top.bmHeight*bmp_top.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight*bmp_top.bmWidthBytes, TopBuffer);
+ tb = TopBuffer + vstep_t*(bmp_top.bmHeight - 1);
+ vstep_t = -vstep_t;
+ }
+
+ if (bmp_bottom_mask.bmBits) {
+ BottomMaskBuffer = (uint8_t*)bmp_bottom_mask.bmBits;
+ bmb = BottomMaskBuffer;
+ }
+ else {
+ BottomMaskBuffer = (uint8_t*)mir_alloc(bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
+ bmb = BottomMaskBuffer + vstep_bm*(bmp_bottom_mask.bmHeight - 1);
+ vstep_bm = -vstep_bm;
+
+ }
+
+ if (bmp_top_mask.bmBits) {
+ TopMaskBuffer = (uint8_t*)bmp_top_mask.bmBits;
+ tmb = TopMaskBuffer;
+ }
+ else {
+ TopMaskBuffer = (uint8_t*)mir_alloc(bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes, TopMaskBuffer);
+ tmb = TopMaskBuffer + vstep_tm*(bmp_top_mask.bmHeight - 1);
+ vstep_tm = -vstep_tm;
+ }
+
+ BOOL topHasAlpha = ske_CheckHasAlfaChannel(TopBuffer, bmp_top.bmWidthBytes, bmp_top.bmHeight);
+ BOOL bottomHasAlpha = ske_CheckHasAlfaChannel(BottomBuffer, bmp_bottom.bmWidthBytes, bmp_bottom.bmHeight);
+ BOOL topHasMask = ske_CheckIconHasMask(TopMaskBuffer);
+ BOOL bottomHasMask = ske_CheckIconHasMask(BottomMaskBuffer);
+ for (int y = 0; y < 16; y++) {
+ for (int x = 0; x < 16; x++) {
+ BOOL mask_b = ske_GetMaskBit(bmb, x);
+ BOOL mask_t = ske_GetMaskBit(tmb, x);
+ uint32_t bottom_d = ((uint32_t*)bb)[x];
+ uint32_t top_d = ((uint32_t*)tb)[x];
+ if (topHasMask) {
+ if (mask_t == 1 && !topHasAlpha) top_d &= 0xFFFFFF;
+ else if (!topHasAlpha) top_d |= 0xFF000000;
+ }
+ if (bottomHasMask) {
+ if (mask_b == 1 && !bottomHasAlpha) bottom_d &= 0xFFFFFF;
+ else if (!bottomHasAlpha) bottom_d |= 0xFF000000;
+ }
+ ((uint32_t*)db)[x] = ske_Blend(bottom_d, top_d, alpha);
+ }
+ bb += vstep_b;
+ tb += vstep_t;
+ bmb += vstep_bm;
+ tmb += vstep_tm;
+ db += vstep_d;
+ }
+
+ if (!bmp_bottom.bmBits) mir_free(BottomBuffer);
+ if (!bmp_top.bmBits) mir_free(TopBuffer);
+ if (!bmp_bottom_mask.bmBits) mir_free(BottomMaskBuffer);
+ if (!bmp_top_mask.bmBits) mir_free(TopMaskBuffer);
+ }
+ else {
+ ske_DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL);
+ ske_DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL | (alpha << 24));
+ }
+
+ DeleteObject(iciBottom.hbmColor);
+ DeleteObject(iciTop.hbmColor);
+ DeleteObject(iciBottom.hbmMask);
+ DeleteObject(iciTop.hbmMask);
+
+ SelectObject(tempDC, oImage);
+ DeleteDC(tempDC);
+
+ uint8_t p[32] = { 0 };
+ HBITMAP nMask = CreateBitmap(16, 16, 1, 1, (void*)&p);
+ {
+ HDC tempDC2 = CreateCompatibleDC(nullptr);
+ HDC tempDC3 = CreateCompatibleDC(nullptr);
+ HBITMAP hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
+ HBITMAP obmp = (HBITMAP)SelectObject(tempDC2, nMask);
+ HBITMAP obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
+ DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK);
+ DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK);
+ BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);
+ SelectObject(tempDC2, obmp);
+ SelectObject(tempDC3, obmp2);
+ DeleteObject(hbm);
+ DeleteDC(tempDC2);
+ DeleteDC(tempDC3);
+ }
+ iNew.fIcon = TRUE;
+ iNew.hbmColor = nImage;
+ iNew.hbmMask = nMask;
+ HICON res = CreateIconIndirect(&iNew);
+ DeleteObject(nImage);
+ DeleteObject(nMask);
+ return res;
+}
+
+#define NEWJOINEDSTR(destination, first, separator, last) \
+ destination = (char*)alloca(mir_strlen(first)+mir_strlen(separator)+mir_strlen(last)+1); \
+ if (destination) { \
+ *destination = '\0'; \
+ mir_strcat(destination,first); \
+ mir_strcat(destination,separator); \
+ mir_strcat(destination,last); \
+ }
+
+#define SKINSETSECTION "SkinnedSettings"
+
+BOOL SkinDBGetContactSetting(MCONTACT hContact, const char *szSection, const char *szKey, DBVARIANT *retdbv, BOOL *bSkinned)
+{
+ if (!hContact) { //only for not contact settings
+ char *szSkinKey;
+ NEWJOINEDSTR(szSkinKey, szSection, "@", szKey);
+ if (!db_get(hContact, SKINSETSECTION, szSkinKey, retdbv)) {
+ if (bSkinned) *bSkinned = TRUE;
+ return FALSE;
+ }
+ }
+ // not skinned
+ if (bSkinned) bSkinned = FALSE;
+ return db_get(hContact, szSection, szKey, retdbv);
+}
+
+uint8_t SkinDBGetContactSettingByte(MCONTACT hContact, const char *szSection, const char *szKey, uint8_t bDefault)
+{
+ DBVARIANT dbv = { 0 };
+ BOOL bSkinned = FALSE;
+ if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
+ if (dbv.type == DBVT_BYTE) {
+ uint8_t retVal = dbv.bVal;
+ db_free(&dbv);
+ return retVal;
+ }
+ else {
+ db_free(&dbv);
+ if (!bSkinned)
+ return db_get_b(hContact, szSection, szKey, bDefault);
+ }
+ }
+ return bDefault;
+}
+
+uint16_t SkinDBGetContactSettingWord(MCONTACT hContact, const char *szSection, const char *szKey, uint16_t wDefault)
+{
+ BOOL bSkinned = FALSE;
+ DBVARIANT dbv = { 0 };
+ if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
+ if (dbv.type == DBVT_WORD) {
+ uint16_t retVal = dbv.wVal;
+ db_free(&dbv);
+ return retVal;
+ }
+ db_free(&dbv);
+ if (!bSkinned)
+ return db_get_w(hContact, szSection, szKey, wDefault);
+ }
+ return wDefault;
+}
+
+uint32_t SkinDBGetContactSettingDword(MCONTACT hContact, const char *szSection, const char *szKey, uint32_t dwDefault)
+{
+ DBVARIANT dbv = { 0 };
+ BOOL bSkinned = FALSE;
+ if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
+ if (dbv.type == DBVT_DWORD) {
+ uint32_t retVal = dbv.dVal;
+ db_free(&dbv);
+ return retVal;
+ }
+ db_free(&dbv);
+ if (!bSkinned)
+ return db_get_dw(hContact, szSection, szKey, dwDefault);
+ }
+ return dwDefault;
+}
diff --git a/plugins/Clist_modern/src/modern_skinopt.cpp b/plugins/Clist_modern/src/modern_skinopt.cpp index 5c18867fe2..c13f0c8bb8 100644 --- a/plugins/Clist_modern/src/modern_skinopt.cpp +++ b/plugins/Clist_modern/src/modern_skinopt.cpp @@ -1,546 +1,546 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or ( at your option ) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "modern_sync.h" - -/*******************************/ -// Main skin selection routine // -/*******************************/ -#define MAX_NAME 100 - -struct SkinListData -{ - wchar_t Name[MAX_NAME]; - wchar_t File[MAX_PATH]; -}; - -HBITMAP hPreviewBitmap = nullptr; -HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data); -HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName); -HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file); -HTREEITEM FillAvailableSkinList(HWND hwndDlg); - -INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); - -int SkinOptInit(WPARAM wParam, LPARAM) -{ - if (!g_CluiData.fDisableSkinEngine) { - //Tabbed settings - OPTIONSDIALOGPAGE odp = {}; - odp.position = -200000000; - odp.pfnDlgProc = DlgSkinOpts; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SKIN); - odp.szGroup.w = LPGENW("Skins"); - odp.szTitle.w = LPGENW("Contact list"); - odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; - g_plugin.addOptions(wParam, &odp); - } - return 0; -} - -INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_DESTROY: - if (hPreviewBitmap) - ske_UnloadGlyphImage(hPreviewBitmap); - break; - - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SetDlgItemText(hwndDlg, IDC_SKINFOLDERLABEL, SkinsFolder); - { - HTREEITEM it = FillAvailableSkinList(hwndDlg); - TreeView_SelectItem(GetDlgItem(hwndDlg, IDC_TREE1), it); - } - return 0; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_COLOUR_MENUNORMAL: - case IDC_COLOUR_MENUSELECTED: - case IDC_COLOUR_FRAMES: - case IDC_COLOUR_STATUSBAR: - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case IDC_BUTTON_INFO: - { - HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1)); - if (hti == nullptr) - return 0; - - TVITEM tvi = { 0 }; - tvi.hItem = hti; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi); - SkinListData *sd = (SkinListData*)(tvi.lParam); - if (!sd) - return 0; - - wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000]; - if (!wcschr(sd->File, '%')) { - GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd->File); - GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd->File); - GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd->File); - GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd->File); - mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s):\t %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"), - sd->Name, Description, Author, Contact, URL, sd->File); - } - else { - mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s): %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"), - TranslateT("reVista for Modern v0.5"), - TranslateT("This is second default Modern Contact list skin in Vista Aero style"), - TranslateT("Angeli-Ka (graphics), FYR (template)"), - L"JID: fyr@jabber.ru", - L"fyr.mirandaim.ru", - TranslateT("Inside library")); - } - MessageBox(hwndDlg, text, TranslateT("Skin information"), MB_OK | MB_ICONINFORMATION); - } - break; - - case IDC_BUTTON_APPLY_SKIN: - if (HIWORD(wParam) == BN_CLICKED) { - HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1)); - if (hti == nullptr) - return 0; - - TVITEM tvi = { 0 }; - tvi.hItem = hti; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi); - SkinListData *sd = (SkinListData*)(tvi.lParam); - if (!sd) - return 0; - - ske_LoadSkinFromIniFile(sd->File, FALSE); - ske_LoadSkinFromDB(); - Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0); - Sync(CLUIFrames_OnClistResize_mod, 0, 0); - ske_RedrawCompleteWindow(); - Sync(CLUIFrames_OnClistResize_mod, 0, 0); - - RECT rc = {}; - GetWindowRect(g_clistApi.hwndContactList, &rc); - Sync(CLUIFrames_OnMoving, g_clistApi.hwndContactList, &rc); - - if (g_hCLUIOptionsWnd) { - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT)); - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT)); - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT)); - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT)); - } - } - break; - - case IDC_GETSKINS: - if (HIWORD(wParam) == BN_CLICKED) - Utils_OpenUrl("https://miranda-ng.org/tags/modern-contact-list/"); - break; - - - case IDC_BUTTON_RESCAN: - if (HIWORD(wParam) == BN_CLICKED) { - HTREEITEM it = FillAvailableSkinList(hwndDlg); - HWND wnd = GetDlgItem(hwndDlg, IDC_TREE1); - TreeView_SelectItem(wnd, it); - } - } - break; - - case WM_DRAWITEM: - if (wParam == IDC_PREVIEW) { - // TODO:Draw hPreviewBitmap here - HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam; - int mWidth = dis->rcItem.right - dis->rcItem.left; - int mHeight = dis->rcItem.bottom - dis->rcItem.top; - HDC memDC = CreateCompatibleDC(dis->hDC); - HBITMAP hbmp = ske_CreateDIB32(mWidth, mHeight); - HBITMAP holdbmp = (HBITMAP)SelectObject(memDC, hbmp); - RECT workRect = dis->rcItem; - OffsetRect(&workRect, -workRect.left, -workRect.top); - FillRect(memDC, &workRect, hbr); - DeleteObject(hbr); - if (hPreviewBitmap) { - // variables - BITMAP bmp = {}; - GetObject(hPreviewBitmap, sizeof(bmp), &bmp); - - // GetSize - float xScale = 1, yScale = 1; - int wWidth = workRect.right - workRect.left; - int wHeight = workRect.bottom - workRect.top; - if (wWidth < bmp.bmWidth) - xScale = (float)wWidth / bmp.bmWidth; - if (wHeight < bmp.bmHeight) - yScale = (float)wHeight / bmp.bmHeight; - xScale = min(xScale, yScale); - yScale = xScale; - int dWidth = (int)(xScale*bmp.bmWidth); - int dHeight = (int)(yScale*bmp.bmHeight); - - // CalcPosition - POINT imgPos = { 0 }; - imgPos.x = workRect.left + ((wWidth - dWidth) >> 1); - imgPos.y = workRect.top + ((wHeight - dHeight) >> 1); - - // DrawImage - DrawAvatarImageWithGDIp(memDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255); - } - BitBlt(dis->hDC, dis->rcItem.left, dis->rcItem.top, mWidth, mHeight, memDC, 0, 0, SRCCOPY); - SelectObject(memDC, holdbmp); - DeleteObject(hbmp); - DeleteDC(memDC); - } - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - if (((LPNMHDR)lParam)->code == PSN_APPLY) { - Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0); - NotifyEventHooks(g_CluiData.hEventBkgrChanged, 0, 0); - Clist_Broadcast(INTM_INVALIDATE, 0, 0); - RedrawWindow(GetParent(g_clistApi.hwndContactTree), nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); - } - break; - - case IDC_TREE1: - NMTREEVIEW *nmtv = (NMTREEVIEW*)lParam; - if (nmtv == nullptr) - return 0; - - if (nmtv->hdr.code == TVN_SELCHANGED) { - if (hPreviewBitmap) { - ske_UnloadGlyphImage(hPreviewBitmap); - hPreviewBitmap = nullptr; - } - - if (nmtv->itemNew.lParam) { - SkinListData *sd = (SkinListData*)nmtv->itemNew.lParam; - - wchar_t buf[MAX_PATH]; - PathToRelativeW(sd->File, buf); - SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, buf); - - wchar_t prfn[MAX_PATH] = { 0 }, imfn[MAX_PATH] = { 0 }, skinfolder[MAX_PATH] = { 0 }; - GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), sd->File); - IniParser::GetSkinFolder(sd->File, skinfolder); - mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn); - PathToAbsoluteW(prfn, imfn); - hPreviewBitmap = ske_LoadGlyphImage(imfn); - - EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), TRUE); - EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), TRUE); - if (hPreviewBitmap) - InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEW), nullptr, TRUE); - else { // prepare text - HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1)); - if (hti == nullptr) - return 0; - - TVITEM tvi = { 0 }; - tvi.hItem = hti; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi); - SkinListData *sd2 = (SkinListData*)(tvi.lParam); - if (!sd2) - return 0; - - wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000]; - if (!wcschr(sd2->File, '%')) { - GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd2->File); - GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd2->File); - GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd2->File); - GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd2->File); - mir_snwprintf(text, TranslateT("Preview is not available\n\n%s\n----------------------\n\n%s\n\nAUTHOR(S):\n%s\n\nCONTACT:\n%s\n\nHOMEPAGE:\n%s"), - sd2->Name, Description, Author, Contact, URL); - } - else { - mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAUTHORS:\n%s\n\nCONTACT:\n%s\n\nWEB:\n%s\n\n\n"), - TranslateT("reVista for Modern v0.5"), - TranslateT("This is second default Modern Contact list skin in Vista Aero style"), - TranslateT("graphics by Angeli-Ka\ntemplate by FYR"), - L"JID: fyr@jabber.ru", - L"fyr.mirandaim.ru"); - } - ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_INFO), SW_SHOW); - SetDlgItemText(hwndDlg, IDC_STATIC_INFO, text); - } - } - else { - // no selected - SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, TranslateT("Select skin from list")); - EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), FALSE); - SetDlgItemText(hwndDlg, IDC_STATIC_INFO, TranslateT("Please select skin to apply")); - ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE); - } - ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), hPreviewBitmap ? SW_SHOW : SW_HIDE); - return 0; - } - else if (nmtv->hdr.code == TVN_DELETEITEM) { - mir_free_and_nil(nmtv->itemOld.lParam); - return 0; - } - break; - } - } - return 0; -} - -int SearchSkinFiles(HWND hwndDlg, wchar_t *Folder) -{ - wchar_t mask[MAX_PATH]; - mir_snwprintf(mask, L"%s\\*.msf", Folder); - - struct _wfinddata_t fd = {}; - intptr_t hFile = _wfindfirst(mask, &fd); - if (hFile != -1) { - do { - AddSkinToList(hwndDlg, Folder, fd.name); - } while (!_wfindnext(hFile, &fd)); - _findclose(hFile); - } - - mir_snwprintf(mask, L"%s\\*", Folder); - hFile = _wfindfirst(mask, &fd); - - do { - if (fd.attrib & _A_SUBDIR && !(mir_wstrcmpi(fd.name, L".") == 0 || mir_wstrcmpi(fd.name, L"..") == 0)) { //Next level of subfolders - wchar_t path[MAX_PATH]; - mir_snwprintf(path, L"%s\\%s", Folder, fd.name); - SearchSkinFiles(hwndDlg, path); - } - } while (!_wfindnext(hFile, &fd)); - - _findclose(hFile); - return 0; -} - -HTREEITEM FillAvailableSkinList(HWND hwndDlg) -{ - TreeView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_TREE1)); - AddSkinToList(hwndDlg, TranslateT("Default Skin"), L"%Default Skin%"); - int attrib = GetFileAttributes(SkinsFolder); - if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY)) - SearchSkinFiles(hwndDlg, SkinsFolder); - - HTREEITEM res = (HTREEITEM)-1; - wchar_t skinfull[MAX_PATH]; - ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile")); - if (skinfile) { - PathToAbsoluteW(skinfile, skinfull); - res = AddSkinToListFullName(hwndDlg, skinfull); - } - - return res; -} - -HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName) -{ - wchar_t path[MAX_PATH] = {}, file[MAX_PATH] = {}; - mir_wstrncpy(path, fullName, _countof(path)); - - wchar_t *buf = path + mir_wstrlen(path); - while (buf > path) { - if (*buf == '\\') { - *buf = '\0'; - break; - } - buf--; - } - buf++; - mir_wstrncpy(file, buf, _countof(file)); - return AddSkinToList(hwndDlg, path, file); -} - -HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file) -{ - wchar_t fullName[MAX_PATH], defskinname[MAX_PATH]; - SkinListData *sd = (SkinListData*)mir_alloc(sizeof(SkinListData)); - if (!sd) - return nullptr; - - if (!file || wcschr(file, '%')) { - mir_snwprintf(sd->File, L"%%Default Skin%%"); - mir_snwprintf(sd->Name, TranslateT("%Default Skin%")); - wcsncpy_s(fullName, TranslateT("Default Skin"), _TRUNCATE); - } - else { - mir_snwprintf(fullName, L"%s\\%s", path, file); - wcsncpy_s(defskinname, file, _TRUNCATE); - wchar_t *p = wcsrchr(defskinname, '.'); if (p) *p = 0; - GetPrivateProfileString(L"Skin_Description_Section", L"Name", defskinname, sd->Name, _countof(sd->Name), fullName); - wcsncpy_s(sd->File, fullName, _TRUNCATE); - } - return AddItemToTree(GetDlgItem(hwndDlg, IDC_TREE1), sd->Name, sd); -} - -HTREEITEM FindChild(HWND hTree, HTREEITEM Parent, wchar_t *Caption, void *data) -{ - HTREEITEM tmp = nullptr; - if (Parent) - tmp = TreeView_GetChild(hTree, Parent); - else - tmp = TreeView_GetRoot(hTree); - - while (tmp) { - TVITEM tvi; - wchar_t buf[255]; - tvi.hItem = tmp; - tvi.mask = TVIF_TEXT | TVIF_HANDLE; - tvi.pszText = buf; - tvi.cchTextMax = _countof(buf); - TreeView_GetItem(hTree, &tvi); - if (mir_wstrcmpi(Caption, tvi.pszText) == 0) { - if (!data) - return tmp; - - TVITEM tvi2 = { 0 }; - tvi2.hItem = tmp; - tvi2.mask = TVIF_HANDLE | TVIF_PARAM; - TreeView_GetItem(hTree, &tvi2); - SkinListData *sd = (SkinListData*)tvi2.lParam; - if (sd) - if (!mir_wstrcmpi(sd->File, ((SkinListData*)data)->File)) - return tmp; - } - tmp = TreeView_GetNextSibling(hTree, tmp); - } - return tmp; -} - -HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data) -{ - HTREEITEM cItem = nullptr; - // Insert item node - cItem = FindChild(hTree, nullptr, itemName, data); - if (!cItem) { - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_PARAM; - tvis.item.pszText = itemName; - tvis.item.lParam = (LPARAM)data; - return TreeView_InsertItem(hTree, &tvis); - } - - mir_free(data); // need to free otherwise memory leak - return cItem; -} - -INT_PTR SvcActiveSkin(WPARAM, LPARAM) -{ - ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile")); - if (skinfile) { - wchar_t skinfull[MAX_PATH]; - PathToAbsoluteW(skinfile, skinfull); - return (INT_PTR)mir_wstrdup(skinfull); - } - - return 0; -} - -INT_PTR SvcApplySkin(WPARAM, LPARAM lParam) -{ - ske_LoadSkinFromIniFile((wchar_t*)lParam, FALSE); - ske_LoadSkinFromDB(); - Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0); - Sync(CLUIFrames_OnClistResize_mod, 0, 0); - ske_RedrawCompleteWindow(); - Sync(CLUIFrames_OnClistResize_mod, 0, 0); - - HWND hwnd = g_clistApi.hwndContactList; - RECT rc = { 0 }; - GetWindowRect(hwnd, &rc); - Sync(CLUIFrames_OnMoving, hwnd, &rc); - - g_bChangingMode = TRUE; - CLUI_UpdateLayeredMode(); - CLUI_ChangeWindowMode(); - SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged - CLUI_ReloadCLUIOptions(); - cliShowHide(true); - g_bChangingMode = FALSE; - - if (g_hCLUIOptionsWnd) { - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT)); - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT)); - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT)); - SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT)); - } - return 0; -} - -INT_PTR SvcPreviewSkin(WPARAM wParam, LPARAM lParam) -{ - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)wParam; - RECT workRect = dis->rcItem; - OffsetRect(&workRect, -workRect.left, -workRect.top); - - if (lParam) { - wchar_t prfn[MAX_PATH] = { 0 }; - wchar_t imfn[MAX_PATH] = { 0 }; - wchar_t skinfolder[MAX_PATH] = { 0 }; - GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), (LPCTSTR)lParam); - IniParser::GetSkinFolder((LPCTSTR)lParam, skinfolder); - mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn); - PathToAbsoluteW(prfn, imfn); - - hPreviewBitmap = ske_LoadGlyphImage(imfn); - if (hPreviewBitmap) { - // variables - BITMAP bmp = { 0 }; - GetObject(hPreviewBitmap, sizeof(BITMAP), &bmp); - - // GetSize - float xScale = 1, yScale = 1; - int wWidth = workRect.right - workRect.left; - int wHeight = workRect.bottom - workRect.top; - if (wWidth < bmp.bmWidth) - xScale = (float)wWidth / bmp.bmWidth; - if (wHeight < bmp.bmHeight) - yScale = (float)wHeight / bmp.bmHeight; - xScale = min(xScale, yScale); - yScale = xScale; - int dWidth = (int)(xScale*bmp.bmWidth); - int dHeight = (int)(yScale*bmp.bmHeight); - - // CalcPosition - POINT imgPos = { 0 }; - imgPos.x = workRect.left + ((wWidth - dWidth) >> 1); - imgPos.y = workRect.top + ((wHeight - dHeight) >> 1); - - // DrawImage - DrawAvatarImageWithGDIp(dis->hDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255); - ske_UnloadGlyphImage(hPreviewBitmap); - } - } - - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "modern_sync.h"
+
+/*******************************/
+// Main skin selection routine //
+/*******************************/
+#define MAX_NAME 100
+
+struct SkinListData
+{
+ wchar_t Name[MAX_NAME];
+ wchar_t File[MAX_PATH];
+};
+
+HBITMAP hPreviewBitmap = nullptr;
+HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data);
+HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName);
+HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file);
+HTREEITEM FillAvailableSkinList(HWND hwndDlg);
+
+INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+int SkinOptInit(WPARAM wParam, LPARAM)
+{
+ if (!g_CluiData.fDisableSkinEngine) {
+ //Tabbed settings
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -200000000;
+ odp.pfnDlgProc = DlgSkinOpts;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SKIN);
+ odp.szGroup.w = LPGENW("Skins");
+ odp.szTitle.w = LPGENW("Contact list");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE;
+ g_plugin.addOptions(wParam, &odp);
+ }
+ return 0;
+}
+
+INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_DESTROY:
+ if (hPreviewBitmap)
+ ske_UnloadGlyphImage(hPreviewBitmap);
+ break;
+
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemText(hwndDlg, IDC_SKINFOLDERLABEL, SkinsFolder);
+ {
+ HTREEITEM it = FillAvailableSkinList(hwndDlg);
+ TreeView_SelectItem(GetDlgItem(hwndDlg, IDC_TREE1), it);
+ }
+ return 0;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_COLOUR_MENUNORMAL:
+ case IDC_COLOUR_MENUSELECTED:
+ case IDC_COLOUR_FRAMES:
+ case IDC_COLOUR_STATUSBAR:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_BUTTON_INFO:
+ {
+ HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
+ if (hti == nullptr)
+ return 0;
+
+ TVITEM tvi = { 0 };
+ tvi.hItem = hti;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
+ SkinListData *sd = (SkinListData*)(tvi.lParam);
+ if (!sd)
+ return 0;
+
+ wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000];
+ if (!wcschr(sd->File, '%')) {
+ GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd->File);
+ mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s):\t %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"),
+ sd->Name, Description, Author, Contact, URL, sd->File);
+ }
+ else {
+ mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s): %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"),
+ TranslateT("reVista for Modern v0.5"),
+ TranslateT("This is second default Modern Contact list skin in Vista Aero style"),
+ TranslateT("Angeli-Ka (graphics), FYR (template)"),
+ L"JID: fyr@jabber.ru",
+ L"fyr.mirandaim.ru",
+ TranslateT("Inside library"));
+ }
+ MessageBox(hwndDlg, text, TranslateT("Skin information"), MB_OK | MB_ICONINFORMATION);
+ }
+ break;
+
+ case IDC_BUTTON_APPLY_SKIN:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
+ if (hti == nullptr)
+ return 0;
+
+ TVITEM tvi = { 0 };
+ tvi.hItem = hti;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
+ SkinListData *sd = (SkinListData*)(tvi.lParam);
+ if (!sd)
+ return 0;
+
+ ske_LoadSkinFromIniFile(sd->File, FALSE);
+ ske_LoadSkinFromDB();
+ Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+ ske_RedrawCompleteWindow();
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+
+ RECT rc = {};
+ GetWindowRect(g_clistApi.hwndContactList, &rc);
+ Sync(CLUIFrames_OnMoving, g_clistApi.hwndContactList, &rc);
+
+ if (g_hCLUIOptionsWnd) {
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT));
+ }
+ }
+ break;
+
+ case IDC_GETSKINS:
+ if (HIWORD(wParam) == BN_CLICKED)
+ Utils_OpenUrl("https://miranda-ng.org/tags/modern-contact-list/");
+ break;
+
+
+ case IDC_BUTTON_RESCAN:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ HTREEITEM it = FillAvailableSkinList(hwndDlg);
+ HWND wnd = GetDlgItem(hwndDlg, IDC_TREE1);
+ TreeView_SelectItem(wnd, it);
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ if (wParam == IDC_PREVIEW) {
+ // TODO:Draw hPreviewBitmap here
+ HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam;
+ int mWidth = dis->rcItem.right - dis->rcItem.left;
+ int mHeight = dis->rcItem.bottom - dis->rcItem.top;
+ HDC memDC = CreateCompatibleDC(dis->hDC);
+ HBITMAP hbmp = ske_CreateDIB32(mWidth, mHeight);
+ HBITMAP holdbmp = (HBITMAP)SelectObject(memDC, hbmp);
+ RECT workRect = dis->rcItem;
+ OffsetRect(&workRect, -workRect.left, -workRect.top);
+ FillRect(memDC, &workRect, hbr);
+ DeleteObject(hbr);
+ if (hPreviewBitmap) {
+ // variables
+ BITMAP bmp = {};
+ GetObject(hPreviewBitmap, sizeof(bmp), &bmp);
+
+ // GetSize
+ float xScale = 1, yScale = 1;
+ int wWidth = workRect.right - workRect.left;
+ int wHeight = workRect.bottom - workRect.top;
+ if (wWidth < bmp.bmWidth)
+ xScale = (float)wWidth / bmp.bmWidth;
+ if (wHeight < bmp.bmHeight)
+ yScale = (float)wHeight / bmp.bmHeight;
+ xScale = min(xScale, yScale);
+ yScale = xScale;
+ int dWidth = (int)(xScale*bmp.bmWidth);
+ int dHeight = (int)(yScale*bmp.bmHeight);
+
+ // CalcPosition
+ POINT imgPos = { 0 };
+ imgPos.x = workRect.left + ((wWidth - dWidth) >> 1);
+ imgPos.y = workRect.top + ((wHeight - dHeight) >> 1);
+
+ // DrawImage
+ DrawAvatarImageWithGDIp(memDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255);
+ }
+ BitBlt(dis->hDC, dis->rcItem.left, dis->rcItem.top, mWidth, mHeight, memDC, 0, 0, SRCCOPY);
+ SelectObject(memDC, holdbmp);
+ DeleteObject(hbmp);
+ DeleteDC(memDC);
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY) {
+ Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
+ NotifyEventHooks(g_CluiData.hEventBkgrChanged, 0, 0);
+ Clist_Broadcast(INTM_INVALIDATE, 0, 0);
+ RedrawWindow(GetParent(g_clistApi.hwndContactTree), nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
+ }
+ break;
+
+ case IDC_TREE1:
+ NMTREEVIEW *nmtv = (NMTREEVIEW*)lParam;
+ if (nmtv == nullptr)
+ return 0;
+
+ if (nmtv->hdr.code == TVN_SELCHANGED) {
+ if (hPreviewBitmap) {
+ ske_UnloadGlyphImage(hPreviewBitmap);
+ hPreviewBitmap = nullptr;
+ }
+
+ if (nmtv->itemNew.lParam) {
+ SkinListData *sd = (SkinListData*)nmtv->itemNew.lParam;
+
+ wchar_t buf[MAX_PATH];
+ PathToRelativeW(sd->File, buf);
+ SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, buf);
+
+ wchar_t prfn[MAX_PATH] = { 0 }, imfn[MAX_PATH] = { 0 }, skinfolder[MAX_PATH] = { 0 };
+ GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), sd->File);
+ IniParser::GetSkinFolder(sd->File, skinfolder);
+ mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn);
+ PathToAbsoluteW(prfn, imfn);
+ hPreviewBitmap = ske_LoadGlyphImage(imfn);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), TRUE);
+ if (hPreviewBitmap)
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEW), nullptr, TRUE);
+ else { // prepare text
+ HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
+ if (hti == nullptr)
+ return 0;
+
+ TVITEM tvi = { 0 };
+ tvi.hItem = hti;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
+ SkinListData *sd2 = (SkinListData*)(tvi.lParam);
+ if (!sd2)
+ return 0;
+
+ wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000];
+ if (!wcschr(sd2->File, '%')) {
+ GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd2->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd2->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd2->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd2->File);
+ mir_snwprintf(text, TranslateT("Preview is not available\n\n%s\n----------------------\n\n%s\n\nAUTHOR(S):\n%s\n\nCONTACT:\n%s\n\nHOMEPAGE:\n%s"),
+ sd2->Name, Description, Author, Contact, URL);
+ }
+ else {
+ mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAUTHORS:\n%s\n\nCONTACT:\n%s\n\nWEB:\n%s\n\n\n"),
+ TranslateT("reVista for Modern v0.5"),
+ TranslateT("This is second default Modern Contact list skin in Vista Aero style"),
+ TranslateT("graphics by Angeli-Ka\ntemplate by FYR"),
+ L"JID: fyr@jabber.ru",
+ L"fyr.mirandaim.ru");
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_INFO), SW_SHOW);
+ SetDlgItemText(hwndDlg, IDC_STATIC_INFO, text);
+ }
+ }
+ else {
+ // no selected
+ SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, TranslateT("Select skin from list"));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), FALSE);
+ SetDlgItemText(hwndDlg, IDC_STATIC_INFO, TranslateT("Please select skin to apply"));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), hPreviewBitmap ? SW_SHOW : SW_HIDE);
+ return 0;
+ }
+ else if (nmtv->hdr.code == TVN_DELETEITEM) {
+ mir_free_and_nil(nmtv->itemOld.lParam);
+ return 0;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+int SearchSkinFiles(HWND hwndDlg, wchar_t *Folder)
+{
+ wchar_t mask[MAX_PATH];
+ mir_snwprintf(mask, L"%s\\*.msf", Folder);
+
+ struct _wfinddata_t fd = {};
+ intptr_t hFile = _wfindfirst(mask, &fd);
+ if (hFile != -1) {
+ do {
+ AddSkinToList(hwndDlg, Folder, fd.name);
+ } while (!_wfindnext(hFile, &fd));
+ _findclose(hFile);
+ }
+
+ mir_snwprintf(mask, L"%s\\*", Folder);
+ hFile = _wfindfirst(mask, &fd);
+
+ do {
+ if (fd.attrib & _A_SUBDIR && !(mir_wstrcmpi(fd.name, L".") == 0 || mir_wstrcmpi(fd.name, L"..") == 0)) { //Next level of subfolders
+ wchar_t path[MAX_PATH];
+ mir_snwprintf(path, L"%s\\%s", Folder, fd.name);
+ SearchSkinFiles(hwndDlg, path);
+ }
+ } while (!_wfindnext(hFile, &fd));
+
+ _findclose(hFile);
+ return 0;
+}
+
+HTREEITEM FillAvailableSkinList(HWND hwndDlg)
+{
+ TreeView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_TREE1));
+ AddSkinToList(hwndDlg, TranslateT("Default Skin"), L"%Default Skin%");
+ int attrib = GetFileAttributes(SkinsFolder);
+ if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY))
+ SearchSkinFiles(hwndDlg, SkinsFolder);
+
+ HTREEITEM res = (HTREEITEM)-1;
+ wchar_t skinfull[MAX_PATH];
+ ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile"));
+ if (skinfile) {
+ PathToAbsoluteW(skinfile, skinfull);
+ res = AddSkinToListFullName(hwndDlg, skinfull);
+ }
+
+ return res;
+}
+
+HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName)
+{
+ wchar_t path[MAX_PATH] = {}, file[MAX_PATH] = {};
+ mir_wstrncpy(path, fullName, _countof(path));
+
+ wchar_t *buf = path + mir_wstrlen(path);
+ while (buf > path) {
+ if (*buf == '\\') {
+ *buf = '\0';
+ break;
+ }
+ buf--;
+ }
+ buf++;
+ mir_wstrncpy(file, buf, _countof(file));
+ return AddSkinToList(hwndDlg, path, file);
+}
+
+HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file)
+{
+ wchar_t fullName[MAX_PATH], defskinname[MAX_PATH];
+ SkinListData *sd = (SkinListData*)mir_alloc(sizeof(SkinListData));
+ if (!sd)
+ return nullptr;
+
+ if (!file || wcschr(file, '%')) {
+ mir_snwprintf(sd->File, L"%%Default Skin%%");
+ mir_snwprintf(sd->Name, TranslateT("%Default Skin%"));
+ wcsncpy_s(fullName, TranslateT("Default Skin"), _TRUNCATE);
+ }
+ else {
+ mir_snwprintf(fullName, L"%s\\%s", path, file);
+ wcsncpy_s(defskinname, file, _TRUNCATE);
+ wchar_t *p = wcsrchr(defskinname, '.'); if (p) *p = 0;
+ GetPrivateProfileString(L"Skin_Description_Section", L"Name", defskinname, sd->Name, _countof(sd->Name), fullName);
+ wcsncpy_s(sd->File, fullName, _TRUNCATE);
+ }
+ return AddItemToTree(GetDlgItem(hwndDlg, IDC_TREE1), sd->Name, sd);
+}
+
+HTREEITEM FindChild(HWND hTree, HTREEITEM Parent, wchar_t *Caption, void *data)
+{
+ HTREEITEM tmp = nullptr;
+ if (Parent)
+ tmp = TreeView_GetChild(hTree, Parent);
+ else
+ tmp = TreeView_GetRoot(hTree);
+
+ while (tmp) {
+ TVITEM tvi;
+ wchar_t buf[255];
+ tvi.hItem = tmp;
+ tvi.mask = TVIF_TEXT | TVIF_HANDLE;
+ tvi.pszText = buf;
+ tvi.cchTextMax = _countof(buf);
+ TreeView_GetItem(hTree, &tvi);
+ if (mir_wstrcmpi(Caption, tvi.pszText) == 0) {
+ if (!data)
+ return tmp;
+
+ TVITEM tvi2 = { 0 };
+ tvi2.hItem = tmp;
+ tvi2.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(hTree, &tvi2);
+ SkinListData *sd = (SkinListData*)tvi2.lParam;
+ if (sd)
+ if (!mir_wstrcmpi(sd->File, ((SkinListData*)data)->File))
+ return tmp;
+ }
+ tmp = TreeView_GetNextSibling(hTree, tmp);
+ }
+ return tmp;
+}
+
+HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data)
+{
+ HTREEITEM cItem = nullptr;
+ // Insert item node
+ cItem = FindChild(hTree, nullptr, itemName, data);
+ if (!cItem) {
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_PARAM;
+ tvis.item.pszText = itemName;
+ tvis.item.lParam = (LPARAM)data;
+ return TreeView_InsertItem(hTree, &tvis);
+ }
+
+ mir_free(data); // need to free otherwise memory leak
+ return cItem;
+}
+
+INT_PTR SvcActiveSkin(WPARAM, LPARAM)
+{
+ ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile"));
+ if (skinfile) {
+ wchar_t skinfull[MAX_PATH];
+ PathToAbsoluteW(skinfile, skinfull);
+ return (INT_PTR)mir_wstrdup(skinfull);
+ }
+
+ return 0;
+}
+
+INT_PTR SvcApplySkin(WPARAM, LPARAM lParam)
+{
+ ske_LoadSkinFromIniFile((wchar_t*)lParam, FALSE);
+ ske_LoadSkinFromDB();
+ Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+ ske_RedrawCompleteWindow();
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+
+ HWND hwnd = g_clistApi.hwndContactList;
+ RECT rc = { 0 };
+ GetWindowRect(hwnd, &rc);
+ Sync(CLUIFrames_OnMoving, hwnd, &rc);
+
+ g_bChangingMode = TRUE;
+ CLUI_UpdateLayeredMode();
+ CLUI_ChangeWindowMode();
+ SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
+ CLUI_ReloadCLUIOptions();
+ cliShowHide(true);
+ g_bChangingMode = FALSE;
+
+ if (g_hCLUIOptionsWnd) {
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT));
+ }
+ return 0;
+}
+
+INT_PTR SvcPreviewSkin(WPARAM wParam, LPARAM lParam)
+{
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)wParam;
+ RECT workRect = dis->rcItem;
+ OffsetRect(&workRect, -workRect.left, -workRect.top);
+
+ if (lParam) {
+ wchar_t prfn[MAX_PATH] = { 0 };
+ wchar_t imfn[MAX_PATH] = { 0 };
+ wchar_t skinfolder[MAX_PATH] = { 0 };
+ GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), (LPCTSTR)lParam);
+ IniParser::GetSkinFolder((LPCTSTR)lParam, skinfolder);
+ mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn);
+ PathToAbsoluteW(prfn, imfn);
+
+ hPreviewBitmap = ske_LoadGlyphImage(imfn);
+ if (hPreviewBitmap) {
+ // variables
+ BITMAP bmp = { 0 };
+ GetObject(hPreviewBitmap, sizeof(BITMAP), &bmp);
+
+ // GetSize
+ float xScale = 1, yScale = 1;
+ int wWidth = workRect.right - workRect.left;
+ int wHeight = workRect.bottom - workRect.top;
+ if (wWidth < bmp.bmWidth)
+ xScale = (float)wWidth / bmp.bmWidth;
+ if (wHeight < bmp.bmHeight)
+ yScale = (float)wHeight / bmp.bmHeight;
+ xScale = min(xScale, yScale);
+ yScale = xScale;
+ int dWidth = (int)(xScale*bmp.bmWidth);
+ int dHeight = (int)(yScale*bmp.bmHeight);
+
+ // CalcPosition
+ POINT imgPos = { 0 };
+ imgPos.x = workRect.left + ((wWidth - dWidth) >> 1);
+ imgPos.y = workRect.top + ((wHeight - dHeight) >> 1);
+
+ // DrawImage
+ DrawAvatarImageWithGDIp(dis->hDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255);
+ ske_UnloadGlyphImage(hPreviewBitmap);
+ }
+ }
+
+ return 0;
+}
diff --git a/plugins/Clist_modern/src/modern_skinselector.cpp b/plugins/Clist_modern/src/modern_skinselector.cpp index e1a5a0633c..d68177c48b 100644 --- a/plugins/Clist_modern/src/modern_skinselector.cpp +++ b/plugins/Clist_modern/src/modern_skinselector.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_skinselector.h b/plugins/Clist_modern/src/modern_skinselector.h index a5e4ed6f94..c4ea040f4d 100644 --- a/plugins/Clist_modern/src/modern_skinselector.h +++ b/plugins/Clist_modern/src/modern_skinselector.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_static_clui.h b/plugins/Clist_modern/src/modern_static_clui.h index a13723f8c0..b416c3f001 100644 --- a/plugins/Clist_modern/src/modern_static_clui.h +++ b/plugins/Clist_modern/src/modern_static_clui.h @@ -1,114 +1,114 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-08 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -/************************************************************************/ -/** FILE CONTAINS HEADER PART FOR .../modernb/clui.c file **/ -/** **/ -/** !!! DO NOT INCLUDE IN TO OTHER FILES !!! **/ -/************************************************************************/ - -/* Definitions */ -#pragma once - -#define TM_AUTOALPHA 1 -#define TM_DELAYEDSIZING 2 -#define TM_BRINGOUTTIMEOUT 3 -#define TM_BRINGINTIMEOUT 4 -#define TM_UPDATEBRINGTIMER 5 -#define TM_SMOTHALPHATRANSITION 20 -#define TM_WINDOWUPDATE 100 -#define TM_STATUSBARUPDATE 200 - -#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu" -#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu" - -#define AC_SRC_NO_PREMULT_ALPHA 0x01 -#define AC_SRC_NO_ALPHA 0x02 -#define AC_DST_NO_PREMULT_ALPHA 0x10 -#define AC_DST_NO_ALPHA 0x20 - -#define ANIMATION_STEP 40 - -#define AEROGLASS_MINALPHA 24 - -/* Declaration of prototypes in other modules */ - -int ClcEnterDragToScroll(HWND hwnd, int Y); - -int CListMod_ContactListShutdownProc(WPARAM wParam, LPARAM lParam); -int CListMod_HideWindow(HWND hwndContactList, int mode); - -int CLUIServices_LoadModule(void); -INT_PTR CLUIServices_SortList(WPARAM wParam, LPARAM lParam); - -void Docking_GetMonitorRectFromWindow(HWND hWnd, RECT *rc); - -int EventArea_Create(HWND hCluiWnd); - -int ExtraImage_ExtraIDToColumnNum(int extra); - -int ModernSkinButtonLoadModule(); -int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *r); - -void ske_ApplyTranslucency(); -HBITMAP ske_CreateDIB32(int cx, int cy); -HBITMAP ske_CreateDIB32Point(int cx, int cy, void ** bits); -int ske_JustUpdateWindowImage(); -void ske_LoadSkinFromDB(void); -int ske_RedrawCompleteWindow(); -int ske_UpdateWindowImage(); -int ske_ValidateFrameImageProc(RECT *r); - -HWND StatusBar_Create(HWND parent); - -int UnhookAll(); - -/* Module function prototypes */ - -int CLUI_IsInMainWindow(HWND hwnd); -int CLUI_SizingOnBorder(POINT pt, int size); -int CLUI_SmoothAlphaTransition(HWND hwnd, uint8_t GoalAlpha, BOOL wParam); -int CLUI_TestCursorOnBorders(); - -static int CLUI_SmoothAlphaThreadTransition(); - -/* structs */ - -struct CHECKFILLING -{ - HDC hDC; - RECT rcRect; -}; - -int CheckFramesPos(RECT *wr); //cluiframes.c -int CLUIFrames_ApplyNewSizes(int mode); //cluiframes.c -int CLUIFrames_GetTotalHeight(); //cluiframes.c -int CLUIFrames_RepaintSubContainers(); //cluiframes.c -int CLUIFramesGetMinHeight(); //cluiframes.c - -int SizeFramesByWindowRect(RECT *r, HDWP * PosBatch, int mode); //cluiframes.c - -int InitSkinHotKeys(); -BOOL amWakeThread(); -void CreateViewModeFrame(); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-08 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/************************************************************************/
+/** FILE CONTAINS HEADER PART FOR .../modernb/clui.c file **/
+/** **/
+/** !!! DO NOT INCLUDE IN TO OTHER FILES !!! **/
+/************************************************************************/
+
+/* Definitions */
+#pragma once
+
+#define TM_AUTOALPHA 1
+#define TM_DELAYEDSIZING 2
+#define TM_BRINGOUTTIMEOUT 3
+#define TM_BRINGINTIMEOUT 4
+#define TM_UPDATEBRINGTIMER 5
+#define TM_SMOTHALPHATRANSITION 20
+#define TM_WINDOWUPDATE 100
+#define TM_STATUSBARUPDATE 200
+
+#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu"
+#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu"
+
+#define AC_SRC_NO_PREMULT_ALPHA 0x01
+#define AC_SRC_NO_ALPHA 0x02
+#define AC_DST_NO_PREMULT_ALPHA 0x10
+#define AC_DST_NO_ALPHA 0x20
+
+#define ANIMATION_STEP 40
+
+#define AEROGLASS_MINALPHA 24
+
+/* Declaration of prototypes in other modules */
+
+int ClcEnterDragToScroll(HWND hwnd, int Y);
+
+int CListMod_ContactListShutdownProc(WPARAM wParam, LPARAM lParam);
+int CListMod_HideWindow(HWND hwndContactList, int mode);
+
+int CLUIServices_LoadModule(void);
+INT_PTR CLUIServices_SortList(WPARAM wParam, LPARAM lParam);
+
+void Docking_GetMonitorRectFromWindow(HWND hWnd, RECT *rc);
+
+int EventArea_Create(HWND hCluiWnd);
+
+int ExtraImage_ExtraIDToColumnNum(int extra);
+
+int ModernSkinButtonLoadModule();
+int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *r);
+
+void ske_ApplyTranslucency();
+HBITMAP ske_CreateDIB32(int cx, int cy);
+HBITMAP ske_CreateDIB32Point(int cx, int cy, void ** bits);
+int ske_JustUpdateWindowImage();
+void ske_LoadSkinFromDB(void);
+int ske_RedrawCompleteWindow();
+int ske_UpdateWindowImage();
+int ske_ValidateFrameImageProc(RECT *r);
+
+HWND StatusBar_Create(HWND parent);
+
+int UnhookAll();
+
+/* Module function prototypes */
+
+int CLUI_IsInMainWindow(HWND hwnd);
+int CLUI_SizingOnBorder(POINT pt, int size);
+int CLUI_SmoothAlphaTransition(HWND hwnd, uint8_t GoalAlpha, BOOL wParam);
+int CLUI_TestCursorOnBorders();
+
+static int CLUI_SmoothAlphaThreadTransition();
+
+/* structs */
+
+struct CHECKFILLING
+{
+ HDC hDC;
+ RECT rcRect;
+};
+
+int CheckFramesPos(RECT *wr); //cluiframes.c
+int CLUIFrames_ApplyNewSizes(int mode); //cluiframes.c
+int CLUIFrames_GetTotalHeight(); //cluiframes.c
+int CLUIFrames_RepaintSubContainers(); //cluiframes.c
+int CLUIFramesGetMinHeight(); //cluiframes.c
+
+int SizeFramesByWindowRect(RECT *r, HDWP * PosBatch, int mode); //cluiframes.c
+
+int InitSkinHotKeys();
+BOOL amWakeThread();
+void CreateViewModeFrame();
diff --git a/plugins/Clist_modern/src/modern_statusbar_options.cpp b/plugins/Clist_modern/src/modern_statusbar_options.cpp index 1df676fafe..953920171e 100644 --- a/plugins/Clist_modern/src/modern_statusbar_options.cpp +++ b/plugins/Clist_modern/src/modern_statusbar_options.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_toolbar.cpp b/plugins/Clist_modern/src/modern_toolbar.cpp index c6c7844c38..564cb31117 100644 --- a/plugins/Clist_modern/src/modern_toolbar.cpp +++ b/plugins/Clist_modern/src/modern_toolbar.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
Copyright 2007 Artem Shpynov
diff --git a/plugins/Clist_modern/src/modern_viewmodebar.cpp b/plugins/Clist_modern/src/modern_viewmodebar.cpp index 5507206936..6e687e9407 100644 --- a/plugins/Clist_modern/src/modern_viewmodebar.cpp +++ b/plugins/Clist_modern/src/modern_viewmodebar.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/stdafx.h b/plugins/Clist_modern/src/stdafx.h index 814ee05d0b..683384d396 100644 --- a/plugins/Clist_modern/src/stdafx.h +++ b/plugins/Clist_modern/src/stdafx.h @@ -5,7 +5,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/Docking.cpp b/plugins/Clist_nicer/src/Docking.cpp index 860c42e17a..47b9ffc90b 100644 --- a/plugins/Clist_nicer/src/Docking.cpp +++ b/plugins/Clist_nicer/src/Docking.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/alphablend.cpp b/plugins/Clist_nicer/src/alphablend.cpp index d9bf9c14b8..eb369f4a93 100644 --- a/plugins/Clist_nicer/src/alphablend.cpp +++ b/plugins/Clist_nicer/src/alphablend.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/alphablend.h b/plugins/Clist_nicer/src/alphablend.h index 8298faf75c..1f30c04a71 100644 --- a/plugins/Clist_nicer/src/alphablend.h +++ b/plugins/Clist_nicer/src/alphablend.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clc.cpp b/plugins/Clist_nicer/src/clc.cpp index dc43f20aeb..4924dccd16 100644 --- a/plugins/Clist_nicer/src/clc.cpp +++ b/plugins/Clist_nicer/src/clc.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clc.h b/plugins/Clist_nicer/src/clc.h index 5913377c6c..ba06b835be 100644 --- a/plugins/Clist_nicer/src/clc.h +++ b/plugins/Clist_nicer/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcitems.cpp b/plugins/Clist_nicer/src/clcitems.cpp index 80900a0150..7682c77ca5 100644 --- a/plugins/Clist_nicer/src/clcitems.cpp +++ b/plugins/Clist_nicer/src/clcitems.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcmsgs.cpp b/plugins/Clist_nicer/src/clcmsgs.cpp index 6bf599b703..fbd9dd0430 100644 --- a/plugins/Clist_nicer/src/clcmsgs.cpp +++ b/plugins/Clist_nicer/src/clcmsgs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcopts.cpp b/plugins/Clist_nicer/src/clcopts.cpp index 4b419fd134..cc3c0c5923 100644 --- a/plugins/Clist_nicer/src/clcopts.cpp +++ b/plugins/Clist_nicer/src/clcopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcpaint.cpp b/plugins/Clist_nicer/src/clcpaint.cpp index a9e425ed08..e7f49f900c 100644 --- a/plugins/Clist_nicer/src/clcpaint.cpp +++ b/plugins/Clist_nicer/src/clcpaint.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcutils.cpp b/plugins/Clist_nicer/src/clcutils.cpp index c27ffec502..5be75febe9 100644 --- a/plugins/Clist_nicer/src/clcutils.cpp +++ b/plugins/Clist_nicer/src/clcutils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clist.h b/plugins/Clist_nicer/src/clist.h index 2c14e865ce..be2c18f62e 100644 --- a/plugins/Clist_nicer/src/clist.h +++ b/plugins/Clist_nicer/src/clist.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistevents.cpp b/plugins/Clist_nicer/src/clistevents.cpp index c2eeadf296..134738fcff 100644 --- a/plugins/Clist_nicer/src/clistevents.cpp +++ b/plugins/Clist_nicer/src/clistevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistmenus.cpp b/plugins/Clist_nicer/src/clistmenus.cpp index f018828fef..4ee15c4fe5 100644 --- a/plugins/Clist_nicer/src/clistmenus.cpp +++ b/plugins/Clist_nicer/src/clistmenus.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistmod.cpp b/plugins/Clist_nicer/src/clistmod.cpp index 5943c891c0..44db56f4e1 100644 --- a/plugins/Clist_nicer/src/clistmod.cpp +++ b/plugins/Clist_nicer/src/clistmod.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-10 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistopts.cpp b/plugins/Clist_nicer/src/clistopts.cpp index 8869c4a965..cf2701715e 100644 --- a/plugins/Clist_nicer/src/clistopts.cpp +++ b/plugins/Clist_nicer/src/clistopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistsettings.cpp b/plugins/Clist_nicer/src/clistsettings.cpp index ff20975394..773c070a71 100644 --- a/plugins/Clist_nicer/src/clistsettings.cpp +++ b/plugins/Clist_nicer/src/clistsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clisttray.cpp b/plugins/Clist_nicer/src/clisttray.cpp index bdfe001037..c3e7def30b 100644 --- a/plugins/Clist_nicer/src/clisttray.cpp +++ b/plugins/Clist_nicer/src/clisttray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clui.cpp b/plugins/Clist_nicer/src/clui.cpp index f81267c173..d7854a4197 100644 --- a/plugins/Clist_nicer/src/clui.cpp +++ b/plugins/Clist_nicer/src/clui.cpp @@ -1,1934 +1,1934 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-03 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include <m_findadd.h> -#include "cluiframes.h" -#include "coolscroll.h" - -#define TM_AUTOALPHA 1 -#define TIMERID_AUTOSIZE 100 -#define MENU_MIRANDAMENU 0xFFFF1234 - -int g_fading_active = 0; - -static RECT g_PreSizeRect, g_SizingRect; -static int g_sizingmethod; -static LONG g_CLUI_x_off, g_CLUI_y_off, g_CLUI_y1_off, g_CLUI_x1_off; -static RECT rcWPC; - -static int transparentFocus = 1; -static byte oldhideoffline; -static int disableautoupd = 1; -static int hFrameContactTree; -extern RECT old_window_rect, new_window_rect; - -extern BOOL g_trayTooltipActive; -extern POINT tray_hover_pos; -extern HWND g_hwndViewModeFrame, g_hwndEventArea, g_hwndToolbarFrame; - -extern ImageItem *g_CLUIImageItem; -extern HBRUSH g_CLUISkinnedBkColor; -extern HWND g_hwndSFL; -extern ButtonItem *g_ButtonItems; -extern COLORREF g_CLUISkinnedBkColorRGB; -extern FRAMEWND *wndFrameCLC; -extern HPEN g_hPenCLUIFrames; - -static uint8_t old_cliststate, show_on_first_autosize = FALSE; - -RECT cluiPos; - -wchar_t *statusNames[12]; - -extern LRESULT CALLBACK EventAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); -extern int hNotifyFrame; - -void MF_InitCheck(void); -void InitGroupMenus(); -void FS_RegisterFonts(); -void LoadExtraIconModule(); -void RemoveFromTaskBar(HWND hWnd); - -extern LONG g_cxsmIcon, g_cysmIcon; - -SIZE g_oldSize = { 0 }; -POINT g_oldPos = { 0 }; -int during_sizing = 0; -extern int dock_prevent_moving; - -static HDC hdcLockedPoint = nullptr; -static HBITMAP hbmLockedPoint = nullptr, hbmOldLockedPoint = nullptr; - -HICON overlayicons[10]; - -static IconItem myIcons[] = { - { LPGEN("Toggle show online/offline"), "CLN_online", IDI_HIDEOFFLINE }, - { LPGEN("Toggle groups"), "CLN_groups", IDI_HIDEGROUPS }, - { LPGEN("Find contacts"), "CLN_findadd", IDI_FINDANDADD }, - { LPGEN("Open preferences"), "CLN_options", IDI_TBOPTIONS }, - { LPGEN("Toggle sounds"), "CLN_sound", IDI_SOUNDSON }, - { LPGEN("Minimize contact list"), "CLN_minimize", IDI_MINIMIZE }, - { LPGEN("Show TabSRMM session list"), "CLN_slist", IDI_TABSRMMSESSIONLIST }, - { LPGEN("Show TabSRMM menu"), "CLN_menu", IDI_TABSRMMMENU }, - { LPGEN("Sounds are off"), "CLN_soundsoff", IDI_SOUNDSOFF }, - { LPGEN("Select view mode"), "CLN_CLVM_select", IDI_CLVM_SELECT }, - { LPGEN("Reset view mode"), "CLN_CLVM_reset", IDI_DELETE }, - { LPGEN("Configure view modes"), "CLN_CLVM_options", IDI_CLVM_OPTIONS }, - { LPGEN("Show menu"), "CLN_topmenu", IDI_TBTOPMENU }, - { LPGEN("Setup accounts"), "CLN_accounts", IDI_TBACCOUNTS } -}; - -HWND hTbMenu, hTbGlobalStatus; - -static void Tweak_It(COLORREF clr) -{ - SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED); - SetLayeredWindowAttributes(g_clistApi.hwndContactList, clr, 0, LWA_COLORKEY); - cfg::dat.colorkey = clr; -} - -static void LayoutButtons(HWND hwnd, RECT *rc) -{ - RECT rect; - uint8_t left_offset = cfg::dat.bCLeft - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0); - uint8_t right_offset = cfg::dat.bCRight - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0); - uint8_t delta = left_offset + right_offset; - ButtonItem *btnItems = g_ButtonItems; - - if (rc == nullptr) - GetClientRect(hwnd, &rect); - else - rect = *rc; - - rect.bottom -= cfg::dat.bCBottom; - - if (g_ButtonItems) { - while (btnItems) { - LONG x = (btnItems->xOff >= 0) ? rect.left + btnItems->xOff : rect.right - abs(btnItems->xOff); - LONG y = (btnItems->yOff >= 0) ? rect.top + btnItems->yOff : rect.bottom - cfg::dat.statusBarHeight; - - SetWindowPos(btnItems->hWnd, nullptr, x, y, btnItems->width, btnItems->height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); - btnItems = btnItems->nextItem; - } - } - - SetWindowPos(hTbMenu, nullptr, 2 + left_offset, rect.bottom - cfg::dat.statusBarHeight - 21 - 1, - 21 * 3, 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); - - SetWindowPos(hTbGlobalStatus, nullptr, left_offset + (3 * 21) + 3, rect.bottom - cfg::dat.statusBarHeight - 21 - 1, - rect.right - delta - (3 * 21 + 5), 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); - -} - -static int FS_FontsChanged(WPARAM, LPARAM) -{ - COLORREF clr_cluiframes = db_get_dw(0, "CLUI", "clr_frameborder", RGB(40, 40, 40)); - - if (g_hPenCLUIFrames) - DeleteObject(g_hPenCLUIFrames); - g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, clr_cluiframes); - - Clist_ClcOptionsChanged(); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; -} - -// create the CLC control, but not yet the frame. The frame containing the CLC should be created as the -// last frame of all. -static HWND PreCreateCLC(HWND parent) -{ - g_clistApi.hwndContactTree = CreateWindow(CLISTCONTROL_CLASSW, L"", - WS_CHILD | CLS_CONTACTLIST | (Clist::UseGroups ? CLS_USEGROUPS : 0) | (Clist::HideOffline ? CLS_HIDEOFFLINE : 0) | (Clist::HideEmptyGroups ? CLS_HIDEEMPTYGROUPS : 0) | CLS_MULTICOLUMN, - 0, 0, 0, 0, parent, nullptr, g_plugin.getInst(), (LPVOID)0xff00ff00); - - cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0); - return g_clistApi.hwndContactTree; -} - -// create internal frames, including the last frame (actual CLC control) -static int CreateCLC() -{ - ExtraIcon_Reload(); - g_clistApi.pfnSetHideOffline(oldhideoffline); - disableautoupd = 0; - { - CLISTFrame frame = { 0 }; - frame.cbSize = sizeof(frame); - frame.szName.a = "EventArea"; - frame.szTBname.a = LPGEN("Event area"); - frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME); - frame.height = 20; - frame.Flags = F_VISIBLE | F_SHOWTBTIP | F_NOBORDER; - frame.align = alBottom; - frame.hWnd = CreateWindowExA(0, "EventAreaClass", "evt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, g_clistApi.hwndContactList, (HMENU)nullptr, g_plugin.getInst(), nullptr); - g_hwndEventArea = frame.hWnd; - hNotifyFrame = g_plugin.addFrame(&frame); - CallService(MS_CLIST_FRAMES_UPDATEFRAME, hNotifyFrame, FU_FMPOS); - HideShowNotifyFrame(); - CreateViewModeFrame(); - } - { - CLISTFrame Frame = { 0 }; - Frame.cbSize = sizeof(CLISTFrame); - Frame.hWnd = g_clistApi.hwndContactTree; - Frame.align = alClient; - Frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME); - Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER; - Frame.szName.a = "My contacts"; - Frame.szTBname.a = LPGEN("My contacts"); - Frame.height = 200; - hFrameContactTree = g_plugin.addFrame(&Frame); - CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_TBTIPNAME | FO_UNICODETEXT, hFrameContactTree), (LPARAM)TranslateT("My contacts")); - - // ugly, but working hack. Prevent that annoying little scroll bar from appearing in the "My Contacts" title bar - uint32_t flags = (uint32_t)CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), 0); - flags |= F_VISIBLE; - CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), flags); - } - - SetButtonToSkinned(); - return 0; -} - -static int CluiModulesLoaded(WPARAM, LPARAM) -{ - FS_RegisterFonts(); - HookEvent(ME_FONT_RELOAD, FS_FontsChanged); - return 0; -} - -static HICON hIconSaved = nullptr; - -void ClearIcons(int mode) -{ - for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) { - if (overlayicons[i - IDI_OVL_OFFLINE] != nullptr) { - if (mode) - DestroyIcon(overlayicons[i - IDI_OVL_OFFLINE]); - overlayicons[i - IDI_OVL_OFFLINE] = nullptr; - } - } -} - -static void CacheClientIcons() -{ - ClearIcons(0); - - for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) { - char szBuffer[128]; - mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE)); - overlayicons[i - IDI_OVL_OFFLINE] = IcoLib_GetIcon(szBuffer); - } -} - -static void InitIcoLib() -{ - g_plugin.registerIcon(LPGEN("Contact list") "/" LPGEN("Default"), myIcons); - - for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) { - char szBuffer[128]; - mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE)); - IconItemT icon[] = { { Clist_GetStatusModeDescription(ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE), 0), szBuffer, i } }; - g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Overlay icons"), icon); - } - - for (auto &pa : Accounts()) { - if (!pa->IsEnabled() || CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0) - continue; - - wchar_t szDescr[128]; - mir_snwprintf(szDescr, TranslateT("%s connecting"), pa->tszAccountName); - IconItemT icon[] = { { szDescr, "conn", IDI_PROTOCONNECTING } }; - g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Connecting icons"), icon, pa->szModuleName); - } -} - -static int IcoLibChanged(WPARAM, LPARAM) -{ - IcoLibReloadIcons(); - return 0; -} - -void CreateButtonBar(HWND hWnd) -{ - hTbMenu = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBMENU, g_plugin.getInst(), nullptr); - CustomizeButton(hTbMenu, false, false, false); - SetWindowText(hTbMenu, TranslateT("Menu")); - SendMessage(hTbMenu, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_OTHER_MAINMENU)); - SendMessage(hTbMenu, BUTTONSETSENDONDOWN, TRUE, 0); - SendMessage(hTbMenu, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open main menu"), 0); - - hTbGlobalStatus = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBGLOBALSTATUS, g_plugin.getInst(), nullptr); - CustomizeButton(hTbGlobalStatus, false, false, false); - SetWindowText(hTbGlobalStatus, TranslateT("Offline")); - SendMessage(hTbGlobalStatus, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_STATUS_OFFLINE)); - SendMessage(hTbGlobalStatus, BUTTONSETSENDONDOWN, TRUE, 0); - SendMessage(hTbGlobalStatus, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Set status modes"), 0); -} - -// if mode != 0 we do first time init, otherwise only reload the extra icon stuff -void CLN_LoadAllIcons(BOOL mode) -{ - if (mode) { - InitIcoLib(); - HookEvent(ME_SKIN_ICONSCHANGED, IcoLibChanged); - } - CacheClientIcons(); -} - -void ConfigureEventArea() -{ - int iCount = GetMenuItemCount(cfg::dat.hMenuNotify); - uint32_t dwFlags = cfg::dat.dwFlags; - int oldstate = cfg::dat.notifyActive; - int dwVisible = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hNotifyFrame), 0) & F_VISIBLE; - - if (dwVisible) { - if (dwFlags & CLUI_FRAME_AUTOHIDENOTIFY) - cfg::dat.notifyActive = iCount > 0 ? 1 : 0; - else - cfg::dat.notifyActive = 1; - } - else - cfg::dat.notifyActive = 0; - - if (oldstate != cfg::dat.notifyActive) - HideShowNotifyFrame(); -} - -void ConfigureFrame() -{ - int show = cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? SW_SHOW : SW_HIDE; - ShowWindow(hTbMenu, show); - ShowWindow(hTbGlobalStatus, show); -} - -void IcoLibReloadIcons() -{ - CacheClientIcons(); - ExtraIcon_Reload(); - ExtraIcon_SetAll(); - - Clist_Broadcast(CLM_AUTOREBUILD, 0, 0); - SendMessage(g_hwndViewModeFrame, WM_USER + 100, 0, 0); -} - -void ConfigureCLUIGeometry(int mode) -{ - RECT rcStatus; - uint32_t clmargins = db_get_dw(0, "CLUI", "clmargins", 0); - - cfg::dat.bCLeft = LOBYTE(LOWORD(clmargins)); - cfg::dat.bCRight = HIBYTE(LOWORD(clmargins)); - cfg::dat.bCTop = LOBYTE(HIWORD(clmargins)); - cfg::dat.bCBottom = HIBYTE(HIWORD(clmargins)); - - if (mode) { - if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) { - SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0); - GetWindowRect(g_clistApi.hwndStatus, &rcStatus); - cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top); - } - else cfg::dat.statusBarHeight = 0; - } - - cfg::dat.topOffset = cfg::dat.bCTop; - cfg::dat.bottomOffset = (cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? 2 + 21 : 0) + cfg::dat.bCBottom; - - if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) { - cfg::dat.topOffset += 2; - cfg::dat.bottomOffset += 2; - cfg::dat.bCLeft += 3; - cfg::dat.bCRight += 3; - } -} - -// set the states of defined database action buttons (only if button is a toggle) -void SetDBButtonStates(MCONTACT hPassedContact) -{ - ButtonItem *buttonItem = g_ButtonItems; - MCONTACT hContact = 0, hFinalContact = 0; - char *szModule, *szSetting; - ClcContact *contact = nullptr; - - if (cfg::clcdat && hPassedContact == 0) { - g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr); - if (contact && contact->type == CLCIT_CONTACT) { - hContact = contact->hContact; - } - } - - while (buttonItem) { - BOOL result = FALSE; - - if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) { - buttonItem = buttonItem->nextItem; - continue; - } - szModule = buttonItem->szModule; - szSetting = buttonItem->szSetting; - if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) { - if (hContact == 0) { - SendMessage(buttonItem->hWnd, BM_SETCHECK, BST_UNCHECKED, 0); - buttonItem = buttonItem->nextItem; - continue; - } - if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) - szModule = Proto_GetBaseAccountName(hContact); - hFinalContact = hContact; - } - else - hFinalContact = 0; - - if (buttonItem->type == DBVT_ASCIIZ) { - DBVARIANT dbv = { 0 }; - - if (!db_get_s(hFinalContact, szModule, szSetting, &dbv)) { - result = !mir_strcmp((char *)buttonItem->bValuePush, dbv.pszVal); - db_free(&dbv); - } - } - else { - switch (buttonItem->type) { - case DBVT_BYTE: { - uint8_t val = db_get_b(hFinalContact, szModule, szSetting, 0); - result = (val == buttonItem->bValuePush[0]); - break; - } - case DBVT_WORD: { - uint16_t val = db_get_w(hFinalContact, szModule, szSetting, 0); - result = (val == *((uint16_t *)&buttonItem->bValuePush)); - break; - } - case DBVT_DWORD: - uint32_t val = db_get_dw(hFinalContact, szModule, szSetting, 0); - result = (val == *((uint32_t *)&buttonItem->bValuePush)); - break; - } - } - SendMessage(buttonItem->hWnd, BM_SETCHECK, (WPARAM)result, 0); - buttonItem = buttonItem->nextItem; - } -} - -// set states of standard buttons (pressed/unpressed) -void SetButtonStates() -{ - ButtonItem *buttonItem = g_ButtonItems; - - if (g_ButtonItems) { - while (buttonItem) { - if (buttonItem->dwFlags & BUTTON_ISINTERNAL) { - switch (buttonItem->uId) { - case IDC_STBSOUND: - SendMessage(buttonItem->hWnd, BM_SETCHECK, cfg::dat.soundsOff ? BST_CHECKED : BST_UNCHECKED, 0); - break; - case IDC_STBHIDEOFFLINE: - SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::HideOffline, 0); - break; - case IDC_STBHIDEGROUPS: - SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::UseGroups, 0); - break; - } - } - buttonItem = buttonItem->nextItem; - } - } -} - -void BlitWallpaper(HDC hdc, RECT *rc, struct ClcData *dat) -{ - int x, y; - int bitx, bity; - int maxx, maxy; - int destw, desth, height, width; - BITMAP *bmp = &cfg::dat.bminfoBg; - LONG clip = cfg::dat.bClipBorder; - - if (dat == nullptr) - return; - - SetStretchBltMode(hdc, HALFTONE); - - y = rc->top; - - rc->left = max(rc->left, clip); - rc->right = min(rc->right - clip, rc->right); - rc->top = max(rc->top, clip); - rc->bottom = min(rc->bottom - clip, rc->bottom); - - width = rc->right - rc->left; - height = rc->bottom - rc->top; - HRGN my_rgn = CreateRectRgn(rc->left, rc->top, rc->right, rc->bottom); - SelectClipRgn(hdc, my_rgn); - maxx = dat->backgroundBmpUse & CLBF_TILEH ? rc->right : rc->left + 1; - maxy = dat->backgroundBmpUse & CLBF_TILEV ? rc->bottom : y + 1; - switch (dat->backgroundBmpUse & CLBM_TYPE) { - case CLB_STRETCH: - if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) { - if (width * bmp->bmHeight < height * bmp->bmWidth) { - desth = height; - destw = desth * bmp->bmWidth / bmp->bmHeight; - } - else { - destw = width; - desth = destw * bmp->bmHeight / bmp->bmWidth; - } - } - else { - destw = width; - desth = height; - } - break; - case CLB_STRETCHH: - if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) { - destw = width; - desth = destw * bmp->bmHeight / bmp->bmWidth; - } - else { - destw = width; - desth = bmp->bmHeight; - } - break; - - case CLB_STRETCHV: - if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) { - desth = height; - destw = desth * bmp->bmWidth / bmp->bmHeight; - } - else { - destw = bmp->bmWidth; - desth = height; - } - break; - - default: - //clb_topleft - destw = bmp->bmWidth; - desth = bmp->bmHeight; - break; - } - - bitx = 0; - bity = 0; - for (; y < maxy; y += desth) { - for (x = rc->left; x < maxx; x += destw) - StretchBlt(hdc, x, y, destw, desth, cfg::dat.hdcPic, bitx, bity, bmp->bmWidth, bmp->bmHeight, SRCCOPY); - } - SelectClipRgn(hdc, nullptr); - DeleteObject(my_rgn); -} - -void ReloadThemedOptions() -{ - cfg::dat.bSkinnedStatusBar = db_get_b(0, "CLUI", "sb_skinned", 0); - cfg::dat.bUsePerProto = db_get_b(0, "CLCExt", "useperproto", 0); - cfg::dat.bOverridePerStatusColors = db_get_b(0, "CLCExt", "override_status", 0); - cfg::dat.bRowSpacing = db_get_b(0, "CLC", "RowGap", 0); - cfg::dat.bApplyIndentToBg = db_get_b(0, "CLCExt", "applyindentbg", 0); - cfg::dat.bWallpaperMode = db_get_b(0, "CLUI", "UseBkSkin", 1); - cfg::dat.bClipBorder = db_get_b(0, "CLUI", "clipborder", 0); - cfg::dat.cornerRadius = db_get_b(0, "CLCExt", "CornerRad", 6); - cfg::dat.gapBetweenFrames = (uint8_t)db_get_dw(0, "CLUIFrames", "GapBetweenFrames", 1); - cfg::dat.bUseDCMirroring = db_get_b(0, "CLC", "MirrorDC", 0); - cfg::dat.bGroupAlign = db_get_b(0, "CLC", "GroupAlign", 0); - if (cfg::dat.hBrushColorKey) - DeleteObject(cfg::dat.hBrushColorKey); - cfg::dat.hBrushColorKey = CreateSolidBrush(RGB(255, 0, 255)); - cfg::dat.bWantFastGradients = db_get_b(0, "CLCExt", "FastGradients", 0); - cfg::dat.titleBarHeight = db_get_b(0, "CLCExt", "frame_height", DEFAULT_TITLEBAR_HEIGHT); - cfg::dat.group_padding = db_get_dw(0, "CLCExt", "grp_padding", 0); -} - -static RECT rcWindow = { 0 }; - -static void sttProcessResize(HWND hwnd, NMCLISTCONTROL *nmc) -{ - RECT rcTree, rcWorkArea, rcOld; - int maxHeight, newHeight; - int winstyle, skinHeight = 0; - - if (disableautoupd) - return; - - if (!db_get_b(0, "CLUI", "AutoSize", 0)) - return; - - if (Docking_IsDocked(0, 0)) - return; - if (hFrameContactTree == 0) - return; - - maxHeight = db_get_b(0, "CLUI", "MaxSizeHeight", 75); - rcOld = rcWindow; - - GetWindowRect(hwnd, &rcWindow); - GetWindowRect(g_clistApi.hwndContactTree, &rcTree); - winstyle = GetWindowLongPtr(g_clistApi.hwndContactTree, GWL_STYLE); - - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE); - HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMon, &mi)) - rcWorkArea = mi.rcWork; - - if (nmc->pt.y > (rcWorkArea.bottom - rcWorkArea.top)) { - nmc->pt.y = (rcWorkArea.bottom - rcWorkArea.top); - } - - if (winstyle & CLS_SKINNEDFRAME) { - BOOL hasTitleBar = wndFrameCLC ? wndFrameCLC->TitleBar.ShowTitleBar : 0; - StatusItems_t *item = arStatusItems[(hasTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB : ID_EXTBKOWNEDFRAMEBORDER) - ID_STATUS_OFFLINE]; - skinHeight = item->IGNORED ? 0 : item->MARGIN_BOTTOM + item->MARGIN_TOP; - } - - newHeight = max(nmc->pt.y, 3) + 1 + ((winstyle & WS_BORDER) ? 2 : 0) + skinHeight + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top); - if (newHeight == (rcWindow.bottom - rcWindow.top) && show_on_first_autosize == FALSE) - return; - - if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100) - newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100; - if (db_get_b(0, "CLUI", "AutoSizeUpward", 0)) { - rcWindow.top = rcWindow.bottom - newHeight; - if (rcWindow.top < rcWorkArea.top) rcWindow.top = rcWorkArea.top; - } - else { - rcWindow.bottom = rcWindow.top + newHeight; - if (rcWindow.bottom > rcWorkArea.bottom) rcWindow.bottom = rcWorkArea.bottom; - } - if (cfg::dat.szOldCTreeSize.cx != rcTree.right - rcTree.left) { - cfg::dat.szOldCTreeSize.cx = rcTree.right - rcTree.left; - return; - } - KillTimer(hwnd, TIMERID_AUTOSIZE); - SetTimer(hwnd, TIMERID_AUTOSIZE, 100, nullptr); -} - -int CustomDrawScrollBars(NMCSBCUSTOMDRAW *nmcsbcd) -{ - switch (nmcsbcd->hdr.code) { - case NM_COOLSB_CUSTOMDRAW: - static HDC hdcScroll = nullptr; - static HBITMAP hbmScroll, hbmScrollOld; - static LONG scrollLeft, scrollRight, scrollHeight, scrollYmin, scrollYmax; - - switch (nmcsbcd->dwDrawStage) { - case CDDS_PREPAINT: - if (cfg::dat.bSkinnedScrollbar) // XXX fix (verify skin items to be complete, otherwise don't draw - return CDRF_SKIPDEFAULT; - return CDRF_DODEFAULT; - - case CDDS_POSTPAINT: - return 0; - - case CDDS_ITEMPREPAINT: - HDC hdc = nmcsbcd->hdc; - StatusItems_t *item = nullptr, *arrowItem = nullptr; - UINT uItemID = ID_EXTBKSCROLLBACK; - HRGN rgn = nullptr; - - RECT rc; - GetWindowRect(g_clistApi.hwndContactTree, &rc); - - POINT pt; - pt.x = rc.left; - pt.y = rc.top; - ScreenToClient(g_clistApi.hwndContactList, &pt); - hdcScroll = hdc; - BitBlt(hdcScroll, nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right - nmcsbcd->rect.left, - nmcsbcd->rect.bottom - nmcsbcd->rect.top, cfg::dat.hdcBg, pt.x + nmcsbcd->rect.left, pt.y + nmcsbcd->rect.top, SRCCOPY); - - switch (nmcsbcd->uItem) { - case HTSCROLL_UP: - case HTSCROLL_DOWN: - uItemID = (nmcsbcd->uState == CDIS_DEFAULT || nmcsbcd->uState == CDIS_DISABLED) ? ID_EXTBKSCROLLBUTTON : - (nmcsbcd->uState == CDIS_HOT ? ID_EXTBKSCROLLBUTTONHOVER : ID_EXTBKSCROLLBUTTONPRESSED); - break; - case HTSCROLL_PAGEGDOWN: - case HTSCROLL_PAGEGUP: - uItemID = nmcsbcd->uItem == HTSCROLL_PAGEGUP ? ID_EXTBKSCROLLBACK : ID_EXTBKSCROLLBACKLOWER; - rgn = CreateRectRgn(nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right, nmcsbcd->rect.bottom); - SelectClipRgn(hdcScroll, rgn); - break; - case HTSCROLL_THUMB: - uItemID = nmcsbcd->uState == CDIS_SELECTED ? ID_EXTBKSCROLLTHUMBPRESSED : ID_EXTBKSCROLLTHUMB; - break; - default: - break; - } - - uItemID -= ID_STATUS_OFFLINE; - item = arStatusItems[uItemID]; - if (!item->IGNORED) { - int alpha = nmcsbcd->uState == CDIS_DISABLED ? item->ALPHA - 50 : item->ALPHA; - DrawAlpha(hdcScroll, &nmcsbcd->rect, item->COLOR, alpha, item->COLOR2, item->COLOR2_TRANSPARENT, - item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); - } - uint32_t dfcFlags = DFCS_FLAT | (nmcsbcd->uState == CDIS_DISABLED ? DFCS_INACTIVE : - (nmcsbcd->uState == CDIS_HOT ? DFCS_HOT : (nmcsbcd->uState == CDIS_SELECTED ? DFCS_PUSHED : 0))); - - if (nmcsbcd->uItem == HTSCROLL_UP) - arrowItem = arStatusItems[ID_EXTBKSCROLLARROWUP - ID_STATUS_OFFLINE]; - if (nmcsbcd->uItem == HTSCROLL_DOWN) - arrowItem = arStatusItems[ID_EXTBKSCROLLARROWDOWN - ID_STATUS_OFFLINE]; - if (arrowItem && !arrowItem->IGNORED) - DrawAlpha(hdcScroll, &nmcsbcd->rect, arrowItem->COLOR, arrowItem->ALPHA, arrowItem->COLOR2, arrowItem->COLOR2_TRANSPARENT, - arrowItem->GRADIENT, arrowItem->CORNER, arrowItem->BORDERSTYLE, arrowItem->imageItem); - else if (arrowItem) - DrawFrameControl(hdcScroll, &nmcsbcd->rect, DFC_SCROLL, (nmcsbcd->uItem == HTSCROLL_UP ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | dfcFlags); - - if (rgn) { - SelectClipRgn(hdcScroll, nullptr); - DeleteObject(rgn); - } - } - } - return 0; -} - -static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact) -{ - if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) { - if (hContact == 0) - return 0; - - if (item->dwFlags & BUTTON_PASSHCONTACTW) - *wParam = hContact; - else if (item->dwFlags & BUTTON_PASSHCONTACTL) - *lParam = hContact; - } - return 1; -} - -static void ShowCLUI(HWND hwnd) -{ - int state = old_cliststate; - int onTop = g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT); - - SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE); - - if (state == SETTING_STATE_NORMAL) { - SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0); - ShowWindow(g_clistApi.hwndContactList, SW_SHOWNORMAL); - SendMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0); - } - else if (state == SETTING_STATE_MINIMIZED) { - cfg::dat.forceResize = TRUE; - ShowWindow(g_clistApi.hwndContactList, SW_HIDE); - } - else if (state == SETTING_STATE_HIDDEN) { - cfg::dat.forceResize = TRUE; - ShowWindow(g_clistApi.hwndContactList, SW_HIDE); - } - SetWindowPos(g_clistApi.hwndContactList, onTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING); - DrawMenuBar(hwnd); - if (cfg::dat.autosize) { - SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0); - SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); - } -} - -static void GetButtonRect(HWND hwnd, RECT *rc) -{ - if (hwnd) - GetWindowRect(hwnd, rc); - else { - POINT pt; - GetCursorPos(&pt); - rc->bottom = rc->top = pt.y; - rc->left = rc->right = pt.x; - } -} - -LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - - switch (msg) { - case WM_CREATE: - { - int flags = WS_CHILD | CCS_BOTTOM; - flags |= db_get_b(0, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0; - flags |= db_get_b(0, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0; - g_clistApi.hwndStatus = CreateWindow(STATUSCLASSNAME, nullptr, flags, 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr); - if (flags & WS_VISIBLE) { - ShowWindow(g_clistApi.hwndStatus, SW_SHOW); - SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0); - } - mir_subclassWindow(g_clistApi.hwndStatus, NewStatusBarWndProc); - SetClassLong(g_clistApi.hwndStatus, GCL_STYLE, GetClassLong(g_clistApi.hwndStatus, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); - } - g_oldSize.cx = g_oldSize.cy = 0; - old_cliststate = g_plugin.getByte("State", SETTING_STATE_NORMAL); - g_plugin.setByte("State", SETTING_STATE_HIDDEN); - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VISIBLE); - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | WS_CLIPCHILDREN); - if (!cfg::dat.bFirstRun) - ConfigureEventArea(); - ConfigureCLUIGeometry(0); - CluiProtocolStatusChanged(0, nullptr); - - for (int i = ID_STATUS_OFFLINE; i <= ID_STATUS_MAX; i++) - statusNames[i - ID_STATUS_OFFLINE] = Clist_GetStatusModeDescription(i, 0); - - //delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded) - if (cfg::dat.bLayeredHack) { - SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | (WS_EX_LAYERED)); - SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA); - } - - if (cfg::dat.isTransparent) { - SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - } - transparentFocus = 1; - - TranslateMenu(GetMenu(hwnd)); - PostMessage(hwnd, M_CREATECLC, 0, 0); - return FALSE; - - case WM_NCCREATE: - { - LPCREATESTRUCT p = (LPCREATESTRUCT)lParam; - p->style &= ~(CS_HREDRAW | CS_VREDRAW); - } - break; - - case M_CREATECLC: { - if (db_get_b(0, "CLUI", "useskin", 0)) - IMG_LoadItems(); - CreateButtonBar(hwnd); - SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE); - { - LONG style; - uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_TOOLWINDOW); - ShowWindow(g_clistApi.hwndContactList, SW_HIDE); - style = GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE); - if (windowStyle != SETTING_WINDOWSTYLE_DEFAULT) { - style |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE; - style &= ~WS_EX_APPWINDOW; - } - else { - style &= ~(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE); - if (g_plugin.getByte("AlwaysHideOnTB", 1)) - style &= ~WS_EX_APPWINDOW; - else - style |= WS_EX_APPWINDOW; - } - - SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, style); - ApplyCLUIBorderStyle(); - - SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE); - } - - if (cfg::dat.bSkinnedButtonMode) - SetButtonToSkinned(); - ConfigureFrame(); - SetButtonStates(); - - CreateCLC(); - cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0); - - if (cfg::dat.bFullTransparent) { - if (g_CLUISkinnedBkColorRGB) - Tweak_It(g_CLUISkinnedBkColorRGB); - else if (cfg::dat.bClipBorder || (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME)) - Tweak_It(RGB(255, 0, 255)); - else - Tweak_It(cfg::clcdat->bkColour); - } - - g_plugin.setByte("State", old_cliststate); - - if (g_plugin.getByte("AutoApplyLastViewMode", 0)) { - DBVARIANT dbv = { 0 }; - if (!g_plugin.getString("LastViewMode", &dbv)) { - if (mir_strlen(dbv.pszVal) > 2) { - if (db_get_dw(0, CLVM_MODULE, dbv.pszVal, -1) != 0xffffffff) - ApplyViewMode((char *)dbv.pszVal); - } - db_free(&dbv); - } - } - if (!cfg::dat.autosize) - ShowCLUI(hwnd); - else { - show_on_first_autosize = TRUE; - RecalcScrollBar(g_clistApi.hwndContactTree, cfg::clcdat); - } - return 0; - } - case WM_ERASEBKGND: - return TRUE; - /* - if (cfg::dat.bSkinnedButtonMode) - return TRUE; - return DefWindowProc(hwnd, msg, wParam, lParam); - */ - - case WM_PAINT: - { - PAINTSTRUCT ps; - RECT rcFrame, rcClient; - HDC hdc; - HRGN rgn = nullptr; - HDC hdcReal = BeginPaint(hwnd, &ps); - - if (during_sizing) - rcClient = rcWPC; - else - GetClientRect(hwnd, &rcClient); - CopyRect(&rc, &rcClient); - - if (!cfg::dat.hdcBg || rc.right > cfg::dat.dcSize.cx || rc.bottom + cfg::dat.statusBarHeight > cfg::dat.dcSize.cy) { - RECT rcWorkArea; - - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE); - HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMon, &mi)) - rcWorkArea = mi.rcWork; - - cfg::dat.dcSize.cy = max(rc.bottom + cfg::dat.statusBarHeight, rcWorkArea.bottom - rcWorkArea.top); - cfg::dat.dcSize.cx = max(rc.right, (rcWorkArea.right - rcWorkArea.left) / 2); - - if (cfg::dat.hdcBg) { - SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld); - DeleteObject(cfg::dat.hbmBg); - DeleteDC(cfg::dat.hdcBg); - } - cfg::dat.hdcBg = CreateCompatibleDC(hdcReal); - cfg::dat.hbmBg = CreateCompatibleBitmap(hdcReal, cfg::dat.dcSize.cx, cfg::dat.dcSize.cy); - cfg::dat.hbmBgOld = reinterpret_cast<HBITMAP>(SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBg)); - } - - if (cfg::shutDown) { - EndPaint(hwnd, &ps); - return 0; - } - - hdc = cfg::dat.hdcBg; - - CopyRect(&rcFrame, &rcClient); - if (g_CLUISkinnedBkColor) { - if (cfg::dat.fOnDesktop) { - HDC dc = GetDC(nullptr); - RECT rcWin; - - GetWindowRect(hwnd, &rcWin); - BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, dc, rcWin.left, rcWin.top, SRCCOPY); - ReleaseDC(nullptr, dc); - } - else FillRect(hdc, &rcClient, g_CLUISkinnedBkColor); - } - - if (cfg::dat.bClipBorder != 0 || cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) { - int docked = Clist_IsDocked(); - int clip = cfg::dat.bClipBorder; - - if (!g_CLUISkinnedBkColor) - FillRect(hdc, &rcClient, cfg::dat.hBrushColorKey); - if (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) - rgn = CreateRoundRectRgn(clip, docked ? 0 : clip, rcClient.right - clip + 1, rcClient.bottom - (docked ? 0 : clip - 1), 8 + clip, 8 + clip); - else - rgn = CreateRectRgn(clip, docked ? 0 : clip, rcClient.right - clip, rcClient.bottom - (docked ? 0 : clip)); - SelectClipRgn(hdc, rgn); - } - - if (g_CLUIImageItem) { - IMG_RenderImageItem(hdc, g_CLUIImageItem, &rcFrame); - cfg::dat.ptW.x = cfg::dat.ptW.y = 0; - ClientToScreen(hwnd, &cfg::dat.ptW); - goto skipbg; - } - - if (cfg::dat.bWallpaperMode) - FillRect(hdc, &rcClient, cfg::dat.hBrushCLCBk); - else - FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE)); - - rcFrame.left += (cfg::dat.bCLeft - 1); - rcFrame.right -= (cfg::dat.bCRight - 1); - rcFrame.bottom++; - rcFrame.bottom -= cfg::dat.statusBarHeight; - rcFrame.top += (cfg::dat.topOffset - 1); - - if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) { - if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) { - InflateRect(&rcFrame, -1, -1); - if (cfg::dat.bmpBackground) - BlitWallpaper(hdc, &rcFrame, cfg::clcdat); - cfg::dat.ptW.x = cfg::dat.ptW.y = 0; - ClientToScreen(hwnd, &cfg::dat.ptW); - } - InflateRect(&rcFrame, 1, 1); - if (cfg::dat.bSkinnedButtonMode) - rcFrame.bottom -= (cfg::dat.bottomOffset); - DrawEdge(hdc, &rcFrame, BDR_SUNKENOUTER, BF_RECT); - } - else if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) { - if (cfg::dat.bmpBackground) - BlitWallpaper(hdc, &rcFrame, cfg::clcdat); - cfg::dat.ptW.x = cfg::dat.ptW.y = 0; - ClientToScreen(hwnd, &cfg::dat.ptW); - } -skipbg: - BitBlt(hdcReal, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdc, 0, 0, SRCCOPY); - if (rgn) { - SelectClipRgn(hdc, nullptr); - DeleteObject(rgn); - } - EndPaint(hwnd, &ps); - } - return 0; - - case WM_ENTERSIZEMOVE: - { - POINT pt = { 0 }; - - GetWindowRect(hwnd, &g_PreSizeRect); - GetClientRect(hwnd, &rc); - ClientToScreen(hwnd, &pt); - g_CLUI_x_off = pt.x - g_PreSizeRect.left; - g_CLUI_y_off = pt.y - g_PreSizeRect.top; - pt.x = rc.right; - ClientToScreen(hwnd, &pt); - g_CLUI_x1_off = g_PreSizeRect.right - pt.x; - pt.x = 0; - pt.y = rc.bottom; - ClientToScreen(hwnd, &pt); - g_CLUI_y1_off = g_PreSizeRect.bottom - pt.y; - } - break; - - case WM_EXITSIZEMOVE: - PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0); - break; - - case WM_SIZING: - break; - - case WM_WINDOWPOSCHANGED: - if (Docking_IsDocked(0, 0)) - break; - - case WM_WINDOWPOSCHANGING: - if (g_clistApi.hwndContactList != nullptr) { - WINDOWPOS *wp = (WINDOWPOS *)lParam; - if (!wp || (wp->flags & SWP_NOSIZE)) - return FALSE; - - RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); - during_sizing = true; - - new_window_rect.left = 0; - new_window_rect.right = wp->cx - (g_CLUI_x_off + g_CLUI_x1_off); - new_window_rect.top = 0; - new_window_rect.bottom = wp->cy - g_CLUI_y_off - g_CLUI_y1_off; - - if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) { - RECT rcStatus; - SetWindowPos(g_clistApi.hwndStatus, nullptr, 0, new_window_rect.bottom - 20, new_window_rect.right, 20, SWP_NOZORDER); - GetWindowRect(g_clistApi.hwndStatus, &rcStatus); - cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top); - if (wp->cx != g_oldSize.cx) - SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0); - RedrawWindow(g_clistApi.hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); - } - else - cfg::dat.statusBarHeight = 0; - - SizeFramesByWindowRect(&new_window_rect); - dock_prevent_moving = 0; - LayoutButtons(hwnd, &new_window_rect); - dock_prevent_moving = 1; - g_oldPos.x = wp->x; - g_oldPos.y = wp->y; - g_oldSize.cx = wp->cx; - g_oldSize.cy = wp->cy; - rcWPC = new_window_rect; - } - during_sizing = false; - return 0; - - case WM_SIZE: - if ((wParam == 0 && lParam == 0) || Docking_IsDocked(0, 0)) { - - if (IsZoomed(hwnd)) - ShowWindow(hwnd, SW_SHOWNORMAL); - - if (g_clistApi.hwndContactList != nullptr) { - SendMessage(hwnd, WM_ENTERSIZEMOVE, 0, 0); - GetWindowRect(hwnd, &rc); - WINDOWPOS wp = {}; - wp.cx = rc.right - rc.left; - wp.cy = rc.bottom - rc.top; - wp.x = rc.left; - wp.y = rc.top; - wp.flags = 0; - SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); - SendMessage(hwnd, WM_EXITSIZEMOVE, 0, 0); - } - } - - case WM_MOVE: - if (!IsIconic(hwnd)) { - GetWindowRect(hwnd, &rc); - - if (!Docking_IsDocked(0, 0)) { - cluiPos.bottom = (uint32_t)(rc.bottom - rc.top); - cluiPos.left = rc.left; - cluiPos.top = rc.top; - } - cluiPos.right = rc.right - rc.left; - if (cfg::dat.realTimeSaving) { - GetWindowRect(hwnd, &rc); - - // if docked, dont remember pos (except for width) - if (!Clist_IsDocked()) { - g_plugin.setDword("Height", (uint32_t)(rc.bottom - rc.top)); - g_plugin.setDword("x", (uint32_t)rc.left); - g_plugin.setDword("y", (uint32_t)rc.top); - } - g_plugin.setDword("Width", (uint32_t)(rc.right - rc.left)); - } - } - return TRUE; - - case WM_SETFOCUS: - SetFocus(g_clistApi.hwndContactTree); - return 0; - - case CLUIINTM_REMOVEFROMTASKBAR: { - uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT); - if (windowStyle == SETTING_WINDOWSTYLE_DEFAULT && g_plugin.getByte("AlwaysHideOnTB", 0)) - RemoveFromTaskBar(hwnd); - return 0; - } - case WM_ACTIVATE: - if (g_fading_active) { - if (wParam != WA_INACTIVE && cfg::dat.isTransparent) - transparentFocus = 1; - return DefWindowProc(hwnd, msg, wParam, lParam); - } - if (wParam == WA_INACTIVE) { - if ((HWND)wParam != hwnd) - if (cfg::dat.isTransparent) - if (transparentFocus) - SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr); - } - else { - if (cfg::dat.isTransparent) { - KillTimer(hwnd, TM_AUTOALPHA); - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - transparentFocus = 1; - } - SetWindowPos(g_clistApi.hwndContactList, g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING); - } - PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0); - return DefWindowProc(hwnd, msg, wParam, lParam); - - case WM_SETCURSOR: - if (cfg::dat.isTransparent) { - if (!transparentFocus && GetForegroundWindow() != hwnd) { - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - transparentFocus = 1; - SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr); - } - } - return DefWindowProc(hwnd, msg, wParam, lParam); - - case WM_NCHITTEST: { - LRESULT result; - RECT r; - POINT pt; - int clip = cfg::dat.bClipBorder; - - GetWindowRect(hwnd, &r); - GetCursorPos(&pt); - if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6 && !db_get_b(0, "CLUI", "AutoSize", 0)) { - if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) - return HTBOTTOM; - if (pt.x < r.left + clip + 10) - return HTBOTTOMLEFT; - if (pt.x > r.right - clip - 10) - return HTBOTTOMRIGHT; - } - else if (pt.y >= r.top && pt.y <= r.top + 3 && !db_get_b(0, "CLUI", "AutoSize", 0)) { - if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) - return HTTOP; - if (pt.x < r.left + clip + 10) - return HTTOPLEFT; - if (pt.x > r.right - clip - 10) - return HTTOPRIGHT; - } - else if (pt.x >= r.left && pt.x <= r.left + clip + 6) - return HTLEFT; - else if (pt.x >= r.right - clip - 6 && pt.x <= r.right) - return HTRIGHT; - - result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam); - if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT || result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT) - if (cfg::dat.autosize) - return HTCLIENT; - return result; - } - - case WM_TIMER: - if (wParam == TM_AUTOALPHA) { - int inwnd; - - if (GetForegroundWindow() == hwnd) { - KillTimer(hwnd, TM_AUTOALPHA); - inwnd = 1; - } - else { - POINT pt; - HWND hwndPt; - pt.x = (short)LOWORD(GetMessagePos()); - pt.y = (short)HIWORD(GetMessagePos()); - hwndPt = WindowFromPoint(pt); - inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd); - } - if (inwnd != transparentFocus) { - //change - transparentFocus = inwnd; - if (transparentFocus) - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - else - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.autoalpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - } - if (!transparentFocus) - KillTimer(hwnd, TM_AUTOALPHA); - } - else if (wParam == TIMERID_AUTOSIZE) { - KillTimer(hwnd, wParam); - SetWindowPos(hwnd, nullptr, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING); - PostMessage(hwnd, WM_SIZE, 0, 0); - PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0); - } - return TRUE; - - case WM_SHOWWINDOW: - { - static int noRecurse = 0; - uint32_t thisTick, startTick; - int sourceAlpha, destAlpha; - - if (cfg::dat.forceResize && wParam != SW_HIDE) { - cfg::dat.forceResize = FALSE; - SendMessage(hwnd, WM_SIZE, 0, 0); - PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0); - } - PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0); - - if (lParam) - return DefWindowProc(hwnd, msg, wParam, lParam); - if (noRecurse) - return DefWindowProc(hwnd, msg, wParam, lParam); - if (!cfg::dat.fadeinout) - return DefWindowProc(hwnd, msg, wParam, lParam); - - g_fading_active = 1; - - if (wParam) { - sourceAlpha = 0; - destAlpha = cfg::dat.isTransparent ? cfg::dat.alpha : 255; - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? (COLORREF)cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)sourceAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - noRecurse = 1; - ShowWindow(hwnd, SW_SHOW); - RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - noRecurse = 0; - } - else { - sourceAlpha = cfg::dat.isTransparent ? (transparentFocus ? cfg::dat.alpha : cfg::dat.autoalpha) : 255; - destAlpha = 0; - } - for (startTick = GetTickCount();;) { - thisTick = GetTickCount(); - if (thisTick >= startTick + 200) { - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)destAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - g_fading_active = 0; - return DefWindowProc(hwnd, msg, wParam, lParam); - } - SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)(sourceAlpha + (destAlpha - sourceAlpha) * (int)(thisTick - startTick) / 200), LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); - } - } - - case WM_SYSCOMMAND: - { - uint8_t bWindowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT); - if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle) { - if (wParam == SC_RESTORE) { - CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); - SendMessage(hwnd, WM_SIZE, 0, 0); - SendMessage(hwnd, CLUIINTM_REDRAW, 0, 0); - SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0); - g_plugin.setByte("State", SETTING_STATE_NORMAL); - break; - } - } - - if (wParam == SC_MAXIMIZE) - return 0; - - if (wParam == SC_MINIMIZE) { - if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle && !g_plugin.getByte("AlwaysHideOnTB", 0)) { - g_plugin.setByte("State", SETTING_STATE_MINIMIZED); - break; - } - g_clistApi.pfnShowHide(); - return 0; - } - if (wParam == SC_RESTORE) { - g_clistApi.pfnShowHide(); - return 0; - } - } - return DefWindowProc(hwnd, msg, wParam, lParam); - - case WM_COMMAND: - { - uint32_t dwOldFlags = cfg::dat.dwFlags; - if (HIWORD(wParam) == BN_CLICKED && lParam != 0) { - if (LOWORD(wParam) == IDC_TBFIRSTUID - 1) - break; - - else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling - ButtonItem *item = g_ButtonItems; - WPARAM wwParam = 0; - LPARAM llParam = 0; - MCONTACT hContact = 0; - ClcContact *contact = nullptr; - int serviceFailure = FALSE; - - if (cfg::clcdat) { - g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr); - if (contact && contact->type == CLCIT_CONTACT) - hContact = contact->hContact; - } - while (item) { - if (item->uId == (uint32_t)LOWORD(wParam)) { - int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact); - - if (item->dwFlags & BUTTON_ISSERVICE) { - if (ServiceExists(item->szService) && contactOK) - CallService(item->szService, wwParam, llParam); - else if (contactOK) - serviceFailure = TRUE; - } - else if (item->dwFlags & BUTTON_ISPROTOSERVICE && cfg::clcdat) { - if (contactOK) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (ProtoServiceExists(szProto, item->szService)) - CallProtoService(szProto, item->szService, wwParam, llParam); - else - serviceFailure = TRUE; - } - } - else if (item->dwFlags & BUTTON_ISDBACTION) { - uint8_t *pValue; - char *szModule = item->szModule; - char *szSetting = item->szSetting; - MCONTACT finalhContact = 0; - - if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) { - contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact); - if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION) - szModule = Proto_GetBaseAccountName(hContact); - finalhContact = hContact; - } - else - contactOK = 1; - - if (contactOK) { - BOOL fDelete = FALSE; - - if (item->dwFlags & BUTTON_ISTOGGLE) { - BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED); - - pValue = fChecked ? item->bValueRelease : item->bValuePush; - if (fChecked && pValue[0] == 0) - fDelete = TRUE; - } - else - pValue = item->bValuePush; - - if (fDelete) - db_unset(finalhContact, szModule, szSetting); - else { - switch (item->type) { - case DBVT_BYTE: - db_set_b(finalhContact, szModule, szSetting, pValue[0]); - break; - case DBVT_WORD: - db_set_w(finalhContact, szModule, szSetting, *((uint16_t *)&pValue[0])); - break; - case DBVT_DWORD: - db_set_dw(finalhContact, szModule, szSetting, *((uint32_t *)&pValue[0])); - break; - case DBVT_ASCIIZ: - db_set_s(finalhContact, szModule, szSetting, (char *)pValue); - break; - } - } - } - else if (item->dwFlags & BUTTON_ISTOGGLE) - SendMessage(item->hWnd, BM_SETCHECK, 0, 0); - } - if (!contactOK) - MessageBox(nullptr, TranslateT("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat."), TranslateT("Parameter mismatch"), MB_OK); - if (serviceFailure) { - wchar_t szError[512]; - mir_snwprintf(szError, TranslateT("The service %S specified by the %S button definition was not found. You may need to install additional plugins."), item->szService, item->szName); - MessageBox(nullptr, szError, TranslateT("Service failure"), MB_OK); - } - break; - } - item = item->nextItem; - } - goto buttons_done; - } - - switch (LOWORD(wParam)) { - case IDC_TBMENU: - case IDC_TBTOPMENU: - case IDC_STBTOPMENU: - GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc); - TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBMENU ? rc.top : rc.bottom, 0, hwnd, nullptr); - return 0; - - case IDC_TBTOPSTATUS: - case IDC_STBTOPSTATUS: - case IDC_TBGLOBALSTATUS: - GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc); - TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBGLOBALSTATUS ? rc.top : rc.bottom, 0, hwnd, nullptr); - return 0; - - case IDC_TBSOUND: - case IDC_STBSOUND: - cfg::dat.soundsOff = !cfg::dat.soundsOff; - db_set_b(0, "CLUI", "NoSounds", (uint8_t)cfg::dat.soundsOff); - db_set_b(0, "Skin", "UseSound", (uint8_t)(cfg::dat.soundsOff ? 0 : 1)); - return 0; - - case IDC_TBSELECTVIEWMODE: - case IDC_STBSELECTVIEWMODE: - SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_SELECTMODE, lParam); - break; - case IDC_TBCLEARVIEWMODE: - case IDC_STBCLEARVIEWMODE: - SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_RESETMODES, lParam); - break; - case IDC_TBCONFIGUREVIEWMODE: - case IDC_STBCONFIGUREVIEWMODE: - SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_CONFIGUREMODES, lParam); - break; - case IDC_TBFINDANDADD: - case IDC_STBFINDANDADD: - CallService(MS_FINDADD_FINDADD, 0, 0); - return 0; - case IDC_TBACCOUNTS: - case IDC_STBACCOUNTS: - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - break; - case IDC_TBOPTIONS: - case IDC_STBOPTIONS: - CallService("Options/OptionsCommand", 0, 0); - return 0; - } - } - else if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_MAINMENU, NULL)) - return 0; - -buttons_done: - switch (LOWORD(wParam)) { - case ID_TRAY_EXIT: - cfg::shutDown = 1; - if (Miranda_OkToExit()) - DestroyWindow(hwnd); - break; - case ID_TRAY_HIDE: - case IDC_TBMINIMIZE: - case IDC_STBMINIMIZE: - g_clistApi.pfnShowHide(); - break; - case POPUP_NEWGROUP: - SendMessage(g_clistApi.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0); - SendMessage(g_clistApi.hwndContactTree, CLM_SETUSEGROUPS, 1, 0); - Clist_GroupCreate(NULL, nullptr); - break; - case POPUP_HIDEOFFLINE: - case IDC_TBHIDEOFFLINE: - case IDC_STBHIDEOFFLINE: - g_clistApi.pfnSetHideOffline(-1); - break; - case POPUP_HIDEOFFLINEROOT: - CallService(MS_CLIST_TOGGLEHIDEOFFLINEROOT, 0, 0); - break; - case POPUP_HIDEEMPTYGROUPS: - CallService(MS_CLIST_TOGGLEEMPTYGROUPS, 0, 0); - break; - case IDC_TBHIDEGROUPS: - case IDC_STBHIDEGROUPS: - case POPUP_DISABLEGROUPS: - ClcSetButtonState(IDC_TBHIDEGROUPS, CallService(MS_CLIST_TOGGLEGROUPS, 0, 0)); - SetButtonStates(); - break; - case POPUP_HIDEMIRANDA: - g_clistApi.pfnShowHide(); - break; - case POPUP_SHOWMETAICONS: - cfg::dat.dwFlags ^= CLUI_USEMETAICONS; - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - break; - case POPUP_FRAME: - cfg::dat.dwFlags ^= CLUI_FRAME_CLISTSUNKEN; - break; - case POPUP_BUTTONS: - cfg::dat.dwFlags ^= CLUI_FRAME_SHOWBOTTOMBUTTONS; - break; - case POPUP_SHOWSTATUSICONS: - cfg::dat.dwFlags ^= CLUI_FRAME_STATUSICONS; - break; - } - if (dwOldFlags != cfg::dat.dwFlags) { - InvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE); - db_set_dw(0, "CLUI", "Frameflags", cfg::dat.dwFlags); - if ((dwOldFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN)) != (cfg::dat.dwFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN))) { - ConfigureFrame(); - ConfigureCLUIGeometry(1); - } - ConfigureEventArea(); - PostMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0); - PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0); - } - } - return FALSE; - - case WM_LBUTTONDOWN: - if (g_ButtonItems) { - POINT pt; - GetCursorPos(&pt); - return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); - } - break; - - case WM_DISPLAYCHANGE: - SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged - break; - - case WM_NOTIFY: - if (((LPNMHDR)lParam)->hwndFrom == g_clistApi.hwndContactTree) { - switch (((LPNMHDR)lParam)->code) { - case CLN_LISTSIZECHANGE: - sttProcessResize(hwnd, (NMCLISTCONTROL *)lParam); - return FALSE; - - case NM_CLICK: - { - NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam; - uint32_t hitFlags; - SendMessage(g_clistApi.hwndContactTree, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y)); - if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0) - break; - - if (db_get_b(0, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) { - POINT pt; - pt = nm->pt; - ClientToScreen(g_clistApi.hwndContactTree, &pt); - return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); - } - } - return FALSE; - } - } - break; - - case WM_CONTEXTMENU: - GetWindowRect(g_clistApi.hwndContactTree, &rc); - { - // x/y might be -1 if it was generated by a kb click - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - if (pt.x == -1 && pt.y == -1) { - // all this is done in screen-coords! - GetCursorPos(&pt); - // the mouse isnt near the window, so put it in the middle of the window - if (!PtInRect(&rc, pt)) { - pt.x = rc.left + (rc.right - rc.left) / 2; - pt.y = rc.top + (rc.bottom - rc.top) / 2; - } - } - if (PtInRect(&rc, pt)) { - HMENU hMenu = Menu_BuildGroupMenu(); - TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr); - Menu_DestroyNestedMenu(hMenu); - return 0; - } - GetWindowRect(g_clistApi.hwndStatus, &rc); - if (PtInRect(&rc, pt)) { - HMENU hMenu; - if (db_get_b(0, "CLUI", "SBarRightClk", 0)) - hMenu = Menu_GetMainMenu(); - else - hMenu = Menu_GetStatusMenu(); - TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr); - return 0; - } - } - break; - - case WM_MEASUREITEM: - if (((LPMEASUREITEMSTRUCT)lParam)->itemData == MENU_MIRANDAMENU) { - ((LPMEASUREITEMSTRUCT)lParam)->itemWidth = g_cxsmIcon * 4 / 3; - ((LPMEASUREITEMSTRUCT)lParam)->itemHeight = 0; - return TRUE; - } - return Menu_MeasureItem(lParam); - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; - - if (hbmLockedPoint == nullptr) { - hdcLockedPoint = CreateCompatibleDC(dis->hDC); - hbmLockedPoint = CreateCompatibleBitmap(dis->hDC, 5, 5); - hbmOldLockedPoint = reinterpret_cast<HBITMAP>(SelectObject(hdcLockedPoint, hbmLockedPoint)); - } - if (dis->hwndItem == g_clistApi.hwndStatus) { - ProtocolData *pd = (ProtocolData *)dis->itemData; - if (IsBadCodePtr((FARPROC)pd)) - return TRUE; - if (cfg::shutDown) - return TRUE; - - char *szProto = pd->RealName; - PROTOACCOUNT *pa = Proto_GetAccount(szProto); - if (pa == nullptr) - return TRUE; - - int nParts = SendMessage(g_clistApi.hwndStatus, SB_GETPARTS, 0, 0); - SIZE textSize; - uint8_t showOpts = db_get_b(0, "CLUI", "SBarShow", 1); - - SetBkMode(dis->hDC, TRANSPARENT); - int x = dis->rcItem.left; - - if (showOpts & 1) { - HICON hIcon; - - if (pa->iRealStatus >= ID_STATUS_CONNECTING && pa->iRealStatus < ID_STATUS_OFFLINE) { - char szBuffer[128]; - mir_snprintf(szBuffer, "%s_conn", pd->RealName); - hIcon = IcoLib_GetIcon(szBuffer); - } - else if (cfg::dat.bShowXStatusOnSbar && pa->iRealStatus > ID_STATUS_OFFLINE) { - int xStatus; - CUSTOM_STATUS cst = { sizeof(cst) }; - cst.flags = CSSF_MASK_STATUS; - cst.status = &xStatus; - if (ProtoServiceExists(pd->RealName, PS_GETCUSTOMSTATUSEX) && !CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cst) && xStatus > 0) - hIcon = (HICON)CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); // get OWN xStatus icon (if set) - else - hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus); - } - else hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus); - - if (!(showOpts & 6) && cfg::dat.bEqualSections) - x = (dis->rcItem.left + dis->rcItem.right - 16) >> 1; - if (pd->protopos == 0) - x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft); - else if (pd->protopos == nParts - 1) - x -= (cfg::dat.bCRight / 2); - DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL); - IcoLib_ReleaseIcon(hIcon); - - if (db_get_b(0, "CLUI", "sbar_showlocked", 1)) { - if (pa->bIsLocked) { - hIcon = Skin_LoadIcon(SKINICON_OTHER_STATUS_LOCKED); - if (hIcon != nullptr) { - DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL); - IcoLib_ReleaseIcon(hIcon); - } - } - } - x += 18; - } - else { - x += 2; - if (pd->protopos == 0) - x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft); - else if (pd->protopos == nParts - 1) - x -= (cfg::dat.bCRight / 2); - } - - if (showOpts & 2) { - wchar_t szName[64]; - wcsncpy_s(szName, pa->tszAccountName, _TRUNCATE); - - if (mir_wstrlen(szName) < _countof(szName) - 1) - mir_wstrcat(szName, L" "); - GetTextExtentPoint32(dis->hDC, szName, (int)mir_wstrlen(szName), &textSize); - TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szName, (int)mir_wstrlen(szName)); - x += textSize.cx; - } - if (showOpts & 4) { - wchar_t *szStatus = Clist_GetStatusModeDescription(pa->iRealStatus, 0); - GetTextExtentPoint32(dis->hDC, szStatus, (int)mir_wstrlen(szStatus), &textSize); - TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szStatus, (int)mir_wstrlen(szStatus)); - } - } - else if (dis->CtlType == ODT_MENU) { - if (dis->itemData == MENU_MIRANDAMENU) - break; - return Menu_DrawItem(lParam); - } - } - return 0; - - case WM_CLOSE: - if (SETTING_WINDOWSTYLE_DEFAULT == db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT) && !g_plugin.getByte("AlwaysHideOnTB", 0)) { - PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); - return 0; - } - g_clistApi.pfnShowHide(); - return 0; - - case CLUIINTM_REDRAW: - if (show_on_first_autosize) { - show_on_first_autosize = FALSE; - ShowCLUI(hwnd); - } - RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; - - case CLUIINTM_STATUSBARUPDATE: - CluiProtocolStatusChanged(0, nullptr); - return 0; - - case WM_THEMECHANGED: - API::updateState(); - break; - - case WM_DESTROY: - if (cfg::dat.hdcBg) { - SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld); - DeleteObject(cfg::dat.hbmBg); - DeleteDC(cfg::dat.hdcBg); - cfg::dat.hdcBg = nullptr; - } - if (cfg::dat.bmpBackground) { - SelectObject(cfg::dat.hdcPic, cfg::dat.hbmPicOld); - DeleteDC(cfg::dat.hdcPic); - DeleteObject(cfg::dat.bmpBackground); - cfg::dat.bmpBackground = nullptr; - } - FreeProtocolData(); - if (hdcLockedPoint) { - SelectObject(hdcLockedPoint, hbmOldLockedPoint); - DeleteObject(hbmLockedPoint); - DeleteDC(hdcLockedPoint); - } - // if this has not yet been set, do it now. - // indicates that clist is shutting down and prevents various things - // from happening at shutdown. - if (!cfg::shutDown) - cfg::shutDown = 1; - CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)hFrameContactTree, 0); - break; - } - - return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam); -} - -#ifndef CS_DROPSHADOW -#define CS_DROPSHADOW 0x00020000 -#endif - -static int MetaChanged(WPARAM wParam, LPARAM lParam) -{ - Clist_Broadcast(INTM_METACHANGEDEVENT, wParam, lParam); - return 0; -} - -static INT_PTR CLN_ShowMainMenu(WPARAM, LPARAM) -{ - POINT pt; - GetCursorPos(&pt); - TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr); - return 0; -} - -static INT_PTR CLN_ShowStatusMenu(WPARAM, LPARAM) -{ - POINT pt; - GetCursorPos(&pt); - TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr); - return 0; -} - -#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu" -#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu" - -void LoadCLUIModule(void) -{ - HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded); - - WNDCLASS wndclass; - wndclass.style = 0; - wndclass.lpfnWndProc = EventAreaWndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = g_plugin.getInst(); - wndclass.hIcon = nullptr; - wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW); - wndclass.hbrBackground = (HBRUSH)COLOR_3DFACE; - wndclass.lpszMenuName = nullptr; - wndclass.lpszClassName = L"EventAreaClass"; - RegisterClass(&wndclass); - - oldhideoffline = Clist::HideOffline; - cluiPos.left = g_plugin.getDword("x", 600); - cluiPos.top = g_plugin.getDword("y", 200); - cluiPos.right = g_plugin.getDword("Width", 150); - cluiPos.bottom = g_plugin.getDword("Height", 350); - - LoadExtraIconModule(); - LoadCLUIFramesModule(); - - CreateServiceFunction(MS_CLUI_SHOWMAINMENU, CLN_ShowMainMenu); - CreateServiceFunction(MS_CLUI_SHOWSTATUSMENU, CLN_ShowStatusMenu); - - if (db_get_b(0, "CLUI", "FloaterMode", 0)) { - MessageBox(nullptr, - TranslateT("You need the FloatingContacts plugin, cause the embedded floating contacts were removed."), - TranslateT("Warning"), MB_OK | MB_ICONWARNING); - db_unset(0, "CLUI", "FloaterMode"); - } - - MF_InitCheck(); -} - -void OnCreateClc() -{ - HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged); - HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaChanged); - - InitGroupMenus(); - LoadExtBkSettingsFromDB(); - PreCreateCLC(g_clistApi.hwndContactList); -} - -struct -{ - const wchar_t *tszName; - int iMask; -} -static clistFontDescr[] = -{ - { LPGENW("Standard contacts"), FIDF_CLASSGENERAL }, - { LPGENW("Online contacts to whom you have a different visibility"), FIDF_CLASSGENERAL }, - { LPGENW("Offline contacts"), FIDF_CLASSGENERAL }, - { LPGENW("Contacts which are 'not on list'"), FIDF_CLASSGENERAL }, - { LPGENW("Groups"), FIDF_CLASSHEADER }, - { LPGENW("Group member counts"), FIDF_CLASSHEADER }, - { LPGENW("Dividers"), FIDF_CLASSSMALL }, - { LPGENW("Offline contacts to whom you have a different visibility"), FIDF_CLASSGENERAL }, - { LPGENW("Status mode"), FIDF_CLASSGENERAL }, - { LPGENW("Frame titles"), FIDF_CLASSGENERAL }, - { LPGENW("Event area"), FIDF_CLASSGENERAL }, - { LPGENW("Contact list local time"), FIDF_CLASSGENERAL } -}; - -void FS_RegisterFonts() -{ - FontIDW fid = {}; - wcsncpy_s(fid.group, LPGENW("Contact list"), _TRUNCATE); - strncpy_s(fid.dbSettingsGroup, "CLC", _TRUNCATE); - fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS | FIDF_APPENDNAME | FIDF_SAVEPOINTSIZE; - - HDC hdc = GetDC(nullptr); - for (int i = 0; i < _countof(clistFontDescr); i++) { - LOGFONT lf; - Clist_GetFontSetting(i, &lf, &fid.deffontsettings.colour); - lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72); - - wcsncpy_s(fid.deffontsettings.szFace, lf.lfFaceName, _TRUNCATE); - fid.deffontsettings.charset = lf.lfCharSet; - fid.deffontsettings.size = (char)lf.lfHeight; - fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0); - - fid.flags &= ~FIDF_CLASSMASK; - fid.flags |= clistFontDescr[i].iMask; - - wcsncpy_s(fid.name, clistFontDescr[i].tszName, _TRUNCATE); - - char idstr[10]; - mir_snprintf(idstr, "Font%d", i); - strncpy_s(fid.setting, idstr, _TRUNCATE); - fid.order = i; - g_plugin.addFont(&fid); - } - ReleaseDC(nullptr, hdc); - - // and colours - ColourIDW colourid = {}; - colourid.order = 0; - strncpy_s(colourid.dbSettingsGroup, "CLC", _TRUNCATE); - - strncpy_s(colourid.setting, "BkColour", _TRUNCATE); - wcsncpy_s(colourid.name, LPGENW("Background"), _TRUNCATE); - wcsncpy_s(colourid.group, LPGENW("Contact list"), _TRUNCATE); - colourid.defcolour = CLCDEFAULT_BKCOLOUR; - g_plugin.addColor(&colourid); - - strncpy_s(colourid.setting, "SelTextColour", _TRUNCATE); - wcsncpy_s(colourid.name, LPGENW("Selected text"), _TRUNCATE); - colourid.order = 1; - colourid.defcolour = CLCDEFAULT_SELTEXTCOLOUR; - g_plugin.addColor(&colourid); - - strncpy_s(colourid.setting, "HotTextColour", _TRUNCATE); - wcsncpy_s(colourid.name, LPGENW("Hottrack text"), _TRUNCATE); - colourid.order = 1; - colourid.defcolour = CLCDEFAULT_HOTTEXTCOLOUR; - g_plugin.addColor(&colourid); - - strncpy_s(colourid.setting, "QuickSearchColour", _TRUNCATE); - wcsncpy_s(colourid.name, LPGENW("Quicksearch text"), _TRUNCATE); - colourid.order = 1; - colourid.defcolour = CLCDEFAULT_QUICKSEARCHCOLOUR; - g_plugin.addColor(&colourid); - - strncpy_s(colourid.dbSettingsGroup, "CLUI", _TRUNCATE); - strncpy_s(colourid.setting, "clr_frameborder", _TRUNCATE); - wcsncpy_s(colourid.name, LPGENW("Embedded frames border"), _TRUNCATE); - colourid.order = 1; - colourid.defcolour = RGB(40, 40, 40); - g_plugin.addColor(&colourid); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-03 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include <m_findadd.h>
+#include "cluiframes.h"
+#include "coolscroll.h"
+
+#define TM_AUTOALPHA 1
+#define TIMERID_AUTOSIZE 100
+#define MENU_MIRANDAMENU 0xFFFF1234
+
+int g_fading_active = 0;
+
+static RECT g_PreSizeRect, g_SizingRect;
+static int g_sizingmethod;
+static LONG g_CLUI_x_off, g_CLUI_y_off, g_CLUI_y1_off, g_CLUI_x1_off;
+static RECT rcWPC;
+
+static int transparentFocus = 1;
+static byte oldhideoffline;
+static int disableautoupd = 1;
+static int hFrameContactTree;
+extern RECT old_window_rect, new_window_rect;
+
+extern BOOL g_trayTooltipActive;
+extern POINT tray_hover_pos;
+extern HWND g_hwndViewModeFrame, g_hwndEventArea, g_hwndToolbarFrame;
+
+extern ImageItem *g_CLUIImageItem;
+extern HBRUSH g_CLUISkinnedBkColor;
+extern HWND g_hwndSFL;
+extern ButtonItem *g_ButtonItems;
+extern COLORREF g_CLUISkinnedBkColorRGB;
+extern FRAMEWND *wndFrameCLC;
+extern HPEN g_hPenCLUIFrames;
+
+static uint8_t old_cliststate, show_on_first_autosize = FALSE;
+
+RECT cluiPos;
+
+wchar_t *statusNames[12];
+
+extern LRESULT CALLBACK EventAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+extern int hNotifyFrame;
+
+void MF_InitCheck(void);
+void InitGroupMenus();
+void FS_RegisterFonts();
+void LoadExtraIconModule();
+void RemoveFromTaskBar(HWND hWnd);
+
+extern LONG g_cxsmIcon, g_cysmIcon;
+
+SIZE g_oldSize = { 0 };
+POINT g_oldPos = { 0 };
+int during_sizing = 0;
+extern int dock_prevent_moving;
+
+static HDC hdcLockedPoint = nullptr;
+static HBITMAP hbmLockedPoint = nullptr, hbmOldLockedPoint = nullptr;
+
+HICON overlayicons[10];
+
+static IconItem myIcons[] = {
+ { LPGEN("Toggle show online/offline"), "CLN_online", IDI_HIDEOFFLINE },
+ { LPGEN("Toggle groups"), "CLN_groups", IDI_HIDEGROUPS },
+ { LPGEN("Find contacts"), "CLN_findadd", IDI_FINDANDADD },
+ { LPGEN("Open preferences"), "CLN_options", IDI_TBOPTIONS },
+ { LPGEN("Toggle sounds"), "CLN_sound", IDI_SOUNDSON },
+ { LPGEN("Minimize contact list"), "CLN_minimize", IDI_MINIMIZE },
+ { LPGEN("Show TabSRMM session list"), "CLN_slist", IDI_TABSRMMSESSIONLIST },
+ { LPGEN("Show TabSRMM menu"), "CLN_menu", IDI_TABSRMMMENU },
+ { LPGEN("Sounds are off"), "CLN_soundsoff", IDI_SOUNDSOFF },
+ { LPGEN("Select view mode"), "CLN_CLVM_select", IDI_CLVM_SELECT },
+ { LPGEN("Reset view mode"), "CLN_CLVM_reset", IDI_DELETE },
+ { LPGEN("Configure view modes"), "CLN_CLVM_options", IDI_CLVM_OPTIONS },
+ { LPGEN("Show menu"), "CLN_topmenu", IDI_TBTOPMENU },
+ { LPGEN("Setup accounts"), "CLN_accounts", IDI_TBACCOUNTS }
+};
+
+HWND hTbMenu, hTbGlobalStatus;
+
+static void Tweak_It(COLORREF clr)
+{
+ SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes(g_clistApi.hwndContactList, clr, 0, LWA_COLORKEY);
+ cfg::dat.colorkey = clr;
+}
+
+static void LayoutButtons(HWND hwnd, RECT *rc)
+{
+ RECT rect;
+ uint8_t left_offset = cfg::dat.bCLeft - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
+ uint8_t right_offset = cfg::dat.bCRight - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
+ uint8_t delta = left_offset + right_offset;
+ ButtonItem *btnItems = g_ButtonItems;
+
+ if (rc == nullptr)
+ GetClientRect(hwnd, &rect);
+ else
+ rect = *rc;
+
+ rect.bottom -= cfg::dat.bCBottom;
+
+ if (g_ButtonItems) {
+ while (btnItems) {
+ LONG x = (btnItems->xOff >= 0) ? rect.left + btnItems->xOff : rect.right - abs(btnItems->xOff);
+ LONG y = (btnItems->yOff >= 0) ? rect.top + btnItems->yOff : rect.bottom - cfg::dat.statusBarHeight;
+
+ SetWindowPos(btnItems->hWnd, nullptr, x, y, btnItems->width, btnItems->height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ btnItems = btnItems->nextItem;
+ }
+ }
+
+ SetWindowPos(hTbMenu, nullptr, 2 + left_offset, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
+ 21 * 3, 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
+
+ SetWindowPos(hTbGlobalStatus, nullptr, left_offset + (3 * 21) + 3, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
+ rect.right - delta - (3 * 21 + 5), 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
+
+}
+
+static int FS_FontsChanged(WPARAM, LPARAM)
+{
+ COLORREF clr_cluiframes = db_get_dw(0, "CLUI", "clr_frameborder", RGB(40, 40, 40));
+
+ if (g_hPenCLUIFrames)
+ DeleteObject(g_hPenCLUIFrames);
+ g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, clr_cluiframes);
+
+ Clist_ClcOptionsChanged();
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+// create the CLC control, but not yet the frame. The frame containing the CLC should be created as the
+// last frame of all.
+static HWND PreCreateCLC(HWND parent)
+{
+ g_clistApi.hwndContactTree = CreateWindow(CLISTCONTROL_CLASSW, L"",
+ WS_CHILD | CLS_CONTACTLIST | (Clist::UseGroups ? CLS_USEGROUPS : 0) | (Clist::HideOffline ? CLS_HIDEOFFLINE : 0) | (Clist::HideEmptyGroups ? CLS_HIDEEMPTYGROUPS : 0) | CLS_MULTICOLUMN,
+ 0, 0, 0, 0, parent, nullptr, g_plugin.getInst(), (LPVOID)0xff00ff00);
+
+ cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
+ return g_clistApi.hwndContactTree;
+}
+
+// create internal frames, including the last frame (actual CLC control)
+static int CreateCLC()
+{
+ ExtraIcon_Reload();
+ g_clistApi.pfnSetHideOffline(oldhideoffline);
+ disableautoupd = 0;
+ {
+ CLISTFrame frame = { 0 };
+ frame.cbSize = sizeof(frame);
+ frame.szName.a = "EventArea";
+ frame.szTBname.a = LPGEN("Event area");
+ frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
+ frame.height = 20;
+ frame.Flags = F_VISIBLE | F_SHOWTBTIP | F_NOBORDER;
+ frame.align = alBottom;
+ frame.hWnd = CreateWindowExA(0, "EventAreaClass", "evt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, g_clistApi.hwndContactList, (HMENU)nullptr, g_plugin.getInst(), nullptr);
+ g_hwndEventArea = frame.hWnd;
+ hNotifyFrame = g_plugin.addFrame(&frame);
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, hNotifyFrame, FU_FMPOS);
+ HideShowNotifyFrame();
+ CreateViewModeFrame();
+ }
+ {
+ CLISTFrame Frame = { 0 };
+ Frame.cbSize = sizeof(CLISTFrame);
+ Frame.hWnd = g_clistApi.hwndContactTree;
+ Frame.align = alClient;
+ Frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
+ Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER;
+ Frame.szName.a = "My contacts";
+ Frame.szTBname.a = LPGEN("My contacts");
+ Frame.height = 200;
+ hFrameContactTree = g_plugin.addFrame(&Frame);
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_TBTIPNAME | FO_UNICODETEXT, hFrameContactTree), (LPARAM)TranslateT("My contacts"));
+
+ // ugly, but working hack. Prevent that annoying little scroll bar from appearing in the "My Contacts" title bar
+ uint32_t flags = (uint32_t)CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), 0);
+ flags |= F_VISIBLE;
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), flags);
+ }
+
+ SetButtonToSkinned();
+ return 0;
+}
+
+static int CluiModulesLoaded(WPARAM, LPARAM)
+{
+ FS_RegisterFonts();
+ HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
+ return 0;
+}
+
+static HICON hIconSaved = nullptr;
+
+void ClearIcons(int mode)
+{
+ for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
+ if (overlayicons[i - IDI_OVL_OFFLINE] != nullptr) {
+ if (mode)
+ DestroyIcon(overlayicons[i - IDI_OVL_OFFLINE]);
+ overlayicons[i - IDI_OVL_OFFLINE] = nullptr;
+ }
+ }
+}
+
+static void CacheClientIcons()
+{
+ ClearIcons(0);
+
+ for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
+ char szBuffer[128];
+ mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
+ overlayicons[i - IDI_OVL_OFFLINE] = IcoLib_GetIcon(szBuffer);
+ }
+}
+
+static void InitIcoLib()
+{
+ g_plugin.registerIcon(LPGEN("Contact list") "/" LPGEN("Default"), myIcons);
+
+ for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
+ char szBuffer[128];
+ mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
+ IconItemT icon[] = { { Clist_GetStatusModeDescription(ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE), 0), szBuffer, i } };
+ g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Overlay icons"), icon);
+ }
+
+ for (auto &pa : Accounts()) {
+ if (!pa->IsEnabled() || CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
+ continue;
+
+ wchar_t szDescr[128];
+ mir_snwprintf(szDescr, TranslateT("%s connecting"), pa->tszAccountName);
+ IconItemT icon[] = { { szDescr, "conn", IDI_PROTOCONNECTING } };
+ g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Connecting icons"), icon, pa->szModuleName);
+ }
+}
+
+static int IcoLibChanged(WPARAM, LPARAM)
+{
+ IcoLibReloadIcons();
+ return 0;
+}
+
+void CreateButtonBar(HWND hWnd)
+{
+ hTbMenu = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBMENU, g_plugin.getInst(), nullptr);
+ CustomizeButton(hTbMenu, false, false, false);
+ SetWindowText(hTbMenu, TranslateT("Menu"));
+ SendMessage(hTbMenu, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_OTHER_MAINMENU));
+ SendMessage(hTbMenu, BUTTONSETSENDONDOWN, TRUE, 0);
+ SendMessage(hTbMenu, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open main menu"), 0);
+
+ hTbGlobalStatus = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBGLOBALSTATUS, g_plugin.getInst(), nullptr);
+ CustomizeButton(hTbGlobalStatus, false, false, false);
+ SetWindowText(hTbGlobalStatus, TranslateT("Offline"));
+ SendMessage(hTbGlobalStatus, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_STATUS_OFFLINE));
+ SendMessage(hTbGlobalStatus, BUTTONSETSENDONDOWN, TRUE, 0);
+ SendMessage(hTbGlobalStatus, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Set status modes"), 0);
+}
+
+// if mode != 0 we do first time init, otherwise only reload the extra icon stuff
+void CLN_LoadAllIcons(BOOL mode)
+{
+ if (mode) {
+ InitIcoLib();
+ HookEvent(ME_SKIN_ICONSCHANGED, IcoLibChanged);
+ }
+ CacheClientIcons();
+}
+
+void ConfigureEventArea()
+{
+ int iCount = GetMenuItemCount(cfg::dat.hMenuNotify);
+ uint32_t dwFlags = cfg::dat.dwFlags;
+ int oldstate = cfg::dat.notifyActive;
+ int dwVisible = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hNotifyFrame), 0) & F_VISIBLE;
+
+ if (dwVisible) {
+ if (dwFlags & CLUI_FRAME_AUTOHIDENOTIFY)
+ cfg::dat.notifyActive = iCount > 0 ? 1 : 0;
+ else
+ cfg::dat.notifyActive = 1;
+ }
+ else
+ cfg::dat.notifyActive = 0;
+
+ if (oldstate != cfg::dat.notifyActive)
+ HideShowNotifyFrame();
+}
+
+void ConfigureFrame()
+{
+ int show = cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? SW_SHOW : SW_HIDE;
+ ShowWindow(hTbMenu, show);
+ ShowWindow(hTbGlobalStatus, show);
+}
+
+void IcoLibReloadIcons()
+{
+ CacheClientIcons();
+ ExtraIcon_Reload();
+ ExtraIcon_SetAll();
+
+ Clist_Broadcast(CLM_AUTOREBUILD, 0, 0);
+ SendMessage(g_hwndViewModeFrame, WM_USER + 100, 0, 0);
+}
+
+void ConfigureCLUIGeometry(int mode)
+{
+ RECT rcStatus;
+ uint32_t clmargins = db_get_dw(0, "CLUI", "clmargins", 0);
+
+ cfg::dat.bCLeft = LOBYTE(LOWORD(clmargins));
+ cfg::dat.bCRight = HIBYTE(LOWORD(clmargins));
+ cfg::dat.bCTop = LOBYTE(HIWORD(clmargins));
+ cfg::dat.bCBottom = HIBYTE(HIWORD(clmargins));
+
+ if (mode) {
+ if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
+ SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0);
+ GetWindowRect(g_clistApi.hwndStatus, &rcStatus);
+ cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
+ }
+ else cfg::dat.statusBarHeight = 0;
+ }
+
+ cfg::dat.topOffset = cfg::dat.bCTop;
+ cfg::dat.bottomOffset = (cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? 2 + 21 : 0) + cfg::dat.bCBottom;
+
+ if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
+ cfg::dat.topOffset += 2;
+ cfg::dat.bottomOffset += 2;
+ cfg::dat.bCLeft += 3;
+ cfg::dat.bCRight += 3;
+ }
+}
+
+// set the states of defined database action buttons (only if button is a toggle)
+void SetDBButtonStates(MCONTACT hPassedContact)
+{
+ ButtonItem *buttonItem = g_ButtonItems;
+ MCONTACT hContact = 0, hFinalContact = 0;
+ char *szModule, *szSetting;
+ ClcContact *contact = nullptr;
+
+ if (cfg::clcdat && hPassedContact == 0) {
+ g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
+ if (contact && contact->type == CLCIT_CONTACT) {
+ hContact = contact->hContact;
+ }
+ }
+
+ while (buttonItem) {
+ BOOL result = FALSE;
+
+ if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+ szModule = buttonItem->szModule;
+ szSetting = buttonItem->szSetting;
+ if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (hContact == 0) {
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, BST_UNCHECKED, 0);
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+ if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = Proto_GetBaseAccountName(hContact);
+ hFinalContact = hContact;
+ }
+ else
+ hFinalContact = 0;
+
+ if (buttonItem->type == DBVT_ASCIIZ) {
+ DBVARIANT dbv = { 0 };
+
+ if (!db_get_s(hFinalContact, szModule, szSetting, &dbv)) {
+ result = !mir_strcmp((char *)buttonItem->bValuePush, dbv.pszVal);
+ db_free(&dbv);
+ }
+ }
+ else {
+ switch (buttonItem->type) {
+ case DBVT_BYTE: {
+ uint8_t val = db_get_b(hFinalContact, szModule, szSetting, 0);
+ result = (val == buttonItem->bValuePush[0]);
+ break;
+ }
+ case DBVT_WORD: {
+ uint16_t val = db_get_w(hFinalContact, szModule, szSetting, 0);
+ result = (val == *((uint16_t *)&buttonItem->bValuePush));
+ break;
+ }
+ case DBVT_DWORD:
+ uint32_t val = db_get_dw(hFinalContact, szModule, szSetting, 0);
+ result = (val == *((uint32_t *)&buttonItem->bValuePush));
+ break;
+ }
+ }
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, (WPARAM)result, 0);
+ buttonItem = buttonItem->nextItem;
+ }
+}
+
+// set states of standard buttons (pressed/unpressed)
+void SetButtonStates()
+{
+ ButtonItem *buttonItem = g_ButtonItems;
+
+ if (g_ButtonItems) {
+ while (buttonItem) {
+ if (buttonItem->dwFlags & BUTTON_ISINTERNAL) {
+ switch (buttonItem->uId) {
+ case IDC_STBSOUND:
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, cfg::dat.soundsOff ? BST_CHECKED : BST_UNCHECKED, 0);
+ break;
+ case IDC_STBHIDEOFFLINE:
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::HideOffline, 0);
+ break;
+ case IDC_STBHIDEGROUPS:
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::UseGroups, 0);
+ break;
+ }
+ }
+ buttonItem = buttonItem->nextItem;
+ }
+ }
+}
+
+void BlitWallpaper(HDC hdc, RECT *rc, struct ClcData *dat)
+{
+ int x, y;
+ int bitx, bity;
+ int maxx, maxy;
+ int destw, desth, height, width;
+ BITMAP *bmp = &cfg::dat.bminfoBg;
+ LONG clip = cfg::dat.bClipBorder;
+
+ if (dat == nullptr)
+ return;
+
+ SetStretchBltMode(hdc, HALFTONE);
+
+ y = rc->top;
+
+ rc->left = max(rc->left, clip);
+ rc->right = min(rc->right - clip, rc->right);
+ rc->top = max(rc->top, clip);
+ rc->bottom = min(rc->bottom - clip, rc->bottom);
+
+ width = rc->right - rc->left;
+ height = rc->bottom - rc->top;
+ HRGN my_rgn = CreateRectRgn(rc->left, rc->top, rc->right, rc->bottom);
+ SelectClipRgn(hdc, my_rgn);
+ maxx = dat->backgroundBmpUse & CLBF_TILEH ? rc->right : rc->left + 1;
+ maxy = dat->backgroundBmpUse & CLBF_TILEV ? rc->bottom : y + 1;
+ switch (dat->backgroundBmpUse & CLBM_TYPE) {
+ case CLB_STRETCH:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ if (width * bmp->bmHeight < height * bmp->bmWidth) {
+ desth = height;
+ destw = desth * bmp->bmWidth / bmp->bmHeight;
+ }
+ else {
+ destw = width;
+ desth = destw * bmp->bmHeight / bmp->bmWidth;
+ }
+ }
+ else {
+ destw = width;
+ desth = height;
+ }
+ break;
+ case CLB_STRETCHH:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ destw = width;
+ desth = destw * bmp->bmHeight / bmp->bmWidth;
+ }
+ else {
+ destw = width;
+ desth = bmp->bmHeight;
+ }
+ break;
+
+ case CLB_STRETCHV:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ desth = height;
+ destw = desth * bmp->bmWidth / bmp->bmHeight;
+ }
+ else {
+ destw = bmp->bmWidth;
+ desth = height;
+ }
+ break;
+
+ default:
+ //clb_topleft
+ destw = bmp->bmWidth;
+ desth = bmp->bmHeight;
+ break;
+ }
+
+ bitx = 0;
+ bity = 0;
+ for (; y < maxy; y += desth) {
+ for (x = rc->left; x < maxx; x += destw)
+ StretchBlt(hdc, x, y, destw, desth, cfg::dat.hdcPic, bitx, bity, bmp->bmWidth, bmp->bmHeight, SRCCOPY);
+ }
+ SelectClipRgn(hdc, nullptr);
+ DeleteObject(my_rgn);
+}
+
+void ReloadThemedOptions()
+{
+ cfg::dat.bSkinnedStatusBar = db_get_b(0, "CLUI", "sb_skinned", 0);
+ cfg::dat.bUsePerProto = db_get_b(0, "CLCExt", "useperproto", 0);
+ cfg::dat.bOverridePerStatusColors = db_get_b(0, "CLCExt", "override_status", 0);
+ cfg::dat.bRowSpacing = db_get_b(0, "CLC", "RowGap", 0);
+ cfg::dat.bApplyIndentToBg = db_get_b(0, "CLCExt", "applyindentbg", 0);
+ cfg::dat.bWallpaperMode = db_get_b(0, "CLUI", "UseBkSkin", 1);
+ cfg::dat.bClipBorder = db_get_b(0, "CLUI", "clipborder", 0);
+ cfg::dat.cornerRadius = db_get_b(0, "CLCExt", "CornerRad", 6);
+ cfg::dat.gapBetweenFrames = (uint8_t)db_get_dw(0, "CLUIFrames", "GapBetweenFrames", 1);
+ cfg::dat.bUseDCMirroring = db_get_b(0, "CLC", "MirrorDC", 0);
+ cfg::dat.bGroupAlign = db_get_b(0, "CLC", "GroupAlign", 0);
+ if (cfg::dat.hBrushColorKey)
+ DeleteObject(cfg::dat.hBrushColorKey);
+ cfg::dat.hBrushColorKey = CreateSolidBrush(RGB(255, 0, 255));
+ cfg::dat.bWantFastGradients = db_get_b(0, "CLCExt", "FastGradients", 0);
+ cfg::dat.titleBarHeight = db_get_b(0, "CLCExt", "frame_height", DEFAULT_TITLEBAR_HEIGHT);
+ cfg::dat.group_padding = db_get_dw(0, "CLCExt", "grp_padding", 0);
+}
+
+static RECT rcWindow = { 0 };
+
+static void sttProcessResize(HWND hwnd, NMCLISTCONTROL *nmc)
+{
+ RECT rcTree, rcWorkArea, rcOld;
+ int maxHeight, newHeight;
+ int winstyle, skinHeight = 0;
+
+ if (disableautoupd)
+ return;
+
+ if (!db_get_b(0, "CLUI", "AutoSize", 0))
+ return;
+
+ if (Docking_IsDocked(0, 0))
+ return;
+ if (hFrameContactTree == 0)
+ return;
+
+ maxHeight = db_get_b(0, "CLUI", "MaxSizeHeight", 75);
+ rcOld = rcWindow;
+
+ GetWindowRect(hwnd, &rcWindow);
+ GetWindowRect(g_clistApi.hwndContactTree, &rcTree);
+ winstyle = GetWindowLongPtr(g_clistApi.hwndContactTree, GWL_STYLE);
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
+ HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ rcWorkArea = mi.rcWork;
+
+ if (nmc->pt.y > (rcWorkArea.bottom - rcWorkArea.top)) {
+ nmc->pt.y = (rcWorkArea.bottom - rcWorkArea.top);
+ }
+
+ if (winstyle & CLS_SKINNEDFRAME) {
+ BOOL hasTitleBar = wndFrameCLC ? wndFrameCLC->TitleBar.ShowTitleBar : 0;
+ StatusItems_t *item = arStatusItems[(hasTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB : ID_EXTBKOWNEDFRAMEBORDER) - ID_STATUS_OFFLINE];
+ skinHeight = item->IGNORED ? 0 : item->MARGIN_BOTTOM + item->MARGIN_TOP;
+ }
+
+ newHeight = max(nmc->pt.y, 3) + 1 + ((winstyle & WS_BORDER) ? 2 : 0) + skinHeight + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top);
+ if (newHeight == (rcWindow.bottom - rcWindow.top) && show_on_first_autosize == FALSE)
+ return;
+
+ if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100)
+ newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100;
+ if (db_get_b(0, "CLUI", "AutoSizeUpward", 0)) {
+ rcWindow.top = rcWindow.bottom - newHeight;
+ if (rcWindow.top < rcWorkArea.top) rcWindow.top = rcWorkArea.top;
+ }
+ else {
+ rcWindow.bottom = rcWindow.top + newHeight;
+ if (rcWindow.bottom > rcWorkArea.bottom) rcWindow.bottom = rcWorkArea.bottom;
+ }
+ if (cfg::dat.szOldCTreeSize.cx != rcTree.right - rcTree.left) {
+ cfg::dat.szOldCTreeSize.cx = rcTree.right - rcTree.left;
+ return;
+ }
+ KillTimer(hwnd, TIMERID_AUTOSIZE);
+ SetTimer(hwnd, TIMERID_AUTOSIZE, 100, nullptr);
+}
+
+int CustomDrawScrollBars(NMCSBCUSTOMDRAW *nmcsbcd)
+{
+ switch (nmcsbcd->hdr.code) {
+ case NM_COOLSB_CUSTOMDRAW:
+ static HDC hdcScroll = nullptr;
+ static HBITMAP hbmScroll, hbmScrollOld;
+ static LONG scrollLeft, scrollRight, scrollHeight, scrollYmin, scrollYmax;
+
+ switch (nmcsbcd->dwDrawStage) {
+ case CDDS_PREPAINT:
+ if (cfg::dat.bSkinnedScrollbar) // XXX fix (verify skin items to be complete, otherwise don't draw
+ return CDRF_SKIPDEFAULT;
+ return CDRF_DODEFAULT;
+
+ case CDDS_POSTPAINT:
+ return 0;
+
+ case CDDS_ITEMPREPAINT:
+ HDC hdc = nmcsbcd->hdc;
+ StatusItems_t *item = nullptr, *arrowItem = nullptr;
+ UINT uItemID = ID_EXTBKSCROLLBACK;
+ HRGN rgn = nullptr;
+
+ RECT rc;
+ GetWindowRect(g_clistApi.hwndContactTree, &rc);
+
+ POINT pt;
+ pt.x = rc.left;
+ pt.y = rc.top;
+ ScreenToClient(g_clistApi.hwndContactList, &pt);
+ hdcScroll = hdc;
+ BitBlt(hdcScroll, nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right - nmcsbcd->rect.left,
+ nmcsbcd->rect.bottom - nmcsbcd->rect.top, cfg::dat.hdcBg, pt.x + nmcsbcd->rect.left, pt.y + nmcsbcd->rect.top, SRCCOPY);
+
+ switch (nmcsbcd->uItem) {
+ case HTSCROLL_UP:
+ case HTSCROLL_DOWN:
+ uItemID = (nmcsbcd->uState == CDIS_DEFAULT || nmcsbcd->uState == CDIS_DISABLED) ? ID_EXTBKSCROLLBUTTON :
+ (nmcsbcd->uState == CDIS_HOT ? ID_EXTBKSCROLLBUTTONHOVER : ID_EXTBKSCROLLBUTTONPRESSED);
+ break;
+ case HTSCROLL_PAGEGDOWN:
+ case HTSCROLL_PAGEGUP:
+ uItemID = nmcsbcd->uItem == HTSCROLL_PAGEGUP ? ID_EXTBKSCROLLBACK : ID_EXTBKSCROLLBACKLOWER;
+ rgn = CreateRectRgn(nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right, nmcsbcd->rect.bottom);
+ SelectClipRgn(hdcScroll, rgn);
+ break;
+ case HTSCROLL_THUMB:
+ uItemID = nmcsbcd->uState == CDIS_SELECTED ? ID_EXTBKSCROLLTHUMBPRESSED : ID_EXTBKSCROLLTHUMB;
+ break;
+ default:
+ break;
+ }
+
+ uItemID -= ID_STATUS_OFFLINE;
+ item = arStatusItems[uItemID];
+ if (!item->IGNORED) {
+ int alpha = nmcsbcd->uState == CDIS_DISABLED ? item->ALPHA - 50 : item->ALPHA;
+ DrawAlpha(hdcScroll, &nmcsbcd->rect, item->COLOR, alpha, item->COLOR2, item->COLOR2_TRANSPARENT,
+ item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ }
+ uint32_t dfcFlags = DFCS_FLAT | (nmcsbcd->uState == CDIS_DISABLED ? DFCS_INACTIVE :
+ (nmcsbcd->uState == CDIS_HOT ? DFCS_HOT : (nmcsbcd->uState == CDIS_SELECTED ? DFCS_PUSHED : 0)));
+
+ if (nmcsbcd->uItem == HTSCROLL_UP)
+ arrowItem = arStatusItems[ID_EXTBKSCROLLARROWUP - ID_STATUS_OFFLINE];
+ if (nmcsbcd->uItem == HTSCROLL_DOWN)
+ arrowItem = arStatusItems[ID_EXTBKSCROLLARROWDOWN - ID_STATUS_OFFLINE];
+ if (arrowItem && !arrowItem->IGNORED)
+ DrawAlpha(hdcScroll, &nmcsbcd->rect, arrowItem->COLOR, arrowItem->ALPHA, arrowItem->COLOR2, arrowItem->COLOR2_TRANSPARENT,
+ arrowItem->GRADIENT, arrowItem->CORNER, arrowItem->BORDERSTYLE, arrowItem->imageItem);
+ else if (arrowItem)
+ DrawFrameControl(hdcScroll, &nmcsbcd->rect, DFC_SCROLL, (nmcsbcd->uItem == HTSCROLL_UP ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | dfcFlags);
+
+ if (rgn) {
+ SelectClipRgn(hdcScroll, nullptr);
+ DeleteObject(rgn);
+ }
+ }
+ }
+ return 0;
+}
+
+static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact)
+{
+ if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (hContact == 0)
+ return 0;
+
+ if (item->dwFlags & BUTTON_PASSHCONTACTW)
+ *wParam = hContact;
+ else if (item->dwFlags & BUTTON_PASSHCONTACTL)
+ *lParam = hContact;
+ }
+ return 1;
+}
+
+static void ShowCLUI(HWND hwnd)
+{
+ int state = old_cliststate;
+ int onTop = g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT);
+
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
+
+ if (state == SETTING_STATE_NORMAL) {
+ SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
+ ShowWindow(g_clistApi.hwndContactList, SW_SHOWNORMAL);
+ SendMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
+ }
+ else if (state == SETTING_STATE_MINIMIZED) {
+ cfg::dat.forceResize = TRUE;
+ ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
+ }
+ else if (state == SETTING_STATE_HIDDEN) {
+ cfg::dat.forceResize = TRUE;
+ ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
+ }
+ SetWindowPos(g_clistApi.hwndContactList, onTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
+ DrawMenuBar(hwnd);
+ if (cfg::dat.autosize) {
+ SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
+ SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0);
+ }
+}
+
+static void GetButtonRect(HWND hwnd, RECT *rc)
+{
+ if (hwnd)
+ GetWindowRect(hwnd, rc);
+ else {
+ POINT pt;
+ GetCursorPos(&pt);
+ rc->bottom = rc->top = pt.y;
+ rc->left = rc->right = pt.x;
+ }
+}
+
+LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+
+ switch (msg) {
+ case WM_CREATE:
+ {
+ int flags = WS_CHILD | CCS_BOTTOM;
+ flags |= db_get_b(0, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0;
+ flags |= db_get_b(0, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0;
+ g_clistApi.hwndStatus = CreateWindow(STATUSCLASSNAME, nullptr, flags, 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr);
+ if (flags & WS_VISIBLE) {
+ ShowWindow(g_clistApi.hwndStatus, SW_SHOW);
+ SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0);
+ }
+ mir_subclassWindow(g_clistApi.hwndStatus, NewStatusBarWndProc);
+ SetClassLong(g_clistApi.hwndStatus, GCL_STYLE, GetClassLong(g_clistApi.hwndStatus, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
+ }
+ g_oldSize.cx = g_oldSize.cy = 0;
+ old_cliststate = g_plugin.getByte("State", SETTING_STATE_NORMAL);
+ g_plugin.setByte("State", SETTING_STATE_HIDDEN);
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VISIBLE);
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | WS_CLIPCHILDREN);
+ if (!cfg::dat.bFirstRun)
+ ConfigureEventArea();
+ ConfigureCLUIGeometry(0);
+ CluiProtocolStatusChanged(0, nullptr);
+
+ for (int i = ID_STATUS_OFFLINE; i <= ID_STATUS_MAX; i++)
+ statusNames[i - ID_STATUS_OFFLINE] = Clist_GetStatusModeDescription(i, 0);
+
+ //delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded)
+ if (cfg::dat.bLayeredHack) {
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | (WS_EX_LAYERED));
+ SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
+ }
+
+ if (cfg::dat.isTransparent) {
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ }
+ transparentFocus = 1;
+
+ TranslateMenu(GetMenu(hwnd));
+ PostMessage(hwnd, M_CREATECLC, 0, 0);
+ return FALSE;
+
+ case WM_NCCREATE:
+ {
+ LPCREATESTRUCT p = (LPCREATESTRUCT)lParam;
+ p->style &= ~(CS_HREDRAW | CS_VREDRAW);
+ }
+ break;
+
+ case M_CREATECLC: {
+ if (db_get_b(0, "CLUI", "useskin", 0))
+ IMG_LoadItems();
+ CreateButtonBar(hwnd);
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
+ {
+ LONG style;
+ uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_TOOLWINDOW);
+ ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
+ style = GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE);
+ if (windowStyle != SETTING_WINDOWSTYLE_DEFAULT) {
+ style |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
+ style &= ~WS_EX_APPWINDOW;
+ }
+ else {
+ style &= ~(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE);
+ if (g_plugin.getByte("AlwaysHideOnTB", 1))
+ style &= ~WS_EX_APPWINDOW;
+ else
+ style |= WS_EX_APPWINDOW;
+ }
+
+ SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, style);
+ ApplyCLUIBorderStyle();
+
+ SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE);
+ }
+
+ if (cfg::dat.bSkinnedButtonMode)
+ SetButtonToSkinned();
+ ConfigureFrame();
+ SetButtonStates();
+
+ CreateCLC();
+ cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
+
+ if (cfg::dat.bFullTransparent) {
+ if (g_CLUISkinnedBkColorRGB)
+ Tweak_It(g_CLUISkinnedBkColorRGB);
+ else if (cfg::dat.bClipBorder || (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME))
+ Tweak_It(RGB(255, 0, 255));
+ else
+ Tweak_It(cfg::clcdat->bkColour);
+ }
+
+ g_plugin.setByte("State", old_cliststate);
+
+ if (g_plugin.getByte("AutoApplyLastViewMode", 0)) {
+ DBVARIANT dbv = { 0 };
+ if (!g_plugin.getString("LastViewMode", &dbv)) {
+ if (mir_strlen(dbv.pszVal) > 2) {
+ if (db_get_dw(0, CLVM_MODULE, dbv.pszVal, -1) != 0xffffffff)
+ ApplyViewMode((char *)dbv.pszVal);
+ }
+ db_free(&dbv);
+ }
+ }
+ if (!cfg::dat.autosize)
+ ShowCLUI(hwnd);
+ else {
+ show_on_first_autosize = TRUE;
+ RecalcScrollBar(g_clistApi.hwndContactTree, cfg::clcdat);
+ }
+ return 0;
+ }
+ case WM_ERASEBKGND:
+ return TRUE;
+ /*
+ if (cfg::dat.bSkinnedButtonMode)
+ return TRUE;
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ */
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ RECT rcFrame, rcClient;
+ HDC hdc;
+ HRGN rgn = nullptr;
+ HDC hdcReal = BeginPaint(hwnd, &ps);
+
+ if (during_sizing)
+ rcClient = rcWPC;
+ else
+ GetClientRect(hwnd, &rcClient);
+ CopyRect(&rc, &rcClient);
+
+ if (!cfg::dat.hdcBg || rc.right > cfg::dat.dcSize.cx || rc.bottom + cfg::dat.statusBarHeight > cfg::dat.dcSize.cy) {
+ RECT rcWorkArea;
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
+ HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ rcWorkArea = mi.rcWork;
+
+ cfg::dat.dcSize.cy = max(rc.bottom + cfg::dat.statusBarHeight, rcWorkArea.bottom - rcWorkArea.top);
+ cfg::dat.dcSize.cx = max(rc.right, (rcWorkArea.right - rcWorkArea.left) / 2);
+
+ if (cfg::dat.hdcBg) {
+ SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
+ DeleteObject(cfg::dat.hbmBg);
+ DeleteDC(cfg::dat.hdcBg);
+ }
+ cfg::dat.hdcBg = CreateCompatibleDC(hdcReal);
+ cfg::dat.hbmBg = CreateCompatibleBitmap(hdcReal, cfg::dat.dcSize.cx, cfg::dat.dcSize.cy);
+ cfg::dat.hbmBgOld = reinterpret_cast<HBITMAP>(SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBg));
+ }
+
+ if (cfg::shutDown) {
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+
+ hdc = cfg::dat.hdcBg;
+
+ CopyRect(&rcFrame, &rcClient);
+ if (g_CLUISkinnedBkColor) {
+ if (cfg::dat.fOnDesktop) {
+ HDC dc = GetDC(nullptr);
+ RECT rcWin;
+
+ GetWindowRect(hwnd, &rcWin);
+ BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, dc, rcWin.left, rcWin.top, SRCCOPY);
+ ReleaseDC(nullptr, dc);
+ }
+ else FillRect(hdc, &rcClient, g_CLUISkinnedBkColor);
+ }
+
+ if (cfg::dat.bClipBorder != 0 || cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) {
+ int docked = Clist_IsDocked();
+ int clip = cfg::dat.bClipBorder;
+
+ if (!g_CLUISkinnedBkColor)
+ FillRect(hdc, &rcClient, cfg::dat.hBrushColorKey);
+ if (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME)
+ rgn = CreateRoundRectRgn(clip, docked ? 0 : clip, rcClient.right - clip + 1, rcClient.bottom - (docked ? 0 : clip - 1), 8 + clip, 8 + clip);
+ else
+ rgn = CreateRectRgn(clip, docked ? 0 : clip, rcClient.right - clip, rcClient.bottom - (docked ? 0 : clip));
+ SelectClipRgn(hdc, rgn);
+ }
+
+ if (g_CLUIImageItem) {
+ IMG_RenderImageItem(hdc, g_CLUIImageItem, &rcFrame);
+ cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
+ ClientToScreen(hwnd, &cfg::dat.ptW);
+ goto skipbg;
+ }
+
+ if (cfg::dat.bWallpaperMode)
+ FillRect(hdc, &rcClient, cfg::dat.hBrushCLCBk);
+ else
+ FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE));
+
+ rcFrame.left += (cfg::dat.bCLeft - 1);
+ rcFrame.right -= (cfg::dat.bCRight - 1);
+ rcFrame.bottom++;
+ rcFrame.bottom -= cfg::dat.statusBarHeight;
+ rcFrame.top += (cfg::dat.topOffset - 1);
+
+ if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
+ if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
+ InflateRect(&rcFrame, -1, -1);
+ if (cfg::dat.bmpBackground)
+ BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
+ cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
+ ClientToScreen(hwnd, &cfg::dat.ptW);
+ }
+ InflateRect(&rcFrame, 1, 1);
+ if (cfg::dat.bSkinnedButtonMode)
+ rcFrame.bottom -= (cfg::dat.bottomOffset);
+ DrawEdge(hdc, &rcFrame, BDR_SUNKENOUTER, BF_RECT);
+ }
+ else if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
+ if (cfg::dat.bmpBackground)
+ BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
+ cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
+ ClientToScreen(hwnd, &cfg::dat.ptW);
+ }
+skipbg:
+ BitBlt(hdcReal, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdc, 0, 0, SRCCOPY);
+ if (rgn) {
+ SelectClipRgn(hdc, nullptr);
+ DeleteObject(rgn);
+ }
+ EndPaint(hwnd, &ps);
+ }
+ return 0;
+
+ case WM_ENTERSIZEMOVE:
+ {
+ POINT pt = { 0 };
+
+ GetWindowRect(hwnd, &g_PreSizeRect);
+ GetClientRect(hwnd, &rc);
+ ClientToScreen(hwnd, &pt);
+ g_CLUI_x_off = pt.x - g_PreSizeRect.left;
+ g_CLUI_y_off = pt.y - g_PreSizeRect.top;
+ pt.x = rc.right;
+ ClientToScreen(hwnd, &pt);
+ g_CLUI_x1_off = g_PreSizeRect.right - pt.x;
+ pt.x = 0;
+ pt.y = rc.bottom;
+ ClientToScreen(hwnd, &pt);
+ g_CLUI_y1_off = g_PreSizeRect.bottom - pt.y;
+ }
+ break;
+
+ case WM_EXITSIZEMOVE:
+ PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ break;
+
+ case WM_SIZING:
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ if (Docking_IsDocked(0, 0))
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ if (g_clistApi.hwndContactList != nullptr) {
+ WINDOWPOS *wp = (WINDOWPOS *)lParam;
+ if (!wp || (wp->flags & SWP_NOSIZE))
+ return FALSE;
+
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ during_sizing = true;
+
+ new_window_rect.left = 0;
+ new_window_rect.right = wp->cx - (g_CLUI_x_off + g_CLUI_x1_off);
+ new_window_rect.top = 0;
+ new_window_rect.bottom = wp->cy - g_CLUI_y_off - g_CLUI_y1_off;
+
+ if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
+ RECT rcStatus;
+ SetWindowPos(g_clistApi.hwndStatus, nullptr, 0, new_window_rect.bottom - 20, new_window_rect.right, 20, SWP_NOZORDER);
+ GetWindowRect(g_clistApi.hwndStatus, &rcStatus);
+ cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
+ if (wp->cx != g_oldSize.cx)
+ SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
+ RedrawWindow(g_clistApi.hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ }
+ else
+ cfg::dat.statusBarHeight = 0;
+
+ SizeFramesByWindowRect(&new_window_rect);
+ dock_prevent_moving = 0;
+ LayoutButtons(hwnd, &new_window_rect);
+ dock_prevent_moving = 1;
+ g_oldPos.x = wp->x;
+ g_oldPos.y = wp->y;
+ g_oldSize.cx = wp->cx;
+ g_oldSize.cy = wp->cy;
+ rcWPC = new_window_rect;
+ }
+ during_sizing = false;
+ return 0;
+
+ case WM_SIZE:
+ if ((wParam == 0 && lParam == 0) || Docking_IsDocked(0, 0)) {
+
+ if (IsZoomed(hwnd))
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ if (g_clistApi.hwndContactList != nullptr) {
+ SendMessage(hwnd, WM_ENTERSIZEMOVE, 0, 0);
+ GetWindowRect(hwnd, &rc);
+ WINDOWPOS wp = {};
+ wp.cx = rc.right - rc.left;
+ wp.cy = rc.bottom - rc.top;
+ wp.x = rc.left;
+ wp.y = rc.top;
+ wp.flags = 0;
+ SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
+ SendMessage(hwnd, WM_EXITSIZEMOVE, 0, 0);
+ }
+ }
+
+ case WM_MOVE:
+ if (!IsIconic(hwnd)) {
+ GetWindowRect(hwnd, &rc);
+
+ if (!Docking_IsDocked(0, 0)) {
+ cluiPos.bottom = (uint32_t)(rc.bottom - rc.top);
+ cluiPos.left = rc.left;
+ cluiPos.top = rc.top;
+ }
+ cluiPos.right = rc.right - rc.left;
+ if (cfg::dat.realTimeSaving) {
+ GetWindowRect(hwnd, &rc);
+
+ // if docked, dont remember pos (except for width)
+ if (!Clist_IsDocked()) {
+ g_plugin.setDword("Height", (uint32_t)(rc.bottom - rc.top));
+ g_plugin.setDword("x", (uint32_t)rc.left);
+ g_plugin.setDword("y", (uint32_t)rc.top);
+ }
+ g_plugin.setDword("Width", (uint32_t)(rc.right - rc.left));
+ }
+ }
+ return TRUE;
+
+ case WM_SETFOCUS:
+ SetFocus(g_clistApi.hwndContactTree);
+ return 0;
+
+ case CLUIINTM_REMOVEFROMTASKBAR: {
+ uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
+ if (windowStyle == SETTING_WINDOWSTYLE_DEFAULT && g_plugin.getByte("AlwaysHideOnTB", 0))
+ RemoveFromTaskBar(hwnd);
+ return 0;
+ }
+ case WM_ACTIVATE:
+ if (g_fading_active) {
+ if (wParam != WA_INACTIVE && cfg::dat.isTransparent)
+ transparentFocus = 1;
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ if (wParam == WA_INACTIVE) {
+ if ((HWND)wParam != hwnd)
+ if (cfg::dat.isTransparent)
+ if (transparentFocus)
+ SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
+ }
+ else {
+ if (cfg::dat.isTransparent) {
+ KillTimer(hwnd, TM_AUTOALPHA);
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ transparentFocus = 1;
+ }
+ SetWindowPos(g_clistApi.hwndContactList, g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
+ }
+ PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_SETCURSOR:
+ if (cfg::dat.isTransparent) {
+ if (!transparentFocus && GetForegroundWindow() != hwnd) {
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ transparentFocus = 1;
+ SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_NCHITTEST: {
+ LRESULT result;
+ RECT r;
+ POINT pt;
+ int clip = cfg::dat.bClipBorder;
+
+ GetWindowRect(hwnd, &r);
+ GetCursorPos(&pt);
+ if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6 && !db_get_b(0, "CLUI", "AutoSize", 0)) {
+ if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
+ return HTBOTTOM;
+ if (pt.x < r.left + clip + 10)
+ return HTBOTTOMLEFT;
+ if (pt.x > r.right - clip - 10)
+ return HTBOTTOMRIGHT;
+ }
+ else if (pt.y >= r.top && pt.y <= r.top + 3 && !db_get_b(0, "CLUI", "AutoSize", 0)) {
+ if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
+ return HTTOP;
+ if (pt.x < r.left + clip + 10)
+ return HTTOPLEFT;
+ if (pt.x > r.right - clip - 10)
+ return HTTOPRIGHT;
+ }
+ else if (pt.x >= r.left && pt.x <= r.left + clip + 6)
+ return HTLEFT;
+ else if (pt.x >= r.right - clip - 6 && pt.x <= r.right)
+ return HTRIGHT;
+
+ result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
+ if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT || result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT)
+ if (cfg::dat.autosize)
+ return HTCLIENT;
+ return result;
+ }
+
+ case WM_TIMER:
+ if (wParam == TM_AUTOALPHA) {
+ int inwnd;
+
+ if (GetForegroundWindow() == hwnd) {
+ KillTimer(hwnd, TM_AUTOALPHA);
+ inwnd = 1;
+ }
+ else {
+ POINT pt;
+ HWND hwndPt;
+ pt.x = (short)LOWORD(GetMessagePos());
+ pt.y = (short)HIWORD(GetMessagePos());
+ hwndPt = WindowFromPoint(pt);
+ inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd);
+ }
+ if (inwnd != transparentFocus) {
+ //change
+ transparentFocus = inwnd;
+ if (transparentFocus)
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ else
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.autoalpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ }
+ if (!transparentFocus)
+ KillTimer(hwnd, TM_AUTOALPHA);
+ }
+ else if (wParam == TIMERID_AUTOSIZE) {
+ KillTimer(hwnd, wParam);
+ SetWindowPos(hwnd, nullptr, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+ PostMessage(hwnd, WM_SIZE, 0, 0);
+ PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ }
+ return TRUE;
+
+ case WM_SHOWWINDOW:
+ {
+ static int noRecurse = 0;
+ uint32_t thisTick, startTick;
+ int sourceAlpha, destAlpha;
+
+ if (cfg::dat.forceResize && wParam != SW_HIDE) {
+ cfg::dat.forceResize = FALSE;
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ }
+ PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
+
+ if (lParam)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ if (noRecurse)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ if (!cfg::dat.fadeinout)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ g_fading_active = 1;
+
+ if (wParam) {
+ sourceAlpha = 0;
+ destAlpha = cfg::dat.isTransparent ? cfg::dat.alpha : 255;
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? (COLORREF)cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)sourceAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ noRecurse = 1;
+ ShowWindow(hwnd, SW_SHOW);
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ noRecurse = 0;
+ }
+ else {
+ sourceAlpha = cfg::dat.isTransparent ? (transparentFocus ? cfg::dat.alpha : cfg::dat.autoalpha) : 255;
+ destAlpha = 0;
+ }
+ for (startTick = GetTickCount();;) {
+ thisTick = GetTickCount();
+ if (thisTick >= startTick + 200) {
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)destAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ g_fading_active = 0;
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)(sourceAlpha + (destAlpha - sourceAlpha) * (int)(thisTick - startTick) / 200), LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ }
+ }
+
+ case WM_SYSCOMMAND:
+ {
+ uint8_t bWindowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
+ if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle) {
+ if (wParam == SC_RESTORE) {
+ CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ SendMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
+ g_plugin.setByte("State", SETTING_STATE_NORMAL);
+ break;
+ }
+ }
+
+ if (wParam == SC_MAXIMIZE)
+ return 0;
+
+ if (wParam == SC_MINIMIZE) {
+ if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle && !g_plugin.getByte("AlwaysHideOnTB", 0)) {
+ g_plugin.setByte("State", SETTING_STATE_MINIMIZED);
+ break;
+ }
+ g_clistApi.pfnShowHide();
+ return 0;
+ }
+ if (wParam == SC_RESTORE) {
+ g_clistApi.pfnShowHide();
+ return 0;
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_COMMAND:
+ {
+ uint32_t dwOldFlags = cfg::dat.dwFlags;
+ if (HIWORD(wParam) == BN_CLICKED && lParam != 0) {
+ if (LOWORD(wParam) == IDC_TBFIRSTUID - 1)
+ break;
+
+ else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling
+ ButtonItem *item = g_ButtonItems;
+ WPARAM wwParam = 0;
+ LPARAM llParam = 0;
+ MCONTACT hContact = 0;
+ ClcContact *contact = nullptr;
+ int serviceFailure = FALSE;
+
+ if (cfg::clcdat) {
+ g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
+ if (contact && contact->type == CLCIT_CONTACT)
+ hContact = contact->hContact;
+ }
+ while (item) {
+ if (item->uId == (uint32_t)LOWORD(wParam)) {
+ int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
+
+ if (item->dwFlags & BUTTON_ISSERVICE) {
+ if (ServiceExists(item->szService) && contactOK)
+ CallService(item->szService, wwParam, llParam);
+ else if (contactOK)
+ serviceFailure = TRUE;
+ }
+ else if (item->dwFlags & BUTTON_ISPROTOSERVICE && cfg::clcdat) {
+ if (contactOK) {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (ProtoServiceExists(szProto, item->szService))
+ CallProtoService(szProto, item->szService, wwParam, llParam);
+ else
+ serviceFailure = TRUE;
+ }
+ }
+ else if (item->dwFlags & BUTTON_ISDBACTION) {
+ uint8_t *pValue;
+ char *szModule = item->szModule;
+ char *szSetting = item->szSetting;
+ MCONTACT finalhContact = 0;
+
+ if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) {
+ contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
+ if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = Proto_GetBaseAccountName(hContact);
+ finalhContact = hContact;
+ }
+ else
+ contactOK = 1;
+
+ if (contactOK) {
+ BOOL fDelete = FALSE;
+
+ if (item->dwFlags & BUTTON_ISTOGGLE) {
+ BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED);
+
+ pValue = fChecked ? item->bValueRelease : item->bValuePush;
+ if (fChecked && pValue[0] == 0)
+ fDelete = TRUE;
+ }
+ else
+ pValue = item->bValuePush;
+
+ if (fDelete)
+ db_unset(finalhContact, szModule, szSetting);
+ else {
+ switch (item->type) {
+ case DBVT_BYTE:
+ db_set_b(finalhContact, szModule, szSetting, pValue[0]);
+ break;
+ case DBVT_WORD:
+ db_set_w(finalhContact, szModule, szSetting, *((uint16_t *)&pValue[0]));
+ break;
+ case DBVT_DWORD:
+ db_set_dw(finalhContact, szModule, szSetting, *((uint32_t *)&pValue[0]));
+ break;
+ case DBVT_ASCIIZ:
+ db_set_s(finalhContact, szModule, szSetting, (char *)pValue);
+ break;
+ }
+ }
+ }
+ else if (item->dwFlags & BUTTON_ISTOGGLE)
+ SendMessage(item->hWnd, BM_SETCHECK, 0, 0);
+ }
+ if (!contactOK)
+ MessageBox(nullptr, TranslateT("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat."), TranslateT("Parameter mismatch"), MB_OK);
+ if (serviceFailure) {
+ wchar_t szError[512];
+ mir_snwprintf(szError, TranslateT("The service %S specified by the %S button definition was not found. You may need to install additional plugins."), item->szService, item->szName);
+ MessageBox(nullptr, szError, TranslateT("Service failure"), MB_OK);
+ }
+ break;
+ }
+ item = item->nextItem;
+ }
+ goto buttons_done;
+ }
+
+ switch (LOWORD(wParam)) {
+ case IDC_TBMENU:
+ case IDC_TBTOPMENU:
+ case IDC_STBTOPMENU:
+ GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
+ TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBMENU ? rc.top : rc.bottom, 0, hwnd, nullptr);
+ return 0;
+
+ case IDC_TBTOPSTATUS:
+ case IDC_STBTOPSTATUS:
+ case IDC_TBGLOBALSTATUS:
+ GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
+ TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBGLOBALSTATUS ? rc.top : rc.bottom, 0, hwnd, nullptr);
+ return 0;
+
+ case IDC_TBSOUND:
+ case IDC_STBSOUND:
+ cfg::dat.soundsOff = !cfg::dat.soundsOff;
+ db_set_b(0, "CLUI", "NoSounds", (uint8_t)cfg::dat.soundsOff);
+ db_set_b(0, "Skin", "UseSound", (uint8_t)(cfg::dat.soundsOff ? 0 : 1));
+ return 0;
+
+ case IDC_TBSELECTVIEWMODE:
+ case IDC_STBSELECTVIEWMODE:
+ SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_SELECTMODE, lParam);
+ break;
+ case IDC_TBCLEARVIEWMODE:
+ case IDC_STBCLEARVIEWMODE:
+ SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_RESETMODES, lParam);
+ break;
+ case IDC_TBCONFIGUREVIEWMODE:
+ case IDC_STBCONFIGUREVIEWMODE:
+ SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_CONFIGUREMODES, lParam);
+ break;
+ case IDC_TBFINDANDADD:
+ case IDC_STBFINDANDADD:
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ return 0;
+ case IDC_TBACCOUNTS:
+ case IDC_STBACCOUNTS:
+ CallService(MS_PROTO_SHOWACCMGR, 0, 0);
+ break;
+ case IDC_TBOPTIONS:
+ case IDC_STBOPTIONS:
+ CallService("Options/OptionsCommand", 0, 0);
+ return 0;
+ }
+ }
+ else if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_MAINMENU, NULL))
+ return 0;
+
+buttons_done:
+ switch (LOWORD(wParam)) {
+ case ID_TRAY_EXIT:
+ cfg::shutDown = 1;
+ if (Miranda_OkToExit())
+ DestroyWindow(hwnd);
+ break;
+ case ID_TRAY_HIDE:
+ case IDC_TBMINIMIZE:
+ case IDC_STBMINIMIZE:
+ g_clistApi.pfnShowHide();
+ break;
+ case POPUP_NEWGROUP:
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0);
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETUSEGROUPS, 1, 0);
+ Clist_GroupCreate(NULL, nullptr);
+ break;
+ case POPUP_HIDEOFFLINE:
+ case IDC_TBHIDEOFFLINE:
+ case IDC_STBHIDEOFFLINE:
+ g_clistApi.pfnSetHideOffline(-1);
+ break;
+ case POPUP_HIDEOFFLINEROOT:
+ CallService(MS_CLIST_TOGGLEHIDEOFFLINEROOT, 0, 0);
+ break;
+ case POPUP_HIDEEMPTYGROUPS:
+ CallService(MS_CLIST_TOGGLEEMPTYGROUPS, 0, 0);
+ break;
+ case IDC_TBHIDEGROUPS:
+ case IDC_STBHIDEGROUPS:
+ case POPUP_DISABLEGROUPS:
+ ClcSetButtonState(IDC_TBHIDEGROUPS, CallService(MS_CLIST_TOGGLEGROUPS, 0, 0));
+ SetButtonStates();
+ break;
+ case POPUP_HIDEMIRANDA:
+ g_clistApi.pfnShowHide();
+ break;
+ case POPUP_SHOWMETAICONS:
+ cfg::dat.dwFlags ^= CLUI_USEMETAICONS;
+ Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
+ break;
+ case POPUP_FRAME:
+ cfg::dat.dwFlags ^= CLUI_FRAME_CLISTSUNKEN;
+ break;
+ case POPUP_BUTTONS:
+ cfg::dat.dwFlags ^= CLUI_FRAME_SHOWBOTTOMBUTTONS;
+ break;
+ case POPUP_SHOWSTATUSICONS:
+ cfg::dat.dwFlags ^= CLUI_FRAME_STATUSICONS;
+ break;
+ }
+ if (dwOldFlags != cfg::dat.dwFlags) {
+ InvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE);
+ db_set_dw(0, "CLUI", "Frameflags", cfg::dat.dwFlags);
+ if ((dwOldFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN)) != (cfg::dat.dwFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN))) {
+ ConfigureFrame();
+ ConfigureCLUIGeometry(1);
+ }
+ ConfigureEventArea();
+ PostMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
+ PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
+ }
+ }
+ return FALSE;
+
+ case WM_LBUTTONDOWN:
+ if (g_ButtonItems) {
+ POINT pt;
+ GetCursorPos(&pt);
+ return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ break;
+
+ case WM_DISPLAYCHANGE:
+ SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->hwndFrom == g_clistApi.hwndContactTree) {
+ switch (((LPNMHDR)lParam)->code) {
+ case CLN_LISTSIZECHANGE:
+ sttProcessResize(hwnd, (NMCLISTCONTROL *)lParam);
+ return FALSE;
+
+ case NM_CLICK:
+ {
+ NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam;
+ uint32_t hitFlags;
+ SendMessage(g_clistApi.hwndContactTree, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y));
+ if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0)
+ break;
+
+ if (db_get_b(0, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) {
+ POINT pt;
+ pt = nm->pt;
+ ClientToScreen(g_clistApi.hwndContactTree, &pt);
+ return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ }
+ return FALSE;
+ }
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ GetWindowRect(g_clistApi.hwndContactTree, &rc);
+ {
+ // x/y might be -1 if it was generated by a kb click
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ if (pt.x == -1 && pt.y == -1) {
+ // all this is done in screen-coords!
+ GetCursorPos(&pt);
+ // the mouse isnt near the window, so put it in the middle of the window
+ if (!PtInRect(&rc, pt)) {
+ pt.x = rc.left + (rc.right - rc.left) / 2;
+ pt.y = rc.top + (rc.bottom - rc.top) / 2;
+ }
+ }
+ if (PtInRect(&rc, pt)) {
+ HMENU hMenu = Menu_BuildGroupMenu();
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
+ Menu_DestroyNestedMenu(hMenu);
+ return 0;
+ }
+ GetWindowRect(g_clistApi.hwndStatus, &rc);
+ if (PtInRect(&rc, pt)) {
+ HMENU hMenu;
+ if (db_get_b(0, "CLUI", "SBarRightClk", 0))
+ hMenu = Menu_GetMainMenu();
+ else
+ hMenu = Menu_GetStatusMenu();
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ if (((LPMEASUREITEMSTRUCT)lParam)->itemData == MENU_MIRANDAMENU) {
+ ((LPMEASUREITEMSTRUCT)lParam)->itemWidth = g_cxsmIcon * 4 / 3;
+ ((LPMEASUREITEMSTRUCT)lParam)->itemHeight = 0;
+ return TRUE;
+ }
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+
+ if (hbmLockedPoint == nullptr) {
+ hdcLockedPoint = CreateCompatibleDC(dis->hDC);
+ hbmLockedPoint = CreateCompatibleBitmap(dis->hDC, 5, 5);
+ hbmOldLockedPoint = reinterpret_cast<HBITMAP>(SelectObject(hdcLockedPoint, hbmLockedPoint));
+ }
+ if (dis->hwndItem == g_clistApi.hwndStatus) {
+ ProtocolData *pd = (ProtocolData *)dis->itemData;
+ if (IsBadCodePtr((FARPROC)pd))
+ return TRUE;
+ if (cfg::shutDown)
+ return TRUE;
+
+ char *szProto = pd->RealName;
+ PROTOACCOUNT *pa = Proto_GetAccount(szProto);
+ if (pa == nullptr)
+ return TRUE;
+
+ int nParts = SendMessage(g_clistApi.hwndStatus, SB_GETPARTS, 0, 0);
+ SIZE textSize;
+ uint8_t showOpts = db_get_b(0, "CLUI", "SBarShow", 1);
+
+ SetBkMode(dis->hDC, TRANSPARENT);
+ int x = dis->rcItem.left;
+
+ if (showOpts & 1) {
+ HICON hIcon;
+
+ if (pa->iRealStatus >= ID_STATUS_CONNECTING && pa->iRealStatus < ID_STATUS_OFFLINE) {
+ char szBuffer[128];
+ mir_snprintf(szBuffer, "%s_conn", pd->RealName);
+ hIcon = IcoLib_GetIcon(szBuffer);
+ }
+ else if (cfg::dat.bShowXStatusOnSbar && pa->iRealStatus > ID_STATUS_OFFLINE) {
+ int xStatus;
+ CUSTOM_STATUS cst = { sizeof(cst) };
+ cst.flags = CSSF_MASK_STATUS;
+ cst.status = &xStatus;
+ if (ProtoServiceExists(pd->RealName, PS_GETCUSTOMSTATUSEX) && !CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cst) && xStatus > 0)
+ hIcon = (HICON)CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); // get OWN xStatus icon (if set)
+ else
+ hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus);
+ }
+ else hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus);
+
+ if (!(showOpts & 6) && cfg::dat.bEqualSections)
+ x = (dis->rcItem.left + dis->rcItem.right - 16) >> 1;
+ if (pd->protopos == 0)
+ x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
+ else if (pd->protopos == nParts - 1)
+ x -= (cfg::dat.bCRight / 2);
+ DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+ IcoLib_ReleaseIcon(hIcon);
+
+ if (db_get_b(0, "CLUI", "sbar_showlocked", 1)) {
+ if (pa->bIsLocked) {
+ hIcon = Skin_LoadIcon(SKINICON_OTHER_STATUS_LOCKED);
+ if (hIcon != nullptr) {
+ DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+ IcoLib_ReleaseIcon(hIcon);
+ }
+ }
+ }
+ x += 18;
+ }
+ else {
+ x += 2;
+ if (pd->protopos == 0)
+ x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
+ else if (pd->protopos == nParts - 1)
+ x -= (cfg::dat.bCRight / 2);
+ }
+
+ if (showOpts & 2) {
+ wchar_t szName[64];
+ wcsncpy_s(szName, pa->tszAccountName, _TRUNCATE);
+
+ if (mir_wstrlen(szName) < _countof(szName) - 1)
+ mir_wstrcat(szName, L" ");
+ GetTextExtentPoint32(dis->hDC, szName, (int)mir_wstrlen(szName), &textSize);
+ TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szName, (int)mir_wstrlen(szName));
+ x += textSize.cx;
+ }
+ if (showOpts & 4) {
+ wchar_t *szStatus = Clist_GetStatusModeDescription(pa->iRealStatus, 0);
+ GetTextExtentPoint32(dis->hDC, szStatus, (int)mir_wstrlen(szStatus), &textSize);
+ TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szStatus, (int)mir_wstrlen(szStatus));
+ }
+ }
+ else if (dis->CtlType == ODT_MENU) {
+ if (dis->itemData == MENU_MIRANDAMENU)
+ break;
+ return Menu_DrawItem(lParam);
+ }
+ }
+ return 0;
+
+ case WM_CLOSE:
+ if (SETTING_WINDOWSTYLE_DEFAULT == db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT) && !g_plugin.getByte("AlwaysHideOnTB", 0)) {
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
+ return 0;
+ }
+ g_clistApi.pfnShowHide();
+ return 0;
+
+ case CLUIINTM_REDRAW:
+ if (show_on_first_autosize) {
+ show_on_first_autosize = FALSE;
+ ShowCLUI(hwnd);
+ }
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+
+ case CLUIINTM_STATUSBARUPDATE:
+ CluiProtocolStatusChanged(0, nullptr);
+ return 0;
+
+ case WM_THEMECHANGED:
+ API::updateState();
+ break;
+
+ case WM_DESTROY:
+ if (cfg::dat.hdcBg) {
+ SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
+ DeleteObject(cfg::dat.hbmBg);
+ DeleteDC(cfg::dat.hdcBg);
+ cfg::dat.hdcBg = nullptr;
+ }
+ if (cfg::dat.bmpBackground) {
+ SelectObject(cfg::dat.hdcPic, cfg::dat.hbmPicOld);
+ DeleteDC(cfg::dat.hdcPic);
+ DeleteObject(cfg::dat.bmpBackground);
+ cfg::dat.bmpBackground = nullptr;
+ }
+ FreeProtocolData();
+ if (hdcLockedPoint) {
+ SelectObject(hdcLockedPoint, hbmOldLockedPoint);
+ DeleteObject(hbmLockedPoint);
+ DeleteDC(hdcLockedPoint);
+ }
+ // if this has not yet been set, do it now.
+ // indicates that clist is shutting down and prevents various things
+ // from happening at shutdown.
+ if (!cfg::shutDown)
+ cfg::shutDown = 1;
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)hFrameContactTree, 0);
+ break;
+ }
+
+ return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
+}
+
+#ifndef CS_DROPSHADOW
+#define CS_DROPSHADOW 0x00020000
+#endif
+
+static int MetaChanged(WPARAM wParam, LPARAM lParam)
+{
+ Clist_Broadcast(INTM_METACHANGEDEVENT, wParam, lParam);
+ return 0;
+}
+
+static INT_PTR CLN_ShowMainMenu(WPARAM, LPARAM)
+{
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr);
+ return 0;
+}
+
+static INT_PTR CLN_ShowStatusMenu(WPARAM, LPARAM)
+{
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr);
+ return 0;
+}
+
+#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu"
+#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu"
+
+void LoadCLUIModule(void)
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded);
+
+ WNDCLASS wndclass;
+ wndclass.style = 0;
+ wndclass.lpfnWndProc = EventAreaWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = g_plugin.getInst();
+ wndclass.hIcon = nullptr;
+ wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH)COLOR_3DFACE;
+ wndclass.lpszMenuName = nullptr;
+ wndclass.lpszClassName = L"EventAreaClass";
+ RegisterClass(&wndclass);
+
+ oldhideoffline = Clist::HideOffline;
+ cluiPos.left = g_plugin.getDword("x", 600);
+ cluiPos.top = g_plugin.getDword("y", 200);
+ cluiPos.right = g_plugin.getDword("Width", 150);
+ cluiPos.bottom = g_plugin.getDword("Height", 350);
+
+ LoadExtraIconModule();
+ LoadCLUIFramesModule();
+
+ CreateServiceFunction(MS_CLUI_SHOWMAINMENU, CLN_ShowMainMenu);
+ CreateServiceFunction(MS_CLUI_SHOWSTATUSMENU, CLN_ShowStatusMenu);
+
+ if (db_get_b(0, "CLUI", "FloaterMode", 0)) {
+ MessageBox(nullptr,
+ TranslateT("You need the FloatingContacts plugin, cause the embedded floating contacts were removed."),
+ TranslateT("Warning"), MB_OK | MB_ICONWARNING);
+ db_unset(0, "CLUI", "FloaterMode");
+ }
+
+ MF_InitCheck();
+}
+
+void OnCreateClc()
+{
+ HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged);
+ HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaChanged);
+
+ InitGroupMenus();
+ LoadExtBkSettingsFromDB();
+ PreCreateCLC(g_clistApi.hwndContactList);
+}
+
+struct
+{
+ const wchar_t *tszName;
+ int iMask;
+}
+static clistFontDescr[] =
+{
+ { LPGENW("Standard contacts"), FIDF_CLASSGENERAL },
+ { LPGENW("Online contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
+ { LPGENW("Offline contacts"), FIDF_CLASSGENERAL },
+ { LPGENW("Contacts which are 'not on list'"), FIDF_CLASSGENERAL },
+ { LPGENW("Groups"), FIDF_CLASSHEADER },
+ { LPGENW("Group member counts"), FIDF_CLASSHEADER },
+ { LPGENW("Dividers"), FIDF_CLASSSMALL },
+ { LPGENW("Offline contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
+ { LPGENW("Status mode"), FIDF_CLASSGENERAL },
+ { LPGENW("Frame titles"), FIDF_CLASSGENERAL },
+ { LPGENW("Event area"), FIDF_CLASSGENERAL },
+ { LPGENW("Contact list local time"), FIDF_CLASSGENERAL }
+};
+
+void FS_RegisterFonts()
+{
+ FontIDW fid = {};
+ wcsncpy_s(fid.group, LPGENW("Contact list"), _TRUNCATE);
+ strncpy_s(fid.dbSettingsGroup, "CLC", _TRUNCATE);
+ fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS | FIDF_APPENDNAME | FIDF_SAVEPOINTSIZE;
+
+ HDC hdc = GetDC(nullptr);
+ for (int i = 0; i < _countof(clistFontDescr); i++) {
+ LOGFONT lf;
+ Clist_GetFontSetting(i, &lf, &fid.deffontsettings.colour);
+ lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+
+ wcsncpy_s(fid.deffontsettings.szFace, lf.lfFaceName, _TRUNCATE);
+ fid.deffontsettings.charset = lf.lfCharSet;
+ fid.deffontsettings.size = (char)lf.lfHeight;
+ fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0);
+
+ fid.flags &= ~FIDF_CLASSMASK;
+ fid.flags |= clistFontDescr[i].iMask;
+
+ wcsncpy_s(fid.name, clistFontDescr[i].tszName, _TRUNCATE);
+
+ char idstr[10];
+ mir_snprintf(idstr, "Font%d", i);
+ strncpy_s(fid.setting, idstr, _TRUNCATE);
+ fid.order = i;
+ g_plugin.addFont(&fid);
+ }
+ ReleaseDC(nullptr, hdc);
+
+ // and colours
+ ColourIDW colourid = {};
+ colourid.order = 0;
+ strncpy_s(colourid.dbSettingsGroup, "CLC", _TRUNCATE);
+
+ strncpy_s(colourid.setting, "BkColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Background"), _TRUNCATE);
+ wcsncpy_s(colourid.group, LPGENW("Contact list"), _TRUNCATE);
+ colourid.defcolour = CLCDEFAULT_BKCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.setting, "SelTextColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Selected text"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = CLCDEFAULT_SELTEXTCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.setting, "HotTextColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Hottrack text"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = CLCDEFAULT_HOTTEXTCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.setting, "QuickSearchColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Quicksearch text"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = CLCDEFAULT_QUICKSEARCHCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.dbSettingsGroup, "CLUI", _TRUNCATE);
+ strncpy_s(colourid.setting, "clr_frameborder", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Embedded frames border"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = RGB(40, 40, 40);
+ g_plugin.addColor(&colourid);
+}
diff --git a/plugins/Clist_nicer/src/cluiframes.cpp b/plugins/Clist_nicer/src/cluiframes.cpp index 26dc766248..883fdbea8c 100644 --- a/plugins/Clist_nicer/src/cluiframes.cpp +++ b/plugins/Clist_nicer/src/cluiframes.cpp @@ -1,3050 +1,3050 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-03 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "cluiframes.h" -HFONT __fastcall ChangeToFont(HDC hdc, struct ClcData *dat, int id, int *fontHeight); - -extern HWND g_hwndViewModeFrame, g_hwndEventArea; -extern int mf_updatethread_running; - -extern HANDLE hThreadMFUpdate; - -void MF_UpdateThread(LPVOID); - -HANDLE hStatusBarShowToolTipEvent, hStatusBarHideToolTipEvent; -HANDLE g_hEventThread = nullptr; - -LOGFONT TitleBarLogFont = { 0 }; - -// we use dynamic frame list, -// but who wants so huge number of frames ?? -#define MAX_FRAMES 40 - -#define UNCOLLAPSED_FRAME_SIZE 0 - -// legacy menu support -#define frame_menu_lock 1 -#define frame_menu_visible 2 -#define frame_menu_showtitlebar 3 -#define frame_menu_floating 4 -#define frame_menu_skinned 5 - -static int UpdateTBToolTip(int framepos); -INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam); -int CLUIFrameResizeFloatingFrame(int framepos); -static int CLUIFramesReSort(); - -boolean FramesSysNotStarted = TRUE; -HPEN g_hPenCLUIFrames = nullptr; - -static SortData g_sd[MAX_FRAMES]; - -static HHOOK g_hFrameHook = nullptr; - -static int sortfunc(const void *a, const void *b) -{ - SortData *sd1, *sd2; - sd1 = (SortData *)a; - sd2 = (SortData *)b; - if (sd1->order > sd2->order) - return 1; - if (sd1->order < sd2->order) - return -1; - return 0; -} - -static FRAMEWND *Frames = nullptr; - -FRAMEWND *wndFrameCLC = nullptr, *wndFrameEventArea = nullptr, *wndFrameViewMode = nullptr; - -static int nFramescount = 0; -static int alclientFrame = -1;//for fast access to frame with alclient properties -static int NextFrameId = 100; - -static int TitleBarH = DEFAULT_TITLEBAR_HEIGHT; -static boolean resizing = FALSE; - -// menus -static FrameMenuHandles cont; -static LIST<TMO_IntMenuItem> g_frameMenus(10); - -// others -static int ContactListHeight; -static int LastStoreTick = 0; - -static int lbypos = -1; -static int oldframeheight = -1; -static int curdragbar = -1; -static mir_cs csFrameHook; - -static bool CLUIFramesFitInSize(void); -HWND hWndExplorerToolBar; -static int GapBetweenFrames = 1; - -static int RemoveItemFromList(int pos, FRAMEWND **lpFrames, int *FrameItemCount) -{ - memcpy(&((*lpFrames)[pos]), &((*lpFrames)[pos + 1]), sizeof(FRAMEWND) * (*FrameItemCount - pos - 1)); - (*FrameItemCount)--; - return 0; -} - -static int id2pos(int id) -{ - int i; - - if (FramesSysNotStarted) - return -1; - - for (i = 0; i < nFramescount; i++) { - if (Frames[i].id == id) - return i; - } - return -1; -} - -int __forceinline btoint(bool b) -{ - return (b ? 1 : 0); -} - -static FRAMEWND* FindFrameByWnd(HWND hwnd) -{ - if (hwnd == nullptr) - return nullptr; - - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (F.floating && F.ContainerWnd == hwnd) - return &F; - } - - return nullptr; -} - -static void DockThumbs(FRAMEWND *pThumbLeft, FRAMEWND *pThumbRight, BOOL) -{ - if ((pThumbRight->dockOpt.hwndLeft == nullptr) && (pThumbLeft->dockOpt.hwndRight == nullptr)) { - pThumbRight->dockOpt.hwndLeft = pThumbLeft->ContainerWnd; - pThumbLeft->dockOpt.hwndRight = pThumbRight->ContainerWnd; - } -} - -static void UndockThumbs(FRAMEWND *pThumb1, FRAMEWND *pThumb2) -{ - if ((pThumb1 == nullptr) || (pThumb2 == nullptr)) - return; - - if (pThumb1->dockOpt.hwndRight == pThumb2->ContainerWnd) - pThumb1->dockOpt.hwndRight = nullptr; - - if (pThumb1->dockOpt.hwndLeft == pThumb2->ContainerWnd) - pThumb1->dockOpt.hwndLeft = nullptr; - - if (pThumb2->dockOpt.hwndRight == pThumb1->ContainerWnd) - pThumb2->dockOpt.hwndRight = nullptr; - - if (pThumb2->dockOpt.hwndLeft == pThumb1->ContainerWnd) - pThumb2->dockOpt.hwndLeft = nullptr; -} - -BOOLEAN bMoveTogether; - -static void PositionThumb(FRAMEWND *pThumb, short nX, short nY) -{ - FRAMEWND *pCurThumb = &Frames[0]; - FRAMEWND *pDockThumb = pThumb; - FRAMEWND fakeMainWindow; - FRAMEWND fakeTaskBarWindow; - RECT rc; - RECT rcThumb; - RECT rcOld; - SIZE sizeScreen; - int nOffs = 10; - POINT pt; - RECT rcLeft; - RECT rcTop; - RECT rcRight; - RECT rcBottom; - int frmidx = 0; - - if (pThumb == nullptr) - return; - - sizeScreen.cx = GetSystemMetrics(SM_CXSCREEN); - sizeScreen.cy = GetSystemMetrics(SM_CYSCREEN); - - // Get thumb dimnsions - GetWindowRect(pThumb->ContainerWnd, &rcThumb); - int nWidth = rcThumb.right - rcThumb.left; - int nHeight = rcThumb.bottom - rcThumb.top; - - // Docking to the edges of the screen - int nNewX = nX < nOffs ? 0 : nX; - nNewX = nNewX >(sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX; - int nNewY = nY < nOffs ? 0 : nY; - nNewY = nNewY >(sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY; - - bool bLeading = pThumb->dockOpt.hwndRight != nullptr; - - if (bMoveTogether) { - UndockThumbs(pThumb, FindFrameByWnd(pThumb->dockOpt.hwndLeft)); - GetWindowRect(pThumb->ContainerWnd, &rcOld); - } - - memset(&fakeMainWindow, 0, sizeof(fakeMainWindow)); - fakeMainWindow.ContainerWnd = g_clistApi.hwndContactList; - fakeMainWindow.floating = TRUE; - - memset(&fakeTaskBarWindow, 0, sizeof(fakeTaskBarWindow)); - fakeTaskBarWindow.ContainerWnd = hWndExplorerToolBar; - fakeTaskBarWindow.floating = TRUE; - - while (pCurThumb != nullptr) { - if (pCurThumb->floating) { - - if (pCurThumb != pThumb) { - GetWindowRect(pThumb->ContainerWnd, &rcThumb); - OffsetRect(&rcThumb, nX - rcThumb.left, nY - rcThumb.top); - - GetWindowRect(pCurThumb->ContainerWnd, &rc); - - rcLeft.left = rc.left - nOffs; - rcLeft.top = rc.top - nOffs; - rcLeft.right = rc.left + nOffs; - rcLeft.bottom = rc.bottom + nOffs; - - rcTop.left = rc.left - nOffs; - rcTop.top = rc.top - nOffs; - rcTop.right = rc.right + nOffs; - rcTop.bottom = rc.top + nOffs; - - rcRight.left = rc.right - nOffs; - rcRight.top = rc.top - nOffs; - rcRight.right = rc.right + nOffs; - rcRight.bottom = rc.bottom + nOffs; - - rcBottom.left = rc.left - nOffs; - rcBottom.top = rc.bottom - nOffs; - rcBottom.right = rc.right + nOffs; - rcBottom.bottom = rc.bottom + nOffs; - - bool bDockedLeft = false, bDockedRight = false, bDocked = false; - - // Upper-left - pt.x = rcThumb.left; - pt.y = rcThumb.top; - - if (PtInRect(&rcRight, pt)) { - nNewX = rc.right; - bDocked = true; - } - - if (PtInRect(&rcBottom, pt)) { - nNewY = rc.bottom; - if (PtInRect(&rcLeft, pt)) - nNewX = rc.left; - } - - if (PtInRect(&rcTop, pt)) { - nNewY = rc.top; - bDockedLeft = bDocked; - } - - // Upper-right - pt.x = rcThumb.right; - pt.y = rcThumb.top; - bDocked = false; - - if (!bLeading && PtInRect(&rcLeft, pt)) { - if (!bDockedLeft) { - nNewX = rc.left - nWidth; - bDocked = true; - } - else if (rc.right == rcThumb.left) - bDocked = true; - } - - - if (PtInRect(&rcBottom, pt)) { - nNewY = rc.bottom; - if (PtInRect(&rcRight, pt)) - nNewX = rc.right - nWidth; - } - - if (!bLeading && PtInRect(&rcTop, pt)) { - nNewY = rc.top; - bDockedRight = bDocked; - } - - if (bMoveTogether) { - if (bDockedRight) - DockThumbs(pThumb, pCurThumb, TRUE); - - if (bDockedLeft) - DockThumbs(pCurThumb, pThumb, FALSE); - } - - // Lower-left - pt.x = rcThumb.left; - pt.y = rcThumb.bottom; - - if (PtInRect(&rcRight, pt)) - nNewX = rc.right; - - if (PtInRect(&rcTop, pt)) { - nNewY = rc.top - nHeight; - - if (PtInRect(&rcLeft, pt)) - nNewX = rc.left; - } - - - // Lower-right - pt.x = rcThumb.right; - pt.y = rcThumb.bottom; - - if (!bLeading && PtInRect(&rcLeft, pt)) - nNewX = rc.left - nWidth; - - if (!bLeading && PtInRect(&rcTop, pt)) { - nNewY = rc.top - nHeight; - - if (PtInRect(&rcRight, pt)) - nNewX = rc.right - nWidth; - } - } - } - - frmidx++; - if (pCurThumb->ContainerWnd == fakeTaskBarWindow.ContainerWnd) - break; - - if (pCurThumb->ContainerWnd == fakeMainWindow.ContainerWnd) { - pCurThumb = &fakeTaskBarWindow; - continue; - } - if (frmidx == nFramescount) { - pCurThumb = &fakeMainWindow; - continue; - } - pCurThumb = &Frames[frmidx]; - } - - // Adjust coords once again - nNewX = nNewX < nOffs ? 0 : nNewX; - nNewX = nNewX > (sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX; - nNewY = nNewY < nOffs ? 0 : nNewY; - nNewY = nNewY > (sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY; - SetWindowPos(pThumb->ContainerWnd, nullptr, nNewX, nNewY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); - - // OK, move all docked thumbs - if (bMoveTogether) { - pDockThumb = FindFrameByWnd(pDockThumb->dockOpt.hwndRight); - PositionThumb(pDockThumb, (short)(nNewX + nWidth), (short)nNewY); - } -} - -void GetBorderSize(HWND hwnd, RECT *rect) -{ - RECT wr, cr; - POINT pt1, pt2; - - GetWindowRect(hwnd, &wr); - GetClientRect(hwnd, &cr); - pt1.y = cr.top; - pt1.x = cr.left; - pt2.y = cr.bottom; - pt2.x = cr.right; - - ClientToScreen(hwnd, &pt1); - ClientToScreen(hwnd, &pt2); - - cr.top = pt1.y; - cr.left = pt1.x; - cr.bottom = pt2.y; - cr.right = pt2.x; - - rect->top = cr.top - wr.top; - rect->left = cr.left - wr.left; - rect->right = wr.right - cr.right; - rect->bottom = wr.bottom - cr.bottom; -} - -int DBLoadFrameSettingsAtPos(int pos, int Frameid) -{ - CMStringA buf; - - Frames[Frameid].collapsed = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), Frames[Frameid].collapsed); - - Frames[Frameid].Locked = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), Frames[Frameid].Locked); - Frames[Frameid].visible = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), Frames[Frameid].visible); - Frames[Frameid].TitleBar.ShowTitleBar = 0 != db_get_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), Frames[Frameid].TitleBar.ShowTitleBar); - - Frames[Frameid].height = db_get_w(0, CLUIFrameModule, buf.Format("Height%d", pos), Frames[Frameid].height); - Frames[Frameid].HeightWhenCollapsed = db_get_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), 0); - Frames[Frameid].align = db_get_w(0, CLUIFrameModule, buf.Format("Align%d", pos), Frames[Frameid].align); - - Frames[Frameid].FloatingPos.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatX%d", pos), 100, 0, 1024); - Frames[Frameid].FloatingPos.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatY%d", pos), 100, 0, 1024); - Frames[Frameid].FloatingSize.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatW%d", pos), 100, 0, 1024); - Frames[Frameid].FloatingSize.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatH%d", pos), 100, 0, 1024); - - Frames[Frameid].floating = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), 0); - Frames[Frameid].order = db_get_w(0, CLUIFrameModule, buf.Format("Order%d", pos), 0); - - Frames[Frameid].UseBorder = 0 != db_get_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), Frames[Frameid].UseBorder); - Frames[Frameid].Skinned = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned); - return 0; -} - -int DBStoreFrameSettingsAtPos(int pos, int Frameid) -{ - CMStringA buf; - - db_set_ws(0, CLUIFrameModule, buf.Format("Name%d", pos), Frames[Frameid].name); - //boolean - db_set_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), (uint8_t)btoint(Frames[Frameid].collapsed)); - db_set_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), (uint8_t)btoint(Frames[Frameid].Locked)); - db_set_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), (uint8_t)btoint(Frames[Frameid].visible)); - db_set_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), (uint8_t)btoint(Frames[Frameid].TitleBar.ShowTitleBar)); - - db_set_w(0, CLUIFrameModule, buf.Format("Height%d", pos), (uint16_t)Frames[Frameid].height); - db_set_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), (uint16_t)Frames[Frameid].HeightWhenCollapsed); - db_set_w(0, CLUIFrameModule, buf.Format("Align%d", pos), (uint16_t)Frames[Frameid].align); - //FloatingPos - db_set_w(0, CLUIFrameModule, buf.Format("FloatX%d", pos), (uint16_t)Frames[Frameid].FloatingPos.x); - db_set_w(0, CLUIFrameModule, buf.Format("FloatY%d", pos), (uint16_t)Frames[Frameid].FloatingPos.y); - db_set_w(0, CLUIFrameModule, buf.Format("FloatW%d", pos), (uint16_t)Frames[Frameid].FloatingSize.x); - db_set_w(0, CLUIFrameModule, buf.Format("FloatH%d", pos), (uint16_t)Frames[Frameid].FloatingSize.y); - - db_set_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), (uint8_t)btoint(Frames[Frameid].floating)); - db_set_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), (uint8_t)btoint(Frames[Frameid].UseBorder)); - db_set_w(0, CLUIFrameModule, buf.Format("Order%d", pos), (uint16_t)Frames[Frameid].order); - - db_set_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned); - return 0; -} - -int LocateStorePosition(int Frameid, int maxstored) -{ - if (Frames[Frameid].name == nullptr) return -1; - - for (int i = 0; i < maxstored; i++) { - char settingname[255]; - mir_snprintf(settingname, "Name%d", i); - ptrW frmname(db_get_wsa(0, CLUIFrameModule, settingname)); - if (frmname == NULL) continue; - if (mir_wstrcmpi(frmname, Frames[Frameid].name) == 0) - return i; - } - return -1; -} - -int CLUIFramesLoadFrameSettings(int Frameid) -{ - if (FramesSysNotStarted) return -1; - - if (Frameid < 0 || Frameid >= nFramescount) - return -1; - - int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1); - if (maxstored == -1) - return 0; - - int storpos = LocateStorePosition(Frameid, maxstored); - if (storpos == -1) - return 0; - - DBLoadFrameSettingsAtPos(storpos, Frameid); - return 0; -} - -int CLUIFramesStoreFrameSettings(int Frameid) -{ - if (FramesSysNotStarted) - return -1; - - if (Frameid < 0 || Frameid >= nFramescount) - return -1; - - int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1); - if (maxstored == -1) - maxstored = 0; - - int storpos = LocateStorePosition(Frameid, maxstored); - if (storpos == -1) { - storpos = maxstored; - maxstored++; - } - - DBStoreFrameSettingsAtPos(storpos, Frameid); - db_set_w(0, CLUIFrameModule, "StoredFrames", (uint16_t)maxstored); - return 0; -} - -int CLUIFramesStoreAllFrames() -{ - if (FramesSysNotStarted) - return -1; - - if (cfg::shutDown) - return -1; - - mir_cslock lck(csFrameHook); - for (int i = 0; i < nFramescount; i++) - CLUIFramesStoreFrameSettings(i); - return 0; -} - -// Get client frame -int CLUIFramesGetalClientFrame(void) -{ - if (FramesSysNotStarted) - return -1; - - if (alclientFrame != -1) { - /* this value could become invalid if RemoveItemFromList was called, - * so we double-check */ - if (alclientFrame < nFramescount) - if (Frames[alclientFrame].align == alClient) - return alclientFrame; - } - - for (int i = 0; i < nFramescount; i++) - if (Frames[i].align == alClient) { - alclientFrame = i; - return i; - } - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HGENMENU addFrameMenuItem(TMO_MenuItem *pmi, int frameid, bool bMain) -{ - HGENMENU res = (bMain) ? Menu_AddMainMenuItem(pmi) : Menu_AddContextFrameMenuItem(pmi); - if (pmi->pszService != nullptr) - Menu_ConfigureItem(res, MCI_OPT_EXECPARAM, frameid); - return res; -} - -HMENU CLUIFramesCreateMenuForFrame(int frameid, HGENMENU root, int popuppos, bool bMain) -{ - if (FramesSysNotStarted) - return nullptr; - - int framepos = id2pos(frameid); - FrameMenuHandles &fmh = (frameid == -1) ? cont : Frames[framepos].MenuHandles; - - CMenuItem mi((frameid == -1) ? &g_plugin : Frames[framepos].pPlugin); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA); - mi.root = root; - mi.position = popuppos++; - mi.name.a = LPGEN("&FrameTitle"); - mi.flags = CMIF_SYSTEM | CMIF_GRAYED; - fmh.MITitle = addFrameMenuItem(&mi, frameid, bMain); - - popuppos += 100000; - - mi.hIcolibItem = nullptr; - mi.position = popuppos++; - mi.name.a = LPGEN("&Visible"); - mi.flags = CMIF_SYSTEM | CMIF_CHECKED; - mi.pszService = MS_CLIST_FRAMES_SHFRAME; - fmh.MIVisible = addFrameMenuItem(&mi, frameid, bMain); - - mi.position = popuppos++; - mi.name.a = LPGEN("&Show title bar"); - mi.pszService = MS_CLIST_FRAMES_SHFRAMETITLEBAR; - fmh.MITBVisible = addFrameMenuItem(&mi, frameid, bMain); - - popuppos += 100000; - - mi.position = popuppos++; - mi.name.a = LPGEN("&Locked"); - mi.pszService = MS_CLIST_FRAMES_ULFRAME; - fmh.MILock = addFrameMenuItem(&mi, frameid, bMain); - - mi.position = popuppos++; - mi.name.a = LPGEN("&Collapsed"); - mi.pszService = MS_CLIST_FRAMES_UCOLLFRAME; - fmh.MIColl = addFrameMenuItem(&mi, frameid, bMain); - - // floating - mi.position = popuppos++; - mi.name.a = LPGEN("&Floating mode"); - mi.flags = CMIF_SYSTEM; - mi.pszService = "Set_Floating"; - fmh.MIFloating = addFrameMenuItem(&mi, frameid, bMain); - - popuppos += 100000; - - mi.position = popuppos++; - mi.name.a = LPGEN("&Border"); - mi.flags = CMIF_SYSTEM | CMIF_CHECKED; - mi.pszService = MS_CLIST_FRAMES_SETUNBORDER; - fmh.MIBorder = addFrameMenuItem(&mi, frameid, bMain); - - popuppos += 100000; - - mi.position = popuppos++; - mi.name.a = LPGEN("&Skinned frame"); - mi.pszService = MS_CLIST_FRAMES_SETSKINNED; - fmh.MISkinned = addFrameMenuItem(&mi, frameid, bMain); - - popuppos += 100000; - - // alignment root - mi.root = root; - mi.position = popuppos++; - mi.name.a = LPGEN("&Align"); - mi.flags = CMIF_SYSTEM; - mi.pszService = nullptr; - fmh.MIAlignRoot = addFrameMenuItem(&mi, frameid, bMain); - - // align top - mi.root = fmh.MIAlignRoot; - mi.position = popuppos++; - mi.name.a = LPGEN("&Top"); - mi.pszService = CLUIFRAMESSETALIGNALTOP; - fmh.MIAlignTop = addFrameMenuItem(&mi, frameid, bMain); - - // align client - mi.position = popuppos++; - mi.name.a = LPGEN("&Client"); - mi.pszService = CLUIFRAMESSETALIGNALCLIENT; - fmh.MIAlignClient = addFrameMenuItem(&mi, frameid, bMain); - - // align bottom - mi.position = popuppos++; - mi.name.a = LPGEN("&Bottom"); - mi.pszService = CLUIFRAMESSETALIGNALBOTTOM; - fmh.MIAlignBottom = addFrameMenuItem(&mi, frameid, bMain); - - // position root - mi.root = root; - mi.position = popuppos++; - mi.name.a = LPGEN("&Position"); - mi.pszService = nullptr; - mi.root = addFrameMenuItem(&mi, frameid, bMain); - - mi.position = popuppos++; - mi.name.a = LPGEN("&Up"); - mi.pszService = CLUIFRAMESMOVEUP; - addFrameMenuItem(&mi, frameid, bMain); - - mi.position = popuppos++; - mi.name.a = LPGEN("&Down"); - mi.pszService = CLUIFRAMESMOVEDOWN; - addFrameMenuItem(&mi, frameid, bMain); - return nullptr; -} - -static int CLUIFramesModifyContextMenuForFrame(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - mir_cslock lck(csFrameHook); - int pos = id2pos(wParam); - if (pos >= 0 && pos < nFramescount) { - FRAMEWND &p = Frames[pos]; - Menu_ModifyItem(cont.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name); - Menu_SetChecked(cont.MIVisible, p.visible); - Menu_SetChecked(cont.MILock, p.Locked); - Menu_SetChecked(cont.MITBVisible, p.TitleBar.ShowTitleBar); - Menu_SetChecked(cont.MIFloating, p.floating); - Menu_SetChecked(cont.MIBorder, p.UseBorder); - Menu_SetChecked(cont.MISkinned, p.Skinned); - Menu_SetChecked(cont.MIAlignTop, (p.align & alTop) != 0); - Menu_SetChecked(cont.MIAlignClient, (p.align & alClient) != 0); - Menu_SetChecked(cont.MIAlignBottom, (p.align & alBottom) != 0); - - Menu_SetChecked(cont.MIColl, !p.collapsed); - Menu_EnableItem(cont.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame()); - } - return 0; -} - -INT_PTR CLUIFramesModifyMainMenuItems(WPARAM frameId, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - mir_cslock lck(csFrameHook); - int pos = id2pos(frameId); - - if (pos >= 0 && pos < nFramescount) { - FRAMEWND &p = Frames[pos]; - Menu_ModifyItem(p.MenuHandles.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name); - - Menu_SetChecked(p.MenuHandles.MIVisible, p.visible); - Menu_SetChecked(p.MenuHandles.MILock, p.Locked); - Menu_SetChecked(p.MenuHandles.MITBVisible, p.TitleBar.ShowTitleBar); - Menu_SetChecked(p.MenuHandles.MIFloating, p.floating); - Menu_SetChecked(p.MenuHandles.MIBorder, p.UseBorder); - Menu_SetChecked(p.MenuHandles.MISkinned, p.Skinned); - - Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0); - Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alTop) != 0); - - Menu_SetChecked(p.MenuHandles.MIAlignClient, (p.align & alClient) != 0); - - Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0); - Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alBottom) != 0); - - Menu_SetChecked(p.MenuHandles.MIColl, !p.collapsed); - Menu_EnableItem(p.MenuHandles.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame()); - } - return 0; -} - -INT_PTR CLUIFramesGetFrameOptions(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) return -1; - - mir_cslock lck(csFrameHook); - int pos = id2pos(HIWORD(wParam)); - if (pos < 0 || pos >= nFramescount) - return -1; - - switch (LOWORD(wParam)) { - case FO_NAME: - return (INT_PTR)Frames[pos].name; - - case FO_TBNAME: - return (INT_PTR)Frames[pos].TitleBar.tbname; - - case FO_TBTIPNAME: - return (INT_PTR)Frames[pos].TitleBar.tooltip; - - case FO_TBSTYLE: - return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE); - - case FO_TBEXSTYLE: - return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE); - - case FO_ICON: - return (INT_PTR)Frames[pos].TitleBar.hicon; - - case FO_HEIGHT: - return (INT_PTR)Frames[pos].height; - - case FO_ALIGN: - return (INT_PTR)Frames[pos].align; - - case FO_FLOATING: - return (INT_PTR)Frames[pos].floating; - - case FO_FLAGS: - INT_PTR dwFlags = 0; - if (Frames[pos].visible) dwFlags |= F_VISIBLE; - if (!Frames[pos].collapsed) dwFlags |= F_UNCOLLAPSED; - if (Frames[pos].Locked) dwFlags |= F_LOCKED; - if (Frames[pos].TitleBar.ShowTitleBar) dwFlags |= F_SHOWTB; - if (Frames[pos].TitleBar.ShowTitleBarTip) dwFlags |= F_SHOWTBTIP; - if (Frames[pos].Skinned) dwFlags |= F_SKINNED; - if (!(GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE)&WS_BORDER)) dwFlags |= F_NOBORDER; - return dwFlags; - } - - return -1; -} - -INT_PTR CLUIFramesSetFrameOptions(WPARAM wParam, LPARAM lParam) -{ - int retval; // value to be returned - - if (FramesSysNotStarted) - return -1; - - mir_cslockfull lck(csFrameHook); - int pos = id2pos(HIWORD(wParam)); - if (pos < 0 || pos >= nFramescount) - return -1; - - switch (LOWORD(wParam) & ~FO_UNICODETEXT) { - case FO_FLAGS: - { - int flag = lParam; - LONG_PTR style; - - Frames[pos].dwFlags = flag; - Frames[pos].visible = FALSE; - if (flag & F_VISIBLE) Frames[pos].visible = TRUE; - - Frames[pos].collapsed = TRUE; - if (flag & F_UNCOLLAPSED) Frames[pos].collapsed = FALSE; - - Frames[pos].Locked = FALSE; - if (flag & F_LOCKED) Frames[pos].Locked = TRUE; - - Frames[pos].UseBorder = TRUE; - if (flag & F_NOBORDER) Frames[pos].UseBorder = FALSE; - - Frames[pos].TitleBar.ShowTitleBar = FALSE; - if (flag & F_SHOWTB) Frames[pos].TitleBar.ShowTitleBar = TRUE; - - Frames[pos].TitleBar.ShowTitleBarTip = FALSE; - if (flag & F_SHOWTBTIP) Frames[pos].TitleBar.ShowTitleBarTip = TRUE; - - SendMessage(Frames[pos].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[pos].TitleBar.ShowTitleBarTip, 0); - - style = GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE); - style |= WS_BORDER; - style |= CLS_SKINNEDFRAME; - - if (flag & F_NOBORDER) - style &= (~WS_BORDER); - - Frames[pos].Skinned = FALSE; - if (flag & F_SKINNED) - Frames[pos].Skinned = TRUE; - - if (!(flag & F_SKINNED)) - style &= ~CLS_SKINNEDFRAME; - - SetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE, (LONG_PTR)style); - SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, (LONG_PTR)style & ~(WS_VSCROLL | WS_HSCROLL)); - lck.unlock(); - - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - SetWindowPos(Frames[pos].TitleBar.hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - } - return 0; - - case FO_NAME: - if (lParam == 0) - return -1; - - mir_free(Frames[pos].name); - Frames[pos].name = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam); - return 0; - - case FO_TBNAME: - if (lParam == 0) - return -1; - - mir_free(Frames[pos].TitleBar.tbname); - Frames[pos].TitleBar.tbname = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam); - lck.unlock(); - - if (Frames[pos].floating && (Frames[pos].TitleBar.tbname != nullptr)) - SetWindowText(Frames[pos].ContainerWnd, Frames[pos].TitleBar.tbname); - return 0; - - case FO_TBTIPNAME: - if (lParam == 0) - return -1; - - mir_free(Frames[pos].TitleBar.tooltip); - Frames[pos].TitleBar.tooltip = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam); - UpdateTBToolTip(pos); - return 0; - - case FO_TBSTYLE: - SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, lParam); - return 0; - - case FO_TBEXSTYLE: - SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE, lParam); - return 0; - - case FO_ICON: - Frames[pos].TitleBar.hicon = (HICON)lParam; - return 0; - - case FO_HEIGHT: - if (lParam < 0) - return -1; - - if (Frames[pos].Skinned) { - int uID = (Frames[pos].TitleBar.ShowTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE : ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE); - lParam += (arStatusItems[uID]->MARGIN_BOTTOM + arStatusItems[uID]->MARGIN_TOP); - } - if (Frames[pos].collapsed) { - int oldHeight = Frames[pos].height; - retval = Frames[pos].height; - Frames[pos].height = lParam; - if (!CLUIFramesFitInSize()) - Frames[pos].height = retval; - retval = Frames[pos].height; - - if (Frames[pos].height != oldHeight) { - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - if (Frames[pos].Skinned) - RedrawWindow(Frames[pos].hWnd, nullptr, nullptr, RDW_FRAME | RDW_UPDATENOW | RDW_INVALIDATE); - } - } - else { - retval = Frames[pos].HeightWhenCollapsed; - Frames[pos].HeightWhenCollapsed = lParam; - if (!CLUIFramesFitInSize()) - Frames[pos].HeightWhenCollapsed = retval; - retval = Frames[pos].HeightWhenCollapsed; - } - return retval; - - case FO_FLOATING: - if (lParam < 0) - return -1; - else { - int id = Frames[pos].id; - Frames[pos].floating = !(lParam); - lck.unlock(); - - CLUIFrameSetFloat(id, 1);//lparam=1 use stored width and height - } - return wParam; - - case FO_ALIGN: - if (!(lParam&alTop || lParam&alBottom || lParam&alClient)) - return -1; - - if ((lParam&alClient) && (CLUIFramesGetalClientFrame() >= 0)) { //only one alClient frame possible - alclientFrame = -1;//recalc it - return -1; - } - Frames[pos].align = lParam; - return 0; - } - lck.unlock(); - - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - return -1; -} - -static INT_PTR CLUIFramesShowAll(WPARAM, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - for (int i = 0; i < nFramescount; i++) - Frames[i].visible = TRUE; - - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - return 0; -} - -INT_PTR CLUIFramesShowAllTitleBars(WPARAM, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - F.TitleBar.ShowTitleBar = TRUE; - SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - } - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; -} - -INT_PTR CLUIFramesHideAllTitleBars(WPARAM, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - F.TitleBar.ShowTitleBar = FALSE; - SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - } - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; -} - -INT_PTR CLUIFramesShowHideFrame(WPARAM frameId, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - int pos; - { - mir_cslock lck(csFrameHook); - pos = id2pos(frameId); - if (pos >= 0 && !mir_wstrcmp(Frames[pos].name, L"My contacts")) - Frames[pos].visible = 1; - else { - if (pos >= 0 && (int)pos < nFramescount) - Frames[pos].visible = !Frames[pos].visible; - if (Frames[pos].floating) - CLUIFrameResizeFloatingFrame(pos); - } - } - - if (!Frames[pos].floating) - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; -} - -INT_PTR CLUIFramesShowHideFrameTitleBar(WPARAM frameId, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - { - mir_cslock lck(csFrameHook); - int pos = id2pos(frameId); - if (pos >= 0 && (int)pos < nFramescount) { - Frames[pos].TitleBar.ShowTitleBar = !Frames[pos].TitleBar.ShowTitleBar; - SetWindowPos(Frames[pos].hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - } - } - - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; -} - -// lparam=-1 up ,1 down -INT_PTR CLUIFramesMoveUpDown(WPARAM frameId, LPARAM lParam) -{ - int i, tmpval; - - if (FramesSysNotStarted) - return -1; - - mir_cslockfull lck(csFrameHook); - int pos = id2pos(frameId); - if (pos < 0 || pos >= nFramescount) - return 0; - - int curalign = Frames[pos].align; - int v = 0; - memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES); - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (F.floating || (!F.visible) || (F.align != curalign)) - continue; - g_sd[v].order = F.order; - g_sd[v].realpos = i; - v++; - } - if (v == 0) - return 0; - - qsort(g_sd, v, sizeof(SortData), sortfunc); - for (i = 0; i < v; i++) { - if (g_sd[i].realpos == pos) { - if (lParam == -1) { - if (i < 1) break; - tmpval = Frames[g_sd[i - 1].realpos].order; - Frames[g_sd[i - 1].realpos].order = Frames[pos].order; - Frames[pos].order = tmpval; - break; - } - if (lParam == 1) { - if (i > v - 1) break; - tmpval = Frames[g_sd[i + 1].realpos].order; - Frames[g_sd[i + 1].realpos].order = Frames[pos].order; - Frames[pos].order = tmpval; - break; - } - } - } - lck.unlock(); - - CLUIFramesReSort(); - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0); - return 0; -} - -static INT_PTR CLUIFramesMoveUp(WPARAM frameId, LPARAM) -{ - return CLUIFramesMoveUpDown(frameId, -1); -} - -static INT_PTR CLUIFramesMoveDown(WPARAM frameId, LPARAM) -{ - return CLUIFramesMoveUpDown(frameId, 1); -} - -//lparam=alignment -INT_PTR CLUIFramesSetAlign(WPARAM frameId, LPARAM lParam) -{ - if (FramesSysNotStarted) return -1; - - CLUIFramesSetFrameOptions(MAKEWPARAM(FO_ALIGN, frameId), lParam); - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; -} - -INT_PTR CLUIFramesSetAlignalTop(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) return -1; - - return CLUIFramesSetAlign(wParam, alTop); -} - -INT_PTR CLUIFramesSetAlignalBottom(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) return -1; - - return CLUIFramesSetAlign(wParam, alBottom); -} - -INT_PTR CLUIFramesSetAlignalClient(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) return -1; - - return CLUIFramesSetAlign(wParam, alClient); -} - -//wparam=frameid -INT_PTR CLUIFramesLockUnlockFrame(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - mir_cslock lck(csFrameHook); - int pos = id2pos(wParam); - if (pos >= 0 && (int)pos < nFramescount) { - Frames[pos].Locked = !Frames[pos].Locked; - CLUIFramesStoreFrameSettings(pos); - } - return 0; -} - -//wparam=frameid -INT_PTR CLUIFramesSetUnSetBorder(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - HWND hw; - int FrameId, oldflags; - { - mir_cslock lck(csFrameHook); - FrameId = id2pos(wParam); - if (FrameId == -1) - return -1; - - oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0); - if (oldflags & F_NOBORDER) - oldflags &= (~F_NOBORDER); - else - oldflags |= F_NOBORDER; - - hw = Frames[FrameId].hWnd; - } - - CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags); - SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER); - return 0; -} - -//wparam=frameid -INT_PTR CLUIFramesSetUnSetSkinned(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - HWND hw; - int FrameId, oldflags; - { - mir_cslock lck(csFrameHook); - FrameId = id2pos(wParam); - if (FrameId == -1) - return -1; - - oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0); - if (oldflags & F_SKINNED) - oldflags &= ~F_SKINNED; - else - oldflags |= F_SKINNED; - - hw = Frames[FrameId].hWnd; - } - - CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags); - SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER); - return 0; -} - -//wparam=frameid -INT_PTR CLUIFramesCollapseUnCollapseFrame(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - TitleBarH = cfg::dat.titleBarHeight; - - mir_cslockfull lck(csFrameHook); - int FrameId = id2pos(wParam); - if (FrameId < 0 || FrameId >= nFramescount) - return -1; - - int oldHeight; - - // do not collapse/uncollapse client/locked/invisible frames - if (Frames[FrameId].align == alClient && !(Frames[FrameId].Locked || (!Frames[FrameId].visible) || Frames[FrameId].floating)) { - RECT rc; - if (Clist_IsDocked()) - return 0; - - if (db_get_b(0, "CLUI", "AutoSize", 0)) - return 0; - - GetWindowRect(g_clistApi.hwndContactList, &rc); - - if (Frames[FrameId].collapsed == TRUE) { - rc.bottom -= rc.top; - rc.bottom -= Frames[FrameId].height; - Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height; - Frames[FrameId].collapsed = FALSE; - } - else { - rc.bottom -= rc.top; - rc.bottom += Frames[FrameId].HeightWhenCollapsed; - Frames[FrameId].collapsed = TRUE; - } - - SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, rc.right - rc.left, rc.bottom, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); - - CLUIFramesStoreAllFrames(); - lck.unlock(); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return 0; - - } - if (Frames[FrameId].Locked || (!Frames[FrameId].visible)) - return 0; - - oldHeight = Frames[FrameId].height; - - // if collapsed, uncollapse - if (Frames[FrameId].collapsed == TRUE) { - Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height; - Frames[FrameId].height = UNCOLLAPSED_FRAME_SIZE; - Frames[FrameId].collapsed = FALSE; - } - // if uncollapsed, collapse - else { - Frames[FrameId].height = Frames[FrameId].HeightWhenCollapsed; - Frames[FrameId].collapsed = TRUE; - } - - if (!Frames[FrameId].floating) { - - if (!CLUIFramesFitInSize()) { - //cant collapse,we can resize only for height<alclient frame height - int alfrm = CLUIFramesGetalClientFrame(); - - if (alfrm != -1) { - Frames[FrameId].collapsed = FALSE; - if (Frames[alfrm].height > 2 * UNCOLLAPSED_FRAME_SIZE) { - oldHeight = Frames[alfrm].height - UNCOLLAPSED_FRAME_SIZE; - Frames[FrameId].collapsed = TRUE; - } - } - else { - int i, sumheight = 0; - - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) { - sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2; - return FALSE; - } - if (sumheight > ContactListHeight - 0 - 2) - Frames[FrameId].height = (ContactListHeight - 0 - 2) - sumheight; - } - } - Frames[FrameId].height = oldHeight; - if (Frames[FrameId].collapsed == FALSE) { - if (Frames[FrameId].floating) - SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, Frames[FrameId].wndSize.right - Frames[FrameId].wndSize.left + 6, Frames[FrameId].height + DEFAULT_TITLEBAR_HEIGHT + 4, SWP_SHOWWINDOW | SWP_NOMOVE); - return -1; - } - } - } - lck.unlock(); - if (!Frames[FrameId].floating) - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - else { - RECT contwnd; - GetWindowRect(Frames[FrameId].ContainerWnd, &contwnd); - contwnd.top = contwnd.bottom - contwnd.top;//height - contwnd.left = contwnd.right - contwnd.left;//width - - contwnd.top -= (oldHeight - Frames[FrameId].height);//newheight - SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, contwnd.left, contwnd.top, SWP_SHOWWINDOW | SWP_NOMOVE); - } - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - CLUIFramesStoreAllFrames(); - return 0; -} - -static int CLUIFramesLoadMainMenu() -{ - if (FramesSysNotStarted) - return -1; - - for (auto &it : g_frameMenus) - Menu_RemoveItem(it); - g_frameMenus.destroy(); - - // create frames menu - CMenuItem mi(&g_plugin); - mi.root = cont.MainMenuItem; - mi.flags = CMIF_UNICODE | CMIF_SYSTEM; - int separator = (int)3000200000; - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - mi.hIcolibItem = F.TitleBar.hicon; - mi.position = separator; - mi.name.w = F.TitleBar.tbname ? F.TitleBar.tbname : F.name; - mi.pszService = nullptr; - g_frameMenus.insert(F.MenuHandles.MainMenuItem = Menu_AddMainMenuItem(&mi)); - CLUIFramesCreateMenuForFrame(F.id, F.MenuHandles.MainMenuItem, separator, true); - CLUIFramesModifyMainMenuItems(F.id, 0); - CallService(MS_CLIST_FRAMEMENUNOTIFY, (WPARAM)F.id, (LPARAM)F.MenuHandles.MainMenuItem); - separator++; - } - return 0; -} - -static HFONT CLUILoadTitleBarFont() -{ - char facename[] = "MS Shell Dlg"; - LOGFONT logfont; - memset(&logfont, 0, sizeof(logfont)); - memcpy(logfont.lfFaceName, facename, sizeof(facename)); - logfont.lfWeight = FW_NORMAL; - logfont.lfHeight = -10; - return CreateFontIndirect(&logfont); -} - -static int UpdateTBToolTip(int framepos) -{ - TOOLINFO ti; - - memset(&ti, 0, sizeof(ti)); - ti.cbSize = sizeof(ti); - ti.lpszText = Frames[framepos].TitleBar.tooltip; - ti.hinst = g_plugin.getInst(); - ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; - ti.uId = (UINT_PTR)Frames[framepos].TitleBar.hwnd; - - return SendMessage(Frames[framepos].TitleBar.hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); -}; - -int FrameNCPaint(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar) -{ - RECT rcWindow, rc; - HWND hwndParent = GetParent(hwnd); - LRESULT result = 0; - - if (hwndParent != g_clistApi.hwndContactList || !cfg::dat.bSkinnedScrollbar) - result = CallWindowProc(oldWndProc, hwnd, WM_NCPAINT, wParam, lParam); - if (!g_clistApi.hwndContactList || hwndParent != g_clistApi.hwndContactList) - return result; - - if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SKINNEDFRAME) { - StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr; - if (item == nullptr) - return 0; - - GetWindowRect(hwnd, &rcWindow); - rc.left = rc.top = 0; - rc.right = rcWindow.right - rcWindow.left; - rc.bottom = rcWindow.bottom - rcWindow.top; - - HDC hdc = GetWindowDC(hwnd); - if (hwnd == g_clistApi.hwndContactTree) { - HDC realDC = CreateCompatibleDC(hdc); - HBITMAP hbmDraw = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); - HBITMAP hbmOld = reinterpret_cast<HBITMAP>(SelectObject(realDC, hbmDraw)); - - ExcludeClipRect(realDC, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM); - BitBlt(realDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY); - DrawAlpha(realDC, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); - - ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM); - BitBlt(hdc, 0, 0, rc.right, rc.bottom, realDC, 0, 0, SRCCOPY); - SelectObject(realDC, hbmOld); - DeleteObject(hbmDraw); - DeleteDC(realDC); - } - else { - ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM); - BitBlt(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY); - DrawAlpha(hdc, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); - } - ReleaseDC(hwnd, hdc); - return 0; - } - - if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) { - HDC hdc = GetWindowDC(hwnd); - HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames)); - GetWindowRect(hwnd, &rcWindow); - rc.left = rc.top = 0; - rc.right = rcWindow.right - rcWindow.left; - rc.bottom = rcWindow.bottom - rcWindow.top; - HBRUSH brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH))); - Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top); - SelectObject(hdc, hPenOld); - SelectObject(hdc, brold); - ReleaseDC(hwnd, hdc); - return 0; - } - - return result; -} - -int FrameNCCalcSize(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar) -{ - StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr; - LRESULT orig = oldWndProc ? CallWindowProc(oldWndProc, hwnd, WM_NCCALCSIZE, wParam, lParam) : 0; - NCCALCSIZE_PARAMS *nccp = (NCCALCSIZE_PARAMS *)lParam; - uint32_t dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE); - - if (item == nullptr) - return orig; - - if (item->IGNORED || !(dwStyle & CLS_SKINNEDFRAME) || GetParent(hwnd) != g_clistApi.hwndContactList) - return orig; - - nccp->rgrc[0].left += item->MARGIN_LEFT; - nccp->rgrc[0].right -= item->MARGIN_RIGHT; - nccp->rgrc[0].bottom -= item->MARGIN_BOTTOM; - nccp->rgrc[0].top += item->MARGIN_TOP; - return WVR_REDRAW; -} - -static LRESULT CALLBACK FramesSubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int i; - - WNDPROC oldWndProc = nullptr; - BOOL hasTitleBar = FALSE; - - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (F.hWnd == hwnd) { - oldWndProc = F.wndProc; - hasTitleBar = F.TitleBar.ShowTitleBar; - } - } - switch (msg) { - case WM_NCPAINT: - return FrameNCPaint(hwnd, oldWndProc ? oldWndProc : DefWindowProc, wParam, lParam, hasTitleBar); - - case WM_NCCALCSIZE: - return FrameNCCalcSize(hwnd, oldWndProc, wParam, lParam, hasTitleBar); - - case WM_PRINTCLIENT: - return 0; - } - - if (oldWndProc) - return CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam); - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -/* - * re-sort all frames and correct frame ordering - */ - -static int CLUIFramesReSort() -{ - int v = 0, i; - int order = 1; - - mir_cslock lck(csFrameHook); - memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES); - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (F.align != alTop) - continue; - g_sd[v].order = F.order; - g_sd[v].realpos = i; - v++; - } - if (v > 0) { - qsort(g_sd, v, sizeof(SortData), sortfunc); - for (i = 0; i < v; i++) - Frames[g_sd[i].realpos].order = order++; - } - - memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES); - v = 0; - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (F.align != alBottom) - continue; - g_sd[v].order = F.order; - g_sd[v].realpos = i; - v++; - } - if (v > 0) { - qsort(g_sd, v, sizeof(SortData), sortfunc); - for (i = 0; i < v; i++) - Frames[g_sd[i].realpos].order = order++; - } - CLUIFramesStoreAllFrames(); - return 0; -} - -//wparam=(CLISTFrame*)clfrm -INT_PTR CLUIFramesAddFrame(WPARAM wParam, LPARAM lParam) -{ - int style; - CLISTFrame *clfrm = (CLISTFrame *)wParam; - - if (g_clistApi.hwndContactList == nullptr) return -1; - if (FramesSysNotStarted) return -1; - if (clfrm->cbSize != sizeof(CLISTFrame)) return -1; - - mir_cslockfull lck(csFrameHook); - if (nFramescount >= MAX_FRAMES) - return -1; - - if (Frames == nullptr) { - Frames = (FRAMEWND*)malloc(sizeof(FRAMEWND) * (MAX_FRAMES + 2)); - memset(Frames, 0, (sizeof(FRAMEWND) * (MAX_FRAMES + 2))); - } - memset(&Frames[nFramescount], 0, sizeof(FRAMEWND)); - - Frames[nFramescount].id = NextFrameId++; - Frames[nFramescount].align = clfrm->align; - Frames[nFramescount].hWnd = clfrm->hWnd; - Frames[nFramescount].height = clfrm->height; - Frames[nFramescount].TitleBar.hicon = clfrm->hIcon; - Frames[nFramescount].floating = false; - Frames[nFramescount].pPlugin = (HPLUGIN)lParam; - - if (clfrm->Flags & F_NO_SUBCONTAINER) - Frames[nFramescount].OwnerWindow = (HWND)-2; - else - Frames[nFramescount].OwnerWindow = g_clistApi.hwndContactList; - - SetClassLong(clfrm->hWnd, GCL_STYLE, GetClassLong(clfrm->hWnd, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); - SetWindowLongPtr(clfrm->hWnd, GWL_STYLE, GetWindowLongPtr(clfrm->hWnd, GWL_STYLE) | WS_CLIPCHILDREN); - - if (GetCurrentThreadId() == GetWindowThreadProcessId(clfrm->hWnd, nullptr)) { - if (clfrm->hWnd != g_clistApi.hwndContactTree && clfrm->hWnd != g_hwndViewModeFrame && clfrm->hWnd != g_hwndEventArea) { - Frames[nFramescount].wndProc = (WNDPROC)GetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC); - SetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC, (LONG_PTR)FramesSubClassProc); - } - } - - if (clfrm->hWnd == g_hwndEventArea) - wndFrameEventArea = &Frames[nFramescount]; - else if (clfrm->hWnd == g_clistApi.hwndContactTree) - wndFrameCLC = &Frames[nFramescount]; - else if (clfrm->hWnd == g_hwndViewModeFrame) - wndFrameViewMode = &Frames[nFramescount]; - - Frames[nFramescount].dwFlags = clfrm->Flags; - - if (clfrm->szName.a == nullptr || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szName.w) : mir_strlen(clfrm->szName.a)) == 0) { - wchar_t ptszClassName[256]; - GetClassName(Frames[nFramescount].hWnd, ptszClassName, _countof(ptszClassName)); - Frames[nFramescount].name = mir_wstrdup(ptszClassName); - } - else Frames[nFramescount].name = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szName.w) : mir_a2u(clfrm->szName.a); - - if (IsBadCodePtr((FARPROC)clfrm->szTBname.a) || clfrm->szTBname.a == nullptr - || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szTBname.w) : mir_strlen(clfrm->szTBname.a)) == 0) - Frames[nFramescount].TitleBar.tbname = mir_wstrdup(Frames[nFramescount].name); - else - Frames[nFramescount].TitleBar.tbname = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szTBname.w) : mir_a2u(clfrm->szTBname.a); - Frames[nFramescount].needhide = FALSE; - Frames[nFramescount].TitleBar.ShowTitleBar = (clfrm->Flags & F_SHOWTB ? TRUE : FALSE); - Frames[nFramescount].TitleBar.ShowTitleBarTip = (clfrm->Flags & F_SHOWTBTIP ? TRUE : FALSE); - - Frames[nFramescount].collapsed = clfrm->Flags & F_UNCOLLAPSED ? FALSE : TRUE; - Frames[nFramescount].Locked = clfrm->Flags & F_LOCKED ? TRUE : FALSE; - Frames[nFramescount].visible = clfrm->Flags & F_VISIBLE ? TRUE : FALSE; - - Frames[nFramescount].UseBorder = (clfrm->Flags & F_NOBORDER) ? FALSE : TRUE; - Frames[nFramescount].Skinned = (clfrm->Flags & F_SKINNED) ? TRUE : FALSE; - - // create frame - Frames[nFramescount].TitleBar.hwnd = - CreateWindow(CLUIFrameTitleBarClassName, Frames[nFramescount].name, - (db_get_b(0, CLUIFrameModule, "RemoveAllTitleBarBorders", 1) ? 0 : WS_BORDER) - | WS_CHILD | WS_CLIPCHILDREN | (Frames[nFramescount].TitleBar.ShowTitleBar ? WS_VISIBLE : 0) | - WS_CLIPCHILDREN, 0, 0, 0, 0, g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr); - - SetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWLP_USERDATA, Frames[nFramescount].id); - - Frames[nFramescount].TitleBar.hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr); - - SetWindowPos(Frames[nFramescount].TitleBar.hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - { - TOOLINFOA ti = { 0 }; - ti.cbSize = sizeof(ti); - ti.lpszText = ""; - ti.hinst = g_plugin.getInst(); - ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; - ti.uId = (UINT_PTR)Frames[nFramescount].TitleBar.hwnd; - SendMessageA(Frames[nFramescount].TitleBar.hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti); - } - - SendMessage(Frames[nFramescount].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[nFramescount].TitleBar.ShowTitleBarTip, 0); - - Frames[nFramescount].oldstyles = GetWindowLongPtr(Frames[nFramescount].hWnd, GWL_STYLE); - Frames[nFramescount].TitleBar.oldstyles = GetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWL_STYLE); - - int retval = Frames[nFramescount].id; - Frames[nFramescount].order = nFramescount + 1; - nFramescount++; - - CLUIFramesLoadFrameSettings(id2pos(retval)); - style = GetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE); - style &= ~(WS_BORDER); - style |= ((Frames[nFramescount - 1].UseBorder) ? WS_BORDER : 0); - - style |= Frames[nFramescount - 1].Skinned ? CLS_SKINNEDFRAME : 0; - - SetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE, style); - SetWindowLongPtr(Frames[nFramescount - 1].TitleBar.hwnd, GWL_STYLE, style & ~(WS_VSCROLL | WS_HSCROLL)); - - if (Frames[nFramescount - 1].order == 0) - Frames[nFramescount - 1].order = nFramescount; - - lck.unlock(); - - alclientFrame = -1;//recalc it - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - - if (Frames[nFramescount - 1].floating) { - Frames[nFramescount - 1].floating = FALSE; - CLUIFrameSetFloat(retval, 1);//lparam=1 use stored width and height - } - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - return retval; -} - -static INT_PTR CLUIFramesRemoveFrame(WPARAM wParam, LPARAM) -{ - if (FramesSysNotStarted) - return -1; - - { - mir_cslock lck(csFrameHook); - int pos = id2pos(wParam); - if (pos < 0 || pos > nFramescount) - return -1; - - FRAMEWND* F = &Frames[pos]; - if (F->hWnd == g_hwndEventArea) - wndFrameEventArea = nullptr; - else if (F->hWnd == g_clistApi.hwndContactTree) - wndFrameCLC = nullptr; - else if (F->hWnd == g_hwndViewModeFrame) - wndFrameViewMode = nullptr; - - mir_free(F->name); - mir_free(F->TitleBar.tbname); - mir_free(F->TitleBar.tooltip); - - DestroyWindow(F->hWnd); - F->hWnd = (HWND)-1; - DestroyWindow(F->TitleBar.hwnd); - F->TitleBar.hwnd = (HWND)-1; - DestroyWindow(F->ContainerWnd); - F->ContainerWnd = (HWND)-1; - DestroyMenu(F->TitleBar.hmenu); - - RemoveItemFromList(pos, &Frames, &nFramescount); - } - - if (!cfg::shutDown) { - InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE); - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - } - return 0; -} - -INT_PTR CLUIFramesForceUpdateTB(const FRAMEWND *Frame) -{ - if (Frame->TitleBar.hwnd != nullptr) - RedrawWindow(Frame->TitleBar.hwnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); - return 0; -} - -INT_PTR CLUIFramesForceUpdateFrame(const FRAMEWND *Frame) -{ - if (Frame->hWnd != nullptr) - RedrawWindow(Frame->hWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_FRAME | RDW_ERASE | RDW_INVALIDATE); - - if (Frame->floating) - if (Frame->ContainerWnd != nullptr) RedrawWindow(Frame->ContainerWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); - return 0; -} - -int CLUIFrameMoveResize(const FRAMEWND *Frame) -{ - TitleBarH = cfg::dat.titleBarHeight; - // we need to show or hide the frame? - if (Frame->visible && (!Frame->needhide)) { - ShowWindow(Frame->hWnd, SW_SHOW); - ShowWindow(Frame->TitleBar.hwnd, Frame->TitleBar.ShowTitleBar == TRUE ? SW_SHOW : SW_HIDE); - } - else { - ShowWindow(Frame->hWnd, SW_HIDE); - ShowWindow(Frame->TitleBar.hwnd, SW_HIDE); - return 0; - } - - SetWindowPos(Frame->hWnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset, - (Frame->wndSize.right - Frame->wndSize.left), - (Frame->wndSize.bottom - Frame->wndSize.top), SWP_NOZORDER | SWP_NOREDRAW); - if (Frame->TitleBar.ShowTitleBar) { - SetWindowPos(Frame->TitleBar.hwnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset - TitleBarH, - (Frame->wndSize.right - Frame->wndSize.left), - TitleBarH + (Frame->UseBorder ? (!Frame->collapsed ? (Frame->align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER); - } - return 0; -} - -bool CLUIFramesFitInSize(void) -{ - int i; - int sumheight = 0; - int tbh = 0; // title bar height - int clientfrm; - - TitleBarH = cfg::dat.titleBarHeight; - - clientfrm = CLUIFramesGetalClientFrame(); - if (clientfrm != -1) - tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar); - - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) { - sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2/*+btoint(F.UseBorder)*2*/; - if (sumheight > ContactListHeight - tbh - 2) - return FALSE; - } - } - return TRUE; -} - -int CLUIFramesGetMinHeight() -{ - if (g_clistApi.hwndContactList == nullptr) - return 0; - - int i, tbh, clientfrm, sumheight = 0; - RECT border; - int allbord = 0; - { - mir_cslock lck(csFrameHook); - - TitleBarH = cfg::dat.titleBarHeight; - // search for alClient frame and get the titlebar's height - tbh = 0; - clientfrm = CLUIFramesGetalClientFrame(); - if (clientfrm != -1) - tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar); - - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if ((F.align != alClient) && (F.visible) && (!F.needhide) && (!F.floating)) { - RECT wsize; - - GetWindowRect(F.hWnd, &wsize); - sumheight += (wsize.bottom - wsize.top) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 3; - } - } - } - - GetBorderSize(g_clistApi.hwndContactList, &border); - return(sumheight + border.top + border.bottom + allbord + tbh + 3); -} - -int SizeMoveNewSizes() -{ - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (F.floating) - CLUIFrameResizeFloatingFrame(i); - else - CLUIFrameMoveResize(&F); - } - return 0; -} - -/* - * changed Nightwish - * gap calculation was broken. Now, it doesn't calculate and store the gaps in Frames[] anymore. - * instead, it remembers the smallest wndSize.top value (which has to be the top frame) and then passes - * the gap to all following frame(s) to the actual resizing function which just adds the gap to - * wndSize.top and corrects the frame height accordingly. - - * Title bar gap has been removed (can be simulated by using a clist_nicer skin item for frame title bars - * and setting the bottom margin of the skin item - */ - -int CLUIFramesResize(const RECT newsize) -{ - int sumheight = 9999999; - int clientframe = -1; - int i, j; - int topOff = 0, botOff = 0, last_bottomtop; - - GapBetweenFrames = cfg::dat.gapBetweenFrames; - int sepw = GapBetweenFrames; - - if (nFramescount < 1 || cfg::shutDown) - return 0; - - int newheight = newsize.bottom - newsize.top; - TitleBarH = cfg::dat.titleBarHeight; - - // search for alClient frame and get the titlebar's height - int tbh = 0; - int clientfrm = CLUIFramesGetalClientFrame(); - if (clientfrm != -1) - tbh = (TitleBarH)* btoint(Frames[clientfrm].TitleBar.ShowTitleBar); - - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (!F.floating) { - F.needhide = FALSE; - F.wndSize.left = 0; - F.wndSize.right = newsize.right - newsize.left; - } - } - { - //sorting stuff - memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES); - for (i = 0; i < nFramescount; i++) { - g_sd[i].order = Frames[i].order; - g_sd[i].realpos = i; - } - qsort(g_sd, nFramescount, sizeof(SortData), sortfunc); - - } - int drawitems = nFramescount; - while (sumheight >(newheight - tbh) && drawitems > 0) { - sumheight = 0; - drawitems = 0; - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (((F.align != alClient)) && (!F.floating) && (F.visible) && (!F.needhide)) { - drawitems++; - int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar); - sumheight += (F.height) + curfrmtbh + (i > 0 ? sepw : 0) + (F.UseBorder ? 2 : 0); - if (sumheight > newheight - tbh) { - sumheight -= (F.height) + curfrmtbh + (i > 0 ? sepw : 0); - F.needhide = TRUE; - drawitems--; - break; - } - } - } - } - - int prevframe = -1; - int prevframebottomline = 0; - for (j = 0; j < nFramescount; j++) { - // move all alTop frames - i = g_sd[j].realpos; - FRAMEWND &F = Frames[i]; - if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alTop)) { - int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar); - F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (curfrmtbh); - F.wndSize.bottom = F.height + F.wndSize.top + (F.UseBorder ? 2 : 0); - F.prevvisframe = prevframe; - prevframe = i; - prevframebottomline = F.wndSize.bottom; - topOff = prevframebottomline; - } - } - - if (sumheight < newheight) { - for (j = 0; j < nFramescount; j++) { - // move alClient frame - i = g_sd[j].realpos; - FRAMEWND &F = Frames[i]; - if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alClient)) { - int oldh; - F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (tbh); - F.wndSize.bottom = F.wndSize.top + newheight - sumheight - tbh - ((prevframebottomline > 0) ? sepw : 0); - clientframe = i; - oldh = F.height; - F.height = F.wndSize.bottom - F.wndSize.top; - F.prevvisframe = prevframe; - prevframe = i; - prevframebottomline = F.wndSize.bottom; - if (prevframebottomline > newheight) { - // prevframebottomline-=F.height+(tbh+1); - // F.needhide=TRUE; - } - break; - } - } - } - - // newheight - prevframebottomline = last_bottomtop = newheight; - for (j = nFramescount - 1; j >= 0; j--) { - // move all alBottom frames - i = g_sd[j].realpos; - FRAMEWND &F = Frames[i]; - if ((F.visible) && (!F.floating) && (!F.needhide) && (F.align == alBottom)) { - int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar); - F.wndSize.bottom = prevframebottomline - ((prevframebottomline < newheight) ? sepw : 0); - F.wndSize.top = F.wndSize.bottom - F.height - (F.UseBorder ? 2 : 0); - F.prevvisframe = prevframe; - prevframe = i; - prevframebottomline = F.wndSize.top - curfrmtbh; - botOff = prevframebottomline; - last_bottomtop = F.wndSize.top - curfrmtbh; - } - } - - // correct client frame bottom gap if there is no other top frame. - if (clientframe != -1) { - Frames[clientframe].wndSize.bottom = last_bottomtop - (last_bottomtop < newheight ? sepw : 0); - Frames[clientframe].height = Frames[clientframe].wndSize.bottom - Frames[clientframe].wndSize.top; - } - return 0; -} - -INT_PTR CLUIFramesUpdateFrame(WPARAM wParam, LPARAM lParam) -{ - if (FramesSysNotStarted) - return -1; - - if (wParam == -1) { - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - return 0; - } - - if (lParam & FU_FMPOS) - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 1); - - mir_cslock lck(csFrameHook); - int pos = id2pos(wParam); - if (pos < 0 || pos >= nFramescount) - return -1; - - if (lParam & FU_TBREDRAW) - CLUIFramesForceUpdateTB(&Frames[pos]); - if (lParam & FU_FMREDRAW) - CLUIFramesForceUpdateFrame(&Frames[pos]); - return 0; -} - -int dock_prevent_moving = 0; - -int CLUIFramesApplyNewSizes(int mode) -{ - dock_prevent_moving = 0; - - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if ((mode == 1 && F.OwnerWindow != (HWND)-2 && F.OwnerWindow) || - (mode == 2 && F.OwnerWindow == (HWND)-2) || (mode == 3)) - if (F.floating) - CLUIFrameResizeFloatingFrame(i); - else - CLUIFrameMoveResize(&Frames[i]); - } - dock_prevent_moving = 1; - return 0; -} - -RECT old_window_rect = { 0 }, new_window_rect = { 0 }; - -int SizeFramesByWindowRect(RECT *r) -{ - if (FramesSysNotStarted) - return -1; - - TitleBarH = cfg::dat.titleBarHeight; - - mir_cslock lck(csFrameHook); - GapBetweenFrames = cfg::dat.gapBetweenFrames; - - RECT nRect = *r; - nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset); - nRect.right -= cfg::dat.bCRight; - nRect.left = cfg::dat.bCLeft; - nRect.top = cfg::dat.topOffset; - ContactListHeight = nRect.bottom - nRect.top; - - CLUIFramesResize(nRect); - { - int i; - for (i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - if (!F.floating) { - if (F.OwnerWindow && F.OwnerWindow != (HWND)-2) { - SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset, - (F.wndSize.right - F.wndSize.left), - (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); - - if (F.TitleBar.ShowTitleBar) { - SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH, - (F.wndSize.right - F.wndSize.left), - TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); - } - } - else { - // set frame position - SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset, - (F.wndSize.right - F.wndSize.left), - (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOREDRAW); - - // set titlebar position - if (F.TitleBar.ShowTitleBar) { - SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH, - (F.wndSize.right - F.wndSize.left), - TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW); - } - if (F.TitleBar.ShowTitleBar) - UpdateWindow(F.TitleBar.hwnd); - } - } - } - - if (GetTickCount() - LastStoreTick > 1000) { - CLUIFramesStoreAllFrames(); - LastStoreTick = GetTickCount(); - } - } - return 0; -} - -int CLUIFramesOnClistResize(WPARAM wParam, LPARAM lParam) -{ - GapBetweenFrames = cfg::dat.gapBetweenFrames; - - if (FramesSysNotStarted || cfg::shutDown) - return -1; - - RECT nRect, rcStatus; - int tick; - { - mir_cslock lck(csFrameHook); - - GetClientRect(g_clistApi.hwndContactList, &nRect); - if (lParam && lParam != 1) { - RECT oldRect; - POINT pt; - RECT * newRect = (RECT *)lParam; - int dl, dt, dr, db; - GetWindowRect((HWND)wParam, &oldRect); - pt.x = nRect.left; - pt.y = nRect.top; - ClientToScreen(g_clistApi.hwndContactList, &pt); - dl = pt.x - oldRect.left; - dt = pt.y - oldRect.top; - dr = (oldRect.right - oldRect.left) - (nRect.right - nRect.left) - dl; - db = (oldRect.bottom - oldRect.top) - (nRect.bottom - nRect.top) - dt; - nRect.left = newRect->left + dl; - nRect.top = newRect->top + dt; - nRect.bottom = newRect->bottom - db; - nRect.right = newRect->right - dr; - } - - rcStatus.top = rcStatus.bottom = 0; - - nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset); - nRect.right -= cfg::dat.bCRight; - nRect.left = cfg::dat.bCLeft; - nRect.top = cfg::dat.topOffset; - ContactListHeight = nRect.bottom - nRect.top; - - tick = GetTickCount(); - - CLUIFramesResize(nRect); - CLUIFramesApplyNewSizes(3); - } - - tick = GetTickCount() - tick; - - if (g_clistApi.hwndContactList != nullptr) - InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE); - if (g_clistApi.hwndContactList != nullptr) - UpdateWindow(g_clistApi.hwndContactList); - - Sleep(0); - - if (GetTickCount() - LastStoreTick > 2000) { - CLUIFramesStoreAllFrames(); - LastStoreTick = GetTickCount(); - } - return 0; -} - -static HBITMAP hBmpBackground; -static int backgroundBmpUse; -static COLORREF bkColour; -static COLORREF SelBkColour; -boolean AlignCOLLIconToLeft; //will hide frame icon - -int OnFrameTitleBarBackgroundChange() -{ - AlignCOLLIconToLeft = db_get_b(0, "FrameTitleBar", "AlignCOLLIconToLeft", 0); - bkColour = db_get_dw(0, "FrameTitleBar", "BkColour", CLCDEFAULT_BKCOLOUR); - - if (hBmpBackground) { - DeleteObject(hBmpBackground); - hBmpBackground = nullptr; - } - if (db_get_b(0, "FrameTitleBar", "UseBitmap", CLCDEFAULT_USEBITMAP)) { - ptrW tszBitmapName(db_get_wsa(0, "FrameTitleBar", "BkBitmap")); - if (tszBitmapName != NULL) - hBmpBackground = Bitmap_Load(tszBitmapName); - } - backgroundBmpUse = db_get_w(0, "FrameTitleBar", "BkBmpUse", CLCDEFAULT_BKBMPUSE); - - CLUIFramesOnClistResize(0, 0); - return 0; -} - -static int DrawTitleBar(HDC dc, RECT rect, int Frameid) -{ - StatusItems_t *item = arStatusItems[ID_EXTBKFRAMETITLE - ID_STATUS_OFFLINE]; - - /* - * no need to redraw anything while shutting down - */ - if (cfg::shutDown) - return 0; - - TitleBarH = cfg::dat.titleBarHeight; - HDC hdcMem = CreateCompatibleDC(dc); - HBITMAP hBmpOsb = CreateCompatibleBitmap(dc, rect.right, rect.bottom); - HBITMAP hoBmp = reinterpret_cast<HBITMAP>(SelectObject(hdcMem, hBmpOsb)); - - SetBkMode(hdcMem, TRANSPARENT); - - HBRUSH hBack = GetSysColorBrush(COLOR_3DFACE); - HBRUSH hoBrush = reinterpret_cast<HBRUSH>(SelectObject(hdcMem, hBack)); - { - mir_cslock lck(csFrameHook); - int pos = id2pos(Frameid); - if (pos >= 0 && pos < nFramescount) { - HFONT oFont; - int fHeight, fontTop; - GetClientRect(Frames[pos].TitleBar.hwnd, &Frames[pos].TitleBar.wndSize); - - if (cfg::clcdat) { - oFont = ChangeToFont(hdcMem, cfg::clcdat, FONTID_FRAMETITLE, &fHeight); - } - else { - oFont = reinterpret_cast<HFONT>(SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT))); - fHeight = 10; - } - fontTop = (TitleBarH - fHeight) / 2; - - if (cfg::dat.bWallpaperMode && !Frames[pos].floating) - SkinDrawBg(Frames[pos].TitleBar.hwnd, hdcMem); - - if (!item->IGNORED) { - RECT rc = Frames[pos].TitleBar.wndSize; - rc.top += item->MARGIN_TOP; - rc.bottom -= item->MARGIN_BOTTOM; - rc.left += item->MARGIN_LEFT; - rc.right -= item->MARGIN_RIGHT; - DrawAlpha(hdcMem, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, - item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); - SetTextColor(hdcMem, item->TEXTCOLOR); - } - else if (cfg::clcdat) { - FillRect(hdcMem, &rect, hBack); - SetTextColor(hdcMem, cfg::clcdat->fontInfo[FONTID_FRAMETITLE].colour); - } - else { - FillRect(hdcMem, &rect, hBack); - SetTextColor(hdcMem, GetSysColor(COLOR_BTNTEXT)); - } - - const wchar_t *pwszTitle = TranslateW_LP(Frames[pos].TitleBar.tbname, Frames[pos].pPlugin); - int iTitleLen = (int)mir_wstrlen(pwszTitle); - - if (!AlignCOLLIconToLeft) { - if (Frames[pos].TitleBar.hicon != nullptr) { - DrawIconEx(hdcMem, 6 + cfg::dat.bClipBorder, ((TitleBarH >> 1) - 8), Frames[pos].TitleBar.hicon, 16, 16, 0, nullptr, DI_NORMAL); - TextOut(hdcMem, 24 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen); - } - else TextOut(hdcMem, 6 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen); - } - else TextOut(hdcMem, 18 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen); - - if (!AlignCOLLIconToLeft) - DrawIconEx(hdcMem, Frames[pos].TitleBar.wndSize.right - 22, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL); - else - DrawIconEx(hdcMem, 0, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL); - SelectObject(hdcMem, oFont); - } - } - - BitBlt(dc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, SRCCOPY); - SelectObject(hdcMem, hoBmp); - SelectObject(hdcMem, hoBrush); - DeleteDC(hdcMem); - DeleteObject(hBack); - DeleteObject(hBmpOsb); - return 0; -} - -#define MPCF_CONTEXTFRAMEMENU 3 -POINT ptOld; -short nLeft = 0; -short nTop = 0; - -LRESULT CALLBACK CLUIFrameTitleBarProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rect; - int Frameid, Framemod, direction; - int xpos, ypos, framepos; - - Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA); - memset(&rect, 0, sizeof(rect)); - - switch (msg) { - case WM_CREATE: - return FALSE; - - case WM_MEASUREITEM: - return Menu_MeasureItem(lParam); - - case WM_DRAWITEM: - return Menu_DrawItem(lParam); - - case WM_ENABLE: - if (hwnd != nullptr) InvalidateRect(hwnd, nullptr, FALSE); - return 0; - case WM_SIZE: - return 0; - - case WM_COMMAND: - if (Clist_MenuProcessCommand(LOWORD(wParam), 0, Frameid)) - break; - - if (HIWORD(wParam) == 0) {//mouse events for self created menu - framepos = id2pos(Frameid); - if (framepos == -1) - break; - - switch (LOWORD(wParam)) { - case frame_menu_lock: - Frames[framepos].Locked = !Frames[framepos].Locked; - break; - case frame_menu_visible: - Frames[framepos].visible = !Frames[framepos].visible; - break; - case frame_menu_showtitlebar: - Frames[framepos].TitleBar.ShowTitleBar = !Frames[framepos].TitleBar.ShowTitleBar; - break; - case frame_menu_floating: - CLUIFrameSetFloat(Frameid, 0); - break; - } - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - } - break; - - case WM_RBUTTONDOWN: - { - HMENU hmenu; - if (ServiceExists(MS_CLIST_MENUBUILDFRAMECONTEXT)) - hmenu = (HMENU)CallService(MS_CLIST_MENUBUILDFRAMECONTEXT, Frameid, 0); - else { - framepos = id2pos(Frameid); - - mir_cslock lck(csFrameHook); - if (framepos == -1) - break; - - hmenu = CreatePopupMenu(); - AppendMenu(hmenu, MF_STRING | MF_DISABLED | MF_GRAYED, 15, Frames[framepos].name); - AppendMenu(hmenu, MF_SEPARATOR, 16, L""); - - if (Frames[framepos].Locked) - AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_lock, TranslateT("Lock frame")); - else - AppendMenu(hmenu, MF_STRING, frame_menu_lock, TranslateT("Lock frame")); - - if (Frames[framepos].visible) - AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_visible, TranslateT("Visible")); - else - AppendMenu(hmenu, MF_STRING, frame_menu_visible, TranslateT("Visible")); - - if (Frames[framepos].TitleBar.ShowTitleBar) - AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_showtitlebar, TranslateT("Show title bar")); - else - AppendMenu(hmenu, MF_STRING, frame_menu_showtitlebar, TranslateT("Show title bar")); - - if (Frames[framepos].Skinned) - AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_skinned, TranslateT("Skinned frame")); - else - AppendMenu(hmenu, MF_STRING, frame_menu_skinned, TranslateT("Skinned frame")); - - if (Frames[framepos].floating) - AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_floating, TranslateT("Floating")); - else - AppendMenu(hmenu, MF_STRING, frame_menu_floating, TranslateT("Floating")); - } - POINT pt; - GetCursorPos(&pt); - TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, nullptr); - DestroyMenu(hmenu); - } - break; - - case WM_LBUTTONDBLCLK: - Framemod = -1; - lbypos = -1; - oldframeheight = -1; - ReleaseCapture(); - CallService(MS_CLIST_FRAMES_UCOLLFRAME, Frameid, 0); - lbypos = -1; - oldframeheight = -1; - ReleaseCapture(); - break; - - case WM_LBUTTONUP: - if (GetCapture() != hwnd) - break; - - curdragbar = -1; - lbypos = -1; - oldframeheight = -1; - ReleaseCapture(); - RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - break; - - case WM_LBUTTONDOWN: - framepos = id2pos(Frameid); - { - mir_cslock lck(csFrameHook); - if (framepos == -1) - break; - - if (Frames[framepos].floating) { - POINT pt; - GetCursorPos(&pt); - Frames[framepos].TitleBar.oldpos = pt; - } - - if ((!(wParam&MK_CONTROL)) && Frames[framepos].Locked && (!(Frames[framepos].floating))) { - if (db_get_b(0, "CLUI", "ClientAreaDrag", 0)) { - POINT pt; - GetCursorPos(&pt); - return SendMessage(GetParent(hwnd), WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); - } - } - if (Frames[framepos].floating) { - RECT rc; - GetCursorPos(&ptOld); - GetWindowRect(hwnd, &rc); - nLeft = (short)rc.left; - nTop = (short)rc.top; - } - } - SetCapture(hwnd); - break; - - case WM_MOUSEMOVE: - { - mir_cslock lck(csFrameHook); - int pos = id2pos(Frameid); - if (pos != -1) { - int oldflags; - char TBcapt[255]; - mir_snprintf(TBcapt, "%s - h:%d, vis:%d, fl:%d, fl:(%d,%d,%d,%d),or: %d", - Frames[pos].name, Frames[pos].height, Frames[pos].visible, Frames[pos].floating, - Frames[pos].FloatingPos.x, Frames[pos].FloatingPos.y, - Frames[pos].FloatingSize.x, Frames[pos].FloatingSize.y, - Frames[pos].order); - - oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, Frames[pos].id), 0); - if (!(oldflags & F_SHOWTBTIP)) - oldflags |= F_SHOWTBTIP; - } - } - if (wParam & MK_LBUTTON) { - RECT rcMiranda; - RECT rcwnd, rcOverlap; - POINT newpt, ofspt, curpt, newpos; - - mir_cslockfull lck(csFrameHook); - - int pos = id2pos(Frameid); - if (Frames[pos].floating) { - GetCursorPos(&curpt); - rcwnd.bottom = curpt.y + 5; - rcwnd.top = curpt.y; - rcwnd.left = curpt.x; - rcwnd.right = curpt.x + 5; - - GetWindowRect(g_clistApi.hwndContactList, &rcMiranda); - if (IsWindowVisible(g_clistApi.hwndContactList) && IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) { - int id = Frames[pos].id; - - lck.unlock(); - ofspt.x = 0; - ofspt.y = 0; - ClientToScreen(Frames[pos].TitleBar.hwnd, &ofspt); - ofspt.x = curpt.x - ofspt.x; - ofspt.y = curpt.y - ofspt.y; - - CLUIFrameSetFloat(id, 0); - newpt.x = 0; - newpt.y = 0; - ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt); - SetCursorPos(newpt.x + ofspt.x, newpt.y + ofspt.y); - GetCursorPos(&curpt); - - lck.lock(); - Frames[pos].TitleBar.oldpos = curpt; - return 0; - } - } - else { - int id = Frames[pos].id; - - GetCursorPos(&curpt); - rcwnd.bottom = curpt.y + 5; - rcwnd.top = curpt.y; - rcwnd.left = curpt.x; - rcwnd.right = curpt.x + 5; - - GetWindowRect(g_clistApi.hwndContactList, &rcMiranda); - - if (!IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) { - lck.unlock(); - GetCursorPos(&curpt); - GetWindowRect(Frames[pos].hWnd, &rcwnd); - rcwnd.left = rcwnd.right - rcwnd.left; - rcwnd.top = rcwnd.bottom - rcwnd.top; - newpos.x = curpt.x; - newpos.y = curpt.y; - if (curpt.x >= (rcMiranda.right - 1)) - newpos.x = curpt.x + 5; - if (curpt.x <= (rcMiranda.left + 1)) - newpos.x = curpt.x - (rcwnd.left) - 5; - if (curpt.y >= (rcMiranda.bottom - 1)) - newpos.y = curpt.y + 5; - if (curpt.y <= (rcMiranda.top + 1)) - newpos.y = curpt.y - (rcwnd.top) - 5; - - ofspt.x = 0; - ofspt.y = 0; - GetWindowRect(Frames[pos].TitleBar.hwnd, &rcwnd); - ofspt.x = curpt.x - ofspt.x; - ofspt.y = curpt.y - ofspt.y; - Frames[pos].FloatingPos.x = newpos.x; - Frames[pos].FloatingPos.y = newpos.y; - CLUIFrameSetFloat(id, 0); - - lck.lock(); - newpt.x = 0; - newpt.y = 0; - ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt); - GetWindowRect(Frames[pos].hWnd, &rcwnd); - SetCursorPos(newpt.x + (rcwnd.right - rcwnd.left) / 2, newpt.y + (rcwnd.bottom - rcwnd.top) / 2); - GetCursorPos(&curpt); - Frames[pos].TitleBar.oldpos = curpt; - return 0; - } - } - } - if (wParam & MK_LBUTTON) { - int newh = -1, prevold; - - if (GetCapture() != hwnd) - break; - - POINT pt, pt2; - mir_cslockfull lck(csFrameHook); - int pos = id2pos(Frameid); - - if (Frames[pos].floating) { - RECT wndr; - GetCursorPos(&pt); - if ((Frames[pos].TitleBar.oldpos.x != pt.x) || (Frames[pos].TitleBar.oldpos.y != pt.y)) { - pt2 = pt; - ScreenToClient(hwnd, &pt2); - GetWindowRect(Frames[pos].ContainerWnd, &wndr); - - POINT ptNew = pt; - - nLeft += (short)ptNew.x - ptOld.x; - nTop += (short)ptNew.y - ptOld.y; - - if (!(wParam & MK_CONTROL)) - PositionThumb(&Frames[pos], nLeft, nTop); - else - SetWindowPos(Frames[pos].ContainerWnd, nullptr, nLeft, nTop, 0, 0, SWP_NOSIZE | SWP_NOZORDER); - - ptOld = ptNew; - - pt.x = nLeft; - pt.y = nTop; - Frames[pos].TitleBar.oldpos = pt; - } - return 0; - } - if (Frames[pos].prevvisframe != -1) { - GetCursorPos(&pt); - - if ((Frames[pos].TitleBar.oldpos.x == pt.x) && (Frames[pos].TitleBar.oldpos.y == pt.y)) - break; - - ypos = rect.top + pt.y; - xpos = rect.left + pt.x; - Framemod = -1; - - if (Frames[pos].align == alBottom) { - direction = -1; - Framemod = pos; - } - else { - direction = 1; - Framemod = Frames[pos].prevvisframe; - } - if (Frames[Framemod].Locked) - break; - if (curdragbar != -1 && curdragbar != pos) - break; - - if (lbypos == -1) { - curdragbar = pos; - lbypos = ypos; - oldframeheight = Frames[Framemod].height; - SetCapture(hwnd); - break; - } - newh = oldframeheight + direction * (ypos - lbypos); - if (newh > 0) { - prevold = Frames[Framemod].height; - Frames[Framemod].height = newh; - if (!CLUIFramesFitInSize()) { - Frames[Framemod].height = prevold; - return TRUE; - } - Frames[Framemod].height = newh; - if (newh > 3) Frames[Framemod].collapsed = TRUE; - - } - Frames[pos].TitleBar.oldpos = pt; - } - lck.unlock(); - - if (newh > 0) - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - break; - } - curdragbar = -1; - lbypos = -1; - oldframeheight = -1; - ReleaseCapture(); - break; - - case WM_NCPAINT: - if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) { - HDC hdc = GetWindowDC(hwnd); - HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames)); - RECT rcWindow, rc; - HBRUSH brold; - - CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); - GetWindowRect(hwnd, &rcWindow); - rc.left = rc.top = 0; - rc.right = rcWindow.right - rcWindow.left; - rc.bottom = rcWindow.bottom - rcWindow.top; - brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH))); - Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top); - SelectObject(hdc, hPenOld); - SelectObject(hdc, brold); - ReleaseDC(hwnd, hdc); - return 0; - } - break; - - case WM_PRINT: - case WM_PRINTCLIENT: - GetClientRect(hwnd, &rect); - DrawTitleBar((HDC)wParam, rect, Frameid); - - case WM_PAINT: - { - PAINTSTRUCT paintStruct; - HDC paintDC = BeginPaint(hwnd, &paintStruct); - rect = paintStruct.rcPaint; - DrawTitleBar(paintDC, rect, Frameid); - EndPaint(hwnd, &paintStruct); - } - return 0; - - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - } - return TRUE; -} - -int CLUIFrameResizeFloatingFrame(int framepos) -{ - if (!Frames[framepos].floating) - return 0; - if (Frames[framepos].ContainerWnd == nullptr) - return 0; - - RECT rect; - GetClientRect(Frames[framepos].ContainerWnd, &rect); - - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - int floatingHeight = cfg::dat.titleBarHeight; - - if (floatingHeight <= 0 || floatingHeight > 50) - floatingHeight = 18; - - Frames[framepos].visible ? ShowWindow(Frames[framepos].ContainerWnd, SW_SHOWNOACTIVATE) : ShowWindow(Frames[framepos].ContainerWnd, SW_HIDE); - - if (Frames[framepos].TitleBar.ShowTitleBar) { - ShowWindow(Frames[framepos].TitleBar.hwnd, SW_SHOWNOACTIVATE); - Frames[framepos].height = height - floatingHeight; - SetWindowPos(Frames[framepos].TitleBar.hwnd, HWND_TOP, 0, 0, width, floatingHeight, SWP_SHOWWINDOW | SWP_DRAWFRAME | SWP_NOACTIVATE); - InvalidateRect(Frames[framepos].TitleBar.hwnd, nullptr, FALSE); - SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, floatingHeight, width, height - floatingHeight, SWP_SHOWWINDOW | SWP_NOACTIVATE); - - } - else { - Frames[framepos].height = height; - ShowWindow(Frames[framepos].TitleBar.hwnd, SW_HIDE); - SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, 0, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE); - } - - if (Frames[framepos].ContainerWnd != nullptr) - UpdateWindow(Frames[framepos].ContainerWnd); - GetWindowRect(Frames[framepos].hWnd, &Frames[framepos].wndSize); - - if (Frames[framepos].TitleBar.ShowTitleBar) - RedrawWindow(Frames[framepos].TitleBar.hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - - RedrawWindow(Frames[framepos].hWnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - return 0; -} - -static int CLUIFrameOnMainMenuBuild(WPARAM, LPARAM) -{ - CLUIFramesLoadMainMenu(); - return 0; -} - -LRESULT CALLBACK CLUIFrameContainerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int framepos; - RECT rect; - INT_PTR Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch (msg) { - case WM_CREATE: - { - mir_cslockfull lck(csFrameHook); - framepos = id2pos(Frameid); - } - return 0; - - case WM_GETMINMAXINFO: - TitleBarH = cfg::dat.titleBarHeight; - { - mir_cslock lck(csFrameHook); - framepos = id2pos(Frameid); - if (framepos < 0 || framepos >= nFramescount) - break; - - if (!Frames[framepos].minmaxenabled) - break; - - if (Frames[framepos].ContainerWnd == nullptr) - break; - - if (Frames[framepos].Locked) { - RECT rct; - GetWindowRect(hwnd, &rct); - ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = rct.right - rct.left; - ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = rct.bottom - rct.top; - ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = rct.right - rct.left; - ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = rct.bottom - rct.top; - } - - MINMAXINFO minmax; - memset(&minmax, 0, sizeof(minmax)); - if (SendMessage(Frames[framepos].hWnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax) != 0) - return DefWindowProc(hwnd, msg, wParam, lParam); - - RECT border; - int tbh = TitleBarH * btoint(Frames[framepos].TitleBar.ShowTitleBar); - GetBorderSize(hwnd, &border); - if (minmax.ptMaxTrackSize.x != 0 && minmax.ptMaxTrackSize.y != 0) { - ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = minmax.ptMinTrackSize.x; - ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = minmax.ptMinTrackSize.y; - ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = minmax.ptMaxTrackSize.x + border.left + border.right; - ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = minmax.ptMaxTrackSize.y + tbh + border.top + border.bottom; - } - } - - case WM_MOVE: - { - mir_cslock lck(csFrameHook); - framepos = id2pos(Frameid); - if (framepos < 0 || framepos >= nFramescount) - break; - - if (Frames[framepos].ContainerWnd == nullptr) - return 0; - - GetWindowRect(Frames[framepos].ContainerWnd, &rect); - Frames[framepos].FloatingPos.x = rect.left; - Frames[framepos].FloatingPos.y = rect.top; - Frames[framepos].FloatingSize.x = rect.right - rect.left; - Frames[framepos].FloatingSize.y = rect.bottom - rect.top; - CLUIFramesStoreFrameSettings(framepos); - } - return 0; - - case WM_SIZE: - { - mir_cslock lck(csFrameHook); - framepos = id2pos(Frameid); - if (framepos < 0 || framepos >= nFramescount) - break; - - if (Frames[framepos].ContainerWnd == nullptr) - return 0; - - CLUIFrameResizeFloatingFrame(framepos); - - GetWindowRect(Frames[framepos].ContainerWnd, &rect); - Frames[framepos].FloatingPos.x = rect.left; - Frames[framepos].FloatingPos.y = rect.top; - Frames[framepos].FloatingSize.x = rect.right - rect.left; - Frames[framepos].FloatingSize.y = rect.bottom - rect.top; - - CLUIFramesStoreFrameSettings(framepos); - } - return 0; - - case WM_CLOSE: - DestroyWindow(hwnd); - break; - - case WM_DESTROY: - return 0; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -static HWND CreateContainerWindow(HWND parent, int x, int y, int width, int height) -{ - return(CreateWindowA("FramesContainer", "aaaa", WS_POPUP | WS_THICKFRAME, x, y, width, height, parent, nullptr, g_plugin.getInst(), nullptr)); -} - -INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam) -{ - HWND hwndtmp, hwndtooltiptmp; - { - mir_cslock lck(csFrameHook); - wParam = id2pos(wParam); - if ((int)wParam >= 0 && (int)wParam < nFramescount) { - if (Frames[wParam].floating) { - SetParent(Frames[wParam].hWnd, g_clistApi.hwndContactList); - SetParent(Frames[wParam].TitleBar.hwnd, g_clistApi.hwndContactList); - Frames[wParam].floating = FALSE; - DestroyWindow(Frames[wParam].ContainerWnd); - Frames[wParam].ContainerWnd = nullptr; - } - else { - RECT recttb, rectw, border; - int temp; - int neww, newh; - - Frames[wParam].oldstyles = GetWindowLongPtr(Frames[wParam].hWnd, GWL_STYLE); - Frames[wParam].TitleBar.oldstyles = GetWindowLongPtr(Frames[wParam].TitleBar.hwnd, GWL_STYLE); - bool locked = Frames[wParam].Locked; - Frames[wParam].Locked = FALSE; - Frames[wParam].minmaxenabled = FALSE; - - GetWindowRect(Frames[wParam].hWnd, &rectw); - GetWindowRect(Frames[wParam].TitleBar.hwnd, &recttb); - if (!Frames[wParam].TitleBar.ShowTitleBar) - recttb.top = recttb.bottom = recttb.left = recttb.right = 0; - - Frames[wParam].ContainerWnd = CreateContainerWindow(g_clistApi.hwndContactList, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, 10, 10); - - SetParent(Frames[wParam].hWnd, Frames[wParam].ContainerWnd); - SetParent(Frames[wParam].TitleBar.hwnd, Frames[wParam].ContainerWnd); - - GetBorderSize(Frames[wParam].ContainerWnd, &border); - - SetWindowLongPtr(Frames[wParam].ContainerWnd, GWLP_USERDATA, Frames[wParam].id); - if ((lParam == 1)) { - if ((Frames[wParam].FloatingPos.x != 0) && (Frames[wParam].FloatingPos.y != 0)) { - if (Frames[wParam].FloatingPos.x < 20) - Frames[wParam].FloatingPos.x = 40; - - if (Frames[wParam].FloatingPos.y < 20) - Frames[wParam].FloatingPos.y = 40; - - SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, Frames[wParam].FloatingSize.x, Frames[wParam].FloatingSize.y, SWP_HIDEWINDOW); - } - else SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, 120, 120, 140, 140, SWP_HIDEWINDOW); - } - else { - neww = rectw.right - rectw.left + border.left + border.right; - newh = (rectw.bottom - rectw.top) + (recttb.bottom - recttb.top) + border.top + border.bottom; - if (neww < 20) - neww = 40; - - if (newh < 20) - newh = 40; - - if (Frames[wParam].FloatingPos.x < 20) - Frames[wParam].FloatingPos.x = 40; - - if (Frames[wParam].FloatingPos.y < 20) - Frames[wParam].FloatingPos.y = 40; - - SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, neww, newh, SWP_HIDEWINDOW); - } - SetWindowText(Frames[wParam].ContainerWnd, Frames[wParam].TitleBar.tbname); - temp = GetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE); - temp |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; - SetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE, temp); - Frames[wParam].floating = TRUE; - Frames[wParam].Locked = locked; - } - } - - CLUIFramesStoreFrameSettings(wParam); - Frames[wParam].minmaxenabled = TRUE; - hwndtooltiptmp = Frames[wParam].TitleBar.hwndTip; - - hwndtmp = Frames[wParam].ContainerWnd; - } - - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - SendMessage(hwndtmp, WM_SIZE, 0, 0); - SetWindowPos(hwndtooltiptmp, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - return 0; -} - -wchar_t g_ptszEventName[100]; - -static int CLUIFrameOnModulesLoad(WPARAM, LPARAM) -{ - mir_snwprintf(g_ptszEventName, L"mf_update_evt_%d", GetCurrentThreadId()); - g_hEventThread = CreateEvent(nullptr, TRUE, FALSE, g_ptszEventName); - hThreadMFUpdate = mir_forkthread(MF_UpdateThread); - SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_IDLE); - CLUIFramesLoadMainMenu(); - CLUIFramesCreateMenuForFrame(-1, nullptr, 000010000, false); - return 0; -} - -static int CLUIFrameLangChanged(WPARAM, LPARAM) -{ - ApplyViewMode(0); - g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE); - return 0; -} - -static int CLUIFrameOnModulesUnload(WPARAM, LPARAM) -{ - mf_updatethread_running = FALSE; - - SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_NORMAL); - SetEvent(g_hEventThread); - WaitForSingleObject(hThreadMFUpdate, 2000); - CloseHandle(g_hEventThread); - - Menu_RemoveItem(cont.MIVisible); - Menu_RemoveItem(cont.MITitle); - Menu_RemoveItem(cont.MITBVisible); - Menu_RemoveItem(cont.MILock); - Menu_RemoveItem(cont.MIColl); - Menu_RemoveItem(cont.MIFloating); - Menu_RemoveItem(cont.MIAlignRoot); - Menu_RemoveItem(cont.MIAlignTop); - Menu_RemoveItem(cont.MIAlignClient); - Menu_RemoveItem(cont.MIAlignBottom); - Menu_RemoveItem(cont.MIBorder); - return 0; -} - -/* - * wparam=hIcon - * return hImage on success,-1 on failure - */ - -int LoadCLUIFramesModule(void) -{ - GapBetweenFrames = cfg::dat.gapBetweenFrames; - - nFramescount = 0; - - WNDCLASS wndclass = {}; - wndclass.style = CS_DBLCLKS; - wndclass.lpfnWndProc = CLUIFrameTitleBarProc; - wndclass.hInstance = g_plugin.getInst(); - wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW); - wndclass.lpszClassName = CLUIFrameTitleBarClassName; - RegisterClass(&wndclass); - - WNDCLASS cntclass = {}; - cntclass.style = CS_DBLCLKS | CS_DROPSHADOW; - cntclass.lpfnWndProc = CLUIFrameContainerWndProc; - cntclass.hInstance = g_plugin.getInst(); - cntclass.hCursor = LoadCursor(nullptr, IDC_ARROW); - cntclass.lpszClassName = L"FramesContainer"; - RegisterClass(&cntclass); - - // create root menu - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x3931AC4, 0x7A32, 0x4D9C, 0x99, 0x92, 0x94, 0xD4, 0xB5, 0x9B, 0xD6, 0xB6); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FRAME); - mi.position = 3000090000; - mi.name.a = LPGEN("Frames"); - mi.pszService = nullptr; - cont.MainMenuItem = Menu_AddMainMenuItem(&mi); - UNSET_UID(mi); - - mi.root = cont.MainMenuItem; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA); - mi.flags = CMIF_UNMOVABLE; - - // create "show all frames" menu - mi.uid.d[7]++; - mi.position = 4000090000; - mi.name.a = LPGEN("Show all frames"); - mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMES; - Menu_AddMainMenuItem(&mi); - - // create "show all titlebars" menu - mi.uid.d[7]++; - mi.position++; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP); - mi.name.a = LPGEN("Show all title bars"); - mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMESTB; - Menu_AddMainMenuItem(&mi); - - // create "hide all titlebars" menu - mi.uid.d[7]++; - mi.position++; - mi.name.a = LPGEN("Hide all title bars"); - mi.pszService = MS_CLIST_FRAMES_HIDEALLFRAMESTB; - Menu_AddMainMenuItem(&mi); - - HookEvent(ME_SYSTEM_MODULESLOADED, CLUIFrameOnModulesLoad); - HookEvent(ME_CLIST_PREBUILDFRAMEMENU, CLUIFramesModifyContextMenuForFrame); - HookEvent(ME_CLIST_PREBUILDMAINMENU, CLUIFrameOnMainMenuBuild); - HookEvent(ME_SYSTEM_PRESHUTDOWN, CLUIFrameOnModulesUnload); - HookEvent(ME_LANGPACK_CHANGED, CLUIFrameLangChanged); - - CreateServiceFunction(MS_CLIST_FRAMES_ADDFRAME, CLUIFramesAddFrame); - CreateServiceFunction(MS_CLIST_FRAMES_REMOVEFRAME, CLUIFramesRemoveFrame); - - CreateServiceFunction(MS_CLIST_FRAMES_SETFRAMEOPTIONS, CLUIFramesSetFrameOptions); - CreateServiceFunction(MS_CLIST_FRAMES_GETFRAMEOPTIONS, CLUIFramesGetFrameOptions); - CreateServiceFunction(MS_CLIST_FRAMES_UPDATEFRAME, CLUIFramesUpdateFrame); - - CreateServiceFunction(MS_CLIST_FRAMES_SHFRAMETITLEBAR, CLUIFramesShowHideFrameTitleBar); - CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMESTB, CLUIFramesShowAllTitleBars); - CreateServiceFunction(MS_CLIST_FRAMES_HIDEALLFRAMESTB, CLUIFramesHideAllTitleBars); - CreateServiceFunction(MS_CLIST_FRAMES_SHFRAME, CLUIFramesShowHideFrame); - CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMES, CLUIFramesShowAll); - - CreateServiceFunction(MS_CLIST_FRAMES_ULFRAME, CLUIFramesLockUnlockFrame); - CreateServiceFunction(MS_CLIST_FRAMES_UCOLLFRAME, CLUIFramesCollapseUnCollapseFrame); - CreateServiceFunction(MS_CLIST_FRAMES_SETUNBORDER, CLUIFramesSetUnSetBorder); - CreateServiceFunction(MS_CLIST_FRAMES_SETSKINNED, CLUIFramesSetUnSetSkinned); - - CreateServiceFunction(CLUIFRAMESSETALIGN, CLUIFramesSetAlign); - CreateServiceFunction(CLUIFRAMESMOVEDOWN, CLUIFramesMoveDown); - CreateServiceFunction(CLUIFRAMESMOVEUP, CLUIFramesMoveUp); - - CreateServiceFunction(CLUIFRAMESSETALIGNALTOP, CLUIFramesSetAlignalTop); - CreateServiceFunction(CLUIFRAMESSETALIGNALCLIENT, CLUIFramesSetAlignalClient); - CreateServiceFunction(CLUIFRAMESSETALIGNALBOTTOM, CLUIFramesSetAlignalBottom); - - CreateServiceFunction("Set_Floating", CLUIFrameSetFloat); - hWndExplorerToolBar = FindWindowExA(nullptr, nullptr, "Shell_TrayWnd", nullptr); - OnFrameTitleBarBackgroundChange(); - - FramesSysNotStarted = FALSE; - g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, db_get_dw(0, "CLUI", "clr_frameborder", GetSysColor(COLOR_3DDKSHADOW))); - return 0; -} - -void LoadExtraIconModule() -{ - hStatusBarShowToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_SHOW_TOOLTIP); - hStatusBarHideToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_HIDE_TOOLTIP); -} - -int UnLoadCLUIFramesModule(void) -{ - CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0); - CLUIFramesStoreAllFrames(); - DeleteObject(g_hPenCLUIFrames); - - mir_cslock lck(csFrameHook); - FramesSysNotStarted = TRUE; - for (int i = 0; i < nFramescount; i++) { - FRAMEWND &F = Frames[i]; - DestroyWindow(F.hWnd); - F.hWnd = (HWND)-1; - DestroyWindow(F.TitleBar.hwnd); - F.TitleBar.hwnd = (HWND)-1; - DestroyWindow(F.ContainerWnd); - F.ContainerWnd = (HWND)-1; - DestroyMenu(F.TitleBar.hmenu); - - if (F.name != nullptr) - mir_free(F.name); - if (F.TitleBar.tbname != nullptr) - mir_free(F.TitleBar.tbname); - } - free(Frames); - Frames = nullptr; - nFramescount = 0; - UnregisterClass(CLUIFrameTitleBarClassName, g_plugin.getInst()); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-03 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "cluiframes.h"
+HFONT __fastcall ChangeToFont(HDC hdc, struct ClcData *dat, int id, int *fontHeight);
+
+extern HWND g_hwndViewModeFrame, g_hwndEventArea;
+extern int mf_updatethread_running;
+
+extern HANDLE hThreadMFUpdate;
+
+void MF_UpdateThread(LPVOID);
+
+HANDLE hStatusBarShowToolTipEvent, hStatusBarHideToolTipEvent;
+HANDLE g_hEventThread = nullptr;
+
+LOGFONT TitleBarLogFont = { 0 };
+
+// we use dynamic frame list,
+// but who wants so huge number of frames ??
+#define MAX_FRAMES 40
+
+#define UNCOLLAPSED_FRAME_SIZE 0
+
+// legacy menu support
+#define frame_menu_lock 1
+#define frame_menu_visible 2
+#define frame_menu_showtitlebar 3
+#define frame_menu_floating 4
+#define frame_menu_skinned 5
+
+static int UpdateTBToolTip(int framepos);
+INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam);
+int CLUIFrameResizeFloatingFrame(int framepos);
+static int CLUIFramesReSort();
+
+boolean FramesSysNotStarted = TRUE;
+HPEN g_hPenCLUIFrames = nullptr;
+
+static SortData g_sd[MAX_FRAMES];
+
+static HHOOK g_hFrameHook = nullptr;
+
+static int sortfunc(const void *a, const void *b)
+{
+ SortData *sd1, *sd2;
+ sd1 = (SortData *)a;
+ sd2 = (SortData *)b;
+ if (sd1->order > sd2->order)
+ return 1;
+ if (sd1->order < sd2->order)
+ return -1;
+ return 0;
+}
+
+static FRAMEWND *Frames = nullptr;
+
+FRAMEWND *wndFrameCLC = nullptr, *wndFrameEventArea = nullptr, *wndFrameViewMode = nullptr;
+
+static int nFramescount = 0;
+static int alclientFrame = -1;//for fast access to frame with alclient properties
+static int NextFrameId = 100;
+
+static int TitleBarH = DEFAULT_TITLEBAR_HEIGHT;
+static boolean resizing = FALSE;
+
+// menus
+static FrameMenuHandles cont;
+static LIST<TMO_IntMenuItem> g_frameMenus(10);
+
+// others
+static int ContactListHeight;
+static int LastStoreTick = 0;
+
+static int lbypos = -1;
+static int oldframeheight = -1;
+static int curdragbar = -1;
+static mir_cs csFrameHook;
+
+static bool CLUIFramesFitInSize(void);
+HWND hWndExplorerToolBar;
+static int GapBetweenFrames = 1;
+
+static int RemoveItemFromList(int pos, FRAMEWND **lpFrames, int *FrameItemCount)
+{
+ memcpy(&((*lpFrames)[pos]), &((*lpFrames)[pos + 1]), sizeof(FRAMEWND) * (*FrameItemCount - pos - 1));
+ (*FrameItemCount)--;
+ return 0;
+}
+
+static int id2pos(int id)
+{
+ int i;
+
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (i = 0; i < nFramescount; i++) {
+ if (Frames[i].id == id)
+ return i;
+ }
+ return -1;
+}
+
+int __forceinline btoint(bool b)
+{
+ return (b ? 1 : 0);
+}
+
+static FRAMEWND* FindFrameByWnd(HWND hwnd)
+{
+ if (hwnd == nullptr)
+ return nullptr;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.floating && F.ContainerWnd == hwnd)
+ return &F;
+ }
+
+ return nullptr;
+}
+
+static void DockThumbs(FRAMEWND *pThumbLeft, FRAMEWND *pThumbRight, BOOL)
+{
+ if ((pThumbRight->dockOpt.hwndLeft == nullptr) && (pThumbLeft->dockOpt.hwndRight == nullptr)) {
+ pThumbRight->dockOpt.hwndLeft = pThumbLeft->ContainerWnd;
+ pThumbLeft->dockOpt.hwndRight = pThumbRight->ContainerWnd;
+ }
+}
+
+static void UndockThumbs(FRAMEWND *pThumb1, FRAMEWND *pThumb2)
+{
+ if ((pThumb1 == nullptr) || (pThumb2 == nullptr))
+ return;
+
+ if (pThumb1->dockOpt.hwndRight == pThumb2->ContainerWnd)
+ pThumb1->dockOpt.hwndRight = nullptr;
+
+ if (pThumb1->dockOpt.hwndLeft == pThumb2->ContainerWnd)
+ pThumb1->dockOpt.hwndLeft = nullptr;
+
+ if (pThumb2->dockOpt.hwndRight == pThumb1->ContainerWnd)
+ pThumb2->dockOpt.hwndRight = nullptr;
+
+ if (pThumb2->dockOpt.hwndLeft == pThumb1->ContainerWnd)
+ pThumb2->dockOpt.hwndLeft = nullptr;
+}
+
+BOOLEAN bMoveTogether;
+
+static void PositionThumb(FRAMEWND *pThumb, short nX, short nY)
+{
+ FRAMEWND *pCurThumb = &Frames[0];
+ FRAMEWND *pDockThumb = pThumb;
+ FRAMEWND fakeMainWindow;
+ FRAMEWND fakeTaskBarWindow;
+ RECT rc;
+ RECT rcThumb;
+ RECT rcOld;
+ SIZE sizeScreen;
+ int nOffs = 10;
+ POINT pt;
+ RECT rcLeft;
+ RECT rcTop;
+ RECT rcRight;
+ RECT rcBottom;
+ int frmidx = 0;
+
+ if (pThumb == nullptr)
+ return;
+
+ sizeScreen.cx = GetSystemMetrics(SM_CXSCREEN);
+ sizeScreen.cy = GetSystemMetrics(SM_CYSCREEN);
+
+ // Get thumb dimnsions
+ GetWindowRect(pThumb->ContainerWnd, &rcThumb);
+ int nWidth = rcThumb.right - rcThumb.left;
+ int nHeight = rcThumb.bottom - rcThumb.top;
+
+ // Docking to the edges of the screen
+ int nNewX = nX < nOffs ? 0 : nX;
+ nNewX = nNewX >(sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX;
+ int nNewY = nY < nOffs ? 0 : nY;
+ nNewY = nNewY >(sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY;
+
+ bool bLeading = pThumb->dockOpt.hwndRight != nullptr;
+
+ if (bMoveTogether) {
+ UndockThumbs(pThumb, FindFrameByWnd(pThumb->dockOpt.hwndLeft));
+ GetWindowRect(pThumb->ContainerWnd, &rcOld);
+ }
+
+ memset(&fakeMainWindow, 0, sizeof(fakeMainWindow));
+ fakeMainWindow.ContainerWnd = g_clistApi.hwndContactList;
+ fakeMainWindow.floating = TRUE;
+
+ memset(&fakeTaskBarWindow, 0, sizeof(fakeTaskBarWindow));
+ fakeTaskBarWindow.ContainerWnd = hWndExplorerToolBar;
+ fakeTaskBarWindow.floating = TRUE;
+
+ while (pCurThumb != nullptr) {
+ if (pCurThumb->floating) {
+
+ if (pCurThumb != pThumb) {
+ GetWindowRect(pThumb->ContainerWnd, &rcThumb);
+ OffsetRect(&rcThumb, nX - rcThumb.left, nY - rcThumb.top);
+
+ GetWindowRect(pCurThumb->ContainerWnd, &rc);
+
+ rcLeft.left = rc.left - nOffs;
+ rcLeft.top = rc.top - nOffs;
+ rcLeft.right = rc.left + nOffs;
+ rcLeft.bottom = rc.bottom + nOffs;
+
+ rcTop.left = rc.left - nOffs;
+ rcTop.top = rc.top - nOffs;
+ rcTop.right = rc.right + nOffs;
+ rcTop.bottom = rc.top + nOffs;
+
+ rcRight.left = rc.right - nOffs;
+ rcRight.top = rc.top - nOffs;
+ rcRight.right = rc.right + nOffs;
+ rcRight.bottom = rc.bottom + nOffs;
+
+ rcBottom.left = rc.left - nOffs;
+ rcBottom.top = rc.bottom - nOffs;
+ rcBottom.right = rc.right + nOffs;
+ rcBottom.bottom = rc.bottom + nOffs;
+
+ bool bDockedLeft = false, bDockedRight = false, bDocked = false;
+
+ // Upper-left
+ pt.x = rcThumb.left;
+ pt.y = rcThumb.top;
+
+ if (PtInRect(&rcRight, pt)) {
+ nNewX = rc.right;
+ bDocked = true;
+ }
+
+ if (PtInRect(&rcBottom, pt)) {
+ nNewY = rc.bottom;
+ if (PtInRect(&rcLeft, pt))
+ nNewX = rc.left;
+ }
+
+ if (PtInRect(&rcTop, pt)) {
+ nNewY = rc.top;
+ bDockedLeft = bDocked;
+ }
+
+ // Upper-right
+ pt.x = rcThumb.right;
+ pt.y = rcThumb.top;
+ bDocked = false;
+
+ if (!bLeading && PtInRect(&rcLeft, pt)) {
+ if (!bDockedLeft) {
+ nNewX = rc.left - nWidth;
+ bDocked = true;
+ }
+ else if (rc.right == rcThumb.left)
+ bDocked = true;
+ }
+
+
+ if (PtInRect(&rcBottom, pt)) {
+ nNewY = rc.bottom;
+ if (PtInRect(&rcRight, pt))
+ nNewX = rc.right - nWidth;
+ }
+
+ if (!bLeading && PtInRect(&rcTop, pt)) {
+ nNewY = rc.top;
+ bDockedRight = bDocked;
+ }
+
+ if (bMoveTogether) {
+ if (bDockedRight)
+ DockThumbs(pThumb, pCurThumb, TRUE);
+
+ if (bDockedLeft)
+ DockThumbs(pCurThumb, pThumb, FALSE);
+ }
+
+ // Lower-left
+ pt.x = rcThumb.left;
+ pt.y = rcThumb.bottom;
+
+ if (PtInRect(&rcRight, pt))
+ nNewX = rc.right;
+
+ if (PtInRect(&rcTop, pt)) {
+ nNewY = rc.top - nHeight;
+
+ if (PtInRect(&rcLeft, pt))
+ nNewX = rc.left;
+ }
+
+
+ // Lower-right
+ pt.x = rcThumb.right;
+ pt.y = rcThumb.bottom;
+
+ if (!bLeading && PtInRect(&rcLeft, pt))
+ nNewX = rc.left - nWidth;
+
+ if (!bLeading && PtInRect(&rcTop, pt)) {
+ nNewY = rc.top - nHeight;
+
+ if (PtInRect(&rcRight, pt))
+ nNewX = rc.right - nWidth;
+ }
+ }
+ }
+
+ frmidx++;
+ if (pCurThumb->ContainerWnd == fakeTaskBarWindow.ContainerWnd)
+ break;
+
+ if (pCurThumb->ContainerWnd == fakeMainWindow.ContainerWnd) {
+ pCurThumb = &fakeTaskBarWindow;
+ continue;
+ }
+ if (frmidx == nFramescount) {
+ pCurThumb = &fakeMainWindow;
+ continue;
+ }
+ pCurThumb = &Frames[frmidx];
+ }
+
+ // Adjust coords once again
+ nNewX = nNewX < nOffs ? 0 : nNewX;
+ nNewX = nNewX > (sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX;
+ nNewY = nNewY < nOffs ? 0 : nNewY;
+ nNewY = nNewY > (sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY;
+ SetWindowPos(pThumb->ContainerWnd, nullptr, nNewX, nNewY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+
+ // OK, move all docked thumbs
+ if (bMoveTogether) {
+ pDockThumb = FindFrameByWnd(pDockThumb->dockOpt.hwndRight);
+ PositionThumb(pDockThumb, (short)(nNewX + nWidth), (short)nNewY);
+ }
+}
+
+void GetBorderSize(HWND hwnd, RECT *rect)
+{
+ RECT wr, cr;
+ POINT pt1, pt2;
+
+ GetWindowRect(hwnd, &wr);
+ GetClientRect(hwnd, &cr);
+ pt1.y = cr.top;
+ pt1.x = cr.left;
+ pt2.y = cr.bottom;
+ pt2.x = cr.right;
+
+ ClientToScreen(hwnd, &pt1);
+ ClientToScreen(hwnd, &pt2);
+
+ cr.top = pt1.y;
+ cr.left = pt1.x;
+ cr.bottom = pt2.y;
+ cr.right = pt2.x;
+
+ rect->top = cr.top - wr.top;
+ rect->left = cr.left - wr.left;
+ rect->right = wr.right - cr.right;
+ rect->bottom = wr.bottom - cr.bottom;
+}
+
+int DBLoadFrameSettingsAtPos(int pos, int Frameid)
+{
+ CMStringA buf;
+
+ Frames[Frameid].collapsed = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), Frames[Frameid].collapsed);
+
+ Frames[Frameid].Locked = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), Frames[Frameid].Locked);
+ Frames[Frameid].visible = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), Frames[Frameid].visible);
+ Frames[Frameid].TitleBar.ShowTitleBar = 0 != db_get_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), Frames[Frameid].TitleBar.ShowTitleBar);
+
+ Frames[Frameid].height = db_get_w(0, CLUIFrameModule, buf.Format("Height%d", pos), Frames[Frameid].height);
+ Frames[Frameid].HeightWhenCollapsed = db_get_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), 0);
+ Frames[Frameid].align = db_get_w(0, CLUIFrameModule, buf.Format("Align%d", pos), Frames[Frameid].align);
+
+ Frames[Frameid].FloatingPos.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatX%d", pos), 100, 0, 1024);
+ Frames[Frameid].FloatingPos.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatY%d", pos), 100, 0, 1024);
+ Frames[Frameid].FloatingSize.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatW%d", pos), 100, 0, 1024);
+ Frames[Frameid].FloatingSize.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatH%d", pos), 100, 0, 1024);
+
+ Frames[Frameid].floating = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), 0);
+ Frames[Frameid].order = db_get_w(0, CLUIFrameModule, buf.Format("Order%d", pos), 0);
+
+ Frames[Frameid].UseBorder = 0 != db_get_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), Frames[Frameid].UseBorder);
+ Frames[Frameid].Skinned = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned);
+ return 0;
+}
+
+int DBStoreFrameSettingsAtPos(int pos, int Frameid)
+{
+ CMStringA buf;
+
+ db_set_ws(0, CLUIFrameModule, buf.Format("Name%d", pos), Frames[Frameid].name);
+ //boolean
+ db_set_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), (uint8_t)btoint(Frames[Frameid].collapsed));
+ db_set_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), (uint8_t)btoint(Frames[Frameid].Locked));
+ db_set_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), (uint8_t)btoint(Frames[Frameid].visible));
+ db_set_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), (uint8_t)btoint(Frames[Frameid].TitleBar.ShowTitleBar));
+
+ db_set_w(0, CLUIFrameModule, buf.Format("Height%d", pos), (uint16_t)Frames[Frameid].height);
+ db_set_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), (uint16_t)Frames[Frameid].HeightWhenCollapsed);
+ db_set_w(0, CLUIFrameModule, buf.Format("Align%d", pos), (uint16_t)Frames[Frameid].align);
+ //FloatingPos
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatX%d", pos), (uint16_t)Frames[Frameid].FloatingPos.x);
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatY%d", pos), (uint16_t)Frames[Frameid].FloatingPos.y);
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatW%d", pos), (uint16_t)Frames[Frameid].FloatingSize.x);
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatH%d", pos), (uint16_t)Frames[Frameid].FloatingSize.y);
+
+ db_set_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), (uint8_t)btoint(Frames[Frameid].floating));
+ db_set_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), (uint8_t)btoint(Frames[Frameid].UseBorder));
+ db_set_w(0, CLUIFrameModule, buf.Format("Order%d", pos), (uint16_t)Frames[Frameid].order);
+
+ db_set_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned);
+ return 0;
+}
+
+int LocateStorePosition(int Frameid, int maxstored)
+{
+ if (Frames[Frameid].name == nullptr) return -1;
+
+ for (int i = 0; i < maxstored; i++) {
+ char settingname[255];
+ mir_snprintf(settingname, "Name%d", i);
+ ptrW frmname(db_get_wsa(0, CLUIFrameModule, settingname));
+ if (frmname == NULL) continue;
+ if (mir_wstrcmpi(frmname, Frames[Frameid].name) == 0)
+ return i;
+ }
+ return -1;
+}
+
+int CLUIFramesLoadFrameSettings(int Frameid)
+{
+ if (FramesSysNotStarted) return -1;
+
+ if (Frameid < 0 || Frameid >= nFramescount)
+ return -1;
+
+ int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1);
+ if (maxstored == -1)
+ return 0;
+
+ int storpos = LocateStorePosition(Frameid, maxstored);
+ if (storpos == -1)
+ return 0;
+
+ DBLoadFrameSettingsAtPos(storpos, Frameid);
+ return 0;
+}
+
+int CLUIFramesStoreFrameSettings(int Frameid)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (Frameid < 0 || Frameid >= nFramescount)
+ return -1;
+
+ int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1);
+ if (maxstored == -1)
+ maxstored = 0;
+
+ int storpos = LocateStorePosition(Frameid, maxstored);
+ if (storpos == -1) {
+ storpos = maxstored;
+ maxstored++;
+ }
+
+ DBStoreFrameSettingsAtPos(storpos, Frameid);
+ db_set_w(0, CLUIFrameModule, "StoredFrames", (uint16_t)maxstored);
+ return 0;
+}
+
+int CLUIFramesStoreAllFrames()
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (cfg::shutDown)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ for (int i = 0; i < nFramescount; i++)
+ CLUIFramesStoreFrameSettings(i);
+ return 0;
+}
+
+// Get client frame
+int CLUIFramesGetalClientFrame(void)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (alclientFrame != -1) {
+ /* this value could become invalid if RemoveItemFromList was called,
+ * so we double-check */
+ if (alclientFrame < nFramescount)
+ if (Frames[alclientFrame].align == alClient)
+ return alclientFrame;
+ }
+
+ for (int i = 0; i < nFramescount; i++)
+ if (Frames[i].align == alClient) {
+ alclientFrame = i;
+ return i;
+ }
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HGENMENU addFrameMenuItem(TMO_MenuItem *pmi, int frameid, bool bMain)
+{
+ HGENMENU res = (bMain) ? Menu_AddMainMenuItem(pmi) : Menu_AddContextFrameMenuItem(pmi);
+ if (pmi->pszService != nullptr)
+ Menu_ConfigureItem(res, MCI_OPT_EXECPARAM, frameid);
+ return res;
+}
+
+HMENU CLUIFramesCreateMenuForFrame(int frameid, HGENMENU root, int popuppos, bool bMain)
+{
+ if (FramesSysNotStarted)
+ return nullptr;
+
+ int framepos = id2pos(frameid);
+ FrameMenuHandles &fmh = (frameid == -1) ? cont : Frames[framepos].MenuHandles;
+
+ CMenuItem mi((frameid == -1) ? &g_plugin : Frames[framepos].pPlugin);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.root = root;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&FrameTitle");
+ mi.flags = CMIF_SYSTEM | CMIF_GRAYED;
+ fmh.MITitle = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.hIcolibItem = nullptr;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Visible");
+ mi.flags = CMIF_SYSTEM | CMIF_CHECKED;
+ mi.pszService = MS_CLIST_FRAMES_SHFRAME;
+ fmh.MIVisible = addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Show title bar");
+ mi.pszService = MS_CLIST_FRAMES_SHFRAMETITLEBAR;
+ fmh.MITBVisible = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Locked");
+ mi.pszService = MS_CLIST_FRAMES_ULFRAME;
+ fmh.MILock = addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Collapsed");
+ mi.pszService = MS_CLIST_FRAMES_UCOLLFRAME;
+ fmh.MIColl = addFrameMenuItem(&mi, frameid, bMain);
+
+ // floating
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Floating mode");
+ mi.flags = CMIF_SYSTEM;
+ mi.pszService = "Set_Floating";
+ fmh.MIFloating = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Border");
+ mi.flags = CMIF_SYSTEM | CMIF_CHECKED;
+ mi.pszService = MS_CLIST_FRAMES_SETUNBORDER;
+ fmh.MIBorder = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Skinned frame");
+ mi.pszService = MS_CLIST_FRAMES_SETSKINNED;
+ fmh.MISkinned = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ // alignment root
+ mi.root = root;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Align");
+ mi.flags = CMIF_SYSTEM;
+ mi.pszService = nullptr;
+ fmh.MIAlignRoot = addFrameMenuItem(&mi, frameid, bMain);
+
+ // align top
+ mi.root = fmh.MIAlignRoot;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Top");
+ mi.pszService = CLUIFRAMESSETALIGNALTOP;
+ fmh.MIAlignTop = addFrameMenuItem(&mi, frameid, bMain);
+
+ // align client
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Client");
+ mi.pszService = CLUIFRAMESSETALIGNALCLIENT;
+ fmh.MIAlignClient = addFrameMenuItem(&mi, frameid, bMain);
+
+ // align bottom
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Bottom");
+ mi.pszService = CLUIFRAMESSETALIGNALBOTTOM;
+ fmh.MIAlignBottom = addFrameMenuItem(&mi, frameid, bMain);
+
+ // position root
+ mi.root = root;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Position");
+ mi.pszService = nullptr;
+ mi.root = addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Up");
+ mi.pszService = CLUIFRAMESMOVEUP;
+ addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Down");
+ mi.pszService = CLUIFRAMESMOVEDOWN;
+ addFrameMenuItem(&mi, frameid, bMain);
+ return nullptr;
+}
+
+static int CLUIFramesModifyContextMenuForFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos >= 0 && pos < nFramescount) {
+ FRAMEWND &p = Frames[pos];
+ Menu_ModifyItem(cont.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name);
+ Menu_SetChecked(cont.MIVisible, p.visible);
+ Menu_SetChecked(cont.MILock, p.Locked);
+ Menu_SetChecked(cont.MITBVisible, p.TitleBar.ShowTitleBar);
+ Menu_SetChecked(cont.MIFloating, p.floating);
+ Menu_SetChecked(cont.MIBorder, p.UseBorder);
+ Menu_SetChecked(cont.MISkinned, p.Skinned);
+ Menu_SetChecked(cont.MIAlignTop, (p.align & alTop) != 0);
+ Menu_SetChecked(cont.MIAlignClient, (p.align & alClient) != 0);
+ Menu_SetChecked(cont.MIAlignBottom, (p.align & alBottom) != 0);
+
+ Menu_SetChecked(cont.MIColl, !p.collapsed);
+ Menu_EnableItem(cont.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame());
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesModifyMainMenuItems(WPARAM frameId, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(frameId);
+
+ if (pos >= 0 && pos < nFramescount) {
+ FRAMEWND &p = Frames[pos];
+ Menu_ModifyItem(p.MenuHandles.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name);
+
+ Menu_SetChecked(p.MenuHandles.MIVisible, p.visible);
+ Menu_SetChecked(p.MenuHandles.MILock, p.Locked);
+ Menu_SetChecked(p.MenuHandles.MITBVisible, p.TitleBar.ShowTitleBar);
+ Menu_SetChecked(p.MenuHandles.MIFloating, p.floating);
+ Menu_SetChecked(p.MenuHandles.MIBorder, p.UseBorder);
+ Menu_SetChecked(p.MenuHandles.MISkinned, p.Skinned);
+
+ Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0);
+ Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alTop) != 0);
+
+ Menu_SetChecked(p.MenuHandles.MIAlignClient, (p.align & alClient) != 0);
+
+ Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0);
+ Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alBottom) != 0);
+
+ Menu_SetChecked(p.MenuHandles.MIColl, !p.collapsed);
+ Menu_EnableItem(p.MenuHandles.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame());
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesGetFrameOptions(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(HIWORD(wParam));
+ if (pos < 0 || pos >= nFramescount)
+ return -1;
+
+ switch (LOWORD(wParam)) {
+ case FO_NAME:
+ return (INT_PTR)Frames[pos].name;
+
+ case FO_TBNAME:
+ return (INT_PTR)Frames[pos].TitleBar.tbname;
+
+ case FO_TBTIPNAME:
+ return (INT_PTR)Frames[pos].TitleBar.tooltip;
+
+ case FO_TBSTYLE:
+ return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE);
+
+ case FO_TBEXSTYLE:
+ return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE);
+
+ case FO_ICON:
+ return (INT_PTR)Frames[pos].TitleBar.hicon;
+
+ case FO_HEIGHT:
+ return (INT_PTR)Frames[pos].height;
+
+ case FO_ALIGN:
+ return (INT_PTR)Frames[pos].align;
+
+ case FO_FLOATING:
+ return (INT_PTR)Frames[pos].floating;
+
+ case FO_FLAGS:
+ INT_PTR dwFlags = 0;
+ if (Frames[pos].visible) dwFlags |= F_VISIBLE;
+ if (!Frames[pos].collapsed) dwFlags |= F_UNCOLLAPSED;
+ if (Frames[pos].Locked) dwFlags |= F_LOCKED;
+ if (Frames[pos].TitleBar.ShowTitleBar) dwFlags |= F_SHOWTB;
+ if (Frames[pos].TitleBar.ShowTitleBarTip) dwFlags |= F_SHOWTBTIP;
+ if (Frames[pos].Skinned) dwFlags |= F_SKINNED;
+ if (!(GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE)&WS_BORDER)) dwFlags |= F_NOBORDER;
+ return dwFlags;
+ }
+
+ return -1;
+}
+
+INT_PTR CLUIFramesSetFrameOptions(WPARAM wParam, LPARAM lParam)
+{
+ int retval; // value to be returned
+
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslockfull lck(csFrameHook);
+ int pos = id2pos(HIWORD(wParam));
+ if (pos < 0 || pos >= nFramescount)
+ return -1;
+
+ switch (LOWORD(wParam) & ~FO_UNICODETEXT) {
+ case FO_FLAGS:
+ {
+ int flag = lParam;
+ LONG_PTR style;
+
+ Frames[pos].dwFlags = flag;
+ Frames[pos].visible = FALSE;
+ if (flag & F_VISIBLE) Frames[pos].visible = TRUE;
+
+ Frames[pos].collapsed = TRUE;
+ if (flag & F_UNCOLLAPSED) Frames[pos].collapsed = FALSE;
+
+ Frames[pos].Locked = FALSE;
+ if (flag & F_LOCKED) Frames[pos].Locked = TRUE;
+
+ Frames[pos].UseBorder = TRUE;
+ if (flag & F_NOBORDER) Frames[pos].UseBorder = FALSE;
+
+ Frames[pos].TitleBar.ShowTitleBar = FALSE;
+ if (flag & F_SHOWTB) Frames[pos].TitleBar.ShowTitleBar = TRUE;
+
+ Frames[pos].TitleBar.ShowTitleBarTip = FALSE;
+ if (flag & F_SHOWTBTIP) Frames[pos].TitleBar.ShowTitleBarTip = TRUE;
+
+ SendMessage(Frames[pos].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[pos].TitleBar.ShowTitleBarTip, 0);
+
+ style = GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE);
+ style |= WS_BORDER;
+ style |= CLS_SKINNEDFRAME;
+
+ if (flag & F_NOBORDER)
+ style &= (~WS_BORDER);
+
+ Frames[pos].Skinned = FALSE;
+ if (flag & F_SKINNED)
+ Frames[pos].Skinned = TRUE;
+
+ if (!(flag & F_SKINNED))
+ style &= ~CLS_SKINNEDFRAME;
+
+ SetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE, (LONG_PTR)style);
+ SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, (LONG_PTR)style & ~(WS_VSCROLL | WS_HSCROLL));
+ lck.unlock();
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ SetWindowPos(Frames[pos].TitleBar.hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ return 0;
+
+ case FO_NAME:
+ if (lParam == 0)
+ return -1;
+
+ mir_free(Frames[pos].name);
+ Frames[pos].name = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
+ return 0;
+
+ case FO_TBNAME:
+ if (lParam == 0)
+ return -1;
+
+ mir_free(Frames[pos].TitleBar.tbname);
+ Frames[pos].TitleBar.tbname = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
+ lck.unlock();
+
+ if (Frames[pos].floating && (Frames[pos].TitleBar.tbname != nullptr))
+ SetWindowText(Frames[pos].ContainerWnd, Frames[pos].TitleBar.tbname);
+ return 0;
+
+ case FO_TBTIPNAME:
+ if (lParam == 0)
+ return -1;
+
+ mir_free(Frames[pos].TitleBar.tooltip);
+ Frames[pos].TitleBar.tooltip = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
+ UpdateTBToolTip(pos);
+ return 0;
+
+ case FO_TBSTYLE:
+ SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, lParam);
+ return 0;
+
+ case FO_TBEXSTYLE:
+ SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE, lParam);
+ return 0;
+
+ case FO_ICON:
+ Frames[pos].TitleBar.hicon = (HICON)lParam;
+ return 0;
+
+ case FO_HEIGHT:
+ if (lParam < 0)
+ return -1;
+
+ if (Frames[pos].Skinned) {
+ int uID = (Frames[pos].TitleBar.ShowTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE : ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE);
+ lParam += (arStatusItems[uID]->MARGIN_BOTTOM + arStatusItems[uID]->MARGIN_TOP);
+ }
+ if (Frames[pos].collapsed) {
+ int oldHeight = Frames[pos].height;
+ retval = Frames[pos].height;
+ Frames[pos].height = lParam;
+ if (!CLUIFramesFitInSize())
+ Frames[pos].height = retval;
+ retval = Frames[pos].height;
+
+ if (Frames[pos].height != oldHeight) {
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ if (Frames[pos].Skinned)
+ RedrawWindow(Frames[pos].hWnd, nullptr, nullptr, RDW_FRAME | RDW_UPDATENOW | RDW_INVALIDATE);
+ }
+ }
+ else {
+ retval = Frames[pos].HeightWhenCollapsed;
+ Frames[pos].HeightWhenCollapsed = lParam;
+ if (!CLUIFramesFitInSize())
+ Frames[pos].HeightWhenCollapsed = retval;
+ retval = Frames[pos].HeightWhenCollapsed;
+ }
+ return retval;
+
+ case FO_FLOATING:
+ if (lParam < 0)
+ return -1;
+ else {
+ int id = Frames[pos].id;
+ Frames[pos].floating = !(lParam);
+ lck.unlock();
+
+ CLUIFrameSetFloat(id, 1);//lparam=1 use stored width and height
+ }
+ return wParam;
+
+ case FO_ALIGN:
+ if (!(lParam&alTop || lParam&alBottom || lParam&alClient))
+ return -1;
+
+ if ((lParam&alClient) && (CLUIFramesGetalClientFrame() >= 0)) { //only one alClient frame possible
+ alclientFrame = -1;//recalc it
+ return -1;
+ }
+ Frames[pos].align = lParam;
+ return 0;
+ }
+ lck.unlock();
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ return -1;
+}
+
+static INT_PTR CLUIFramesShowAll(WPARAM, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (int i = 0; i < nFramescount; i++)
+ Frames[i].visible = TRUE;
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ return 0;
+}
+
+INT_PTR CLUIFramesShowAllTitleBars(WPARAM, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ F.TitleBar.ShowTitleBar = TRUE;
+ SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesHideAllTitleBars(WPARAM, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ F.TitleBar.ShowTitleBar = FALSE;
+ SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesShowHideFrame(WPARAM frameId, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ int pos;
+ {
+ mir_cslock lck(csFrameHook);
+ pos = id2pos(frameId);
+ if (pos >= 0 && !mir_wstrcmp(Frames[pos].name, L"My contacts"))
+ Frames[pos].visible = 1;
+ else {
+ if (pos >= 0 && (int)pos < nFramescount)
+ Frames[pos].visible = !Frames[pos].visible;
+ if (Frames[pos].floating)
+ CLUIFrameResizeFloatingFrame(pos);
+ }
+ }
+
+ if (!Frames[pos].floating)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesShowHideFrameTitleBar(WPARAM frameId, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(frameId);
+ if (pos >= 0 && (int)pos < nFramescount) {
+ Frames[pos].TitleBar.ShowTitleBar = !Frames[pos].TitleBar.ShowTitleBar;
+ SetWindowPos(Frames[pos].hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ }
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+// lparam=-1 up ,1 down
+INT_PTR CLUIFramesMoveUpDown(WPARAM frameId, LPARAM lParam)
+{
+ int i, tmpval;
+
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslockfull lck(csFrameHook);
+ int pos = id2pos(frameId);
+ if (pos < 0 || pos >= nFramescount)
+ return 0;
+
+ int curalign = Frames[pos].align;
+ int v = 0;
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.floating || (!F.visible) || (F.align != curalign))
+ continue;
+ g_sd[v].order = F.order;
+ g_sd[v].realpos = i;
+ v++;
+ }
+ if (v == 0)
+ return 0;
+
+ qsort(g_sd, v, sizeof(SortData), sortfunc);
+ for (i = 0; i < v; i++) {
+ if (g_sd[i].realpos == pos) {
+ if (lParam == -1) {
+ if (i < 1) break;
+ tmpval = Frames[g_sd[i - 1].realpos].order;
+ Frames[g_sd[i - 1].realpos].order = Frames[pos].order;
+ Frames[pos].order = tmpval;
+ break;
+ }
+ if (lParam == 1) {
+ if (i > v - 1) break;
+ tmpval = Frames[g_sd[i + 1].realpos].order;
+ Frames[g_sd[i + 1].realpos].order = Frames[pos].order;
+ Frames[pos].order = tmpval;
+ break;
+ }
+ }
+ }
+ lck.unlock();
+
+ CLUIFramesReSort();
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
+ return 0;
+}
+
+static INT_PTR CLUIFramesMoveUp(WPARAM frameId, LPARAM)
+{
+ return CLUIFramesMoveUpDown(frameId, -1);
+}
+
+static INT_PTR CLUIFramesMoveDown(WPARAM frameId, LPARAM)
+{
+ return CLUIFramesMoveUpDown(frameId, 1);
+}
+
+//lparam=alignment
+INT_PTR CLUIFramesSetAlign(WPARAM frameId, LPARAM lParam)
+{
+ if (FramesSysNotStarted) return -1;
+
+ CLUIFramesSetFrameOptions(MAKEWPARAM(FO_ALIGN, frameId), lParam);
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesSetAlignalTop(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ return CLUIFramesSetAlign(wParam, alTop);
+}
+
+INT_PTR CLUIFramesSetAlignalBottom(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ return CLUIFramesSetAlign(wParam, alBottom);
+}
+
+INT_PTR CLUIFramesSetAlignalClient(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ return CLUIFramesSetAlign(wParam, alClient);
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesLockUnlockFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos >= 0 && (int)pos < nFramescount) {
+ Frames[pos].Locked = !Frames[pos].Locked;
+ CLUIFramesStoreFrameSettings(pos);
+ }
+ return 0;
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesSetUnSetBorder(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ HWND hw;
+ int FrameId, oldflags;
+ {
+ mir_cslock lck(csFrameHook);
+ FrameId = id2pos(wParam);
+ if (FrameId == -1)
+ return -1;
+
+ oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0);
+ if (oldflags & F_NOBORDER)
+ oldflags &= (~F_NOBORDER);
+ else
+ oldflags |= F_NOBORDER;
+
+ hw = Frames[FrameId].hWnd;
+ }
+
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags);
+ SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER);
+ return 0;
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesSetUnSetSkinned(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ HWND hw;
+ int FrameId, oldflags;
+ {
+ mir_cslock lck(csFrameHook);
+ FrameId = id2pos(wParam);
+ if (FrameId == -1)
+ return -1;
+
+ oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0);
+ if (oldflags & F_SKINNED)
+ oldflags &= ~F_SKINNED;
+ else
+ oldflags |= F_SKINNED;
+
+ hw = Frames[FrameId].hWnd;
+ }
+
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags);
+ SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER);
+ return 0;
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesCollapseUnCollapseFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ mir_cslockfull lck(csFrameHook);
+ int FrameId = id2pos(wParam);
+ if (FrameId < 0 || FrameId >= nFramescount)
+ return -1;
+
+ int oldHeight;
+
+ // do not collapse/uncollapse client/locked/invisible frames
+ if (Frames[FrameId].align == alClient && !(Frames[FrameId].Locked || (!Frames[FrameId].visible) || Frames[FrameId].floating)) {
+ RECT rc;
+ if (Clist_IsDocked())
+ return 0;
+
+ if (db_get_b(0, "CLUI", "AutoSize", 0))
+ return 0;
+
+ GetWindowRect(g_clistApi.hwndContactList, &rc);
+
+ if (Frames[FrameId].collapsed == TRUE) {
+ rc.bottom -= rc.top;
+ rc.bottom -= Frames[FrameId].height;
+ Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height;
+ Frames[FrameId].collapsed = FALSE;
+ }
+ else {
+ rc.bottom -= rc.top;
+ rc.bottom += Frames[FrameId].HeightWhenCollapsed;
+ Frames[FrameId].collapsed = TRUE;
+ }
+
+ SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, rc.right - rc.left, rc.bottom, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
+
+ CLUIFramesStoreAllFrames();
+ lck.unlock();
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+
+ }
+ if (Frames[FrameId].Locked || (!Frames[FrameId].visible))
+ return 0;
+
+ oldHeight = Frames[FrameId].height;
+
+ // if collapsed, uncollapse
+ if (Frames[FrameId].collapsed == TRUE) {
+ Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height;
+ Frames[FrameId].height = UNCOLLAPSED_FRAME_SIZE;
+ Frames[FrameId].collapsed = FALSE;
+ }
+ // if uncollapsed, collapse
+ else {
+ Frames[FrameId].height = Frames[FrameId].HeightWhenCollapsed;
+ Frames[FrameId].collapsed = TRUE;
+ }
+
+ if (!Frames[FrameId].floating) {
+
+ if (!CLUIFramesFitInSize()) {
+ //cant collapse,we can resize only for height<alclient frame height
+ int alfrm = CLUIFramesGetalClientFrame();
+
+ if (alfrm != -1) {
+ Frames[FrameId].collapsed = FALSE;
+ if (Frames[alfrm].height > 2 * UNCOLLAPSED_FRAME_SIZE) {
+ oldHeight = Frames[alfrm].height - UNCOLLAPSED_FRAME_SIZE;
+ Frames[FrameId].collapsed = TRUE;
+ }
+ }
+ else {
+ int i, sumheight = 0;
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) {
+ sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2;
+ return FALSE;
+ }
+ if (sumheight > ContactListHeight - 0 - 2)
+ Frames[FrameId].height = (ContactListHeight - 0 - 2) - sumheight;
+ }
+ }
+ Frames[FrameId].height = oldHeight;
+ if (Frames[FrameId].collapsed == FALSE) {
+ if (Frames[FrameId].floating)
+ SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, Frames[FrameId].wndSize.right - Frames[FrameId].wndSize.left + 6, Frames[FrameId].height + DEFAULT_TITLEBAR_HEIGHT + 4, SWP_SHOWWINDOW | SWP_NOMOVE);
+ return -1;
+ }
+ }
+ }
+ lck.unlock();
+ if (!Frames[FrameId].floating)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ else {
+ RECT contwnd;
+ GetWindowRect(Frames[FrameId].ContainerWnd, &contwnd);
+ contwnd.top = contwnd.bottom - contwnd.top;//height
+ contwnd.left = contwnd.right - contwnd.left;//width
+
+ contwnd.top -= (oldHeight - Frames[FrameId].height);//newheight
+ SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, contwnd.left, contwnd.top, SWP_SHOWWINDOW | SWP_NOMOVE);
+ }
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ CLUIFramesStoreAllFrames();
+ return 0;
+}
+
+static int CLUIFramesLoadMainMenu()
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (auto &it : g_frameMenus)
+ Menu_RemoveItem(it);
+ g_frameMenus.destroy();
+
+ // create frames menu
+ CMenuItem mi(&g_plugin);
+ mi.root = cont.MainMenuItem;
+ mi.flags = CMIF_UNICODE | CMIF_SYSTEM;
+ int separator = (int)3000200000;
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ mi.hIcolibItem = F.TitleBar.hicon;
+ mi.position = separator;
+ mi.name.w = F.TitleBar.tbname ? F.TitleBar.tbname : F.name;
+ mi.pszService = nullptr;
+ g_frameMenus.insert(F.MenuHandles.MainMenuItem = Menu_AddMainMenuItem(&mi));
+ CLUIFramesCreateMenuForFrame(F.id, F.MenuHandles.MainMenuItem, separator, true);
+ CLUIFramesModifyMainMenuItems(F.id, 0);
+ CallService(MS_CLIST_FRAMEMENUNOTIFY, (WPARAM)F.id, (LPARAM)F.MenuHandles.MainMenuItem);
+ separator++;
+ }
+ return 0;
+}
+
+static HFONT CLUILoadTitleBarFont()
+{
+ char facename[] = "MS Shell Dlg";
+ LOGFONT logfont;
+ memset(&logfont, 0, sizeof(logfont));
+ memcpy(logfont.lfFaceName, facename, sizeof(facename));
+ logfont.lfWeight = FW_NORMAL;
+ logfont.lfHeight = -10;
+ return CreateFontIndirect(&logfont);
+}
+
+static int UpdateTBToolTip(int framepos)
+{
+ TOOLINFO ti;
+
+ memset(&ti, 0, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.lpszText = Frames[framepos].TitleBar.tooltip;
+ ti.hinst = g_plugin.getInst();
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)Frames[framepos].TitleBar.hwnd;
+
+ return SendMessage(Frames[framepos].TitleBar.hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
+};
+
+int FrameNCPaint(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar)
+{
+ RECT rcWindow, rc;
+ HWND hwndParent = GetParent(hwnd);
+ LRESULT result = 0;
+
+ if (hwndParent != g_clistApi.hwndContactList || !cfg::dat.bSkinnedScrollbar)
+ result = CallWindowProc(oldWndProc, hwnd, WM_NCPAINT, wParam, lParam);
+ if (!g_clistApi.hwndContactList || hwndParent != g_clistApi.hwndContactList)
+ return result;
+
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SKINNEDFRAME) {
+ StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr;
+ if (item == nullptr)
+ return 0;
+
+ GetWindowRect(hwnd, &rcWindow);
+ rc.left = rc.top = 0;
+ rc.right = rcWindow.right - rcWindow.left;
+ rc.bottom = rcWindow.bottom - rcWindow.top;
+
+ HDC hdc = GetWindowDC(hwnd);
+ if (hwnd == g_clistApi.hwndContactTree) {
+ HDC realDC = CreateCompatibleDC(hdc);
+ HBITMAP hbmDraw = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(SelectObject(realDC, hbmDraw));
+
+ ExcludeClipRect(realDC, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
+ BitBlt(realDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY);
+ DrawAlpha(realDC, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+
+ ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
+ BitBlt(hdc, 0, 0, rc.right, rc.bottom, realDC, 0, 0, SRCCOPY);
+ SelectObject(realDC, hbmOld);
+ DeleteObject(hbmDraw);
+ DeleteDC(realDC);
+ }
+ else {
+ ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
+ BitBlt(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY);
+ DrawAlpha(hdc, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ }
+ ReleaseDC(hwnd, hdc);
+ return 0;
+ }
+
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) {
+ HDC hdc = GetWindowDC(hwnd);
+ HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames));
+ GetWindowRect(hwnd, &rcWindow);
+ rc.left = rc.top = 0;
+ rc.right = rcWindow.right - rcWindow.left;
+ rc.bottom = rcWindow.bottom - rcWindow.top;
+ HBRUSH brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)));
+ Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
+ SelectObject(hdc, hPenOld);
+ SelectObject(hdc, brold);
+ ReleaseDC(hwnd, hdc);
+ return 0;
+ }
+
+ return result;
+}
+
+int FrameNCCalcSize(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar)
+{
+ StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr;
+ LRESULT orig = oldWndProc ? CallWindowProc(oldWndProc, hwnd, WM_NCCALCSIZE, wParam, lParam) : 0;
+ NCCALCSIZE_PARAMS *nccp = (NCCALCSIZE_PARAMS *)lParam;
+ uint32_t dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
+
+ if (item == nullptr)
+ return orig;
+
+ if (item->IGNORED || !(dwStyle & CLS_SKINNEDFRAME) || GetParent(hwnd) != g_clistApi.hwndContactList)
+ return orig;
+
+ nccp->rgrc[0].left += item->MARGIN_LEFT;
+ nccp->rgrc[0].right -= item->MARGIN_RIGHT;
+ nccp->rgrc[0].bottom -= item->MARGIN_BOTTOM;
+ nccp->rgrc[0].top += item->MARGIN_TOP;
+ return WVR_REDRAW;
+}
+
+static LRESULT CALLBACK FramesSubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int i;
+
+ WNDPROC oldWndProc = nullptr;
+ BOOL hasTitleBar = FALSE;
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.hWnd == hwnd) {
+ oldWndProc = F.wndProc;
+ hasTitleBar = F.TitleBar.ShowTitleBar;
+ }
+ }
+ switch (msg) {
+ case WM_NCPAINT:
+ return FrameNCPaint(hwnd, oldWndProc ? oldWndProc : DefWindowProc, wParam, lParam, hasTitleBar);
+
+ case WM_NCCALCSIZE:
+ return FrameNCCalcSize(hwnd, oldWndProc, wParam, lParam, hasTitleBar);
+
+ case WM_PRINTCLIENT:
+ return 0;
+ }
+
+ if (oldWndProc)
+ return CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+/*
+ * re-sort all frames and correct frame ordering
+ */
+
+static int CLUIFramesReSort()
+{
+ int v = 0, i;
+ int order = 1;
+
+ mir_cslock lck(csFrameHook);
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.align != alTop)
+ continue;
+ g_sd[v].order = F.order;
+ g_sd[v].realpos = i;
+ v++;
+ }
+ if (v > 0) {
+ qsort(g_sd, v, sizeof(SortData), sortfunc);
+ for (i = 0; i < v; i++)
+ Frames[g_sd[i].realpos].order = order++;
+ }
+
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ v = 0;
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.align != alBottom)
+ continue;
+ g_sd[v].order = F.order;
+ g_sd[v].realpos = i;
+ v++;
+ }
+ if (v > 0) {
+ qsort(g_sd, v, sizeof(SortData), sortfunc);
+ for (i = 0; i < v; i++)
+ Frames[g_sd[i].realpos].order = order++;
+ }
+ CLUIFramesStoreAllFrames();
+ return 0;
+}
+
+//wparam=(CLISTFrame*)clfrm
+INT_PTR CLUIFramesAddFrame(WPARAM wParam, LPARAM lParam)
+{
+ int style;
+ CLISTFrame *clfrm = (CLISTFrame *)wParam;
+
+ if (g_clistApi.hwndContactList == nullptr) return -1;
+ if (FramesSysNotStarted) return -1;
+ if (clfrm->cbSize != sizeof(CLISTFrame)) return -1;
+
+ mir_cslockfull lck(csFrameHook);
+ if (nFramescount >= MAX_FRAMES)
+ return -1;
+
+ if (Frames == nullptr) {
+ Frames = (FRAMEWND*)malloc(sizeof(FRAMEWND) * (MAX_FRAMES + 2));
+ memset(Frames, 0, (sizeof(FRAMEWND) * (MAX_FRAMES + 2)));
+ }
+ memset(&Frames[nFramescount], 0, sizeof(FRAMEWND));
+
+ Frames[nFramescount].id = NextFrameId++;
+ Frames[nFramescount].align = clfrm->align;
+ Frames[nFramescount].hWnd = clfrm->hWnd;
+ Frames[nFramescount].height = clfrm->height;
+ Frames[nFramescount].TitleBar.hicon = clfrm->hIcon;
+ Frames[nFramescount].floating = false;
+ Frames[nFramescount].pPlugin = (HPLUGIN)lParam;
+
+ if (clfrm->Flags & F_NO_SUBCONTAINER)
+ Frames[nFramescount].OwnerWindow = (HWND)-2;
+ else
+ Frames[nFramescount].OwnerWindow = g_clistApi.hwndContactList;
+
+ SetClassLong(clfrm->hWnd, GCL_STYLE, GetClassLong(clfrm->hWnd, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
+ SetWindowLongPtr(clfrm->hWnd, GWL_STYLE, GetWindowLongPtr(clfrm->hWnd, GWL_STYLE) | WS_CLIPCHILDREN);
+
+ if (GetCurrentThreadId() == GetWindowThreadProcessId(clfrm->hWnd, nullptr)) {
+ if (clfrm->hWnd != g_clistApi.hwndContactTree && clfrm->hWnd != g_hwndViewModeFrame && clfrm->hWnd != g_hwndEventArea) {
+ Frames[nFramescount].wndProc = (WNDPROC)GetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC);
+ SetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC, (LONG_PTR)FramesSubClassProc);
+ }
+ }
+
+ if (clfrm->hWnd == g_hwndEventArea)
+ wndFrameEventArea = &Frames[nFramescount];
+ else if (clfrm->hWnd == g_clistApi.hwndContactTree)
+ wndFrameCLC = &Frames[nFramescount];
+ else if (clfrm->hWnd == g_hwndViewModeFrame)
+ wndFrameViewMode = &Frames[nFramescount];
+
+ Frames[nFramescount].dwFlags = clfrm->Flags;
+
+ if (clfrm->szName.a == nullptr || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szName.w) : mir_strlen(clfrm->szName.a)) == 0) {
+ wchar_t ptszClassName[256];
+ GetClassName(Frames[nFramescount].hWnd, ptszClassName, _countof(ptszClassName));
+ Frames[nFramescount].name = mir_wstrdup(ptszClassName);
+ }
+ else Frames[nFramescount].name = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szName.w) : mir_a2u(clfrm->szName.a);
+
+ if (IsBadCodePtr((FARPROC)clfrm->szTBname.a) || clfrm->szTBname.a == nullptr
+ || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szTBname.w) : mir_strlen(clfrm->szTBname.a)) == 0)
+ Frames[nFramescount].TitleBar.tbname = mir_wstrdup(Frames[nFramescount].name);
+ else
+ Frames[nFramescount].TitleBar.tbname = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szTBname.w) : mir_a2u(clfrm->szTBname.a);
+ Frames[nFramescount].needhide = FALSE;
+ Frames[nFramescount].TitleBar.ShowTitleBar = (clfrm->Flags & F_SHOWTB ? TRUE : FALSE);
+ Frames[nFramescount].TitleBar.ShowTitleBarTip = (clfrm->Flags & F_SHOWTBTIP ? TRUE : FALSE);
+
+ Frames[nFramescount].collapsed = clfrm->Flags & F_UNCOLLAPSED ? FALSE : TRUE;
+ Frames[nFramescount].Locked = clfrm->Flags & F_LOCKED ? TRUE : FALSE;
+ Frames[nFramescount].visible = clfrm->Flags & F_VISIBLE ? TRUE : FALSE;
+
+ Frames[nFramescount].UseBorder = (clfrm->Flags & F_NOBORDER) ? FALSE : TRUE;
+ Frames[nFramescount].Skinned = (clfrm->Flags & F_SKINNED) ? TRUE : FALSE;
+
+ // create frame
+ Frames[nFramescount].TitleBar.hwnd =
+ CreateWindow(CLUIFrameTitleBarClassName, Frames[nFramescount].name,
+ (db_get_b(0, CLUIFrameModule, "RemoveAllTitleBarBorders", 1) ? 0 : WS_BORDER)
+ | WS_CHILD | WS_CLIPCHILDREN | (Frames[nFramescount].TitleBar.ShowTitleBar ? WS_VISIBLE : 0) |
+ WS_CLIPCHILDREN, 0, 0, 0, 0, g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr);
+
+ SetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWLP_USERDATA, Frames[nFramescount].id);
+
+ Frames[nFramescount].TitleBar.hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr);
+
+ SetWindowPos(Frames[nFramescount].TitleBar.hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ {
+ TOOLINFOA ti = { 0 };
+ ti.cbSize = sizeof(ti);
+ ti.lpszText = "";
+ ti.hinst = g_plugin.getInst();
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)Frames[nFramescount].TitleBar.hwnd;
+ SendMessageA(Frames[nFramescount].TitleBar.hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+ }
+
+ SendMessage(Frames[nFramescount].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[nFramescount].TitleBar.ShowTitleBarTip, 0);
+
+ Frames[nFramescount].oldstyles = GetWindowLongPtr(Frames[nFramescount].hWnd, GWL_STYLE);
+ Frames[nFramescount].TitleBar.oldstyles = GetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWL_STYLE);
+
+ int retval = Frames[nFramescount].id;
+ Frames[nFramescount].order = nFramescount + 1;
+ nFramescount++;
+
+ CLUIFramesLoadFrameSettings(id2pos(retval));
+ style = GetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE);
+ style &= ~(WS_BORDER);
+ style |= ((Frames[nFramescount - 1].UseBorder) ? WS_BORDER : 0);
+
+ style |= Frames[nFramescount - 1].Skinned ? CLS_SKINNEDFRAME : 0;
+
+ SetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE, style);
+ SetWindowLongPtr(Frames[nFramescount - 1].TitleBar.hwnd, GWL_STYLE, style & ~(WS_VSCROLL | WS_HSCROLL));
+
+ if (Frames[nFramescount - 1].order == 0)
+ Frames[nFramescount - 1].order = nFramescount;
+
+ lck.unlock();
+
+ alclientFrame = -1;//recalc it
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+
+ if (Frames[nFramescount - 1].floating) {
+ Frames[nFramescount - 1].floating = FALSE;
+ CLUIFrameSetFloat(retval, 1);//lparam=1 use stored width and height
+ }
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return retval;
+}
+
+static INT_PTR CLUIFramesRemoveFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos < 0 || pos > nFramescount)
+ return -1;
+
+ FRAMEWND* F = &Frames[pos];
+ if (F->hWnd == g_hwndEventArea)
+ wndFrameEventArea = nullptr;
+ else if (F->hWnd == g_clistApi.hwndContactTree)
+ wndFrameCLC = nullptr;
+ else if (F->hWnd == g_hwndViewModeFrame)
+ wndFrameViewMode = nullptr;
+
+ mir_free(F->name);
+ mir_free(F->TitleBar.tbname);
+ mir_free(F->TitleBar.tooltip);
+
+ DestroyWindow(F->hWnd);
+ F->hWnd = (HWND)-1;
+ DestroyWindow(F->TitleBar.hwnd);
+ F->TitleBar.hwnd = (HWND)-1;
+ DestroyWindow(F->ContainerWnd);
+ F->ContainerWnd = (HWND)-1;
+ DestroyMenu(F->TitleBar.hmenu);
+
+ RemoveItemFromList(pos, &Frames, &nFramescount);
+ }
+
+ if (!cfg::shutDown) {
+ InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesForceUpdateTB(const FRAMEWND *Frame)
+{
+ if (Frame->TitleBar.hwnd != nullptr)
+ RedrawWindow(Frame->TitleBar.hwnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+ return 0;
+}
+
+INT_PTR CLUIFramesForceUpdateFrame(const FRAMEWND *Frame)
+{
+ if (Frame->hWnd != nullptr)
+ RedrawWindow(Frame->hWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_FRAME | RDW_ERASE | RDW_INVALIDATE);
+
+ if (Frame->floating)
+ if (Frame->ContainerWnd != nullptr) RedrawWindow(Frame->ContainerWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+ return 0;
+}
+
+int CLUIFrameMoveResize(const FRAMEWND *Frame)
+{
+ TitleBarH = cfg::dat.titleBarHeight;
+ // we need to show or hide the frame?
+ if (Frame->visible && (!Frame->needhide)) {
+ ShowWindow(Frame->hWnd, SW_SHOW);
+ ShowWindow(Frame->TitleBar.hwnd, Frame->TitleBar.ShowTitleBar == TRUE ? SW_SHOW : SW_HIDE);
+ }
+ else {
+ ShowWindow(Frame->hWnd, SW_HIDE);
+ ShowWindow(Frame->TitleBar.hwnd, SW_HIDE);
+ return 0;
+ }
+
+ SetWindowPos(Frame->hWnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset,
+ (Frame->wndSize.right - Frame->wndSize.left),
+ (Frame->wndSize.bottom - Frame->wndSize.top), SWP_NOZORDER | SWP_NOREDRAW);
+ if (Frame->TitleBar.ShowTitleBar) {
+ SetWindowPos(Frame->TitleBar.hwnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset - TitleBarH,
+ (Frame->wndSize.right - Frame->wndSize.left),
+ TitleBarH + (Frame->UseBorder ? (!Frame->collapsed ? (Frame->align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER);
+ }
+ return 0;
+}
+
+bool CLUIFramesFitInSize(void)
+{
+ int i;
+ int sumheight = 0;
+ int tbh = 0; // title bar height
+ int clientfrm;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ clientfrm = CLUIFramesGetalClientFrame();
+ if (clientfrm != -1)
+ tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) {
+ sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2/*+btoint(F.UseBorder)*2*/;
+ if (sumheight > ContactListHeight - tbh - 2)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int CLUIFramesGetMinHeight()
+{
+ if (g_clistApi.hwndContactList == nullptr)
+ return 0;
+
+ int i, tbh, clientfrm, sumheight = 0;
+ RECT border;
+ int allbord = 0;
+ {
+ mir_cslock lck(csFrameHook);
+
+ TitleBarH = cfg::dat.titleBarHeight;
+ // search for alClient frame and get the titlebar's height
+ tbh = 0;
+ clientfrm = CLUIFramesGetalClientFrame();
+ if (clientfrm != -1)
+ tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((F.align != alClient) && (F.visible) && (!F.needhide) && (!F.floating)) {
+ RECT wsize;
+
+ GetWindowRect(F.hWnd, &wsize);
+ sumheight += (wsize.bottom - wsize.top) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 3;
+ }
+ }
+ }
+
+ GetBorderSize(g_clistApi.hwndContactList, &border);
+ return(sumheight + border.top + border.bottom + allbord + tbh + 3);
+}
+
+int SizeMoveNewSizes()
+{
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.floating)
+ CLUIFrameResizeFloatingFrame(i);
+ else
+ CLUIFrameMoveResize(&F);
+ }
+ return 0;
+}
+
+/*
+ * changed Nightwish
+ * gap calculation was broken. Now, it doesn't calculate and store the gaps in Frames[] anymore.
+ * instead, it remembers the smallest wndSize.top value (which has to be the top frame) and then passes
+ * the gap to all following frame(s) to the actual resizing function which just adds the gap to
+ * wndSize.top and corrects the frame height accordingly.
+
+ * Title bar gap has been removed (can be simulated by using a clist_nicer skin item for frame title bars
+ * and setting the bottom margin of the skin item
+ */
+
+int CLUIFramesResize(const RECT newsize)
+{
+ int sumheight = 9999999;
+ int clientframe = -1;
+ int i, j;
+ int topOff = 0, botOff = 0, last_bottomtop;
+
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+ int sepw = GapBetweenFrames;
+
+ if (nFramescount < 1 || cfg::shutDown)
+ return 0;
+
+ int newheight = newsize.bottom - newsize.top;
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ // search for alClient frame and get the titlebar's height
+ int tbh = 0;
+ int clientfrm = CLUIFramesGetalClientFrame();
+ if (clientfrm != -1)
+ tbh = (TitleBarH)* btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (!F.floating) {
+ F.needhide = FALSE;
+ F.wndSize.left = 0;
+ F.wndSize.right = newsize.right - newsize.left;
+ }
+ }
+ {
+ //sorting stuff
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ for (i = 0; i < nFramescount; i++) {
+ g_sd[i].order = Frames[i].order;
+ g_sd[i].realpos = i;
+ }
+ qsort(g_sd, nFramescount, sizeof(SortData), sortfunc);
+
+ }
+ int drawitems = nFramescount;
+ while (sumheight >(newheight - tbh) && drawitems > 0) {
+ sumheight = 0;
+ drawitems = 0;
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (((F.align != alClient)) && (!F.floating) && (F.visible) && (!F.needhide)) {
+ drawitems++;
+ int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
+ sumheight += (F.height) + curfrmtbh + (i > 0 ? sepw : 0) + (F.UseBorder ? 2 : 0);
+ if (sumheight > newheight - tbh) {
+ sumheight -= (F.height) + curfrmtbh + (i > 0 ? sepw : 0);
+ F.needhide = TRUE;
+ drawitems--;
+ break;
+ }
+ }
+ }
+ }
+
+ int prevframe = -1;
+ int prevframebottomline = 0;
+ for (j = 0; j < nFramescount; j++) {
+ // move all alTop frames
+ i = g_sd[j].realpos;
+ FRAMEWND &F = Frames[i];
+ if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alTop)) {
+ int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
+ F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (curfrmtbh);
+ F.wndSize.bottom = F.height + F.wndSize.top + (F.UseBorder ? 2 : 0);
+ F.prevvisframe = prevframe;
+ prevframe = i;
+ prevframebottomline = F.wndSize.bottom;
+ topOff = prevframebottomline;
+ }
+ }
+
+ if (sumheight < newheight) {
+ for (j = 0; j < nFramescount; j++) {
+ // move alClient frame
+ i = g_sd[j].realpos;
+ FRAMEWND &F = Frames[i];
+ if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alClient)) {
+ int oldh;
+ F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (tbh);
+ F.wndSize.bottom = F.wndSize.top + newheight - sumheight - tbh - ((prevframebottomline > 0) ? sepw : 0);
+ clientframe = i;
+ oldh = F.height;
+ F.height = F.wndSize.bottom - F.wndSize.top;
+ F.prevvisframe = prevframe;
+ prevframe = i;
+ prevframebottomline = F.wndSize.bottom;
+ if (prevframebottomline > newheight) {
+ // prevframebottomline-=F.height+(tbh+1);
+ // F.needhide=TRUE;
+ }
+ break;
+ }
+ }
+ }
+
+ // newheight
+ prevframebottomline = last_bottomtop = newheight;
+ for (j = nFramescount - 1; j >= 0; j--) {
+ // move all alBottom frames
+ i = g_sd[j].realpos;
+ FRAMEWND &F = Frames[i];
+ if ((F.visible) && (!F.floating) && (!F.needhide) && (F.align == alBottom)) {
+ int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
+ F.wndSize.bottom = prevframebottomline - ((prevframebottomline < newheight) ? sepw : 0);
+ F.wndSize.top = F.wndSize.bottom - F.height - (F.UseBorder ? 2 : 0);
+ F.prevvisframe = prevframe;
+ prevframe = i;
+ prevframebottomline = F.wndSize.top - curfrmtbh;
+ botOff = prevframebottomline;
+ last_bottomtop = F.wndSize.top - curfrmtbh;
+ }
+ }
+
+ // correct client frame bottom gap if there is no other top frame.
+ if (clientframe != -1) {
+ Frames[clientframe].wndSize.bottom = last_bottomtop - (last_bottomtop < newheight ? sepw : 0);
+ Frames[clientframe].height = Frames[clientframe].wndSize.bottom - Frames[clientframe].wndSize.top;
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesUpdateFrame(WPARAM wParam, LPARAM lParam)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (wParam == -1) {
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ return 0;
+ }
+
+ if (lParam & FU_FMPOS)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 1);
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos < 0 || pos >= nFramescount)
+ return -1;
+
+ if (lParam & FU_TBREDRAW)
+ CLUIFramesForceUpdateTB(&Frames[pos]);
+ if (lParam & FU_FMREDRAW)
+ CLUIFramesForceUpdateFrame(&Frames[pos]);
+ return 0;
+}
+
+int dock_prevent_moving = 0;
+
+int CLUIFramesApplyNewSizes(int mode)
+{
+ dock_prevent_moving = 0;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((mode == 1 && F.OwnerWindow != (HWND)-2 && F.OwnerWindow) ||
+ (mode == 2 && F.OwnerWindow == (HWND)-2) || (mode == 3))
+ if (F.floating)
+ CLUIFrameResizeFloatingFrame(i);
+ else
+ CLUIFrameMoveResize(&Frames[i]);
+ }
+ dock_prevent_moving = 1;
+ return 0;
+}
+
+RECT old_window_rect = { 0 }, new_window_rect = { 0 };
+
+int SizeFramesByWindowRect(RECT *r)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ mir_cslock lck(csFrameHook);
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+
+ RECT nRect = *r;
+ nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset);
+ nRect.right -= cfg::dat.bCRight;
+ nRect.left = cfg::dat.bCLeft;
+ nRect.top = cfg::dat.topOffset;
+ ContactListHeight = nRect.bottom - nRect.top;
+
+ CLUIFramesResize(nRect);
+ {
+ int i;
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (!F.floating) {
+ if (F.OwnerWindow && F.OwnerWindow != (HWND)-2) {
+ SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset,
+ (F.wndSize.right - F.wndSize.left),
+ (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+
+ if (F.TitleBar.ShowTitleBar) {
+ SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH,
+ (F.wndSize.right - F.wndSize.left),
+ TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+ }
+ }
+ else {
+ // set frame position
+ SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset,
+ (F.wndSize.right - F.wndSize.left),
+ (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOREDRAW);
+
+ // set titlebar position
+ if (F.TitleBar.ShowTitleBar) {
+ SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH,
+ (F.wndSize.right - F.wndSize.left),
+ TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ }
+ if (F.TitleBar.ShowTitleBar)
+ UpdateWindow(F.TitleBar.hwnd);
+ }
+ }
+ }
+
+ if (GetTickCount() - LastStoreTick > 1000) {
+ CLUIFramesStoreAllFrames();
+ LastStoreTick = GetTickCount();
+ }
+ }
+ return 0;
+}
+
+int CLUIFramesOnClistResize(WPARAM wParam, LPARAM lParam)
+{
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+
+ if (FramesSysNotStarted || cfg::shutDown)
+ return -1;
+
+ RECT nRect, rcStatus;
+ int tick;
+ {
+ mir_cslock lck(csFrameHook);
+
+ GetClientRect(g_clistApi.hwndContactList, &nRect);
+ if (lParam && lParam != 1) {
+ RECT oldRect;
+ POINT pt;
+ RECT * newRect = (RECT *)lParam;
+ int dl, dt, dr, db;
+ GetWindowRect((HWND)wParam, &oldRect);
+ pt.x = nRect.left;
+ pt.y = nRect.top;
+ ClientToScreen(g_clistApi.hwndContactList, &pt);
+ dl = pt.x - oldRect.left;
+ dt = pt.y - oldRect.top;
+ dr = (oldRect.right - oldRect.left) - (nRect.right - nRect.left) - dl;
+ db = (oldRect.bottom - oldRect.top) - (nRect.bottom - nRect.top) - dt;
+ nRect.left = newRect->left + dl;
+ nRect.top = newRect->top + dt;
+ nRect.bottom = newRect->bottom - db;
+ nRect.right = newRect->right - dr;
+ }
+
+ rcStatus.top = rcStatus.bottom = 0;
+
+ nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset);
+ nRect.right -= cfg::dat.bCRight;
+ nRect.left = cfg::dat.bCLeft;
+ nRect.top = cfg::dat.topOffset;
+ ContactListHeight = nRect.bottom - nRect.top;
+
+ tick = GetTickCount();
+
+ CLUIFramesResize(nRect);
+ CLUIFramesApplyNewSizes(3);
+ }
+
+ tick = GetTickCount() - tick;
+
+ if (g_clistApi.hwndContactList != nullptr)
+ InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ if (g_clistApi.hwndContactList != nullptr)
+ UpdateWindow(g_clistApi.hwndContactList);
+
+ Sleep(0);
+
+ if (GetTickCount() - LastStoreTick > 2000) {
+ CLUIFramesStoreAllFrames();
+ LastStoreTick = GetTickCount();
+ }
+ return 0;
+}
+
+static HBITMAP hBmpBackground;
+static int backgroundBmpUse;
+static COLORREF bkColour;
+static COLORREF SelBkColour;
+boolean AlignCOLLIconToLeft; //will hide frame icon
+
+int OnFrameTitleBarBackgroundChange()
+{
+ AlignCOLLIconToLeft = db_get_b(0, "FrameTitleBar", "AlignCOLLIconToLeft", 0);
+ bkColour = db_get_dw(0, "FrameTitleBar", "BkColour", CLCDEFAULT_BKCOLOUR);
+
+ if (hBmpBackground) {
+ DeleteObject(hBmpBackground);
+ hBmpBackground = nullptr;
+ }
+ if (db_get_b(0, "FrameTitleBar", "UseBitmap", CLCDEFAULT_USEBITMAP)) {
+ ptrW tszBitmapName(db_get_wsa(0, "FrameTitleBar", "BkBitmap"));
+ if (tszBitmapName != NULL)
+ hBmpBackground = Bitmap_Load(tszBitmapName);
+ }
+ backgroundBmpUse = db_get_w(0, "FrameTitleBar", "BkBmpUse", CLCDEFAULT_BKBMPUSE);
+
+ CLUIFramesOnClistResize(0, 0);
+ return 0;
+}
+
+static int DrawTitleBar(HDC dc, RECT rect, int Frameid)
+{
+ StatusItems_t *item = arStatusItems[ID_EXTBKFRAMETITLE - ID_STATUS_OFFLINE];
+
+ /*
+ * no need to redraw anything while shutting down
+ */
+ if (cfg::shutDown)
+ return 0;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+ HDC hdcMem = CreateCompatibleDC(dc);
+ HBITMAP hBmpOsb = CreateCompatibleBitmap(dc, rect.right, rect.bottom);
+ HBITMAP hoBmp = reinterpret_cast<HBITMAP>(SelectObject(hdcMem, hBmpOsb));
+
+ SetBkMode(hdcMem, TRANSPARENT);
+
+ HBRUSH hBack = GetSysColorBrush(COLOR_3DFACE);
+ HBRUSH hoBrush = reinterpret_cast<HBRUSH>(SelectObject(hdcMem, hBack));
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(Frameid);
+ if (pos >= 0 && pos < nFramescount) {
+ HFONT oFont;
+ int fHeight, fontTop;
+ GetClientRect(Frames[pos].TitleBar.hwnd, &Frames[pos].TitleBar.wndSize);
+
+ if (cfg::clcdat) {
+ oFont = ChangeToFont(hdcMem, cfg::clcdat, FONTID_FRAMETITLE, &fHeight);
+ }
+ else {
+ oFont = reinterpret_cast<HFONT>(SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT)));
+ fHeight = 10;
+ }
+ fontTop = (TitleBarH - fHeight) / 2;
+
+ if (cfg::dat.bWallpaperMode && !Frames[pos].floating)
+ SkinDrawBg(Frames[pos].TitleBar.hwnd, hdcMem);
+
+ if (!item->IGNORED) {
+ RECT rc = Frames[pos].TitleBar.wndSize;
+ rc.top += item->MARGIN_TOP;
+ rc.bottom -= item->MARGIN_BOTTOM;
+ rc.left += item->MARGIN_LEFT;
+ rc.right -= item->MARGIN_RIGHT;
+ DrawAlpha(hdcMem, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT,
+ item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ SetTextColor(hdcMem, item->TEXTCOLOR);
+ }
+ else if (cfg::clcdat) {
+ FillRect(hdcMem, &rect, hBack);
+ SetTextColor(hdcMem, cfg::clcdat->fontInfo[FONTID_FRAMETITLE].colour);
+ }
+ else {
+ FillRect(hdcMem, &rect, hBack);
+ SetTextColor(hdcMem, GetSysColor(COLOR_BTNTEXT));
+ }
+
+ const wchar_t *pwszTitle = TranslateW_LP(Frames[pos].TitleBar.tbname, Frames[pos].pPlugin);
+ int iTitleLen = (int)mir_wstrlen(pwszTitle);
+
+ if (!AlignCOLLIconToLeft) {
+ if (Frames[pos].TitleBar.hicon != nullptr) {
+ DrawIconEx(hdcMem, 6 + cfg::dat.bClipBorder, ((TitleBarH >> 1) - 8), Frames[pos].TitleBar.hicon, 16, 16, 0, nullptr, DI_NORMAL);
+ TextOut(hdcMem, 24 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
+ }
+ else TextOut(hdcMem, 6 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
+ }
+ else TextOut(hdcMem, 18 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
+
+ if (!AlignCOLLIconToLeft)
+ DrawIconEx(hdcMem, Frames[pos].TitleBar.wndSize.right - 22, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL);
+ else
+ DrawIconEx(hdcMem, 0, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL);
+ SelectObject(hdcMem, oFont);
+ }
+ }
+
+ BitBlt(dc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, SRCCOPY);
+ SelectObject(hdcMem, hoBmp);
+ SelectObject(hdcMem, hoBrush);
+ DeleteDC(hdcMem);
+ DeleteObject(hBack);
+ DeleteObject(hBmpOsb);
+ return 0;
+}
+
+#define MPCF_CONTEXTFRAMEMENU 3
+POINT ptOld;
+short nLeft = 0;
+short nTop = 0;
+
+LRESULT CALLBACK CLUIFrameTitleBarProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rect;
+ int Frameid, Framemod, direction;
+ int xpos, ypos, framepos;
+
+ Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ memset(&rect, 0, sizeof(rect));
+
+ switch (msg) {
+ case WM_CREATE:
+ return FALSE;
+
+ case WM_MEASUREITEM:
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM:
+ return Menu_DrawItem(lParam);
+
+ case WM_ENABLE:
+ if (hwnd != nullptr) InvalidateRect(hwnd, nullptr, FALSE);
+ return 0;
+ case WM_SIZE:
+ return 0;
+
+ case WM_COMMAND:
+ if (Clist_MenuProcessCommand(LOWORD(wParam), 0, Frameid))
+ break;
+
+ if (HIWORD(wParam) == 0) {//mouse events for self created menu
+ framepos = id2pos(Frameid);
+ if (framepos == -1)
+ break;
+
+ switch (LOWORD(wParam)) {
+ case frame_menu_lock:
+ Frames[framepos].Locked = !Frames[framepos].Locked;
+ break;
+ case frame_menu_visible:
+ Frames[framepos].visible = !Frames[framepos].visible;
+ break;
+ case frame_menu_showtitlebar:
+ Frames[framepos].TitleBar.ShowTitleBar = !Frames[framepos].TitleBar.ShowTitleBar;
+ break;
+ case frame_menu_floating:
+ CLUIFrameSetFloat(Frameid, 0);
+ break;
+ }
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ }
+ break;
+
+ case WM_RBUTTONDOWN:
+ {
+ HMENU hmenu;
+ if (ServiceExists(MS_CLIST_MENUBUILDFRAMECONTEXT))
+ hmenu = (HMENU)CallService(MS_CLIST_MENUBUILDFRAMECONTEXT, Frameid, 0);
+ else {
+ framepos = id2pos(Frameid);
+
+ mir_cslock lck(csFrameHook);
+ if (framepos == -1)
+ break;
+
+ hmenu = CreatePopupMenu();
+ AppendMenu(hmenu, MF_STRING | MF_DISABLED | MF_GRAYED, 15, Frames[framepos].name);
+ AppendMenu(hmenu, MF_SEPARATOR, 16, L"");
+
+ if (Frames[framepos].Locked)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_lock, TranslateT("Lock frame"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_lock, TranslateT("Lock frame"));
+
+ if (Frames[framepos].visible)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_visible, TranslateT("Visible"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_visible, TranslateT("Visible"));
+
+ if (Frames[framepos].TitleBar.ShowTitleBar)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_showtitlebar, TranslateT("Show title bar"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_showtitlebar, TranslateT("Show title bar"));
+
+ if (Frames[framepos].Skinned)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_skinned, TranslateT("Skinned frame"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_skinned, TranslateT("Skinned frame"));
+
+ if (Frames[framepos].floating)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_floating, TranslateT("Floating"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_floating, TranslateT("Floating"));
+ }
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, nullptr);
+ DestroyMenu(hmenu);
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ Framemod = -1;
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ CallService(MS_CLIST_FRAMES_UCOLLFRAME, Frameid, 0);
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ break;
+
+ case WM_LBUTTONUP:
+ if (GetCapture() != hwnd)
+ break;
+
+ curdragbar = -1;
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ break;
+
+ case WM_LBUTTONDOWN:
+ framepos = id2pos(Frameid);
+ {
+ mir_cslock lck(csFrameHook);
+ if (framepos == -1)
+ break;
+
+ if (Frames[framepos].floating) {
+ POINT pt;
+ GetCursorPos(&pt);
+ Frames[framepos].TitleBar.oldpos = pt;
+ }
+
+ if ((!(wParam&MK_CONTROL)) && Frames[framepos].Locked && (!(Frames[framepos].floating))) {
+ if (db_get_b(0, "CLUI", "ClientAreaDrag", 0)) {
+ POINT pt;
+ GetCursorPos(&pt);
+ return SendMessage(GetParent(hwnd), WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ }
+ if (Frames[framepos].floating) {
+ RECT rc;
+ GetCursorPos(&ptOld);
+ GetWindowRect(hwnd, &rc);
+ nLeft = (short)rc.left;
+ nTop = (short)rc.top;
+ }
+ }
+ SetCapture(hwnd);
+ break;
+
+ case WM_MOUSEMOVE:
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(Frameid);
+ if (pos != -1) {
+ int oldflags;
+ char TBcapt[255];
+ mir_snprintf(TBcapt, "%s - h:%d, vis:%d, fl:%d, fl:(%d,%d,%d,%d),or: %d",
+ Frames[pos].name, Frames[pos].height, Frames[pos].visible, Frames[pos].floating,
+ Frames[pos].FloatingPos.x, Frames[pos].FloatingPos.y,
+ Frames[pos].FloatingSize.x, Frames[pos].FloatingSize.y,
+ Frames[pos].order);
+
+ oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, Frames[pos].id), 0);
+ if (!(oldflags & F_SHOWTBTIP))
+ oldflags |= F_SHOWTBTIP;
+ }
+ }
+ if (wParam & MK_LBUTTON) {
+ RECT rcMiranda;
+ RECT rcwnd, rcOverlap;
+ POINT newpt, ofspt, curpt, newpos;
+
+ mir_cslockfull lck(csFrameHook);
+
+ int pos = id2pos(Frameid);
+ if (Frames[pos].floating) {
+ GetCursorPos(&curpt);
+ rcwnd.bottom = curpt.y + 5;
+ rcwnd.top = curpt.y;
+ rcwnd.left = curpt.x;
+ rcwnd.right = curpt.x + 5;
+
+ GetWindowRect(g_clistApi.hwndContactList, &rcMiranda);
+ if (IsWindowVisible(g_clistApi.hwndContactList) && IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) {
+ int id = Frames[pos].id;
+
+ lck.unlock();
+ ofspt.x = 0;
+ ofspt.y = 0;
+ ClientToScreen(Frames[pos].TitleBar.hwnd, &ofspt);
+ ofspt.x = curpt.x - ofspt.x;
+ ofspt.y = curpt.y - ofspt.y;
+
+ CLUIFrameSetFloat(id, 0);
+ newpt.x = 0;
+ newpt.y = 0;
+ ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt);
+ SetCursorPos(newpt.x + ofspt.x, newpt.y + ofspt.y);
+ GetCursorPos(&curpt);
+
+ lck.lock();
+ Frames[pos].TitleBar.oldpos = curpt;
+ return 0;
+ }
+ }
+ else {
+ int id = Frames[pos].id;
+
+ GetCursorPos(&curpt);
+ rcwnd.bottom = curpt.y + 5;
+ rcwnd.top = curpt.y;
+ rcwnd.left = curpt.x;
+ rcwnd.right = curpt.x + 5;
+
+ GetWindowRect(g_clistApi.hwndContactList, &rcMiranda);
+
+ if (!IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) {
+ lck.unlock();
+ GetCursorPos(&curpt);
+ GetWindowRect(Frames[pos].hWnd, &rcwnd);
+ rcwnd.left = rcwnd.right - rcwnd.left;
+ rcwnd.top = rcwnd.bottom - rcwnd.top;
+ newpos.x = curpt.x;
+ newpos.y = curpt.y;
+ if (curpt.x >= (rcMiranda.right - 1))
+ newpos.x = curpt.x + 5;
+ if (curpt.x <= (rcMiranda.left + 1))
+ newpos.x = curpt.x - (rcwnd.left) - 5;
+ if (curpt.y >= (rcMiranda.bottom - 1))
+ newpos.y = curpt.y + 5;
+ if (curpt.y <= (rcMiranda.top + 1))
+ newpos.y = curpt.y - (rcwnd.top) - 5;
+
+ ofspt.x = 0;
+ ofspt.y = 0;
+ GetWindowRect(Frames[pos].TitleBar.hwnd, &rcwnd);
+ ofspt.x = curpt.x - ofspt.x;
+ ofspt.y = curpt.y - ofspt.y;
+ Frames[pos].FloatingPos.x = newpos.x;
+ Frames[pos].FloatingPos.y = newpos.y;
+ CLUIFrameSetFloat(id, 0);
+
+ lck.lock();
+ newpt.x = 0;
+ newpt.y = 0;
+ ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt);
+ GetWindowRect(Frames[pos].hWnd, &rcwnd);
+ SetCursorPos(newpt.x + (rcwnd.right - rcwnd.left) / 2, newpt.y + (rcwnd.bottom - rcwnd.top) / 2);
+ GetCursorPos(&curpt);
+ Frames[pos].TitleBar.oldpos = curpt;
+ return 0;
+ }
+ }
+ }
+ if (wParam & MK_LBUTTON) {
+ int newh = -1, prevold;
+
+ if (GetCapture() != hwnd)
+ break;
+
+ POINT pt, pt2;
+ mir_cslockfull lck(csFrameHook);
+ int pos = id2pos(Frameid);
+
+ if (Frames[pos].floating) {
+ RECT wndr;
+ GetCursorPos(&pt);
+ if ((Frames[pos].TitleBar.oldpos.x != pt.x) || (Frames[pos].TitleBar.oldpos.y != pt.y)) {
+ pt2 = pt;
+ ScreenToClient(hwnd, &pt2);
+ GetWindowRect(Frames[pos].ContainerWnd, &wndr);
+
+ POINT ptNew = pt;
+
+ nLeft += (short)ptNew.x - ptOld.x;
+ nTop += (short)ptNew.y - ptOld.y;
+
+ if (!(wParam & MK_CONTROL))
+ PositionThumb(&Frames[pos], nLeft, nTop);
+ else
+ SetWindowPos(Frames[pos].ContainerWnd, nullptr, nLeft, nTop, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+
+ ptOld = ptNew;
+
+ pt.x = nLeft;
+ pt.y = nTop;
+ Frames[pos].TitleBar.oldpos = pt;
+ }
+ return 0;
+ }
+ if (Frames[pos].prevvisframe != -1) {
+ GetCursorPos(&pt);
+
+ if ((Frames[pos].TitleBar.oldpos.x == pt.x) && (Frames[pos].TitleBar.oldpos.y == pt.y))
+ break;
+
+ ypos = rect.top + pt.y;
+ xpos = rect.left + pt.x;
+ Framemod = -1;
+
+ if (Frames[pos].align == alBottom) {
+ direction = -1;
+ Framemod = pos;
+ }
+ else {
+ direction = 1;
+ Framemod = Frames[pos].prevvisframe;
+ }
+ if (Frames[Framemod].Locked)
+ break;
+ if (curdragbar != -1 && curdragbar != pos)
+ break;
+
+ if (lbypos == -1) {
+ curdragbar = pos;
+ lbypos = ypos;
+ oldframeheight = Frames[Framemod].height;
+ SetCapture(hwnd);
+ break;
+ }
+ newh = oldframeheight + direction * (ypos - lbypos);
+ if (newh > 0) {
+ prevold = Frames[Framemod].height;
+ Frames[Framemod].height = newh;
+ if (!CLUIFramesFitInSize()) {
+ Frames[Framemod].height = prevold;
+ return TRUE;
+ }
+ Frames[Framemod].height = newh;
+ if (newh > 3) Frames[Framemod].collapsed = TRUE;
+
+ }
+ Frames[pos].TitleBar.oldpos = pt;
+ }
+ lck.unlock();
+
+ if (newh > 0)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ break;
+ }
+ curdragbar = -1;
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ break;
+
+ case WM_NCPAINT:
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) {
+ HDC hdc = GetWindowDC(hwnd);
+ HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames));
+ RECT rcWindow, rc;
+ HBRUSH brold;
+
+ CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
+ GetWindowRect(hwnd, &rcWindow);
+ rc.left = rc.top = 0;
+ rc.right = rcWindow.right - rcWindow.left;
+ rc.bottom = rcWindow.bottom - rcWindow.top;
+ brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)));
+ Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
+ SelectObject(hdc, hPenOld);
+ SelectObject(hdc, brold);
+ ReleaseDC(hwnd, hdc);
+ return 0;
+ }
+ break;
+
+ case WM_PRINT:
+ case WM_PRINTCLIENT:
+ GetClientRect(hwnd, &rect);
+ DrawTitleBar((HDC)wParam, rect, Frameid);
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT paintStruct;
+ HDC paintDC = BeginPaint(hwnd, &paintStruct);
+ rect = paintStruct.rcPaint;
+ DrawTitleBar(paintDC, rect, Frameid);
+ EndPaint(hwnd, &paintStruct);
+ }
+ return 0;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ return TRUE;
+}
+
+int CLUIFrameResizeFloatingFrame(int framepos)
+{
+ if (!Frames[framepos].floating)
+ return 0;
+ if (Frames[framepos].ContainerWnd == nullptr)
+ return 0;
+
+ RECT rect;
+ GetClientRect(Frames[framepos].ContainerWnd, &rect);
+
+ int width = rect.right - rect.left;
+ int height = rect.bottom - rect.top;
+ int floatingHeight = cfg::dat.titleBarHeight;
+
+ if (floatingHeight <= 0 || floatingHeight > 50)
+ floatingHeight = 18;
+
+ Frames[framepos].visible ? ShowWindow(Frames[framepos].ContainerWnd, SW_SHOWNOACTIVATE) : ShowWindow(Frames[framepos].ContainerWnd, SW_HIDE);
+
+ if (Frames[framepos].TitleBar.ShowTitleBar) {
+ ShowWindow(Frames[framepos].TitleBar.hwnd, SW_SHOWNOACTIVATE);
+ Frames[framepos].height = height - floatingHeight;
+ SetWindowPos(Frames[framepos].TitleBar.hwnd, HWND_TOP, 0, 0, width, floatingHeight, SWP_SHOWWINDOW | SWP_DRAWFRAME | SWP_NOACTIVATE);
+ InvalidateRect(Frames[framepos].TitleBar.hwnd, nullptr, FALSE);
+ SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, floatingHeight, width, height - floatingHeight, SWP_SHOWWINDOW | SWP_NOACTIVATE);
+
+ }
+ else {
+ Frames[framepos].height = height;
+ ShowWindow(Frames[framepos].TitleBar.hwnd, SW_HIDE);
+ SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, 0, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
+ }
+
+ if (Frames[framepos].ContainerWnd != nullptr)
+ UpdateWindow(Frames[framepos].ContainerWnd);
+ GetWindowRect(Frames[framepos].hWnd, &Frames[framepos].wndSize);
+
+ if (Frames[framepos].TitleBar.ShowTitleBar)
+ RedrawWindow(Frames[framepos].TitleBar.hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
+
+ RedrawWindow(Frames[framepos].hWnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
+ return 0;
+}
+
+static int CLUIFrameOnMainMenuBuild(WPARAM, LPARAM)
+{
+ CLUIFramesLoadMainMenu();
+ return 0;
+}
+
+LRESULT CALLBACK CLUIFrameContainerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int framepos;
+ RECT rect;
+ INT_PTR Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_CREATE:
+ {
+ mir_cslockfull lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ }
+ return 0;
+
+ case WM_GETMINMAXINFO:
+ TitleBarH = cfg::dat.titleBarHeight;
+ {
+ mir_cslock lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ if (framepos < 0 || framepos >= nFramescount)
+ break;
+
+ if (!Frames[framepos].minmaxenabled)
+ break;
+
+ if (Frames[framepos].ContainerWnd == nullptr)
+ break;
+
+ if (Frames[framepos].Locked) {
+ RECT rct;
+ GetWindowRect(hwnd, &rct);
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = rct.right - rct.left;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = rct.bottom - rct.top;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = rct.right - rct.left;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = rct.bottom - rct.top;
+ }
+
+ MINMAXINFO minmax;
+ memset(&minmax, 0, sizeof(minmax));
+ if (SendMessage(Frames[framepos].hWnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax) != 0)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ RECT border;
+ int tbh = TitleBarH * btoint(Frames[framepos].TitleBar.ShowTitleBar);
+ GetBorderSize(hwnd, &border);
+ if (minmax.ptMaxTrackSize.x != 0 && minmax.ptMaxTrackSize.y != 0) {
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = minmax.ptMinTrackSize.x;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = minmax.ptMinTrackSize.y;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = minmax.ptMaxTrackSize.x + border.left + border.right;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = minmax.ptMaxTrackSize.y + tbh + border.top + border.bottom;
+ }
+ }
+
+ case WM_MOVE:
+ {
+ mir_cslock lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ if (framepos < 0 || framepos >= nFramescount)
+ break;
+
+ if (Frames[framepos].ContainerWnd == nullptr)
+ return 0;
+
+ GetWindowRect(Frames[framepos].ContainerWnd, &rect);
+ Frames[framepos].FloatingPos.x = rect.left;
+ Frames[framepos].FloatingPos.y = rect.top;
+ Frames[framepos].FloatingSize.x = rect.right - rect.left;
+ Frames[framepos].FloatingSize.y = rect.bottom - rect.top;
+ CLUIFramesStoreFrameSettings(framepos);
+ }
+ return 0;
+
+ case WM_SIZE:
+ {
+ mir_cslock lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ if (framepos < 0 || framepos >= nFramescount)
+ break;
+
+ if (Frames[framepos].ContainerWnd == nullptr)
+ return 0;
+
+ CLUIFrameResizeFloatingFrame(framepos);
+
+ GetWindowRect(Frames[framepos].ContainerWnd, &rect);
+ Frames[framepos].FloatingPos.x = rect.left;
+ Frames[framepos].FloatingPos.y = rect.top;
+ Frames[framepos].FloatingSize.x = rect.right - rect.left;
+ Frames[framepos].FloatingSize.y = rect.bottom - rect.top;
+
+ CLUIFramesStoreFrameSettings(framepos);
+ }
+ return 0;
+
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+
+ case WM_DESTROY:
+ return 0;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+static HWND CreateContainerWindow(HWND parent, int x, int y, int width, int height)
+{
+ return(CreateWindowA("FramesContainer", "aaaa", WS_POPUP | WS_THICKFRAME, x, y, width, height, parent, nullptr, g_plugin.getInst(), nullptr));
+}
+
+INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndtmp, hwndtooltiptmp;
+ {
+ mir_cslock lck(csFrameHook);
+ wParam = id2pos(wParam);
+ if ((int)wParam >= 0 && (int)wParam < nFramescount) {
+ if (Frames[wParam].floating) {
+ SetParent(Frames[wParam].hWnd, g_clistApi.hwndContactList);
+ SetParent(Frames[wParam].TitleBar.hwnd, g_clistApi.hwndContactList);
+ Frames[wParam].floating = FALSE;
+ DestroyWindow(Frames[wParam].ContainerWnd);
+ Frames[wParam].ContainerWnd = nullptr;
+ }
+ else {
+ RECT recttb, rectw, border;
+ int temp;
+ int neww, newh;
+
+ Frames[wParam].oldstyles = GetWindowLongPtr(Frames[wParam].hWnd, GWL_STYLE);
+ Frames[wParam].TitleBar.oldstyles = GetWindowLongPtr(Frames[wParam].TitleBar.hwnd, GWL_STYLE);
+ bool locked = Frames[wParam].Locked;
+ Frames[wParam].Locked = FALSE;
+ Frames[wParam].minmaxenabled = FALSE;
+
+ GetWindowRect(Frames[wParam].hWnd, &rectw);
+ GetWindowRect(Frames[wParam].TitleBar.hwnd, &recttb);
+ if (!Frames[wParam].TitleBar.ShowTitleBar)
+ recttb.top = recttb.bottom = recttb.left = recttb.right = 0;
+
+ Frames[wParam].ContainerWnd = CreateContainerWindow(g_clistApi.hwndContactList, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, 10, 10);
+
+ SetParent(Frames[wParam].hWnd, Frames[wParam].ContainerWnd);
+ SetParent(Frames[wParam].TitleBar.hwnd, Frames[wParam].ContainerWnd);
+
+ GetBorderSize(Frames[wParam].ContainerWnd, &border);
+
+ SetWindowLongPtr(Frames[wParam].ContainerWnd, GWLP_USERDATA, Frames[wParam].id);
+ if ((lParam == 1)) {
+ if ((Frames[wParam].FloatingPos.x != 0) && (Frames[wParam].FloatingPos.y != 0)) {
+ if (Frames[wParam].FloatingPos.x < 20)
+ Frames[wParam].FloatingPos.x = 40;
+
+ if (Frames[wParam].FloatingPos.y < 20)
+ Frames[wParam].FloatingPos.y = 40;
+
+ SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, Frames[wParam].FloatingSize.x, Frames[wParam].FloatingSize.y, SWP_HIDEWINDOW);
+ }
+ else SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, 120, 120, 140, 140, SWP_HIDEWINDOW);
+ }
+ else {
+ neww = rectw.right - rectw.left + border.left + border.right;
+ newh = (rectw.bottom - rectw.top) + (recttb.bottom - recttb.top) + border.top + border.bottom;
+ if (neww < 20)
+ neww = 40;
+
+ if (newh < 20)
+ newh = 40;
+
+ if (Frames[wParam].FloatingPos.x < 20)
+ Frames[wParam].FloatingPos.x = 40;
+
+ if (Frames[wParam].FloatingPos.y < 20)
+ Frames[wParam].FloatingPos.y = 40;
+
+ SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, neww, newh, SWP_HIDEWINDOW);
+ }
+ SetWindowText(Frames[wParam].ContainerWnd, Frames[wParam].TitleBar.tbname);
+ temp = GetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE);
+ temp |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+ SetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE, temp);
+ Frames[wParam].floating = TRUE;
+ Frames[wParam].Locked = locked;
+ }
+ }
+
+ CLUIFramesStoreFrameSettings(wParam);
+ Frames[wParam].minmaxenabled = TRUE;
+ hwndtooltiptmp = Frames[wParam].TitleBar.hwndTip;
+
+ hwndtmp = Frames[wParam].ContainerWnd;
+ }
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ SendMessage(hwndtmp, WM_SIZE, 0, 0);
+ SetWindowPos(hwndtooltiptmp, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ return 0;
+}
+
+wchar_t g_ptszEventName[100];
+
+static int CLUIFrameOnModulesLoad(WPARAM, LPARAM)
+{
+ mir_snwprintf(g_ptszEventName, L"mf_update_evt_%d", GetCurrentThreadId());
+ g_hEventThread = CreateEvent(nullptr, TRUE, FALSE, g_ptszEventName);
+ hThreadMFUpdate = mir_forkthread(MF_UpdateThread);
+ SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_IDLE);
+ CLUIFramesLoadMainMenu();
+ CLUIFramesCreateMenuForFrame(-1, nullptr, 000010000, false);
+ return 0;
+}
+
+static int CLUIFrameLangChanged(WPARAM, LPARAM)
+{
+ ApplyViewMode(0);
+ g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ return 0;
+}
+
+static int CLUIFrameOnModulesUnload(WPARAM, LPARAM)
+{
+ mf_updatethread_running = FALSE;
+
+ SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_NORMAL);
+ SetEvent(g_hEventThread);
+ WaitForSingleObject(hThreadMFUpdate, 2000);
+ CloseHandle(g_hEventThread);
+
+ Menu_RemoveItem(cont.MIVisible);
+ Menu_RemoveItem(cont.MITitle);
+ Menu_RemoveItem(cont.MITBVisible);
+ Menu_RemoveItem(cont.MILock);
+ Menu_RemoveItem(cont.MIColl);
+ Menu_RemoveItem(cont.MIFloating);
+ Menu_RemoveItem(cont.MIAlignRoot);
+ Menu_RemoveItem(cont.MIAlignTop);
+ Menu_RemoveItem(cont.MIAlignClient);
+ Menu_RemoveItem(cont.MIAlignBottom);
+ Menu_RemoveItem(cont.MIBorder);
+ return 0;
+}
+
+/*
+ * wparam=hIcon
+ * return hImage on success,-1 on failure
+ */
+
+int LoadCLUIFramesModule(void)
+{
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+
+ nFramescount = 0;
+
+ WNDCLASS wndclass = {};
+ wndclass.style = CS_DBLCLKS;
+ wndclass.lpfnWndProc = CLUIFrameTitleBarProc;
+ wndclass.hInstance = g_plugin.getInst();
+ wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wndclass.lpszClassName = CLUIFrameTitleBarClassName;
+ RegisterClass(&wndclass);
+
+ WNDCLASS cntclass = {};
+ cntclass.style = CS_DBLCLKS | CS_DROPSHADOW;
+ cntclass.lpfnWndProc = CLUIFrameContainerWndProc;
+ cntclass.hInstance = g_plugin.getInst();
+ cntclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ cntclass.lpszClassName = L"FramesContainer";
+ RegisterClass(&cntclass);
+
+ // create root menu
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x3931AC4, 0x7A32, 0x4D9C, 0x99, 0x92, 0x94, 0xD4, 0xB5, 0x9B, 0xD6, 0xB6);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FRAME);
+ mi.position = 3000090000;
+ mi.name.a = LPGEN("Frames");
+ mi.pszService = nullptr;
+ cont.MainMenuItem = Menu_AddMainMenuItem(&mi);
+ UNSET_UID(mi);
+
+ mi.root = cont.MainMenuItem;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.flags = CMIF_UNMOVABLE;
+
+ // create "show all frames" menu
+ mi.uid.d[7]++;
+ mi.position = 4000090000;
+ mi.name.a = LPGEN("Show all frames");
+ mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMES;
+ Menu_AddMainMenuItem(&mi);
+
+ // create "show all titlebars" menu
+ mi.uid.d[7]++;
+ mi.position++;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP);
+ mi.name.a = LPGEN("Show all title bars");
+ mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMESTB;
+ Menu_AddMainMenuItem(&mi);
+
+ // create "hide all titlebars" menu
+ mi.uid.d[7]++;
+ mi.position++;
+ mi.name.a = LPGEN("Hide all title bars");
+ mi.pszService = MS_CLIST_FRAMES_HIDEALLFRAMESTB;
+ Menu_AddMainMenuItem(&mi);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, CLUIFrameOnModulesLoad);
+ HookEvent(ME_CLIST_PREBUILDFRAMEMENU, CLUIFramesModifyContextMenuForFrame);
+ HookEvent(ME_CLIST_PREBUILDMAINMENU, CLUIFrameOnMainMenuBuild);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, CLUIFrameOnModulesUnload);
+ HookEvent(ME_LANGPACK_CHANGED, CLUIFrameLangChanged);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_ADDFRAME, CLUIFramesAddFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_REMOVEFRAME, CLUIFramesRemoveFrame);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_SETFRAMEOPTIONS, CLUIFramesSetFrameOptions);
+ CreateServiceFunction(MS_CLIST_FRAMES_GETFRAMEOPTIONS, CLUIFramesGetFrameOptions);
+ CreateServiceFunction(MS_CLIST_FRAMES_UPDATEFRAME, CLUIFramesUpdateFrame);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_SHFRAMETITLEBAR, CLUIFramesShowHideFrameTitleBar);
+ CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMESTB, CLUIFramesShowAllTitleBars);
+ CreateServiceFunction(MS_CLIST_FRAMES_HIDEALLFRAMESTB, CLUIFramesHideAllTitleBars);
+ CreateServiceFunction(MS_CLIST_FRAMES_SHFRAME, CLUIFramesShowHideFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMES, CLUIFramesShowAll);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_ULFRAME, CLUIFramesLockUnlockFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_UCOLLFRAME, CLUIFramesCollapseUnCollapseFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_SETUNBORDER, CLUIFramesSetUnSetBorder);
+ CreateServiceFunction(MS_CLIST_FRAMES_SETSKINNED, CLUIFramesSetUnSetSkinned);
+
+ CreateServiceFunction(CLUIFRAMESSETALIGN, CLUIFramesSetAlign);
+ CreateServiceFunction(CLUIFRAMESMOVEDOWN, CLUIFramesMoveDown);
+ CreateServiceFunction(CLUIFRAMESMOVEUP, CLUIFramesMoveUp);
+
+ CreateServiceFunction(CLUIFRAMESSETALIGNALTOP, CLUIFramesSetAlignalTop);
+ CreateServiceFunction(CLUIFRAMESSETALIGNALCLIENT, CLUIFramesSetAlignalClient);
+ CreateServiceFunction(CLUIFRAMESSETALIGNALBOTTOM, CLUIFramesSetAlignalBottom);
+
+ CreateServiceFunction("Set_Floating", CLUIFrameSetFloat);
+ hWndExplorerToolBar = FindWindowExA(nullptr, nullptr, "Shell_TrayWnd", nullptr);
+ OnFrameTitleBarBackgroundChange();
+
+ FramesSysNotStarted = FALSE;
+ g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, db_get_dw(0, "CLUI", "clr_frameborder", GetSysColor(COLOR_3DDKSHADOW)));
+ return 0;
+}
+
+void LoadExtraIconModule()
+{
+ hStatusBarShowToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_SHOW_TOOLTIP);
+ hStatusBarHideToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_HIDE_TOOLTIP);
+}
+
+int UnLoadCLUIFramesModule(void)
+{
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ CLUIFramesStoreAllFrames();
+ DeleteObject(g_hPenCLUIFrames);
+
+ mir_cslock lck(csFrameHook);
+ FramesSysNotStarted = TRUE;
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ DestroyWindow(F.hWnd);
+ F.hWnd = (HWND)-1;
+ DestroyWindow(F.TitleBar.hwnd);
+ F.TitleBar.hwnd = (HWND)-1;
+ DestroyWindow(F.ContainerWnd);
+ F.ContainerWnd = (HWND)-1;
+ DestroyMenu(F.TitleBar.hmenu);
+
+ if (F.name != nullptr)
+ mir_free(F.name);
+ if (F.TitleBar.tbname != nullptr)
+ mir_free(F.TitleBar.tbname);
+ }
+ free(Frames);
+ Frames = nullptr;
+ nFramescount = 0;
+ UnregisterClass(CLUIFrameTitleBarClassName, g_plugin.getInst());
+ return 0;
+}
diff --git a/plugins/Clist_nicer/src/cluiopts.cpp b/plugins/Clist_nicer/src/cluiopts.cpp index dff966b643..1ea16fee6b 100644 --- a/plugins/Clist_nicer/src/cluiopts.cpp +++ b/plugins/Clist_nicer/src/cluiopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/cluiservices.cpp b/plugins/Clist_nicer/src/cluiservices.cpp index 3cf9271360..f5bb7a06cc 100644 --- a/plugins/Clist_nicer/src/cluiservices.cpp +++ b/plugins/Clist_nicer/src/cluiservices.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/config.cpp b/plugins/Clist_nicer/src/config.cpp index 22160f77f6..022bf2a195 100644 --- a/plugins/Clist_nicer/src/config.cpp +++ b/plugins/Clist_nicer/src/config.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/config.h b/plugins/Clist_nicer/src/config.h index 3a2a947e2a..0fb24a4f9c 100644 --- a/plugins/Clist_nicer/src/config.h +++ b/plugins/Clist_nicer/src/config.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/contact.cpp b/plugins/Clist_nicer/src/contact.cpp index 9f626b4a9c..e0825ca835 100644 --- a/plugins/Clist_nicer/src/contact.cpp +++ b/plugins/Clist_nicer/src/contact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/extBackg.cpp b/plugins/Clist_nicer/src/extBackg.cpp index c6dc99c19c..5655d67a07 100644 --- a/plugins/Clist_nicer/src/extBackg.cpp +++ b/plugins/Clist_nicer/src/extBackg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/extBackg.h b/plugins/Clist_nicer/src/extBackg.h index e418eff6ca..6892e71007 100644 --- a/plugins/Clist_nicer/src/extBackg.h +++ b/plugins/Clist_nicer/src/extBackg.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/groupmenu.cpp b/plugins/Clist_nicer/src/groupmenu.cpp index 0688aea6fa..24f68f487d 100644 --- a/plugins/Clist_nicer/src/groupmenu.cpp +++ b/plugins/Clist_nicer/src/groupmenu.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/init.cpp b/plugins/Clist_nicer/src/init.cpp index 5fe0bbcd0d..a46350cb87 100644 --- a/plugins/Clist_nicer/src/init.cpp +++ b/plugins/Clist_nicer/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/rowheight_funcs.cpp b/plugins/Clist_nicer/src/rowheight_funcs.cpp index 7f2a5722e1..6a44596915 100644 --- a/plugins/Clist_nicer/src/rowheight_funcs.cpp +++ b/plugins/Clist_nicer/src/rowheight_funcs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/statusbar.cpp b/plugins/Clist_nicer/src/statusbar.cpp index 3850a1b69c..71cbff13f6 100644 --- a/plugins/Clist_nicer/src/statusbar.cpp +++ b/plugins/Clist_nicer/src/statusbar.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/stdafx.cxx b/plugins/Clist_nicer/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/Clist_nicer/src/stdafx.cxx +++ b/plugins/Clist_nicer/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Clist_nicer/src/stdafx.h b/plugins/Clist_nicer/src/stdafx.h index 4cbd1fbf71..6762de295e 100644 --- a/plugins/Clist_nicer/src/stdafx.h +++ b/plugins/Clist_nicer/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/viewmodes.cpp b/plugins/Clist_nicer/src/viewmodes.cpp index 7a9e410a58..8d4b8e264f 100644 --- a/plugins/Clist_nicer/src/viewmodes.cpp +++ b/plugins/Clist_nicer/src/viewmodes.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/wallpaper.cpp b/plugins/Clist_nicer/src/wallpaper.cpp index e02498c784..1aa486116e 100644 --- a/plugins/Clist_nicer/src/wallpaper.cpp +++ b/plugins/Clist_nicer/src/wallpaper.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Cln_skinedit/src/main.cpp b/plugins/Cln_skinedit/src/main.cpp index e0b33ce15f..6f7c76632c 100644 --- a/plugins/Cln_skinedit/src/main.cpp +++ b/plugins/Cln_skinedit/src/main.cpp @@ -1,900 +1,900 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-04 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define ID_EXTBKSEPARATOR 40200 - -CMPlugin g_plugin; - -StatusItems_t **StatusItems; -ChangedSItems_t ChangedSItems = { 0 }; - -static int LastModifiedItem = -1; -static int last_selcount = 0; -static int last_indizes[64]; -static int ID_EXTBK_LAST = 0, ID_EXTBK_FIRST = 0; - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {21948C89-B549-4C9D-8B4F-3F3726EC6B4B} - {0x21948c89, 0xb549, 0x4c9d, {0x8b, 0x4f, 0x3f, 0x37, 0x26, 0xec, 0x6b, 0x4b}} -}; - -CMPlugin::CMPlugin() : - PLUGIN<CMPlugin>("Skin editor", pluginInfoEx) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// prototypes - -static void ChangeControlItems(HWND hwndDlg, int status, int except); -static BOOL CheckItem(int item, HWND hwndDlg); - -static void ReActiveCombo(HWND hwndDlg) -{ - if (IsDlgButtonChecked(hwndDlg, IDC_IGNORE)) { - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE); - } - else { - ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT)); - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER)); - } -} - -// enabled or disabled the whole status controlitems group (with exceptional control) -static void ChangeControlItems(HWND hwndDlg, int status, int except) -{ - if (except != IDC_GRADIENT) - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT), status); - if (except != IDC_GRADIENT_LR) - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), status); - if (except != IDC_GRADIENT_RL) - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), status); - if (except != IDC_GRADIENT_TB) - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), status); - if (except != IDC_GRADIENT_BT) - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), status); - if (except != IDC_CORNER) - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER), status); - if (except != IDC_CORNER_TL) - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status); - if (except != IDC_CORNER_TR) - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), status); - if (except != IDC_CORNER_BR) - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), status); - if (except != IDC_CORNER_BL) - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), status); - if (except != IDC_CORNER_TL) - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status); - if (except != IDC_MARGINLABLE) - EnableWindow(GetDlgItem(hwndDlg, IDC_MARGINLABLE), status); - if (except != IDC_MRGN_TOP) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP), status); - if (except != IDC_MRGN_RIGHT) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT), status); - if (except != IDC_MRGN_BOTTOM) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM), status); - if (except != IDC_MRGN_LEFT) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT), status); - if (except != IDC_MRGN_TOP_SPIN) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP_SPIN), status); - if (except != IDC_MRGN_RIGHT_SPIN) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT_SPIN), status); - if (except != IDC_MRGN_BOTTOM_SPIN) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM_SPIN), status); - if (except != IDC_MRGN_LEFT_SPIN) - EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT_SPIN), status); - if (except != IDC_BASECOLOUR) - EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR), status); - if (except != IDC_COLORLABLE) - EnableWindow(GetDlgItem(hwndDlg, IDC_COLORLABLE), status); - if (except != IDC_BASECOLOUR2) - EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), status); - if (except != IDC_COLOR2LABLE) - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), status); - if (except != IDC_COLOR2_TRANSPARENT) - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2_TRANSPARENT), status); - if (except != IDC_TEXTCOLOUR) - EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOUR), status); - if (except != IDC_TEXTCOLOURLABLE) - EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOURLABLE), status); - - if (except != IDC_ALPHA) - EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHA), status); - if (except != IDC_ALPHASPIN) - EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHASPIN), status); - if (except != IDC_ALPHALABLE) - EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHALABLE), status); - if (except != IDC_IGNORE) - EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORE), status); - - if (except != IDC_BORDERTYPE) - EnableWindow(GetDlgItem(hwndDlg, IDC_BORDERTYPE), status); -} - -static void FillOptionDialogByStatusItem(HWND hwndDlg, StatusItems_t *item) -{ - char itoabuf[15]; - uint32_t ret; - int index; - - CheckDlgButton(hwndDlg, IDC_IGNORE, (item->IGNORED) ? BST_CHECKED : BST_UNCHECKED); - - CheckDlgButton(hwndDlg, IDC_GRADIENT, (item->GRADIENT & GRADIENT_ACTIVE) ? BST_CHECKED : BST_UNCHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), item->GRADIENT & GRADIENT_ACTIVE); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), item->GRADIENT & GRADIENT_ACTIVE); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), item->GRADIENT & GRADIENT_ACTIVE); - EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), item->GRADIENT & GRADIENT_ACTIVE); - CheckDlgButton(hwndDlg, IDC_GRADIENT_LR, (item->GRADIENT & GRADIENT_LR) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_GRADIENT_RL, (item->GRADIENT & GRADIENT_RL) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_GRADIENT_TB, (item->GRADIENT & GRADIENT_TB) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_GRADIENT_BT, (item->GRADIENT & GRADIENT_BT) ? BST_CHECKED : BST_UNCHECKED); - - CheckDlgButton(hwndDlg, IDC_CORNER, (item->CORNER & CORNER_ACTIVE) ? BST_CHECKED : BST_UNCHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), item->CORNER & CORNER_ACTIVE); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), item->CORNER & CORNER_ACTIVE); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), item->CORNER & CORNER_ACTIVE); - EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), item->CORNER & CORNER_ACTIVE); - - CheckDlgButton(hwndDlg, IDC_CORNER_TL, (item->CORNER & CORNER_TL) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_CORNER_TR, (item->CORNER & CORNER_TR) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_CORNER_BR, (item->CORNER & CORNER_BR) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_CORNER_BL, (item->CORNER & CORNER_BL) ? BST_CHECKED : BST_UNCHECKED); - - ret = item->COLOR; - SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR); - SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETCOLOUR, 0, ret); - - ret = item->COLOR2; - SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR2); - SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETCOLOUR, 0, ret); - - CheckDlgButton(hwndDlg, IDC_COLOR2_TRANSPARENT, (item->COLOR2_TRANSPARENT) ? BST_CHECKED : BST_UNCHECKED); - - ret = item->TEXTCOLOR; - SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_TEXTCOLOR); - SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETCOLOUR, 0, ret); - - if (item->ALPHA == -1) { - SetDlgItemTextA(hwndDlg, IDC_ALPHA, ""); - } - else { - ret = item->ALPHA; - _itoa(ret, itoabuf, 10); - SetDlgItemTextA(hwndDlg, IDC_ALPHA, itoabuf); - } - - if (item->MARGIN_LEFT == -1) - SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, ""); - else { - ret = item->MARGIN_LEFT; - _itoa(ret, itoabuf, 10); - SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, itoabuf); - } - - if (item->MARGIN_TOP == -1) - SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, ""); - else { - ret = item->MARGIN_TOP; - _itoa(ret, itoabuf, 10); - SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, itoabuf); - } - - if (item->MARGIN_RIGHT == -1) - SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, ""); - else { - ret = item->MARGIN_RIGHT; - _itoa(ret, itoabuf, 10); - SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, itoabuf); - } - - if (item->MARGIN_BOTTOM == -1) - SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, ""); - else { - ret = item->MARGIN_BOTTOM; - _itoa(ret, itoabuf, 10); - SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, itoabuf); - } - if (item->BORDERSTYLE == -1) - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, 0, 0); - else { - index = 0; - switch (item->BORDERSTYLE) { - case 0: - case -1: - index = 0; - break; - case BDR_RAISEDOUTER: - index = 1; - break; - case BDR_SUNKENINNER: - index = 2; - break; - case EDGE_BUMP: - index = 3; - break; - case EDGE_ETCHED: - index = 4; - break; - } - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, (WPARAM)index, 0); - } - ReActiveCombo(hwndDlg); -} -// update dlg with selected item -static void FillOptionDialogByCurrentSel(HWND hwndDlg) -{ - int index = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0); - int itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, index, 0); - if (itemData != ID_EXTBKSEPARATOR) { - LastModifiedItem = itemData - ID_EXTBK_FIRST; - - if (CheckItem(itemData - ID_EXTBK_FIRST, hwndDlg)) - FillOptionDialogByStatusItem(hwndDlg, StatusItems[itemData - ID_EXTBK_FIRST]); - } -} - - -// enabled all status controls if the selected item is a separator -static BOOL CheckItem(int item, HWND hwndDlg) -{ - if (StatusItems[item]->statusID == ID_EXTBKSEPARATOR) { - ChangeControlItems(hwndDlg, 0, 0); - return FALSE; - } - else { - ChangeControlItems(hwndDlg, 1, 0); - return TRUE; - } -} - -static void SetChangedStatusItemFlag(WPARAM wParam, HWND hwndDlg) -{ - if (LOWORD(wParam) != IDC_ITEMS - && (GetDlgItem(hwndDlg, LOWORD(wParam)) == GetFocus() || HIWORD(wParam) == CPN_COLOURCHANGED) - && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CPN_COLOURCHANGED)) { - switch (LOWORD(wParam)) { - case IDC_IGNORE: - ChangedSItems.bIGNORED = TRUE; break; - case IDC_GRADIENT: - ChangedSItems.bGRADIENT = TRUE; break; - case IDC_GRADIENT_LR: - ChangedSItems.bGRADIENT = TRUE; break; - case IDC_GRADIENT_RL: - ChangedSItems.bGRADIENT = TRUE; break; - case IDC_GRADIENT_BT: - ChangedSItems.bGRADIENT = TRUE; break; - case IDC_GRADIENT_TB: - ChangedSItems.bGRADIENT = TRUE; break; - - case IDC_CORNER: - ChangedSItems.bCORNER = TRUE; break; - case IDC_CORNER_TL: - ChangedSItems.bCORNER = TRUE; break; - case IDC_CORNER_TR: - ChangedSItems.bCORNER = TRUE; break; - case IDC_CORNER_BR: - ChangedSItems.bCORNER = TRUE; break; - case IDC_CORNER_BL: - ChangedSItems.bCORNER = TRUE; break; - - case IDC_BASECOLOUR: - ChangedSItems.bCOLOR = TRUE; break; - case IDC_BASECOLOUR2: - ChangedSItems.bCOLOR2 = TRUE; break; - case IDC_COLOR2_TRANSPARENT: - ChangedSItems.bCOLOR2_TRANSPARENT = TRUE; break; - case IDC_TEXTCOLOUR: - ChangedSItems.bTEXTCOLOR = TRUE; break; - - case IDC_ALPHA: - ChangedSItems.bALPHA = TRUE; break; - case IDC_ALPHASPIN: - ChangedSItems.bALPHA = TRUE; break; - - case IDC_MRGN_LEFT: - ChangedSItems.bMARGIN_LEFT = TRUE; break; - case IDC_MRGN_LEFT_SPIN: - ChangedSItems.bMARGIN_LEFT = TRUE; break; - - case IDC_MRGN_TOP: - ChangedSItems.bMARGIN_TOP = TRUE; break; - case IDC_MRGN_TOP_SPIN: - ChangedSItems.bMARGIN_TOP = TRUE; break; - - case IDC_MRGN_RIGHT: - ChangedSItems.bMARGIN_RIGHT = TRUE; break; - case IDC_MRGN_RIGHT_SPIN: - ChangedSItems.bMARGIN_RIGHT = TRUE; break; - - case IDC_MRGN_BOTTOM: - ChangedSItems.bMARGIN_BOTTOM = TRUE; break; - case IDC_MRGN_BOTTOM_SPIN: - ChangedSItems.bMARGIN_BOTTOM = TRUE; break; - - case IDC_BORDERTYPE: - ChangedSItems.bBORDERSTYLE = TRUE; break; - } - } -} - -// updates the struct with the changed dlg item -static void UpdateStatusStructSettingsFromOptDlg(HWND hwndDlg, int index) -{ - char buf[15]; - ULONG bdrtype; - StatusItems_t *p = StatusItems[index]; - - if (ChangedSItems.bIGNORED) - p->IGNORED = IsDlgButtonChecked(hwndDlg, IDC_IGNORE); - - if (ChangedSItems.bGRADIENT) { - p->GRADIENT = GRADIENT_NONE; - if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT)) - p->GRADIENT |= GRADIENT_ACTIVE; - if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_LR)) - p->GRADIENT |= GRADIENT_LR; - if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_RL)) - p->GRADIENT |= GRADIENT_RL; - if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_TB)) - p->GRADIENT |= GRADIENT_TB; - if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_BT)) - p->GRADIENT |= GRADIENT_BT; - } - if (ChangedSItems.bCORNER) { - p->CORNER = CORNER_NONE; - if (IsDlgButtonChecked(hwndDlg, IDC_CORNER)) - p->CORNER |= CORNER_ACTIVE; - if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TL)) - p->CORNER |= CORNER_TL; - if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TR)) - p->CORNER |= CORNER_TR; - if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BR)) - p->CORNER |= CORNER_BR; - if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BL)) - p->CORNER |= CORNER_BL; - } - - if (ChangedSItems.bCOLOR) - p->COLOR = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_GETCOLOUR, 0, 0); - - if (ChangedSItems.bCOLOR2) - p->COLOR2 = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_GETCOLOUR, 0, 0); - - if (ChangedSItems.bCOLOR2_TRANSPARENT) - p->COLOR2_TRANSPARENT = IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT); - - if (ChangedSItems.bTEXTCOLOR) - p->TEXTCOLOR = SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_GETCOLOUR, 0, 0); - - if (ChangedSItems.bALPHA) { - GetDlgItemTextA(hwndDlg, IDC_ALPHA, buf, 10); // can be removed now - if (buf[0] != 0) - p->ALPHA = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_GETPOS, 0, 0); - } - - if (ChangedSItems.bMARGIN_LEFT) { - GetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, buf, 10); - if (buf[0] != 0) - p->MARGIN_LEFT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_GETPOS, 0, 0); - } - - if (ChangedSItems.bMARGIN_TOP) { - GetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, buf, 10); - if (buf[0] != 0) - p->MARGIN_TOP = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_GETPOS, 0, 0); - } - - if (ChangedSItems.bMARGIN_RIGHT) { - GetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, buf, 10); - if (buf[0] != 0) - p->MARGIN_RIGHT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_GETPOS, 0, 0); - } - - if (ChangedSItems.bMARGIN_BOTTOM) { - GetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, buf, 10); - if (buf[0] != 0) - p->MARGIN_BOTTOM = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_GETPOS, 0, 0); - } - if (ChangedSItems.bBORDERSTYLE) { - bdrtype = SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_GETCURSEL, 0, 0); - if (bdrtype == CB_ERR) - p->BORDERSTYLE = 0; - else { - switch (bdrtype) { - case 0: - p->BORDERSTYLE = 0; - break; - case 1: - p->BORDERSTYLE = BDR_RAISEDOUTER; - break; - case 2: - p->BORDERSTYLE = BDR_SUNKENINNER; - break; - case 3: - p->BORDERSTYLE = EDGE_BUMP; - break; - case 4: - p->BORDERSTYLE = EDGE_ETCHED; - break; - default: - p->BORDERSTYLE = 0; - break; - } - } - } -} - -static void SaveLatestChanges(HWND hwndDlg) -{ - int n, itemData; - // process old selection - if (last_selcount > 0) { - for (n = 0; n < last_selcount; n++) { - itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0); - if (itemData != ID_EXTBKSEPARATOR) { - UpdateStatusStructSettingsFromOptDlg(hwndDlg, itemData - ID_EXTBK_FIRST); - } - } - } - - // reset bChange - ChangedSItems.bALPHA = FALSE; - ChangedSItems.bGRADIENT = FALSE; - ChangedSItems.bCORNER = FALSE; - ChangedSItems.bCOLOR = FALSE; - ChangedSItems.bCOLOR2 = FALSE; - ChangedSItems.bCOLOR2_TRANSPARENT = FALSE; - ChangedSItems.bTEXTCOLOR = FALSE; - ChangedSItems.bMARGIN_LEFT = FALSE; - ChangedSItems.bMARGIN_TOP = FALSE; - ChangedSItems.bMARGIN_RIGHT = FALSE; - ChangedSItems.bMARGIN_BOTTOM = FALSE; - ChangedSItems.bIGNORED = FALSE; - ChangedSItems.bBORDERSTYLE = FALSE; -} - -static UINT _controls_to_refresh[] = { - IDC_BORDERTYPE, - IDC_3DDARKCOLOR, - IDC_3DLIGHTCOLOR, - IDC_MRGN_BOTTOM, - IDC_MRGN_LEFT, - IDC_ALPHASPIN, - IDC_CORNER, - IDC_MRGN_TOP_SPIN, - IDC_MRGN_RIGHT_SPIN, - IDC_MRGN_BOTTOM_SPIN, - IDC_MRGN_LEFT_SPIN, - IDC_GRADIENT, - IDC_GRADIENT_LR, - IDC_GRADIENT_RL, - IDC_GRADIENT_TB, - IDC_BASECOLOUR, - IDC_ALPHA, - IDC_MRGN_TOP, - IDC_MRGN_RIGHT, - IDC_GRADIENT_BT, - IDC_BASECOLOUR2, - IDC_TEXTCOLOUR, - IDC_CORNER_TL, - IDC_CORNER_TR, - IDC_CORNER_BR, - IDC_CORNER_BL, - IDC_IGNORE, - IDC_ALPHALABLE, - IDC_COLOR2LABLE, - IDC_COLORLABLE, - IDC_TEXTCOLOURLABLE, - IDC_COLOR2_TRANSPARENT, - 0 -}; - -static void RefreshControls(HWND hwnd) -{ - for (int i = 0; _controls_to_refresh[i]; i++) - InvalidateRect(GetDlgItem(hwnd, _controls_to_refresh[i]), nullptr, FALSE); -} - -// wenn die listbox geändert wurde -static void OnListItemsChange(HWND hwndDlg) -{ - SendMessage(hwndDlg, WM_SETREDRAW, FALSE, 0); - SaveLatestChanges(hwndDlg); - - // set new selection - last_selcount = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELCOUNT, 0, 0); - if (last_selcount > 0) { - int n, itemData, first_item; - - // get selected indizes - SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELITEMS, 64, (LPARAM)last_indizes); - - // initialize with first items value - - first_item = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[0], 0) - ID_EXTBK_FIRST; - StatusItems_t *pFirst = StatusItems[first_item]; - StatusItems_t DialogSettingForMultiSel = *StatusItems[first_item]; - for (n = 0; n < last_selcount; n++) { - itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0); - if (itemData == ID_EXTBKSEPARATOR) - continue; - - StatusItems_t *p = StatusItems[itemData - ID_EXTBK_FIRST]; - if (p->ALPHA != pFirst->ALPHA) - DialogSettingForMultiSel.ALPHA = -1; - if (p->COLOR != pFirst->COLOR) - DialogSettingForMultiSel.COLOR = CLCDEFAULT_COLOR; - if (p->COLOR2 != pFirst->COLOR2) - DialogSettingForMultiSel.COLOR2 = CLCDEFAULT_COLOR2; - if (p->COLOR2_TRANSPARENT != pFirst->COLOR2_TRANSPARENT) - DialogSettingForMultiSel.COLOR2_TRANSPARENT = CLCDEFAULT_COLOR2_TRANSPARENT; - if (p->TEXTCOLOR != pFirst->TEXTCOLOR) - DialogSettingForMultiSel.TEXTCOLOR = CLCDEFAULT_TEXTCOLOR; - if (p->CORNER != pFirst->CORNER) - DialogSettingForMultiSel.CORNER = CLCDEFAULT_CORNER; - if (p->GRADIENT != pFirst->GRADIENT) - DialogSettingForMultiSel.GRADIENT = CLCDEFAULT_GRADIENT; - if (p->IGNORED != pFirst->IGNORED) - DialogSettingForMultiSel.IGNORED = CLCDEFAULT_IGNORE; - if (p->MARGIN_BOTTOM != pFirst->MARGIN_BOTTOM) - DialogSettingForMultiSel.MARGIN_BOTTOM = -1; - if (p->MARGIN_LEFT != pFirst->MARGIN_LEFT) - DialogSettingForMultiSel.MARGIN_LEFT = -1; - if (p->MARGIN_RIGHT != pFirst->MARGIN_RIGHT) - DialogSettingForMultiSel.MARGIN_RIGHT = -1; - if (p->MARGIN_TOP != pFirst->MARGIN_TOP) - DialogSettingForMultiSel.MARGIN_TOP = -1; - if (p->BORDERSTYLE != pFirst->BORDERSTYLE) - DialogSettingForMultiSel.BORDERSTYLE = -1; - } - - if (last_selcount == 1 && pFirst->statusID == ID_EXTBKSEPARATOR) { - ChangeControlItems(hwndDlg, 0, 0); - last_selcount = 0; - } - else - ChangeControlItems(hwndDlg, 1, 0); - FillOptionDialogByStatusItem(hwndDlg, &DialogSettingForMultiSel); - InvalidateRect(GetDlgItem(hwndDlg, IDC_ITEMS), nullptr, FALSE); - } - SendMessage(hwndDlg, WM_SETREDRAW, TRUE, 0); - RefreshControls(hwndDlg); -} - -// fills the combobox of the options dlg for the first time -static void FillItemList(HWND hwndDlg) -{ - int n, iOff; - UINT item; - - for (n = 0; n < ID_EXTBK_LAST - ID_EXTBK_FIRST; n++) { - iOff = 0; - if (strstr(StatusItems[n]->szName, "{-}")) { - item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)"------------------------"); - SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBKSEPARATOR); - iOff = 3; - } - item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)StatusItems[n]->szName[iOff]); - SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBK_FIRST + n); - } -} - -static INT_PTR CALLBACK SkinEdit_ExtBkDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - SKINDESCRIPTION *psd = (SKINDESCRIPTION *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (psd) { - ID_EXTBK_FIRST = psd->firstItem; - ID_EXTBK_LAST = psd->lastItem; - StatusItems = psd->StatusItems; - } - - switch (msg) { - case WM_INITDIALOG: - psd = (SKINDESCRIPTION *)malloc(sizeof(SKINDESCRIPTION)); - if (psd == nullptr) - return FALSE; - memset(psd, 0, sizeof(SKINDESCRIPTION)); - memcpy(psd, (void *)lParam, sizeof(SKINDESCRIPTION)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)psd); - - ID_EXTBK_FIRST = psd->firstItem; - ID_EXTBK_LAST = psd->lastItem; - StatusItems = psd->StatusItems; - - TranslateDialogDefault(hwndDlg); - FillItemList(hwndDlg); - SendMessage(hwndDlg, WM_USER + 101, 0, 0); - - psd->hMenuItems = CreatePopupMenu(); - AppendMenu(psd->hMenuItems, MF_STRING | MF_DISABLED, (UINT_PTR)0, LPGENW("Copy from")); - AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr); - - { - for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) { - int iOff = StatusItems[i - ID_EXTBK_FIRST]->szName[0] == '{' ? 3 : 0; - if (iOff) - AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr); - AppendMenuA(psd->hMenuItems, MF_STRING, (UINT_PTR)i, &StatusItems[i - ID_EXTBK_FIRST]->szName[iOff]); - } - } - return TRUE; - - case WM_USER + 101: - SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0)); - SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0)); - SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0)); - SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0)); - SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_SETRANGE, 0, MAKELONG(100, 0)); - - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("<None>")); - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Raised")); - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Sunken")); - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Bumped")); - SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Etched")); - - SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3ddark", RGB(224, 224, 224))); - SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3dbright", RGB(224, 224, 224))); - return 0; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - int iItem = dis->itemData; - StatusItems_t *item = nullptr; - - SetBkMode(dis->hDC, TRANSPARENT); - FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW)); - - if (iItem >= ID_EXTBK_FIRST && iItem < ID_EXTBK_LAST) - item = StatusItems[iItem - ID_EXTBK_FIRST]; - - if (dis->itemState & ODS_SELECTED && iItem != ID_EXTBKSEPARATOR) { - FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT)); - SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - } - else { - FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW)); - if (item && item->IGNORED) - SetTextColor(dis->hDC, RGB(255, 0, 0)); - else - SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT)); - } - if (iItem == ID_EXTBKSEPARATOR) { - HPEN hPen, hPenOld; - POINT pt; - - hPen = CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWTEXT)); - hPenOld = (HPEN)SelectObject(dis->hDC, hPen); - - MoveToEx(dis->hDC, dis->rcItem.left, (dis->rcItem.top + dis->rcItem.bottom) / 2, &pt); - LineTo(dis->hDC, dis->rcItem.right, (dis->rcItem.top + dis->rcItem.bottom) / 2); - SelectObject(dis->hDC, hPenOld); - DeleteObject((HGDIOBJ)hPen); - } - else if (item) { - char *szName = item->szName[0] == '{' ? &item->szName[3] : item->szName; - TextOutA(dis->hDC, dis->rcItem.left, dis->rcItem.top, szName, (int)mir_strlen(szName)); - } - return TRUE; - } - - case WM_CONTEXTMENU: - { - HWND hwndList = GetDlgItem(hwndDlg, IDC_ITEMS); - - POINT pt; - GetCursorPos(&pt); - - RECT rc; - GetWindowRect(hwndList, &rc); - - if (PtInRect(&rc, pt)) { - int iSelection = (int)TrackPopupMenu(psd->hMenuItems, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr); - - if (iSelection >= ID_EXTBK_FIRST && iSelection < ID_EXTBK_LAST) { - iSelection -= ID_EXTBK_FIRST; - StatusItems_t *pSel = StatusItems[iSelection]; - - for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) { - if (SendMessage(hwndList, LB_GETSEL, i - ID_EXTBK_FIRST, 0) <= 0) - continue; - - int iIndex = SendMessage(hwndList, LB_GETITEMDATA, i - ID_EXTBK_FIRST, 0); - iIndex -= ID_EXTBK_FIRST; - if (iIndex < 0) - continue; - - StatusItems_t *p = StatusItems[iIndex]; - p->ALPHA = pSel->ALPHA; - p->BORDERSTYLE = pSel->BORDERSTYLE; - p->COLOR = pSel->COLOR; - p->COLOR2 = pSel->COLOR2; - p->COLOR2_TRANSPARENT = pSel->COLOR2_TRANSPARENT; - p->CORNER = pSel->CORNER; - p->GRADIENT = pSel->GRADIENT; - p->IGNORED = pSel->IGNORED; - p->imageItem = pSel->imageItem; - p->MARGIN_BOTTOM = pSel->MARGIN_BOTTOM; - p->MARGIN_LEFT = pSel->MARGIN_LEFT; - p->MARGIN_RIGHT = pSel->MARGIN_RIGHT; - p->MARGIN_TOP = pSel->MARGIN_TOP; - p->TEXTCOLOR = pSel->TEXTCOLOR; - } - OnListItemsChange(hwndDlg); - } - } - } - break; - - case WM_COMMAND: - // this will check if the user changed some actual statusitems values - // if yes the flag bChanged will be set to TRUE - SetChangedStatusItemFlag(wParam, hwndDlg); - switch (LOWORD(wParam)) { - case IDC_ITEMS: - if (HIWORD(wParam) != LBN_SELCHANGE) - return FALSE; - { - int iItem = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0), 0); - if (iItem == ID_EXTBKSEPARATOR) - return FALSE; - } - OnListItemsChange(hwndDlg); - if (psd->pfnClcOptionsChanged) - psd->pfnClcOptionsChanged(); - break; - case IDC_GRADIENT: - ReActiveCombo(hwndDlg); - break; - case IDC_CORNER: - ReActiveCombo(hwndDlg); - break; - case IDC_IGNORE: - ReActiveCombo(hwndDlg); - break; - case IDC_COLOR2_TRANSPARENT: - ReActiveCombo(hwndDlg); - break; - case IDC_BORDERTYPE: - break; - } - if ((LOWORD(wParam) == IDC_ALPHA || LOWORD(wParam) == IDC_MRGN_LEFT || LOWORD(wParam) == IDC_MRGN_BOTTOM || LOWORD(wParam) == IDC_MRGN_TOP || LOWORD(wParam) == IDC_MRGN_RIGHT) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())) - return 0; - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - // save user made changes - SaveLatestChanges(hwndDlg); - // save struct to DB - if (psd->pfnSaveCompleteStruct) - psd->pfnSaveCompleteStruct(); - db_set_dw(0, "CLCExt", "3dbright", SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_GETCOLOUR, 0, 0)); - db_set_dw(0, "CLCExt", "3ddark", SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_GETCOLOUR, 0, 0)); - - if (psd->pfnClcOptionsChanged) - psd->pfnClcOptionsChanged(); - if (psd->hwndCLUI) { - SendMessage(psd->hwndCLUI, WM_SIZE, 0, 0); - PostMessage(psd->hwndCLUI, WM_USER + 100, 0, 0); // CLUIINTM_REDRAW - } - break; - } - } - break; - case WM_DESTROY: - DestroyMenu(psd->hMenuItems); - break; - case WM_NCDESTROY: - free(psd); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} - -/* - * unimplemented -*/ - -static INT_PTR SkinEdit_FillByCurrentSel(WPARAM wParam, LPARAM) -{ - if (wParam) - FillOptionDialogByCurrentSel((HWND)wParam); - return 0; -} - -/* - * service function - * creates additional tab pages under the given parent window handle - * expects a SKINDESCRIPTON * in lParam -*/ - -static INT_PTR SkinEdit_Invoke(WPARAM, LPARAM lParam) -{ - SKINDESCRIPTION *psd = (SKINDESCRIPTION *)lParam; - TCITEM tci = { 0 }; - RECT rcClient; - int iTabs; - - if (psd->cbSize != sizeof(SKINDESCRIPTION)) - return 0; - - iTabs = TabCtrl_GetItemCount(psd->hWndTab); - GetClientRect(psd->hWndParent, &rcClient); - - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SKINITEMEDIT), psd->hWndParent, SkinEdit_ExtBkDlgProc, (LPARAM)psd); - - tci.pszText = TranslateT("Skin items"); - TabCtrl_InsertItem(psd->hWndTab, iTabs++, &tci); - MoveWindow((HWND)tci.lParam, 5, 25, rcClient.right - 9, rcClient.bottom - 60, 1); - psd->hwndSkinEdit = (HWND)tci.lParam; - return (INT_PTR)psd->hwndSkinEdit; -} - -int CMPlugin::Load() -{ - CreateServiceFunction(MS_CLNSE_INVOKE, SkinEdit_Invoke); - CreateServiceFunction(MS_CLNSE_FILLBYCURRENTSEL, SkinEdit_FillByCurrentSel); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-04 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define ID_EXTBKSEPARATOR 40200
+
+CMPlugin g_plugin;
+
+StatusItems_t **StatusItems;
+ChangedSItems_t ChangedSItems = { 0 };
+
+static int LastModifiedItem = -1;
+static int last_selcount = 0;
+static int last_indizes[64];
+static int ID_EXTBK_LAST = 0, ID_EXTBK_FIRST = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {21948C89-B549-4C9D-8B4F-3F3726EC6B4B}
+ {0x21948c89, 0xb549, 0x4c9d, {0x8b, 0x4f, 0x3f, 0x37, 0x26, 0xec, 0x6b, 0x4b}}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>("Skin editor", pluginInfoEx)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// prototypes
+
+static void ChangeControlItems(HWND hwndDlg, int status, int except);
+static BOOL CheckItem(int item, HWND hwndDlg);
+
+static void ReActiveCombo(HWND hwndDlg)
+{
+ if (IsDlgButtonChecked(hwndDlg, IDC_IGNORE)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE);
+ }
+ else {
+ ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ }
+}
+
+// enabled or disabled the whole status controlitems group (with exceptional control)
+static void ChangeControlItems(HWND hwndDlg, int status, int except)
+{
+ if (except != IDC_GRADIENT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT), status);
+ if (except != IDC_GRADIENT_LR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), status);
+ if (except != IDC_GRADIENT_RL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), status);
+ if (except != IDC_GRADIENT_TB)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), status);
+ if (except != IDC_GRADIENT_BT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), status);
+ if (except != IDC_CORNER)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER), status);
+ if (except != IDC_CORNER_TL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status);
+ if (except != IDC_CORNER_TR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), status);
+ if (except != IDC_CORNER_BR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), status);
+ if (except != IDC_CORNER_BL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), status);
+ if (except != IDC_CORNER_TL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status);
+ if (except != IDC_MARGINLABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MARGINLABLE), status);
+ if (except != IDC_MRGN_TOP)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP), status);
+ if (except != IDC_MRGN_RIGHT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT), status);
+ if (except != IDC_MRGN_BOTTOM)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM), status);
+ if (except != IDC_MRGN_LEFT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT), status);
+ if (except != IDC_MRGN_TOP_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP_SPIN), status);
+ if (except != IDC_MRGN_RIGHT_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT_SPIN), status);
+ if (except != IDC_MRGN_BOTTOM_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM_SPIN), status);
+ if (except != IDC_MRGN_LEFT_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT_SPIN), status);
+ if (except != IDC_BASECOLOUR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR), status);
+ if (except != IDC_COLORLABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLORLABLE), status);
+ if (except != IDC_BASECOLOUR2)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), status);
+ if (except != IDC_COLOR2LABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), status);
+ if (except != IDC_COLOR2_TRANSPARENT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2_TRANSPARENT), status);
+ if (except != IDC_TEXTCOLOUR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOUR), status);
+ if (except != IDC_TEXTCOLOURLABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOURLABLE), status);
+
+ if (except != IDC_ALPHA)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHA), status);
+ if (except != IDC_ALPHASPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHASPIN), status);
+ if (except != IDC_ALPHALABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHALABLE), status);
+ if (except != IDC_IGNORE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORE), status);
+
+ if (except != IDC_BORDERTYPE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BORDERTYPE), status);
+}
+
+static void FillOptionDialogByStatusItem(HWND hwndDlg, StatusItems_t *item)
+{
+ char itoabuf[15];
+ uint32_t ret;
+ int index;
+
+ CheckDlgButton(hwndDlg, IDC_IGNORE, (item->IGNORED) ? BST_CHECKED : BST_UNCHECKED);
+
+ CheckDlgButton(hwndDlg, IDC_GRADIENT, (item->GRADIENT & GRADIENT_ACTIVE) ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), item->GRADIENT & GRADIENT_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), item->GRADIENT & GRADIENT_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), item->GRADIENT & GRADIENT_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), item->GRADIENT & GRADIENT_ACTIVE);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_LR, (item->GRADIENT & GRADIENT_LR) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_RL, (item->GRADIENT & GRADIENT_RL) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_TB, (item->GRADIENT & GRADIENT_TB) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_BT, (item->GRADIENT & GRADIENT_BT) ? BST_CHECKED : BST_UNCHECKED);
+
+ CheckDlgButton(hwndDlg, IDC_CORNER, (item->CORNER & CORNER_ACTIVE) ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), item->CORNER & CORNER_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), item->CORNER & CORNER_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), item->CORNER & CORNER_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), item->CORNER & CORNER_ACTIVE);
+
+ CheckDlgButton(hwndDlg, IDC_CORNER_TL, (item->CORNER & CORNER_TL) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CORNER_TR, (item->CORNER & CORNER_TR) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CORNER_BR, (item->CORNER & CORNER_BR) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CORNER_BL, (item->CORNER & CORNER_BL) ? BST_CHECKED : BST_UNCHECKED);
+
+ ret = item->COLOR;
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR);
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETCOLOUR, 0, ret);
+
+ ret = item->COLOR2;
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR2);
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETCOLOUR, 0, ret);
+
+ CheckDlgButton(hwndDlg, IDC_COLOR2_TRANSPARENT, (item->COLOR2_TRANSPARENT) ? BST_CHECKED : BST_UNCHECKED);
+
+ ret = item->TEXTCOLOR;
+ SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_TEXTCOLOR);
+ SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETCOLOUR, 0, ret);
+
+ if (item->ALPHA == -1) {
+ SetDlgItemTextA(hwndDlg, IDC_ALPHA, "");
+ }
+ else {
+ ret = item->ALPHA;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_ALPHA, itoabuf);
+ }
+
+ if (item->MARGIN_LEFT == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, "");
+ else {
+ ret = item->MARGIN_LEFT;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, itoabuf);
+ }
+
+ if (item->MARGIN_TOP == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, "");
+ else {
+ ret = item->MARGIN_TOP;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, itoabuf);
+ }
+
+ if (item->MARGIN_RIGHT == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, "");
+ else {
+ ret = item->MARGIN_RIGHT;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, itoabuf);
+ }
+
+ if (item->MARGIN_BOTTOM == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, "");
+ else {
+ ret = item->MARGIN_BOTTOM;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, itoabuf);
+ }
+ if (item->BORDERSTYLE == -1)
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, 0, 0);
+ else {
+ index = 0;
+ switch (item->BORDERSTYLE) {
+ case 0:
+ case -1:
+ index = 0;
+ break;
+ case BDR_RAISEDOUTER:
+ index = 1;
+ break;
+ case BDR_SUNKENINNER:
+ index = 2;
+ break;
+ case EDGE_BUMP:
+ index = 3;
+ break;
+ case EDGE_ETCHED:
+ index = 4;
+ break;
+ }
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, (WPARAM)index, 0);
+ }
+ ReActiveCombo(hwndDlg);
+}
+// update dlg with selected item
+static void FillOptionDialogByCurrentSel(HWND hwndDlg)
+{
+ int index = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0);
+ int itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, index, 0);
+ if (itemData != ID_EXTBKSEPARATOR) {
+ LastModifiedItem = itemData - ID_EXTBK_FIRST;
+
+ if (CheckItem(itemData - ID_EXTBK_FIRST, hwndDlg))
+ FillOptionDialogByStatusItem(hwndDlg, StatusItems[itemData - ID_EXTBK_FIRST]);
+ }
+}
+
+
+// enabled all status controls if the selected item is a separator
+static BOOL CheckItem(int item, HWND hwndDlg)
+{
+ if (StatusItems[item]->statusID == ID_EXTBKSEPARATOR) {
+ ChangeControlItems(hwndDlg, 0, 0);
+ return FALSE;
+ }
+ else {
+ ChangeControlItems(hwndDlg, 1, 0);
+ return TRUE;
+ }
+}
+
+static void SetChangedStatusItemFlag(WPARAM wParam, HWND hwndDlg)
+{
+ if (LOWORD(wParam) != IDC_ITEMS
+ && (GetDlgItem(hwndDlg, LOWORD(wParam)) == GetFocus() || HIWORD(wParam) == CPN_COLOURCHANGED)
+ && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CPN_COLOURCHANGED)) {
+ switch (LOWORD(wParam)) {
+ case IDC_IGNORE:
+ ChangedSItems.bIGNORED = TRUE; break;
+ case IDC_GRADIENT:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_LR:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_RL:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_BT:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_TB:
+ ChangedSItems.bGRADIENT = TRUE; break;
+
+ case IDC_CORNER:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_TL:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_TR:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_BR:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_BL:
+ ChangedSItems.bCORNER = TRUE; break;
+
+ case IDC_BASECOLOUR:
+ ChangedSItems.bCOLOR = TRUE; break;
+ case IDC_BASECOLOUR2:
+ ChangedSItems.bCOLOR2 = TRUE; break;
+ case IDC_COLOR2_TRANSPARENT:
+ ChangedSItems.bCOLOR2_TRANSPARENT = TRUE; break;
+ case IDC_TEXTCOLOUR:
+ ChangedSItems.bTEXTCOLOR = TRUE; break;
+
+ case IDC_ALPHA:
+ ChangedSItems.bALPHA = TRUE; break;
+ case IDC_ALPHASPIN:
+ ChangedSItems.bALPHA = TRUE; break;
+
+ case IDC_MRGN_LEFT:
+ ChangedSItems.bMARGIN_LEFT = TRUE; break;
+ case IDC_MRGN_LEFT_SPIN:
+ ChangedSItems.bMARGIN_LEFT = TRUE; break;
+
+ case IDC_MRGN_TOP:
+ ChangedSItems.bMARGIN_TOP = TRUE; break;
+ case IDC_MRGN_TOP_SPIN:
+ ChangedSItems.bMARGIN_TOP = TRUE; break;
+
+ case IDC_MRGN_RIGHT:
+ ChangedSItems.bMARGIN_RIGHT = TRUE; break;
+ case IDC_MRGN_RIGHT_SPIN:
+ ChangedSItems.bMARGIN_RIGHT = TRUE; break;
+
+ case IDC_MRGN_BOTTOM:
+ ChangedSItems.bMARGIN_BOTTOM = TRUE; break;
+ case IDC_MRGN_BOTTOM_SPIN:
+ ChangedSItems.bMARGIN_BOTTOM = TRUE; break;
+
+ case IDC_BORDERTYPE:
+ ChangedSItems.bBORDERSTYLE = TRUE; break;
+ }
+ }
+}
+
+// updates the struct with the changed dlg item
+static void UpdateStatusStructSettingsFromOptDlg(HWND hwndDlg, int index)
+{
+ char buf[15];
+ ULONG bdrtype;
+ StatusItems_t *p = StatusItems[index];
+
+ if (ChangedSItems.bIGNORED)
+ p->IGNORED = IsDlgButtonChecked(hwndDlg, IDC_IGNORE);
+
+ if (ChangedSItems.bGRADIENT) {
+ p->GRADIENT = GRADIENT_NONE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT))
+ p->GRADIENT |= GRADIENT_ACTIVE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_LR))
+ p->GRADIENT |= GRADIENT_LR;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_RL))
+ p->GRADIENT |= GRADIENT_RL;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_TB))
+ p->GRADIENT |= GRADIENT_TB;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_BT))
+ p->GRADIENT |= GRADIENT_BT;
+ }
+ if (ChangedSItems.bCORNER) {
+ p->CORNER = CORNER_NONE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER))
+ p->CORNER |= CORNER_ACTIVE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TL))
+ p->CORNER |= CORNER_TL;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TR))
+ p->CORNER |= CORNER_TR;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BR))
+ p->CORNER |= CORNER_BR;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BL))
+ p->CORNER |= CORNER_BL;
+ }
+
+ if (ChangedSItems.bCOLOR)
+ p->COLOR = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_GETCOLOUR, 0, 0);
+
+ if (ChangedSItems.bCOLOR2)
+ p->COLOR2 = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_GETCOLOUR, 0, 0);
+
+ if (ChangedSItems.bCOLOR2_TRANSPARENT)
+ p->COLOR2_TRANSPARENT = IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT);
+
+ if (ChangedSItems.bTEXTCOLOR)
+ p->TEXTCOLOR = SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_GETCOLOUR, 0, 0);
+
+ if (ChangedSItems.bALPHA) {
+ GetDlgItemTextA(hwndDlg, IDC_ALPHA, buf, 10); // can be removed now
+ if (buf[0] != 0)
+ p->ALPHA = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_LEFT) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_LEFT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_TOP) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_TOP = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_RIGHT) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_RIGHT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_BOTTOM) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_BOTTOM = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_GETPOS, 0, 0);
+ }
+ if (ChangedSItems.bBORDERSTYLE) {
+ bdrtype = SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_GETCURSEL, 0, 0);
+ if (bdrtype == CB_ERR)
+ p->BORDERSTYLE = 0;
+ else {
+ switch (bdrtype) {
+ case 0:
+ p->BORDERSTYLE = 0;
+ break;
+ case 1:
+ p->BORDERSTYLE = BDR_RAISEDOUTER;
+ break;
+ case 2:
+ p->BORDERSTYLE = BDR_SUNKENINNER;
+ break;
+ case 3:
+ p->BORDERSTYLE = EDGE_BUMP;
+ break;
+ case 4:
+ p->BORDERSTYLE = EDGE_ETCHED;
+ break;
+ default:
+ p->BORDERSTYLE = 0;
+ break;
+ }
+ }
+ }
+}
+
+static void SaveLatestChanges(HWND hwndDlg)
+{
+ int n, itemData;
+ // process old selection
+ if (last_selcount > 0) {
+ for (n = 0; n < last_selcount; n++) {
+ itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0);
+ if (itemData != ID_EXTBKSEPARATOR) {
+ UpdateStatusStructSettingsFromOptDlg(hwndDlg, itemData - ID_EXTBK_FIRST);
+ }
+ }
+ }
+
+ // reset bChange
+ ChangedSItems.bALPHA = FALSE;
+ ChangedSItems.bGRADIENT = FALSE;
+ ChangedSItems.bCORNER = FALSE;
+ ChangedSItems.bCOLOR = FALSE;
+ ChangedSItems.bCOLOR2 = FALSE;
+ ChangedSItems.bCOLOR2_TRANSPARENT = FALSE;
+ ChangedSItems.bTEXTCOLOR = FALSE;
+ ChangedSItems.bMARGIN_LEFT = FALSE;
+ ChangedSItems.bMARGIN_TOP = FALSE;
+ ChangedSItems.bMARGIN_RIGHT = FALSE;
+ ChangedSItems.bMARGIN_BOTTOM = FALSE;
+ ChangedSItems.bIGNORED = FALSE;
+ ChangedSItems.bBORDERSTYLE = FALSE;
+}
+
+static UINT _controls_to_refresh[] = {
+ IDC_BORDERTYPE,
+ IDC_3DDARKCOLOR,
+ IDC_3DLIGHTCOLOR,
+ IDC_MRGN_BOTTOM,
+ IDC_MRGN_LEFT,
+ IDC_ALPHASPIN,
+ IDC_CORNER,
+ IDC_MRGN_TOP_SPIN,
+ IDC_MRGN_RIGHT_SPIN,
+ IDC_MRGN_BOTTOM_SPIN,
+ IDC_MRGN_LEFT_SPIN,
+ IDC_GRADIENT,
+ IDC_GRADIENT_LR,
+ IDC_GRADIENT_RL,
+ IDC_GRADIENT_TB,
+ IDC_BASECOLOUR,
+ IDC_ALPHA,
+ IDC_MRGN_TOP,
+ IDC_MRGN_RIGHT,
+ IDC_GRADIENT_BT,
+ IDC_BASECOLOUR2,
+ IDC_TEXTCOLOUR,
+ IDC_CORNER_TL,
+ IDC_CORNER_TR,
+ IDC_CORNER_BR,
+ IDC_CORNER_BL,
+ IDC_IGNORE,
+ IDC_ALPHALABLE,
+ IDC_COLOR2LABLE,
+ IDC_COLORLABLE,
+ IDC_TEXTCOLOURLABLE,
+ IDC_COLOR2_TRANSPARENT,
+ 0
+};
+
+static void RefreshControls(HWND hwnd)
+{
+ for (int i = 0; _controls_to_refresh[i]; i++)
+ InvalidateRect(GetDlgItem(hwnd, _controls_to_refresh[i]), nullptr, FALSE);
+}
+
+// wenn die listbox geändert wurde
+static void OnListItemsChange(HWND hwndDlg)
+{
+ SendMessage(hwndDlg, WM_SETREDRAW, FALSE, 0);
+ SaveLatestChanges(hwndDlg);
+
+ // set new selection
+ last_selcount = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELCOUNT, 0, 0);
+ if (last_selcount > 0) {
+ int n, itemData, first_item;
+
+ // get selected indizes
+ SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELITEMS, 64, (LPARAM)last_indizes);
+
+ // initialize with first items value
+
+ first_item = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[0], 0) - ID_EXTBK_FIRST;
+ StatusItems_t *pFirst = StatusItems[first_item];
+ StatusItems_t DialogSettingForMultiSel = *StatusItems[first_item];
+ for (n = 0; n < last_selcount; n++) {
+ itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0);
+ if (itemData == ID_EXTBKSEPARATOR)
+ continue;
+
+ StatusItems_t *p = StatusItems[itemData - ID_EXTBK_FIRST];
+ if (p->ALPHA != pFirst->ALPHA)
+ DialogSettingForMultiSel.ALPHA = -1;
+ if (p->COLOR != pFirst->COLOR)
+ DialogSettingForMultiSel.COLOR = CLCDEFAULT_COLOR;
+ if (p->COLOR2 != pFirst->COLOR2)
+ DialogSettingForMultiSel.COLOR2 = CLCDEFAULT_COLOR2;
+ if (p->COLOR2_TRANSPARENT != pFirst->COLOR2_TRANSPARENT)
+ DialogSettingForMultiSel.COLOR2_TRANSPARENT = CLCDEFAULT_COLOR2_TRANSPARENT;
+ if (p->TEXTCOLOR != pFirst->TEXTCOLOR)
+ DialogSettingForMultiSel.TEXTCOLOR = CLCDEFAULT_TEXTCOLOR;
+ if (p->CORNER != pFirst->CORNER)
+ DialogSettingForMultiSel.CORNER = CLCDEFAULT_CORNER;
+ if (p->GRADIENT != pFirst->GRADIENT)
+ DialogSettingForMultiSel.GRADIENT = CLCDEFAULT_GRADIENT;
+ if (p->IGNORED != pFirst->IGNORED)
+ DialogSettingForMultiSel.IGNORED = CLCDEFAULT_IGNORE;
+ if (p->MARGIN_BOTTOM != pFirst->MARGIN_BOTTOM)
+ DialogSettingForMultiSel.MARGIN_BOTTOM = -1;
+ if (p->MARGIN_LEFT != pFirst->MARGIN_LEFT)
+ DialogSettingForMultiSel.MARGIN_LEFT = -1;
+ if (p->MARGIN_RIGHT != pFirst->MARGIN_RIGHT)
+ DialogSettingForMultiSel.MARGIN_RIGHT = -1;
+ if (p->MARGIN_TOP != pFirst->MARGIN_TOP)
+ DialogSettingForMultiSel.MARGIN_TOP = -1;
+ if (p->BORDERSTYLE != pFirst->BORDERSTYLE)
+ DialogSettingForMultiSel.BORDERSTYLE = -1;
+ }
+
+ if (last_selcount == 1 && pFirst->statusID == ID_EXTBKSEPARATOR) {
+ ChangeControlItems(hwndDlg, 0, 0);
+ last_selcount = 0;
+ }
+ else
+ ChangeControlItems(hwndDlg, 1, 0);
+ FillOptionDialogByStatusItem(hwndDlg, &DialogSettingForMultiSel);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_ITEMS), nullptr, FALSE);
+ }
+ SendMessage(hwndDlg, WM_SETREDRAW, TRUE, 0);
+ RefreshControls(hwndDlg);
+}
+
+// fills the combobox of the options dlg for the first time
+static void FillItemList(HWND hwndDlg)
+{
+ int n, iOff;
+ UINT item;
+
+ for (n = 0; n < ID_EXTBK_LAST - ID_EXTBK_FIRST; n++) {
+ iOff = 0;
+ if (strstr(StatusItems[n]->szName, "{-}")) {
+ item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)"------------------------");
+ SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBKSEPARATOR);
+ iOff = 3;
+ }
+ item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)StatusItems[n]->szName[iOff]);
+ SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBK_FIRST + n);
+ }
+}
+
+static INT_PTR CALLBACK SkinEdit_ExtBkDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ SKINDESCRIPTION *psd = (SKINDESCRIPTION *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (psd) {
+ ID_EXTBK_FIRST = psd->firstItem;
+ ID_EXTBK_LAST = psd->lastItem;
+ StatusItems = psd->StatusItems;
+ }
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ psd = (SKINDESCRIPTION *)malloc(sizeof(SKINDESCRIPTION));
+ if (psd == nullptr)
+ return FALSE;
+ memset(psd, 0, sizeof(SKINDESCRIPTION));
+ memcpy(psd, (void *)lParam, sizeof(SKINDESCRIPTION));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)psd);
+
+ ID_EXTBK_FIRST = psd->firstItem;
+ ID_EXTBK_LAST = psd->lastItem;
+ StatusItems = psd->StatusItems;
+
+ TranslateDialogDefault(hwndDlg);
+ FillItemList(hwndDlg);
+ SendMessage(hwndDlg, WM_USER + 101, 0, 0);
+
+ psd->hMenuItems = CreatePopupMenu();
+ AppendMenu(psd->hMenuItems, MF_STRING | MF_DISABLED, (UINT_PTR)0, LPGENW("Copy from"));
+ AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr);
+
+ {
+ for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) {
+ int iOff = StatusItems[i - ID_EXTBK_FIRST]->szName[0] == '{' ? 3 : 0;
+ if (iOff)
+ AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr);
+ AppendMenuA(psd->hMenuItems, MF_STRING, (UINT_PTR)i, &StatusItems[i - ID_EXTBK_FIRST]->szName[iOff]);
+ }
+ }
+ return TRUE;
+
+ case WM_USER + 101:
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("<None>"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Raised"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Sunken"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Bumped"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Etched"));
+
+ SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3ddark", RGB(224, 224, 224)));
+ SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3dbright", RGB(224, 224, 224)));
+ return 0;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+ int iItem = dis->itemData;
+ StatusItems_t *item = nullptr;
+
+ SetBkMode(dis->hDC, TRANSPARENT);
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+
+ if (iItem >= ID_EXTBK_FIRST && iItem < ID_EXTBK_LAST)
+ item = StatusItems[iItem - ID_EXTBK_FIRST];
+
+ if (dis->itemState & ODS_SELECTED && iItem != ID_EXTBKSEPARATOR) {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ if (item && item->IGNORED)
+ SetTextColor(dis->hDC, RGB(255, 0, 0));
+ else
+ SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT));
+ }
+ if (iItem == ID_EXTBKSEPARATOR) {
+ HPEN hPen, hPenOld;
+ POINT pt;
+
+ hPen = CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWTEXT));
+ hPenOld = (HPEN)SelectObject(dis->hDC, hPen);
+
+ MoveToEx(dis->hDC, dis->rcItem.left, (dis->rcItem.top + dis->rcItem.bottom) / 2, &pt);
+ LineTo(dis->hDC, dis->rcItem.right, (dis->rcItem.top + dis->rcItem.bottom) / 2);
+ SelectObject(dis->hDC, hPenOld);
+ DeleteObject((HGDIOBJ)hPen);
+ }
+ else if (item) {
+ char *szName = item->szName[0] == '{' ? &item->szName[3] : item->szName;
+ TextOutA(dis->hDC, dis->rcItem.left, dis->rcItem.top, szName, (int)mir_strlen(szName));
+ }
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_ITEMS);
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rc;
+ GetWindowRect(hwndList, &rc);
+
+ if (PtInRect(&rc, pt)) {
+ int iSelection = (int)TrackPopupMenu(psd->hMenuItems, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr);
+
+ if (iSelection >= ID_EXTBK_FIRST && iSelection < ID_EXTBK_LAST) {
+ iSelection -= ID_EXTBK_FIRST;
+ StatusItems_t *pSel = StatusItems[iSelection];
+
+ for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) {
+ if (SendMessage(hwndList, LB_GETSEL, i - ID_EXTBK_FIRST, 0) <= 0)
+ continue;
+
+ int iIndex = SendMessage(hwndList, LB_GETITEMDATA, i - ID_EXTBK_FIRST, 0);
+ iIndex -= ID_EXTBK_FIRST;
+ if (iIndex < 0)
+ continue;
+
+ StatusItems_t *p = StatusItems[iIndex];
+ p->ALPHA = pSel->ALPHA;
+ p->BORDERSTYLE = pSel->BORDERSTYLE;
+ p->COLOR = pSel->COLOR;
+ p->COLOR2 = pSel->COLOR2;
+ p->COLOR2_TRANSPARENT = pSel->COLOR2_TRANSPARENT;
+ p->CORNER = pSel->CORNER;
+ p->GRADIENT = pSel->GRADIENT;
+ p->IGNORED = pSel->IGNORED;
+ p->imageItem = pSel->imageItem;
+ p->MARGIN_BOTTOM = pSel->MARGIN_BOTTOM;
+ p->MARGIN_LEFT = pSel->MARGIN_LEFT;
+ p->MARGIN_RIGHT = pSel->MARGIN_RIGHT;
+ p->MARGIN_TOP = pSel->MARGIN_TOP;
+ p->TEXTCOLOR = pSel->TEXTCOLOR;
+ }
+ OnListItemsChange(hwndDlg);
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ // this will check if the user changed some actual statusitems values
+ // if yes the flag bChanged will be set to TRUE
+ SetChangedStatusItemFlag(wParam, hwndDlg);
+ switch (LOWORD(wParam)) {
+ case IDC_ITEMS:
+ if (HIWORD(wParam) != LBN_SELCHANGE)
+ return FALSE;
+ {
+ int iItem = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0), 0);
+ if (iItem == ID_EXTBKSEPARATOR)
+ return FALSE;
+ }
+ OnListItemsChange(hwndDlg);
+ if (psd->pfnClcOptionsChanged)
+ psd->pfnClcOptionsChanged();
+ break;
+ case IDC_GRADIENT:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_CORNER:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_IGNORE:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_COLOR2_TRANSPARENT:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_BORDERTYPE:
+ break;
+ }
+ if ((LOWORD(wParam) == IDC_ALPHA || LOWORD(wParam) == IDC_MRGN_LEFT || LOWORD(wParam) == IDC_MRGN_BOTTOM || LOWORD(wParam) == IDC_MRGN_TOP || LOWORD(wParam) == IDC_MRGN_RIGHT) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
+ return 0;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ // save user made changes
+ SaveLatestChanges(hwndDlg);
+ // save struct to DB
+ if (psd->pfnSaveCompleteStruct)
+ psd->pfnSaveCompleteStruct();
+ db_set_dw(0, "CLCExt", "3dbright", SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_GETCOLOUR, 0, 0));
+ db_set_dw(0, "CLCExt", "3ddark", SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_GETCOLOUR, 0, 0));
+
+ if (psd->pfnClcOptionsChanged)
+ psd->pfnClcOptionsChanged();
+ if (psd->hwndCLUI) {
+ SendMessage(psd->hwndCLUI, WM_SIZE, 0, 0);
+ PostMessage(psd->hwndCLUI, WM_USER + 100, 0, 0); // CLUIINTM_REDRAW
+ }
+ break;
+ }
+ }
+ break;
+ case WM_DESTROY:
+ DestroyMenu(psd->hMenuItems);
+ break;
+ case WM_NCDESTROY:
+ free(psd);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+/*
+ * unimplemented
+*/
+
+static INT_PTR SkinEdit_FillByCurrentSel(WPARAM wParam, LPARAM)
+{
+ if (wParam)
+ FillOptionDialogByCurrentSel((HWND)wParam);
+ return 0;
+}
+
+/*
+ * service function
+ * creates additional tab pages under the given parent window handle
+ * expects a SKINDESCRIPTON * in lParam
+*/
+
+static INT_PTR SkinEdit_Invoke(WPARAM, LPARAM lParam)
+{
+ SKINDESCRIPTION *psd = (SKINDESCRIPTION *)lParam;
+ TCITEM tci = { 0 };
+ RECT rcClient;
+ int iTabs;
+
+ if (psd->cbSize != sizeof(SKINDESCRIPTION))
+ return 0;
+
+ iTabs = TabCtrl_GetItemCount(psd->hWndTab);
+ GetClientRect(psd->hWndParent, &rcClient);
+
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SKINITEMEDIT), psd->hWndParent, SkinEdit_ExtBkDlgProc, (LPARAM)psd);
+
+ tci.pszText = TranslateT("Skin items");
+ TabCtrl_InsertItem(psd->hWndTab, iTabs++, &tci);
+ MoveWindow((HWND)tci.lParam, 5, 25, rcClient.right - 9, rcClient.bottom - 60, 1);
+ psd->hwndSkinEdit = (HWND)tci.lParam;
+ return (INT_PTR)psd->hwndSkinEdit;
+}
+
+int CMPlugin::Load()
+{
+ CreateServiceFunction(MS_CLNSE_INVOKE, SkinEdit_Invoke);
+ CreateServiceFunction(MS_CLNSE_FILLBYCURRENTSEL, SkinEdit_FillByCurrentSel);
+ return 0;
+}
diff --git a/plugins/Cln_skinedit/src/stdafx.cxx b/plugins/Cln_skinedit/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Cln_skinedit/src/stdafx.cxx +++ b/plugins/Cln_skinedit/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Cln_skinedit/src/stdafx.h b/plugins/Cln_skinedit/src/stdafx.h index 8bd8e8f793..830c83e5ab 100644 --- a/plugins/Cln_skinedit/src/stdafx.h +++ b/plugins/Cln_skinedit/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/CmdLine/src/stdafx.cxx b/plugins/CmdLine/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/CmdLine/src/stdafx.cxx +++ b/plugins/CmdLine/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Console/src/Console.cpp b/plugins/Console/src/Console.cpp index 51696191c2..5b6de12dcd 100644 --- a/plugins/Console/src/Console.cpp +++ b/plugins/Console/src/Console.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Console/src/init.cpp b/plugins/Console/src/init.cpp index 89bf4c5a31..5b3f3fe099 100644 --- a/plugins/Console/src/init.cpp +++ b/plugins/Console/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Console/src/stdafx.cxx b/plugins/Console/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Console/src/stdafx.cxx +++ b/plugins/Console/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Console/src/stdafx.h b/plugins/Console/src/stdafx.h index 2c68175f6f..f3afeaf2d3 100644 --- a/plugins/Console/src/stdafx.h +++ b/plugins/Console/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/ContactsPlus/src/stdafx.cxx b/plugins/ContactsPlus/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ContactsPlus/src/stdafx.cxx +++ b/plugins/ContactsPlus/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/CountryFlags/src/stdafx.cxx b/plugins/CountryFlags/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/CountryFlags/src/stdafx.cxx +++ b/plugins/CountryFlags/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/CrashDumper/src/stdafx.cxx b/plugins/CrashDumper/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/CrashDumper/src/stdafx.cxx +++ b/plugins/CrashDumper/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/CrashDumper/src/version.h b/plugins/CrashDumper/src/version.h index 191606abac..f493899a6b 100644 --- a/plugins/CrashDumper/src/version.h +++ b/plugins/CrashDumper/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Crash Dumper and Version Information for Miranda NG."
#define __AUTHOR "borkra"
#define __AUTHORWEB "https://miranda-ng.org/p/CrashDumper"
-#define __COPYRIGHT "© 2008-12 Boris Krasnovskiy, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2008-12 Boris Krasnovskiy, 2012-23 Miranda NG team"
diff --git a/plugins/CryptoPP/src/stdafx.cpp b/plugins/CryptoPP/src/stdafx.cpp index b1991ac69e..73ac0be178 100644 --- a/plugins/CryptoPP/src/stdafx.cpp +++ b/plugins/CryptoPP/src/stdafx.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/CyrTranslit/src/stdafx.cxx b/plugins/CyrTranslit/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/CyrTranslit/src/stdafx.cxx +++ b/plugins/CyrTranslit/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Db3x_mmap/src/database.cpp b/plugins/Db3x_mmap/src/database.cpp index da212ebbfc..ae0bfaa74e 100644 --- a/plugins/Db3x_mmap/src/database.cpp +++ b/plugins/Db3x_mmap/src/database.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/database.h b/plugins/Db3x_mmap/src/database.h index 4078db6b9f..e69550552c 100644 --- a/plugins/Db3x_mmap/src/database.h +++ b/plugins/Db3x_mmap/src/database.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbcache.cpp b/plugins/Db3x_mmap/src/dbcache.cpp index 92af668352..c591dfc01e 100644 --- a/plugins/Db3x_mmap/src/dbcache.cpp +++ b/plugins/Db3x_mmap/src/dbcache.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbcontacts.cpp b/plugins/Db3x_mmap/src/dbcontacts.cpp index a42b010187..222228d454 100644 --- a/plugins/Db3x_mmap/src/dbcontacts.cpp +++ b/plugins/Db3x_mmap/src/dbcontacts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbcrypt.cpp b/plugins/Db3x_mmap/src/dbcrypt.cpp index ee90c2fdf8..00424d458b 100644 --- a/plugins/Db3x_mmap/src/dbcrypt.cpp +++ b/plugins/Db3x_mmap/src/dbcrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbevents.cpp b/plugins/Db3x_mmap/src/dbevents.cpp index d4e99675ed..139a573b6b 100644 --- a/plugins/Db3x_mmap/src/dbevents.cpp +++ b/plugins/Db3x_mmap/src/dbevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbheaders.cpp b/plugins/Db3x_mmap/src/dbheaders.cpp index 3f8ab34067..f5c9d92ea2 100644 --- a/plugins/Db3x_mmap/src/dbheaders.cpp +++ b/plugins/Db3x_mmap/src/dbheaders.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbintf.cpp b/plugins/Db3x_mmap/src/dbintf.cpp index 25f9efb597..6d2916d109 100644 --- a/plugins/Db3x_mmap/src/dbintf.cpp +++ b/plugins/Db3x_mmap/src/dbintf.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbintf.h b/plugins/Db3x_mmap/src/dbintf.h index 981a9a6928..dd425f70dc 100644 --- a/plugins/Db3x_mmap/src/dbintf.h +++ b/plugins/Db3x_mmap/src/dbintf.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbmodulechain.cpp b/plugins/Db3x_mmap/src/dbmodulechain.cpp index d29847bfea..d8b0eaf5cb 100644 --- a/plugins/Db3x_mmap/src/dbmodulechain.cpp +++ b/plugins/Db3x_mmap/src/dbmodulechain.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbsettings.cpp b/plugins/Db3x_mmap/src/dbsettings.cpp index bcc84ad428..b50226b9b3 100644 --- a/plugins/Db3x_mmap/src/dbsettings.cpp +++ b/plugins/Db3x_mmap/src/dbsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/init.cpp b/plugins/Db3x_mmap/src/init.cpp index 4068ad3632..b7f6365b54 100644 --- a/plugins/Db3x_mmap/src/init.cpp +++ b/plugins/Db3x_mmap/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/stdafx.cxx b/plugins/Db3x_mmap/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Db3x_mmap/src/stdafx.cxx +++ b/plugins/Db3x_mmap/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Db3x_mmap/src/stdafx.h b/plugins/Db3x_mmap/src/stdafx.h index 5facbe33cc..963c59fe0c 100644 --- a/plugins/Db3x_mmap/src/stdafx.h +++ b/plugins/Db3x_mmap/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/version.h b/plugins/Db3x_mmap/src/version.h index d9dbacd9dc..c1eae24738 100644 --- a/plugins/Db3x_mmap/src/version.h +++ b/plugins/Db3x_mmap/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
#define __AUTHOR "Miranda-NG project"
#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_mmap"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/plugins/DbEditorPP/src/stdafx.cxx b/plugins/DbEditorPP/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/DbEditorPP/src/stdafx.cxx +++ b/plugins/DbEditorPP/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/DbEditorPP/src/utils.cpp b/plugins/DbEditorPP/src/utils.cpp index 19733db080..6662ba4f83 100644 --- a/plugins/DbEditorPP/src/utils.cpp +++ b/plugins/DbEditorPP/src/utils.cpp @@ -1,386 +1,386 @@ -/* -Copyright (C) 2012-22 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" - -extern uint8_t nameOrder[NAMEORDERCOUNT]; - -///////////////////////////////////////////////////////////////////////////////////////// - -int ListView_GetItemTextA(HWND hwndLV, int i, int iSubItem, char *pszText, int cchTextMax) -{ - LV_ITEMA lvi; - lvi.iSubItem = iSubItem; - lvi.cchTextMax = cchTextMax; - lvi.pszText = pszText; - return SendMessageA(hwndLV, LVM_GETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi); -} - -int ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, const char *pszText) -{ - LV_ITEMA lvi; - lvi.iSubItem = iSubItem; - lvi.pszText = (char*)pszText; - return SendMessageA(hwndLV, LVM_SETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* StringFromBlob(uint8_t *blob, uint16_t len) -{ - int j; - char tmp[16]; - - char *data = (char*)mir_alloc(3 * (len + 2)); - data[0] = 0; - - for (j = 0; j < len; j++) { - mir_snprintf(tmp, "%02X ", blob[j]); - mir_strcat(data, tmp); - } - return data; -} - -int WriteBlobFromString(MCONTACT hContact, const char *szModule, const char *szSetting, const char *szValue, int len) -{ - int j = 0, i = 0; - uint8_t b; - int tmp, res = 0; - uint8_t *data = (uint8_t*)mir_alloc(2 + len / 2); - - if (!data) - return 0; - - while (j < len) { - b = szValue[j]; - - if ((b >= '0' && b <= '9') || - (b >= 'A' && b <= 'F') || - (b >= 'a' && b <= 'f')) { - if (sscanf(&szValue[j], "%02X", &tmp) == 1) { - data[i++] = (uint8_t)(tmp & 0xFF); - j++; - } - } - j++; - } - - - if (i) - res = !db_set_blob(hContact, szModule, szSetting, data, (uint16_t)i); - - mir_free(data); - return res; -} - -wchar_t* DBVType(uint8_t type) -{ - switch (type) { - case DBVT_BYTE: return L"BYTE"; - case DBVT_WORD: return L"WORD"; - case DBVT_DWORD: return L"DWORD"; - case DBVT_ASCIIZ: return L"STRING"; - case DBVT_WCHAR: - case DBVT_UTF8: return L"UNICODE"; - case DBVT_BLOB: return L"BLOB"; - case DBVT_DELETED: return L"DELETED"; - } - return L""; -} - -uint32_t getNumericValue(DBVARIANT *dbv) -{ - switch (dbv->type) { - case DBVT_DWORD: - return dbv->dVal; - case DBVT_WORD: - return dbv->wVal; - case DBVT_BYTE: - return dbv->bVal; - } - return 0; -} - -int setNumericValue(MCONTACT hContact, const char *module, const char *setting, uint32_t value, int type) -{ - switch (type) { - case DBVT_BYTE: - if (value <= 0xFF) - return !db_set_b(hContact, module, setting, (uint8_t)value); - break; - - case DBVT_WORD: - if (value <= 0xFFFF) - return !db_set_w(hContact, module, setting, (uint16_t)value); - break; - - case DBVT_DWORD: - return !db_set_dw(hContact, module, setting, value); - } - return 0; -} - -int IsRealUnicode(wchar_t *value) -{ - BOOL nonascii = 0; - WideCharToMultiByte(Langpack_GetDefaultCodePage(), WC_NO_BEST_FIT_CHARS, value, -1, nullptr, 0, nullptr, &nonascii); - return nonascii; -} - -int setTextValue(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int type) -{ - if (type == DBVT_UTF8 || type == DBVT_WCHAR) - return !db_set_ws(hContact, module, setting, value); - - if (type == DBVT_ASCIIZ && IsRealUnicode(value)) - return 0; - - return !db_set_s(hContact, module, setting, _T2A(value)); -} - -int GetValueA(MCONTACT hContact, const char *module, const char *setting, char *value, int length) -{ - DBVARIANT dbv = {}; - - if (!module || !setting || !value) - return 0; - - if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) { - switch (dbv.type) { - - case DBVT_ASCIIZ: - mir_strncpy(value, dbv.pszVal, length); - break; - - case DBVT_DWORD: - case DBVT_WORD: - case DBVT_BYTE: - _ultoa(getNumericValue(&dbv), value, 10); - break; - - case DBVT_WCHAR: - mir_strncpy(value, ptrA(mir_u2a(dbv.pwszVal)), length); - break; - - case DBVT_UTF8: - mir_strncpy(value, ptrA(mir_utf8decodeA(dbv.pszVal)), length); - break; - - case DBVT_DELETED: - value[0] = 0; - return 0; - } - - int type = dbv.type; - db_free(&dbv); - return type; - } - - value[0] = 0; - return 0; -} - -int GetValueW(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int length) -{ - DBVARIANT dbv = {}; - - if (!module || !setting || !value) - return 0; - - if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) { - switch (dbv.type) { - - case DBVT_ASCIIZ: - mir_wstrncpy(value, ptrW(mir_a2u(dbv.pszVal)), length); - break; - - case DBVT_DWORD: - case DBVT_WORD: - case DBVT_BYTE: - _ultow(getNumericValue(&dbv), value, 10); - break; - - case DBVT_WCHAR: - mir_wstrncpy(value, dbv.pwszVal, length); - break; - - case DBVT_UTF8: - mir_wstrncpy(value, ptrW(mir_utf8decodeW(dbv.pszVal)), length); - break; - - case DBVT_DELETED: - value[0] = 0; - return 0; - } - - int type = dbv.type; - db_free(&dbv); - return type; - } - - value[0] = 0; - return 0; -} - -int GetContactName(MCONTACT hContact, const char *proto, wchar_t *value, int maxlen) -{ - if (!value) - return 0; - - if (!hContact) { - mir_wstrncpy(value, TranslateT("Settings"), maxlen); - return 1; - } - - char *szProto = (char*)proto; - char tmp[FLD_SIZE]; - wchar_t name[NAME_SIZE]; name[0] = 0; - - if (hContact && (!proto || !proto[0])) - if (!db_get_static(hContact, "Protocol", "p", tmp, _countof(tmp))) - szProto = tmp; - - for (int i = 0; i < NAMEORDERCOUNT - 1; i++) { - switch (nameOrder[i]) { - case 0: // custom name - GetValueW(hContact, "CList", "MyHandle", name, _countof(name)); - break; - - case 1: // nick - if (!szProto) break; - GetValueW(hContact, szProto, "Nick", name, _countof(name)); - break; - - case 2: // First Name - // if (!szProto) break; - // GetValueW(hContact, szProto, "FirstName", name, _countof(name)); - break; - - case 3: // E-mail - if (!szProto) break; - GetValueW(hContact, szProto, "e-mail", name, _countof(name)); - break; - - case 4: // Last Name - // GetValueW(hContact, szProto, "LastName", name, _countof(name)); - break; - - case 5: // Unique id - if (szProto) { - // protocol must define a PFLAG_UNIQUEIDSETTING - const char *uid = Proto_GetUniqueId(szProto); - if (uid) - GetValueW(hContact, szProto, uid, name, _countof(name)); - } - break; - - case 6: // first + last name - if (szProto) { - GetValueW(hContact, szProto, "FirstName", name, _countof(name)); - - int len = (int)mir_wstrlen(name); - if (len + 2 < _countof(name)) { - if (len) - mir_wstrncat(name, L" ", _countof(name)); - len++; - GetValueW(hContact, szProto, "LastName", &name[len], _countof(name) - len); - } - } - break; - } - - if (name[0]) - break; - } - - if (!name[0]) - mir_wstrncpy(name, TranslateT("<UNKNOWN>"), _countof(name)); - - if (szProto && szProto[0]) { - if (g_Order) - mir_snwprintf(value, maxlen, L"(%S) %s", szProto, name); - else - mir_snwprintf(value, maxlen, L"%s (%S)", name, szProto); - } - else mir_wstrncpy(value, name, maxlen); - - PROTOACCOUNT *pa = Proto_GetAccount(szProto); - if (!pa->IsEnabled()) { - mir_wstrncat(value, L" ", maxlen); - mir_wstrncat(value, TranslateT("[UNLOADED]"), maxlen); - } - - return 1; -} - -int ApplyProtoFilter(MCONTACT hContact) -{ - if (g_Mode == MODE_ALL) return 0; - - int loaded = 0; - char szProto[FLD_SIZE]; - - if (!db_get_static(hContact, "Protocol", "p", szProto, _countof(szProto))) - loaded = Proto_GetAccount(szProto) ? 1 : 0; - - if ((loaded && g_Mode == MODE_UNLOADED) || (!loaded && g_Mode == MODE_LOADED)) - return 1; - - return 0; -} - -void loadListSettings(HWND hwnd, ColumnsSettings *cs) -{ - LVCOLUMN sLC = {}; - sLC.fmt = LVCFMT_LEFT; - sLC.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; - int i = 0; - while (cs[i].name) { - sLC.pszText = TranslateW(cs[i].name); - sLC.cx = g_plugin.getWord(cs[i].dbname, cs[i].defsize); - ListView_InsertColumn(hwnd, cs[i].index, &sLC); - i++; - } -} - -void saveListSettings(HWND hwnd, ColumnsSettings *cs) -{ - char tmp[FLD_SIZE]; - LVCOLUMN sLC = {}; - sLC.mask = LVCF_WIDTH; - int i = 0; - while (cs[i].name) { - if (ListView_GetColumn(hwnd, cs[i].index, &sLC)) { - mir_snprintf(tmp, cs[i].dbname, i); - g_plugin.setWord(tmp, (uint16_t)sLC.cx); - } - i++; - } -} - -int CALLBACK ColumnsCompare(LPARAM lParam1, LPARAM lParam2, LPARAM myParam) -{ - ColumnsSortParams params = *(ColumnsSortParams *)myParam; - const int maxSize = 1024; - wchar_t text1[maxSize]; - wchar_t text2[maxSize]; - ListView_GetItemText(params.hList, lParam1, params.column, text1, _countof(text1)); - ListView_GetItemText(params.hList, lParam2, params.column, text2, _countof(text2)); - - int res = mir_wstrcmpi(text1, text2); - return (params.column == params.last) ? -res : res; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+extern uint8_t nameOrder[NAMEORDERCOUNT];
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int ListView_GetItemTextA(HWND hwndLV, int i, int iSubItem, char *pszText, int cchTextMax)
+{
+ LV_ITEMA lvi;
+ lvi.iSubItem = iSubItem;
+ lvi.cchTextMax = cchTextMax;
+ lvi.pszText = pszText;
+ return SendMessageA(hwndLV, LVM_GETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi);
+}
+
+int ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, const char *pszText)
+{
+ LV_ITEMA lvi;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = (char*)pszText;
+ return SendMessageA(hwndLV, LVM_SETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* StringFromBlob(uint8_t *blob, uint16_t len)
+{
+ int j;
+ char tmp[16];
+
+ char *data = (char*)mir_alloc(3 * (len + 2));
+ data[0] = 0;
+
+ for (j = 0; j < len; j++) {
+ mir_snprintf(tmp, "%02X ", blob[j]);
+ mir_strcat(data, tmp);
+ }
+ return data;
+}
+
+int WriteBlobFromString(MCONTACT hContact, const char *szModule, const char *szSetting, const char *szValue, int len)
+{
+ int j = 0, i = 0;
+ uint8_t b;
+ int tmp, res = 0;
+ uint8_t *data = (uint8_t*)mir_alloc(2 + len / 2);
+
+ if (!data)
+ return 0;
+
+ while (j < len) {
+ b = szValue[j];
+
+ if ((b >= '0' && b <= '9') ||
+ (b >= 'A' && b <= 'F') ||
+ (b >= 'a' && b <= 'f')) {
+ if (sscanf(&szValue[j], "%02X", &tmp) == 1) {
+ data[i++] = (uint8_t)(tmp & 0xFF);
+ j++;
+ }
+ }
+ j++;
+ }
+
+
+ if (i)
+ res = !db_set_blob(hContact, szModule, szSetting, data, (uint16_t)i);
+
+ mir_free(data);
+ return res;
+}
+
+wchar_t* DBVType(uint8_t type)
+{
+ switch (type) {
+ case DBVT_BYTE: return L"BYTE";
+ case DBVT_WORD: return L"WORD";
+ case DBVT_DWORD: return L"DWORD";
+ case DBVT_ASCIIZ: return L"STRING";
+ case DBVT_WCHAR:
+ case DBVT_UTF8: return L"UNICODE";
+ case DBVT_BLOB: return L"BLOB";
+ case DBVT_DELETED: return L"DELETED";
+ }
+ return L"";
+}
+
+uint32_t getNumericValue(DBVARIANT *dbv)
+{
+ switch (dbv->type) {
+ case DBVT_DWORD:
+ return dbv->dVal;
+ case DBVT_WORD:
+ return dbv->wVal;
+ case DBVT_BYTE:
+ return dbv->bVal;
+ }
+ return 0;
+}
+
+int setNumericValue(MCONTACT hContact, const char *module, const char *setting, uint32_t value, int type)
+{
+ switch (type) {
+ case DBVT_BYTE:
+ if (value <= 0xFF)
+ return !db_set_b(hContact, module, setting, (uint8_t)value);
+ break;
+
+ case DBVT_WORD:
+ if (value <= 0xFFFF)
+ return !db_set_w(hContact, module, setting, (uint16_t)value);
+ break;
+
+ case DBVT_DWORD:
+ return !db_set_dw(hContact, module, setting, value);
+ }
+ return 0;
+}
+
+int IsRealUnicode(wchar_t *value)
+{
+ BOOL nonascii = 0;
+ WideCharToMultiByte(Langpack_GetDefaultCodePage(), WC_NO_BEST_FIT_CHARS, value, -1, nullptr, 0, nullptr, &nonascii);
+ return nonascii;
+}
+
+int setTextValue(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int type)
+{
+ if (type == DBVT_UTF8 || type == DBVT_WCHAR)
+ return !db_set_ws(hContact, module, setting, value);
+
+ if (type == DBVT_ASCIIZ && IsRealUnicode(value))
+ return 0;
+
+ return !db_set_s(hContact, module, setting, _T2A(value));
+}
+
+int GetValueA(MCONTACT hContact, const char *module, const char *setting, char *value, int length)
+{
+ DBVARIANT dbv = {};
+
+ if (!module || !setting || !value)
+ return 0;
+
+ if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) {
+ switch (dbv.type) {
+
+ case DBVT_ASCIIZ:
+ mir_strncpy(value, dbv.pszVal, length);
+ break;
+
+ case DBVT_DWORD:
+ case DBVT_WORD:
+ case DBVT_BYTE:
+ _ultoa(getNumericValue(&dbv), value, 10);
+ break;
+
+ case DBVT_WCHAR:
+ mir_strncpy(value, ptrA(mir_u2a(dbv.pwszVal)), length);
+ break;
+
+ case DBVT_UTF8:
+ mir_strncpy(value, ptrA(mir_utf8decodeA(dbv.pszVal)), length);
+ break;
+
+ case DBVT_DELETED:
+ value[0] = 0;
+ return 0;
+ }
+
+ int type = dbv.type;
+ db_free(&dbv);
+ return type;
+ }
+
+ value[0] = 0;
+ return 0;
+}
+
+int GetValueW(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int length)
+{
+ DBVARIANT dbv = {};
+
+ if (!module || !setting || !value)
+ return 0;
+
+ if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) {
+ switch (dbv.type) {
+
+ case DBVT_ASCIIZ:
+ mir_wstrncpy(value, ptrW(mir_a2u(dbv.pszVal)), length);
+ break;
+
+ case DBVT_DWORD:
+ case DBVT_WORD:
+ case DBVT_BYTE:
+ _ultow(getNumericValue(&dbv), value, 10);
+ break;
+
+ case DBVT_WCHAR:
+ mir_wstrncpy(value, dbv.pwszVal, length);
+ break;
+
+ case DBVT_UTF8:
+ mir_wstrncpy(value, ptrW(mir_utf8decodeW(dbv.pszVal)), length);
+ break;
+
+ case DBVT_DELETED:
+ value[0] = 0;
+ return 0;
+ }
+
+ int type = dbv.type;
+ db_free(&dbv);
+ return type;
+ }
+
+ value[0] = 0;
+ return 0;
+}
+
+int GetContactName(MCONTACT hContact, const char *proto, wchar_t *value, int maxlen)
+{
+ if (!value)
+ return 0;
+
+ if (!hContact) {
+ mir_wstrncpy(value, TranslateT("Settings"), maxlen);
+ return 1;
+ }
+
+ char *szProto = (char*)proto;
+ char tmp[FLD_SIZE];
+ wchar_t name[NAME_SIZE]; name[0] = 0;
+
+ if (hContact && (!proto || !proto[0]))
+ if (!db_get_static(hContact, "Protocol", "p", tmp, _countof(tmp)))
+ szProto = tmp;
+
+ for (int i = 0; i < NAMEORDERCOUNT - 1; i++) {
+ switch (nameOrder[i]) {
+ case 0: // custom name
+ GetValueW(hContact, "CList", "MyHandle", name, _countof(name));
+ break;
+
+ case 1: // nick
+ if (!szProto) break;
+ GetValueW(hContact, szProto, "Nick", name, _countof(name));
+ break;
+
+ case 2: // First Name
+ // if (!szProto) break;
+ // GetValueW(hContact, szProto, "FirstName", name, _countof(name));
+ break;
+
+ case 3: // E-mail
+ if (!szProto) break;
+ GetValueW(hContact, szProto, "e-mail", name, _countof(name));
+ break;
+
+ case 4: // Last Name
+ // GetValueW(hContact, szProto, "LastName", name, _countof(name));
+ break;
+
+ case 5: // Unique id
+ if (szProto) {
+ // protocol must define a PFLAG_UNIQUEIDSETTING
+ const char *uid = Proto_GetUniqueId(szProto);
+ if (uid)
+ GetValueW(hContact, szProto, uid, name, _countof(name));
+ }
+ break;
+
+ case 6: // first + last name
+ if (szProto) {
+ GetValueW(hContact, szProto, "FirstName", name, _countof(name));
+
+ int len = (int)mir_wstrlen(name);
+ if (len + 2 < _countof(name)) {
+ if (len)
+ mir_wstrncat(name, L" ", _countof(name));
+ len++;
+ GetValueW(hContact, szProto, "LastName", &name[len], _countof(name) - len);
+ }
+ }
+ break;
+ }
+
+ if (name[0])
+ break;
+ }
+
+ if (!name[0])
+ mir_wstrncpy(name, TranslateT("<UNKNOWN>"), _countof(name));
+
+ if (szProto && szProto[0]) {
+ if (g_Order)
+ mir_snwprintf(value, maxlen, L"(%S) %s", szProto, name);
+ else
+ mir_snwprintf(value, maxlen, L"%s (%S)", name, szProto);
+ }
+ else mir_wstrncpy(value, name, maxlen);
+
+ PROTOACCOUNT *pa = Proto_GetAccount(szProto);
+ if (!pa->IsEnabled()) {
+ mir_wstrncat(value, L" ", maxlen);
+ mir_wstrncat(value, TranslateT("[UNLOADED]"), maxlen);
+ }
+
+ return 1;
+}
+
+int ApplyProtoFilter(MCONTACT hContact)
+{
+ if (g_Mode == MODE_ALL) return 0;
+
+ int loaded = 0;
+ char szProto[FLD_SIZE];
+
+ if (!db_get_static(hContact, "Protocol", "p", szProto, _countof(szProto)))
+ loaded = Proto_GetAccount(szProto) ? 1 : 0;
+
+ if ((loaded && g_Mode == MODE_UNLOADED) || (!loaded && g_Mode == MODE_LOADED))
+ return 1;
+
+ return 0;
+}
+
+void loadListSettings(HWND hwnd, ColumnsSettings *cs)
+{
+ LVCOLUMN sLC = {};
+ sLC.fmt = LVCFMT_LEFT;
+ sLC.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
+ int i = 0;
+ while (cs[i].name) {
+ sLC.pszText = TranslateW(cs[i].name);
+ sLC.cx = g_plugin.getWord(cs[i].dbname, cs[i].defsize);
+ ListView_InsertColumn(hwnd, cs[i].index, &sLC);
+ i++;
+ }
+}
+
+void saveListSettings(HWND hwnd, ColumnsSettings *cs)
+{
+ char tmp[FLD_SIZE];
+ LVCOLUMN sLC = {};
+ sLC.mask = LVCF_WIDTH;
+ int i = 0;
+ while (cs[i].name) {
+ if (ListView_GetColumn(hwnd, cs[i].index, &sLC)) {
+ mir_snprintf(tmp, cs[i].dbname, i);
+ g_plugin.setWord(tmp, (uint16_t)sLC.cx);
+ }
+ i++;
+ }
+}
+
+int CALLBACK ColumnsCompare(LPARAM lParam1, LPARAM lParam2, LPARAM myParam)
+{
+ ColumnsSortParams params = *(ColumnsSortParams *)myParam;
+ const int maxSize = 1024;
+ wchar_t text1[maxSize];
+ wchar_t text2[maxSize];
+ ListView_GetItemText(params.hList, lParam1, params.column, text1, _countof(text1));
+ ListView_GetItemText(params.hList, lParam2, params.column, text2, _countof(text2));
+
+ int res = mir_wstrcmpi(text1, text2);
+ return (params.column == params.last) ? -res : res;
+}
diff --git a/plugins/DbEditorPP/src/version.h b/plugins/DbEditorPP/src/version.h index 19d7f43571..082cc92b36 100644 --- a/plugins/DbEditorPP/src/version.h +++ b/plugins/DbEditorPP/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Advanced Database Editor."
#define __AUTHOR "Bio, Jonathan Gordon"
#define __AUTHORWEB "https://miranda-ng.org/p/DbEditorPP"
-#define __COPYRIGHT "© 2003-22 Bio, Jonathan Gordon, Miranda NG team"
+#define __COPYRIGHT "© 2003-23 Bio, Jonathan Gordon, Miranda NG team"
diff --git a/plugins/Db_autobackups/src/options.h b/plugins/Db_autobackups/src/options.h index df231e0ac7..3efb713158 100644 --- a/plugins/Db_autobackups/src/options.h +++ b/plugins/Db_autobackups/src/options.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db_autobackups/src/stdafx.cxx b/plugins/Db_autobackups/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Db_autobackups/src/stdafx.cxx +++ b/plugins/Db_autobackups/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Db_autobackups/src/version.h b/plugins/Db_autobackups/src/version.h index 4fc7564caf..a015445ec2 100644 --- a/plugins/Db_autobackups/src/version.h +++ b/plugins/Db_autobackups/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Database autobackuper plugin."
#define __AUTHOR "chaos.persei, sje, Kildor, Billy_Bons"
#define __AUTHORWEB "https://miranda-ng.org/p/Db_autobackups"
-#define __COPYRIGHT "© 2005-22 chaos.persei, sje, Kildor, Billy_Bons, Vasilich, Miranda NG team"
+#define __COPYRIGHT "© 2005-23 chaos.persei, sje, Kildor, Billy_Bons, Vasilich, Miranda NG team"
diff --git a/plugins/Dbx_mdbx/src/dbcheck.cpp b/plugins/Dbx_mdbx/src/dbcheck.cpp index ed812365be..48449d360b 100644 --- a/plugins/Dbx_mdbx/src/dbcheck.cpp +++ b/plugins/Dbx_mdbx/src/dbcheck.cpp @@ -1,117 +1,117 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// we are tracing EventsSort table to verify that they have correct event ids - -int CDbxMDBX::CheckEvents1(void) -{ - txn_ptr trnlck(this); - cursor_ptr cursor(trnlck, m_dbEventsSort); - - MDBX_val key, data; - for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) { - auto *pData = (DBEventSortingKey *)key.iov_base; - - // if that's not a member of system event, check contact's existence first - if (pData->hContact != 0) { - auto *cc = m_cache->GetCachedContact(pData->hContact); - if (cc == nullptr) { - mdbx_cursor_del(cursor, MDBX_UPSERT); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong contact ID %d, deleting"), pData->hContact)); - continue; - } - } - - if (GetBlobSize(pData->hEvent) == -1) { - mdbx_cursor_del(cursor, MDBX_UPSERT); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong event ID %d:%08X, deleting"), pData->hContact, pData->hEvent)); - continue; - } - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// we are tracing EventId table to verify that they have correct event ids - -int CDbxMDBX::CheckEvents2(void) -{ - txn_ptr trnlck(this); - cursor_ptr cursor(trnlck, m_dbEventIds); - - MDBX_val key, data; - for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) { - MEVENT hDbEvent = *(MEVENT *)data.iov_base; - if (GetBlobSize(hDbEvent) == -1) { - mdbx_cursor_del(cursor, MDBX_UPSERT); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event id with wrong event ID %08X, deleting"), hDbEvent)); - continue; - } - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// we are tracing Settings table to verify that they have correct contact ids - -int CDbxMDBX::CheckEvents3(void) -{ - txn_ptr trnlck(this); - cursor_ptr cursor(trnlck, m_dbSettings); - - MDBX_val key, data; - for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) { - auto *pKey = (DBSettingKey *)key.iov_base; - - if (pKey->hContact) { - auto *cc = m_cache->GetCachedContact(pKey->hContact); - if (cc == nullptr) { - mdbx_cursor_del(cursor, MDBX_UPSERT); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned setting with wrong contact ID %08X, deleting"), pKey->hContact)); - continue; - } - } - } - - return 0; -} - - -/////////////////////////////////////////////////////////////////////////////// -// MIDatabaseChecker - -int CDbxMDBX::CheckDb(int phase) -{ - switch (phase) { - case 0: return CheckEvents1(); - case 1: return CheckEvents2(); - case 2: return CheckEvents3(); - } - - return ERROR_OUT_OF_PAPER; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// we are tracing EventsSort table to verify that they have correct event ids
+
+int CDbxMDBX::CheckEvents1(void)
+{
+ txn_ptr trnlck(this);
+ cursor_ptr cursor(trnlck, m_dbEventsSort);
+
+ MDBX_val key, data;
+ for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ auto *pData = (DBEventSortingKey *)key.iov_base;
+
+ // if that's not a member of system event, check contact's existence first
+ if (pData->hContact != 0) {
+ auto *cc = m_cache->GetCachedContact(pData->hContact);
+ if (cc == nullptr) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong contact ID %d, deleting"), pData->hContact));
+ continue;
+ }
+ }
+
+ if (GetBlobSize(pData->hEvent) == -1) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong event ID %d:%08X, deleting"), pData->hContact, pData->hEvent));
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// we are tracing EventId table to verify that they have correct event ids
+
+int CDbxMDBX::CheckEvents2(void)
+{
+ txn_ptr trnlck(this);
+ cursor_ptr cursor(trnlck, m_dbEventIds);
+
+ MDBX_val key, data;
+ for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ MEVENT hDbEvent = *(MEVENT *)data.iov_base;
+ if (GetBlobSize(hDbEvent) == -1) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event id with wrong event ID %08X, deleting"), hDbEvent));
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// we are tracing Settings table to verify that they have correct contact ids
+
+int CDbxMDBX::CheckEvents3(void)
+{
+ txn_ptr trnlck(this);
+ cursor_ptr cursor(trnlck, m_dbSettings);
+
+ MDBX_val key, data;
+ for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ auto *pKey = (DBSettingKey *)key.iov_base;
+
+ if (pKey->hContact) {
+ auto *cc = m_cache->GetCachedContact(pKey->hContact);
+ if (cc == nullptr) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned setting with wrong contact ID %08X, deleting"), pKey->hContact));
+ continue;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// MIDatabaseChecker
+
+int CDbxMDBX::CheckDb(int phase)
+{
+ switch (phase) {
+ case 0: return CheckEvents1();
+ case 1: return CheckEvents2();
+ case 2: return CheckEvents3();
+ }
+
+ return ERROR_OUT_OF_PAPER;
+}
diff --git a/plugins/Dbx_mdbx/src/dbcontacts.cpp b/plugins/Dbx_mdbx/src/dbcontacts.cpp index 13beacb0ac..d96a94f806 100644 --- a/plugins/Dbx_mdbx/src/dbcontacts.cpp +++ b/plugins/Dbx_mdbx/src/dbcontacts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbcrypt.cpp b/plugins/Dbx_mdbx/src/dbcrypt.cpp index 5b67f18ea7..7dd373f743 100644 --- a/plugins/Dbx_mdbx/src/dbcrypt.cpp +++ b/plugins/Dbx_mdbx/src/dbcrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbevents.cpp b/plugins/Dbx_mdbx/src/dbevents.cpp index 581a762c0a..0d26fa1510 100644 --- a/plugins/Dbx_mdbx/src/dbevents.cpp +++ b/plugins/Dbx_mdbx/src/dbevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbintf.cpp b/plugins/Dbx_mdbx/src/dbintf.cpp index 3570b60690..32993384b8 100644 --- a/plugins/Dbx_mdbx/src/dbintf.cpp +++ b/plugins/Dbx_mdbx/src/dbintf.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbintf.h b/plugins/Dbx_mdbx/src/dbintf.h index cdbdfc7cf3..ee612dba63 100644 --- a/plugins/Dbx_mdbx/src/dbintf.h +++ b/plugins/Dbx_mdbx/src/dbintf.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbmodulechain.cpp b/plugins/Dbx_mdbx/src/dbmodulechain.cpp index e528c23397..229f6cb4fc 100644 --- a/plugins/Dbx_mdbx/src/dbmodulechain.cpp +++ b/plugins/Dbx_mdbx/src/dbmodulechain.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbsettings.cpp b/plugins/Dbx_mdbx/src/dbsettings.cpp index 68fdcbe46a..074c107634 100644 --- a/plugins/Dbx_mdbx/src/dbsettings.cpp +++ b/plugins/Dbx_mdbx/src/dbsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbutils.cpp b/plugins/Dbx_mdbx/src/dbutils.cpp index 380b714069..ebe6c85123 100644 --- a/plugins/Dbx_mdbx/src/dbutils.cpp +++ b/plugins/Dbx_mdbx/src/dbutils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/init.cpp b/plugins/Dbx_mdbx/src/init.cpp index d3911ae0c9..b4f0bf711f 100644 --- a/plugins/Dbx_mdbx/src/init.cpp +++ b/plugins/Dbx_mdbx/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/stdafx.cxx b/plugins/Dbx_mdbx/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Dbx_mdbx/src/stdafx.cxx +++ b/plugins/Dbx_mdbx/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Dbx_mdbx/src/stdafx.h b/plugins/Dbx_mdbx/src/stdafx.h index f150177cd9..f329331d8f 100644 --- a/plugins/Dbx_mdbx/src/stdafx.h +++ b/plugins/Dbx_mdbx/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/version.h b/plugins/Dbx_mdbx/src/version.h index 29a2eeb1b7..bd19e10b94 100644 --- a/plugins/Dbx_mdbx/src/version.h +++ b/plugins/Dbx_mdbx/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
#define __AUTHOR "Miranda-NG project"
#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_mdbx"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/Dbx_sqlite/src/version.h b/plugins/Dbx_sqlite/src/version.h index 00fd440b6f..ffe245bc61 100644 --- a/plugins/Dbx_sqlite/src/version.h +++ b/plugins/Dbx_sqlite/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
#define __AUTHOR "Miranda-NG project"
#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_sqlite"
-#define __COPYRIGHT "© 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2018-23 Miranda NG team"
diff --git a/plugins/Exchange/src/stdafx.cxx b/plugins/Exchange/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Exchange/src/stdafx.cxx +++ b/plugins/Exchange/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ExternalAPI/m_assocmgr.h b/plugins/ExternalAPI/m_assocmgr.h index 9ffdb98953..e1d06c8f3e 100644 --- a/plugins/ExternalAPI/m_assocmgr.h +++ b/plugins/ExternalAPI/m_assocmgr.h @@ -3,7 +3,7 @@ 'File Association Manager'-Plugin for
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (C) 2005-2007 H. Herkenrath
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_dbeditorpp.h b/plugins/ExternalAPI/m_dbeditorpp.h index 36418f9162..10785ee0c5 100644 --- a/plugins/ExternalAPI/m_dbeditorpp.h +++ b/plugins/ExternalAPI/m_dbeditorpp.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2003-11 Bio, Jonathan Gordon
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_proto_listeningto.h b/plugins/ExternalAPI/m_proto_listeningto.h index 0411a21660..24a0cd0ddf 100644 --- a/plugins/ExternalAPI/m_proto_listeningto.h +++ b/plugins/ExternalAPI/m_proto_listeningto.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-06 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/ExternalAPI/m_shutdown.h b/plugins/ExternalAPI/m_shutdown.h index edd66b6271..56d7e8a301 100644 --- a/plugins/ExternalAPI/m_shutdown.h +++ b/plugins/ExternalAPI/m_shutdown.h @@ -3,7 +3,7 @@ 'AutoShutdown'-Plugin for
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (C) 2004-2007 H. Herkenrath
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_skin_eng.h b/plugins/ExternalAPI/m_skin_eng.h index f22b6a3e1b..6ea48aced9 100644 --- a/plugins/ExternalAPI/m_skin_eng.h +++ b/plugins/ExternalAPI/m_skin_eng.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/ExternalAPI/m_stopspam.h b/plugins/ExternalAPI/m_stopspam.h index d2f843ab6a..692630eaaa 100644 --- a/plugins/ExternalAPI/m_stopspam.h +++ b/plugins/ExternalAPI/m_stopspam.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2004-009 Roman Miklashevsky, A. Petkevich, Kosh&chka, persei
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_userinfoex.h b/plugins/ExternalAPI/m_userinfoex.h index 08b412b16e..f73c23d1d9 100644 --- a/plugins/ExternalAPI/m_userinfoex.h +++ b/plugins/ExternalAPI/m_userinfoex.h @@ -1,7 +1,7 @@ /*
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-09 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/FTPFileYM/src/stdafx.cxx b/plugins/FTPFileYM/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/FTPFileYM/src/stdafx.cxx +++ b/plugins/FTPFileYM/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/FavContacts/src/stdafx.cxx b/plugins/FavContacts/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/FavContacts/src/stdafx.cxx +++ b/plugins/FavContacts/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/FingerprintNG/src/fingerprint.cpp b/plugins/FingerprintNG/src/fingerprint.cpp index 0842cc1007..e15f77536d 100644 --- a/plugins/FingerprintNG/src/fingerprint.cpp +++ b/plugins/FingerprintNG/src/fingerprint.cpp @@ -1,913 +1,913 @@ -fv/* -Fingerprint NG (client version) icons module for Miranda NG -Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -//Start of header -#include "stdafx.h" - -static UINT g_LPCodePage; -static wchar_t g_szSkinLib[MAX_PATH]; -static HANDLE hExtraIcon = nullptr; -static HANDLE hFolderChanged = nullptr, hIconFolder = nullptr; - -static int CompareFI(const FOUNDINFO *p1, const FOUNDINFO *p2) -{ - if (p1->iBase != p2->iBase) - return p1->iBase - p2->iBase; - return p1->iOverlay - p2->iOverlay; -} - -static OBJLIST<FOUNDINFO> arFI(50, CompareFI); - -static LIST<void> arMonitoredWindows(3, PtrKeySortT); - -static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM lParam); - -/* -* Prepare -* prepares upperstring masks and registers them in IcoLib -*/ - -static wchar_t* getSectionName(int flag) -{ - switch (flag) - { -#include "finger_groups.h" - } - return nullptr; -} - -void __fastcall Prepare(KN_FP_MASK* mask, bool bEnable) -{ - mask->szMaskUpper = nullptr; - - if (mask->hIcolibItem) - IcoLib_RemoveIcon(mask->szIconName); - mask->hIcolibItem = nullptr; - - if (!mask->szMask || !bEnable) - return; - - size_t iMaskLen = mir_wstrlen(mask->szMask) + 1; - LPTSTR pszNewMask = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, iMaskLen * sizeof(wchar_t)); - wcscpy_s(pszNewMask, iMaskLen, mask->szMask); - _wcsupr_s(pszNewMask, iMaskLen); - mask->szMaskUpper = pszNewMask; - - wchar_t destfile[MAX_PATH]; - if (mask->iIconIndex == IDI_NOTFOUND || mask->iIconIndex == IDI_UNKNOWN || mask->iIconIndex == IDI_UNDETECTED) - GetModuleFileName(g_plugin.getInst(), destfile, MAX_PATH); - else { - wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE); - - struct _stat64i32 stFileInfo; - if (_wstat(destfile, &stFileInfo) == -1) - return; - } - - LPTSTR SectName = getSectionName(mask->iSectionFlag); - if (SectName == nullptr) - return; - - SKINICONDESC sid = {}; - sid.flags = SIDF_ALL_UNICODE; - sid.section.w = SectName; - sid.pszName = mask->szIconName; - sid.description.w = mask->szClientDescription; - sid.defaultFile.w = destfile; - sid.iDefaultIndex = -mask->iIconIndex; - mask->hIcolibItem = g_plugin.addIcon(&sid); -} - -/* -* Register icons -*/ - -void RegisterIcons() -{ - // prepare masks - int i; - - if (hHeap) - HeapDestroy(hHeap); - hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0); - - for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++) - Prepare(&def_kn_fp_mask[i], true); - - for (i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++) - Prepare(&def_kn_fp_overlays_mask[i], true); - - if (g_plugin.getByte("GroupMirandaVersion", 0)) { - for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++) - Prepare(&def_kn_fp_overlays2_mask[i], true); - } - else { - for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++) - Prepare(&def_kn_fp_overlays2_mask[i], true); - for (; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++) - Prepare(&def_kn_fp_overlays2_mask[i], false); - } - - if (g_plugin.getByte("GroupOverlaysUnicode", 1)) { - for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++) - Prepare(&def_kn_fp_overlays3_mask[i], true); - } - else { - for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_NO_UNICODE_COUNT; i++) - Prepare(&def_kn_fp_overlays3_mask[i], true); - for (; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++) - Prepare(&def_kn_fp_overlays3_mask[i], false); - } - - for (i = 0; i < DEFAULT_KN_FP_OVERLAYS4_COUNT; i++) - Prepare(&def_kn_fp_overlays4_mask[i], true); -} - -/* ApplyFingerprintImage -* 1)Try to find appropriate mask -* 2)Register icon in extraimage list if not yet registered (EMPTY_EXTRA_ICON) -* 3)Set ExtraImage for contact -*/ - -static void SetSrmmIcon(MCONTACT hContact, LPTSTR ptszMirver) -{ - if (mir_wstrlen(ptszMirver)) - Srmm_ModifyIcon(hContact, MODULENAME, 1, (HICON)ServiceGetClientIconW((WPARAM)ptszMirver, TRUE), ptszMirver); - else - Srmm_SetIconFlags(hContact, MODULENAME, 1, MBF_HIDDEN); -} - -int __fastcall ApplyFingerprintImage(MCONTACT hContact, LPTSTR szMirVer) -{ - if (hContact == NULL) - return 0; - - HANDLE hImage = INVALID_HANDLE_VALUE; - if (szMirVer) - hImage = GetIconIndexFromFI(szMirVer); - - ExtraIcon_SetIcon(hExtraIcon, hContact, hImage); - - if (arMonitoredWindows.getIndex((HANDLE)hContact) != -1) - SetSrmmIcon(hContact, szMirVer); - - return 0; -} - -int OnExtraIconClick(WPARAM wParam, LPARAM, LPARAM) -{ - CallService(MS_USERINFO_SHOWDIALOG, wParam, NULL); - return 0; -} - - -/* -* WildCompare -* Compare 'name' string with 'mask' strings. -* Masks can contain '*' or '?' wild symbols -* Asterics '*' symbol covers 'empty' symbol too e.g WildCompare("Tst","T*st*"), returns TRUE -* In order to handle situation 'at least one any sybol' use "?*" combination: -* e.g WildCompare("Tst","T?*st*"), returns FALSE, but both WildCompare("Test","T?*st*") and -* WildCompare("Teeest","T?*st*") return TRUE. -* -* Function is case sensitive! so convert input or modify func to use _qtoupper() -* -* Mask can contain several submasks. In this case each submask (including first) -* should start from '|' e.g: "|first*submask|second*mask". -* -* Dec 25, 2006 by FYR: -* Added Exception to masks: the mask "|^mask3|mask2|mask1" means: -* if NOT according to mask 3 AND (mask1 OR mask2) -* EXCEPTION should be BEFORE main mask: -* IF Exception match - the comparing stops as FALSE -* IF Exception does not match - comparing continue -* IF Mask match - comparing stops as TRUE -* IF Mask does not not match comparing continue -*/ -BOOL __fastcall WildCompare(LPWSTR wszName, LPWSTR wszMask) -{ - if (wszMask == nullptr) - return NULL; - - if (*wszMask != L'|') - return wildcmpw(wszName, wszMask); - - size_t s = 1, e = 1; - LPWSTR wszTemp = (LPWSTR)_alloca(mir_wstrlen(wszMask) * sizeof(wchar_t) + sizeof(wchar_t)); - BOOL bExcept; - - while (wszMask[e] != L'\0') - { - s = e; - while (wszMask[e] != L'\0' && wszMask[e] != L'|') e++; - - // exception mask - bExcept = (*(wszMask + s) == L'^'); - if (bExcept) s++; - - memcpy(wszTemp, wszMask + s, (e - s) * sizeof(wchar_t)); - wszTemp[e - s] = L'\0'; - - if (wildcmpw(wszName, wszTemp)) - return !bExcept; - - if (wszMask[e] != L'\0') - e++; - else - return FALSE; - } - return FALSE; -} - -static void MatchMasks(wchar_t* szMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4) -{ - int i = 0, j = -1, k = -1, n = -1, m = -1; - - for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++) { - KN_FP_MASK& p = def_kn_fp_mask[i]; - if (p.hIcolibItem == nullptr) - continue; - - if (!WildCompare(szMirVer, p.szMaskUpper)) - continue; - - if (p.iIconIndex != IDI_NOTFOUND && p.iIconIndex != IDI_UNKNOWN && p.iIconIndex != IDI_UNDETECTED) { - wchar_t destfile[MAX_PATH]; - wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE); - - struct _stat64i32 stFileInfo; - if (_wstat(destfile, &stFileInfo) == -1) - i = NOTFOUND_MASK_NUMBER; - } - break; - } - if (i == DEFAULT_KN_FP_MASK_COUNT - 1) - i = -1; - - else if (!def_kn_fp_mask[i].fNotUseOverlay && i < DEFAULT_KN_FP_MASK_COUNT) { - for (j = 0; j < DEFAULT_KN_FP_OVERLAYS_COUNT; j++) { - KN_FP_MASK& p = def_kn_fp_overlays_mask[j]; - if (p.hIcolibItem == nullptr) - continue; - - if (!WildCompare(szMirVer, p.szMaskUpper)) - continue; - - struct _stat64i32 stFileInfo; - if (_wstat(g_szSkinLib, &stFileInfo) != -1) - break; - } - - for (k = 0; k < DEFAULT_KN_FP_OVERLAYS2_COUNT; k++) { - KN_FP_MASK& p = def_kn_fp_overlays2_mask[k]; - if (p.hIcolibItem == nullptr) - continue; - - if (WildCompare(szMirVer, p.szMaskUpper)) - break; - } - - for (n = 0; n < DEFAULT_KN_FP_OVERLAYS3_COUNT; n++) { - KN_FP_MASK& p = def_kn_fp_overlays3_mask[n]; - if (p.hIcolibItem == nullptr) - continue; - - if (WildCompare(szMirVer, p.szMaskUpper)) - break; - } - - for (m = 0; m < DEFAULT_KN_FP_OVERLAYS4_COUNT; m++) { - KN_FP_MASK& p = def_kn_fp_overlays4_mask[m]; - if (p.hIcolibItem == nullptr) - continue; - - if (WildCompare(szMirVer, p.szMaskUpper)) - break; - } - } - - *base = (i < DEFAULT_KN_FP_MASK_COUNT) ? i : -1; - *overlay = (j < DEFAULT_KN_FP_OVERLAYS_COUNT) ? j : -1; - *overlay2 = (k < DEFAULT_KN_FP_OVERLAYS2_COUNT) ? k : -1; - *overlay3 = (n < DEFAULT_KN_FP_OVERLAYS3_COUNT) ? n : -1; - *overlay4 = (m < DEFAULT_KN_FP_OVERLAYS4_COUNT) ? m : -1; -} - -/* GetIconsIndexes -* Retrieves Icons indexes by Mirver -*/ - -void __fastcall GetIconsIndexes(LPWSTR wszMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4) -{ - if (mir_wstrcmp(wszMirVer, L"?") == 0) { - *base = UNKNOWN_MASK_NUMBER; - *overlay = -1; - *overlay2 = -1; - *overlay3 = -1; - *overlay4 = -1; - return; - } - - LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer); - _wcsupr(wszMirVerUp); - MatchMasks(wszMirVerUp, base, overlay, overlay2, overlay3, overlay4); -} - -/* -* CreateIconFromIndexes -* returns hIcon of joined icon by given indexes -*/ - -HICON __fastcall CreateIconFromIndexes(int base, int overlay, int overlay2, int overlay3, int overlay4) -{ - HICON hIcon = nullptr; // returned HICON - HICON hTmp = nullptr; - HICON icMain = nullptr; - HICON icOverlay = nullptr; - HICON icOverlay2 = nullptr; - HICON icOverlay3 = nullptr; - HICON icOverlay4 = nullptr; - - KN_FP_MASK* mainMask = &(def_kn_fp_mask[base]); - icMain = IcoLib_GetIconByHandle(mainMask->hIcolibItem); - - if (icMain) { - KN_FP_MASK* overlayMask = (overlay != -1) ? &(def_kn_fp_overlays_mask[overlay]) : nullptr; - KN_FP_MASK* overlay2Mask = (overlay2 != -1) ? &(def_kn_fp_overlays2_mask[overlay2]) : nullptr; - KN_FP_MASK* overlay3Mask = (overlay3 != -1) ? &(def_kn_fp_overlays3_mask[overlay3]) : nullptr; - KN_FP_MASK* overlay4Mask = (overlay4 != -1) ? &(def_kn_fp_overlays4_mask[overlay4]) : nullptr; - icOverlay = (overlayMask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlayMask->hIcolibItem); - icOverlay2 = (overlay2Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay2Mask->hIcolibItem); - icOverlay3 = (overlay3Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay3Mask->hIcolibItem); - icOverlay4 = (overlay4Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay4Mask->hIcolibItem); - - hIcon = icMain; - - if (overlayMask) - hTmp = hIcon = CreateJoinedIcon(hIcon, icOverlay); - - if (overlay2Mask) { - hIcon = CreateJoinedIcon(hIcon, icOverlay2); - if (hTmp) DestroyIcon(hTmp); - hTmp = hIcon; - } - - if (overlay3Mask) { - hIcon = CreateJoinedIcon(hIcon, icOverlay3); - if (hTmp) DestroyIcon(hTmp); - hTmp = hIcon; - } - - if (overlay4Mask) { - hIcon = CreateJoinedIcon(hIcon, icOverlay4); - if (hTmp) DestroyIcon(hTmp); - } - } - - if (hIcon == icMain) - hIcon = CopyIcon(icMain); - - IcoLib_ReleaseIcon(icMain); - IcoLib_ReleaseIcon(icOverlay); - IcoLib_ReleaseIcon(icOverlay2); - IcoLib_ReleaseIcon(icOverlay3); - IcoLib_ReleaseIcon(icOverlay4); - return hIcon; -} - -/****************************************************************************** - * Futher routines is for creating joined 'overlay' icons. - ******************************************************************************/ - - /* - * CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy - */ - -HBITMAP __inline CreateBitmap32(int cx, int cy) -{ - return CreateBitmap32Point(cx, cy, nullptr); -} - -/* -* CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy and put reference -* to new bitmap pixel image memory area to void ** bits -*/ -HBITMAP __fastcall CreateBitmap32Point(int cx, int cy, LPVOID* bits) -{ - LPVOID ptPixels = nullptr; - - if (cx < 0 || cy < 0) return nullptr; - - BITMAPINFO bmpi = { 0 }; - bmpi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmpi.bmiHeader.biWidth = cx; - bmpi.bmiHeader.biHeight = cy; - bmpi.bmiHeader.biPlanes = 1; - bmpi.bmiHeader.biBitCount = 32; - HBITMAP DirectBitmap = CreateDIBSection(nullptr, &bmpi, DIB_RGB_COLORS, &ptPixels, nullptr, 0); - - GdiFlush(); - if (ptPixels) memset(ptPixels, 0, cx * cy * 4); - if (bits != nullptr) *bits = ptPixels; - - return DirectBitmap; -} - -/* -* checkHasAlfa - checks if image has at least one uint8_t in alpha channel -* that is not a 0. (is image real 32 bit or just 24 bit) -*/ -BOOL __fastcall checkHasAlfa(LPBYTE from, int width, int height) -{ - LPDWORD pt = (LPDWORD)from; - LPDWORD lim = pt + width * height; - while (pt < lim) - { - if (*pt & 0xFF000000) - return TRUE; - pt++; - } - - return FALSE; -} - -/* -* checkMaskUsed - checks if mask image has at least one that is not a 0. -* Not sure is it required or not -*/ -BOOL __fastcall checkMaskUsed(LPBYTE from) -{ - int i; - for (i = 0; i < 16 * 16 / 8; i++) - { - if (from[i] != 0) return TRUE; - } - return FALSE; -} - -/* -* GetMaskBit - return value of apropriate mask bit in line at x position -*/ -BOOL __inline GetMaskBit(LPBYTE line, int x) -{ - return ((*(line + (x >> 3))) & (0x01 << (7 - (x & 0x07)))) != 0; -} - -/* -* blend - alpha blend ARGB values of 2 pixels. X1 - underlaying, -* X2 - overlaying points. -*/ -uint32_t __fastcall blend(uint32_t X1, uint32_t X2) -{ - RGBA* q1 = (RGBA*)&X1; - RGBA* q2 = (RGBA*)&X2; - uint8_t a_1 = ~q1->a; - uint8_t a_2 = ~q2->a; - uint16_t am = q1->a * a_2; - - uint16_t ar = q1->a + ((a_1 * q2->a) / 255); - // if a2 more than 0 than result should be more - // or equal (if a1==0) to a2, else in combination - // with mask we can get here black points - - ar = (q2->a > ar) ? q2->a : ar; - - if (ar == 0) return 0; - - { - uint16_t arm = ar * 255; - uint16_t rr = ((q1->r * am + q2->r * q2->a * 255)) / arm; - uint16_t gr = ((q1->g * am + q2->g * q2->a * 255)) / arm; - uint16_t br = ((q1->b * am + q2->b * q2->a * 255)) / arm; - return (ar << 24) | (rr << 16) | (gr << 8) | br; - } -} - -/* -* CreateJoinedIcon - creates new icon by drawing hTop over hBottom. -*/ -HICON __fastcall CreateJoinedIcon(HICON hBottom, HICON hTop) -{ - BOOL drawn = FALSE; - HDC tempDC, tempDC2, tempDC3; - HICON res = nullptr; - HBITMAP oImage, nImage; - HBITMAP nMask, hbm, obmp, obmp2; - LPBYTE ptPixels = nullptr; - ICONINFO iNew = { 0 }; - uint8_t p[32] = { 0 }; - - tempDC = CreateCompatibleDC(nullptr); - nImage = CreateBitmap32Point(16, 16, (LPVOID*)&ptPixels); - oImage = (HBITMAP)SelectObject(tempDC, nImage); - - ICONINFO iciBottom = { 0 }; - ICONINFO iciTop = { 0 }; - - BITMAP bmp_top = { 0 }; - BITMAP bmp_top_mask = { 0 }; - - BITMAP bmp_bottom = { 0 }; - BITMAP bmp_bottom_mask = { 0 }; - - GetIconInfo(hBottom, &iciBottom); - GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom); - GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask); - - GetIconInfo(hTop, &iciTop); - GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top); - GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask); - - if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32) - { - LPBYTE BottomBuffer, TopBuffer, BottomMaskBuffer, TopMaskBuffer; - LPBYTE bb, tb, bmb, tmb; - LPBYTE db = ptPixels; - int vstep_d = 16 * 4; - int vstep_b = bmp_bottom.bmWidthBytes; - int vstep_t = bmp_top.bmWidthBytes; - int vstep_bm = bmp_bottom_mask.bmWidthBytes; - int vstep_tm = bmp_top_mask.bmWidthBytes; - - if (bmp_bottom.bmBits) - bb = BottomBuffer = (LPBYTE)bmp_bottom.bmBits; - else - { - BottomBuffer = (LPBYTE)_alloca(bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes); - GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes, BottomBuffer); - bb = BottomBuffer + vstep_b * (bmp_bottom.bmHeight - 1); - vstep_b = -vstep_b; - } - if (bmp_top.bmBits) - tb = TopBuffer = (LPBYTE)bmp_top.bmBits; - else - { - TopBuffer = (LPBYTE)_alloca(bmp_top.bmHeight * bmp_top.bmWidthBytes); - GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight * bmp_top.bmWidthBytes, TopBuffer); - tb = TopBuffer + vstep_t * (bmp_top.bmHeight - 1); - vstep_t = -vstep_t; - } - if (bmp_bottom_mask.bmBits) - bmb = BottomMaskBuffer = (LPBYTE)bmp_bottom_mask.bmBits; - else - { - BottomMaskBuffer = (LPBYTE)_alloca(bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes); - GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer); - bmb = BottomMaskBuffer + vstep_bm * (bmp_bottom_mask.bmHeight - 1); - vstep_bm = -vstep_bm; - } - if (bmp_top_mask.bmBits) - tmb = TopMaskBuffer = (LPBYTE)bmp_top_mask.bmBits; - else - { - TopMaskBuffer = (LPBYTE)_alloca(bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes); - GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes, TopMaskBuffer); - tmb = TopMaskBuffer + vstep_tm * (bmp_top_mask.bmHeight - 1); - vstep_tm = -vstep_tm; - } - { - int x; int y; - BOOL topHasAlpha = checkHasAlfa(TopBuffer, bmp_top.bmWidth, bmp_top.bmHeight); - BOOL bottomHasAlpha = checkHasAlfa(BottomBuffer, bmp_bottom.bmWidth, bmp_bottom.bmHeight); - BOOL topMaskUsed = !topHasAlpha && checkMaskUsed(TopMaskBuffer); - BOOL bottomMaskUsed = !bottomHasAlpha && checkMaskUsed(BottomMaskBuffer); - - for (y = 0; y < 16; y++) - { - for (x = 0; x < 16; x++) - { - uint32_t bottom_d = ((LPDWORD)bb)[x]; - uint32_t top_d = ((LPDWORD)tb)[x]; - - if (topMaskUsed) - { - if (GetMaskBit(tmb, x)) - top_d &= 0x00FFFFFF; - else - top_d |= 0xFF000000; - } - else if (!topHasAlpha) - top_d |= 0xFF000000; - - if (bottomMaskUsed) - { - if (GetMaskBit(bmb, x)) - bottom_d &= 0x00FFFFFF; - else - bottom_d |= 0xFF000000; - } - else if (!bottomHasAlpha) - bottom_d |= 0xFF000000; - - ((LPDWORD)db)[x] = blend(bottom_d, top_d); - } - bb += vstep_b; - tb += vstep_t; - bmb += vstep_bm; - tmb += vstep_tm; - db += vstep_d; - } - } - - drawn = TRUE; - } - - DeleteObject(iciBottom.hbmColor); - DeleteObject(iciTop.hbmColor); - DeleteObject(iciBottom.hbmMask); - DeleteObject(iciTop.hbmMask); - - if (!drawn) { - DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL); - DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL); - } - - nMask = CreateBitmap(16, 16, 1, 1, p); - tempDC2 = CreateCompatibleDC(nullptr); - tempDC3 = CreateCompatibleDC(nullptr); - hbm = CreateCompatibleBitmap(tempDC3, 16, 16); - obmp = (HBITMAP)SelectObject(tempDC2, nMask); - obmp2 = (HBITMAP)SelectObject(tempDC3, hbm); - DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK); - DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK); - BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND); - - GdiFlush(); - - SelectObject(tempDC2, obmp); - DeleteDC(tempDC2); - - SelectObject(tempDC3, obmp2); - DeleteDC(tempDC3); - - SelectObject(tempDC, oImage); - DeleteDC(tempDC); - - DeleteObject(hbm); - - iNew.fIcon = TRUE; - iNew.hbmColor = nImage; - iNew.hbmMask = nMask; - res = CreateIconIndirect(&iNew); - - DeleteObject(nImage); - DeleteObject(nMask); - - return res; -} - -HANDLE __fastcall GetIconIndexFromFI(LPTSTR szMirVer) -{ - int base, overlay, overlay2, overlay3, overlay4; - GetIconsIndexes(szMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4); - if (base == -1) - return INVALID_HANDLE_VALUE; - - // MAX: 256 + 64 + 64 + 64 + 64 - FOUNDINFO tmp = { base, ((overlay & 0xFF) << 18) | ((overlay2 & 0x3F) << 12) | ((overlay3 & 0x3F) << 6) | (overlay4 & 0x3F) }; - auto *F = arFI.find(&tmp); - if (F != nullptr) - return F->hRegisteredImage; - - // not found - then add - F = new FOUNDINFO(tmp); - HICON hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4); - if (hIcon != nullptr) { - F->hRegisteredImage = ExtraIcon_AddIcon(hIcon); - DestroyIcon(hIcon); - } - else F->hRegisteredImage = INVALID_HANDLE_VALUE; - - arFI.insert(F); - - return F->hRegisteredImage; -} - -VOID ClearFI() -{ - arFI.destroy(); -} - -/**************************************************************************************** -* ServiceGetClientIconW -* MS_FP_GETCLIENTICONW service implementation. -* wParam - LPWSTR MirVer value to get client for. -* lParam - int noCopy - if wParam is equal to "1" will return icon handler without copiing icon. -* ICON IS ALWAYS COPIED!!! -*/ - -static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM) -{ - LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for. - if (wszMirVer == nullptr) - return 0; - - int base, overlay, overlay2, overlay3, overlay4; - GetIconsIndexes(wszMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4); - - HICON hIcon = nullptr; // returned HICON - if (base != -1) - hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4); - - return (INT_PTR)hIcon; -} - -/**************************************************************************************** - * ServiceGetClientDescrW - * MS_FP_GETCLIENTDESCRW service implementation. - * wParam - LPCWSTR MirVer value - * lParam - (NULL) unused - * returns LPCWSTR: client description (do not destroy) or NULL - */ - -static INT_PTR ServiceGetClientDescrW(WPARAM wParam, LPARAM) -{ - LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for. - if (wszMirVer == nullptr) - return 0; - - LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer); _wcsupr(wszMirVerUp); - if (mir_wstrcmp(wszMirVerUp, L"?") == 0) - return (INT_PTR)def_kn_fp_mask[UNKNOWN_MASK_NUMBER].szClientDescription; - - for (int index = 0; index < DEFAULT_KN_FP_MASK_COUNT; index++) - if (WildCompare(wszMirVerUp, def_kn_fp_mask[index].szMaskUpper)) - return (INT_PTR)def_kn_fp_mask[index].szClientDescription; - - return NULL; -} - -/**************************************************************************************** - * ServiceSameClientW - * MS_FP_SAMECLIENTSW service implementation. - * wParam - LPWSTR first MirVer value - * lParam - LPWSTR second MirVer value - * returns LPCWSTR: client description (do not destroy) if clients are same or NULL - */ - -static INT_PTR ServiceSameClientsW(WPARAM wParam, LPARAM lParam) -{ - if (!wParam || !lParam) - return NULL; //one of its is not null - - INT_PTR res1 = ServiceGetClientDescrW(wParam, 0); - INT_PTR res2 = ServiceGetClientDescrW(lParam, 0); - return (res1 == res2 && res1 != 0) ? res1 : NULL; -} - -/**************************************************************************************** -* OnExtraIconListRebuild -* Set all registered indexes in array to EMPTY_EXTRA_ICON (unregistered icon) -*/ - -static int OnExtraIconListRebuild(WPARAM, LPARAM) -{ - ClearFI(); - return 0; -} - -/**************************************************************************************** -* OnIconsChanged -*/ - -static int OnIconsChanged(WPARAM, LPARAM) -{ - ClearFI(); - return 0; -} - -/**************************************************************************************** -* OnExtraImageApply -* Try to get MirVer value from db for contact and if success calls ApplyFingerprintImage -*/ - -int OnExtraImageApply(WPARAM hContact, LPARAM) -{ - if (hContact == NULL) - return 0; - - ptrW tszMirver; - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto != nullptr) - tszMirver = db_get_wsa(hContact, szProto, "MirVer"); - - ApplyFingerprintImage(hContact, tszMirver); - return 0; -} - -/**************************************************************************************** -* OnContactSettingChanged -* if contact settings changed apply new image or remove it -*/ - -static int OnContactSettingChanged(WPARAM hContact, LPARAM lParam) -{ - if (hContact == NULL) - return 0; - - DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam; - if (cws && cws->szSetting && !strcmp(cws->szSetting, "MirVer")) { - switch (cws->value.type) { - case DBVT_UTF8: - ApplyFingerprintImage(hContact, ptrW(mir_utf8decodeW(cws->value.pszVal))); - break; - case DBVT_ASCIIZ: - ApplyFingerprintImage(hContact, _A2T(cws->value.pszVal)); - break; - case DBVT_WCHAR: - ApplyFingerprintImage(hContact, cws->value.pwszVal); - break; - default: - ApplyFingerprintImage(hContact, nullptr); - } - } - return 0; -} - -/**************************************************************************************** -* OnSrmmWindowEvent -* Monitors SRMM window's creation to draw a statusbar icon -*/ - -static int OnSrmmWindowEvent(WPARAM, LPARAM lParam) -{ - if (!g_plugin.getByte("StatusBarIcon", 1)) - return 0; - - MessageWindowEventData *event = (MessageWindowEventData *)lParam; - if (event == nullptr) - return 0; - - if (event->uType == MSG_WINDOW_EVT_OPEN) { - ptrW ptszMirVer; - char *szProto = Proto_GetBaseAccountName(event->hContact); - if (szProto != nullptr) - ptszMirVer = db_get_wsa(event->hContact, szProto, "MirVer"); - SetSrmmIcon(event->hContact, ptszMirVer); - arMonitoredWindows.insert((HANDLE)event->hContact); - } - else if (event->uType == MSG_WINDOW_EVT_CLOSE) - arMonitoredWindows.remove((HANDLE)event->hContact); - - return 0; -} - -/**************************************************************************************** -* OnModulesLoaded -* Hook necessary events here -*/ - -int OnModulesLoaded(WPARAM, LPARAM) -{ - g_LPCodePage = Langpack_GetDefaultCodePage(); - - //Hook necessary events - HookEvent(ME_SKIN_ICONSCHANGED, OnIconsChanged); - HookEvent(ME_MSG_WINDOWEVENT, OnSrmmWindowEvent); - - HookEvent(ME_MC_DEFAULTTCHANGED, OnExtraImageApply); - - PathToAbsoluteW(DEFAULT_SKIN_FOLDER, g_szSkinLib); - - RegisterIcons(); - - hExtraIcon = ExtraIcon_RegisterCallback("Client", LPGEN("Fingerprint"), "client_Miranda_unknown", - OnExtraIconListRebuild, OnExtraImageApply, OnExtraIconClick); - - if (g_plugin.getByte("StatusBarIcon", 1)) { - StatusIconData sid = {}; - sid.szModule = MODULENAME; - sid.flags = MBF_HIDDEN; - sid.dwId = 1; - Srmm_AddIcon(&sid, &g_plugin); - } - - return 0; -} - -void InitFingerModule() -{ - HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); - HookEvent(ME_OPT_INITIALISE, OnOptInitialise); - HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged); - - CreateServiceFunction(MS_FP_SAMECLIENTSW, ServiceSameClientsW); - CreateServiceFunction(MS_FP_GETCLIENTDESCRW, ServiceGetClientDescrW); - CreateServiceFunction(MS_FP_GETCLIENTICONW, ServiceGetClientIconW); -} +fv/*
+Fingerprint NG (client version) icons module for Miranda NG
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//Start of header
+#include "stdafx.h"
+
+static UINT g_LPCodePage;
+static wchar_t g_szSkinLib[MAX_PATH];
+static HANDLE hExtraIcon = nullptr;
+static HANDLE hFolderChanged = nullptr, hIconFolder = nullptr;
+
+static int CompareFI(const FOUNDINFO *p1, const FOUNDINFO *p2)
+{
+ if (p1->iBase != p2->iBase)
+ return p1->iBase - p2->iBase;
+ return p1->iOverlay - p2->iOverlay;
+}
+
+static OBJLIST<FOUNDINFO> arFI(50, CompareFI);
+
+static LIST<void> arMonitoredWindows(3, PtrKeySortT);
+
+static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM lParam);
+
+/*
+* Prepare
+* prepares upperstring masks and registers them in IcoLib
+*/
+
+static wchar_t* getSectionName(int flag)
+{
+ switch (flag)
+ {
+#include "finger_groups.h"
+ }
+ return nullptr;
+}
+
+void __fastcall Prepare(KN_FP_MASK* mask, bool bEnable)
+{
+ mask->szMaskUpper = nullptr;
+
+ if (mask->hIcolibItem)
+ IcoLib_RemoveIcon(mask->szIconName);
+ mask->hIcolibItem = nullptr;
+
+ if (!mask->szMask || !bEnable)
+ return;
+
+ size_t iMaskLen = mir_wstrlen(mask->szMask) + 1;
+ LPTSTR pszNewMask = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, iMaskLen * sizeof(wchar_t));
+ wcscpy_s(pszNewMask, iMaskLen, mask->szMask);
+ _wcsupr_s(pszNewMask, iMaskLen);
+ mask->szMaskUpper = pszNewMask;
+
+ wchar_t destfile[MAX_PATH];
+ if (mask->iIconIndex == IDI_NOTFOUND || mask->iIconIndex == IDI_UNKNOWN || mask->iIconIndex == IDI_UNDETECTED)
+ GetModuleFileName(g_plugin.getInst(), destfile, MAX_PATH);
+ else {
+ wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE);
+
+ struct _stat64i32 stFileInfo;
+ if (_wstat(destfile, &stFileInfo) == -1)
+ return;
+ }
+
+ LPTSTR SectName = getSectionName(mask->iSectionFlag);
+ if (SectName == nullptr)
+ return;
+
+ SKINICONDESC sid = {};
+ sid.flags = SIDF_ALL_UNICODE;
+ sid.section.w = SectName;
+ sid.pszName = mask->szIconName;
+ sid.description.w = mask->szClientDescription;
+ sid.defaultFile.w = destfile;
+ sid.iDefaultIndex = -mask->iIconIndex;
+ mask->hIcolibItem = g_plugin.addIcon(&sid);
+}
+
+/*
+* Register icons
+*/
+
+void RegisterIcons()
+{
+ // prepare masks
+ int i;
+
+ if (hHeap)
+ HeapDestroy(hHeap);
+ hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
+
+ for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++)
+ Prepare(&def_kn_fp_mask[i], true);
+
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++)
+ Prepare(&def_kn_fp_overlays_mask[i], true);
+
+ if (g_plugin.getByte("GroupMirandaVersion", 0)) {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
+ Prepare(&def_kn_fp_overlays2_mask[i], true);
+ }
+ else {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++)
+ Prepare(&def_kn_fp_overlays2_mask[i], true);
+ for (; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
+ Prepare(&def_kn_fp_overlays2_mask[i], false);
+ }
+
+ if (g_plugin.getByte("GroupOverlaysUnicode", 1)) {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
+ Prepare(&def_kn_fp_overlays3_mask[i], true);
+ }
+ else {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_NO_UNICODE_COUNT; i++)
+ Prepare(&def_kn_fp_overlays3_mask[i], true);
+ for (; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
+ Prepare(&def_kn_fp_overlays3_mask[i], false);
+ }
+
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS4_COUNT; i++)
+ Prepare(&def_kn_fp_overlays4_mask[i], true);
+}
+
+/* ApplyFingerprintImage
+* 1)Try to find appropriate mask
+* 2)Register icon in extraimage list if not yet registered (EMPTY_EXTRA_ICON)
+* 3)Set ExtraImage for contact
+*/
+
+static void SetSrmmIcon(MCONTACT hContact, LPTSTR ptszMirver)
+{
+ if (mir_wstrlen(ptszMirver))
+ Srmm_ModifyIcon(hContact, MODULENAME, 1, (HICON)ServiceGetClientIconW((WPARAM)ptszMirver, TRUE), ptszMirver);
+ else
+ Srmm_SetIconFlags(hContact, MODULENAME, 1, MBF_HIDDEN);
+}
+
+int __fastcall ApplyFingerprintImage(MCONTACT hContact, LPTSTR szMirVer)
+{
+ if (hContact == NULL)
+ return 0;
+
+ HANDLE hImage = INVALID_HANDLE_VALUE;
+ if (szMirVer)
+ hImage = GetIconIndexFromFI(szMirVer);
+
+ ExtraIcon_SetIcon(hExtraIcon, hContact, hImage);
+
+ if (arMonitoredWindows.getIndex((HANDLE)hContact) != -1)
+ SetSrmmIcon(hContact, szMirVer);
+
+ return 0;
+}
+
+int OnExtraIconClick(WPARAM wParam, LPARAM, LPARAM)
+{
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, NULL);
+ return 0;
+}
+
+
+/*
+* WildCompare
+* Compare 'name' string with 'mask' strings.
+* Masks can contain '*' or '?' wild symbols
+* Asterics '*' symbol covers 'empty' symbol too e.g WildCompare("Tst","T*st*"), returns TRUE
+* In order to handle situation 'at least one any sybol' use "?*" combination:
+* e.g WildCompare("Tst","T?*st*"), returns FALSE, but both WildCompare("Test","T?*st*") and
+* WildCompare("Teeest","T?*st*") return TRUE.
+*
+* Function is case sensitive! so convert input or modify func to use _qtoupper()
+*
+* Mask can contain several submasks. In this case each submask (including first)
+* should start from '|' e.g: "|first*submask|second*mask".
+*
+* Dec 25, 2006 by FYR:
+* Added Exception to masks: the mask "|^mask3|mask2|mask1" means:
+* if NOT according to mask 3 AND (mask1 OR mask2)
+* EXCEPTION should be BEFORE main mask:
+* IF Exception match - the comparing stops as FALSE
+* IF Exception does not match - comparing continue
+* IF Mask match - comparing stops as TRUE
+* IF Mask does not not match comparing continue
+*/
+BOOL __fastcall WildCompare(LPWSTR wszName, LPWSTR wszMask)
+{
+ if (wszMask == nullptr)
+ return NULL;
+
+ if (*wszMask != L'|')
+ return wildcmpw(wszName, wszMask);
+
+ size_t s = 1, e = 1;
+ LPWSTR wszTemp = (LPWSTR)_alloca(mir_wstrlen(wszMask) * sizeof(wchar_t) + sizeof(wchar_t));
+ BOOL bExcept;
+
+ while (wszMask[e] != L'\0')
+ {
+ s = e;
+ while (wszMask[e] != L'\0' && wszMask[e] != L'|') e++;
+
+ // exception mask
+ bExcept = (*(wszMask + s) == L'^');
+ if (bExcept) s++;
+
+ memcpy(wszTemp, wszMask + s, (e - s) * sizeof(wchar_t));
+ wszTemp[e - s] = L'\0';
+
+ if (wildcmpw(wszName, wszTemp))
+ return !bExcept;
+
+ if (wszMask[e] != L'\0')
+ e++;
+ else
+ return FALSE;
+ }
+ return FALSE;
+}
+
+static void MatchMasks(wchar_t* szMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4)
+{
+ int i = 0, j = -1, k = -1, n = -1, m = -1;
+
+ for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++) {
+ KN_FP_MASK& p = def_kn_fp_mask[i];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (!WildCompare(szMirVer, p.szMaskUpper))
+ continue;
+
+ if (p.iIconIndex != IDI_NOTFOUND && p.iIconIndex != IDI_UNKNOWN && p.iIconIndex != IDI_UNDETECTED) {
+ wchar_t destfile[MAX_PATH];
+ wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE);
+
+ struct _stat64i32 stFileInfo;
+ if (_wstat(destfile, &stFileInfo) == -1)
+ i = NOTFOUND_MASK_NUMBER;
+ }
+ break;
+ }
+ if (i == DEFAULT_KN_FP_MASK_COUNT - 1)
+ i = -1;
+
+ else if (!def_kn_fp_mask[i].fNotUseOverlay && i < DEFAULT_KN_FP_MASK_COUNT) {
+ for (j = 0; j < DEFAULT_KN_FP_OVERLAYS_COUNT; j++) {
+ KN_FP_MASK& p = def_kn_fp_overlays_mask[j];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (!WildCompare(szMirVer, p.szMaskUpper))
+ continue;
+
+ struct _stat64i32 stFileInfo;
+ if (_wstat(g_szSkinLib, &stFileInfo) != -1)
+ break;
+ }
+
+ for (k = 0; k < DEFAULT_KN_FP_OVERLAYS2_COUNT; k++) {
+ KN_FP_MASK& p = def_kn_fp_overlays2_mask[k];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (WildCompare(szMirVer, p.szMaskUpper))
+ break;
+ }
+
+ for (n = 0; n < DEFAULT_KN_FP_OVERLAYS3_COUNT; n++) {
+ KN_FP_MASK& p = def_kn_fp_overlays3_mask[n];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (WildCompare(szMirVer, p.szMaskUpper))
+ break;
+ }
+
+ for (m = 0; m < DEFAULT_KN_FP_OVERLAYS4_COUNT; m++) {
+ KN_FP_MASK& p = def_kn_fp_overlays4_mask[m];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (WildCompare(szMirVer, p.szMaskUpper))
+ break;
+ }
+ }
+
+ *base = (i < DEFAULT_KN_FP_MASK_COUNT) ? i : -1;
+ *overlay = (j < DEFAULT_KN_FP_OVERLAYS_COUNT) ? j : -1;
+ *overlay2 = (k < DEFAULT_KN_FP_OVERLAYS2_COUNT) ? k : -1;
+ *overlay3 = (n < DEFAULT_KN_FP_OVERLAYS3_COUNT) ? n : -1;
+ *overlay4 = (m < DEFAULT_KN_FP_OVERLAYS4_COUNT) ? m : -1;
+}
+
+/* GetIconsIndexes
+* Retrieves Icons indexes by Mirver
+*/
+
+void __fastcall GetIconsIndexes(LPWSTR wszMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4)
+{
+ if (mir_wstrcmp(wszMirVer, L"?") == 0) {
+ *base = UNKNOWN_MASK_NUMBER;
+ *overlay = -1;
+ *overlay2 = -1;
+ *overlay3 = -1;
+ *overlay4 = -1;
+ return;
+ }
+
+ LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer);
+ _wcsupr(wszMirVerUp);
+ MatchMasks(wszMirVerUp, base, overlay, overlay2, overlay3, overlay4);
+}
+
+/*
+* CreateIconFromIndexes
+* returns hIcon of joined icon by given indexes
+*/
+
+HICON __fastcall CreateIconFromIndexes(int base, int overlay, int overlay2, int overlay3, int overlay4)
+{
+ HICON hIcon = nullptr; // returned HICON
+ HICON hTmp = nullptr;
+ HICON icMain = nullptr;
+ HICON icOverlay = nullptr;
+ HICON icOverlay2 = nullptr;
+ HICON icOverlay3 = nullptr;
+ HICON icOverlay4 = nullptr;
+
+ KN_FP_MASK* mainMask = &(def_kn_fp_mask[base]);
+ icMain = IcoLib_GetIconByHandle(mainMask->hIcolibItem);
+
+ if (icMain) {
+ KN_FP_MASK* overlayMask = (overlay != -1) ? &(def_kn_fp_overlays_mask[overlay]) : nullptr;
+ KN_FP_MASK* overlay2Mask = (overlay2 != -1) ? &(def_kn_fp_overlays2_mask[overlay2]) : nullptr;
+ KN_FP_MASK* overlay3Mask = (overlay3 != -1) ? &(def_kn_fp_overlays3_mask[overlay3]) : nullptr;
+ KN_FP_MASK* overlay4Mask = (overlay4 != -1) ? &(def_kn_fp_overlays4_mask[overlay4]) : nullptr;
+ icOverlay = (overlayMask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlayMask->hIcolibItem);
+ icOverlay2 = (overlay2Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay2Mask->hIcolibItem);
+ icOverlay3 = (overlay3Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay3Mask->hIcolibItem);
+ icOverlay4 = (overlay4Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay4Mask->hIcolibItem);
+
+ hIcon = icMain;
+
+ if (overlayMask)
+ hTmp = hIcon = CreateJoinedIcon(hIcon, icOverlay);
+
+ if (overlay2Mask) {
+ hIcon = CreateJoinedIcon(hIcon, icOverlay2);
+ if (hTmp) DestroyIcon(hTmp);
+ hTmp = hIcon;
+ }
+
+ if (overlay3Mask) {
+ hIcon = CreateJoinedIcon(hIcon, icOverlay3);
+ if (hTmp) DestroyIcon(hTmp);
+ hTmp = hIcon;
+ }
+
+ if (overlay4Mask) {
+ hIcon = CreateJoinedIcon(hIcon, icOverlay4);
+ if (hTmp) DestroyIcon(hTmp);
+ }
+ }
+
+ if (hIcon == icMain)
+ hIcon = CopyIcon(icMain);
+
+ IcoLib_ReleaseIcon(icMain);
+ IcoLib_ReleaseIcon(icOverlay);
+ IcoLib_ReleaseIcon(icOverlay2);
+ IcoLib_ReleaseIcon(icOverlay3);
+ IcoLib_ReleaseIcon(icOverlay4);
+ return hIcon;
+}
+
+/******************************************************************************
+ * Futher routines is for creating joined 'overlay' icons.
+ ******************************************************************************/
+
+ /*
+ * CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy
+ */
+
+HBITMAP __inline CreateBitmap32(int cx, int cy)
+{
+ return CreateBitmap32Point(cx, cy, nullptr);
+}
+
+/*
+* CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy and put reference
+* to new bitmap pixel image memory area to void ** bits
+*/
+HBITMAP __fastcall CreateBitmap32Point(int cx, int cy, LPVOID* bits)
+{
+ LPVOID ptPixels = nullptr;
+
+ if (cx < 0 || cy < 0) return nullptr;
+
+ BITMAPINFO bmpi = { 0 };
+ bmpi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmpi.bmiHeader.biWidth = cx;
+ bmpi.bmiHeader.biHeight = cy;
+ bmpi.bmiHeader.biPlanes = 1;
+ bmpi.bmiHeader.biBitCount = 32;
+ HBITMAP DirectBitmap = CreateDIBSection(nullptr, &bmpi, DIB_RGB_COLORS, &ptPixels, nullptr, 0);
+
+ GdiFlush();
+ if (ptPixels) memset(ptPixels, 0, cx * cy * 4);
+ if (bits != nullptr) *bits = ptPixels;
+
+ return DirectBitmap;
+}
+
+/*
+* checkHasAlfa - checks if image has at least one uint8_t in alpha channel
+* that is not a 0. (is image real 32 bit or just 24 bit)
+*/
+BOOL __fastcall checkHasAlfa(LPBYTE from, int width, int height)
+{
+ LPDWORD pt = (LPDWORD)from;
+ LPDWORD lim = pt + width * height;
+ while (pt < lim)
+ {
+ if (*pt & 0xFF000000)
+ return TRUE;
+ pt++;
+ }
+
+ return FALSE;
+}
+
+/*
+* checkMaskUsed - checks if mask image has at least one that is not a 0.
+* Not sure is it required or not
+*/
+BOOL __fastcall checkMaskUsed(LPBYTE from)
+{
+ int i;
+ for (i = 0; i < 16 * 16 / 8; i++)
+ {
+ if (from[i] != 0) return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+* GetMaskBit - return value of apropriate mask bit in line at x position
+*/
+BOOL __inline GetMaskBit(LPBYTE line, int x)
+{
+ return ((*(line + (x >> 3))) & (0x01 << (7 - (x & 0x07)))) != 0;
+}
+
+/*
+* blend - alpha blend ARGB values of 2 pixels. X1 - underlaying,
+* X2 - overlaying points.
+*/
+uint32_t __fastcall blend(uint32_t X1, uint32_t X2)
+{
+ RGBA* q1 = (RGBA*)&X1;
+ RGBA* q2 = (RGBA*)&X2;
+ uint8_t a_1 = ~q1->a;
+ uint8_t a_2 = ~q2->a;
+ uint16_t am = q1->a * a_2;
+
+ uint16_t ar = q1->a + ((a_1 * q2->a) / 255);
+ // if a2 more than 0 than result should be more
+ // or equal (if a1==0) to a2, else in combination
+ // with mask we can get here black points
+
+ ar = (q2->a > ar) ? q2->a : ar;
+
+ if (ar == 0) return 0;
+
+ {
+ uint16_t arm = ar * 255;
+ uint16_t rr = ((q1->r * am + q2->r * q2->a * 255)) / arm;
+ uint16_t gr = ((q1->g * am + q2->g * q2->a * 255)) / arm;
+ uint16_t br = ((q1->b * am + q2->b * q2->a * 255)) / arm;
+ return (ar << 24) | (rr << 16) | (gr << 8) | br;
+ }
+}
+
+/*
+* CreateJoinedIcon - creates new icon by drawing hTop over hBottom.
+*/
+HICON __fastcall CreateJoinedIcon(HICON hBottom, HICON hTop)
+{
+ BOOL drawn = FALSE;
+ HDC tempDC, tempDC2, tempDC3;
+ HICON res = nullptr;
+ HBITMAP oImage, nImage;
+ HBITMAP nMask, hbm, obmp, obmp2;
+ LPBYTE ptPixels = nullptr;
+ ICONINFO iNew = { 0 };
+ uint8_t p[32] = { 0 };
+
+ tempDC = CreateCompatibleDC(nullptr);
+ nImage = CreateBitmap32Point(16, 16, (LPVOID*)&ptPixels);
+ oImage = (HBITMAP)SelectObject(tempDC, nImage);
+
+ ICONINFO iciBottom = { 0 };
+ ICONINFO iciTop = { 0 };
+
+ BITMAP bmp_top = { 0 };
+ BITMAP bmp_top_mask = { 0 };
+
+ BITMAP bmp_bottom = { 0 };
+ BITMAP bmp_bottom_mask = { 0 };
+
+ GetIconInfo(hBottom, &iciBottom);
+ GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
+ GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);
+
+ GetIconInfo(hTop, &iciTop);
+ GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
+ GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);
+
+ if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32)
+ {
+ LPBYTE BottomBuffer, TopBuffer, BottomMaskBuffer, TopMaskBuffer;
+ LPBYTE bb, tb, bmb, tmb;
+ LPBYTE db = ptPixels;
+ int vstep_d = 16 * 4;
+ int vstep_b = bmp_bottom.bmWidthBytes;
+ int vstep_t = bmp_top.bmWidthBytes;
+ int vstep_bm = bmp_bottom_mask.bmWidthBytes;
+ int vstep_tm = bmp_top_mask.bmWidthBytes;
+
+ if (bmp_bottom.bmBits)
+ bb = BottomBuffer = (LPBYTE)bmp_bottom.bmBits;
+ else
+ {
+ BottomBuffer = (LPBYTE)_alloca(bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes, BottomBuffer);
+ bb = BottomBuffer + vstep_b * (bmp_bottom.bmHeight - 1);
+ vstep_b = -vstep_b;
+ }
+ if (bmp_top.bmBits)
+ tb = TopBuffer = (LPBYTE)bmp_top.bmBits;
+ else
+ {
+ TopBuffer = (LPBYTE)_alloca(bmp_top.bmHeight * bmp_top.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight * bmp_top.bmWidthBytes, TopBuffer);
+ tb = TopBuffer + vstep_t * (bmp_top.bmHeight - 1);
+ vstep_t = -vstep_t;
+ }
+ if (bmp_bottom_mask.bmBits)
+ bmb = BottomMaskBuffer = (LPBYTE)bmp_bottom_mask.bmBits;
+ else
+ {
+ BottomMaskBuffer = (LPBYTE)_alloca(bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
+ bmb = BottomMaskBuffer + vstep_bm * (bmp_bottom_mask.bmHeight - 1);
+ vstep_bm = -vstep_bm;
+ }
+ if (bmp_top_mask.bmBits)
+ tmb = TopMaskBuffer = (LPBYTE)bmp_top_mask.bmBits;
+ else
+ {
+ TopMaskBuffer = (LPBYTE)_alloca(bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes, TopMaskBuffer);
+ tmb = TopMaskBuffer + vstep_tm * (bmp_top_mask.bmHeight - 1);
+ vstep_tm = -vstep_tm;
+ }
+ {
+ int x; int y;
+ BOOL topHasAlpha = checkHasAlfa(TopBuffer, bmp_top.bmWidth, bmp_top.bmHeight);
+ BOOL bottomHasAlpha = checkHasAlfa(BottomBuffer, bmp_bottom.bmWidth, bmp_bottom.bmHeight);
+ BOOL topMaskUsed = !topHasAlpha && checkMaskUsed(TopMaskBuffer);
+ BOOL bottomMaskUsed = !bottomHasAlpha && checkMaskUsed(BottomMaskBuffer);
+
+ for (y = 0; y < 16; y++)
+ {
+ for (x = 0; x < 16; x++)
+ {
+ uint32_t bottom_d = ((LPDWORD)bb)[x];
+ uint32_t top_d = ((LPDWORD)tb)[x];
+
+ if (topMaskUsed)
+ {
+ if (GetMaskBit(tmb, x))
+ top_d &= 0x00FFFFFF;
+ else
+ top_d |= 0xFF000000;
+ }
+ else if (!topHasAlpha)
+ top_d |= 0xFF000000;
+
+ if (bottomMaskUsed)
+ {
+ if (GetMaskBit(bmb, x))
+ bottom_d &= 0x00FFFFFF;
+ else
+ bottom_d |= 0xFF000000;
+ }
+ else if (!bottomHasAlpha)
+ bottom_d |= 0xFF000000;
+
+ ((LPDWORD)db)[x] = blend(bottom_d, top_d);
+ }
+ bb += vstep_b;
+ tb += vstep_t;
+ bmb += vstep_bm;
+ tmb += vstep_tm;
+ db += vstep_d;
+ }
+ }
+
+ drawn = TRUE;
+ }
+
+ DeleteObject(iciBottom.hbmColor);
+ DeleteObject(iciTop.hbmColor);
+ DeleteObject(iciBottom.hbmMask);
+ DeleteObject(iciTop.hbmMask);
+
+ if (!drawn) {
+ DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL);
+ DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL);
+ }
+
+ nMask = CreateBitmap(16, 16, 1, 1, p);
+ tempDC2 = CreateCompatibleDC(nullptr);
+ tempDC3 = CreateCompatibleDC(nullptr);
+ hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
+ obmp = (HBITMAP)SelectObject(tempDC2, nMask);
+ obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
+ DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK);
+ DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK);
+ BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);
+
+ GdiFlush();
+
+ SelectObject(tempDC2, obmp);
+ DeleteDC(tempDC2);
+
+ SelectObject(tempDC3, obmp2);
+ DeleteDC(tempDC3);
+
+ SelectObject(tempDC, oImage);
+ DeleteDC(tempDC);
+
+ DeleteObject(hbm);
+
+ iNew.fIcon = TRUE;
+ iNew.hbmColor = nImage;
+ iNew.hbmMask = nMask;
+ res = CreateIconIndirect(&iNew);
+
+ DeleteObject(nImage);
+ DeleteObject(nMask);
+
+ return res;
+}
+
+HANDLE __fastcall GetIconIndexFromFI(LPTSTR szMirVer)
+{
+ int base, overlay, overlay2, overlay3, overlay4;
+ GetIconsIndexes(szMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4);
+ if (base == -1)
+ return INVALID_HANDLE_VALUE;
+
+ // MAX: 256 + 64 + 64 + 64 + 64
+ FOUNDINFO tmp = { base, ((overlay & 0xFF) << 18) | ((overlay2 & 0x3F) << 12) | ((overlay3 & 0x3F) << 6) | (overlay4 & 0x3F) };
+ auto *F = arFI.find(&tmp);
+ if (F != nullptr)
+ return F->hRegisteredImage;
+
+ // not found - then add
+ F = new FOUNDINFO(tmp);
+ HICON hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4);
+ if (hIcon != nullptr) {
+ F->hRegisteredImage = ExtraIcon_AddIcon(hIcon);
+ DestroyIcon(hIcon);
+ }
+ else F->hRegisteredImage = INVALID_HANDLE_VALUE;
+
+ arFI.insert(F);
+
+ return F->hRegisteredImage;
+}
+
+VOID ClearFI()
+{
+ arFI.destroy();
+}
+
+/****************************************************************************************
+* ServiceGetClientIconW
+* MS_FP_GETCLIENTICONW service implementation.
+* wParam - LPWSTR MirVer value to get client for.
+* lParam - int noCopy - if wParam is equal to "1" will return icon handler without copiing icon.
+* ICON IS ALWAYS COPIED!!!
+*/
+
+static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM)
+{
+ LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for.
+ if (wszMirVer == nullptr)
+ return 0;
+
+ int base, overlay, overlay2, overlay3, overlay4;
+ GetIconsIndexes(wszMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4);
+
+ HICON hIcon = nullptr; // returned HICON
+ if (base != -1)
+ hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4);
+
+ return (INT_PTR)hIcon;
+}
+
+/****************************************************************************************
+ * ServiceGetClientDescrW
+ * MS_FP_GETCLIENTDESCRW service implementation.
+ * wParam - LPCWSTR MirVer value
+ * lParam - (NULL) unused
+ * returns LPCWSTR: client description (do not destroy) or NULL
+ */
+
+static INT_PTR ServiceGetClientDescrW(WPARAM wParam, LPARAM)
+{
+ LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for.
+ if (wszMirVer == nullptr)
+ return 0;
+
+ LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer); _wcsupr(wszMirVerUp);
+ if (mir_wstrcmp(wszMirVerUp, L"?") == 0)
+ return (INT_PTR)def_kn_fp_mask[UNKNOWN_MASK_NUMBER].szClientDescription;
+
+ for (int index = 0; index < DEFAULT_KN_FP_MASK_COUNT; index++)
+ if (WildCompare(wszMirVerUp, def_kn_fp_mask[index].szMaskUpper))
+ return (INT_PTR)def_kn_fp_mask[index].szClientDescription;
+
+ return NULL;
+}
+
+/****************************************************************************************
+ * ServiceSameClientW
+ * MS_FP_SAMECLIENTSW service implementation.
+ * wParam - LPWSTR first MirVer value
+ * lParam - LPWSTR second MirVer value
+ * returns LPCWSTR: client description (do not destroy) if clients are same or NULL
+ */
+
+static INT_PTR ServiceSameClientsW(WPARAM wParam, LPARAM lParam)
+{
+ if (!wParam || !lParam)
+ return NULL; //one of its is not null
+
+ INT_PTR res1 = ServiceGetClientDescrW(wParam, 0);
+ INT_PTR res2 = ServiceGetClientDescrW(lParam, 0);
+ return (res1 == res2 && res1 != 0) ? res1 : NULL;
+}
+
+/****************************************************************************************
+* OnExtraIconListRebuild
+* Set all registered indexes in array to EMPTY_EXTRA_ICON (unregistered icon)
+*/
+
+static int OnExtraIconListRebuild(WPARAM, LPARAM)
+{
+ ClearFI();
+ return 0;
+}
+
+/****************************************************************************************
+* OnIconsChanged
+*/
+
+static int OnIconsChanged(WPARAM, LPARAM)
+{
+ ClearFI();
+ return 0;
+}
+
+/****************************************************************************************
+* OnExtraImageApply
+* Try to get MirVer value from db for contact and if success calls ApplyFingerprintImage
+*/
+
+int OnExtraImageApply(WPARAM hContact, LPARAM)
+{
+ if (hContact == NULL)
+ return 0;
+
+ ptrW tszMirver;
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto != nullptr)
+ tszMirver = db_get_wsa(hContact, szProto, "MirVer");
+
+ ApplyFingerprintImage(hContact, tszMirver);
+ return 0;
+}
+
+/****************************************************************************************
+* OnContactSettingChanged
+* if contact settings changed apply new image or remove it
+*/
+
+static int OnContactSettingChanged(WPARAM hContact, LPARAM lParam)
+{
+ if (hContact == NULL)
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ if (cws && cws->szSetting && !strcmp(cws->szSetting, "MirVer")) {
+ switch (cws->value.type) {
+ case DBVT_UTF8:
+ ApplyFingerprintImage(hContact, ptrW(mir_utf8decodeW(cws->value.pszVal)));
+ break;
+ case DBVT_ASCIIZ:
+ ApplyFingerprintImage(hContact, _A2T(cws->value.pszVal));
+ break;
+ case DBVT_WCHAR:
+ ApplyFingerprintImage(hContact, cws->value.pwszVal);
+ break;
+ default:
+ ApplyFingerprintImage(hContact, nullptr);
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************************
+* OnSrmmWindowEvent
+* Monitors SRMM window's creation to draw a statusbar icon
+*/
+
+static int OnSrmmWindowEvent(WPARAM, LPARAM lParam)
+{
+ if (!g_plugin.getByte("StatusBarIcon", 1))
+ return 0;
+
+ MessageWindowEventData *event = (MessageWindowEventData *)lParam;
+ if (event == nullptr)
+ return 0;
+
+ if (event->uType == MSG_WINDOW_EVT_OPEN) {
+ ptrW ptszMirVer;
+ char *szProto = Proto_GetBaseAccountName(event->hContact);
+ if (szProto != nullptr)
+ ptszMirVer = db_get_wsa(event->hContact, szProto, "MirVer");
+ SetSrmmIcon(event->hContact, ptszMirVer);
+ arMonitoredWindows.insert((HANDLE)event->hContact);
+ }
+ else if (event->uType == MSG_WINDOW_EVT_CLOSE)
+ arMonitoredWindows.remove((HANDLE)event->hContact);
+
+ return 0;
+}
+
+/****************************************************************************************
+* OnModulesLoaded
+* Hook necessary events here
+*/
+
+int OnModulesLoaded(WPARAM, LPARAM)
+{
+ g_LPCodePage = Langpack_GetDefaultCodePage();
+
+ //Hook necessary events
+ HookEvent(ME_SKIN_ICONSCHANGED, OnIconsChanged);
+ HookEvent(ME_MSG_WINDOWEVENT, OnSrmmWindowEvent);
+
+ HookEvent(ME_MC_DEFAULTTCHANGED, OnExtraImageApply);
+
+ PathToAbsoluteW(DEFAULT_SKIN_FOLDER, g_szSkinLib);
+
+ RegisterIcons();
+
+ hExtraIcon = ExtraIcon_RegisterCallback("Client", LPGEN("Fingerprint"), "client_Miranda_unknown",
+ OnExtraIconListRebuild, OnExtraImageApply, OnExtraIconClick);
+
+ if (g_plugin.getByte("StatusBarIcon", 1)) {
+ StatusIconData sid = {};
+ sid.szModule = MODULENAME;
+ sid.flags = MBF_HIDDEN;
+ sid.dwId = 1;
+ Srmm_AddIcon(&sid, &g_plugin);
+ }
+
+ return 0;
+}
+
+void InitFingerModule()
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_OPT_INITIALISE, OnOptInitialise);
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
+
+ CreateServiceFunction(MS_FP_SAMECLIENTSW, ServiceSameClientsW);
+ CreateServiceFunction(MS_FP_GETCLIENTDESCRW, ServiceGetClientDescrW);
+ CreateServiceFunction(MS_FP_GETCLIENTICONW, ServiceGetClientIconW);
+}
diff --git a/plugins/FingerprintNG/src/main.cpp b/plugins/FingerprintNG/src/main.cpp index bc0b262031..aaabd2d796 100644 --- a/plugins/FingerprintNG/src/main.cpp +++ b/plugins/FingerprintNG/src/main.cpp @@ -1,6 +1,6 @@ /*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 FYR, Bio, nullbie, ghazan, mataes, HierOS, faith_healer and all respective contributors.
+Copyright © 2006-23 FYR, Bio, nullbie, ghazan, mataes, HierOS, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/masks.cpp b/plugins/FingerprintNG/src/masks.cpp index 64439f0615..c87335b7c1 100644 --- a/plugins/FingerprintNG/src/masks.cpp +++ b/plugins/FingerprintNG/src/masks.cpp @@ -1,6 +1,6 @@ /*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/options.cpp b/plugins/FingerprintNG/src/options.cpp index f589db85da..60fc532249 100644 --- a/plugins/FingerprintNG/src/options.cpp +++ b/plugins/FingerprintNG/src/options.cpp @@ -1,7 +1,7 @@ /*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/stdafx.cxx b/plugins/FingerprintNG/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/FingerprintNG/src/stdafx.cxx +++ b/plugins/FingerprintNG/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/FingerprintNG/src/stdafx.h b/plugins/FingerprintNG/src/stdafx.h index 6a62fb57e8..091716993f 100644 --- a/plugins/FingerprintNG/src/stdafx.h +++ b/plugins/FingerprintNG/src/stdafx.h @@ -1,7 +1,7 @@ /*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/version.h b/plugins/FingerprintNG/src/version.h index 1691434647..311340c4b9 100644 --- a/plugins/FingerprintNG/src/version.h +++ b/plugins/FingerprintNG/src/version.h @@ -1,6 +1,6 @@ /*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -29,4 +29,4 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define __DESCRIPTION "Fingerprint NG (client version) icons module for Miranda NG."
#define __AUTHOR "faith_healer, ghazan, Mataes"
#define __AUTHORWEB "https://miranda-ng.org/p/Fingerprint"
-#define __COPYRIGHT "© 2006-22 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors."
+#define __COPYRIGHT "© 2006-23 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors."
diff --git a/plugins/FloatingContacts/src/stdafx.cxx b/plugins/FloatingContacts/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/FloatingContacts/src/stdafx.cxx +++ b/plugins/FloatingContacts/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Folders/src/stdafx.cxx b/plugins/Folders/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Folders/src/stdafx.cxx +++ b/plugins/Folders/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Folders/src/version.h b/plugins/Folders/src/version.h index 1dcea4ef9c..55c76bffe1 100644 --- a/plugins/Folders/src/version.h +++ b/plugins/Folders/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Allows plugins to save their data to user selected folders; supports variables."
#define __AUTHOR "Cristian Libotean, Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Folders"
-#define __COPYRIGHT "© 2005-2012 Cristian Libotean, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2005-2012 Cristian Libotean, 2012-23 Miranda NG team"
diff --git a/plugins/HTTPServer/src/stdafx.cpp b/plugins/HTTPServer/src/stdafx.cpp index 3665ac85d9..9bc36959d7 100644 --- a/plugins/HTTPServer/src/stdafx.cpp +++ b/plugins/HTTPServer/src/stdafx.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/HistoryLinkListPlus/src/stdafx.cxx b/plugins/HistoryLinkListPlus/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/HistoryLinkListPlus/src/stdafx.cxx +++ b/plugins/HistoryLinkListPlus/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/HistoryStats/src/stdafx.cxx b/plugins/HistoryStats/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/HistoryStats/src/stdafx.cxx +++ b/plugins/HistoryStats/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/HistorySweeperLight/src/stdafx.cxx b/plugins/HistorySweeperLight/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/HistorySweeperLight/src/stdafx.cxx +++ b/plugins/HistorySweeperLight/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/HwHotKeys/src/stdafx.cxx b/plugins/HwHotKeys/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/HwHotKeys/src/stdafx.cxx +++ b/plugins/HwHotKeys/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/HwHotKeys/src/version.h b/plugins/HwHotKeys/src/version.h index 86ab1761f1..55de414fcf 100644 --- a/plugins/HwHotKeys/src/version.h +++ b/plugins/HwHotKeys/src/version.h @@ -1,37 +1,37 @@ -/* ============================================================================ -Hardware HotKeys plugin for Miranda NG. -Copyright © Eugene f2065, http://f2065.narod.ru, f2065 mail.ru, ICQ 35078112 - -This program is free software; you can redistribute it and / or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA. -============================================================================ */ - -// история версий: -// 1.0.0.6 - первая версия Hardware HotKeys для Miranda NG (более ранние версии были для Miranda IM), исправление старых ошибок. - - -#define __MAJOR_VERSION 1 -#define __MINOR_VERSION 0 -#define __RELEASE_NUM 0 -#define __BUILD_NUM 6 - -#include <stdver.h> - -#define __PLUGIN_NAME "Hardware HotKeys" // dll-fileinfo "FileVersion" и "ProductName", меню в настройках миранды, название плагина в миранде. Нелокализуемое! -#define __FILENAME "HwHotKeys.dll" // dll-fileinfo "OriginalFilename" -#define __DESCRIPTION "Allows you to assign expanded multimedia keys (only for PS/2 keyboards)." // описание плагина в миранде (локализуемое) -#define __AUTHOR "Eugene f2065" // описание плагина в миранде -#define __AUTHORWEB "http://f2065.narod.ru/" // описание плагина в dll-fileinfo "CompanyName", описание плагина в миранде -#define __COPYRIGHT "© 2010-22 Eugene f2065" // описание плагина в dll-fileinfo "LegalCopyright", описание плагина в миранде - +/* ============================================================================
+Hardware HotKeys plugin for Miranda NG.
+Copyright © Eugene f2065, http://f2065.narod.ru, f2065 mail.ru, ICQ 35078112
+
+This program is free software; you can redistribute it and / or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA.
+============================================================================ */
+
+// история версий:
+// 1.0.0.6 - первая версия Hardware HotKeys для Miranda NG (более ранние версии были для Miranda IM), исправление старых ошибок.
+
+
+#define __MAJOR_VERSION 1
+#define __MINOR_VERSION 0
+#define __RELEASE_NUM 0
+#define __BUILD_NUM 6
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Hardware HotKeys" // dll-fileinfo "FileVersion" и "ProductName", меню в настройках миранды, название плагина в миранде. Нелокализуемое!
+#define __FILENAME "HwHotKeys.dll" // dll-fileinfo "OriginalFilename"
+#define __DESCRIPTION "Allows you to assign expanded multimedia keys (only for PS/2 keyboards)." // описание плагина в миранде (локализуемое)
+#define __AUTHOR "Eugene f2065" // описание плагина в миранде
+#define __AUTHORWEB "http://f2065.narod.ru/" // описание плагина в dll-fileinfo "CompanyName", описание плагина в миранде
+#define __COPYRIGHT "© 2010-23 Eugene f2065" // описание плагина в dll-fileinfo "LegalCopyright", описание плагина в миранде
+
diff --git a/plugins/IEHistory/src/version.h b/plugins/IEHistory/src/version.h index 9175a02035..c725c65f6c 100644 --- a/plugins/IEHistory/src/version.h +++ b/plugins/IEHistory/src/version.h @@ -1,39 +1,39 @@ -/* -Bonsai plugin for Miranda IM - -Copyright © 2006 Cristian Libotean - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef M_IEHISTORY_VERSION_H -#define M_IEHISTORY_VERSION_H - -#define __MAJOR_VERSION 0 -#define __MINOR_VERSION 0 -#define __RELEASE_NUM 1 -#define __BUILD_NUM 7 - -#include <stdver.h> - -#define __DESCRIPTION "Shows the history for a given contact using IEView." -#define __PLUGIN_NAME "IE History" -#define __FILENAME "IEHistory.dll" -#define __AUTHOR "Cristian Libotean, Miranda NG team" -#define __COPYRIGHT "© 2006 Cristian Libotean, 2014-22 Miranda NG team" -#define __AUTHORWEB "https://miranda-ng.org/p/IEHistory" -#define __PLUGIN_DISPLAY_NAME "IEView history viewer" - -#endif //M_IEHISTORY_VERSION_H +/*
+Bonsai plugin for Miranda IM
+
+Copyright © 2006 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_IEHISTORY_VERSION_H
+#define M_IEHISTORY_VERSION_H
+
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 0
+#define __RELEASE_NUM 1
+#define __BUILD_NUM 7
+
+#include <stdver.h>
+
+#define __DESCRIPTION "Shows the history for a given contact using IEView."
+#define __PLUGIN_NAME "IE History"
+#define __FILENAME "IEHistory.dll"
+#define __AUTHOR "Cristian Libotean, Miranda NG team"
+#define __COPYRIGHT "© 2006 Cristian Libotean, 2014-23 Miranda NG team"
+#define __AUTHORWEB "https://miranda-ng.org/p/IEHistory"
+#define __PLUGIN_DISPLAY_NAME "IEView history viewer"
+
+#endif //M_IEHISTORY_VERSION_H
diff --git a/plugins/IEView/src/stdafx.cxx b/plugins/IEView/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/IEView/src/stdafx.cxx +++ b/plugins/IEView/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/IgnoreState/src/stdafx.cxx b/plugins/IgnoreState/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/IgnoreState/src/stdafx.cxx +++ b/plugins/IgnoreState/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/dbrw/dbcontacts.cpp b/plugins/Import/src/dbrw/dbcontacts.cpp index ce5fd91a6d..bd989afb47 100644 --- a/plugins/Import/src/dbrw/dbcontacts.cpp +++ b/plugins/Import/src/dbrw/dbcontacts.cpp @@ -1,68 +1,68 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -void CDbxSQLite::FillContacts() -{ - sqlite3_stmt *st = nullptr; - if (sql_prepare(m_sqlite, "SELECT c.id, s.val FROM dbrw_contacts as c INNER JOIN dbrw_settings as s on s.id = c.id WHERE s.module = 'Protocol' and s.setting = 'p';", &st) != SQLITE_OK) - return; - - while (sql_step(st) == SQLITE_ROW) { - MCONTACT contactID = sqlite3_column_int(st, 0); - const char *proto = (const char*)sqlite3_column_text(st, 1); - DBCachedContact *cc = m_cache->AddContactToCache(contactID); - cc->szProto = mir_strdup(proto); - } - sql_finalize(st); -} - -STDMETHODIMP_(BOOL) CDbxSQLite::IsDbContact(MCONTACT contactID) -{ - DBCachedContact *cc = m_cache->GetCachedContact(contactID); - return (cc != nullptr); -} - -STDMETHODIMP_(int) CDbxSQLite::GetContactCount(void) -{ - int res = 0; - if (sql_step(ctc_stmts_prep[SQL_CTC_STMT_COUNT]) == SQLITE_ROW) - res = sqlite3_column_int(ctc_stmts_prep[SQL_CTC_STMT_COUNT], 0); - sql_reset(ctc_stmts_prep[SQL_CTC_STMT_COUNT]); - return res; -} - -STDMETHODIMP_(int) CDbxSQLite::GetContactSize(void) -{ - return sizeof(DBCachedContact); -} - -STDMETHODIMP_(MCONTACT) CDbxSQLite::AddContact(void) -{ - return INVALID_CONTACT_ID; -} - -STDMETHODIMP_(int) CDbxSQLite::DeleteContact(MCONTACT) -{ - return 1; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+void CDbxSQLite::FillContacts()
+{
+ sqlite3_stmt *st = nullptr;
+ if (sql_prepare(m_sqlite, "SELECT c.id, s.val FROM dbrw_contacts as c INNER JOIN dbrw_settings as s on s.id = c.id WHERE s.module = 'Protocol' and s.setting = 'p';", &st) != SQLITE_OK)
+ return;
+
+ while (sql_step(st) == SQLITE_ROW) {
+ MCONTACT contactID = sqlite3_column_int(st, 0);
+ const char *proto = (const char*)sqlite3_column_text(st, 1);
+ DBCachedContact *cc = m_cache->AddContactToCache(contactID);
+ cc->szProto = mir_strdup(proto);
+ }
+ sql_finalize(st);
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::IsDbContact(MCONTACT contactID)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(contactID);
+ return (cc != nullptr);
+}
+
+STDMETHODIMP_(int) CDbxSQLite::GetContactCount(void)
+{
+ int res = 0;
+ if (sql_step(ctc_stmts_prep[SQL_CTC_STMT_COUNT]) == SQLITE_ROW)
+ res = sqlite3_column_int(ctc_stmts_prep[SQL_CTC_STMT_COUNT], 0);
+ sql_reset(ctc_stmts_prep[SQL_CTC_STMT_COUNT]);
+ return res;
+}
+
+STDMETHODIMP_(int) CDbxSQLite::GetContactSize(void)
+{
+ return sizeof(DBCachedContact);
+}
+
+STDMETHODIMP_(MCONTACT) CDbxSQLite::AddContact(void)
+{
+ return INVALID_CONTACT_ID;
+}
+
+STDMETHODIMP_(int) CDbxSQLite::DeleteContact(MCONTACT)
+{
+ return 1;
+}
diff --git a/plugins/Import/src/dbrw/dbevents.cpp b/plugins/Import/src/dbrw/dbevents.cpp index 97091d0ab3..06995bd4a9 100644 --- a/plugins/Import/src/dbrw/dbevents.cpp +++ b/plugins/Import/src/dbrw/dbevents.cpp @@ -1,195 +1,195 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -STDMETHODIMP_(int) CDbxSQLite::GetEventCount(MCONTACT contactID) -{ - mir_cslock lock(m_csDbAccess); - - int res = 0; - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 1, contactID); - if (sql_step(evt_stmts_prep[SQL_EVT_STMT_COUNT]) == SQLITE_ROW) - res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 0); - sql_reset(evt_stmts_prep[SQL_EVT_STMT_COUNT]); - return res; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::AddEvent(MCONTACT, const DBEVENTINFO*) -{ - return 0; -} - -STDMETHODIMP_(BOOL) CDbxSQLite::DeleteEvent(MEVENT) -{ - return FALSE; -} - -BOOL CDbxSQLite::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*) -{ - return 1; -} - -STDMETHODIMP_(int) CDbxSQLite::GetBlobSize(MEVENT hDbEvent) -{ - mir_cslock lock(m_csDbAccess); - - int res = -1; - sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE]; - sqlite3_bind_int(stmt, 1, hDbEvent); - if (sql_step(stmt) == SQLITE_ROW) - res = sqlite3_column_int(stmt, 0); - sql_reset(stmt); - return res; -} - -STDMETHODIMP_(BOOL) CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei) -{ - if (dbei == nullptr) - return 1; - - if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) { - dbei->cbBlob = 0; - return 1; - } - - mir_cslock lock(m_csDbAccess); - - int res = 1; - sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GET]; - sqlite3_bind_int(stmt, 1, hDbEvent); - if (sql_step(stmt) == SQLITE_ROW) { - const void *blob = sqlite3_column_blob(stmt, 4); - - dbei->timestamp = (uint32_t)sqlite3_column_int(stmt, 1); - dbei->flags = (uint32_t)sqlite3_column_int(stmt, 2); - dbei->eventType = (uint16_t)sqlite3_column_int(stmt, 3); - dbei->szModule = mir_strdup((char*)sqlite3_column_text(stmt, 7)); - - uint32_t cbBlob = sqlite3_column_int(stmt, 5); - size_t bytesToCopy = cbBlob; - if (dbei->cbBlob == -1) - dbei->pBlob = (uint8_t*)mir_calloc(cbBlob + 2); - else if (dbei->cbBlob < cbBlob) - bytesToCopy = dbei->cbBlob; - - dbei->cbBlob = cbBlob; - if (bytesToCopy && dbei->pBlob) - memcpy(dbei->pBlob, blob, bytesToCopy); - res = 0; - } - sql_reset(stmt); - return res; -} - -STDMETHODIMP_(BOOL) CDbxSQLite::MarkEventRead(MCONTACT, MEVENT) -{ - return FALSE; -} - -STDMETHODIMP_(MCONTACT) CDbxSQLite::GetEventContact(MEVENT hDbEvent) -{ - mir_cslock lock(m_csDbAccess); - - int res = INVALID_CONTACT_ID; - sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETCONTACT]; - sqlite3_bind_int(stmt, 1, hDbEvent); - if (sql_step(stmt) == SQLITE_ROW) - res = sqlite3_column_int(stmt, 0); - sql_reset(stmt); - return res; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstEvent(MCONTACT contactID) -{ - mir_cslock lock(m_csDbAccess); - - int res = 0; - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 1, contactID); - if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]) == SQLITE_ROW) - res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 0); - sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]); - return res; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstUnreadEvent(MCONTACT contactID) -{ - mir_cslock lock(m_csDbAccess); - - int res = 0; - uint32_t flags = 0; - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1, contactID); - while (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]) == SQLITE_ROW) { - flags = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 0); - if (!(flags & (DBEF_READ | DBEF_SENT))) { - res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1); - sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]); - return res; - } - } - sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]); - return res; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::FindLastEvent(MCONTACT contactID) -{ - mir_cslock lock(m_csDbAccess); - - int res = 0; - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 1, contactID); - if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]) == SQLITE_ROW) - res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 0); - sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]); - return res; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) -{ - if (hDbEvent == NULL) - return 0; - - mir_cslock lock(m_csDbAccess); - - int res = 0; - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 1, contactID); - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 2, hDbEvent); - if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]) == SQLITE_ROW) - res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 0); - sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]); - return res; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) -{ - if (!hDbEvent) - return 0; - - mir_cslock lock(m_csDbAccess); - - int res = 0; - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 1, contactID); - sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 2, hDbEvent); - if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]) == SQLITE_ROW) - res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 0); - sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]); - return res; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+STDMETHODIMP_(int) CDbxSQLite::GetEventCount(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 1, contactID);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_COUNT]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_COUNT]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::AddEvent(MCONTACT, const DBEVENTINFO*)
+{
+ return 0;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::DeleteEvent(MEVENT)
+{
+ return FALSE;
+}
+
+BOOL CDbxSQLite::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*)
+{
+ return 1;
+}
+
+STDMETHODIMP_(int) CDbxSQLite::GetBlobSize(MEVENT hDbEvent)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = -1;
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ if (sql_step(stmt) == SQLITE_ROW)
+ res = sqlite3_column_int(stmt, 0);
+ sql_reset(stmt);
+ return res;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei)
+{
+ if (dbei == nullptr)
+ return 1;
+
+ if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) {
+ dbei->cbBlob = 0;
+ return 1;
+ }
+
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 1;
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GET];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ if (sql_step(stmt) == SQLITE_ROW) {
+ const void *blob = sqlite3_column_blob(stmt, 4);
+
+ dbei->timestamp = (uint32_t)sqlite3_column_int(stmt, 1);
+ dbei->flags = (uint32_t)sqlite3_column_int(stmt, 2);
+ dbei->eventType = (uint16_t)sqlite3_column_int(stmt, 3);
+ dbei->szModule = mir_strdup((char*)sqlite3_column_text(stmt, 7));
+
+ uint32_t cbBlob = sqlite3_column_int(stmt, 5);
+ size_t bytesToCopy = cbBlob;
+ if (dbei->cbBlob == -1)
+ dbei->pBlob = (uint8_t*)mir_calloc(cbBlob + 2);
+ else if (dbei->cbBlob < cbBlob)
+ bytesToCopy = dbei->cbBlob;
+
+ dbei->cbBlob = cbBlob;
+ if (bytesToCopy && dbei->pBlob)
+ memcpy(dbei->pBlob, blob, bytesToCopy);
+ res = 0;
+ }
+ sql_reset(stmt);
+ return res;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::MarkEventRead(MCONTACT, MEVENT)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(MCONTACT) CDbxSQLite::GetEventContact(MEVENT hDbEvent)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = INVALID_CONTACT_ID;
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETCONTACT];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ if (sql_step(stmt) == SQLITE_ROW)
+ res = sqlite3_column_int(stmt, 0);
+ sql_reset(stmt);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstEvent(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 1, contactID);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstUnreadEvent(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ uint32_t flags = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1, contactID);
+ while (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]) == SQLITE_ROW) {
+ flags = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 0);
+ if (!(flags & (DBEF_READ | DBEF_SENT))) {
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
+ return res;
+ }
+ }
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindLastEvent(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 1, contactID);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindNextEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (hDbEvent == NULL)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 1, contactID);
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 2, hDbEvent);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (!hDbEvent)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 1, contactID);
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 2, hDbEvent);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]);
+ return res;
+}
diff --git a/plugins/Import/src/dbrw/dbintf.cpp b/plugins/Import/src/dbrw/dbintf.cpp index 9749ae4437..d7c1751371 100644 --- a/plugins/Import/src/dbrw/dbintf.cpp +++ b/plugins/Import/src/dbrw/dbintf.cpp @@ -1,87 +1,87 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -CDbxSQLite::CDbxSQLite() - : sql_prepare_len(0) -{ - sql_prepare_add(ctc_stmts, ctc_stmts_prep, SQL_CTC_STMT_NUM); - sql_prepare_add(evt_stmts, evt_stmts_prep, SQL_EVT_STMT_NUM); - sql_prepare_add(set_stmts, set_stmts_prep, SQL_SET_STMT_NUM); -} - -CDbxSQLite::~CDbxSQLite() -{ - sql_close(m_sqlite); - - for (int i = 0; i < sql_prepare_len; i++) - sql_finalize(*sql_prepare_stmt[i]); - mir_free(sql_prepare_text); - mir_free(sql_prepare_stmt); -} - -int CDbxSQLite::Open(const wchar_t *profile) -{ - T2Utf path(profile); - if (sql_open(path, &m_sqlite) != SQLITE_OK) - return 1; - - //utils_vacuum_check(); - { - sql_exec(m_sqlite, "BEGIN TRANSACTION;"); - sql_exec(m_sqlite, "PRAGMA locking_mode = EXCLUSIVE;"); - sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;"); - sql_exec(m_sqlite, "PRAGMA cache_size = 6000;"); - sql_exec(m_sqlite, "PRAGMA temp_store = MEMORY;"); - sql_exec(m_sqlite, "COMMIT;"); - } - - sql_prepare_statements(); - - FillContacts(); - - return 0; -} - -STDMETHODIMP_(void) CDbxSQLite::SetCacheSafetyMode(BOOL safeMode) -{ - if (safeMode) - sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;"); - else - sql_exec(m_sqlite, "PRAGMA synchronous = OFF;"); -} - -STDMETHODIMP_(BOOL) CDbxSQLite::MetaMergeHistory(DBCachedContact*, DBCachedContact*) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) CDbxSQLite::MetaSplitHistory(DBCachedContact*, DBCachedContact*) -{ - return FALSE; -} - -STDMETHODIMP_(MEVENT) CDbxSQLite::GetEventById(LPCSTR, LPCSTR) -{ - return 0; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+CDbxSQLite::CDbxSQLite()
+ : sql_prepare_len(0)
+{
+ sql_prepare_add(ctc_stmts, ctc_stmts_prep, SQL_CTC_STMT_NUM);
+ sql_prepare_add(evt_stmts, evt_stmts_prep, SQL_EVT_STMT_NUM);
+ sql_prepare_add(set_stmts, set_stmts_prep, SQL_SET_STMT_NUM);
+}
+
+CDbxSQLite::~CDbxSQLite()
+{
+ sql_close(m_sqlite);
+
+ for (int i = 0; i < sql_prepare_len; i++)
+ sql_finalize(*sql_prepare_stmt[i]);
+ mir_free(sql_prepare_text);
+ mir_free(sql_prepare_stmt);
+}
+
+int CDbxSQLite::Open(const wchar_t *profile)
+{
+ T2Utf path(profile);
+ if (sql_open(path, &m_sqlite) != SQLITE_OK)
+ return 1;
+
+ //utils_vacuum_check();
+ {
+ sql_exec(m_sqlite, "BEGIN TRANSACTION;");
+ sql_exec(m_sqlite, "PRAGMA locking_mode = EXCLUSIVE;");
+ sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;");
+ sql_exec(m_sqlite, "PRAGMA cache_size = 6000;");
+ sql_exec(m_sqlite, "PRAGMA temp_store = MEMORY;");
+ sql_exec(m_sqlite, "COMMIT;");
+ }
+
+ sql_prepare_statements();
+
+ FillContacts();
+
+ return 0;
+}
+
+STDMETHODIMP_(void) CDbxSQLite::SetCacheSafetyMode(BOOL safeMode)
+{
+ if (safeMode)
+ sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;");
+ else
+ sql_exec(m_sqlite, "PRAGMA synchronous = OFF;");
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::MetaMergeHistory(DBCachedContact*, DBCachedContact*)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::MetaSplitHistory(DBCachedContact*, DBCachedContact*)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::GetEventById(LPCSTR, LPCSTR)
+{
+ return 0;
+}
diff --git a/plugins/Import/src/dbrw/dbintf.h b/plugins/Import/src/dbrw/dbintf.h index 9ff2ee40ff..2a843b025d 100644 --- a/plugins/Import/src/dbrw/dbintf.h +++ b/plugins/Import/src/dbrw/dbintf.h @@ -1,159 +1,159 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#pragma once - -enum SQL_CTC_STMT -{ - SQL_CTC_STMT_COUNT = 0, - SQL_CTC_STMT_EXISTS, - SQL_CTC_STMT_NUM -}; - -static char *ctc_stmts[SQL_CTC_STMT_NUM] = -{ - "SELECT count(*) FROM dbrw_contacts;", - "SELECT id FROM dbrw_contacts WHERE id = ? LIMIT 1;" -}; - -enum SQL_EVT_STMT -{ - SQL_EVT_STMT_COUNT = 0, - SQL_EVT_STMT_BLOBSIZE, - SQL_EVT_STMT_GET, - SQL_EVT_STMT_GETFLAGS, - SQL_EVT_STMT_GETCONTACT, - SQL_EVT_STMT_FINDFIRST, - SQL_EVT_STMT_FINDFIRSTUNREAD, - SQL_EVT_STMT_FINDLAST, - SQL_EVT_STMT_FINDNEXT, - SQL_EVT_STMT_FINDPREV, - SQL_EVT_STMT_NUM -}; - -static char *evt_stmts[SQL_EVT_STMT_NUM] = -{ - "SELECT count(*) FROM dbrw_events where contactid = ?;", - "SELECT blobsize FROM dbrw_events where id = ? LIMIT 1;", - "SELECT * FROM dbrw_events where id = ? LIMIT 1;", - "SELECT flags FROM dbrw_events where id = ? LIMIT 1;", - "SELECT contactid FROM dbrw_events where id = ? LIMIT 1;", - "SELECT id FROM dbrw_events where contactid = ? ORDER by id LIMIT 1;", - "SELECT flags, id FROM dbrw_events where contactid = ? ORDER by id;", - "SELECT id FROM dbrw_events where contactid = ? ORDER by id DESC;", - "SELECT id FROM dbrw_events where contactid = ? AND id > ? ORDER by id LIMIT 1;", - "SELECT id FROM dbrw_events where contactid = ? AND id < ? ORDER by id DESC LIMIT 1;" -}; - -enum SQL_SET_STMT -{ - SQL_SET_STMT_READ = 0, - SQL_SET_STMT_ENUM, - SQL_SET_STMT_ENUMMODULES, - SQL_SET_STMT_SETTINGCHECK, - SQL_SET_STMT_NUM -}; - -static char *set_stmts[SQL_SET_STMT_NUM] = -{ - "SELECT type, val FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ? LIMIT 1;", - "SELECT setting from dbrw_settings where id = ? AND module = ? ORDER by setting;", - "SELECT DISTINCT module from dbrw_settings;", - "SELECT count(*) FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ?;", -}; - -struct TSqlMessage { - int op; - sqlite3 *pDb; - sqlite3_stmt *pStmt; - int retCode; - const char *zIn; - HANDLE hDoneEvent; -}; - -struct CDbxSQLite : public MDatabaseReadonly, public MZeroedObject -{ -private: - sqlite3 *m_sqlite; - - int sql_prepare_len; - char **sql_prepare_text; - sqlite3_stmt ***sql_prepare_stmt; - sqlite3_stmt *ctc_stmts_prep[SQL_CTC_STMT_NUM] = { 0 }; - sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = { 0 }; - sqlite3_stmt *set_stmts_prep[SQL_SET_STMT_NUM] = { 0 }; - - void sql_prepare_add(char **text, sqlite3_stmt **stmts, int len); - void sql_prepare_statements(); - static void CALLBACK sql_server_sync_apc(UINT_PTR dwParam); - void sql_server_sync(TSqlMessage *msg); - int sql_step(sqlite3_stmt *stmt); - int sql_reset(sqlite3_stmt *stmt); - int sql_exec(sqlite3 *sql, const char *query); - int sql_open(const char *path, sqlite3 **sql); - int sql_close(sqlite3 *sql); - int sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt); - int sql_finalize(sqlite3_stmt *stmt); - - void FillContacts(); - -public: - CDbxSQLite(); - ~CDbxSQLite(); - - int Open(const wchar_t *profile); - - STDMETHODIMP_(BOOL) IsRelational(void) override { return FALSE; } - STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override; - - STDMETHODIMP_(int) GetContactCount(void) override; - STDMETHODIMP_(int) DeleteContact(MCONTACT contactID) override; - STDMETHODIMP_(MCONTACT) AddContact(void) override; - STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override; - STDMETHODIMP_(int) GetContactSize(void) override; - - STDMETHODIMP_(int) GetEventCount(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, const DBEVENTINFO *dbe) override; - STDMETHODIMP_(BOOL) DeleteEvent(MEVENT hDbEvent) override; - STDMETHODIMP_(BOOL) EditEvent(MCONTACT contactID, MEVENT hDbEvent, const DBEVENTINFO *dbe) override; - STDMETHODIMP_(int) GetBlobSize(MEVENT hDbEvent) override; - STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override; - STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override; - STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override; - STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override; - STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override; - - STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override; - - STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) override; - STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override; - - STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override; - STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override; - - STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override; - - STDMETHODIMP_(DATABASELINK *) GetDriver(); -}; +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#pragma once
+
+enum SQL_CTC_STMT
+{
+ SQL_CTC_STMT_COUNT = 0,
+ SQL_CTC_STMT_EXISTS,
+ SQL_CTC_STMT_NUM
+};
+
+static char *ctc_stmts[SQL_CTC_STMT_NUM] =
+{
+ "SELECT count(*) FROM dbrw_contacts;",
+ "SELECT id FROM dbrw_contacts WHERE id = ? LIMIT 1;"
+};
+
+enum SQL_EVT_STMT
+{
+ SQL_EVT_STMT_COUNT = 0,
+ SQL_EVT_STMT_BLOBSIZE,
+ SQL_EVT_STMT_GET,
+ SQL_EVT_STMT_GETFLAGS,
+ SQL_EVT_STMT_GETCONTACT,
+ SQL_EVT_STMT_FINDFIRST,
+ SQL_EVT_STMT_FINDFIRSTUNREAD,
+ SQL_EVT_STMT_FINDLAST,
+ SQL_EVT_STMT_FINDNEXT,
+ SQL_EVT_STMT_FINDPREV,
+ SQL_EVT_STMT_NUM
+};
+
+static char *evt_stmts[SQL_EVT_STMT_NUM] =
+{
+ "SELECT count(*) FROM dbrw_events where contactid = ?;",
+ "SELECT blobsize FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT * FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT flags FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT contactid FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT id FROM dbrw_events where contactid = ? ORDER by id LIMIT 1;",
+ "SELECT flags, id FROM dbrw_events where contactid = ? ORDER by id;",
+ "SELECT id FROM dbrw_events where contactid = ? ORDER by id DESC;",
+ "SELECT id FROM dbrw_events where contactid = ? AND id > ? ORDER by id LIMIT 1;",
+ "SELECT id FROM dbrw_events where contactid = ? AND id < ? ORDER by id DESC LIMIT 1;"
+};
+
+enum SQL_SET_STMT
+{
+ SQL_SET_STMT_READ = 0,
+ SQL_SET_STMT_ENUM,
+ SQL_SET_STMT_ENUMMODULES,
+ SQL_SET_STMT_SETTINGCHECK,
+ SQL_SET_STMT_NUM
+};
+
+static char *set_stmts[SQL_SET_STMT_NUM] =
+{
+ "SELECT type, val FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ? LIMIT 1;",
+ "SELECT setting from dbrw_settings where id = ? AND module = ? ORDER by setting;",
+ "SELECT DISTINCT module from dbrw_settings;",
+ "SELECT count(*) FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ?;",
+};
+
+struct TSqlMessage {
+ int op;
+ sqlite3 *pDb;
+ sqlite3_stmt *pStmt;
+ int retCode;
+ const char *zIn;
+ HANDLE hDoneEvent;
+};
+
+struct CDbxSQLite : public MDatabaseReadonly, public MZeroedObject
+{
+private:
+ sqlite3 *m_sqlite;
+
+ int sql_prepare_len;
+ char **sql_prepare_text;
+ sqlite3_stmt ***sql_prepare_stmt;
+ sqlite3_stmt *ctc_stmts_prep[SQL_CTC_STMT_NUM] = { 0 };
+ sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = { 0 };
+ sqlite3_stmt *set_stmts_prep[SQL_SET_STMT_NUM] = { 0 };
+
+ void sql_prepare_add(char **text, sqlite3_stmt **stmts, int len);
+ void sql_prepare_statements();
+ static void CALLBACK sql_server_sync_apc(UINT_PTR dwParam);
+ void sql_server_sync(TSqlMessage *msg);
+ int sql_step(sqlite3_stmt *stmt);
+ int sql_reset(sqlite3_stmt *stmt);
+ int sql_exec(sqlite3 *sql, const char *query);
+ int sql_open(const char *path, sqlite3 **sql);
+ int sql_close(sqlite3 *sql);
+ int sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt);
+ int sql_finalize(sqlite3_stmt *stmt);
+
+ void FillContacts();
+
+public:
+ CDbxSQLite();
+ ~CDbxSQLite();
+
+ int Open(const wchar_t *profile);
+
+ STDMETHODIMP_(BOOL) IsRelational(void) override { return FALSE; }
+ STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override;
+
+ STDMETHODIMP_(int) GetContactCount(void) override;
+ STDMETHODIMP_(int) DeleteContact(MCONTACT contactID) override;
+ STDMETHODIMP_(MCONTACT) AddContact(void) override;
+ STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override;
+ STDMETHODIMP_(int) GetContactSize(void) override;
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, const DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(BOOL) DeleteEvent(MEVENT hDbEvent) override;
+ STDMETHODIMP_(BOOL) EditEvent(MCONTACT contactID, MEVENT hDbEvent, const DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(int) GetBlobSize(MEVENT hDbEvent) override;
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override;
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+
+ STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override;
+
+ STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) override;
+ STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override;
+
+ STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
+ STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
+
+ STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override;
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
diff --git a/plugins/Import/src/dbrw/dbrw.cpp b/plugins/Import/src/dbrw/dbrw.cpp index fded599583..7be7b2634d 100644 --- a/plugins/Import/src/dbrw/dbrw.cpp +++ b/plugins/Import/src/dbrw/dbrw.cpp @@ -1,105 +1,105 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static int dbrw_makeDatabase(const wchar_t*) -{ - return 1; -} - -static int dbrw_grokHeader(const wchar_t *profile) -{ - HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL); - int rc = 1; - int err = EGROKPRF_CANTREAD; - - if (hFile != INVALID_HANDLE_VALUE) { - BOOL r; - char buf[64]; - DWORD dwRead; - - ZeroMemory(buf, sizeof(buf)); - r = ReadFile(hFile, buf, sizeof(buf), &dwRead, NULL); - CloseHandle(hFile); - if (r && memcmp(buf, DBRW_HEADER_STR, strlen(DBRW_HEADER_STR)) == 0) { - sqlite3 *sqlcheck = NULL; - char *szPath = mir_utf8encodeW(profile); - - rc = sqlite3_open(szPath, &sqlcheck); - mir_free(szPath); - if (rc == SQLITE_OK) { - sqlite3_stmt *stmt; - err = EGROKPRF_UNKHEADER; - - sqlite3_prepare_v2(sqlcheck, "select * from sqlite_master where type = 'table' and name = 'dbrw_core';", -1, &stmt, NULL); - if (sqlite3_step(stmt) == SQLITE_ROW) { - - sqlite3_finalize(stmt); - sqlite3_prepare_v2(sqlcheck, "select val from dbrw_core where setting = 'SchemaVersion';", -1, &stmt, NULL); - if (sqlite3_step(stmt) == SQLITE_ROW) { - int sVersion; - - sVersion = sqlite3_column_int(stmt, 0); - if (sVersion == atoi(DBRW_SCHEMA_VERSION)) - rc = 0; - else { - // TODO: Return as valid and upgrade in - // dbrw_Load() if schema version is upgradable - } - } - } - sqlite3_finalize(stmt); - sqlite3_close(sqlcheck); - } - } - else err = r ? EGROKPRF_UNKHEADER : EGROKPRF_CANTREAD; - } - return rc; -} - -static MDatabaseCommon* dbrw_Load(const wchar_t *profile, BOOL) -{ - CDbxSQLite *db = new CDbxSQLite(); - db->Open(profile); - return db; -} - -static DATABASELINK dblink = -{ - 0, - "dbrw", - L"dbx SQLite driver", - dbrw_makeDatabase, - dbrw_grokHeader, - dbrw_Load -}; - -STDMETHODIMP_(DATABASELINK *) CDbxSQLite::GetDriver() -{ - return &g_patternDbLink; -} - -void RegisterDbrw() -{ - RegisterDatabasePlugin(&dblink); -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static int dbrw_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int dbrw_grokHeader(const wchar_t *profile)
+{
+ HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);
+ int rc = 1;
+ int err = EGROKPRF_CANTREAD;
+
+ if (hFile != INVALID_HANDLE_VALUE) {
+ BOOL r;
+ char buf[64];
+ DWORD dwRead;
+
+ ZeroMemory(buf, sizeof(buf));
+ r = ReadFile(hFile, buf, sizeof(buf), &dwRead, NULL);
+ CloseHandle(hFile);
+ if (r && memcmp(buf, DBRW_HEADER_STR, strlen(DBRW_HEADER_STR)) == 0) {
+ sqlite3 *sqlcheck = NULL;
+ char *szPath = mir_utf8encodeW(profile);
+
+ rc = sqlite3_open(szPath, &sqlcheck);
+ mir_free(szPath);
+ if (rc == SQLITE_OK) {
+ sqlite3_stmt *stmt;
+ err = EGROKPRF_UNKHEADER;
+
+ sqlite3_prepare_v2(sqlcheck, "select * from sqlite_master where type = 'table' and name = 'dbrw_core';", -1, &stmt, NULL);
+ if (sqlite3_step(stmt) == SQLITE_ROW) {
+
+ sqlite3_finalize(stmt);
+ sqlite3_prepare_v2(sqlcheck, "select val from dbrw_core where setting = 'SchemaVersion';", -1, &stmt, NULL);
+ if (sqlite3_step(stmt) == SQLITE_ROW) {
+ int sVersion;
+
+ sVersion = sqlite3_column_int(stmt, 0);
+ if (sVersion == atoi(DBRW_SCHEMA_VERSION))
+ rc = 0;
+ else {
+ // TODO: Return as valid and upgrade in
+ // dbrw_Load() if schema version is upgradable
+ }
+ }
+ }
+ sqlite3_finalize(stmt);
+ sqlite3_close(sqlcheck);
+ }
+ }
+ else err = r ? EGROKPRF_UNKHEADER : EGROKPRF_CANTREAD;
+ }
+ return rc;
+}
+
+static MDatabaseCommon* dbrw_Load(const wchar_t *profile, BOOL)
+{
+ CDbxSQLite *db = new CDbxSQLite();
+ db->Open(profile);
+ return db;
+}
+
+static DATABASELINK dblink =
+{
+ 0,
+ "dbrw",
+ L"dbx SQLite driver",
+ dbrw_makeDatabase,
+ dbrw_grokHeader,
+ dbrw_Load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxSQLite::GetDriver()
+{
+ return &g_patternDbLink;
+}
+
+void RegisterDbrw()
+{
+ RegisterDatabasePlugin(&dblink);
+}
diff --git a/plugins/Import/src/dbrw/dbrw.h b/plugins/Import/src/dbrw/dbrw.h index daf32ba745..11b153221a 100644 --- a/plugins/Import/src/dbrw/dbrw.h +++ b/plugins/Import/src/dbrw/dbrw.h @@ -1,30 +1,30 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#pragma once - -#include <sqlite3.h> - -#define DBRW_SCHEMA_VERSION "2" -#define DBRW_HEADER_STR "SQLite format 3" - -void RegisterDbrw(); +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#pragma once
+
+#include <sqlite3.h>
+
+#define DBRW_SCHEMA_VERSION "2"
+#define DBRW_HEADER_STR "SQLite format 3"
+
+void RegisterDbrw();
diff --git a/plugins/Import/src/dbrw/dbsettings.cpp b/plugins/Import/src/dbrw/dbsettings.cpp index 360a56ea94..9efe213bcf 100644 --- a/plugins/Import/src/dbrw/dbsettings.cpp +++ b/plugins/Import/src/dbrw/dbsettings.cpp @@ -1,171 +1,171 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "..\stdafx.h" - -STDMETHODIMP_(BOOL) CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) -{ - mir_cslockfull lock(m_csDbAccess); - - int res = 0; - while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]) == SQLITE_ROW && !res) { - const char *szModule = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUMMODULES], 0); - lock.unlock(); - res = (pFunc)(szModule, pParam); - lock.lock(); - } - sql_reset(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]); - return res; -} - -STDMETHODIMP_(BOOL) CDbxSQLite::GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) -{ - if (!szSetting || !szModule) - return 1; - - size_t settingNameLen = strlen(szSetting); - size_t moduleNameLen = strlen(szModule); - - mir_cslock lock(m_csDbAccess); - - char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); - DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 0); - if (pCachedValue != nullptr) { - if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) { - int cbOrigLen = dbv->cchVal; - char *cbOrigPtr = dbv->pszVal; - memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); - if (isStatic) { - int cbLen = 0; - if (pCachedValue->pszVal != nullptr) - cbLen = (int)strlen(pCachedValue->pszVal); - - cbOrigLen--; - dbv->pszVal = cbOrigPtr; - if (cbLen < cbOrigLen) - cbOrigLen = cbLen; - memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen); - dbv->pszVal[cbOrigLen] = 0; - dbv->cchVal = cbLen; - } - else { - dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1); - strcpy(dbv->pszVal, pCachedValue->pszVal); - } - } - else memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); - - return (pCachedValue->type == DBVT_DELETED) ? 1 : 0; - } - - // never look db for the resident variable - if (szCachedSettingName[-1] != 0) - return 1; - - sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 1, szSetting, -1, SQLITE_STATIC); - sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 2, szModule, -1, SQLITE_STATIC); - sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_READ], 3, contactID); - if (sql_step(set_stmts_prep[SQL_SET_STMT_READ]) != SQLITE_ROW) { - /*if (dbv->type != DBVT_BLOB) { - DBVARIANT* pCachedValue = settings_getCachedValue(contactID, szCachedSettingName, 1); - - if (pCachedValue != NULL) - pCachedValue->type = DBVT_DELETED; - }*/ - sql_reset(set_stmts_prep[SQL_SET_STMT_READ]); - return 1; - } - dbv->type = (int)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 0); - switch (dbv->type) { - case DBVT_BYTE: - dbv->bVal = (uint8_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1); - break; - case DBVT_WORD: - dbv->wVal = (uint16_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1); - break; - case DBVT_DWORD: - dbv->dVal = (uint32_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1); - break; - case DBVT_UTF8: - case DBVT_ASCIIZ: - { - const char *p = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_READ], 1); - - if (p != NULL) { - size_t len = strlen(p) + 1; - size_t copylen = isStatic ? (len < dbv->cchVal ? len : dbv->cchVal) : len; - if (!isStatic) - dbv->pszVal = (char*)mir_alloc(len); - memmove(dbv->pszVal, p, copylen); - } - else - dbv->pszVal = 0; - break; - } - case DBVT_BLOB: - { - size_t len = sqlite3_column_bytes(set_stmts_prep[SQL_SET_STMT_READ], 1); - - if (len) { - size_t copylen = isStatic ? (len < dbv->cpbVal ? len : dbv->cpbVal) : len; - if (!isStatic) - dbv->pbVal = (uint8_t*)mir_alloc(copylen); - memcpy(dbv->pbVal, sqlite3_column_blob(set_stmts_prep[SQL_SET_STMT_READ], 1), copylen); - dbv->cpbVal = (uint16_t)copylen; - } - else { - dbv = 0; - } - } - } - sql_reset(set_stmts_prep[SQL_SET_STMT_READ]); - // add to cache - if (dbv->type != DBVT_BLOB/* && dbv->type != DBVT_ENCRYPTED*/) { - pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 1); - if (pCachedValue != nullptr) - m_cache->SetCachedVariant(dbv, pCachedValue); - } - - return 0; -} - -STDMETHODIMP_(BOOL) CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) -{ - if (szModule == nullptr) - return -1; - - mir_cslockfull lock(m_csDbAccess); - - int res = -1; - sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_ENUM], 1, hContact); - sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_ENUM], 2, szModule, -1, SQLITE_STATIC); - while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUM]) == SQLITE_ROW) { - const char *sczSetting = (const char*)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUM], 0); - if (sczSetting) { - lock.unlock(); - res = (pfnEnumProc)(sczSetting, param); - lock.lock(); - } - } - sql_reset(set_stmts_prep[SQL_SET_STMT_ENUM]); - return res; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "..\stdafx.h"
+
+STDMETHODIMP_(BOOL) CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam)
+{
+ mir_cslockfull lock(m_csDbAccess);
+
+ int res = 0;
+ while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]) == SQLITE_ROW && !res) {
+ const char *szModule = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUMMODULES], 0);
+ lock.unlock();
+ res = (pFunc)(szModule, pParam);
+ lock.lock();
+ }
+ sql_reset(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]);
+ return res;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic)
+{
+ if (!szSetting || !szModule)
+ return 1;
+
+ size_t settingNameLen = strlen(szSetting);
+ size_t moduleNameLen = strlen(szModule);
+
+ mir_cslock lock(m_csDbAccess);
+
+ char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
+ strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ // never look db for the resident variable
+ if (szCachedSettingName[-1] != 0)
+ return 1;
+
+ sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 1, szSetting, -1, SQLITE_STATIC);
+ sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 2, szModule, -1, SQLITE_STATIC);
+ sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_READ], 3, contactID);
+ if (sql_step(set_stmts_prep[SQL_SET_STMT_READ]) != SQLITE_ROW) {
+ /*if (dbv->type != DBVT_BLOB) {
+ DBVARIANT* pCachedValue = settings_getCachedValue(contactID, szCachedSettingName, 1);
+
+ if (pCachedValue != NULL)
+ pCachedValue->type = DBVT_DELETED;
+ }*/
+ sql_reset(set_stmts_prep[SQL_SET_STMT_READ]);
+ return 1;
+ }
+ dbv->type = (int)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 0);
+ switch (dbv->type) {
+ case DBVT_BYTE:
+ dbv->bVal = (uint8_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
+ break;
+ case DBVT_WORD:
+ dbv->wVal = (uint16_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
+ break;
+ case DBVT_DWORD:
+ dbv->dVal = (uint32_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
+ break;
+ case DBVT_UTF8:
+ case DBVT_ASCIIZ:
+ {
+ const char *p = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_READ], 1);
+
+ if (p != NULL) {
+ size_t len = strlen(p) + 1;
+ size_t copylen = isStatic ? (len < dbv->cchVal ? len : dbv->cchVal) : len;
+ if (!isStatic)
+ dbv->pszVal = (char*)mir_alloc(len);
+ memmove(dbv->pszVal, p, copylen);
+ }
+ else
+ dbv->pszVal = 0;
+ break;
+ }
+ case DBVT_BLOB:
+ {
+ size_t len = sqlite3_column_bytes(set_stmts_prep[SQL_SET_STMT_READ], 1);
+
+ if (len) {
+ size_t copylen = isStatic ? (len < dbv->cpbVal ? len : dbv->cpbVal) : len;
+ if (!isStatic)
+ dbv->pbVal = (uint8_t*)mir_alloc(copylen);
+ memcpy(dbv->pbVal, sqlite3_column_blob(set_stmts_prep[SQL_SET_STMT_READ], 1), copylen);
+ dbv->cpbVal = (uint16_t)copylen;
+ }
+ else {
+ dbv = 0;
+ }
+ }
+ }
+ sql_reset(set_stmts_prep[SQL_SET_STMT_READ]);
+ // add to cache
+ if (dbv->type != DBVT_BLOB/* && dbv->type != DBVT_ENCRYPTED*/) {
+ pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 1);
+ if (pCachedValue != nullptr)
+ m_cache->SetCachedVariant(dbv, pCachedValue);
+ }
+
+ return 0;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param)
+{
+ if (szModule == nullptr)
+ return -1;
+
+ mir_cslockfull lock(m_csDbAccess);
+
+ int res = -1;
+ sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_ENUM], 1, hContact);
+ sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_ENUM], 2, szModule, -1, SQLITE_STATIC);
+ while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUM]) == SQLITE_ROW) {
+ const char *sczSetting = (const char*)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUM], 0);
+ if (sczSetting) {
+ lock.unlock();
+ res = (pfnEnumProc)(sczSetting, param);
+ lock.lock();
+ }
+ }
+ sql_reset(set_stmts_prep[SQL_SET_STMT_ENUM]);
+ return res;
+}
diff --git a/plugins/Import/src/dbrw/dbsql.cpp b/plugins/Import/src/dbrw/dbsql.cpp index 516f378435..3eaf3ef0d6 100644 --- a/plugins/Import/src/dbrw/dbsql.cpp +++ b/plugins/Import/src/dbrw/dbsql.cpp @@ -1,162 +1,162 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "..\stdafx.h" - -enum SQL -{ - SQL_STEP, - SQL_RESET, - SQL_EXEC, - SQL_OPEN, - SQL_CLOSE, - SQL_PREPARE, - SQL_FINALIZE -}; - -void CDbxSQLite::sql_prepare_add(char **text, sqlite3_stmt **stmts, int len) -{ - sql_prepare_text = (char**)mir_realloc(sql_prepare_text, (sql_prepare_len + len) * sizeof(char*)); - sql_prepare_stmt = (sqlite3_stmt***)mir_realloc(sql_prepare_stmt, (sql_prepare_len + len) * sizeof(sqlite3_stmt**)); - for (int i = 0; i < len; i++) { - sql_prepare_text[sql_prepare_len + i] = text[i]; - sql_prepare_stmt[sql_prepare_len + i] = &stmts[i]; - } - sql_prepare_len += len; -} - -void CDbxSQLite::sql_prepare_statements() -{ - for (int i = 0; i < sql_prepare_len; i++) - sql_prepare(m_sqlite, sql_prepare_text[i], sql_prepare_stmt[i]); -} - -void CALLBACK CDbxSQLite::sql_server_sync_apc(UINT_PTR dwParam) -{ - TSqlMessage *msg = (TSqlMessage*)dwParam; - - if (!msg) - return; - - switch (msg->op) { - case SQL_STEP: - msg->retCode = sqlite3_step(msg->pStmt); - break; - case SQL_RESET: - msg->retCode = sqlite3_reset(msg->pStmt); - break; - case SQL_EXEC: - msg->retCode = sqlite3_exec(msg->pDb, msg->zIn, NULL, NULL, NULL); - break; - case SQL_OPEN: - msg->retCode = sqlite3_open(msg->zIn, &msg->pDb); - break; - case SQL_CLOSE: - msg->retCode = sqlite3_close(msg->pDb); - break; - case SQL_PREPARE: - msg->retCode = sqlite3_prepare_v2(msg->pDb, msg->zIn, -1, &msg->pStmt, NULL); - break; - case SQL_FINALIZE: - msg->retCode = sqlite3_finalize(msg->pStmt); - break; - default: - return; - } - SetEvent(msg->hDoneEvent); -} - -void CDbxSQLite::sql_server_sync(TSqlMessage *msg) -{ - msg->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - sql_server_sync_apc((UINT_PTR)msg); - PostMessage(Miranda_GetSystemWindow()->GetHwnd(), WM_NULL, 0, 0); - WaitForSingleObject(msg->hDoneEvent, INFINITE); - CloseHandle(msg->hDoneEvent); -} - -int CDbxSQLite::sql_step(sqlite3_stmt *stmt) -{ - TSqlMessage msg; - msg.op = SQL_STEP; - msg.pStmt = stmt; - sql_server_sync(&msg); - return msg.retCode; -} - -int CDbxSQLite::sql_reset(sqlite3_stmt *stmt) -{ - TSqlMessage msg; - msg.op = SQL_RESET; - msg.pStmt = stmt; - sql_server_sync(&msg); - return msg.retCode; -} - -int CDbxSQLite::sql_exec(sqlite3 *sql, const char *query) -{ - TSqlMessage msg; - msg.op = SQL_EXEC; - msg.pDb = sql; - msg.zIn = query; - sql_server_sync(&msg); - return msg.retCode; -} - -int CDbxSQLite::sql_open(const char *path, sqlite3 **sql) -{ - TSqlMessage msg; - msg.op = SQL_OPEN; - msg.zIn = path; - sql_server_sync(&msg); - *sql = msg.pDb; - return msg.retCode; -} - -int CDbxSQLite::sql_close(sqlite3 *sql) -{ - TSqlMessage msg; - msg.op = SQL_CLOSE; - msg.pDb = sql; - sql_server_sync(&msg); - return msg.retCode; -} - -int CDbxSQLite::sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt) -{ - TSqlMessage msg; - msg.op = SQL_PREPARE; - msg.pDb = sql; - msg.zIn = query; - sql_server_sync(&msg); - *stmt = msg.pStmt; - return msg.retCode; -} - -int CDbxSQLite::sql_finalize(sqlite3_stmt *stmt) -{ - TSqlMessage msg; - msg.op = SQL_FINALIZE; - msg.pStmt = stmt; - sql_server_sync(&msg); - return msg.retCode; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "..\stdafx.h"
+
+enum SQL
+{
+ SQL_STEP,
+ SQL_RESET,
+ SQL_EXEC,
+ SQL_OPEN,
+ SQL_CLOSE,
+ SQL_PREPARE,
+ SQL_FINALIZE
+};
+
+void CDbxSQLite::sql_prepare_add(char **text, sqlite3_stmt **stmts, int len)
+{
+ sql_prepare_text = (char**)mir_realloc(sql_prepare_text, (sql_prepare_len + len) * sizeof(char*));
+ sql_prepare_stmt = (sqlite3_stmt***)mir_realloc(sql_prepare_stmt, (sql_prepare_len + len) * sizeof(sqlite3_stmt**));
+ for (int i = 0; i < len; i++) {
+ sql_prepare_text[sql_prepare_len + i] = text[i];
+ sql_prepare_stmt[sql_prepare_len + i] = &stmts[i];
+ }
+ sql_prepare_len += len;
+}
+
+void CDbxSQLite::sql_prepare_statements()
+{
+ for (int i = 0; i < sql_prepare_len; i++)
+ sql_prepare(m_sqlite, sql_prepare_text[i], sql_prepare_stmt[i]);
+}
+
+void CALLBACK CDbxSQLite::sql_server_sync_apc(UINT_PTR dwParam)
+{
+ TSqlMessage *msg = (TSqlMessage*)dwParam;
+
+ if (!msg)
+ return;
+
+ switch (msg->op) {
+ case SQL_STEP:
+ msg->retCode = sqlite3_step(msg->pStmt);
+ break;
+ case SQL_RESET:
+ msg->retCode = sqlite3_reset(msg->pStmt);
+ break;
+ case SQL_EXEC:
+ msg->retCode = sqlite3_exec(msg->pDb, msg->zIn, NULL, NULL, NULL);
+ break;
+ case SQL_OPEN:
+ msg->retCode = sqlite3_open(msg->zIn, &msg->pDb);
+ break;
+ case SQL_CLOSE:
+ msg->retCode = sqlite3_close(msg->pDb);
+ break;
+ case SQL_PREPARE:
+ msg->retCode = sqlite3_prepare_v2(msg->pDb, msg->zIn, -1, &msg->pStmt, NULL);
+ break;
+ case SQL_FINALIZE:
+ msg->retCode = sqlite3_finalize(msg->pStmt);
+ break;
+ default:
+ return;
+ }
+ SetEvent(msg->hDoneEvent);
+}
+
+void CDbxSQLite::sql_server_sync(TSqlMessage *msg)
+{
+ msg->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ sql_server_sync_apc((UINT_PTR)msg);
+ PostMessage(Miranda_GetSystemWindow()->GetHwnd(), WM_NULL, 0, 0);
+ WaitForSingleObject(msg->hDoneEvent, INFINITE);
+ CloseHandle(msg->hDoneEvent);
+}
+
+int CDbxSQLite::sql_step(sqlite3_stmt *stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_STEP;
+ msg.pStmt = stmt;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_reset(sqlite3_stmt *stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_RESET;
+ msg.pStmt = stmt;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_exec(sqlite3 *sql, const char *query)
+{
+ TSqlMessage msg;
+ msg.op = SQL_EXEC;
+ msg.pDb = sql;
+ msg.zIn = query;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_open(const char *path, sqlite3 **sql)
+{
+ TSqlMessage msg;
+ msg.op = SQL_OPEN;
+ msg.zIn = path;
+ sql_server_sync(&msg);
+ *sql = msg.pDb;
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_close(sqlite3 *sql)
+{
+ TSqlMessage msg;
+ msg.op = SQL_CLOSE;
+ msg.pDb = sql;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_PREPARE;
+ msg.pDb = sql;
+ msg.zIn = query;
+ sql_server_sync(&msg);
+ *stmt = msg.pStmt;
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_finalize(sqlite3_stmt *stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_FINALIZE;
+ msg.pStmt = stmt;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
diff --git a/plugins/Import/src/import.cpp b/plugins/Import/src/import.cpp index 865d786b93..17aeaa073a 100644 --- a/plugins/Import/src/import.cpp +++ b/plugins/Import/src/import.cpp @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/main.cpp b/plugins/Import/src/main.cpp index c800b5d611..f1c62ba97b 100644 --- a/plugins/Import/src/main.cpp +++ b/plugins/Import/src/main.cpp @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/mcontacts.cpp b/plugins/Import/src/mcontacts.cpp index cfa826769f..5eb09ed6aa 100644 --- a/plugins/Import/src/mcontacts.cpp +++ b/plugins/Import/src/mcontacts.cpp @@ -1,322 +1,322 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" - -#include <memory> -#include <vector> - -#define HEADER_STR "HB" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbxMcontacts database driver, read-only - -#pragma pack(push, 1) - -struct MC_FileHeader -{ - uint8_t signature[2]; - uint32_t version; - uint32_t dataSize; -}; - -struct MC_MsgHeader32 -{ - uint32_t cbSize; // size of the structure in bytes - uint32_t szModule; // pointer to name of the module that 'owns' this - // event, ie the one that is in control of the data format - uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until - // 2106 unless you use the standard C library which is - // signed and can only do until 2038. In GMT. - uint32_t flags; // the omnipresent flags - uint32_t eventType; // module-defined event type field - uint32_t cbBlob; // size of pBlob in bytes - uint32_t pBlob; // pointer to buffer containing module-defined event data -}; - -struct MC_MsgHeader64 -{ - uint64_t cbSize; // size of the structure in bytes - uint64_t szModule; // pointer to name of the module that 'owns' this - // event, ie the one that is in control of the data format - uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until - // 2106 unless you use the standard C library which is - // signed and can only do until 2038. In GMT. - uint32_t flags; // the omnipresent flags - uint32_t eventType; // module-defined event type field - uint32_t cbBlob; // size of pBlob in bytes - uint64_t pBlob; // pointer to buffer containing module-defined event data -}; - -#pragma pack(pop) - -class CDbxMc : public MDatabaseReadonly, public MZeroedObject -{ - HANDLE m_hFile = INVALID_HANDLE_VALUE; - - MC_FileHeader m_hdr; - - std::vector<uint32_t> m_events; - std::vector<uint32_t>::iterator m_curr; - - CMStringA readString() - { - CMStringA res; - char c; - DWORD dwRead; - while (ReadFile(m_hFile, &c, 1, &dwRead, 0)) { - if (c == 0) - break; - res.AppendChar(c); - } - return res; - } - -public: - CDbxMc() - {} - - ~CDbxMc() - { - if (m_hFile != INVALID_HANDLE_VALUE) - ::CloseHandle(m_hFile); - } - - void Load() - { - // mcontacts operates with the only contact with pseudo id=1 - m_cache->AddContactToCache(1); - - uint32_t pos = 0; - while (pos < m_hdr.dataSize) { - DWORD dwPos = SetFilePointer(m_hFile, 0, 0, FILE_CURRENT), dwRead, dwSize; - BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, 0); - if (!r || dwRead < sizeof(dwSize)) - return; - if (dwSize != sizeof(MC_MsgHeader32) && dwSize != sizeof(MC_MsgHeader64)) - return; - - m_events.push_back(dwPos); - SetFilePointer(m_hFile, -4, 0, FILE_CURRENT); - - if (dwSize == sizeof(MC_MsgHeader32)) { - MC_MsgHeader32 hdr; - r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0); - if (!r || dwRead < sizeof(hdr)) - return; - SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT); - } - else { - MC_MsgHeader64 hdr; - r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0); - if (!r || dwRead < sizeof(hdr)) - return; - SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT); - } - pos += dwSize; - } - } - - int Open(const wchar_t *profile) - { - m_hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0); - if (m_hFile == INVALID_HANDLE_VALUE) - return EGROKPRF_CANTREAD; - - DWORD dwRead; - BOOL r = ReadFile(m_hFile, &m_hdr, sizeof(m_hdr), &dwRead, nullptr); - if (!r) - return EGROKPRF_CANTREAD; - - return memcmp(&m_hdr.signature, HEADER_STR, 2) ? EGROKPRF_UNKHEADER : EGROKPRF_NOERROR; - } - - // mcontacts format always store history for one contact only - STDMETHODIMP_(int) GetBlobSize(MEVENT dwOffset) override - { - if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN)) - return 0; - - DWORD dwRead, dwSize; - BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr); - if (!r || dwRead != sizeof(dwSize)) - return 0; - - SetFilePointer(m_hFile, -4, 0, FILE_CURRENT); - - if (dwSize == sizeof(MC_MsgHeader32)) { - MC_MsgHeader32 hdr; - r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0); - if (!r || dwRead != sizeof(hdr)) - return 0; - return hdr.cbBlob+1; - } - if (dwSize == sizeof(MC_MsgHeader64)) { - MC_MsgHeader64 hdr; - r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0); - if (!r || dwRead != sizeof(hdr)) - return 0; - return hdr.cbBlob+1; - } - return 0; - } - - STDMETHODIMP_(int) GetContactCount(void) override - { - return 1; - } - - STDMETHODIMP_(int) GetEventCount(MCONTACT) override - { - return (int)m_events.size(); - } - - STDMETHODIMP_(BOOL) GetEvent(MEVENT dwOffset, DBEVENTINFO *dbei) override - { - if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN)) - return 1; - - DWORD dwRead, dwSize; - BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr); - if (!r || dwRead != sizeof(dwSize)) - return 1; - - SetFilePointer(m_hFile, -4, 0, FILE_CURRENT); - - int cbLen; - if (dwSize == sizeof(MC_MsgHeader32)) { - MC_MsgHeader32 hdr; - r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0); - if (!r || dwRead != sizeof(hdr)) - return 1; - - dbei->eventType = hdr.eventType; - cbLen = hdr.cbBlob; - dbei->flags = hdr.flags; - dbei->timestamp = hdr.timestamp; - } - else if (dwSize == sizeof(MC_MsgHeader64)) { - MC_MsgHeader64 hdr; - r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0); - if (!r || dwRead != sizeof(hdr)) - return 1; - - dbei->eventType = hdr.eventType; - cbLen = hdr.cbBlob; - dbei->flags = hdr.flags; - dbei->timestamp = hdr.timestamp; - } - else return 1; - - if (dbei->cbBlob && cbLen) { - uint32_t copySize = min(uint32_t(cbLen), dbei->cbBlob-1); - if (!ReadFile(m_hFile, dbei->pBlob, copySize, &dwRead, 0) || dwRead != copySize) - return 0; - - dbei->cbBlob = copySize; - dbei->pBlob[copySize] = 0; - } - - return 0; - } - - STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override - { - if (m_events.empty()) - return 0; - - m_curr = m_events.begin(); - return *m_curr; - } - - STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT) override - { - if (m_curr == m_events.end()) - return 0; - - ++m_curr; - return *m_curr; - } - - STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override - { - if (m_events.empty()) - return 0; - - m_curr = m_events.end(); - return *m_curr; - } - - STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT) override - { - if (m_curr == m_events.begin()) - return 0; - - --m_curr; - return *m_curr; - } - - STDMETHODIMP_(DATABASELINK *) GetDriver() override; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// database link functions - -static int mc_makeDatabase(const wchar_t*) -{ - return 1; -} - -static int mc_grokHeader(const wchar_t *profile) -{ - return CDbxMc().Open(profile); -} - -static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL) -{ - std::unique_ptr<CDbxMc> db(new CDbxMc()); - if (db->Open(profile)) - return nullptr; - - db->Load(); - return db.release(); -} - -static DATABASELINK dblink = -{ - 0, - "mcontacts", - L"mContacts file driver", - mc_makeDatabase, - mc_grokHeader, - mc_load -}; - -STDMETHODIMP_(DATABASELINK *) CDbxMc::GetDriver() -{ - return &dblink; -} - -void RegisterMContacts() -{ - RegisterDatabasePlugin(&dblink); -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <memory>
+#include <vector>
+
+#define HEADER_STR "HB"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbxMcontacts database driver, read-only
+
+#pragma pack(push, 1)
+
+struct MC_FileHeader
+{
+ uint8_t signature[2];
+ uint32_t version;
+ uint32_t dataSize;
+};
+
+struct MC_MsgHeader32
+{
+ uint32_t cbSize; // size of the structure in bytes
+ uint32_t szModule; // pointer to name of the module that 'owns' this
+ // event, ie the one that is in control of the data format
+ uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until
+ // 2106 unless you use the standard C library which is
+ // signed and can only do until 2038. In GMT.
+ uint32_t flags; // the omnipresent flags
+ uint32_t eventType; // module-defined event type field
+ uint32_t cbBlob; // size of pBlob in bytes
+ uint32_t pBlob; // pointer to buffer containing module-defined event data
+};
+
+struct MC_MsgHeader64
+{
+ uint64_t cbSize; // size of the structure in bytes
+ uint64_t szModule; // pointer to name of the module that 'owns' this
+ // event, ie the one that is in control of the data format
+ uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until
+ // 2106 unless you use the standard C library which is
+ // signed and can only do until 2038. In GMT.
+ uint32_t flags; // the omnipresent flags
+ uint32_t eventType; // module-defined event type field
+ uint32_t cbBlob; // size of pBlob in bytes
+ uint64_t pBlob; // pointer to buffer containing module-defined event data
+};
+
+#pragma pack(pop)
+
+class CDbxMc : public MDatabaseReadonly, public MZeroedObject
+{
+ HANDLE m_hFile = INVALID_HANDLE_VALUE;
+
+ MC_FileHeader m_hdr;
+
+ std::vector<uint32_t> m_events;
+ std::vector<uint32_t>::iterator m_curr;
+
+ CMStringA readString()
+ {
+ CMStringA res;
+ char c;
+ DWORD dwRead;
+ while (ReadFile(m_hFile, &c, 1, &dwRead, 0)) {
+ if (c == 0)
+ break;
+ res.AppendChar(c);
+ }
+ return res;
+ }
+
+public:
+ CDbxMc()
+ {}
+
+ ~CDbxMc()
+ {
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ ::CloseHandle(m_hFile);
+ }
+
+ void Load()
+ {
+ // mcontacts operates with the only contact with pseudo id=1
+ m_cache->AddContactToCache(1);
+
+ uint32_t pos = 0;
+ while (pos < m_hdr.dataSize) {
+ DWORD dwPos = SetFilePointer(m_hFile, 0, 0, FILE_CURRENT), dwRead, dwSize;
+ BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, 0);
+ if (!r || dwRead < sizeof(dwSize))
+ return;
+ if (dwSize != sizeof(MC_MsgHeader32) && dwSize != sizeof(MC_MsgHeader64))
+ return;
+
+ m_events.push_back(dwPos);
+ SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
+
+ if (dwSize == sizeof(MC_MsgHeader32)) {
+ MC_MsgHeader32 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead < sizeof(hdr))
+ return;
+ SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT);
+ }
+ else {
+ MC_MsgHeader64 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead < sizeof(hdr))
+ return;
+ SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT);
+ }
+ pos += dwSize;
+ }
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ m_hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ return EGROKPRF_CANTREAD;
+
+ DWORD dwRead;
+ BOOL r = ReadFile(m_hFile, &m_hdr, sizeof(m_hdr), &dwRead, nullptr);
+ if (!r)
+ return EGROKPRF_CANTREAD;
+
+ return memcmp(&m_hdr.signature, HEADER_STR, 2) ? EGROKPRF_UNKHEADER : EGROKPRF_NOERROR;
+ }
+
+ // mcontacts format always store history for one contact only
+ STDMETHODIMP_(int) GetBlobSize(MEVENT dwOffset) override
+ {
+ if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN))
+ return 0;
+
+ DWORD dwRead, dwSize;
+ BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr);
+ if (!r || dwRead != sizeof(dwSize))
+ return 0;
+
+ SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
+
+ if (dwSize == sizeof(MC_MsgHeader32)) {
+ MC_MsgHeader32 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 0;
+ return hdr.cbBlob+1;
+ }
+ if (dwSize == sizeof(MC_MsgHeader64)) {
+ MC_MsgHeader64 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 0;
+ return hdr.cbBlob+1;
+ }
+ return 0;
+ }
+
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return 1;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return (int)m_events.size();
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT dwOffset, DBEVENTINFO *dbei) override
+ {
+ if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN))
+ return 1;
+
+ DWORD dwRead, dwSize;
+ BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr);
+ if (!r || dwRead != sizeof(dwSize))
+ return 1;
+
+ SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
+
+ int cbLen;
+ if (dwSize == sizeof(MC_MsgHeader32)) {
+ MC_MsgHeader32 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 1;
+
+ dbei->eventType = hdr.eventType;
+ cbLen = hdr.cbBlob;
+ dbei->flags = hdr.flags;
+ dbei->timestamp = hdr.timestamp;
+ }
+ else if (dwSize == sizeof(MC_MsgHeader64)) {
+ MC_MsgHeader64 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 1;
+
+ dbei->eventType = hdr.eventType;
+ cbLen = hdr.cbBlob;
+ dbei->flags = hdr.flags;
+ dbei->timestamp = hdr.timestamp;
+ }
+ else return 1;
+
+ if (dbei->cbBlob && cbLen) {
+ uint32_t copySize = min(uint32_t(cbLen), dbei->cbBlob-1);
+ if (!ReadFile(m_hFile, dbei->pBlob, copySize, &dwRead, 0) || dwRead != copySize)
+ return 0;
+
+ dbei->cbBlob = copySize;
+ dbei->pBlob[copySize] = 0;
+ }
+
+ return 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override
+ {
+ if (m_events.empty())
+ return 0;
+
+ m_curr = m_events.begin();
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT) override
+ {
+ if (m_curr == m_events.end())
+ return 0;
+
+ ++m_curr;
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override
+ {
+ if (m_events.empty())
+ return 0;
+
+ m_curr = m_events.end();
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT) override
+ {
+ if (m_curr == m_events.begin())
+ return 0;
+
+ --m_curr;
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver() override;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// database link functions
+
+static int mc_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int mc_grokHeader(const wchar_t *profile)
+{
+ return CDbxMc().Open(profile);
+}
+
+static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxMc> db(new CDbxMc());
+ if (db->Open(profile))
+ return nullptr;
+
+ db->Load();
+ return db.release();
+}
+
+static DATABASELINK dblink =
+{
+ 0,
+ "mcontacts",
+ L"mContacts file driver",
+ mc_makeDatabase,
+ mc_grokHeader,
+ mc_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxMc::GetDriver()
+{
+ return &dblink;
+}
+
+void RegisterMContacts()
+{
+ RegisterDatabasePlugin(&dblink);
+}
diff --git a/plugins/Import/src/miranda.cpp b/plugins/Import/src/miranda.cpp index 7970027fe6..c43fc61a4d 100644 --- a/plugins/Import/src/miranda.cpp +++ b/plugins/Import/src/miranda.cpp @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/patterns.cpp b/plugins/Import/src/patterns.cpp index 094bf1f33c..9dadce018c 100644 --- a/plugins/Import/src/patterns.cpp +++ b/plugins/Import/src/patterns.cpp @@ -1,732 +1,732 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" - -#include <memory> -#include <vector> - -void CMPlugin::LoadPatterns() -{ - wchar_t wszPath[MAX_PATH], wszFullPath[MAX_PATH]; - GetModuleFileNameW(m_hInst, wszPath, _countof(wszPath)); - if (auto *p = wcsrchr(wszPath, '\\')) - *p = 0; - - mir_snwprintf(wszFullPath, L"%s\\Import\\*.ini", wszPath); - - WIN32_FIND_DATAW fd; - HANDLE hFind = FindFirstFileW(wszFullPath, &fd); - if (hFind == INVALID_HANDLE_VALUE) - return; - - do { - mir_snwprintf(wszFullPath, L"%s\\Import\\%s", wszPath, fd.cFileName); - LoadPattern(wszFullPath); - } - while (FindNextFileW(hFind, &fd) != 0); -} - -void CMPlugin::LoadPattern(const wchar_t *pwszFileName) -{ - // [General] section - wchar_t buf[1024]; - if (!GetPrivateProfileStringW(L"General", L"Name", L"", buf, _countof(buf), pwszFileName)) - return; - - std::unique_ptr<CImportPattern> pNew(new CImportPattern()); - pNew->wszName = buf; - pNew->iType = GetPrivateProfileIntW(L"General", L"Type", 1, pwszFileName); - - if (GetPrivateProfileStringW(L"General", L"DefaultExtension", L"", buf, _countof(buf), pwszFileName)) - pNew->wszExt = buf; - - if (pNew->iType == 1) { - if (GetPrivateProfileStringW(L"General", L"Charset", L"", buf, _countof(buf), pwszFileName)) { - if (!wcsicmp(buf, L"ANSI")) - pNew->iCodePage = GetPrivateProfileIntW(L"General", L"Codepage", CP_ACP, pwszFileName); - else if (!wcsicmp(buf, L"UCS2")) - pNew->iCodePage = 1200; - } - else return; - } - - pNew->iUseHeader = GetPrivateProfileIntW(L"General", L"UseHeader", 0, pwszFileName); - pNew->iUsePreMsg = GetPrivateProfileIntW(L"General", L"UsePreMsg", 0, pwszFileName); - pNew->iUseFilename = GetPrivateProfileIntW(L"General", L"UseFileName", 0, pwszFileName); - - // [Message] section - int erroffset; - const char *err; - if (pNew->iType == 1) { - if (GetPrivateProfileStringW(L"Message", L"Pattern", L"", buf, _countof(buf), pwszFileName)) { - if ((pNew->regMessage.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr) - return; - pNew->regMessage.extra = pcre16_study(pNew->regMessage.pattern, 0, &err); - } - else return; - - if (GetPrivateProfileStringW(L"Message", L"In", L"", buf, _countof(buf), pwszFileName)) - pNew->wszIncoming = buf; - if (GetPrivateProfileStringW(L"Message", L"Out", L"", buf, _countof(buf), pwszFileName)) - pNew->wszOutgoing = buf; - - pNew->iDirection = GetPrivateProfileIntW(L"Message", L"Direction", 0, pwszFileName); - pNew->iDay = GetPrivateProfileIntW(L"Message", L"Day", 0, pwszFileName); - pNew->iMonth = GetPrivateProfileIntW(L"Message", L"Month", 0, pwszFileName); - pNew->iYear = GetPrivateProfileIntW(L"Message", L"Year", 0, pwszFileName); - pNew->iHours = GetPrivateProfileIntW(L"Message", L"Hours", 0, pwszFileName); - pNew->iMinutes = GetPrivateProfileIntW(L"Message", L"Minutes", 0, pwszFileName); - pNew->iSeconds = GetPrivateProfileIntW(L"Message", L"Seconds", 0, pwszFileName); - } - - if (pNew->iUseHeader) { - if (GetPrivateProfileStringW(L"Header", L"Pattern", L"", buf, _countof(buf), pwszFileName)) { - if ((pNew->regHeader.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr) - return; - pNew->regHeader.extra = pcre16_study(pNew->regMessage.pattern, 0, &err); - } - else return; - - pNew->iHdrIncoming = GetPrivateProfileIntW(L"Header", L"In", 0, pwszFileName); - pNew->iHdrOutgoing = GetPrivateProfileIntW(L"Header", L"Out", 0, pwszFileName); - pNew->iHdrInNick = GetPrivateProfileIntW(L"Header", L"InNick", 0, pwszFileName); - pNew->iHdrOutNick = GetPrivateProfileIntW(L"Header", L"OutNick", 0, pwszFileName); - pNew->iHdrInUID = GetPrivateProfileIntW(L"Header", L"InUID", 0, pwszFileName); - pNew->iHdrOutUID = GetPrivateProfileIntW(L"Header", L"OutUID", 0, pwszFileName); - } - - if (pNew->iUsePreMsg) { - pNew->preRN = GetPrivateProfileIntW(L"PreMessage", L"PreRN", -1, pwszFileName); - pNew->preSP = GetPrivateProfileIntW(L"PreMessage", L"PreSP", 0, pwszFileName); - pNew->afterRN = GetPrivateProfileIntW(L"PreMessage", L"AfterRN", -1, pwszFileName); - pNew->afterSP = GetPrivateProfileIntW(L"PreMessage", L"AfterSP", 0, pwszFileName); - } - - if (pNew->iUseFilename) { - if (!GetPrivateProfileStringW(L"FileName", L"Pattern", L"", buf, _countof(buf), pwszFileName)) - return; - if ((pNew->regFilename.pattern = pcre16_compile(buf, 0, &err, &erroffset, nullptr)) == nullptr) - return; - pNew->regFilename.extra = pcre16_study(pNew->regFilename.pattern, 0, &err); - - pNew->iInNick = GetPrivateProfileIntW(L"FileName", L"InNick", 0, pwszFileName); - pNew->iInUID = GetPrivateProfileIntW(L"FileName", L"InUID", 0, pwszFileName); - pNew->iOutNick = GetPrivateProfileIntW(L"FileName", L"OutNick", 0, pwszFileName); - pNew->iOutUID = GetPrivateProfileIntW(L"FileName", L"OutUID", 0, pwszFileName); - } - - m_patterns.insert(pNew.release()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// pattern-based database driver - -class CDbxPattern : public MDatabaseReadonly, public MZeroedObject -{ - typedef MDatabaseReadonly CSuper; - - CMStringW m_buf, m_folder; - MCONTACT m_hCurrContact = INVALID_CONTACT_ID; - HANDLE m_hFile = INVALID_HANDLE_VALUE, m_hMap = 0; - const uint8_t *m_pFile = 0; - int m_iFileVersion = 0, m_iMsgHeaderSize = 0; - - std::vector<uint32_t> m_events; - std::vector<CMStringW> m_files; - - bool CheckContact(MCONTACT hContact) - { - if (hContact != m_hCurrContact) { - m_hCurrContact = hContact; - if (!Load(m_files[hContact - 1])) - return false; - } - return true; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // QHF file format - - bool LoadBinaryFile(const uint8_t *pFile, uint32_t iSize) - { - if (memicmp(pFile, "QHF", 3)) { - AddMessage(LPGENW("Invalid file header")); - return false; - } - - m_iFileVersion = pFile[3]; - - uint32_t fsz = RLInteger(&pFile[4]); pFile += 0x2C; - uint32_t UIDLen = RLWord(pFile); pFile += 2; - char *UIDStr = (char*)_alloca(UIDLen + 2); - if (m_iFileVersion <= 2) - strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen); - else - strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen + 1); - pFile += UIDLen; - - uint32_t NickLen = RLWord(pFile); pFile += 2; - char *NickStr = (char*)_alloca(NickLen + 2); - if (m_iFileVersion <= 2) - strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen); - else - strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen + 1); - pFile += NickLen; - - DBCONTACTWRITESETTING dbcws = {}; - dbcws.szModule = "Pattern"; - dbcws.value.type = DBVT_UTF8; - - dbcws.szSetting = "ID"; - dbcws.value.pszVal = UIDStr; - WriteContactSetting(m_hCurrContact, &dbcws); - - dbcws.szSetting = "Nick"; - dbcws.value.pszVal = NickStr; - WriteContactSetting(m_hCurrContact, &dbcws); - - uint32_t iHeaderSize = 0x30 + NickLen + UIDLen; - if (fsz != iSize - iHeaderSize) - fsz = iSize - iHeaderSize; - - m_iMsgHeaderSize = (m_iFileVersion >= 3) ? 0x23 : 0x21; - for (uint32_t i = 0; i < fsz; i += m_iMsgHeaderSize) { - m_events.push_back(i + iHeaderSize); - i += RLWord(&pFile[i + m_iMsgHeaderSize - 2]); - } - - return true; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // Text file format, parsed by regexps - - bool LoadTextFile(const uint8_t *pFile) - { - auto *pPattern = g_pBatch->m_pPattern; - - switch (pPattern->iCodePage) { - case CP_UTF8: - m_buf = mir_utf8decodeW((char*)pFile); - break; - case 1200: - m_buf = mir_wstrdup((wchar_t*)pFile); - break; - default: - m_buf = mir_a2u_cp((char*)pFile, pPattern->iCodePage); - break; - } - - - // smth went wrong or empty file - if (m_buf.IsEmpty()) - return false; - - int iOffset = 0; - if (m_buf[0] == 0xFEFF) - m_buf.Delete(0); - - if (pPattern->iUseHeader) { - int offsets[99]; - int nMatch = pcre16_exec(pPattern->regHeader.pattern, pPattern->regHeader.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets)); - if (nMatch <= 0) { - AddMessage(LPGENW("Cannot parse file header, skipping file")); - return false; - } - - const wchar_t **substrings; - if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) { - if (pPattern->iUseHeader & 1) { - pPattern->wszIncoming = substrings[pPattern->iHdrIncoming]; - pPattern->wszOutgoing = substrings[pPattern->iHdrOutgoing]; - } - - if (pPattern->iUseHeader & 2) { - DBCONTACTWRITESETTING dbcws = {}; - dbcws.szModule = "Pattern"; - dbcws.value.type = DBVT_WCHAR; - - if (pPattern->iInUID && substrings[pPattern->iHdrInUID]) { - dbcws.szSetting = "ID"; - dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInUID]; - WriteContactSetting(m_hCurrContact, &dbcws); - } - - if (pPattern->iInNick && substrings[pPattern->iHdrInNick]) { - dbcws.szSetting = "Nick"; - dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInNick]; - WriteContactSetting(m_hCurrContact, &dbcws); - } - } - } - - iOffset = offsets[1]; - } - - while (true) { - int offsets[99]; - int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets)); - if (nMatch <= 0) - break; - - m_events.push_back(offsets[0]); - iOffset = offsets[1]; - } - return true; - } - -public: - CDbxPattern() - {} - - ~CDbxPattern() - { - Close(); - } - - void Close() - { - if (m_pFile != nullptr) - ::UnmapViewOfFile(m_pFile); - - if (m_hMap != nullptr) - ::CloseHandle(m_hMap); - - if (m_hFile != INVALID_HANDLE_VALUE) - ::CloseHandle(m_hFile); - } - - bool Load(const wchar_t *pwszFileName) - { - m_buf.Empty(); - m_events.clear(); - Close(); - - AddMessage(LPGENW("Loading file '%s'..."), pwszFileName); - - m_hFile = ::CreateFileW(pwszFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0); - if (m_hFile == INVALID_HANDLE_VALUE) { - AddMessage(LPGENW("Failed to open file <%s> for import: %d"), pwszFileName, GetLastError()); - return false; - } - - uint32_t cbLen = ::GetFileSize(m_hFile, 0); - m_hMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, L"ImportMapfile"); - if (m_hMap == nullptr) { - AddMessage(LPGENW("Failed to mmap file <%s> for import: %d"), pwszFileName, GetLastError()); - return false; - } - - m_pFile = (const uint8_t*)::MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0); - if (m_pFile == nullptr) { - AddMessage(LPGENW("Failed to map view of file <%s> for import: %d"), pwszFileName, GetLastError()); - return false; - } - - if (g_pBatch->m_pPattern->iType == 1) // text file - return LoadTextFile(m_pFile); - return LoadBinaryFile(m_pFile, cbLen); - } - - int Open(const wchar_t *profile) - { - CMStringW wszBaseFolder(profile); - auto *pPattern = g_pBatch->m_pPattern; - - // create a mask for loading multiple data files for a folder - uint32_t dwAttr = GetFileAttributesW(profile); - if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { - wszBaseFolder = profile; - m_folder.AppendFormat(L"%s\\*.%s", profile, pPattern->wszExt.c_str()); - } - else { - int i = wszBaseFolder.ReverseFind('\\'); - if (i != -1) - wszBaseFolder = wszBaseFolder.Left(i); - m_folder = profile; - } - - int hContact = 1; - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile(m_folder, &fd); - if (hFind != INVALID_HANDLE_VALUE) { - do { - // find all subfolders except "." and ".." - if (!mir_wstrcmp(fd.cFileName, L".") || !mir_wstrcmp(fd.cFileName, L"..")) - continue; - - CMStringW wszFullName(wszBaseFolder + L"\\" + fd.cFileName); - m_files.push_back(wszFullName); - - auto *cc = m_cache->AddContactToCache(hContact); - cc->szProto = "Pattern"; - - // we try to restore user id from the file name - if (pPattern->iUseFilename) { - int offsets[100]; - int nMatch = pcre16_exec(pPattern->regFilename.pattern, pPattern->regFilename.extra, wszFullName, wszFullName.GetLength(), 0, 0, offsets, _countof(offsets)); - if (nMatch > 0) { - const wchar_t **substrings; - if (pcre16_get_substring_list(wszFullName, offsets, nMatch, &substrings) >= 0) { - DBCONTACTWRITESETTING dbcws = {}; - dbcws.szModule = cc->szProto; - dbcws.value.type = DBVT_WCHAR; - - if (pPattern->iInUID && substrings[pPattern->iInUID]) { - dbcws.szSetting = "ID"; - dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInUID]; - WriteContactSetting(hContact, &dbcws); - } - - if (pPattern->iInNick && substrings[pPattern->iInNick]) { - dbcws.szSetting = "Nick"; - dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInNick]; - WriteContactSetting(hContact, &dbcws); - } - - pcre16_free_substring_list(substrings); - } - } - } - hContact++; - } - while (FindNextFile(hFind, &fd)); - - FindClose(hFind); - } - - if (m_files.empty()) - return EGROKPRF_CANTREAD; - - return EGROKPRF_NOERROR; - } - - // patterns file always stores history for the single contact only - STDMETHODIMP_(int) GetBlobSize(MEVENT idx) override - { - if (m_events.size() == 0 || idx < 1 || idx > m_events.size()) - return 0; - - if (g_pBatch->m_pPattern->iType == 1) { - int iStart = m_events[idx-1], iEnd = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx]; - CMStringW msg = m_buf.Mid(iStart, iEnd - iStart); - return (LONG)mir_strlen(ptrA(mir_utf8encodeW(msg))) + 1; - } - - if (m_pFile == nullptr) - return 0; - - const uint8_t *pMsg = m_pFile + m_events[idx-1]; - return RLWord(&pMsg[m_iMsgHeaderSize - 2]) + 1; - } - - STDMETHODIMP_(int) GetContactCount(void) override - { - return (int)m_files.size(); - } - - STDMETHODIMP_(MCONTACT) FindFirstContact(const char *szProto) override - { - MCONTACT ret = CSuper::FindFirstContact(szProto); - if (ret != 0) - if (!CheckContact(ret)) - return 0; - return ret; - } - - STDMETHODIMP_(MCONTACT) FindNextContact(MCONTACT contactID, const char *szProto) override - { - MCONTACT ret = CSuper::FindNextContact(contactID, szProto); - if (ret != 0) - if (!CheckContact(ret)) - return 0; - return ret; - } - - STDMETHODIMP_(int) GetEventCount(MCONTACT) override - { - return (int)m_events.size(); - } - - STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT* dbv, int isStatic) - { - if (szSetting == nullptr || szModule == nullptr) - return 1; - - DBCachedContact *cc = nullptr; - if (hContact) { - cc = m_cache->GetCachedContact(hContact); - if (cc == nullptr) - return 1; - } - - size_t settingNameLen = strlen(szSetting); - size_t moduleNameLen = strlen(szModule); - char* szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); - - DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 0); - if (pCachedValue != nullptr) { - if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) { - int cbOrigLen = dbv->cchVal; - char *cbOrigPtr = dbv->pszVal; - memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); - if (isStatic) { - int cbLen = 0; - if (pCachedValue->pszVal != nullptr) - cbLen = (int)strlen(pCachedValue->pszVal); - - cbOrigLen--; - dbv->pszVal = cbOrigPtr; - if (cbLen < cbOrigLen) - cbOrigLen = cbLen; - memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen); - dbv->pszVal[cbOrigLen] = 0; - dbv->cchVal = cbLen; - } - else { - dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1); - strcpy(dbv->pszVal, pCachedValue->pszVal); - } - } - else memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); - - return (pCachedValue->type == DBVT_DELETED) ? 1 : 0; - } - - return 1; - } - - STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws) - { - if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr) - return 1; - - if (hContact) { - DBCachedContact* cc = m_cache->GetCachedContact(hContact); - if (cc == nullptr) - return 1; - } - - DBCONTACTWRITESETTING dbcwWork = *dbcws; - if (dbcwWork.value.type == DBVT_WCHAR) { - T2Utf value(dbcwWork.value.pwszVal); - dbcwWork.value.pszVal = NEWSTR_ALLOCA(value); - dbcwWork.value.type = DBVT_UTF8; - dbcwWork.value.cchVal = (uint16_t)strlen(dbcwWork.value.pszVal); - } - - char* cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting)); - DBVARIANT* cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1); - if (cachedValue != nullptr) - m_cache->SetCachedVariant(&dbcwWork.value, cachedValue); - - return 0; - } - - //////////////////////////////////////////////////////////////////////////////////////// - - static int str2int(const wchar_t* str) - { - if (str == nullptr || *str == 0) - return 0; - - return _wtoi(str); - } - - int getBinaryEvent(MEVENT idx, DBEVENTINFO *dbei) - { - if (m_pFile == nullptr) - return 1; - - const uint8_t *pMsg = m_pFile + m_events[idx-1]; - - dbei->eventType = EVENTTYPE_MESSAGE; - dbei->flags = DBEF_READ | DBEF_UTF; - if (pMsg[0x1A] != 0) - dbei->flags |= DBEF_SENT; - dbei->timestamp = RLInteger(&pMsg[0x12]); - dbei->timestamp -= TimeZone_ToLocal(dbei->timestamp) - dbei->timestamp; // deduct time zone offset from timestamp - dbei->cbBlob = RLWord(&pMsg[m_iMsgHeaderSize - 2]); - dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob + 1); - memcpy(dbei->pBlob, pMsg + m_iMsgHeaderSize, dbei->cbBlob); - if (m_iFileVersion != 1) - for (int i = 0; i < dbei->cbBlob; i++) { - dbei->pBlob[i] += i+1; - dbei->pBlob[i] = 255 - dbei->pBlob[i]; - } - - dbei->pBlob[dbei->cbBlob] = 0; - return 0; - } - - int getTextEvent(MEVENT idx, DBEVENTINFO *dbei) - { - auto *pPattern = g_pBatch->m_pPattern; - - int offsets[99]; - int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), m_events[idx-1], PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets)); - if (nMatch <= 0) - return 1; - - dbei->eventType = EVENTTYPE_MESSAGE; - dbei->flags = DBEF_READ | DBEF_UTF; - - int h1 = offsets[1], h2 = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx]; - int prn = -1, arn = -1; - if (pPattern->iUsePreMsg) - prn = pPattern->preRN, arn = pPattern->afterRN; - - if (prn != 0) { - int i = 0; - while (m_buf[h1] == '\r' && m_buf[h1 + 1] == '\n' && i < prn) - h1 += 2, i++; - } - - if (arn != 0) { - int i = 0; - while (m_buf[h2 - 2] == '\r' && m_buf[h2 - 1] == '\n' && i < arn) - h2 -= 2, i++; - } - - if (dbei->cbBlob) { - CMStringW wszBody = m_buf.Mid(h1, h2 - h1).Trim(); - if (!wszBody.IsEmpty()) { - ptrA tmp(mir_utf8encodeW(wszBody)); - int copySize = min(dbei->cbBlob - 1, (int)mir_strlen(tmp)); - memcpy(dbei->pBlob, tmp, copySize); - dbei->pBlob[copySize] = 0; - dbei->cbBlob = copySize; - } - else dbei->cbBlob = 0; - } - - const wchar_t **substrings; - if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) { - struct tm st = {}; - st.tm_year = str2int(substrings[pPattern->iYear]); - if (st.tm_year > 1900) - st.tm_year -= 1900; - st.tm_mon = str2int(substrings[pPattern->iMonth]) - 1; - st.tm_mday = str2int(substrings[pPattern->iDay]); - st.tm_hour = str2int(substrings[pPattern->iHours]); - st.tm_min = str2int(substrings[pPattern->iMinutes]); - st.tm_sec = (pPattern->iSeconds) ? str2int(substrings[pPattern->iSeconds]) : 0; - dbei->timestamp = mktime(&st); - - if (pPattern->iDirection) - if (pPattern->wszOutgoing == substrings[pPattern->iDirection]) - dbei->flags |= DBEF_SENT; - - - pcre16_free_substring_list(substrings); - } - return 0; - } - - STDMETHODIMP_(BOOL) GetEvent(MEVENT idx, DBEVENTINFO *dbei) override - { - if (dbei == nullptr || m_events.size() == 0 || idx < 1 || idx > m_events.size()) - return 1; - - if (g_pBatch->m_pPattern->iType == 1) - return getTextEvent(idx, dbei); - - return getBinaryEvent(idx, dbei); - } - - STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT hContact) override - { - // no system history - if (hContact == 0) - return 0; - - if (!CheckContact(hContact)) - return 0; - - return m_events.size() > 0 ? 1 : 0; - } - - STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT idx) override - { - if (idx >= m_events.size()) - return 0; - - return idx + 1; - } - - STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT hContact) override - { - // no system history - if (hContact == 0) - return 0; - - if (!CheckContact(hContact)) - return 0; - - return m_events.size() > 0 ? (MEVENT)m_events.size() : 0; - } - - STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT idx) override - { - return (idx >= 1) ? idx-1 : 0; - } - - STDMETHODIMP_(DATABASELINK *) GetDriver(); -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// database link functions - -static int pattern_makeDatabase(const wchar_t*) -{ - return 1; -} - -static int pattern_grokHeader(const wchar_t *profile) -{ - return CDbxPattern().Open(profile); -} - -static MDatabaseCommon* pattern_load(const wchar_t *profile, BOOL) -{ - std::unique_ptr<CDbxPattern> db(new CDbxPattern()); - if (db->Open(profile)) - return nullptr; - - return db.release(); -} - -DATABASELINK g_patternDbLink = -{ - 0, - "pattern", - L"Pattern-based file driver", - pattern_makeDatabase, - pattern_grokHeader, - pattern_load -}; - -STDMETHODIMP_(DATABASELINK *) CDbxPattern::GetDriver() -{ - return &g_patternDbLink; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <memory>
+#include <vector>
+
+void CMPlugin::LoadPatterns()
+{
+ wchar_t wszPath[MAX_PATH], wszFullPath[MAX_PATH];
+ GetModuleFileNameW(m_hInst, wszPath, _countof(wszPath));
+ if (auto *p = wcsrchr(wszPath, '\\'))
+ *p = 0;
+
+ mir_snwprintf(wszFullPath, L"%s\\Import\\*.ini", wszPath);
+
+ WIN32_FIND_DATAW fd;
+ HANDLE hFind = FindFirstFileW(wszFullPath, &fd);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do {
+ mir_snwprintf(wszFullPath, L"%s\\Import\\%s", wszPath, fd.cFileName);
+ LoadPattern(wszFullPath);
+ }
+ while (FindNextFileW(hFind, &fd) != 0);
+}
+
+void CMPlugin::LoadPattern(const wchar_t *pwszFileName)
+{
+ // [General] section
+ wchar_t buf[1024];
+ if (!GetPrivateProfileStringW(L"General", L"Name", L"", buf, _countof(buf), pwszFileName))
+ return;
+
+ std::unique_ptr<CImportPattern> pNew(new CImportPattern());
+ pNew->wszName = buf;
+ pNew->iType = GetPrivateProfileIntW(L"General", L"Type", 1, pwszFileName);
+
+ if (GetPrivateProfileStringW(L"General", L"DefaultExtension", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszExt = buf;
+
+ if (pNew->iType == 1) {
+ if (GetPrivateProfileStringW(L"General", L"Charset", L"", buf, _countof(buf), pwszFileName)) {
+ if (!wcsicmp(buf, L"ANSI"))
+ pNew->iCodePage = GetPrivateProfileIntW(L"General", L"Codepage", CP_ACP, pwszFileName);
+ else if (!wcsicmp(buf, L"UCS2"))
+ pNew->iCodePage = 1200;
+ }
+ else return;
+ }
+
+ pNew->iUseHeader = GetPrivateProfileIntW(L"General", L"UseHeader", 0, pwszFileName);
+ pNew->iUsePreMsg = GetPrivateProfileIntW(L"General", L"UsePreMsg", 0, pwszFileName);
+ pNew->iUseFilename = GetPrivateProfileIntW(L"General", L"UseFileName", 0, pwszFileName);
+
+ // [Message] section
+ int erroffset;
+ const char *err;
+ if (pNew->iType == 1) {
+ if (GetPrivateProfileStringW(L"Message", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
+ if ((pNew->regMessage.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regMessage.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
+ }
+ else return;
+
+ if (GetPrivateProfileStringW(L"Message", L"In", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszIncoming = buf;
+ if (GetPrivateProfileStringW(L"Message", L"Out", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszOutgoing = buf;
+
+ pNew->iDirection = GetPrivateProfileIntW(L"Message", L"Direction", 0, pwszFileName);
+ pNew->iDay = GetPrivateProfileIntW(L"Message", L"Day", 0, pwszFileName);
+ pNew->iMonth = GetPrivateProfileIntW(L"Message", L"Month", 0, pwszFileName);
+ pNew->iYear = GetPrivateProfileIntW(L"Message", L"Year", 0, pwszFileName);
+ pNew->iHours = GetPrivateProfileIntW(L"Message", L"Hours", 0, pwszFileName);
+ pNew->iMinutes = GetPrivateProfileIntW(L"Message", L"Minutes", 0, pwszFileName);
+ pNew->iSeconds = GetPrivateProfileIntW(L"Message", L"Seconds", 0, pwszFileName);
+ }
+
+ if (pNew->iUseHeader) {
+ if (GetPrivateProfileStringW(L"Header", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
+ if ((pNew->regHeader.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regHeader.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
+ }
+ else return;
+
+ pNew->iHdrIncoming = GetPrivateProfileIntW(L"Header", L"In", 0, pwszFileName);
+ pNew->iHdrOutgoing = GetPrivateProfileIntW(L"Header", L"Out", 0, pwszFileName);
+ pNew->iHdrInNick = GetPrivateProfileIntW(L"Header", L"InNick", 0, pwszFileName);
+ pNew->iHdrOutNick = GetPrivateProfileIntW(L"Header", L"OutNick", 0, pwszFileName);
+ pNew->iHdrInUID = GetPrivateProfileIntW(L"Header", L"InUID", 0, pwszFileName);
+ pNew->iHdrOutUID = GetPrivateProfileIntW(L"Header", L"OutUID", 0, pwszFileName);
+ }
+
+ if (pNew->iUsePreMsg) {
+ pNew->preRN = GetPrivateProfileIntW(L"PreMessage", L"PreRN", -1, pwszFileName);
+ pNew->preSP = GetPrivateProfileIntW(L"PreMessage", L"PreSP", 0, pwszFileName);
+ pNew->afterRN = GetPrivateProfileIntW(L"PreMessage", L"AfterRN", -1, pwszFileName);
+ pNew->afterSP = GetPrivateProfileIntW(L"PreMessage", L"AfterSP", 0, pwszFileName);
+ }
+
+ if (pNew->iUseFilename) {
+ if (!GetPrivateProfileStringW(L"FileName", L"Pattern", L"", buf, _countof(buf), pwszFileName))
+ return;
+ if ((pNew->regFilename.pattern = pcre16_compile(buf, 0, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regFilename.extra = pcre16_study(pNew->regFilename.pattern, 0, &err);
+
+ pNew->iInNick = GetPrivateProfileIntW(L"FileName", L"InNick", 0, pwszFileName);
+ pNew->iInUID = GetPrivateProfileIntW(L"FileName", L"InUID", 0, pwszFileName);
+ pNew->iOutNick = GetPrivateProfileIntW(L"FileName", L"OutNick", 0, pwszFileName);
+ pNew->iOutUID = GetPrivateProfileIntW(L"FileName", L"OutUID", 0, pwszFileName);
+ }
+
+ m_patterns.insert(pNew.release());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// pattern-based database driver
+
+class CDbxPattern : public MDatabaseReadonly, public MZeroedObject
+{
+ typedef MDatabaseReadonly CSuper;
+
+ CMStringW m_buf, m_folder;
+ MCONTACT m_hCurrContact = INVALID_CONTACT_ID;
+ HANDLE m_hFile = INVALID_HANDLE_VALUE, m_hMap = 0;
+ const uint8_t *m_pFile = 0;
+ int m_iFileVersion = 0, m_iMsgHeaderSize = 0;
+
+ std::vector<uint32_t> m_events;
+ std::vector<CMStringW> m_files;
+
+ bool CheckContact(MCONTACT hContact)
+ {
+ if (hContact != m_hCurrContact) {
+ m_hCurrContact = hContact;
+ if (!Load(m_files[hContact - 1]))
+ return false;
+ }
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // QHF file format
+
+ bool LoadBinaryFile(const uint8_t *pFile, uint32_t iSize)
+ {
+ if (memicmp(pFile, "QHF", 3)) {
+ AddMessage(LPGENW("Invalid file header"));
+ return false;
+ }
+
+ m_iFileVersion = pFile[3];
+
+ uint32_t fsz = RLInteger(&pFile[4]); pFile += 0x2C;
+ uint32_t UIDLen = RLWord(pFile); pFile += 2;
+ char *UIDStr = (char*)_alloca(UIDLen + 2);
+ if (m_iFileVersion <= 2)
+ strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen);
+ else
+ strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen + 1);
+ pFile += UIDLen;
+
+ uint32_t NickLen = RLWord(pFile); pFile += 2;
+ char *NickStr = (char*)_alloca(NickLen + 2);
+ if (m_iFileVersion <= 2)
+ strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen);
+ else
+ strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen + 1);
+ pFile += NickLen;
+
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = "Pattern";
+ dbcws.value.type = DBVT_UTF8;
+
+ dbcws.szSetting = "ID";
+ dbcws.value.pszVal = UIDStr;
+ WriteContactSetting(m_hCurrContact, &dbcws);
+
+ dbcws.szSetting = "Nick";
+ dbcws.value.pszVal = NickStr;
+ WriteContactSetting(m_hCurrContact, &dbcws);
+
+ uint32_t iHeaderSize = 0x30 + NickLen + UIDLen;
+ if (fsz != iSize - iHeaderSize)
+ fsz = iSize - iHeaderSize;
+
+ m_iMsgHeaderSize = (m_iFileVersion >= 3) ? 0x23 : 0x21;
+ for (uint32_t i = 0; i < fsz; i += m_iMsgHeaderSize) {
+ m_events.push_back(i + iHeaderSize);
+ i += RLWord(&pFile[i + m_iMsgHeaderSize - 2]);
+ }
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Text file format, parsed by regexps
+
+ bool LoadTextFile(const uint8_t *pFile)
+ {
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ switch (pPattern->iCodePage) {
+ case CP_UTF8:
+ m_buf = mir_utf8decodeW((char*)pFile);
+ break;
+ case 1200:
+ m_buf = mir_wstrdup((wchar_t*)pFile);
+ break;
+ default:
+ m_buf = mir_a2u_cp((char*)pFile, pPattern->iCodePage);
+ break;
+ }
+
+
+ // smth went wrong or empty file
+ if (m_buf.IsEmpty())
+ return false;
+
+ int iOffset = 0;
+ if (m_buf[0] == 0xFEFF)
+ m_buf.Delete(0);
+
+ if (pPattern->iUseHeader) {
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regHeader.pattern, pPattern->regHeader.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0) {
+ AddMessage(LPGENW("Cannot parse file header, skipping file"));
+ return false;
+ }
+
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
+ if (pPattern->iUseHeader & 1) {
+ pPattern->wszIncoming = substrings[pPattern->iHdrIncoming];
+ pPattern->wszOutgoing = substrings[pPattern->iHdrOutgoing];
+ }
+
+ if (pPattern->iUseHeader & 2) {
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = "Pattern";
+ dbcws.value.type = DBVT_WCHAR;
+
+ if (pPattern->iInUID && substrings[pPattern->iHdrInUID]) {
+ dbcws.szSetting = "ID";
+ dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInUID];
+ WriteContactSetting(m_hCurrContact, &dbcws);
+ }
+
+ if (pPattern->iInNick && substrings[pPattern->iHdrInNick]) {
+ dbcws.szSetting = "Nick";
+ dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInNick];
+ WriteContactSetting(m_hCurrContact, &dbcws);
+ }
+ }
+ }
+
+ iOffset = offsets[1];
+ }
+
+ while (true) {
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0)
+ break;
+
+ m_events.push_back(offsets[0]);
+ iOffset = offsets[1];
+ }
+ return true;
+ }
+
+public:
+ CDbxPattern()
+ {}
+
+ ~CDbxPattern()
+ {
+ Close();
+ }
+
+ void Close()
+ {
+ if (m_pFile != nullptr)
+ ::UnmapViewOfFile(m_pFile);
+
+ if (m_hMap != nullptr)
+ ::CloseHandle(m_hMap);
+
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ ::CloseHandle(m_hFile);
+ }
+
+ bool Load(const wchar_t *pwszFileName)
+ {
+ m_buf.Empty();
+ m_events.clear();
+ Close();
+
+ AddMessage(LPGENW("Loading file '%s'..."), pwszFileName);
+
+ m_hFile = ::CreateFileW(pwszFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0);
+ if (m_hFile == INVALID_HANDLE_VALUE) {
+ AddMessage(LPGENW("Failed to open file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ uint32_t cbLen = ::GetFileSize(m_hFile, 0);
+ m_hMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, L"ImportMapfile");
+ if (m_hMap == nullptr) {
+ AddMessage(LPGENW("Failed to mmap file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ m_pFile = (const uint8_t*)::MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0);
+ if (m_pFile == nullptr) {
+ AddMessage(LPGENW("Failed to map view of file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ if (g_pBatch->m_pPattern->iType == 1) // text file
+ return LoadTextFile(m_pFile);
+ return LoadBinaryFile(m_pFile, cbLen);
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ CMStringW wszBaseFolder(profile);
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ // create a mask for loading multiple data files for a folder
+ uint32_t dwAttr = GetFileAttributesW(profile);
+ if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ wszBaseFolder = profile;
+ m_folder.AppendFormat(L"%s\\*.%s", profile, pPattern->wszExt.c_str());
+ }
+ else {
+ int i = wszBaseFolder.ReverseFind('\\');
+ if (i != -1)
+ wszBaseFolder = wszBaseFolder.Left(i);
+ m_folder = profile;
+ }
+
+ int hContact = 1;
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(m_folder, &fd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ // find all subfolders except "." and ".."
+ if (!mir_wstrcmp(fd.cFileName, L".") || !mir_wstrcmp(fd.cFileName, L".."))
+ continue;
+
+ CMStringW wszFullName(wszBaseFolder + L"\\" + fd.cFileName);
+ m_files.push_back(wszFullName);
+
+ auto *cc = m_cache->AddContactToCache(hContact);
+ cc->szProto = "Pattern";
+
+ // we try to restore user id from the file name
+ if (pPattern->iUseFilename) {
+ int offsets[100];
+ int nMatch = pcre16_exec(pPattern->regFilename.pattern, pPattern->regFilename.extra, wszFullName, wszFullName.GetLength(), 0, 0, offsets, _countof(offsets));
+ if (nMatch > 0) {
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(wszFullName, offsets, nMatch, &substrings) >= 0) {
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = cc->szProto;
+ dbcws.value.type = DBVT_WCHAR;
+
+ if (pPattern->iInUID && substrings[pPattern->iInUID]) {
+ dbcws.szSetting = "ID";
+ dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInUID];
+ WriteContactSetting(hContact, &dbcws);
+ }
+
+ if (pPattern->iInNick && substrings[pPattern->iInNick]) {
+ dbcws.szSetting = "Nick";
+ dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInNick];
+ WriteContactSetting(hContact, &dbcws);
+ }
+
+ pcre16_free_substring_list(substrings);
+ }
+ }
+ }
+ hContact++;
+ }
+ while (FindNextFile(hFind, &fd));
+
+ FindClose(hFind);
+ }
+
+ if (m_files.empty())
+ return EGROKPRF_CANTREAD;
+
+ return EGROKPRF_NOERROR;
+ }
+
+ // patterns file always stores history for the single contact only
+ STDMETHODIMP_(int) GetBlobSize(MEVENT idx) override
+ {
+ if (m_events.size() == 0 || idx < 1 || idx > m_events.size())
+ return 0;
+
+ if (g_pBatch->m_pPattern->iType == 1) {
+ int iStart = m_events[idx-1], iEnd = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
+ CMStringW msg = m_buf.Mid(iStart, iEnd - iStart);
+ return (LONG)mir_strlen(ptrA(mir_utf8encodeW(msg))) + 1;
+ }
+
+ if (m_pFile == nullptr)
+ return 0;
+
+ const uint8_t *pMsg = m_pFile + m_events[idx-1];
+ return RLWord(&pMsg[m_iMsgHeaderSize - 2]) + 1;
+ }
+
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return (int)m_files.size();
+ }
+
+ STDMETHODIMP_(MCONTACT) FindFirstContact(const char *szProto) override
+ {
+ MCONTACT ret = CSuper::FindFirstContact(szProto);
+ if (ret != 0)
+ if (!CheckContact(ret))
+ return 0;
+ return ret;
+ }
+
+ STDMETHODIMP_(MCONTACT) FindNextContact(MCONTACT contactID, const char *szProto) override
+ {
+ MCONTACT ret = CSuper::FindNextContact(contactID, szProto);
+ if (ret != 0)
+ if (!CheckContact(ret))
+ return 0;
+ return ret;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return (int)m_events.size();
+ }
+
+ STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT* dbv, int isStatic)
+ {
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ DBCachedContact *cc = nullptr;
+ if (hContact) {
+ cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ size_t settingNameLen = strlen(szSetting);
+ size_t moduleNameLen = strlen(szModule);
+ char* szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
+
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
+ strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ return 1;
+ }
+
+ STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws)
+ {
+ if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact* cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ DBCONTACTWRITESETTING dbcwWork = *dbcws;
+ if (dbcwWork.value.type == DBVT_WCHAR) {
+ T2Utf value(dbcwWork.value.pwszVal);
+ dbcwWork.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwWork.value.type = DBVT_UTF8;
+ dbcwWork.value.cchVal = (uint16_t)strlen(dbcwWork.value.pszVal);
+ }
+
+ char* cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting));
+ DBVARIANT* cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (cachedValue != nullptr)
+ m_cache->SetCachedVariant(&dbcwWork.value, cachedValue);
+
+ return 0;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ static int str2int(const wchar_t* str)
+ {
+ if (str == nullptr || *str == 0)
+ return 0;
+
+ return _wtoi(str);
+ }
+
+ int getBinaryEvent(MEVENT idx, DBEVENTINFO *dbei)
+ {
+ if (m_pFile == nullptr)
+ return 1;
+
+ const uint8_t *pMsg = m_pFile + m_events[idx-1];
+
+ dbei->eventType = EVENTTYPE_MESSAGE;
+ dbei->flags = DBEF_READ | DBEF_UTF;
+ if (pMsg[0x1A] != 0)
+ dbei->flags |= DBEF_SENT;
+ dbei->timestamp = RLInteger(&pMsg[0x12]);
+ dbei->timestamp -= TimeZone_ToLocal(dbei->timestamp) - dbei->timestamp; // deduct time zone offset from timestamp
+ dbei->cbBlob = RLWord(&pMsg[m_iMsgHeaderSize - 2]);
+ dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob + 1);
+ memcpy(dbei->pBlob, pMsg + m_iMsgHeaderSize, dbei->cbBlob);
+ if (m_iFileVersion != 1)
+ for (int i = 0; i < dbei->cbBlob; i++) {
+ dbei->pBlob[i] += i+1;
+ dbei->pBlob[i] = 255 - dbei->pBlob[i];
+ }
+
+ dbei->pBlob[dbei->cbBlob] = 0;
+ return 0;
+ }
+
+ int getTextEvent(MEVENT idx, DBEVENTINFO *dbei)
+ {
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), m_events[idx-1], PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0)
+ return 1;
+
+ dbei->eventType = EVENTTYPE_MESSAGE;
+ dbei->flags = DBEF_READ | DBEF_UTF;
+
+ int h1 = offsets[1], h2 = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
+ int prn = -1, arn = -1;
+ if (pPattern->iUsePreMsg)
+ prn = pPattern->preRN, arn = pPattern->afterRN;
+
+ if (prn != 0) {
+ int i = 0;
+ while (m_buf[h1] == '\r' && m_buf[h1 + 1] == '\n' && i < prn)
+ h1 += 2, i++;
+ }
+
+ if (arn != 0) {
+ int i = 0;
+ while (m_buf[h2 - 2] == '\r' && m_buf[h2 - 1] == '\n' && i < arn)
+ h2 -= 2, i++;
+ }
+
+ if (dbei->cbBlob) {
+ CMStringW wszBody = m_buf.Mid(h1, h2 - h1).Trim();
+ if (!wszBody.IsEmpty()) {
+ ptrA tmp(mir_utf8encodeW(wszBody));
+ int copySize = min(dbei->cbBlob - 1, (int)mir_strlen(tmp));
+ memcpy(dbei->pBlob, tmp, copySize);
+ dbei->pBlob[copySize] = 0;
+ dbei->cbBlob = copySize;
+ }
+ else dbei->cbBlob = 0;
+ }
+
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
+ struct tm st = {};
+ st.tm_year = str2int(substrings[pPattern->iYear]);
+ if (st.tm_year > 1900)
+ st.tm_year -= 1900;
+ st.tm_mon = str2int(substrings[pPattern->iMonth]) - 1;
+ st.tm_mday = str2int(substrings[pPattern->iDay]);
+ st.tm_hour = str2int(substrings[pPattern->iHours]);
+ st.tm_min = str2int(substrings[pPattern->iMinutes]);
+ st.tm_sec = (pPattern->iSeconds) ? str2int(substrings[pPattern->iSeconds]) : 0;
+ dbei->timestamp = mktime(&st);
+
+ if (pPattern->iDirection)
+ if (pPattern->wszOutgoing == substrings[pPattern->iDirection])
+ dbei->flags |= DBEF_SENT;
+
+
+ pcre16_free_substring_list(substrings);
+ }
+ return 0;
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT idx, DBEVENTINFO *dbei) override
+ {
+ if (dbei == nullptr || m_events.size() == 0 || idx < 1 || idx > m_events.size())
+ return 1;
+
+ if (g_pBatch->m_pPattern->iType == 1)
+ return getTextEvent(idx, dbei);
+
+ return getBinaryEvent(idx, dbei);
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT hContact) override
+ {
+ // no system history
+ if (hContact == 0)
+ return 0;
+
+ if (!CheckContact(hContact))
+ return 0;
+
+ return m_events.size() > 0 ? 1 : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT idx) override
+ {
+ if (idx >= m_events.size())
+ return 0;
+
+ return idx + 1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT hContact) override
+ {
+ // no system history
+ if (hContact == 0)
+ return 0;
+
+ if (!CheckContact(hContact))
+ return 0;
+
+ return m_events.size() > 0 ? (MEVENT)m_events.size() : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT idx) override
+ {
+ return (idx >= 1) ? idx-1 : 0;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// database link functions
+
+static int pattern_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int pattern_grokHeader(const wchar_t *profile)
+{
+ return CDbxPattern().Open(profile);
+}
+
+static MDatabaseCommon* pattern_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxPattern> db(new CDbxPattern());
+ if (db->Open(profile))
+ return nullptr;
+
+ return db.release();
+}
+
+DATABASELINK g_patternDbLink =
+{
+ 0,
+ "pattern",
+ L"Pattern-based file driver",
+ pattern_makeDatabase,
+ pattern_grokHeader,
+ pattern_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxPattern::GetDriver()
+{
+ return &g_patternDbLink;
+}
diff --git a/plugins/Import/src/progress.cpp b/plugins/Import/src/progress.cpp index b602d0a655..f9ca110438 100644 --- a/plugins/Import/src/progress.cpp +++ b/plugins/Import/src/progress.cpp @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/stdafx.cxx b/plugins/Import/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Import/src/stdafx.cxx +++ b/plugins/Import/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/stdafx.h b/plugins/Import/src/stdafx.h index dccb07afc5..b60d8402a5 100644 --- a/plugins/Import/src/stdafx.h +++ b/plugins/Import/src/stdafx.h @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/textjson.cpp b/plugins/Import/src/textjson.cpp index 22cab70e2b..d419807ac0 100644 --- a/plugins/Import/src/textjson.cpp +++ b/plugins/Import/src/textjson.cpp @@ -1,269 +1,269 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" - -#include <m_json.h> - -static int mc_makeDatabase(const wchar_t*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JSON text driver, read-only - -static int CompareModules(const char *p1, const char *p2) -{ - return mir_strcmp(p1, p2); -} - -class CDbxJson : public MDatabaseReadonly, public MZeroedObject -{ - JSONNode *m_root = nullptr; - LIST<JSONNode> m_events; - LIST<char> m_modules; - -public: - CDbxJson() : - m_events(100), - m_modules(10, CompareModules) - {} - - ~CDbxJson() - { - if (m_root != nullptr) - json_delete(m_root); - - for (auto &it : m_modules) - mir_free(it); - } - - void Load() - { - // json operates with the only contact with pseudo id=1 - m_cache->AddContactToCache(1); - } - - int Open(const wchar_t *profile) - { - HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0); - if (hFile == INVALID_HANDLE_VALUE) - return EGROKPRF_CANTREAD; - - DWORD dwSize = GetFileSize(hFile, nullptr), dwRead; - ptrA szFile((char*)mir_alloc(dwSize + 1)); - BOOL r = ReadFile(hFile, szFile, dwSize, &dwRead, nullptr); - CloseHandle(hFile); - if (!r) - return EGROKPRF_CANTREAD; - - szFile[dwSize] = 0; - if ((m_root = json_parse(szFile)) == nullptr) - return EGROKPRF_DAMAGED; - - for (auto &it : m_root->at("history")) - m_events.insert(&it); - - return EGROKPRF_NOERROR; - } - - // mcontacts format always store history for one contact only - STDMETHODIMP_(int) GetContactCount(void) override - { - return 1; - } - - STDMETHODIMP_(int) GetEventCount(MCONTACT) override - { - return m_events.getCount(); - } - - STDMETHODIMP_(BOOL) GetEvent(MEVENT iEvent, DBEVENTINFO *dbei) override - { - JSONNode *node = m_events[iEvent - 1]; - if (node == nullptr) - return 0; - - dbei->eventType = (*node)["type"].as_int(); - - dbei->timestamp = 0; - std::string szTime = (*node)["time"].as_string(); - if (!szTime.empty()) { - char c; - struct tm st = {}; - int res = sscanf(szTime.c_str(), "%4d%c%2d%c%2d %2d:%2d:%2d", &st.tm_year, &c, &st.tm_mon, &c, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec); - if (res == 8) { - st.tm_mon--; - st.tm_year -= 1900; - time_t tm = mktime(&st); - if (tm != -1) - dbei->timestamp = tm; - } - } - else { - szTime = (*node)["isotime"].as_string(); - if (!szTime.empty()) { - struct tm st = {}; - int res = sscanf(szTime.c_str(), "%4d-%2d-%2dT%2d:%2d:%2dZ", &st.tm_year, &st.tm_mon, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec); - if (res == 6) { - st.tm_mon--; - st.tm_year -= 1900; - time_t tm = _mkgmtime(&st); - if (tm != -1) - dbei->timestamp = tm; - } - } - } - - if (dbei->timestamp == 0) - dbei->timestamp = (*node)["timeStamp"].as_int(); - - dbei->flags = 0; - std::string szFlags = (*node)["flags"].as_string(); - for (auto &c : szFlags) - switch (c) { - case 'm': dbei->flags |= DBEF_SENT; break; - case 'r': dbei->flags |= DBEF_READ; break; - } - - std::string szModule = (*node)["module"].as_string(); - if (!szModule.empty()) { - dbei->szModule = m_modules.find((char*)szModule.c_str()); - if (dbei->szModule == nullptr) { - dbei->szModule = mir_strdup(szModule.c_str()); - m_modules.insert((char*)dbei->szModule); - } - } - - if (dbei->eventType == EVENTTYPE_FILE) { - std::string szFile = (*node)["file"].as_string(); - std::string szDescr = (*node)["descr"].as_string(); - - dbei->flags |= DBEF_UTF; - MBinBuffer buf; - uint32_t tmp = 0; - buf.append(&tmp, sizeof(tmp)); - buf.append(szFile.c_str(), szFile.size()); - if (!szDescr.empty()) { - buf.append(&tmp, 1); - buf.append(szDescr.c_str(), szDescr.size()); - } - buf.append(&tmp, 1); - - dbei->cbBlob = (int)buf.length(); - dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob); - memcpy(dbei->pBlob, buf.data(), buf.length()); - } - else { - std::string szBody = (*node)["body"].as_string(); - if (!szBody.empty()) { - int offset; - switch (dbei->eventType) { - case EVENTTYPE_ADDED: - case EVENTTYPE_FILE: - offset = sizeof(uint32_t); - break; - - case EVENTTYPE_AUTHREQUEST: - offset = sizeof(uint32_t) * 2; - break; - - default: - offset = 0; - } - - dbei->flags |= DBEF_UTF; - dbei->cbBlob = (uint32_t)szBody.size() + offset; - dbei->pBlob = (uint8_t*)mir_calloc(dbei->cbBlob + 1); - memcpy(dbei->pBlob + offset, szBody.c_str(), szBody.size()); - dbei->pBlob[dbei->cbBlob] = 0; - } - } - - return 0; - } - - STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override - { - return 1; - } - - STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT iEvent) override - { - if ((int)iEvent >= m_events.getCount()) - return 0; - - return iEvent+1; - } - - STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override - { - int numEvents = m_events.getCount(); - return numEvents ? numEvents-1 : 0; - } - - STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT iEvent) override - { - if (iEvent <= 1) - return 0; - - return iEvent-1; - } - - STDMETHODIMP_(DATABASELINK *) GetDriver(); -}; - -static int mc_grokHeader(const wchar_t *profile) -{ - return CDbxJson().Open(profile); -} - -static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL) -{ - std::unique_ptr<CDbxJson> db(new CDbxJson()); - if (db->Open(profile)) - return nullptr; - - db->Load(); - return db.release(); -} - -static DATABASELINK dblink = -{ - 0, - "mcontacts", - L"mContacts file driver", - mc_makeDatabase, - mc_grokHeader, - mc_load -}; - -STDMETHODIMP_(DATABASELINK *) CDbxJson::GetDriver() -{ - return &g_patternDbLink; -} - -void RegisterJson() -{ - RegisterDatabasePlugin(&dblink); -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <m_json.h>
+
+static int mc_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JSON text driver, read-only
+
+static int CompareModules(const char *p1, const char *p2)
+{
+ return mir_strcmp(p1, p2);
+}
+
+class CDbxJson : public MDatabaseReadonly, public MZeroedObject
+{
+ JSONNode *m_root = nullptr;
+ LIST<JSONNode> m_events;
+ LIST<char> m_modules;
+
+public:
+ CDbxJson() :
+ m_events(100),
+ m_modules(10, CompareModules)
+ {}
+
+ ~CDbxJson()
+ {
+ if (m_root != nullptr)
+ json_delete(m_root);
+
+ for (auto &it : m_modules)
+ mir_free(it);
+ }
+
+ void Load()
+ {
+ // json operates with the only contact with pseudo id=1
+ m_cache->AddContactToCache(1);
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return EGROKPRF_CANTREAD;
+
+ DWORD dwSize = GetFileSize(hFile, nullptr), dwRead;
+ ptrA szFile((char*)mir_alloc(dwSize + 1));
+ BOOL r = ReadFile(hFile, szFile, dwSize, &dwRead, nullptr);
+ CloseHandle(hFile);
+ if (!r)
+ return EGROKPRF_CANTREAD;
+
+ szFile[dwSize] = 0;
+ if ((m_root = json_parse(szFile)) == nullptr)
+ return EGROKPRF_DAMAGED;
+
+ for (auto &it : m_root->at("history"))
+ m_events.insert(&it);
+
+ return EGROKPRF_NOERROR;
+ }
+
+ // mcontacts format always store history for one contact only
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return 1;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return m_events.getCount();
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT iEvent, DBEVENTINFO *dbei) override
+ {
+ JSONNode *node = m_events[iEvent - 1];
+ if (node == nullptr)
+ return 0;
+
+ dbei->eventType = (*node)["type"].as_int();
+
+ dbei->timestamp = 0;
+ std::string szTime = (*node)["time"].as_string();
+ if (!szTime.empty()) {
+ char c;
+ struct tm st = {};
+ int res = sscanf(szTime.c_str(), "%4d%c%2d%c%2d %2d:%2d:%2d", &st.tm_year, &c, &st.tm_mon, &c, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec);
+ if (res == 8) {
+ st.tm_mon--;
+ st.tm_year -= 1900;
+ time_t tm = mktime(&st);
+ if (tm != -1)
+ dbei->timestamp = tm;
+ }
+ }
+ else {
+ szTime = (*node)["isotime"].as_string();
+ if (!szTime.empty()) {
+ struct tm st = {};
+ int res = sscanf(szTime.c_str(), "%4d-%2d-%2dT%2d:%2d:%2dZ", &st.tm_year, &st.tm_mon, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec);
+ if (res == 6) {
+ st.tm_mon--;
+ st.tm_year -= 1900;
+ time_t tm = _mkgmtime(&st);
+ if (tm != -1)
+ dbei->timestamp = tm;
+ }
+ }
+ }
+
+ if (dbei->timestamp == 0)
+ dbei->timestamp = (*node)["timeStamp"].as_int();
+
+ dbei->flags = 0;
+ std::string szFlags = (*node)["flags"].as_string();
+ for (auto &c : szFlags)
+ switch (c) {
+ case 'm': dbei->flags |= DBEF_SENT; break;
+ case 'r': dbei->flags |= DBEF_READ; break;
+ }
+
+ std::string szModule = (*node)["module"].as_string();
+ if (!szModule.empty()) {
+ dbei->szModule = m_modules.find((char*)szModule.c_str());
+ if (dbei->szModule == nullptr) {
+ dbei->szModule = mir_strdup(szModule.c_str());
+ m_modules.insert((char*)dbei->szModule);
+ }
+ }
+
+ if (dbei->eventType == EVENTTYPE_FILE) {
+ std::string szFile = (*node)["file"].as_string();
+ std::string szDescr = (*node)["descr"].as_string();
+
+ dbei->flags |= DBEF_UTF;
+ MBinBuffer buf;
+ uint32_t tmp = 0;
+ buf.append(&tmp, sizeof(tmp));
+ buf.append(szFile.c_str(), szFile.size());
+ if (!szDescr.empty()) {
+ buf.append(&tmp, 1);
+ buf.append(szDescr.c_str(), szDescr.size());
+ }
+ buf.append(&tmp, 1);
+
+ dbei->cbBlob = (int)buf.length();
+ dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob);
+ memcpy(dbei->pBlob, buf.data(), buf.length());
+ }
+ else {
+ std::string szBody = (*node)["body"].as_string();
+ if (!szBody.empty()) {
+ int offset;
+ switch (dbei->eventType) {
+ case EVENTTYPE_ADDED:
+ case EVENTTYPE_FILE:
+ offset = sizeof(uint32_t);
+ break;
+
+ case EVENTTYPE_AUTHREQUEST:
+ offset = sizeof(uint32_t) * 2;
+ break;
+
+ default:
+ offset = 0;
+ }
+
+ dbei->flags |= DBEF_UTF;
+ dbei->cbBlob = (uint32_t)szBody.size() + offset;
+ dbei->pBlob = (uint8_t*)mir_calloc(dbei->cbBlob + 1);
+ memcpy(dbei->pBlob + offset, szBody.c_str(), szBody.size());
+ dbei->pBlob[dbei->cbBlob] = 0;
+ }
+ }
+
+ return 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override
+ {
+ return 1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT iEvent) override
+ {
+ if ((int)iEvent >= m_events.getCount())
+ return 0;
+
+ return iEvent+1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override
+ {
+ int numEvents = m_events.getCount();
+ return numEvents ? numEvents-1 : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT iEvent) override
+ {
+ if (iEvent <= 1)
+ return 0;
+
+ return iEvent-1;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
+
+static int mc_grokHeader(const wchar_t *profile)
+{
+ return CDbxJson().Open(profile);
+}
+
+static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxJson> db(new CDbxJson());
+ if (db->Open(profile))
+ return nullptr;
+
+ db->Load();
+ return db.release();
+}
+
+static DATABASELINK dblink =
+{
+ 0,
+ "mcontacts",
+ L"mContacts file driver",
+ mc_makeDatabase,
+ mc_grokHeader,
+ mc_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxJson::GetDriver()
+{
+ return &g_patternDbLink;
+}
+
+void RegisterJson()
+{
+ RegisterDatabasePlugin(&dblink);
+}
diff --git a/plugins/Import/src/ui.cpp b/plugins/Import/src/ui.cpp index 12a84455c7..0a5d6cf4e1 100644 --- a/plugins/Import/src/ui.cpp +++ b/plugins/Import/src/ui.cpp @@ -1,130 +1,130 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" - -class CContactImportDlg : public CDlgBase -{ - friend INT_PTR ImportContact(WPARAM hContact, LPARAM); - - MCONTACT m_hContact; - int m_flags = 0; - CImportPattern *m_pPattern = 0; - wchar_t m_wszFileName[MAX_PATH]; - - CCtrlButton m_btnOpenFile; - CCtrlCombo m_cmbFileType; - CCtrlEdit edtFileName; - -public: - CContactImportDlg(MCONTACT hContact) : - CDlgBase(g_plugin, IDD_IMPORT_CONTACT), - m_hContact(hContact), - edtFileName(this, IDC_FILENAME), - m_cmbFileType(this, IDC_FILETYPE), - m_btnOpenFile(this, IDC_OPEN_FILE) - { - m_wszFileName[0] = 0; - - m_btnOpenFile.OnClick = Callback(this, &CContactImportDlg::onClick_OpenFile); - } - - bool OnInitDialog() override - { - CMStringW wszTitle(FORMAT, TranslateT("Import history for %s"), Clist_GetContactDisplayName(m_hContact)); - SetWindowTextW(m_hwnd, wszTitle); - - m_cmbFileType.AddString(TranslateT("Miranda NG database/mContacts"), -1); - m_cmbFileType.AddString(TranslateT("JSON file"), -2); - - int iType = 1; - for (auto &it : g_plugin.m_patterns) - m_cmbFileType.AddString(it->wszName, iType++); - - return true; - } - - bool OnApply() override - { - edtFileName.GetText(m_wszFileName, _countof(m_wszFileName)); - if (m_wszFileName[0] == 0) - return false; - - if (IsDlgButtonChecked(m_hwnd, IDC_CHECK_DUPS)) - m_flags = IOPT_CHECKDUPS; - return true; - } - - void onClick_OpenFile(CCtrlButton*) - { - int iCur = m_cmbFileType.GetCurSel(); - if (iCur == -1) - return; - - CMStringW text, cmbText(ptrW(m_cmbFileType.GetText())); - switch(int idx = m_cmbFileType.GetItemData(iCur)) { - case -1: - text.AppendFormat(L"%s (*.dat,*.bak)%c*.dat;*.bak%c", cmbText.c_str(), 0, 0); - m_pPattern = nullptr; - break; - - case -2: - text.AppendFormat(L"%s (*.json)%c*.json%c", cmbText.c_str(), 0, 0); - m_pPattern = nullptr; - break; - - default: - auto &p = g_plugin.m_patterns[idx-1]; - text.AppendFormat(L"%s (*.%s)%c*.%s%c", cmbText.c_str(), p.wszExt.c_str(), 0, p.wszExt.c_str(), 0); - m_pPattern = &p; - break; - } - text.AppendFormat(L"%s (*.*)%c*.*%c%c", TranslateT("All Files"), 0, 0, 0); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.lpstrFilter = text; - ofn.lpstrDefExt = L"dat"; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT; - ofn.lpstrFile = m_wszFileName; - ofn.nMaxFile = _countof(m_wszFileName); - if (!GetOpenFileNameW(&ofn)) { - m_wszFileName[0] = 0; - m_pPattern = nullptr; - } - else edtFileName.SetText(m_wszFileName); - } -}; - -INT_PTR ImportContact(WPARAM hContact, LPARAM) -{ - CContactImportDlg dlg(hContact); - if (!dlg.DoModal()) - return 0; - - g_pBatch = new CImportBatch(); - wcsncpy_s(g_pBatch->m_wszFileName, dlg.m_wszFileName, _TRUNCATE); - g_pBatch->m_pPattern = dlg.m_pPattern; - g_pBatch->m_hContact = hContact; - g_pBatch->m_iOptions = IOPT_HISTORY + dlg.m_flags; - return RunWizard(new CProgressPageDlg(), true); -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+class CContactImportDlg : public CDlgBase
+{
+ friend INT_PTR ImportContact(WPARAM hContact, LPARAM);
+
+ MCONTACT m_hContact;
+ int m_flags = 0;
+ CImportPattern *m_pPattern = 0;
+ wchar_t m_wszFileName[MAX_PATH];
+
+ CCtrlButton m_btnOpenFile;
+ CCtrlCombo m_cmbFileType;
+ CCtrlEdit edtFileName;
+
+public:
+ CContactImportDlg(MCONTACT hContact) :
+ CDlgBase(g_plugin, IDD_IMPORT_CONTACT),
+ m_hContact(hContact),
+ edtFileName(this, IDC_FILENAME),
+ m_cmbFileType(this, IDC_FILETYPE),
+ m_btnOpenFile(this, IDC_OPEN_FILE)
+ {
+ m_wszFileName[0] = 0;
+
+ m_btnOpenFile.OnClick = Callback(this, &CContactImportDlg::onClick_OpenFile);
+ }
+
+ bool OnInitDialog() override
+ {
+ CMStringW wszTitle(FORMAT, TranslateT("Import history for %s"), Clist_GetContactDisplayName(m_hContact));
+ SetWindowTextW(m_hwnd, wszTitle);
+
+ m_cmbFileType.AddString(TranslateT("Miranda NG database/mContacts"), -1);
+ m_cmbFileType.AddString(TranslateT("JSON file"), -2);
+
+ int iType = 1;
+ for (auto &it : g_plugin.m_patterns)
+ m_cmbFileType.AddString(it->wszName, iType++);
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ edtFileName.GetText(m_wszFileName, _countof(m_wszFileName));
+ if (m_wszFileName[0] == 0)
+ return false;
+
+ if (IsDlgButtonChecked(m_hwnd, IDC_CHECK_DUPS))
+ m_flags = IOPT_CHECKDUPS;
+ return true;
+ }
+
+ void onClick_OpenFile(CCtrlButton*)
+ {
+ int iCur = m_cmbFileType.GetCurSel();
+ if (iCur == -1)
+ return;
+
+ CMStringW text, cmbText(ptrW(m_cmbFileType.GetText()));
+ switch(int idx = m_cmbFileType.GetItemData(iCur)) {
+ case -1:
+ text.AppendFormat(L"%s (*.dat,*.bak)%c*.dat;*.bak%c", cmbText.c_str(), 0, 0);
+ m_pPattern = nullptr;
+ break;
+
+ case -2:
+ text.AppendFormat(L"%s (*.json)%c*.json%c", cmbText.c_str(), 0, 0);
+ m_pPattern = nullptr;
+ break;
+
+ default:
+ auto &p = g_plugin.m_patterns[idx-1];
+ text.AppendFormat(L"%s (*.%s)%c*.%s%c", cmbText.c_str(), p.wszExt.c_str(), 0, p.wszExt.c_str(), 0);
+ m_pPattern = &p;
+ break;
+ }
+ text.AppendFormat(L"%s (*.*)%c*.*%c%c", TranslateT("All Files"), 0, 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.lpstrFilter = text;
+ ofn.lpstrDefExt = L"dat";
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT;
+ ofn.lpstrFile = m_wszFileName;
+ ofn.nMaxFile = _countof(m_wszFileName);
+ if (!GetOpenFileNameW(&ofn)) {
+ m_wszFileName[0] = 0;
+ m_pPattern = nullptr;
+ }
+ else edtFileName.SetText(m_wszFileName);
+ }
+};
+
+INT_PTR ImportContact(WPARAM hContact, LPARAM)
+{
+ CContactImportDlg dlg(hContact);
+ if (!dlg.DoModal())
+ return 0;
+
+ g_pBatch = new CImportBatch();
+ wcsncpy_s(g_pBatch->m_wszFileName, dlg.m_wszFileName, _TRUNCATE);
+ g_pBatch->m_pPattern = dlg.m_pPattern;
+ g_pBatch->m_hContact = hContact;
+ g_pBatch->m_iOptions = IOPT_HISTORY + dlg.m_flags;
+ return RunWizard(new CProgressPageDlg(), true);
+}
diff --git a/plugins/Import/src/utils.cpp b/plugins/Import/src/utils.cpp index 3c83bf11a9..d869ae2737 100644 --- a/plugins/Import/src/utils.cpp +++ b/plugins/Import/src/utils.cpp @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Import/src/version.h b/plugins/Import/src/version.h index d34c120f7d..f76dfe14a3 100644 --- a/plugins/Import/src/version.h +++ b/plugins/Import/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Imports contacts and messages from another Miranda profile or from an external program."
#define __AUTHOR "Miranda team"
#define __AUTHORWEB "https://miranda-ng.org/p/Import"
-#define __COPYRIGHT "© 2012-22 George Hazan"
+#define __COPYRIGHT "© 2012-23 George Hazan"
diff --git a/plugins/Import/src/wizard.cpp b/plugins/Import/src/wizard.cpp index aab7e802a7..dde120289e 100644 --- a/plugins/Import/src/wizard.cpp +++ b/plugins/Import/src/wizard.cpp @@ -2,7 +2,7 @@ Import plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/KeyboardNotify/src/stdafx.cxx b/plugins/KeyboardNotify/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/KeyboardNotify/src/stdafx.cxx +++ b/plugins/KeyboardNotify/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ListeningTo/src/stdafx.cxx b/plugins/ListeningTo/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ListeningTo/src/stdafx.cxx +++ b/plugins/ListeningTo/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/MagneticWindows/src/Version.h b/plugins/MagneticWindows/src/Version.h index 6a45b1f20d..11635c5508 100644 --- a/plugins/MagneticWindows/src/Version.h +++ b/plugins/MagneticWindows/src/Version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Makes the contact list and the chat windows snapping to the desktop border and to each other."
#define __AUTHOR "Michael Kunz"
#define __AUTHORWEB "https://miranda-ng.org/p/MagneticWindows"
-#define __COPYRIGHT " 2006 Michael Kunz, 2018-22 Miranda NG team"
+#define __COPYRIGHT " 2006 Michael Kunz, 2018-23 Miranda NG team"
diff --git a/plugins/MagneticWindows/src/stdafx.cxx b/plugins/MagneticWindows/src/stdafx.cxx index f9c009e3f9..35f341c48b 100644 --- a/plugins/MagneticWindows/src/stdafx.cxx +++ b/plugins/MagneticWindows/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 Miranda NG project (http://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" +/*
+Copyright (C) 2012-23 Miranda NG project (http://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"
diff --git a/plugins/MenuItemEx/src/stdafx.cxx b/plugins/MenuItemEx/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/MenuItemEx/src/stdafx.cxx +++ b/plugins/MenuItemEx/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/MessageState/src/stdafx.cxx b/plugins/MessageState/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/MessageState/src/stdafx.cxx +++ b/plugins/MessageState/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/MessageState/src/version.h b/plugins/MessageState/src/version.h index 479f6cbe5b..c3caed1956 100644 --- a/plugins/MessageState/src/version.h +++ b/plugins/MessageState/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Displays icons in message window showing whether your last outgoing message was read / is still unread."
#define __AUTHOR "MikalaiR"
#define __AUTHORWEB "https://miranda-ng.org/p/MessageState"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/MimCmd/src/stdafx.cxx b/plugins/MimCmd/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/MimCmd/src/stdafx.cxx +++ b/plugins/MimCmd/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/MirFox/src/version.h b/plugins/MirFox/src/version.h index 1ba06c8d39..0b447a7d3f 100644 --- a/plugins/MirFox/src/version.h +++ b/plugins/MirFox/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "MirFox (Miranda NG) - part of Miranda-Firefox integration - http://wsx22.3.xpdev-hosted.com"
#define __AUTHOR "Szymon Tokarz"
#define __AUTHORWEB "https://miranda-ng.org/p/MirFox"
-#define __COPYRIGHT "© 2013-22 Szymon Tokarz"
+#define __COPYRIGHT "© 2013-23 Szymon Tokarz"
diff --git a/plugins/MirLua/src/version.h b/plugins/MirLua/src/version.h index 6f9bf7d623..84527ae5c7 100644 --- a/plugins/MirLua/src/version.h +++ b/plugins/MirLua/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Extends Miranda NG functionality with Lua scripts."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/MirLua"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/MirandaG15/src/stdafx.cxx b/plugins/MirandaG15/src/stdafx.cxx index 4f32cda1a4..4f135bc766 100644 --- a/plugins/MirandaG15/src/stdafx.cxx +++ b/plugins/MirandaG15/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/MobileState/src/clients.h b/plugins/MobileState/src/clients.h index f1740cbc64..2b0d6aebdb 100644 --- a/plugins/MobileState/src/clients.h +++ b/plugins/MobileState/src/clients.h @@ -1,6 +1,6 @@ /*
Mobile State plugin for Miranda NG (www.miranda-ng.org)
- (c) 2012-17 Robert Pösel, 2017-22 Miranda NG team
+ (c) 2012-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/plugins/MobileState/src/main.cpp b/plugins/MobileState/src/main.cpp index fe479ee0d8..8a63a8eaa4 100644 --- a/plugins/MobileState/src/main.cpp +++ b/plugins/MobileState/src/main.cpp @@ -1,6 +1,6 @@ /*
Mobile State plugin for Miranda NG (www.miranda-ng.org)
- (c) 2012-17 by Robert Pösel, 2017-22 Miranda NG team
+ (c) 2012-17 by Robert Pösel, 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/plugins/MobileState/src/stdafx.cxx b/plugins/MobileState/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/MobileState/src/stdafx.cxx +++ b/plugins/MobileState/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/MobileState/src/stdafx.h b/plugins/MobileState/src/stdafx.h index f3581f06b2..7eb4d261f7 100644 --- a/plugins/MobileState/src/stdafx.h +++ b/plugins/MobileState/src/stdafx.h @@ -1,6 +1,6 @@ /*
Mobile State plugin for Miranda NG (www.miranda-ng.org)
- (c) 2012-17 by Robert Pösel, 2017-22 Miranda NG team
+ (c) 2012-17 by Robert Pösel, 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/plugins/MobileState/src/version.h b/plugins/MobileState/src/version.h index 10e440da90..85c8802b95 100644 --- a/plugins/MobileState/src/version.h +++ b/plugins/MobileState/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Plugin shows mobile icon in contact list next to contacts which are using mobile client."
#define __AUTHOR "Robert Pösel"
#define __AUTHORWEB "https://miranda-ng.org/p/MobileState"
-#define __COPYRIGHT "© 2012-17 Robert Pösel, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-17 Robert Pösel, 2017-23 Miranda NG team"
diff --git a/plugins/MsgPopup/src/stdafx.cxx b/plugins/MsgPopup/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/MsgPopup/src/stdafx.cxx +++ b/plugins/MsgPopup/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Msg_Export/src/stdafx.cxx b/plugins/Msg_Export/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Msg_Export/src/stdafx.cxx +++ b/plugins/Msg_Export/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Msg_Export/src/version.h b/plugins/Msg_Export/src/version.h index f260612881..85d9f8cfe4 100644 --- a/plugins/Msg_Export/src/version.h +++ b/plugins/Msg_Export/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Exports every message, URL or file you receive to a text file."
#define __AUTHOR "Kennet Nielsen, mod by ring0"
#define __AUTHORWEB "https://miranda-ng.org/p/Msg_Export"
-#define __COPYRIGHT "© 2002 Kennet Nielsen, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2002 Kennet Nielsen, 2012-23 Miranda NG team"
diff --git a/plugins/MyDetails/src/stdafx.cxx b/plugins/MyDetails/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/MyDetails/src/stdafx.cxx +++ b/plugins/MyDetails/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/NewAwaySysMod/src/stdafx.cxx b/plugins/NewAwaySysMod/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/NewAwaySysMod/src/stdafx.cxx +++ b/plugins/NewAwaySysMod/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/NewEventNotify/src/stdafx.cxx b/plugins/NewEventNotify/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/NewEventNotify/src/stdafx.cxx +++ b/plugins/NewEventNotify/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/NewStory/src/stdafx.cxx b/plugins/NewStory/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/plugins/NewStory/src/stdafx.cxx +++ b/plugins/NewStory/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/plugins/NewStory/src/version.h b/plugins/NewStory/src/version.h index c6cdac6bb4..9be84bdbe7 100644 --- a/plugins/NewStory/src/version.h +++ b/plugins/NewStory/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "History viewer for Miranda NG."
#define __AUTHOR "nullbie"
#define __AUTHORWEB "https://miranda-ng.org/p/NewStory"
-#define __COPYRIGHT "© 2005 Victor Pavlychko, 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2005 Victor Pavlychko, 2018-23 Miranda NG team"
diff --git a/plugins/NewXstatusNotify/src/stdafx.cxx b/plugins/NewXstatusNotify/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/NewXstatusNotify/src/stdafx.cxx +++ b/plugins/NewXstatusNotify/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/New_GPG/src/globals.h b/plugins/New_GPG/src/globals.h index 02b1607b6d..ac64da8ecd 100644 --- a/plugins/New_GPG/src/globals.h +++ b/plugins/New_GPG/src/globals.h @@ -1,42 +1,42 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef GLOBALS_H -#define GLOBALS_H - -struct contact_data -{ - list<string> msgs_to_send;// msgs_to_pass; - string key_in_prescense; -}; - -struct globals_s -{ - bool bDecryptFiles = false; - CMStringW wszInopentag, wszInclosetag, wszOutopentag, wszOutclosetag, wszPassword; - wchar_t key_id_global[17] = { 0 }; - list <JabberAccount*> Accounts; - HFONT bold_font = nullptr; - logtofile debuglog; - bool gpg_valid = false, gpg_keyexist = false; - std::map<MCONTACT, contact_data> hcontact_data; - bool _terminate; -}; - -extern globals_s globals; - - -#endif +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+struct contact_data
+{
+ list<string> msgs_to_send;// msgs_to_pass;
+ string key_in_prescense;
+};
+
+struct globals_s
+{
+ bool bDecryptFiles = false;
+ CMStringW wszInopentag, wszInclosetag, wszOutopentag, wszOutclosetag, wszPassword;
+ wchar_t key_id_global[17] = { 0 };
+ list <JabberAccount*> Accounts;
+ HFONT bold_font = nullptr;
+ logtofile debuglog;
+ bool gpg_valid = false, gpg_keyexist = false;
+ std::map<MCONTACT, contact_data> hcontact_data;
+ bool _terminate;
+};
+
+extern globals_s globals;
+
+
+#endif
diff --git a/plugins/New_GPG/src/gpg_wrapper.cpp b/plugins/New_GPG/src/gpg_wrapper.cpp index 6b719e1e5e..8d12134c7c 100644 --- a/plugins/New_GPG/src/gpg_wrapper.cpp +++ b/plugins/New_GPG/src/gpg_wrapper.cpp @@ -1,168 +1,168 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -namespace bp = boost::process; - -gpg_execution_params::gpg_execution_params() -{ } - -gpg_execution_params::~gpg_execution_params() -{ } - -void pxEexcute_thread(gpg_execution_params *params) -{ - if (!globals.gpg_valid) - return; - - CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath")); - if (_waccess(bin_path, 0)) { - if (globals.debuglog) - globals.debuglog << "GPG executable not found"; - params->result = pxNotFound; - return; - } - - bp::environment env = boost::this_process::environment(); - env.set("LANGUAGE", "en@quot"); - env.set("LC_ALL", "English"); - env.set("LANG", "C"); - - std::vector<std::wstring> argv; - CMStringW home_dir(g_plugin.getMStringW("szHomePath")); - if (!home_dir.IsEmpty()) { // this check are required for first run gpg binary validation - argv.push_back(L"--homedir"); - argv.push_back(home_dir.c_str()); - } - - argv.push_back(L"--display-charset"); - argv.push_back(L"utf-8"); - argv.push_back(L"-z9"); - argv.insert(argv.end(), params->aargv.begin(), params->aargv.end()); - - if (globals.debuglog) { - std::wstring args; - for (unsigned int i = 0; i < argv.size(); ++i) { - args += argv[i]; - args += L" "; - } - args.erase(args.size() - 1, 1); - - globals.debuglog << "gpg in: " << toUTF8(args); - } - - params->out.Empty(); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - - bp::child *c; - std::future<std::string> pout, perr; - boost::asio::io_context ios; - if (params->bNoOutput) - c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), ios); - else - c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), bp::std_out > pout, bp::std_err > perr, ios); - - params->child = c; - - ios.run(); - c->wait(); - - if (!params->bNoOutput) { - std::string s = pout.get(); - if (!s.empty()) - params->out.Append(s.c_str()); - - s = perr.get(); - if (!s.empty()) - params->out.Append(s.c_str()); - - params->out.Replace("\r\n", "\n"); - params->out.Replace("\r\r", ""); - - if (globals.debuglog) - globals.debuglog << "gpg out: " << params->out.c_str(); - } - - params->child = nullptr; - params->code = c->exit_code(); - delete c; - - if (params->code) { - if (globals.debuglog) - globals.debuglog << ": warning: wrong gpg exit status, gpg output: " << params->out.c_str(); - params->result = pxSuccessExitCodeInvalid; - } - else params->result = pxSuccess; -} - -bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t) -{ - bool ret = true; - HANDLE hThread = mir_forkThread<gpg_execution_params>(pxEexcute_thread, ¶ms); - if (WaitForSingleObject(hThread, t.total_milliseconds()) == WAIT_TIMEOUT) { - ret = false; - if (params.child) - params.child->terminate(); - if (globals.debuglog) - globals.debuglog << "GPG execution timed out, aborted"; - } - return ret; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void pxEexcute_passwd_change_thread(gpg_execution_params_pass *params) -{ - if (!globals.gpg_valid) { - params->result = pxNotConfigured; - return; - } - - CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath")); - if (_waccess(bin_path, 0)) { - if (globals.debuglog) - globals.debuglog << "GPG executable not found"; - params->result = pxNotFound; - return; - } - - bp::environment env = boost::this_process::environment(); - env.set("LANGUAGE", "en@quot"); - env.set("LC_ALL", "English"); - - std::vector<std::wstring> argv; - - argv.push_back(bin_path.c_str()); - argv.push_back(L"--homedir"); - argv.push_back(g_plugin.getMStringW("szHomePath").c_str()); - argv.push_back(L"--display-charset"); - argv.push_back(L"utf-8"); - argv.push_back(L"-z9"); - argv.insert(argv.end(), params->aargv.begin(), params->aargv.end()); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - - bp::child c(bin_path.c_str(), argv, env, boost::process::windows::hide); - params->child = &c; - - c.wait(); - - params->child = nullptr; -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+namespace bp = boost::process;
+
+gpg_execution_params::gpg_execution_params()
+{ }
+
+gpg_execution_params::~gpg_execution_params()
+{ }
+
+void pxEexcute_thread(gpg_execution_params *params)
+{
+ if (!globals.gpg_valid)
+ return;
+
+ CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath"));
+ if (_waccess(bin_path, 0)) {
+ if (globals.debuglog)
+ globals.debuglog << "GPG executable not found";
+ params->result = pxNotFound;
+ return;
+ }
+
+ bp::environment env = boost::this_process::environment();
+ env.set("LANGUAGE", "en@quot");
+ env.set("LC_ALL", "English");
+ env.set("LANG", "C");
+
+ std::vector<std::wstring> argv;
+ CMStringW home_dir(g_plugin.getMStringW("szHomePath"));
+ if (!home_dir.IsEmpty()) { // this check are required for first run gpg binary validation
+ argv.push_back(L"--homedir");
+ argv.push_back(home_dir.c_str());
+ }
+
+ argv.push_back(L"--display-charset");
+ argv.push_back(L"utf-8");
+ argv.push_back(L"-z9");
+ argv.insert(argv.end(), params->aargv.begin(), params->aargv.end());
+
+ if (globals.debuglog) {
+ std::wstring args;
+ for (unsigned int i = 0; i < argv.size(); ++i) {
+ args += argv[i];
+ args += L" ";
+ }
+ args.erase(args.size() - 1, 1);
+
+ globals.debuglog << "gpg in: " << toUTF8(args);
+ }
+
+ params->out.Empty();
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+
+ bp::child *c;
+ std::future<std::string> pout, perr;
+ boost::asio::io_context ios;
+ if (params->bNoOutput)
+ c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), ios);
+ else
+ c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), bp::std_out > pout, bp::std_err > perr, ios);
+
+ params->child = c;
+
+ ios.run();
+ c->wait();
+
+ if (!params->bNoOutput) {
+ std::string s = pout.get();
+ if (!s.empty())
+ params->out.Append(s.c_str());
+
+ s = perr.get();
+ if (!s.empty())
+ params->out.Append(s.c_str());
+
+ params->out.Replace("\r\n", "\n");
+ params->out.Replace("\r\r", "");
+
+ if (globals.debuglog)
+ globals.debuglog << "gpg out: " << params->out.c_str();
+ }
+
+ params->child = nullptr;
+ params->code = c->exit_code();
+ delete c;
+
+ if (params->code) {
+ if (globals.debuglog)
+ globals.debuglog << ": warning: wrong gpg exit status, gpg output: " << params->out.c_str();
+ params->result = pxSuccessExitCodeInvalid;
+ }
+ else params->result = pxSuccess;
+}
+
+bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t)
+{
+ bool ret = true;
+ HANDLE hThread = mir_forkThread<gpg_execution_params>(pxEexcute_thread, ¶ms);
+ if (WaitForSingleObject(hThread, t.total_milliseconds()) == WAIT_TIMEOUT) {
+ ret = false;
+ if (params.child)
+ params.child->terminate();
+ if (globals.debuglog)
+ globals.debuglog << "GPG execution timed out, aborted";
+ }
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void pxEexcute_passwd_change_thread(gpg_execution_params_pass *params)
+{
+ if (!globals.gpg_valid) {
+ params->result = pxNotConfigured;
+ return;
+ }
+
+ CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath"));
+ if (_waccess(bin_path, 0)) {
+ if (globals.debuglog)
+ globals.debuglog << "GPG executable not found";
+ params->result = pxNotFound;
+ return;
+ }
+
+ bp::environment env = boost::this_process::environment();
+ env.set("LANGUAGE", "en@quot");
+ env.set("LC_ALL", "English");
+
+ std::vector<std::wstring> argv;
+
+ argv.push_back(bin_path.c_str());
+ argv.push_back(L"--homedir");
+ argv.push_back(g_plugin.getMStringW("szHomePath").c_str());
+ argv.push_back(L"--display-charset");
+ argv.push_back(L"utf-8");
+ argv.push_back(L"-z9");
+ argv.insert(argv.end(), params->aargv.begin(), params->aargv.end());
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+
+ bp::child c(bin_path.c_str(), argv, env, boost::process::windows::hide);
+ params->child = &c;
+
+ c.wait();
+
+ params->child = nullptr;
+}
diff --git a/plugins/New_GPG/src/gpg_wrapper.h b/plugins/New_GPG/src/gpg_wrapper.h index fbf5f1a62a..fc4ec1db8a 100644 --- a/plugins/New_GPG/src/gpg_wrapper.h +++ b/plugins/New_GPG/src/gpg_wrapper.h @@ -1,67 +1,67 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -#ifndef GPG_WRAPPER_H -#define GPG_WRAPPER_H -enum pxResult -{ - pxSuccess, - pxSuccessExitCodeInvalid, - pxCreatePipeFailed, - pxDuplicateHandleFailed, - pxCloseHandleFailed, - pxCreateProcessFailed, - pxThreadWaitFailed, - pxReadFileFailed, - pxBufferOverflow, - pxNotFound, - pxNotConfigured -}; - -struct gpg_execution_params -{ - gpg_execution_params(); - ~gpg_execution_params(); - - std::vector<std::wstring> aargv; - CMStringA out; - uint32_t code = 0; - int bNoOutput = false; - pxResult result = pxSuccess; - boost::process::child *child = nullptr; - - __forceinline void addParam(const std::wstring ¶m) - { aargv.push_back(param); - } -}; - -struct gpg_execution_params_pass : public gpg_execution_params -{ - string &old_pass, &new_pass; - - gpg_execution_params_pass(std::string &o, std::string &n): - old_pass(o), - new_pass(n) - { - } -}; - - -bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t = boost::posix_time::seconds(10)); -void __cdecl pxEexcute_passwd_change_thread(gpg_execution_params_pass *param); - +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+#ifndef GPG_WRAPPER_H
+#define GPG_WRAPPER_H
+enum pxResult
+{
+ pxSuccess,
+ pxSuccessExitCodeInvalid,
+ pxCreatePipeFailed,
+ pxDuplicateHandleFailed,
+ pxCloseHandleFailed,
+ pxCreateProcessFailed,
+ pxThreadWaitFailed,
+ pxReadFileFailed,
+ pxBufferOverflow,
+ pxNotFound,
+ pxNotConfigured
+};
+
+struct gpg_execution_params
+{
+ gpg_execution_params();
+ ~gpg_execution_params();
+
+ std::vector<std::wstring> aargv;
+ CMStringA out;
+ uint32_t code = 0;
+ int bNoOutput = false;
+ pxResult result = pxSuccess;
+ boost::process::child *child = nullptr;
+
+ __forceinline void addParam(const std::wstring ¶m)
+ { aargv.push_back(param);
+ }
+};
+
+struct gpg_execution_params_pass : public gpg_execution_params
+{
+ string &old_pass, &new_pass;
+
+ gpg_execution_params_pass(std::string &o, std::string &n):
+ old_pass(o),
+ new_pass(n)
+ {
+ }
+};
+
+
+bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t = boost::posix_time::seconds(10));
+void __cdecl pxEexcute_passwd_change_thread(gpg_execution_params_pass *param);
+
#endif
\ No newline at end of file diff --git a/plugins/New_GPG/src/icons.cpp b/plugins/New_GPG/src/icons.cpp index a3c7614803..9935766781 100644 --- a/plugins/New_GPG/src/icons.cpp +++ b/plugins/New_GPG/src/icons.cpp @@ -1,55 +1,55 @@ -// Copyright © 2010-22 SecureIM developers (baloo and others), sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -static IconItem iconList[] = -{ - { "Secured", "secured", IDI_SECURED }, - { "Unsecured", "unsecured", IDI_UNSECURED } -}; - -void InitIconLib() -{ - g_plugin.registerIcon(MODULENAME, iconList); -} - -HANDLE IconLibHookIconsChanged(MIRANDAHOOK hook) -{ - return HookEvent(ME_SKIN_ICONSCHANGED, hook); -} - -void setSrmmIcon(MCONTACT h) -{ - MCONTACT hContact = db_mc_isMeta(h) ? metaGetMostOnline(h) : h; - bool enabled = isContactSecured(hContact); - MCONTACT hMC = db_mc_tryMeta(hContact); - - int flags = enabled ? 0 : MBF_HIDDEN; - Srmm_SetIconFlags(hContact, MODULENAME, 1, flags); - if (hMC != hContact) - Srmm_SetIconFlags(hMC, MODULENAME, 1, flags); - - flags = enabled ? MBF_HIDDEN : 0; - Srmm_SetIconFlags(hContact, MODULENAME, 2, flags); - if (hMC != hContact) - Srmm_SetIconFlags(hMC, MODULENAME, 2, flags); - - const char *szIconId = (enabled) ? "secured" : "unsecured"; - ExtraIcon_SetIconByName(g_plugin.hCLIcon, hContact, szIconId); - if (hMC != hContact) - ExtraIcon_SetIconByName(g_plugin.hCLIcon, hMC, szIconId); -} +// Copyright © 2010-23 SecureIM developers (baloo and others), sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+static IconItem iconList[] =
+{
+ { "Secured", "secured", IDI_SECURED },
+ { "Unsecured", "unsecured", IDI_UNSECURED }
+};
+
+void InitIconLib()
+{
+ g_plugin.registerIcon(MODULENAME, iconList);
+}
+
+HANDLE IconLibHookIconsChanged(MIRANDAHOOK hook)
+{
+ return HookEvent(ME_SKIN_ICONSCHANGED, hook);
+}
+
+void setSrmmIcon(MCONTACT h)
+{
+ MCONTACT hContact = db_mc_isMeta(h) ? metaGetMostOnline(h) : h;
+ bool enabled = isContactSecured(hContact);
+ MCONTACT hMC = db_mc_tryMeta(hContact);
+
+ int flags = enabled ? 0 : MBF_HIDDEN;
+ Srmm_SetIconFlags(hContact, MODULENAME, 1, flags);
+ if (hMC != hContact)
+ Srmm_SetIconFlags(hMC, MODULENAME, 1, flags);
+
+ flags = enabled ? MBF_HIDDEN : 0;
+ Srmm_SetIconFlags(hContact, MODULENAME, 2, flags);
+ if (hMC != hContact)
+ Srmm_SetIconFlags(hMC, MODULENAME, 2, flags);
+
+ const char *szIconId = (enabled) ? "secured" : "unsecured";
+ ExtraIcon_SetIconByName(g_plugin.hCLIcon, hContact, szIconId);
+ if (hMC != hContact)
+ ExtraIcon_SetIconByName(g_plugin.hCLIcon, hMC, szIconId);
+}
diff --git a/plugins/New_GPG/src/init.cpp b/plugins/New_GPG/src/init.cpp index cbcf08c80b..4c4406a7ef 100644 --- a/plugins/New_GPG/src/init.cpp +++ b/plugins/New_GPG/src/init.cpp @@ -1,241 +1,241 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -int GpgOptInit(WPARAM, LPARAM); -int OnPreBuildContactMenu(WPARAM, LPARAM); - -INT_PTR RecvMsgSvc(WPARAM, LPARAM); -INT_PTR SendMsgSvc(WPARAM, LPARAM); -INT_PTR onSendFile(WPARAM, LPARAM); - -int HookSendMsg(WPARAM, LPARAM); -int GetJabberInterface(WPARAM, LPARAM); -int onProtoAck(WPARAM, LPARAM); -int onWindowEvent(WPARAM, LPARAM); -int onIconPressed(WPARAM, LPARAM); -int onExtraIconPressed(WPARAM, LPARAM, LPARAM); - -void InitCheck(); -void FirstRun(); -void RemoveHandlers(); - -// global variables -CMPlugin g_plugin; - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {4227c050-8d97-48d2-91ec-6a952b3dab94} - { 0x4227c050, 0x8d97, 0x48d2, { 0x91, 0xec, 0x6a, 0x95, 0x2b, 0x3d, 0xab, 0x94 } } -}; - -CMPlugin::CMPlugin() : - PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx), - bDebugLog(MODULENAME, "bDebugLog", false), - bJabberAPI(MODULENAME, "bJabberAPI", true), - bStripTags(MODULENAME, "bStripTags", false), - bAppendTags(MODULENAME, "bAppendTags", false), - bSameAction(MODULENAME, "bSameAction", false), - bAutoExchange(MODULENAME, "bAutoExchange", false), - bFileTransfers(MODULENAME, "bFileTransfers", false), - bPresenceSigning(MODULENAME, "bPresenceSigning", false), - bSendErrorMessages(MODULENAME, "bSendErrorMessages", false) -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR LoadKey(WPARAM, LPARAM); -INT_PTR SendKey(WPARAM, LPARAM); -INT_PTR ExportGpGKeys(WPARAM, LPARAM); -INT_PTR ImportGpGKeys(WPARAM, LPARAM); -INT_PTR ToggleEncryption(WPARAM, LPARAM); - -void InitIconLib(); - -void init_vars() -{ - globals.wszInopentag = g_plugin.getMStringW("szInOpenTag", L"<GPGdec>"); - globals.wszInclosetag = g_plugin.getMStringW("szInCloseTag", L"</GPGdec>"); - globals.wszOutopentag = g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>"); - globals.wszOutclosetag = g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>"); - globals.wszPassword = g_plugin.getMStringW("szKeyPassword"); - globals.bold_font = CreateFont(14, 0, 0, 0, 600, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Arial"); - - globals.debuglog.init(); -} - -static int OnModulesLoaded(WPARAM, LPARAM) -{ - FirstRun(); - if (!g_plugin.getByte("FirstRun", 1)) - InitCheck(); - - StatusIconData sid = {}; - sid.szModule = MODULENAME; - sid.flags = MBF_HIDDEN; - sid.dwId = 0x00000001; - sid.hIcon = IcoLib_GetIcon("secured"); - sid.szTooltip.a = LPGEN("GPG Turn off encryption"); - Srmm_AddIcon(&sid, &g_plugin); - - sid.dwId = 0x00000002; - sid.hIcon = IcoLib_GetIcon("unsecured"); - sid.szTooltip.a = LPGEN("GPG Turn on encryption"); - Srmm_AddIcon(&sid, &g_plugin); - - if (g_plugin.bJabberAPI) { - GetJabberInterface(0, 0); - HookEvent(ME_PROTO_ACCLISTCHANGED, GetJabberInterface); - } - - HookEvent(ME_MSG_WINDOWEVENT, onWindowEvent); - HookEvent(ME_MSG_ICONPRESSED, onIconPressed); - - Proto_RegisterModule(PROTOTYPE_ENCRYPTION, MODULENAME); - - CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, RecvMsgSvc); - CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SendMsgSvc); - CreateProtoServiceFunction(MODULENAME, PSS_FILE, onSendFile); - clean_temp_dir(); - return 0; -} - -static int OnShutdown(WPARAM, LPARAM) -{ - RemoveHandlers(); - return 0; -} - -static INT_PTR EventGetIcon(WPARAM flags, LPARAM) -{ - HICON hIcon = g_plugin.getIcon(IDI_SECURED); - return (INT_PTR)((flags & LR_SHARED) ? hIcon : CopyIcon(hIcon)); -} - -static INT_PTR GetEventText(WPARAM pEvent, LPARAM datatype) -{ - DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent; - ptrW wszText(mir_utf8decodeW((char *)dbei->pBlob)); - return (datatype != DBVT_WCHAR) ? (INT_PTR)mir_u2a(wszText) : (INT_PTR)wszText.detach(); -} - -int CMPlugin::Load() -{ - DBEVENTTYPEDESCR dbEventType = {}; - dbEventType.module = MODULENAME; - dbEventType.descr = "GPG service event"; - dbEventType.iconService = MODULENAME "/GetEventIcon"; - dbEventType.textService = MODULENAME "/GetEventText"; - DbEvent_RegisterType(&dbEventType); - - CreateServiceFunction(dbEventType.iconService, &EventGetIcon); - CreateServiceFunction(dbEventType.textService, &GetEventText); - - HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildContactMenu); - HookEvent(ME_DB_EVENT_FILTER_ADD, HookSendMsg); - HookEvent(ME_OPT_INITIALISE, GpgOptInit); - HookEvent(ME_PROTO_ACK, onProtoAck); - HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); - HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown); - - InitIconLib(); - init_vars(); - - //////////////////////////////////////////////////////////////////////////////////////// - // Comtact menu items - - CMenuItem mi(&g_plugin); - mi.hIcolibItem = g_plugin.getIconHandle(IDI_SECURED); - - SET_UID(mi, 0xbd22e3f8, 0xc19c, 0x45a8, 0xb7, 0x37, 0x6b, 0x3b, 0x27, 0xf0, 0x8c, 0xbb); - mi.position = -0x7FFFFFFF; - mi.name.a = LPGEN("Load public GPG key"); - mi.pszService = "/LoadPubKey"; - Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, LoadKey); - - SET_UID(mi, 0xc8008193, 0x56a9, 0x414a, 0x82, 0x98, 0x78, 0xe8, 0xa8, 0x84, 0x20, 0x67); - mi.position = -0x7FFFFFFe; - mi.name.a = LPGEN("Toggle GPG encryption"); - mi.pszService = "/ToggleEncryption"; - g_plugin.hToggleEncryption = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, ToggleEncryption); - - SET_UID(mi, 0x42bb535f, 0xd58e, 0x4edb, 0xbf, 0x2c, 0xfa, 0x9a, 0xbf, 0x1e, 0xb8, 0x69); - mi.position = -0x7FFFFFFd; - mi.name.a = LPGEN("Send public key"); - mi.pszService = "/SendKey"; - g_plugin.hSendKey = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, SendKey); - - //////////////////////////////////////////////////////////////////////////////////////// - // Main menu items - - SET_UID(mi, 0x0bac023bb, 0xd2e, 0x46e0, 0x93, 0x13, 0x7c, 0xf9, 0xf6, 0xb5, 0x02, 0xd1); - mi.position = -0x7FFFFFFe; - mi.name.a = "GPG"; - mi.root = Menu_AddMainMenuItem(&mi); - mi.flags = CMIF_UNMOVABLE; - - SET_UID(mi, 0x33a204b2, 0xe3c0, 0x413b, 0xbf, 0xd8, 0x8b, 0x2e, 0x3d, 0xa0, 0xef, 0xa4); - mi.position = -0x7FFFFFFe; - mi.name.a = LPGEN("Export GPG Public keys"); - mi.pszService = "/ExportGPGKeys"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, ExportGpGKeys); - - SET_UID(mi, 0x627fcfc1, 0x4e60, 0x4428, 0xaf, 0x96, 0x11, 0x42, 0x24, 0xeb, 0x07, 0xea); - mi.position = -0x7FFFFFFF; - mi.name.a = LPGEN("Import GPG Public keys"); - mi.pszService = "/ImportGPGKeys"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, ImportGpGKeys); - - //////////////////////////////////////////////////////////////////////////////////////// - // Extra icon - - hCLIcon = ExtraIcon_RegisterIcolib(MODULENAME, Translate("GPG encryption status"), "secured", &onExtraIconPressed); - for (auto &cc : Contacts()) - if (isContactHaveKey(cc)) - setSrmmIcon(cc); - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern list<wstring> transfers; - -int CMPlugin::Unload() -{ - for (auto p : transfers) - if (!p.empty()) - boost::filesystem::remove(p); - - clean_temp_dir(); - return 0; -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+int GpgOptInit(WPARAM, LPARAM);
+int OnPreBuildContactMenu(WPARAM, LPARAM);
+
+INT_PTR RecvMsgSvc(WPARAM, LPARAM);
+INT_PTR SendMsgSvc(WPARAM, LPARAM);
+INT_PTR onSendFile(WPARAM, LPARAM);
+
+int HookSendMsg(WPARAM, LPARAM);
+int GetJabberInterface(WPARAM, LPARAM);
+int onProtoAck(WPARAM, LPARAM);
+int onWindowEvent(WPARAM, LPARAM);
+int onIconPressed(WPARAM, LPARAM);
+int onExtraIconPressed(WPARAM, LPARAM, LPARAM);
+
+void InitCheck();
+void FirstRun();
+void RemoveHandlers();
+
+// global variables
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {4227c050-8d97-48d2-91ec-6a952b3dab94}
+ { 0x4227c050, 0x8d97, 0x48d2, { 0x91, 0xec, 0x6a, 0x95, 0x2b, 0x3d, 0xab, 0x94 } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
+ bDebugLog(MODULENAME, "bDebugLog", false),
+ bJabberAPI(MODULENAME, "bJabberAPI", true),
+ bStripTags(MODULENAME, "bStripTags", false),
+ bAppendTags(MODULENAME, "bAppendTags", false),
+ bSameAction(MODULENAME, "bSameAction", false),
+ bAutoExchange(MODULENAME, "bAutoExchange", false),
+ bFileTransfers(MODULENAME, "bFileTransfers", false),
+ bPresenceSigning(MODULENAME, "bPresenceSigning", false),
+ bSendErrorMessages(MODULENAME, "bSendErrorMessages", false)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR LoadKey(WPARAM, LPARAM);
+INT_PTR SendKey(WPARAM, LPARAM);
+INT_PTR ExportGpGKeys(WPARAM, LPARAM);
+INT_PTR ImportGpGKeys(WPARAM, LPARAM);
+INT_PTR ToggleEncryption(WPARAM, LPARAM);
+
+void InitIconLib();
+
+void init_vars()
+{
+ globals.wszInopentag = g_plugin.getMStringW("szInOpenTag", L"<GPGdec>");
+ globals.wszInclosetag = g_plugin.getMStringW("szInCloseTag", L"</GPGdec>");
+ globals.wszOutopentag = g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>");
+ globals.wszOutclosetag = g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>");
+ globals.wszPassword = g_plugin.getMStringW("szKeyPassword");
+ globals.bold_font = CreateFont(14, 0, 0, 0, 600, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Arial");
+
+ globals.debuglog.init();
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ FirstRun();
+ if (!g_plugin.getByte("FirstRun", 1))
+ InitCheck();
+
+ StatusIconData sid = {};
+ sid.szModule = MODULENAME;
+ sid.flags = MBF_HIDDEN;
+ sid.dwId = 0x00000001;
+ sid.hIcon = IcoLib_GetIcon("secured");
+ sid.szTooltip.a = LPGEN("GPG Turn off encryption");
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ sid.dwId = 0x00000002;
+ sid.hIcon = IcoLib_GetIcon("unsecured");
+ sid.szTooltip.a = LPGEN("GPG Turn on encryption");
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ if (g_plugin.bJabberAPI) {
+ GetJabberInterface(0, 0);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, GetJabberInterface);
+ }
+
+ HookEvent(ME_MSG_WINDOWEVENT, onWindowEvent);
+ HookEvent(ME_MSG_ICONPRESSED, onIconPressed);
+
+ Proto_RegisterModule(PROTOTYPE_ENCRYPTION, MODULENAME);
+
+ CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, RecvMsgSvc);
+ CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SendMsgSvc);
+ CreateProtoServiceFunction(MODULENAME, PSS_FILE, onSendFile);
+ clean_temp_dir();
+ return 0;
+}
+
+static int OnShutdown(WPARAM, LPARAM)
+{
+ RemoveHandlers();
+ return 0;
+}
+
+static INT_PTR EventGetIcon(WPARAM flags, LPARAM)
+{
+ HICON hIcon = g_plugin.getIcon(IDI_SECURED);
+ return (INT_PTR)((flags & LR_SHARED) ? hIcon : CopyIcon(hIcon));
+}
+
+static INT_PTR GetEventText(WPARAM pEvent, LPARAM datatype)
+{
+ DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent;
+ ptrW wszText(mir_utf8decodeW((char *)dbei->pBlob));
+ return (datatype != DBVT_WCHAR) ? (INT_PTR)mir_u2a(wszText) : (INT_PTR)wszText.detach();
+}
+
+int CMPlugin::Load()
+{
+ DBEVENTTYPEDESCR dbEventType = {};
+ dbEventType.module = MODULENAME;
+ dbEventType.descr = "GPG service event";
+ dbEventType.iconService = MODULENAME "/GetEventIcon";
+ dbEventType.textService = MODULENAME "/GetEventText";
+ DbEvent_RegisterType(&dbEventType);
+
+ CreateServiceFunction(dbEventType.iconService, &EventGetIcon);
+ CreateServiceFunction(dbEventType.textService, &GetEventText);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildContactMenu);
+ HookEvent(ME_DB_EVENT_FILTER_ADD, HookSendMsg);
+ HookEvent(ME_OPT_INITIALISE, GpgOptInit);
+ HookEvent(ME_PROTO_ACK, onProtoAck);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown);
+
+ InitIconLib();
+ init_vars();
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Comtact menu items
+
+ CMenuItem mi(&g_plugin);
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_SECURED);
+
+ SET_UID(mi, 0xbd22e3f8, 0xc19c, 0x45a8, 0xb7, 0x37, 0x6b, 0x3b, 0x27, 0xf0, 0x8c, 0xbb);
+ mi.position = -0x7FFFFFFF;
+ mi.name.a = LPGEN("Load public GPG key");
+ mi.pszService = "/LoadPubKey";
+ Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, LoadKey);
+
+ SET_UID(mi, 0xc8008193, 0x56a9, 0x414a, 0x82, 0x98, 0x78, 0xe8, 0xa8, 0x84, 0x20, 0x67);
+ mi.position = -0x7FFFFFFe;
+ mi.name.a = LPGEN("Toggle GPG encryption");
+ mi.pszService = "/ToggleEncryption";
+ g_plugin.hToggleEncryption = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, ToggleEncryption);
+
+ SET_UID(mi, 0x42bb535f, 0xd58e, 0x4edb, 0xbf, 0x2c, 0xfa, 0x9a, 0xbf, 0x1e, 0xb8, 0x69);
+ mi.position = -0x7FFFFFFd;
+ mi.name.a = LPGEN("Send public key");
+ mi.pszService = "/SendKey";
+ g_plugin.hSendKey = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, SendKey);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Main menu items
+
+ SET_UID(mi, 0x0bac023bb, 0xd2e, 0x46e0, 0x93, 0x13, 0x7c, 0xf9, 0xf6, 0xb5, 0x02, 0xd1);
+ mi.position = -0x7FFFFFFe;
+ mi.name.a = "GPG";
+ mi.root = Menu_AddMainMenuItem(&mi);
+ mi.flags = CMIF_UNMOVABLE;
+
+ SET_UID(mi, 0x33a204b2, 0xe3c0, 0x413b, 0xbf, 0xd8, 0x8b, 0x2e, 0x3d, 0xa0, 0xef, 0xa4);
+ mi.position = -0x7FFFFFFe;
+ mi.name.a = LPGEN("Export GPG Public keys");
+ mi.pszService = "/ExportGPGKeys";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, ExportGpGKeys);
+
+ SET_UID(mi, 0x627fcfc1, 0x4e60, 0x4428, 0xaf, 0x96, 0x11, 0x42, 0x24, 0xeb, 0x07, 0xea);
+ mi.position = -0x7FFFFFFF;
+ mi.name.a = LPGEN("Import GPG Public keys");
+ mi.pszService = "/ImportGPGKeys";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, ImportGpGKeys);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Extra icon
+
+ hCLIcon = ExtraIcon_RegisterIcolib(MODULENAME, Translate("GPG encryption status"), "secured", &onExtraIconPressed);
+ for (auto &cc : Contacts())
+ if (isContactHaveKey(cc))
+ setSrmmIcon(cc);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern list<wstring> transfers;
+
+int CMPlugin::Unload()
+{
+ for (auto p : transfers)
+ if (!p.empty())
+ boost::filesystem::remove(p);
+
+ clean_temp_dir();
+ return 0;
+}
diff --git a/plugins/New_GPG/src/jabber_account.h b/plugins/New_GPG/src/jabber_account.h index 5026a410f9..b011f70426 100644 --- a/plugins/New_GPG/src/jabber_account.h +++ b/plugins/New_GPG/src/jabber_account.h @@ -1,52 +1,52 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef JABBER_ACCOUNT_H -#define JABBER_ACCOUNT_H - -class JabberAccount -{ - wchar_t *AccountName = nullptr; - int AccountNumber = -1; - IJabberInterface *JabberInterface = nullptr; - HJHANDLER hSendHandler = INVALID_HANDLE_VALUE, hPresenceHandler = INVALID_HANDLE_VALUE, hMessageHandler = INVALID_HANDLE_VALUE; - -public: - __forceinline JabberAccount() - { - } - - __forceinline ~JabberAccount() - { - mir_free(AccountName); - } - - __forceinline void setAccountName(wchar_t *Name) { AccountName = Name; } - __forceinline void setAccountNumber(int Number) { AccountNumber = Number; } - __forceinline void setJabberInterface(IJabberInterface *JIf) { JabberInterface = JIf; } - __forceinline void setSendHandler(HJHANDLER hHandler) { hSendHandler = hHandler; } - __forceinline void setPresenceHandler(HJHANDLER hHandler) { hPresenceHandler = hHandler; } - __forceinline void setMessageHandler(HJHANDLER hHandler) { hMessageHandler = hHandler; } - - __forceinline wchar_t* getAccountName() const { return AccountName; } - __forceinline int getAccountNumber() const { return AccountNumber; } - __forceinline IJabberInterface* getJabberInterface() const { return JabberInterface; } - __forceinline HJHANDLER getSendHandler() const { return hSendHandler; } - __forceinline HJHANDLER getPresenceHandler() const { return hPresenceHandler; } - __forceinline HJHANDLER getMessageHandler() const { return hMessageHandler; } -}; - -#endif +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef JABBER_ACCOUNT_H
+#define JABBER_ACCOUNT_H
+
+class JabberAccount
+{
+ wchar_t *AccountName = nullptr;
+ int AccountNumber = -1;
+ IJabberInterface *JabberInterface = nullptr;
+ HJHANDLER hSendHandler = INVALID_HANDLE_VALUE, hPresenceHandler = INVALID_HANDLE_VALUE, hMessageHandler = INVALID_HANDLE_VALUE;
+
+public:
+ __forceinline JabberAccount()
+ {
+ }
+
+ __forceinline ~JabberAccount()
+ {
+ mir_free(AccountName);
+ }
+
+ __forceinline void setAccountName(wchar_t *Name) { AccountName = Name; }
+ __forceinline void setAccountNumber(int Number) { AccountNumber = Number; }
+ __forceinline void setJabberInterface(IJabberInterface *JIf) { JabberInterface = JIf; }
+ __forceinline void setSendHandler(HJHANDLER hHandler) { hSendHandler = hHandler; }
+ __forceinline void setPresenceHandler(HJHANDLER hHandler) { hPresenceHandler = hHandler; }
+ __forceinline void setMessageHandler(HJHANDLER hHandler) { hMessageHandler = hHandler; }
+
+ __forceinline wchar_t* getAccountName() const { return AccountName; }
+ __forceinline int getAccountNumber() const { return AccountNumber; }
+ __forceinline IJabberInterface* getJabberInterface() const { return JabberInterface; }
+ __forceinline HJHANDLER getSendHandler() const { return hSendHandler; }
+ __forceinline HJHANDLER getPresenceHandler() const { return hPresenceHandler; }
+ __forceinline HJHANDLER getMessageHandler() const { return hMessageHandler; }
+};
+
+#endif
diff --git a/plugins/New_GPG/src/log.cpp b/plugins/New_GPG/src/log.cpp index 7fa5caf3a0..930ad56167 100644 --- a/plugins/New_GPG/src/log.cpp +++ b/plugins/New_GPG/src/log.cpp @@ -1,49 +1,49 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -static string time_str() -{ - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - return (string)boost::posix_time::to_simple_string(now) + ": "; -} - -logtofile &logtofile::operator<<(const char *buf) -{ - if (bEnabled) - mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf); - return *this; -} - -logtofile& logtofile::operator<<(const string &buf) -{ - if (bEnabled) - mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf.c_str()); - return *this; -} - -void logtofile::init() -{ - if (g_plugin.bDebugLog) - hLogger = mir_createLog("NewGPG", L"NewGPG log file", g_plugin.getMStringW("szLogFilePath", L"C:\\GPGdebug.log"), 0); - else { - mir_closeLog(hLogger); - hLogger = nullptr; - } - - bEnabled = g_plugin.bDebugLog; -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+static string time_str()
+{
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ return (string)boost::posix_time::to_simple_string(now) + ": ";
+}
+
+logtofile &logtofile::operator<<(const char *buf)
+{
+ if (bEnabled)
+ mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf);
+ return *this;
+}
+
+logtofile& logtofile::operator<<(const string &buf)
+{
+ if (bEnabled)
+ mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf.c_str());
+ return *this;
+}
+
+void logtofile::init()
+{
+ if (g_plugin.bDebugLog)
+ hLogger = mir_createLog("NewGPG", L"NewGPG log file", g_plugin.getMStringW("szLogFilePath", L"C:\\GPGdebug.log"), 0);
+ else {
+ mir_closeLog(hLogger);
+ hLogger = nullptr;
+ }
+
+ bEnabled = g_plugin.bDebugLog;
+}
diff --git a/plugins/New_GPG/src/log.h b/plugins/New_GPG/src/log.h index b478f780aa..91328edb21 100644 --- a/plugins/New_GPG/src/log.h +++ b/plugins/New_GPG/src/log.h @@ -1,32 +1,32 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef LOG_H -#define LOG_H - -class logtofile -{ - HANDLE hLogger; - bool bEnabled = false; - -public: - logtofile& operator<<(const char *buf); - logtofile& operator<<(const std::string &buf); - void init(); - - __forceinline operator bool() const { return bEnabled; } -}; - -#endif +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#ifndef LOG_H
+#define LOG_H
+
+class logtofile
+{
+ HANDLE hLogger;
+ bool bEnabled = false;
+
+public:
+ logtofile& operator<<(const char *buf);
+ logtofile& operator<<(const std::string &buf);
+ void init();
+
+ __forceinline operator bool() const { return bEnabled; }
+};
+
+#endif
diff --git a/plugins/New_GPG/src/main.cpp b/plugins/New_GPG/src/main.cpp index bcaafe6ac0..6f2ed6c799 100644 --- a/plugins/New_GPG/src/main.cpp +++ b/plugins/New_GPG/src/main.cpp @@ -1,654 +1,654 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -#pragma comment(lib, "shlwapi.lib") - -///////////////////////////////////////////////////////////////////////////////////////// -// GPG binaries options - -class CDlgGpgBinOpts : public CDlgBase -{ - CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR, btn_OK, btn_GENERATE_RANDOM; - CCtrlEdit edit_BIN_PATH, edit_HOME_DIR; - CCtrlCheck chk_AUTO_EXCHANGE; - -public: - CDlgGpgBinOpts() : - CDlgBase(g_plugin, IDD_BIN_PATH), - btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), - btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR), - btn_OK(this, ID_OK), - btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM), - edit_BIN_PATH(this, IDC_BIN_PATH), - edit_HOME_DIR(this, IDC_HOME_DIR), - chk_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE) - { - btn_SET_BIN_PATH.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_BIN_PATH); - btn_SET_HOME_DIR.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_HOME_DIR); - btn_OK.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_OK); - btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_GENERATE_RANDOM); - } - - bool OnInitDialog() override - { - CMStringW path; - bool gpg_exists = false, lang_exists = false; - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - - CMStringW gpg_path(mir_path); gpg_path.Append(L"\\GnuPG\\gpg.exe"); - CMStringW gpg_lang_path(mir_path); gpg_lang_path.Append(L"\\GnuPG\\gnupg.nls\\en@quot.mo"); - - if (boost::filesystem::exists(gpg_path.c_str())) { - gpg_exists = true; - path = L"GnuPG\\gpg.exe"; - } - else path = gpg_path; - - if (boost::filesystem::exists(gpg_lang_path.c_str())) - lang_exists = true; - if (gpg_exists && !lang_exists) - MessageBox(nullptr, TranslateT("GPG binary found in Miranda folder, but English locale does not exist.\nIt's highly recommended that you place \\gnupg.nls\\en@quot.mo in GnuPG folder under Miranda root.\nWithout this file you may experience many problems with GPG output on non-English systems\nand plugin may completely not work.\nYou have been warned."), TranslateT("Warning"), MB_OK); - - DWORD len = MAX_PATH; - bool bad_version = false; - { - ptrW tmp; - if (!gpg_exists) { - tmp = g_plugin.getWStringA("szGpgBinPath", (SHGetValueW(HKEY_CURRENT_USER, L"Software\\GNU\\GnuPG", L"gpgProgram", 0, (void *)path.c_str(), &len) == ERROR_SUCCESS) ? path.c_str() : L""); - if (tmp[0]) - if (!boost::filesystem::exists((wchar_t *)tmp)) - MessageBoxW(nullptr, TranslateT("Wrong GPG binary location found in system.\nPlease choose another location"), TranslateT("Warning"), MB_OK); - } - else tmp = mir_wstrdup(path.c_str()); - - edit_BIN_PATH.SetText(tmp); - if (gpg_exists/* && lang_exists*/) { - g_plugin.setWString("szGpgBinPath", tmp); - - gpg_execution_params params; - params.addParam(L"--version"); - bool _gpg_valid = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = _gpg_valid; //TODO: check this - g_plugin.delSetting("szGpgBinPath"); - int p1 = params.out.Find("(GnuPG) "); - if (p1 != -1) { - p1 += mir_strlen("(GnuPG) "); - if (params.out[p1] != '1') - bad_version = true; - } - else { - bad_version = false; - MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Error"), MB_OK); - } - if (bad_version) - MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - } - } - - CMStringW tmp(g_plugin.getMStringW("szHomePath")); - if (tmp.IsEmpty()) { - mir_wstrcat(mir_path, L"\\gpg"); - if (_waccess(mir_path, 0) != -1) { - tmp = mir_path; - MessageBoxW(nullptr, TranslateT("\"GPG\" directory found in Miranda root.\nAssuming it's GPG home directory.\nGPG home directory set."), TranslateT("Info"), MB_OK); - } - else { - wstring path_ = _wgetenv(L"APPDATA"); - path_ += L"\\GnuPG"; - tmp = path_.c_str(); - } - } - edit_HOME_DIR.SetText(!gpg_exists ? tmp : L"gpg"); - - // TODO: additional check for write access - if (gpg_exists && lang_exists && !bad_version) - MessageBox(nullptr, TranslateT("Your GPG version is supported. The language file was found.\nGPG plugin should work fine.\nPress OK to continue."), TranslateT("Info"), MB_OK); - chk_AUTO_EXCHANGE.Enable(); - return true; - } - - void OnDestroy() override - { - void InitCheck(); - InitCheck(); - } - - void onClick_SET_BIN_PATH(CCtrlButton *) - { - GetFilePath(L"Choose gpg.exe", "szGpgBinPath", L"*.exe", L"EXE Executables"); - CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); - edit_BIN_PATH.SetText(tmp); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_BIN_PATH.SetText(path); - } - } - - void onClick_SET_HOME_DIR(CCtrlButton *) - { - GetFolderPath(L"Set home directory"); - CMStringW tmp(g_plugin.getMStringW("szHomePath")); - edit_HOME_DIR.SetText(tmp); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_HOME_DIR.SetText(path); - } - } - - void onClick_OK(CCtrlButton *) - { - if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) { - gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText()); - globals.gpg_valid = true; - g_plugin.setByte("FirstRun", 0); - this->Hide(); - this->Close(); - ShowFirstRunDialog(); - } - } - - void onClick_GENERATE_RANDOM(CCtrlButton *) - { - if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) { - gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText()); - globals.gpg_valid = true; - if (gpg_use_new_random_key(nullptr)) { - g_plugin.bAutoExchange = chk_AUTO_EXCHANGE.GetState(); - globals.gpg_valid = true; - g_plugin.setByte("FirstRun", 0); - this->Close(); - } - } - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static int EnumProc(const char *szSetting, void *param) -{ - auto *list = (OBJLIST<CMStringA> *)param; - if (strchr(szSetting, '(') && strchr(szSetting, ')')) - list->insert(new CMStringA(szSetting)); - return 0; -} - -void FirstRun() -{ - if (g_plugin.getByte("CompatLevel") != 1) { - OBJLIST<CMStringA> settings(1); - db_enum_settings(0, EnumProc, MODULENAME, &settings); - - for (auto &it : settings) { - CMStringA newName(*it); - int p1 = newName.Find('('); - newName.Delete(0, p1+1); - int p2 = newName.Find(')'); - if (p2 == -1) - continue; - newName.Delete(p2, 1); - - CMStringW val = g_plugin.getMStringW(it->c_str()); - g_plugin.delSetting(it->c_str()); - g_plugin.setWString(newName, val); - } - - g_plugin.setByte("CompatLevel", 1); - } - - if (g_plugin.getByte("FirstRun", 1)) - (new CDlgGpgBinOpts())->Show(); -} - -void InitCheck() -{ - // parse gpg output - { - ptrW current_home(g_plugin.getWStringA("szHomePath", L"")); - g_plugin.setWString("szHomePath", L""); //we do not need home for gpg binary validation - globals.gpg_valid = isGPGValid(); - g_plugin.setWString("szHomePath", current_home); //return current home dir back - } - { - bool home_dir_access = false, temp_access = false; - std::wstring test_path(ptrW(g_plugin.getWStringA("szHomePath", L""))); - test_path += L"/"; - test_path += toUTF16(get_random(13)); - wfstream test_file; - test_file.open(test_path, std::ios::trunc | std::ios::out); - if (test_file.is_open() && test_file.good()) { - test_file << L"access_test\n"; - if (test_file.good()) - home_dir_access = true; - test_file.close(); - boost::filesystem::remove(test_path); - } - - test_path = _wgetenv(L"TEMP"); - test_path += L"/"; - test_path += toUTF16(get_random(13)); - test_file.open(test_path, std::ios::trunc | std::ios::out); - if (test_file.is_open() && test_file.good()) { - test_file << L"access_test\n"; - if (test_file.good()) - temp_access = true; - test_file.close(); - boost::filesystem::remove(test_path); - } - if (!home_dir_access || !temp_access || !globals.gpg_valid) { - CMStringW buf; - buf.Append(globals.gpg_valid ? TranslateT("GPG binary is set and valid (this is good).\n") : TranslateT("GPG binary unset or invalid (plugin will not work).\n")); - buf.Append(home_dir_access ? TranslateT("Home dir write access granted (this is good).\n") : TranslateT("Home dir has no write access (plugin most probably will not work).\n")); - buf.Append(temp_access ? TranslateT("Temp dir write access granted (this is good).\n") : TranslateT("Temp dir has no write access (plugin should work, but may have some problems, file transfers will not work).")); - if (!globals.gpg_valid) - buf.Append(TranslateT("\nGPG will be disabled until you solve these problems")); - MessageBox(nullptr, buf, TranslateT("GPG plugin problems"), MB_OK); - } - if (!globals.gpg_valid) - return; - globals.gpg_keyexist = isGPGKeyExist(); - - wstring::size_type p = 0, p2 = 0; - - gpg_execution_params params; - params.addParam(L"--list-secret-keys"); - params.addParam(L"--batch"); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - _wmkdir(g_plugin.getMStringW("szHomePath") + L"\\tmp"); - string out(params.out); - - CMStringW wszQuestion; - for (auto &pa : Accounts()) { - if (StriStr(pa->szModuleName, "metacontacts")) - continue; - if (StriStr(pa->szModuleName, "weather")) - continue; - - std::string acc = pa->szModuleName; - acc += "_KeyID"; - CMStringA keyid = g_plugin.getMStringA(acc.c_str()); - if (!keyid.IsEmpty()) { - wszQuestion = TranslateT("Your secret key with ID: "); - keyid = g_plugin.getMStringA("KeyID"); - if ((p = out.find(keyid)) == string::npos) { - wszQuestion += keyid; - wszQuestion += TranslateT(" for account "); - wszQuestion += pa->tszAccountName; - wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - p2 = p; - p = out.find("[", p); - p2 = out.find("\n", p2); - if ((p != std::string::npos) && (p < p2)) { - p = out.find("expires:", p); - p += mir_strlen("expires:"); - p++; - p2 = out.find("]", p); - wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - bool expired = false; - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - wchar_t buf[5]; - wcsncpy_s(buf, expire_date, _TRUNCATE); - int year = _wtoi(buf); - if (year < now.date().year()) - expired = true; - else if (year == now.date().year()) { - wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); - int month = _wtoi(buf); - if (month < now.date().month()) - expired = true; - else if (month == now.date().month()) { - wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); - unsigned day = _wtoi(buf); - if (day <= now.date().day_number()) - expired = true; - } - } - } - if (expired) { - wszQuestion += keyid; - wszQuestion += TranslateT(" for account "); - wszQuestion += pa->tszAccountName; - wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion.c_str(), TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - mir_free(expire_date); - } - } - } - - wszQuestion = TranslateT("Your secret key with ID: "); - CMStringA keyid(g_plugin.getMStringA("KeyID")); - CMStringA key(g_plugin.getMStringA("GPGPubKey")); - if (!g_plugin.getByte("FirstRun", 1) && (keyid.IsEmpty() || key.IsEmpty())) { - wszQuestion = TranslateT("You didn't set a private key.\nWould you like to set it now?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own private key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - if ((p = out.find(keyid)) == string::npos) { - wszQuestion += keyid; - wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - p2 = p; - p = out.find("[", p); - p2 = out.find("\n", p2); - if ((p != std::string::npos) && (p < p2)) { - p = out.find("expires:", p); - p += mir_strlen("expires:"); - p++; - p2 = out.find("]", p); - wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - bool expired = false; - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - wchar_t buf[5]; - wcsncpy_s(buf, expire_date, _TRUNCATE); - int year = _wtoi(buf); - if (year < now.date().year()) - expired = true; - else if (year == now.date().year()) { - wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); - int month = _wtoi(buf); - if (month < now.date().month()) - expired = true; - else if (month == now.date().month()) { - wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); - unsigned day = _wtoi(buf); - if (day <= now.date().day_number()) - expired = true; - } - } - } - if (expired) { - wszQuestion += keyid; - wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - mir_free(expire_date); - } - // TODO: check for expired key - } - { - CMStringW path(g_plugin.getMStringW("szHomePath")); - uint32_t dwFileAttr = GetFileAttributes(path); - if (dwFileAttr != INVALID_FILE_ATTRIBUTES) { - dwFileAttr &= ~FILE_ATTRIBUTE_READONLY; - SetFileAttributes(path, dwFileAttr); - } - } -} - -void ImportKey(MCONTACT hContact, std::wstring new_key) -{ - bool for_all_sub = false; - if (db_mc_isMeta(hContact)) { - if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) - for_all_sub = true; - - if (for_all_sub) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setWString(hcnt, "GPGPubKey", new_key.c_str()); - } - } - else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", new_key.c_str()); - } - else g_plugin.setWString(hContact, "GPGPubKey", new_key.c_str()); - - // gpg execute block - CMStringW tmp2 = g_plugin.getMStringW("szHomePath"); - tmp2 += L"\\temporary_exported.asc"; - boost::filesystem::remove(tmp2.c_str()); - - CMStringW ptmp; - if (db_mc_isMeta(hContact)) - ptmp = g_plugin.getMStringW(metaGetMostOnline(hContact), "GPGPubKey"); - else - ptmp = g_plugin.getMStringW(hContact, "GPGPubKey"); - - wfstream f(tmp2, std::ios::out); - f << ptmp.c_str(); - f.close(); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - string output(params.out); - if (db_mc_isMeta(hContact)) { - if (for_all_sub) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) { - char *tmp = nullptr; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - g_plugin.setString(hcnt, "KeyID", output.substr(s, s2 - s).c_str()); - s = output.find(RUS_QUOTE, s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else s += sizeof(RUS_QUOTE) - 1; - - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find(RUS_ANGLE, s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyMainName", tmp); - mir_free(tmp); - } - - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyComment", tmp); - mir_free(tmp); - s += 3; - s2 = output.find(">", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyMainEmail", tmp); - mir_free(tmp); - } - } - else { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp); - } - } - g_plugin.delSetting(hcnt, "bAlwatsTrust"); - } - } - } - else { - char *tmp = nullptr; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - g_plugin.setString(metaGetMostOnline(hContact), "KeyID", output.substr(s, s2 - s).c_str()); - s = output.find(RUS_QUOTE, s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else s += sizeof(RUS_QUOTE) - 1; - - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find(RUS_ANGLE, s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", tmp); - mir_free(tmp); - } - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", tmp); - mir_free(tmp); - s += 3; - s2 = output.find(">", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", tmp); - mir_free(tmp); - } - } - else { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp); - } - } - g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust"); - } - } - else { - char *tmp = nullptr; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - g_plugin.setString(hContact, "KeyID", output.substr(s, s2 - s).c_str()); - s = output.find(RUS_QUOTE, s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else s += sizeof(RUS_QUOTE) - 1; - - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find(RUS_ANGLE, s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyMainName", tmp); - mir_free(tmp); - } - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyComment", tmp); - mir_free(tmp); - s += 3; - s2 = output.find(">", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyMainEmail", tmp); - mir_free(tmp); - } - } - else { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp); - } - } - g_plugin.delSetting(hContact, "bAlwatsTrust"); - } - - MessageBox(nullptr, toUTF16(output).c_str(), L"", MB_OK); - boost::filesystem::remove(tmp2.c_str()); -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+#pragma comment(lib, "shlwapi.lib")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GPG binaries options
+
+class CDlgGpgBinOpts : public CDlgBase
+{
+ CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR, btn_OK, btn_GENERATE_RANDOM;
+ CCtrlEdit edit_BIN_PATH, edit_HOME_DIR;
+ CCtrlCheck chk_AUTO_EXCHANGE;
+
+public:
+ CDlgGpgBinOpts() :
+ CDlgBase(g_plugin, IDD_BIN_PATH),
+ btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH),
+ btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR),
+ btn_OK(this, ID_OK),
+ btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM),
+ edit_BIN_PATH(this, IDC_BIN_PATH),
+ edit_HOME_DIR(this, IDC_HOME_DIR),
+ chk_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE)
+ {
+ btn_SET_BIN_PATH.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_BIN_PATH);
+ btn_SET_HOME_DIR.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_HOME_DIR);
+ btn_OK.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_OK);
+ btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_GENERATE_RANDOM);
+ }
+
+ bool OnInitDialog() override
+ {
+ CMStringW path;
+ bool gpg_exists = false, lang_exists = false;
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+
+ CMStringW gpg_path(mir_path); gpg_path.Append(L"\\GnuPG\\gpg.exe");
+ CMStringW gpg_lang_path(mir_path); gpg_lang_path.Append(L"\\GnuPG\\gnupg.nls\\en@quot.mo");
+
+ if (boost::filesystem::exists(gpg_path.c_str())) {
+ gpg_exists = true;
+ path = L"GnuPG\\gpg.exe";
+ }
+ else path = gpg_path;
+
+ if (boost::filesystem::exists(gpg_lang_path.c_str()))
+ lang_exists = true;
+ if (gpg_exists && !lang_exists)
+ MessageBox(nullptr, TranslateT("GPG binary found in Miranda folder, but English locale does not exist.\nIt's highly recommended that you place \\gnupg.nls\\en@quot.mo in GnuPG folder under Miranda root.\nWithout this file you may experience many problems with GPG output on non-English systems\nand plugin may completely not work.\nYou have been warned."), TranslateT("Warning"), MB_OK);
+
+ DWORD len = MAX_PATH;
+ bool bad_version = false;
+ {
+ ptrW tmp;
+ if (!gpg_exists) {
+ tmp = g_plugin.getWStringA("szGpgBinPath", (SHGetValueW(HKEY_CURRENT_USER, L"Software\\GNU\\GnuPG", L"gpgProgram", 0, (void *)path.c_str(), &len) == ERROR_SUCCESS) ? path.c_str() : L"");
+ if (tmp[0])
+ if (!boost::filesystem::exists((wchar_t *)tmp))
+ MessageBoxW(nullptr, TranslateT("Wrong GPG binary location found in system.\nPlease choose another location"), TranslateT("Warning"), MB_OK);
+ }
+ else tmp = mir_wstrdup(path.c_str());
+
+ edit_BIN_PATH.SetText(tmp);
+ if (gpg_exists/* && lang_exists*/) {
+ g_plugin.setWString("szGpgBinPath", tmp);
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+ bool _gpg_valid = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = _gpg_valid; //TODO: check this
+ g_plugin.delSetting("szGpgBinPath");
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 != -1) {
+ p1 += mir_strlen("(GnuPG) ");
+ if (params.out[p1] != '1')
+ bad_version = true;
+ }
+ else {
+ bad_version = false;
+ MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Error"), MB_OK);
+ }
+ if (bad_version)
+ MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ }
+ }
+
+ CMStringW tmp(g_plugin.getMStringW("szHomePath"));
+ if (tmp.IsEmpty()) {
+ mir_wstrcat(mir_path, L"\\gpg");
+ if (_waccess(mir_path, 0) != -1) {
+ tmp = mir_path;
+ MessageBoxW(nullptr, TranslateT("\"GPG\" directory found in Miranda root.\nAssuming it's GPG home directory.\nGPG home directory set."), TranslateT("Info"), MB_OK);
+ }
+ else {
+ wstring path_ = _wgetenv(L"APPDATA");
+ path_ += L"\\GnuPG";
+ tmp = path_.c_str();
+ }
+ }
+ edit_HOME_DIR.SetText(!gpg_exists ? tmp : L"gpg");
+
+ // TODO: additional check for write access
+ if (gpg_exists && lang_exists && !bad_version)
+ MessageBox(nullptr, TranslateT("Your GPG version is supported. The language file was found.\nGPG plugin should work fine.\nPress OK to continue."), TranslateT("Info"), MB_OK);
+ chk_AUTO_EXCHANGE.Enable();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ void InitCheck();
+ InitCheck();
+ }
+
+ void onClick_SET_BIN_PATH(CCtrlButton *)
+ {
+ GetFilePath(L"Choose gpg.exe", "szGpgBinPath", L"*.exe", L"EXE Executables");
+ CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
+ edit_BIN_PATH.SetText(tmp);
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_BIN_PATH.SetText(path);
+ }
+ }
+
+ void onClick_SET_HOME_DIR(CCtrlButton *)
+ {
+ GetFolderPath(L"Set home directory");
+ CMStringW tmp(g_plugin.getMStringW("szHomePath"));
+ edit_HOME_DIR.SetText(tmp);
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_HOME_DIR.SetText(path);
+ }
+ }
+
+ void onClick_OK(CCtrlButton *)
+ {
+ if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) {
+ gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText());
+ globals.gpg_valid = true;
+ g_plugin.setByte("FirstRun", 0);
+ this->Hide();
+ this->Close();
+ ShowFirstRunDialog();
+ }
+ }
+
+ void onClick_GENERATE_RANDOM(CCtrlButton *)
+ {
+ if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) {
+ gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText());
+ globals.gpg_valid = true;
+ if (gpg_use_new_random_key(nullptr)) {
+ g_plugin.bAutoExchange = chk_AUTO_EXCHANGE.GetState();
+ globals.gpg_valid = true;
+ g_plugin.setByte("FirstRun", 0);
+ this->Close();
+ }
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int EnumProc(const char *szSetting, void *param)
+{
+ auto *list = (OBJLIST<CMStringA> *)param;
+ if (strchr(szSetting, '(') && strchr(szSetting, ')'))
+ list->insert(new CMStringA(szSetting));
+ return 0;
+}
+
+void FirstRun()
+{
+ if (g_plugin.getByte("CompatLevel") != 1) {
+ OBJLIST<CMStringA> settings(1);
+ db_enum_settings(0, EnumProc, MODULENAME, &settings);
+
+ for (auto &it : settings) {
+ CMStringA newName(*it);
+ int p1 = newName.Find('(');
+ newName.Delete(0, p1+1);
+ int p2 = newName.Find(')');
+ if (p2 == -1)
+ continue;
+ newName.Delete(p2, 1);
+
+ CMStringW val = g_plugin.getMStringW(it->c_str());
+ g_plugin.delSetting(it->c_str());
+ g_plugin.setWString(newName, val);
+ }
+
+ g_plugin.setByte("CompatLevel", 1);
+ }
+
+ if (g_plugin.getByte("FirstRun", 1))
+ (new CDlgGpgBinOpts())->Show();
+}
+
+void InitCheck()
+{
+ // parse gpg output
+ {
+ ptrW current_home(g_plugin.getWStringA("szHomePath", L""));
+ g_plugin.setWString("szHomePath", L""); //we do not need home for gpg binary validation
+ globals.gpg_valid = isGPGValid();
+ g_plugin.setWString("szHomePath", current_home); //return current home dir back
+ }
+ {
+ bool home_dir_access = false, temp_access = false;
+ std::wstring test_path(ptrW(g_plugin.getWStringA("szHomePath", L"")));
+ test_path += L"/";
+ test_path += toUTF16(get_random(13));
+ wfstream test_file;
+ test_file.open(test_path, std::ios::trunc | std::ios::out);
+ if (test_file.is_open() && test_file.good()) {
+ test_file << L"access_test\n";
+ if (test_file.good())
+ home_dir_access = true;
+ test_file.close();
+ boost::filesystem::remove(test_path);
+ }
+
+ test_path = _wgetenv(L"TEMP");
+ test_path += L"/";
+ test_path += toUTF16(get_random(13));
+ test_file.open(test_path, std::ios::trunc | std::ios::out);
+ if (test_file.is_open() && test_file.good()) {
+ test_file << L"access_test\n";
+ if (test_file.good())
+ temp_access = true;
+ test_file.close();
+ boost::filesystem::remove(test_path);
+ }
+ if (!home_dir_access || !temp_access || !globals.gpg_valid) {
+ CMStringW buf;
+ buf.Append(globals.gpg_valid ? TranslateT("GPG binary is set and valid (this is good).\n") : TranslateT("GPG binary unset or invalid (plugin will not work).\n"));
+ buf.Append(home_dir_access ? TranslateT("Home dir write access granted (this is good).\n") : TranslateT("Home dir has no write access (plugin most probably will not work).\n"));
+ buf.Append(temp_access ? TranslateT("Temp dir write access granted (this is good).\n") : TranslateT("Temp dir has no write access (plugin should work, but may have some problems, file transfers will not work)."));
+ if (!globals.gpg_valid)
+ buf.Append(TranslateT("\nGPG will be disabled until you solve these problems"));
+ MessageBox(nullptr, buf, TranslateT("GPG plugin problems"), MB_OK);
+ }
+ if (!globals.gpg_valid)
+ return;
+ globals.gpg_keyexist = isGPGKeyExist();
+
+ wstring::size_type p = 0, p2 = 0;
+
+ gpg_execution_params params;
+ params.addParam(L"--list-secret-keys");
+ params.addParam(L"--batch");
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ _wmkdir(g_plugin.getMStringW("szHomePath") + L"\\tmp");
+ string out(params.out);
+
+ CMStringW wszQuestion;
+ for (auto &pa : Accounts()) {
+ if (StriStr(pa->szModuleName, "metacontacts"))
+ continue;
+ if (StriStr(pa->szModuleName, "weather"))
+ continue;
+
+ std::string acc = pa->szModuleName;
+ acc += "_KeyID";
+ CMStringA keyid = g_plugin.getMStringA(acc.c_str());
+ if (!keyid.IsEmpty()) {
+ wszQuestion = TranslateT("Your secret key with ID: ");
+ keyid = g_plugin.getMStringA("KeyID");
+ if ((p = out.find(keyid)) == string::npos) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" for account ");
+ wszQuestion += pa->tszAccountName;
+ wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ p2 = p;
+ p = out.find("[", p);
+ p2 = out.find("\n", p2);
+ if ((p != std::string::npos) && (p < p2)) {
+ p = out.find("expires:", p);
+ p += mir_strlen("expires:");
+ p++;
+ p2 = out.find("]", p);
+ wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ bool expired = false;
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ wchar_t buf[5];
+ wcsncpy_s(buf, expire_date, _TRUNCATE);
+ int year = _wtoi(buf);
+ if (year < now.date().year())
+ expired = true;
+ else if (year == now.date().year()) {
+ wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
+ int month = _wtoi(buf);
+ if (month < now.date().month())
+ expired = true;
+ else if (month == now.date().month()) {
+ wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
+ unsigned day = _wtoi(buf);
+ if (day <= now.date().day_number())
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" for account ");
+ wszQuestion += pa->tszAccountName;
+ wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion.c_str(), TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ mir_free(expire_date);
+ }
+ }
+ }
+
+ wszQuestion = TranslateT("Your secret key with ID: ");
+ CMStringA keyid(g_plugin.getMStringA("KeyID"));
+ CMStringA key(g_plugin.getMStringA("GPGPubKey"));
+ if (!g_plugin.getByte("FirstRun", 1) && (keyid.IsEmpty() || key.IsEmpty())) {
+ wszQuestion = TranslateT("You didn't set a private key.\nWould you like to set it now?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own private key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ if ((p = out.find(keyid)) == string::npos) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ p2 = p;
+ p = out.find("[", p);
+ p2 = out.find("\n", p2);
+ if ((p != std::string::npos) && (p < p2)) {
+ p = out.find("expires:", p);
+ p += mir_strlen("expires:");
+ p++;
+ p2 = out.find("]", p);
+ wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ bool expired = false;
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ wchar_t buf[5];
+ wcsncpy_s(buf, expire_date, _TRUNCATE);
+ int year = _wtoi(buf);
+ if (year < now.date().year())
+ expired = true;
+ else if (year == now.date().year()) {
+ wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
+ int month = _wtoi(buf);
+ if (month < now.date().month())
+ expired = true;
+ else if (month == now.date().month()) {
+ wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
+ unsigned day = _wtoi(buf);
+ if (day <= now.date().day_number())
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ mir_free(expire_date);
+ }
+ // TODO: check for expired key
+ }
+ {
+ CMStringW path(g_plugin.getMStringW("szHomePath"));
+ uint32_t dwFileAttr = GetFileAttributes(path);
+ if (dwFileAttr != INVALID_FILE_ATTRIBUTES) {
+ dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
+ SetFileAttributes(path, dwFileAttr);
+ }
+ }
+}
+
+void ImportKey(MCONTACT hContact, std::wstring new_key)
+{
+ bool for_all_sub = false;
+ if (db_mc_isMeta(hContact)) {
+ if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES)
+ for_all_sub = true;
+
+ if (for_all_sub) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setWString(hcnt, "GPGPubKey", new_key.c_str());
+ }
+ }
+ else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", new_key.c_str());
+ }
+ else g_plugin.setWString(hContact, "GPGPubKey", new_key.c_str());
+
+ // gpg execute block
+ CMStringW tmp2 = g_plugin.getMStringW("szHomePath");
+ tmp2 += L"\\temporary_exported.asc";
+ boost::filesystem::remove(tmp2.c_str());
+
+ CMStringW ptmp;
+ if (db_mc_isMeta(hContact))
+ ptmp = g_plugin.getMStringW(metaGetMostOnline(hContact), "GPGPubKey");
+ else
+ ptmp = g_plugin.getMStringW(hContact, "GPGPubKey");
+
+ wfstream f(tmp2, std::ios::out);
+ f << ptmp.c_str();
+ f.close();
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ string output(params.out);
+ if (db_mc_isMeta(hContact)) {
+ if (for_all_sub) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt) {
+ char *tmp = nullptr;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ g_plugin.setString(hcnt, "KeyID", output.substr(s, s2 - s).c_str());
+ s = output.find(RUS_QUOTE, s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else s += sizeof(RUS_QUOTE) - 1;
+
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find(RUS_ANGLE, s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyMainName", tmp);
+ mir_free(tmp);
+ }
+
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyComment", tmp);
+ mir_free(tmp);
+ s += 3;
+ s2 = output.find(">", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ }
+ else {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp);
+ }
+ }
+ g_plugin.delSetting(hcnt, "bAlwatsTrust");
+ }
+ }
+ }
+ else {
+ char *tmp = nullptr;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyID", output.substr(s, s2 - s).c_str());
+ s = output.find(RUS_QUOTE, s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else s += sizeof(RUS_QUOTE) - 1;
+
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find(RUS_ANGLE, s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", tmp);
+ mir_free(tmp);
+ }
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", tmp);
+ mir_free(tmp);
+ s += 3;
+ s2 = output.find(">", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ }
+ else {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp);
+ }
+ }
+ g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust");
+ }
+ }
+ else {
+ char *tmp = nullptr;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ g_plugin.setString(hContact, "KeyID", output.substr(s, s2 - s).c_str());
+ s = output.find(RUS_QUOTE, s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else s += sizeof(RUS_QUOTE) - 1;
+
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find(RUS_ANGLE, s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyMainName", tmp);
+ mir_free(tmp);
+ }
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyComment", tmp);
+ mir_free(tmp);
+ s += 3;
+ s2 = output.find(">", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ }
+ else {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp);
+ }
+ }
+ g_plugin.delSetting(hContact, "bAlwatsTrust");
+ }
+
+ MessageBox(nullptr, toUTF16(output).c_str(), L"", MB_OK);
+ boost::filesystem::remove(tmp2.c_str());
+}
diff --git a/plugins/New_GPG/src/messages.cpp b/plugins/New_GPG/src/messages.cpp index f79d09361c..c71281696d 100644 --- a/plugins/New_GPG/src/messages.cpp +++ b/plugins/New_GPG/src/messages.cpp @@ -1,788 +1,788 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -std::list<HANDLE> sent_msgs; - -struct RecvParams -{ - RecvParams(MCONTACT _p1, std::wstring _p2, const char *_p3, uint32_t _p4) : - hContact(_p1), - str(_p2), - msg(_p3), - timestamp(_p4) - {} - - MCONTACT hContact; - std::wstring str; - std::string msg; - uint32_t timestamp; -}; - -static void RecvMsgSvc_func(RecvParams *param) -{ - MCONTACT hContact = param->hContact; - std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact))); - - // check for gpg related data - wstring::size_type s1 = param->str.find(L"-----BEGIN PGP MESSAGE-----"); - wstring::size_type s2 = param->str.find(L"-----END PGP MESSAGE-----"); - if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block - if (!isContactSecured(hContact)) { - if (globals.debuglog) - globals.debuglog << "info: received encrypted message from: " + szScreenName + " with turned off encryption"; - if (MessageBox(nullptr, TranslateT("We received encrypted message from contact with encryption turned off.\nDo you want to turn on encryption for this contact?"), TranslateT("Warning"), MB_YESNO) == IDYES) { - if (!isContactHaveKey(hContact)) - ShowLoadPublicKeyDialog(hContact, true); - else { - g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1); - setSrmmIcon(hContact); - } - - if (isContactHaveKey(hContact)) { - g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1); - setSrmmIcon(hContact); - } - } - else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) { - HistoryLog(hContact, param->msg.c_str(), param->timestamp); - delete param; - return; - } - } - else if (globals.debuglog) - globals.debuglog << "info: received encrypted message from: " + szScreenName; - boost::algorithm::erase_all(param->str, "\r"); - s2 += mir_wstrlen(L"-----END PGP MESSAGE-----"); - - ptrW ptszHomePath(g_plugin.getWStringA("szHomePath", L"")); - wstring encfile = toUTF16(get_random(10)); - wstring decfile = toUTF16(get_random(10)); - { - wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile; - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - { - const int timeout = 5000, step = 100; - int count = 0; - - fstream f(path.c_str(), std::ios::out); - while (!f.is_open()) { - ::Sleep(step); - count += step; - if (count >= timeout) { - g_plugin.setByte(hContact, "GPGEncryption", 0); - setSrmmIcon(hContact); - globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock"; - delete param; - return; - } - f.open(path.c_str(), std::ios::out); - } - char *tmp = mir_u2a(param->str.substr(s1, s2 - s1).c_str()); - f << tmp; - mir_free(tmp); - f.close(); - } - - gpg_execution_params params; - params.addParam(L"--batch"); - { - CMStringA inkeyid = g_plugin.getMStringA(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID"); - CMStringW pass; - if (!inkeyid.IsEmpty()) { - string dbsetting = "szKey_"; - dbsetting += inkeyid; - dbsetting += "_Password"; - pass = g_plugin.getMStringW(dbsetting.c_str()); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to decrypt message from " + szScreenName + " with password"; - } - else { - pass = g_plugin.getMStringW("szKeyPassword"); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + szScreenName + " with password"; - } - if (!pass.IsEmpty()) { - params.addParam(L"--passphrase"); - params.addParam(pass.c_str()); - } - else if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName + " with password"; - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - else if (globals.debuglog) - globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + szScreenName + " without password"; - } - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e); - } - - params.addParam(L"--output"); - params.addParam(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile); - params.addParam(L"-d"); - params.addParam(L"-a"); - params.addParam(path); - - bool bRes = gpg_launcher(params); - if (!bRes || params.result == pxNotFound) { - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - SendErrorMessage(hContact); - HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); - delete param; - return; - } - - // TODO: check gpg output for errors - globals._terminate = false; - - string out(params.out); - while (out.find("public key decryption failed: bad passphrase") != string::npos) { - if (globals.debuglog) - globals.debuglog << "info: failed to decrypt message from " + szScreenName + " password needed, trying to get one"; - if (globals._terminate) { - SendErrorMessage(hContact); - break; - } - { - // save inkey id - string::size_type s = out.find(" encrypted with "); - s = out.find(" ID ", s); - s += mir_strlen(" ID "); - g_plugin.setString(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID", out.substr(s, out.find(",", s) - s).c_str()); - } - - CDlgKeyPasswordMsgBox(hContact).DoModal(); - - gpg_execution_params params2; - params2.aargv = params.aargv; - if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName; - - params2.addParam(L"--passphrase"); - params2.addParam(globals.wszPassword.c_str()); - } - - bRes = gpg_launcher(params2); - if (!bRes || params2.result == pxNotFound) { - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - } - - out.clear(); - bRes = gpg_launcher(params); - if (!bRes || params.result == pxNotFound) { - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e); - } - - if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) { - if (globals.debuglog) - globals.debuglog << "info: Failed to decrypt GPG encrypted message."; - - string str1 = param->msg; - str1.insert(0, "\n"); - str1.insert(0, TranslateU("Received unencrypted message:")); - - HistoryLog(hContact, str1.c_str(), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - - std::string str; - - wstring tszDecPath = wstring(ptszHomePath) + L"\\tmp\\" + decfile; - - fstream f(tszDecPath.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - if (f.is_open()) { - size_t size = f.tellg(); - char *tmp = new char[size + 1]; - f.seekg(0, std::ios::beg); - f.read(tmp, size); - tmp[size] = '\0'; - - str.append(tmp); - delete[] tmp; - f.close(); - if (!globals.debuglog) { - boost::system::error_code ec; - boost::filesystem::remove(tszDecPath, ec); - if (ec) { - //TODO: handle error - } - } - } - - if (str.empty()) { - if (globals.debuglog) - globals.debuglog << "info: Failed to decrypt GPG encrypted message."; - - string szMsg = param->msg; - szMsg.insert(0, TranslateU("Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n")); - - HistoryLog(hContact, param->msg.c_str(), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - - fix_line_term(str); - if (g_plugin.bAppendTags) { - str.insert(0, toUTF8(globals.wszInopentag.c_str())); - str.append(toUTF8(globals.wszInclosetag.c_str())); - } - - HistoryLog(hContact, str.c_str(), param->timestamp); - delete param; - return; - } - } - - if (g_plugin.getByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption")) - HistoryLog(hContact, param->msg.c_str(), param->timestamp, DBEF_READ); - else - HistoryLog(hContact, param->msg.c_str(), param->timestamp); - - delete param; -} - -INT_PTR RecvMsgSvc(WPARAM w, LPARAM l) -{ - CCSDATA *ccs = (CCSDATA*)l; - if (!ccs) - return Proto_ChainRecv(w, ccs); - - PROTORECVEVENT *pre = (PROTORECVEVENT*)(ccs->lParam); - if (!pre) - return Proto_ChainRecv(w, ccs); - - char *msg = pre->szMessage; - if (!msg) - return Proto_ChainRecv(w, ccs); - - if (db_mc_isMeta(ccs->hContact)) { - if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) - return Proto_ChainRecv(w, ccs); - else { - if (globals.debuglog) - globals.debuglog << "info: blocked pgp message to metacontact:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - return 0; - } - } - - wstring str = toUTF16(msg); - size_t s1, s2; - if (g_plugin.bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): parsing key response:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----"); - s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (s1 != wstring::npos && s2 != wstring::npos) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): found pubkey block:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); - g_plugin.setWString(ccs->hContact, "GPGPubKey", str.substr(s1, s2 - s1).c_str()); - { - // gpg execute block - CMStringW tmp2(g_plugin.getMStringW("szHomePath")); - tmp2 += L"\\"; - tmp2 += get_random(5).c_str(); - tmp2 += L".asc"; - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(tmp2.c_str(), e); - } - wfstream f(tmp2, std::ios::out); - { - const int timeout = 5000, step = 100; - int count = 0; - while (!f.is_open()) { - ::Sleep(step); - count += step; - if (count >= timeout) { - g_plugin.setByte(ccs->hContact, "GPGEncryption", 0); - setSrmmIcon(ccs->hContact); - globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock"; - return 1; - } - f.open(tmp2, std::ios::out); - } - } - f << g_plugin.getMStringW(ccs->hContact, "GPGPubKey").c_str(); - f.close(); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - return 1; - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(tmp2.c_str(), e); - } - if (params.result == pxNotFound) - return 1; - - string output(params.out); - s1 = output.find("gpg: key ") + mir_strlen("gpg: key "); - s2 = output.find(":", s1); - g_plugin.setString(ccs->hContact, "KeyID", output.substr(s1, s2 - s1).c_str()); - s2 += 2; - s1 = output.find(RUS_QUOTE, s2); - if (s1 == string::npos) { - s1 = output.find("\"", s2); - s1 += 1; - } - else s1 += sizeof(RUS_QUOTE) - 1; - - if ((s2 = output.find("(", s1)) == string::npos) - s2 = output.find("<", s1); - else if (s2 > output.find("<", s1)) - s2 = output.find("<", s1); - - char *tmp = (char*)mir_alloc(output.substr(s1, s2 - s1 - 1).length() + 1); - mir_strcpy(tmp, output.substr(s1, s2 - s1 - 1).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyMainName", tmp); - mir_free(tmp); - if ((s1 = output.find(")", s2)) == string::npos) - s1 = output.find(">", s2); - else if (s1 > output.find(">", s2)) - s1 = output.find(">", s2); - s2++; - if (output[s1] == ')') { - tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1); - mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyComment", tmp); - mir_free(tmp); - s1 += 3; - s2 = output.find(">", s1); - tmp = (char*)mir_alloc(output.substr(s1, s2 - s1).length() + 1); - mir_strcpy(tmp, output.substr(s1, s2 - s1).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyMainEmail", tmp); - mir_free(tmp); - } - else { - tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1); - mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyMainEmail", output.substr(s2, s1 - s2).c_str()); - mir_free(tmp); - } - g_plugin.setByte(ccs->hContact, "GPGEncryption", 1); - g_plugin.setByte(ccs->hContact, "bAlwatsTrust", 1); - setSrmmIcon(ccs->hContact); - if (db_mc_isSub(ccs->hContact)) - setSrmmIcon(db_mc_getMeta(ccs->hContact)); - - HistoryLog(ccs->hContact, "PGP Encryption turned on by key autoexchange feature"); - } - return 1; - } - } - if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) == wstring::npos) || ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) == wstring::npos)) { - s2 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----"); - s1 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); - } - if ((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key - if (globals.debuglog) - globals.debuglog << "info: received key from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - s1 = 0; - while ((s1 = str.find(L"\r", s1)) != wstring::npos) - str.erase(s1, 1); - if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) - s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); - else if (((s2 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) - s2 += mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----"); - CDlgNewKey *d = new CDlgNewKey(ccs->hContact, str.substr(s1, s2 - s1)); - d->Show(); - HistoryLog(ccs->hContact, msg); - return 0; - } - - if (g_plugin.bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && globals.gpg_valid && globals.gpg_keyexist) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): received key request from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - - CMStringA tmp(g_plugin.getMStringA("GPGPubKey")); - if (!tmp.IsEmpty()) { - int enc_state = g_plugin.getByte(ccs->hContact, "GPGEncryption"); - if (enc_state) - g_plugin.setByte(ccs->hContact, "GPGEncryption", 0); - - string str1 = "-----PGP KEY RESPONSE-----"; - str1.append(tmp); - ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str()); - if (enc_state) - g_plugin.setByte(ccs->hContact, "GPGEncryption", 1); - } - return 0; - } - else if (!isContactHaveKey(ccs->hContact) && g_plugin.bAutoExchange && globals.gpg_valid && globals.gpg_keyexist) { - char *proto = Proto_GetBaseAccountName(ccs->hContact); - ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", "")); - if (jid[0]) { - for (auto p : globals.Accounts) { - ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); - if (caps) { - string str1; - for (int i = 0;; i++) { - str1.push_back(caps[i]); - if (caps[i] == '\0') - if (caps[i + 1] == '\0') - break; - } - - if (str1.find("GPG_Key_Auto_Exchange:0") != string::npos) { - ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); - return 0; - } - } - } - } - } - - if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) - return Proto_ChainRecv(w, ccs); - - mir_forkThread<RecvParams>(RecvMsgSvc_func, new RecvParams(ccs->hContact, str, msg, pre->timestamp)); - return 0; -} - -void SendMsgSvc_func(MCONTACT hContact, char *msg, uint32_t flags) -{ - string str = msg; - if (g_plugin.bStripTags && g_plugin.bAppendTags) { - if (globals.debuglog) - globals.debuglog << "info: stripping tags in outgoing message, name: " + toUTF8(Clist_GetContactDisplayName(hContact)); - strip_tags(str); - } - -LBL_Relaunch: - wstring file = toUTF16(get_random(10)); - gpg_execution_params params; - { - CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID")); - if (tmp.IsEmpty()) { - HistoryLog(hContact, "Failed to encrypt message with GPG (not found key for encryption in db", DBEF_SENT); - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - if (g_plugin.getByte(hContact, "bAlwaysTrust", 0)) { - params.addParam(L"--trust-model"); - params.addParam(L"always"); - } - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"-eatr"); - params.addParam(_A2T(tmp).get()); - } - - CMStringW path(g_plugin.getMStringW("szHomePath")); - path += L"\\tmp\\"; - path += file.c_str(); - params.addParam(path.c_str()); - - const int timeout = 5000, step = 100; - int count = 0; - { - fstream f(path.c_str(), std::ios::out); - while (!f.is_open()) { - ::Sleep(step); - count += step; - if (count >= timeout) { - g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption - setSrmmIcon(hContact); - globals.debuglog << "info: failed to create temporary file for encryption, disabling encryption to avoid deadlock"; - break; - } - f.open(path.c_str(), std::ios::out); - } - if (count < timeout) { - f.write(str.c_str(), str.size()); - f.close(); - } - } - - if (!gpg_launcher(params)) { - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - if (params.result == pxNotFound) { - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - if (params.out.Find("There is no assurance this key belongs to the named user") != -1) { - if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO)) - return; - - g_plugin.setByte(hContact, "bAlwaysTrust", 1); - params.aargv.clear(); - goto LBL_Relaunch; - } - - if (params.out.Find("usage: ") != -1) { - MessageBox(nullptr, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK); - //mir_free(msg); - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path.c_str(), e); - } - return; - } - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path.c_str(), e); - } - - path += L".asc"; - fstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - count = 0; - while (!f.is_open()) { - ::Sleep(step); - f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - count += step; - if (count >= timeout) { - g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption - setSrmmIcon(hContact); - globals.debuglog << "info: gpg failed to encrypt message, disabling encryption to avoid deadlock"; - break; - } - } - - str.clear(); - if (f.is_open()) { - size_t size = f.tellg(); - char *tmp = new char[size + 1]; - f.seekg(0, std::ios::beg); - f.read(tmp, size); - tmp[size] = '\0'; - str.append(tmp); - delete[] tmp; - f.close(); - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path.c_str(), e); - } - } - - if (str.empty()) { - HistoryLog(hContact, "Failed to encrypt message with GPG", DBEF_SENT); - if (globals.debuglog) - globals.debuglog << "info: Failed to encrypt message with GPG"; - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - string str_event = msg; - if (g_plugin.bAppendTags) { - str_event.insert(0, toUTF8(globals.wszOutopentag.c_str())); - str_event.append(toUTF8(globals.wszOutclosetag.c_str())); - } - - if (globals.debuglog) - globals.debuglog << "adding event to contact: " + toUTF8(Clist_GetContactDisplayName(hContact)) + " on send message."; - - fix_line_term(str); - sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)str.c_str())); -} - -INT_PTR SendMsgSvc(WPARAM w, LPARAM l) -{ - CCSDATA *ccs = (CCSDATA*)l; - if (!ccs) - return Proto_ChainSend(w, ccs); - - if (!ccs->lParam) - return Proto_ChainSend(w, ccs); - - std::string szScreenName(toUTF8(Clist_GetContactDisplayName(ccs->hContact))); - char *msg = (char*)ccs->lParam; - if (!msg) { - if (globals.debuglog) - globals.debuglog << "info: failed to get message data, name: " + szScreenName; - return Proto_ChainSend(w, ccs); - } - - if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) { - if (globals.debuglog) - globals.debuglog << "info: encrypted message, let it go, name: " + szScreenName; - return Proto_ChainSend(w, ccs); - } - - if (globals.debuglog) - globals.debuglog << "info: contact have key, name: " + szScreenName; - - if (globals.debuglog && db_mc_isMeta(ccs->hContact)) - globals.debuglog << "info: protocol is metacontacts, name: " + szScreenName; - - if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) { - if (globals.debuglog) - globals.debuglog << "info: contact not secured, name: " + szScreenName; - return Proto_ChainSend(w, ccs); - } - - ProtoBroadcastAsync(Proto_GetBaseAccountName(ccs->hContact), ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)777); - return 777; -} - -int HookSendMsg(WPARAM w, LPARAM l) -{ - if (!l) - return 0; - - DBEVENTINFO *dbei = (DBEVENTINFO*)l; - if (dbei->eventType != EVENTTYPE_MESSAGE || (dbei->flags & DBEF_READ)) - return 0; - - MCONTACT hContact = (MCONTACT)w; - std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact))); - - if (dbei->flags & DBEF_SENT) { - if (isContactSecured(hContact) && strstr((char*)dbei->pBlob, "-----BEGIN PGP MESSAGE-----")) //our service data, can be double added by metacontacts e.w.c. - { - if (globals.debuglog) - globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName; - return 1; - } - if (g_plugin.bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history - { - if (globals.debuglog) - globals.debuglog << "info(send handler): block pgp key request/response event, name: " + szScreenName; - return 1; - } - } - - if (db_mc_isMeta(hContact)) - return 0; - - if (!isContactHaveKey(hContact)) { - if (globals.debuglog) - globals.debuglog << "info: contact have not key, name: " + szScreenName; - - if (g_plugin.bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && globals.gpg_valid) { - if (globals.debuglog) - globals.debuglog << "info: checking for autoexchange possibility, name: " + szScreenName; - - LPSTR proto = Proto_GetBaseAccountName(hContact); - ptrA jid(db_get_utfa(hContact, proto, "jid", "")); - if (jid[0]) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): protocol looks like jabber, name: " + szScreenName; - for (auto p : globals.Accounts) { - ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); - if (caps) { - string str; - for (int i = 0;; i++) { - str.push_back(caps[i]); - if (caps[i] == '\0') - if (caps[i + 1] == '\0') - break; - } - - if (str.find("GPG_Key_Auto_Exchange:0") != string::npos) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + szScreenName; - ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); - globals.hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob); - mir_forkthread(send_encrypted_msgs_thread, (void*)hContact); - return 0; - } - } - } - } - } - else return 0; - } - - if (isContactSecured(hContact) && (dbei->flags & DBEF_SENT)) //aggressive outgoing events filtering - { - SendMsgSvc_func(hContact, (char*)dbei->pBlob, 0); - //TODO: handle errors somehow ... - if (g_plugin.bAppendTags) { - string str_event = (char*)dbei->pBlob; - //mir_free(dbei->pBlob); - str_event.insert(0, toUTF8(globals.wszOutopentag.c_str())); - str_event.append(toUTF8(globals.wszOutclosetag.c_str())); - dbei->pBlob = (uint8_t*)mir_strdup(str_event.c_str()); - dbei->cbBlob = (uint32_t)str_event.length() + 1; - } - - return 0; - } - - if (!isContactSecured(hContact)) { - if (globals.debuglog) - globals.debuglog << "event message: \"" + string((char*)dbei->pBlob) + "\" passed event filter, contact " + szScreenName + " is unsecured"; - return 0; - } - - if (!(dbei->flags & DBEF_SENT) && db_mc_isMeta((MCONTACT)w)) { - char tmp[29]; - strncpy(tmp, (char*)dbei->pBlob, 27); - tmp[28] = '\0'; - if (strstr(tmp, "-----BEGIN PGP MESSAGE-----")) { - if (globals.debuglog) - globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName; - return 1; - } - } - return 0; -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+std::list<HANDLE> sent_msgs;
+
+struct RecvParams
+{
+ RecvParams(MCONTACT _p1, std::wstring _p2, const char *_p3, uint32_t _p4) :
+ hContact(_p1),
+ str(_p2),
+ msg(_p3),
+ timestamp(_p4)
+ {}
+
+ MCONTACT hContact;
+ std::wstring str;
+ std::string msg;
+ uint32_t timestamp;
+};
+
+static void RecvMsgSvc_func(RecvParams *param)
+{
+ MCONTACT hContact = param->hContact;
+ std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact)));
+
+ // check for gpg related data
+ wstring::size_type s1 = param->str.find(L"-----BEGIN PGP MESSAGE-----");
+ wstring::size_type s2 = param->str.find(L"-----END PGP MESSAGE-----");
+ if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block
+ if (!isContactSecured(hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: received encrypted message from: " + szScreenName + " with turned off encryption";
+ if (MessageBox(nullptr, TranslateT("We received encrypted message from contact with encryption turned off.\nDo you want to turn on encryption for this contact?"), TranslateT("Warning"), MB_YESNO) == IDYES) {
+ if (!isContactHaveKey(hContact))
+ ShowLoadPublicKeyDialog(hContact, true);
+ else {
+ g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ }
+
+ if (isContactHaveKey(hContact)) {
+ g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ }
+ }
+ else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) {
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp);
+ delete param;
+ return;
+ }
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: received encrypted message from: " + szScreenName;
+ boost::algorithm::erase_all(param->str, "\r");
+ s2 += mir_wstrlen(L"-----END PGP MESSAGE-----");
+
+ ptrW ptszHomePath(g_plugin.getWStringA("szHomePath", L""));
+ wstring encfile = toUTF16(get_random(10));
+ wstring decfile = toUTF16(get_random(10));
+ {
+ wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile;
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ {
+ const int timeout = 5000, step = 100;
+ int count = 0;
+
+ fstream f(path.c_str(), std::ios::out);
+ while (!f.is_open()) {
+ ::Sleep(step);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ setSrmmIcon(hContact);
+ globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock";
+ delete param;
+ return;
+ }
+ f.open(path.c_str(), std::ios::out);
+ }
+ char *tmp = mir_u2a(param->str.substr(s1, s2 - s1).c_str());
+ f << tmp;
+ mir_free(tmp);
+ f.close();
+ }
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ {
+ CMStringA inkeyid = g_plugin.getMStringA(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID");
+ CMStringW pass;
+ if (!inkeyid.IsEmpty()) {
+ string dbsetting = "szKey_";
+ dbsetting += inkeyid;
+ dbsetting += "_Password";
+ pass = g_plugin.getMStringW(dbsetting.c_str());
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to decrypt message from " + szScreenName + " with password";
+ }
+ else {
+ pass = g_plugin.getMStringW("szKeyPassword");
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + szScreenName + " with password";
+ }
+ if (!pass.IsEmpty()) {
+ params.addParam(L"--passphrase");
+ params.addParam(pass.c_str());
+ }
+ else if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName + " with password";
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + szScreenName + " without password";
+ }
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e);
+ }
+
+ params.addParam(L"--output");
+ params.addParam(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile);
+ params.addParam(L"-d");
+ params.addParam(L"-a");
+ params.addParam(path);
+
+ bool bRes = gpg_launcher(params);
+ if (!bRes || params.result == pxNotFound) {
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ SendErrorMessage(hContact);
+ HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
+ delete param;
+ return;
+ }
+
+ // TODO: check gpg output for errors
+ globals._terminate = false;
+
+ string out(params.out);
+ while (out.find("public key decryption failed: bad passphrase") != string::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info: failed to decrypt message from " + szScreenName + " password needed, trying to get one";
+ if (globals._terminate) {
+ SendErrorMessage(hContact);
+ break;
+ }
+ {
+ // save inkey id
+ string::size_type s = out.find(" encrypted with ");
+ s = out.find(" ID ", s);
+ s += mir_strlen(" ID ");
+ g_plugin.setString(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID", out.substr(s, out.find(",", s) - s).c_str());
+ }
+
+ CDlgKeyPasswordMsgBox(hContact).DoModal();
+
+ gpg_execution_params params2;
+ params2.aargv = params.aargv;
+ if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName;
+
+ params2.addParam(L"--passphrase");
+ params2.addParam(globals.wszPassword.c_str());
+ }
+
+ bRes = gpg_launcher(params2);
+ if (!bRes || params2.result == pxNotFound) {
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+ }
+
+ out.clear();
+ bRes = gpg_launcher(params);
+ if (!bRes || params.result == pxNotFound) {
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e);
+ }
+
+ if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to decrypt GPG encrypted message.";
+
+ string str1 = param->msg;
+ str1.insert(0, "\n");
+ str1.insert(0, TranslateU("Received unencrypted message:"));
+
+ HistoryLog(hContact, str1.c_str(), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+
+ std::string str;
+
+ wstring tszDecPath = wstring(ptszHomePath) + L"\\tmp\\" + decfile;
+
+ fstream f(tszDecPath.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ if (f.is_open()) {
+ size_t size = f.tellg();
+ char *tmp = new char[size + 1];
+ f.seekg(0, std::ios::beg);
+ f.read(tmp, size);
+ tmp[size] = '\0';
+
+ str.append(tmp);
+ delete[] tmp;
+ f.close();
+ if (!globals.debuglog) {
+ boost::system::error_code ec;
+ boost::filesystem::remove(tszDecPath, ec);
+ if (ec) {
+ //TODO: handle error
+ }
+ }
+ }
+
+ if (str.empty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to decrypt GPG encrypted message.";
+
+ string szMsg = param->msg;
+ szMsg.insert(0, TranslateU("Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n"));
+
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+
+ fix_line_term(str);
+ if (g_plugin.bAppendTags) {
+ str.insert(0, toUTF8(globals.wszInopentag.c_str()));
+ str.append(toUTF8(globals.wszInclosetag.c_str()));
+ }
+
+ HistoryLog(hContact, str.c_str(), param->timestamp);
+ delete param;
+ return;
+ }
+ }
+
+ if (g_plugin.getByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption"))
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp, DBEF_READ);
+ else
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp);
+
+ delete param;
+}
+
+INT_PTR RecvMsgSvc(WPARAM w, LPARAM l)
+{
+ CCSDATA *ccs = (CCSDATA*)l;
+ if (!ccs)
+ return Proto_ChainRecv(w, ccs);
+
+ PROTORECVEVENT *pre = (PROTORECVEVENT*)(ccs->lParam);
+ if (!pre)
+ return Proto_ChainRecv(w, ccs);
+
+ char *msg = pre->szMessage;
+ if (!msg)
+ return Proto_ChainRecv(w, ccs);
+
+ if (db_mc_isMeta(ccs->hContact)) {
+ if (!strstr(msg, "-----BEGIN PGP MESSAGE-----"))
+ return Proto_ChainRecv(w, ccs);
+ else {
+ if (globals.debuglog)
+ globals.debuglog << "info: blocked pgp message to metacontact:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ return 0;
+ }
+ }
+
+ wstring str = toUTF16(msg);
+ size_t s1, s2;
+ if (g_plugin.bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): parsing key response:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----");
+ s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (s1 != wstring::npos && s2 != wstring::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): found pubkey block:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
+ g_plugin.setWString(ccs->hContact, "GPGPubKey", str.substr(s1, s2 - s1).c_str());
+ {
+ // gpg execute block
+ CMStringW tmp2(g_plugin.getMStringW("szHomePath"));
+ tmp2 += L"\\";
+ tmp2 += get_random(5).c_str();
+ tmp2 += L".asc";
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(tmp2.c_str(), e);
+ }
+ wfstream f(tmp2, std::ios::out);
+ {
+ const int timeout = 5000, step = 100;
+ int count = 0;
+ while (!f.is_open()) {
+ ::Sleep(step);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 0);
+ setSrmmIcon(ccs->hContact);
+ globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock";
+ return 1;
+ }
+ f.open(tmp2, std::ios::out);
+ }
+ }
+ f << g_plugin.getMStringW(ccs->hContact, "GPGPubKey").c_str();
+ f.close();
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ return 1;
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(tmp2.c_str(), e);
+ }
+ if (params.result == pxNotFound)
+ return 1;
+
+ string output(params.out);
+ s1 = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ s2 = output.find(":", s1);
+ g_plugin.setString(ccs->hContact, "KeyID", output.substr(s1, s2 - s1).c_str());
+ s2 += 2;
+ s1 = output.find(RUS_QUOTE, s2);
+ if (s1 == string::npos) {
+ s1 = output.find("\"", s2);
+ s1 += 1;
+ }
+ else s1 += sizeof(RUS_QUOTE) - 1;
+
+ if ((s2 = output.find("(", s1)) == string::npos)
+ s2 = output.find("<", s1);
+ else if (s2 > output.find("<", s1))
+ s2 = output.find("<", s1);
+
+ char *tmp = (char*)mir_alloc(output.substr(s1, s2 - s1 - 1).length() + 1);
+ mir_strcpy(tmp, output.substr(s1, s2 - s1 - 1).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyMainName", tmp);
+ mir_free(tmp);
+ if ((s1 = output.find(")", s2)) == string::npos)
+ s1 = output.find(">", s2);
+ else if (s1 > output.find(">", s2))
+ s1 = output.find(">", s2);
+ s2++;
+ if (output[s1] == ')') {
+ tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1);
+ mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyComment", tmp);
+ mir_free(tmp);
+ s1 += 3;
+ s2 = output.find(">", s1);
+ tmp = (char*)mir_alloc(output.substr(s1, s2 - s1).length() + 1);
+ mir_strcpy(tmp, output.substr(s1, s2 - s1).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ else {
+ tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1);
+ mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyMainEmail", output.substr(s2, s1 - s2).c_str());
+ mir_free(tmp);
+ }
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 1);
+ g_plugin.setByte(ccs->hContact, "bAlwatsTrust", 1);
+ setSrmmIcon(ccs->hContact);
+ if (db_mc_isSub(ccs->hContact))
+ setSrmmIcon(db_mc_getMeta(ccs->hContact));
+
+ HistoryLog(ccs->hContact, "PGP Encryption turned on by key autoexchange feature");
+ }
+ return 1;
+ }
+ }
+ if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) == wstring::npos) || ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) == wstring::npos)) {
+ s2 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----");
+ s1 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
+ }
+ if ((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key
+ if (globals.debuglog)
+ globals.debuglog << "info: received key from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ s1 = 0;
+ while ((s1 = str.find(L"\r", s1)) != wstring::npos)
+ str.erase(s1, 1);
+ if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos))
+ s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
+ else if (((s2 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos))
+ s2 += mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----");
+ CDlgNewKey *d = new CDlgNewKey(ccs->hContact, str.substr(s1, s2 - s1));
+ d->Show();
+ HistoryLog(ccs->hContact, msg);
+ return 0;
+ }
+
+ if (g_plugin.bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && globals.gpg_valid && globals.gpg_keyexist) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): received key request from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+
+ CMStringA tmp(g_plugin.getMStringA("GPGPubKey"));
+ if (!tmp.IsEmpty()) {
+ int enc_state = g_plugin.getByte(ccs->hContact, "GPGEncryption");
+ if (enc_state)
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 0);
+
+ string str1 = "-----PGP KEY RESPONSE-----";
+ str1.append(tmp);
+ ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str());
+ if (enc_state)
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 1);
+ }
+ return 0;
+ }
+ else if (!isContactHaveKey(ccs->hContact) && g_plugin.bAutoExchange && globals.gpg_valid && globals.gpg_keyexist) {
+ char *proto = Proto_GetBaseAccountName(ccs->hContact);
+ ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", ""));
+ if (jid[0]) {
+ for (auto p : globals.Accounts) {
+ ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
+ if (caps) {
+ string str1;
+ for (int i = 0;; i++) {
+ str1.push_back(caps[i]);
+ if (caps[i] == '\0')
+ if (caps[i + 1] == '\0')
+ break;
+ }
+
+ if (str1.find("GPG_Key_Auto_Exchange:0") != string::npos) {
+ ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----");
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ if (!strstr(msg, "-----BEGIN PGP MESSAGE-----"))
+ return Proto_ChainRecv(w, ccs);
+
+ mir_forkThread<RecvParams>(RecvMsgSvc_func, new RecvParams(ccs->hContact, str, msg, pre->timestamp));
+ return 0;
+}
+
+void SendMsgSvc_func(MCONTACT hContact, char *msg, uint32_t flags)
+{
+ string str = msg;
+ if (g_plugin.bStripTags && g_plugin.bAppendTags) {
+ if (globals.debuglog)
+ globals.debuglog << "info: stripping tags in outgoing message, name: " + toUTF8(Clist_GetContactDisplayName(hContact));
+ strip_tags(str);
+ }
+
+LBL_Relaunch:
+ wstring file = toUTF16(get_random(10));
+ gpg_execution_params params;
+ {
+ CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID"));
+ if (tmp.IsEmpty()) {
+ HistoryLog(hContact, "Failed to encrypt message with GPG (not found key for encryption in db", DBEF_SENT);
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ if (g_plugin.getByte(hContact, "bAlwaysTrust", 0)) {
+ params.addParam(L"--trust-model");
+ params.addParam(L"always");
+ }
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"-eatr");
+ params.addParam(_A2T(tmp).get());
+ }
+
+ CMStringW path(g_plugin.getMStringW("szHomePath"));
+ path += L"\\tmp\\";
+ path += file.c_str();
+ params.addParam(path.c_str());
+
+ const int timeout = 5000, step = 100;
+ int count = 0;
+ {
+ fstream f(path.c_str(), std::ios::out);
+ while (!f.is_open()) {
+ ::Sleep(step);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption
+ setSrmmIcon(hContact);
+ globals.debuglog << "info: failed to create temporary file for encryption, disabling encryption to avoid deadlock";
+ break;
+ }
+ f.open(path.c_str(), std::ios::out);
+ }
+ if (count < timeout) {
+ f.write(str.c_str(), str.size());
+ f.close();
+ }
+ }
+
+ if (!gpg_launcher(params)) {
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ if (params.result == pxNotFound) {
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ if (params.out.Find("There is no assurance this key belongs to the named user") != -1) {
+ if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO))
+ return;
+
+ g_plugin.setByte(hContact, "bAlwaysTrust", 1);
+ params.aargv.clear();
+ goto LBL_Relaunch;
+ }
+
+ if (params.out.Find("usage: ") != -1) {
+ MessageBox(nullptr, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK);
+ //mir_free(msg);
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path.c_str(), e);
+ }
+ return;
+ }
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path.c_str(), e);
+ }
+
+ path += L".asc";
+ fstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ count = 0;
+ while (!f.is_open()) {
+ ::Sleep(step);
+ f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption
+ setSrmmIcon(hContact);
+ globals.debuglog << "info: gpg failed to encrypt message, disabling encryption to avoid deadlock";
+ break;
+ }
+ }
+
+ str.clear();
+ if (f.is_open()) {
+ size_t size = f.tellg();
+ char *tmp = new char[size + 1];
+ f.seekg(0, std::ios::beg);
+ f.read(tmp, size);
+ tmp[size] = '\0';
+ str.append(tmp);
+ delete[] tmp;
+ f.close();
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path.c_str(), e);
+ }
+ }
+
+ if (str.empty()) {
+ HistoryLog(hContact, "Failed to encrypt message with GPG", DBEF_SENT);
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to encrypt message with GPG";
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ string str_event = msg;
+ if (g_plugin.bAppendTags) {
+ str_event.insert(0, toUTF8(globals.wszOutopentag.c_str()));
+ str_event.append(toUTF8(globals.wszOutclosetag.c_str()));
+ }
+
+ if (globals.debuglog)
+ globals.debuglog << "adding event to contact: " + toUTF8(Clist_GetContactDisplayName(hContact)) + " on send message.";
+
+ fix_line_term(str);
+ sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)str.c_str()));
+}
+
+INT_PTR SendMsgSvc(WPARAM w, LPARAM l)
+{
+ CCSDATA *ccs = (CCSDATA*)l;
+ if (!ccs)
+ return Proto_ChainSend(w, ccs);
+
+ if (!ccs->lParam)
+ return Proto_ChainSend(w, ccs);
+
+ std::string szScreenName(toUTF8(Clist_GetContactDisplayName(ccs->hContact)));
+ char *msg = (char*)ccs->lParam;
+ if (!msg) {
+ if (globals.debuglog)
+ globals.debuglog << "info: failed to get message data, name: " + szScreenName;
+ return Proto_ChainSend(w, ccs);
+ }
+
+ if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) {
+ if (globals.debuglog)
+ globals.debuglog << "info: encrypted message, let it go, name: " + szScreenName;
+ return Proto_ChainSend(w, ccs);
+ }
+
+ if (globals.debuglog)
+ globals.debuglog << "info: contact have key, name: " + szScreenName;
+
+ if (globals.debuglog && db_mc_isMeta(ccs->hContact))
+ globals.debuglog << "info: protocol is metacontacts, name: " + szScreenName;
+
+ if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: contact not secured, name: " + szScreenName;
+ return Proto_ChainSend(w, ccs);
+ }
+
+ ProtoBroadcastAsync(Proto_GetBaseAccountName(ccs->hContact), ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)777);
+ return 777;
+}
+
+int HookSendMsg(WPARAM w, LPARAM l)
+{
+ if (!l)
+ return 0;
+
+ DBEVENTINFO *dbei = (DBEVENTINFO*)l;
+ if (dbei->eventType != EVENTTYPE_MESSAGE || (dbei->flags & DBEF_READ))
+ return 0;
+
+ MCONTACT hContact = (MCONTACT)w;
+ std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact)));
+
+ if (dbei->flags & DBEF_SENT) {
+ if (isContactSecured(hContact) && strstr((char*)dbei->pBlob, "-----BEGIN PGP MESSAGE-----")) //our service data, can be double added by metacontacts e.w.c.
+ {
+ if (globals.debuglog)
+ globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName;
+ return 1;
+ }
+ if (g_plugin.bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history
+ {
+ if (globals.debuglog)
+ globals.debuglog << "info(send handler): block pgp key request/response event, name: " + szScreenName;
+ return 1;
+ }
+ }
+
+ if (db_mc_isMeta(hContact))
+ return 0;
+
+ if (!isContactHaveKey(hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: contact have not key, name: " + szScreenName;
+
+ if (g_plugin.bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && globals.gpg_valid) {
+ if (globals.debuglog)
+ globals.debuglog << "info: checking for autoexchange possibility, name: " + szScreenName;
+
+ LPSTR proto = Proto_GetBaseAccountName(hContact);
+ ptrA jid(db_get_utfa(hContact, proto, "jid", ""));
+ if (jid[0]) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): protocol looks like jabber, name: " + szScreenName;
+ for (auto p : globals.Accounts) {
+ ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
+ if (caps) {
+ string str;
+ for (int i = 0;; i++) {
+ str.push_back(caps[i]);
+ if (caps[i] == '\0')
+ if (caps[i + 1] == '\0')
+ break;
+ }
+
+ if (str.find("GPG_Key_Auto_Exchange:0") != string::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + szScreenName;
+ ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----");
+ globals.hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob);
+ mir_forkthread(send_encrypted_msgs_thread, (void*)hContact);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ else return 0;
+ }
+
+ if (isContactSecured(hContact) && (dbei->flags & DBEF_SENT)) //aggressive outgoing events filtering
+ {
+ SendMsgSvc_func(hContact, (char*)dbei->pBlob, 0);
+ //TODO: handle errors somehow ...
+ if (g_plugin.bAppendTags) {
+ string str_event = (char*)dbei->pBlob;
+ //mir_free(dbei->pBlob);
+ str_event.insert(0, toUTF8(globals.wszOutopentag.c_str()));
+ str_event.append(toUTF8(globals.wszOutclosetag.c_str()));
+ dbei->pBlob = (uint8_t*)mir_strdup(str_event.c_str());
+ dbei->cbBlob = (uint32_t)str_event.length() + 1;
+ }
+
+ return 0;
+ }
+
+ if (!isContactSecured(hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "event message: \"" + string((char*)dbei->pBlob) + "\" passed event filter, contact " + szScreenName + " is unsecured";
+ return 0;
+ }
+
+ if (!(dbei->flags & DBEF_SENT) && db_mc_isMeta((MCONTACT)w)) {
+ char tmp[29];
+ strncpy(tmp, (char*)dbei->pBlob, 27);
+ tmp[28] = '\0';
+ if (strstr(tmp, "-----BEGIN PGP MESSAGE-----")) {
+ if (globals.debuglog)
+ globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName;
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/plugins/New_GPG/src/metacontacts.cpp b/plugins/New_GPG/src/metacontacts.cpp index 276a8fa5d3..524fceba1a 100644 --- a/plugins/New_GPG/src/metacontacts.cpp +++ b/plugins/New_GPG/src/metacontacts.cpp @@ -1,29 +1,29 @@ -// Copyright © 2010-22 SecureIM developers (baloo and others), sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -bool metaIsDefaultSubContact(MCONTACT hContact) -{ - return db_mc_getDefault(db_mc_getMeta(hContact)) == hContact; -} - -MCONTACT metaGetMostOnline(MCONTACT hContact) -{ - if (db_mc_isMeta(hContact)) - return db_mc_getMostOnline(hContact); - return NULL; -} +// Copyright © 2010-23 SecureIM developers (baloo and others), sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+bool metaIsDefaultSubContact(MCONTACT hContact)
+{
+ return db_mc_getDefault(db_mc_getMeta(hContact)) == hContact;
+}
+
+MCONTACT metaGetMostOnline(MCONTACT hContact)
+{
+ if (db_mc_isMeta(hContact))
+ return db_mc_getMostOnline(hContact);
+ return NULL;
+}
diff --git a/plugins/New_GPG/src/metacontacts.h b/plugins/New_GPG/src/metacontacts.h index 307a9c3131..a0d9579bab 100644 --- a/plugins/New_GPG/src/metacontacts.h +++ b/plugins/New_GPG/src/metacontacts.h @@ -1,21 +1,21 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#pragma once - -bool metaIsDefaultSubContact(MCONTACT hContact) ; -MCONTACT metaGetMostOnline(MCONTACT hContact); - +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#pragma once
+
+bool metaIsDefaultSubContact(MCONTACT hContact) ;
+MCONTACT metaGetMostOnline(MCONTACT hContact);
+
diff --git a/plugins/New_GPG/src/options.cpp b/plugins/New_GPG/src/options.cpp index 2462694f43..cc214ac95a 100644 --- a/plugins/New_GPG/src/options.cpp +++ b/plugins/New_GPG/src/options.cpp @@ -1,1155 +1,1155 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -globals_s globals; - -HWND hwndCurKey_p = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// Load existing key dialog - -class CDlgLoadExistingKey : public CDlgBase -{ - wchar_t id[16]; - CCtrlListView list_EXISTING_KEY_LIST; - -public: - CDlgLoadExistingKey() : - CDlgBase(g_plugin, IDD_LOAD_EXISTING_KEY), - list_EXISTING_KEY_LIST(this, IDC_EXISTING_KEY_LIST) - { - id[0] = 0; - - list_EXISTING_KEY_LIST.OnClick = Callback(this, &CDlgLoadExistingKey::onChange_EXISTING_KEY_LIST); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - - list_EXISTING_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50); - list_EXISTING_KEY_LIST.AddColumn(1, TranslateT("Email"), 30); - list_EXISTING_KEY_LIST.AddColumn(2, TranslateT("Name"), 250); - list_EXISTING_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30); - list_EXISTING_KEY_LIST.AddColumn(4, TranslateT("Expiration date"), 30); - list_EXISTING_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30); - list_EXISTING_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); - - // parse gpg output - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--list-keys"); - if (!gpg_launcher(params)) - return false; - if (params.result == pxNotFound) - return false; - - int i = 1; - string out(params.out); - string::size_type p = 0, p2 = 0, stop = 0; - while (p != string::npos) { - if ((p = out.find("pub ", p)) == string::npos) - break; - p += 5; - if (p < stop) - break; - stop = p; - p2 = out.find("/", p) - 1; - - int row = list_EXISTING_KEY_LIST.AddItem(L"", 0); - list_EXISTING_KEY_LIST.SetItemText(row, 5, toUTF16(out.substr(p, p2 - p)).c_str()); - - p2 += 2; - p = out.find(" ", p2); - list_EXISTING_KEY_LIST.SetItemText(row, 0, toUTF16(out.substr(p2, p - p2)).c_str()); - - p++; - p2 = out.find("\n", p); - string::size_type p3 = out.substr(p, p2 - p).find("["); - if (p3 != string::npos) { - p3 += p; - p2 = p3; - p2--; - p3++; - p3 += mir_strlen("expires: "); - string::size_type p4 = out.find("]", p3); - list_EXISTING_KEY_LIST.SetItemText(row, 4, toUTF16(out.substr(p3, p4 - p3)).c_str()); - } - else p2--; - - list_EXISTING_KEY_LIST.SetItemText(row, 3, toUTF16(out.substr(p, p2 - p)).c_str()); - - p = out.find("uid ", p); - p += mir_strlen("uid "); - p2 = out.find("\n", p); - p3 = out.substr(p, p2 - p).find("<"); - if (p3 != string::npos) { - p3 += p; - p2 = p3; - p2--; - p3++; - string::size_type p4 = out.find(">", p3); - list_EXISTING_KEY_LIST.SetItemText(row, 1, toUTF16(out.substr(p3, p4 - p3)).c_str()); - } - else p2--; - - p = out.find_first_not_of(" ", p); - list_EXISTING_KEY_LIST.SetItemText(row, 2, toUTF16(out.substr(p, p2 - p)).c_str()); - i++; - } - - if (list_EXISTING_KEY_LIST.GetItemCount()) { - list_EXISTING_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE); - } - return true; - } - - bool OnApply() override - { - int i = list_EXISTING_KEY_LIST.GetSelectionMark(); - if (i == -1) - return false; //TODO: error message - - list_EXISTING_KEY_LIST.GetItemText(i, 0, id, _countof(id)); - extern CCtrlEdit *edit_p_PubKeyEdit; - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(id); - if (!gpg_launcher(params)) - return false; - if (params.result == pxNotFound) - return false; - - string out(params.out); - size_t p1 = out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (p1 != std::string::npos) { - size_t p2 = out.find("-----END PGP PUBLIC KEY BLOCK-----", p1); - if (p2 != std::string::npos) { - p2 += mir_strlen("-----END PGP PUBLIC KEY BLOCK-----"); - out = out.substr(p1, p2 - p1); - if (edit_p_PubKeyEdit) - edit_p_PubKeyEdit->SetText(_A2T(out.c_str())); - } - else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK); - } - else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK); - - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - } - - void onChange_EXISTING_KEY_LIST(CCtrlListView::TEventInfo *) - { - EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Import key dialog - -class CDlgImportKey : public CDlgBase -{ - MCONTACT hContact; - CCtrlCombo combo_KEYSERVER; - CCtrlButton btn_IMPORT; - -public: - CDlgImportKey(MCONTACT _hContact) : - CDlgBase(g_plugin, IDD_IMPORT_KEY), - combo_KEYSERVER(this, IDC_KEYSERVER), - btn_IMPORT(this, IDC_IMPORT) - { - hContact = _hContact; - btn_IMPORT.OnClick = Callback(this, &CDlgImportKey::onClick_IMPORT); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow"); - - combo_KEYSERVER.AddString(L"subkeys.pgp.net"); - combo_KEYSERVER.AddString(L"keys.gnupg.net"); - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow"); - } - - void onClick_IMPORT(CCtrlButton *) - { - gpg_execution_params params; - params.addParam(L"--keyserver"); - params.addParam(combo_KEYSERVER.GetText()); - params.addParam(L"--recv-keys"); - params.addParam(toUTF16(globals.hcontact_data[hContact].key_in_prescense)); - gpg_launcher(params); - - MessageBoxA(nullptr, params.out.c_str(), "GPG output", MB_OK); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// COptGpgMainDlg class - -static class COptGpgMainDlg *g_pMainDlg; - -class COptGpgMainDlg : public CDlgBase -{ - bool old_bFileTransfers = g_plugin.bFileTransfers; - - CCtrlListView list_USERLIST; - CCtrlData lbl_CURRENT_KEY; - CCtrlEdit edit_LOG_FILE_EDIT; - CCtrlCheck check_DEBUG_LOG, check_JABBER_API, check_AUTO_EXCHANGE, check_FILE_TRANSFERS; - CCtrlButton btn_DELETE_KEY_BUTTON, btn_SELECT_KEY, btn_SAVE_KEY_BUTTON, btn_COPY_KEY, btn_LOG_FILE_SET; - -public: - COptGpgMainDlg() : CDlgBase(g_plugin, IDD_OPT_GPG), - list_USERLIST(this, IDC_USERLIST), lbl_CURRENT_KEY(this, IDC_CURRENT_KEY), edit_LOG_FILE_EDIT(this, IDC_LOG_FILE_EDIT), - check_DEBUG_LOG(this, IDC_DEBUG_LOG), check_JABBER_API(this, IDC_JABBER_API), check_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE), check_FILE_TRANSFERS(this, IDC_FILE_TRANSFERS), - btn_DELETE_KEY_BUTTON(this, IDC_DELETE_KEY_BUTTON), btn_SELECT_KEY(this, IDC_SELECT_KEY), btn_SAVE_KEY_BUTTON(this, IDC_SAVE_KEY_BUTTON), btn_COPY_KEY(this, IDC_COPY_KEY), btn_LOG_FILE_SET(this, IDC_LOG_FILE_SET) - { - btn_DELETE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_DELETE_KEY_BUTTON); - btn_SELECT_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_SELECT_KEY); - btn_SAVE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_SAVE_KEY_BUTTON); - btn_COPY_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_COPY_KEY); - btn_LOG_FILE_SET.OnClick = Callback(this, &COptGpgMainDlg::onClick_LOG_FILE_SET); - - check_JABBER_API.OnChange = Callback(this, &COptGpgMainDlg::onChange_JABBER_API); - - list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST); - - CreateLink(check_DEBUG_LOG, g_plugin.bDebugLog); - CreateLink(check_JABBER_API, g_plugin.bJabberAPI); - CreateLink(check_AUTO_EXCHANGE, g_plugin.bAutoExchange); - CreateLink(check_FILE_TRANSFERS, g_plugin.bFileTransfers); - } - - bool OnInitDialog() override - { - g_pMainDlg = this; - - list_USERLIST.AddColumn(0, TranslateT("Contact"), 60); - list_USERLIST.AddColumn(1, TranslateT("Key ID"), 50); - list_USERLIST.AddColumn(2, TranslateT("Name"), 50); - list_USERLIST.AddColumn(3, TranslateT("Email"), 50); - list_USERLIST.AddColumn(4, TranslateT("Account"), 60); - list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); - - for (auto &hContact : Contacts()) { - if (!isContactHaveKey(hContact)) - continue; - - auto *pa = Proto_GetAccount(Proto_GetBaseAccountName(hContact)); - if (pa == nullptr) - continue; - - wchar_t *name = Clist_GetContactDisplayName(hContact); - - int row = list_USERLIST.AddItem(L"", 0, hContact); - list_USERLIST.SetItemText(row, 0, name); - - list_USERLIST.SetItemText(row, 4, pa->tszAccountName); - - CMStringW tmp = g_plugin.getMStringW(hContact, "KeyID", L"not set"); - list_USERLIST.SetItemText(row, 1, tmp); - - tmp = g_plugin.getMStringW(hContact, "KeyMainName", L"not set"); - list_USERLIST.SetItemText(row, 2, tmp); - - tmp = g_plugin.getMStringW(hContact, "KeyMainEmail", L"not set"); - list_USERLIST.SetItemText(row, 3, tmp); - - if (g_plugin.getByte(hContact, "GPGEncryption", 0)) - list_USERLIST.SetCheckState(row, 1); - } - - SetListAutoSize(); - - edit_LOG_FILE_EDIT.SetText(ptrW(g_plugin.getWStringA("szLogFilePath", L""))); - - check_JABBER_API.Enable(); - check_AUTO_EXCHANGE.Enable(g_plugin.bJabberAPI); - - lbl_CURRENT_KEY.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("Default private key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get())); - - check_JABBER_API.SetState(g_plugin.getByte("bJabberAPI", 1)); - check_FILE_TRANSFERS.SetState(g_plugin.getByte("bFileTransfers", 0)); - check_AUTO_EXCHANGE.SetState(g_plugin.getByte("bAutoExchange", 0)); - - //TODO: get rid of following s..t - //////////////// - hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd(); - //////////////// - return true; - } - - bool OnApply() override - { - globals.debuglog.init(); - - if (g_plugin.bFileTransfers != old_bFileTransfers) - g_plugin.bSameAction = false; - - g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText())); - return true; - } - - void OnDestroy() override - { - hwndCurKey_p = nullptr; - g_pMainDlg = nullptr; - } - - void onClick_DELETE_KEY_BUTTON(CCtrlButton*) - { - int idx = list_USERLIST.GetSelectionMark(); - if (idx == -1) - return; - - bool keep = false; - bool ismetacontact = false; - MCONTACT meta = NULL; - MCONTACT hContact = list_USERLIST.GetItemData(idx); - if (db_mc_isMeta(hContact)) { - meta = hContact; - hContact = metaGetMostOnline(hContact); - ismetacontact = true; - } - else if ((meta = db_mc_getMeta(hContact)) != NULL) { - hContact = metaGetMostOnline(meta); - ismetacontact = true; - } - - CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID")); - for (auto &hcnttmp : Contacts()) { - if (hcnttmp != hContact) { - ptrA tmp2(g_plugin.getStringA(hcnttmp, "KeyID")); - if (!mir_strcmp(tmp, tmp2)) { - keep = true; - break; - } - } - } - - if (!keep) - if (MessageBox(nullptr, TranslateT("This key is not used by any contact. Do you want to remove it from public keyring?"), TranslateT("Key info"), MB_YESNO) == IDYES) { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"--delete-key"); - params.addParam(_A2T(tmp).get()); - if (!gpg_launcher(params)) - return; - - if (params.result == pxNotFound) - return; - - if (params.out.Find("--delete-secret-keys") != -1) - MessageBox(nullptr, TranslateT("we have secret key for this public key, do not removing from GPG keyring"), TranslateT("info"), MB_OK); - else - MessageBox(nullptr, TranslateT("Key removed from GPG keyring"), TranslateT("info"), MB_OK); - } - - if (ismetacontact) { - if (MessageBox(nullptr, TranslateT("Do you want to remove key from entire metacontact (all subcontacts)?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { - MCONTACT hcnt = NULL; - int count = db_mc_getSubCount(meta); - for (int i = 0; i < count; i++) { - hcnt = db_mc_getSub(meta, i); - if (hcnt) { - g_plugin.delSetting(hcnt, "KeyID"); - g_plugin.delSetting(hcnt, "GPGPubKey"); - g_plugin.delSetting(hcnt, "KeyMainName"); - g_plugin.delSetting(hcnt, "KeyType"); - g_plugin.delSetting(hcnt, "KeyMainEmail"); - g_plugin.delSetting(hcnt, "KeyComment"); - setSrmmIcon(hcnt); - } - } - } - else { - g_plugin.delSetting(hContact, "KeyID"); - g_plugin.delSetting(hContact, "GPGPubKey"); - g_plugin.delSetting(hContact, "KeyMainName"); - g_plugin.delSetting(hContact, "KeyType"); - g_plugin.delSetting(hContact, "KeyMainEmail"); - g_plugin.delSetting(hContact, "KeyComment"); - setSrmmIcon(hContact); - } - } - else { - g_plugin.delSetting(hContact, "KeyID"); - g_plugin.delSetting(hContact, "GPGPubKey"); - g_plugin.delSetting(hContact, "KeyMainName"); - g_plugin.delSetting(hContact, "KeyType"); - g_plugin.delSetting(hContact, "KeyMainEmail"); - g_plugin.delSetting(hContact, "KeyComment"); - setSrmmIcon(hContact); - } - - list_USERLIST.SetItemText(idx, 3, TranslateT("not set")); - list_USERLIST.SetItemText(idx, 2, TranslateT("not set")); - list_USERLIST.SetItemText(idx, 1, TranslateT("not set")); - } - - void onClick_SELECT_KEY(CCtrlButton*) - { - ShowFirstRunDialog(); - } - - void onClick_SAVE_KEY_BUTTON(CCtrlButton*) - { - int idx = list_USERLIST.GetSelectionMark(); - if (idx == -1) - return; - - MCONTACT hContact = list_USERLIST.GetItemData(idx); - ptrW tmp(GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true)); - if (tmp) { - CMStringW str(g_plugin.getMStringW(hContact, "GPGPubKey")); - str.Replace(L"\r", L""); - - wfstream f(tmp, std::ios::out); - f << str.c_str(); - f.close(); - } - } - - void onClick_COPY_KEY(CCtrlButton *) - { - CMStringW str(g_plugin.getMStringW("GPGPubKey")); - str.Replace(L"\n", L"\r\n"); - Utils_ClipboardCopy(str); - } - - void onClick_LOG_FILE_SET(CCtrlButton*) - { - edit_LOG_FILE_EDIT.SetText(ptrW(GetFilePath(TranslateT("Set log file"), L"*", TranslateT("LOG files"), 1))); - } - - void onChange_JABBER_API(CCtrlCheck *chk) - { - check_AUTO_EXCHANGE.Enable(chk->GetState()); - } - - void onItemChanged_USERLIST(CCtrlListView::TEventInfo *ev) - { - NMLISTVIEW *hdr = ev->nmlv; - if (hdr->iItem == -1) - return; - - MCONTACT hContact = list_USERLIST.GetItemData(hdr->iItem); - if (list_USERLIST.GetCheckState(hdr->iItem)) - g_plugin.setByte(hContact, "GPGEncryption", 1); - else - g_plugin.setByte(hContact, "GPGEncryption", 0); - setSrmmIcon(hContact); - } - - void SetLineText(int i, const wchar_t *pwszText) - { - int idx = list_USERLIST.GetSelectionMark(); - if (idx != -1) - list_USERLIST.SetItemText(idx, i, pwszText); - } - - void SetListAutoSize() - { - if (list_USERLIST.GetItemCount() == 0) - return; - - list_USERLIST.SetColumnWidth(0, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(1, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(2, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(3, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(4, LVSCW_AUTOSIZE); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// COptGpgBinDlg class - -class COptGpgBinDlg : public CDlgBase -{ - CCtrlEdit edit_BIN_PATH, edit_HOME_DIR; - CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR; - -public: - COptGpgBinDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_BIN), - edit_BIN_PATH(this, IDC_BIN_PATH), edit_HOME_DIR(this, IDC_HOME_DIR), - btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR) - { - btn_SET_BIN_PATH.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_BIN_PATH); - btn_SET_HOME_DIR.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_HOME_DIR); - - } - - bool OnInitDialog() override - { - edit_BIN_PATH.SetText(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); - edit_HOME_DIR.SetText(g_plugin.getMStringW("szHomePath", L"gpg")); - return true; - } - - bool OnApply() override - { - g_plugin.setWString("szGpgBinPath", ptrW(edit_BIN_PATH.GetText())); - - ptrW wszHomeDir(edit_HOME_DIR.GetText()); - while (wszHomeDir[mir_wstrlen(wszHomeDir) - 1] == '\\') - wszHomeDir[mir_wstrlen(wszHomeDir) - 1] = '\0'; - g_plugin.setWString("szHomePath", wszHomeDir); - return true; - } - - void onClick_SET_BIN_PATH(CCtrlButton*) - { - GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables")); - CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); - edit_BIN_PATH.SetText(tmp); - bool gpg_exists = false; - { - if (_waccess(tmp, 0) != -1) - gpg_exists = true; - if (gpg_exists) { - bool bad_version = false; - CMStringW tmp_path = g_plugin.getMStringW("szGpgBinPath", L""); - g_plugin.setWString("szGpgBinPath", tmp); - - gpg_execution_params params; - params.addParam(L"--version"); - - bool old_gpg_state = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = old_gpg_state; - g_plugin.setWString("szGpgBinPath", tmp_path); - - int p1 = params.out.Find("(GnuPG) "); - if (p1 != string::npos) { - p1 += mir_strlen("(GnuPG) "); - if (params.out[p1] != '1') - bad_version = true; - } - else { - bad_version = false; - MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - } - } - } - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_BIN_PATH.SetText(path); - } - } - - void onClick_SET_HOME_DIR(CCtrlButton*) - { - GetFolderPath(TranslateT("Set home directory")); - CMStringW tmp(g_plugin.getMStringW("szHomePath", L"")); - edit_HOME_DIR.SetText(tmp); - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_HOME_DIR.SetText(tmp); - } - } -}; - -class COptGpgMsgDlg : public CDlgBase -{ - CCtrlCheck check_APPEND_TAGS, check_STRIP_TAGS; - CCtrlEdit edit_IN_OPEN_TAG, edit_IN_CLOSE_TAG, edit_OUT_OPEN_TAG, edit_OUT_CLOSE_TAG; - -public: - COptGpgMsgDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_MESSAGES), - check_APPEND_TAGS(this, IDC_APPEND_TAGS), check_STRIP_TAGS(this, IDC_STRIP_TAGS), - edit_IN_OPEN_TAG(this, IDC_IN_OPEN_TAG), edit_IN_CLOSE_TAG(this, IDC_IN_CLOSE_TAG), edit_OUT_OPEN_TAG(this, IDC_OUT_OPEN_TAG), edit_OUT_CLOSE_TAG(this, IDC_OUT_CLOSE_TAG) - { - CreateLink(check_STRIP_TAGS, g_plugin.bStripTags); - CreateLink(check_APPEND_TAGS, g_plugin.bAppendTags); - } - - bool OnInitDialog() override - { - edit_IN_OPEN_TAG.SetText(g_plugin.getMStringW("szInOpenTag", L"<GPGdec>")); - edit_IN_CLOSE_TAG.SetText(g_plugin.getMStringW("szInCloseTag", L"</GPGdec>")); - edit_OUT_OPEN_TAG.SetText(g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>")); - edit_OUT_CLOSE_TAG.SetText(g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>")); - return true; - } - - bool OnApply() override - { - ptrW tmp(edit_IN_OPEN_TAG.GetText()); - g_plugin.setWString("szInOpenTag", tmp); - globals.wszInopentag = tmp; - - tmp = edit_IN_CLOSE_TAG.GetText(); - g_plugin.setWString("szInCloseTag", tmp); - globals.wszInclosetag = tmp; - - tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText()); - g_plugin.setWString("szOutOpenTag", tmp); - globals.wszOutopentag = tmp; - - tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText()); - g_plugin.setWString("szOutCloseTag", tmp); - globals.wszOutclosetag = tmp; - return true; - } -}; - -class COptGpgAdvDlg : public CDlgBase -{ - CCtrlButton btn_EXPORT, btn_IMPORT; - CCtrlCheck chkPresenceSub, chkSendErrors; - -public: - COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED), - btn_EXPORT(this, IDC_EXPORT), - btn_IMPORT(this, IDC_IMPORT), - chkSendErrors(this, IDC_SEND_ERRORS), - chkPresenceSub(this, IDC_PRESCENSE_SUBSCRIPTION) - { - btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT); - btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT); - - CreateLink(chkSendErrors, g_plugin.bSendErrorMessages); - CreateLink(chkPresenceSub, g_plugin.bPresenceSigning); - } - - bool OnInitDialog() override - { - chkPresenceSub.Enable(g_plugin.bJabberAPI); - return true; - } - - void onClick_EXPORT(CCtrlButton*) - { - INT_PTR ExportGpGKeys(WPARAM w, LPARAM l); - ExportGpGKeys(NULL, NULL); - } - - void onClick_IMPORT(CCtrlButton*) - { - INT_PTR ImportGpGKeys(WPARAM w, LPARAM l); - ImportGpGKeys(NULL, NULL); - } -}; - -CCtrlEdit *edit_p_PubKeyEdit = nullptr; - -static LRESULT CALLBACK editctrl_ctrl_a(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_KEYDOWN: - if (wParam == 0x41 && GetKeyState(VK_CONTROL) < 0) - SendMessage(hwndDlg, EM_SETSEL, 0, -1); - return 0; - } - return mir_callNextSubclass(hwndDlg, editctrl_ctrl_a, msg, wParam, lParam); -} - -class CDlgLoadPubKeyDlg : public CDlgBase -{ - MCONTACT hContact; - wstring key_buf; - wstring::size_type ws1 = 0, ws2 = 0; - CCtrlCheck chk_ENABLE_ENCRYPTION; - CCtrlButton btn_SELECT_EXISTING, btn_OK, btn_LOAD_FROM_FILE, btn_IMPORT; - CCtrlEdit edit_PUBLIC_KEY_EDIT; - -public: - CDlgLoadPubKeyDlg(MCONTACT _p1) : - CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY), - hContact(_p1), - chk_ENABLE_ENCRYPTION(this, IDC_ENABLE_ENCRYPTION), - btn_SELECT_EXISTING(this, IDC_SELECT_EXISTING), btn_OK(this, ID_OK), btn_LOAD_FROM_FILE(this, ID_LOAD_FROM_FILE), btn_IMPORT(this, IDC_IMPORT), - edit_PUBLIC_KEY_EDIT(this, IDC_PUBLIC_KEY_EDIT) - { - btn_SELECT_EXISTING.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_SELECT_EXISTING); - btn_OK.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_OK); - btn_LOAD_FROM_FILE.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_LOAD_FROM_FILE); - btn_IMPORT.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_IMPORT); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - - mir_subclassWindow(GetDlgItem(m_hwnd, IDC_PUBLIC_KEY_EDIT), editctrl_ctrl_a); - MCONTACT hcnt = db_mc_tryMeta(hContact); - { - wstring msg = TranslateT("Load Public GPG Key for "); - msg += Clist_GetContactDisplayName(hcnt, 0); - this->SetCaption(msg.c_str()); - } - if (!hcnt) { - btn_SELECT_EXISTING.Disable(); - chk_ENABLE_ENCRYPTION.Disable(); - } - if (isContactSecured(hcnt)) - chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn off encryption")); - else { - chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn on encryption")); - chk_ENABLE_ENCRYPTION.SetState(1); - } - if (hcnt) { - wstring str = ptrW(g_plugin.getWStringA(hcnt, "GPGPubKey", L"")); - if (!str.empty()) { - wstring::size_type p = 0, stop = 0; - for (;;) { - if ((p = str.find(L"\n", p + 2)) != wstring::npos) { - if (p > stop) { - stop = p; - str.insert(p, L"\r"); - } - else break; - } - } - } - - if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) { - if (g_plugin.getMStringA(hcnt, "KeyID").IsEmpty()) { - gpg_execution_params params; - params.addParam(L"--export"); - params.addParam(L"-a"); - params.addParam(toUTF16(globals.hcontact_data[hcnt].key_in_prescense)); - gpg_launcher(params); //TODO: handle errors - - if ((params.out.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != -1) && (params.out.Find("-----END PGP PUBLIC KEY BLOCK-----") != -1)) { - params.out.Replace("\n", "\r\n"); - - wchar_t *tmp3 = mir_a2u(params.out.c_str()); - str.clear(); - str.append(tmp3); - mir_free(tmp3); - string msg = Translate("Load Public GPG Key for "); - msg += _T2A(Clist_GetContactDisplayName(hcnt)); - msg += " (Key ID: "; - msg += globals.hcontact_data[hcnt].key_in_prescense; - msg += Translate(" found in presence, and exists in keyring.)"); - SetCaption(toUTF16(msg).c_str()); - } - else { - string msg = Translate("Load Public GPG Key (Key ID: "); - msg += globals.hcontact_data[hcnt].key_in_prescense; - msg += Translate(" found in presence.)"); - SetCaption(toUTF16(msg).c_str()); - btn_IMPORT.Enable(); - } - } - } - - edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L""); - } - edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT; - return true; - } - - virtual void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - edit_p_PubKeyEdit = nullptr; - } - - void onClick_SELECT_EXISTING(CCtrlButton*) - { - (new CDlgLoadExistingKey())->Show(); - } - - void onClick_OK(CCtrlButton*) - { - wchar_t *tmp = mir_wstrdup(edit_PUBLIC_KEY_EDIT.GetText()); - wchar_t *begin, *end; - key_buf.append(tmp); - key_buf.append(L"\n"); //no new line at end of file ) - mir_free(tmp); - while ((ws1 = key_buf.find(L"\r", ws1)) != wstring::npos) { - key_buf.erase(ws1, 1); //remove windows specific trash - } - ws1 = 0; - if (((ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) { - begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PUBLIC KEY BLOCK-----") + 1)); - mir_wstrcpy(begin, L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); - end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----") + 1)); - mir_wstrcpy(end, L"-----END PGP PUBLIC KEY BLOCK-----"); - } - else if (((ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) { - begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PRIVATE KEY BLOCK-----") + 1)); - mir_wstrcpy(begin, L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); - end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----") + 1)); - mir_wstrcpy(end, L"-----END PGP PRIVATE KEY BLOCK-----"); - } - else { - MessageBox(nullptr, TranslateT("This is not public or private key"), L"INFO", MB_OK); - return; - } - ws2 += mir_wstrlen(end); - bool allsubcontacts = false; - { - if (db_mc_isMeta(hContact)) { - if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { - allsubcontacts = true; - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setWString(hcnt, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); - } - } - else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); - } - else g_plugin.setWString(hContact, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); - } - tmp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (key_buf.length() + 1)); - mir_wstrcpy(tmp, key_buf.substr(ws1, ws2 - ws1).c_str()); - { //gpg execute block - std::vector<wstring> cmd; - CMStringW tmp2; - { - MCONTACT hcnt = db_mc_tryMeta(hContact); - tmp2 = g_plugin.getMStringW("szHomePath"); - tmp2 += L"\\temporary_exported.asc"; - boost::filesystem::remove(tmp2.c_str()); - - wfstream f(tmp2, std::ios::out); - CMStringW str = g_plugin.getMStringW(hcnt, "GPGPubKey"); - str.Replace(L"\r", L""); - f << str.c_str(); - f.close(); - } - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - mir_free(begin); - mir_free(end); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.delSetting(hcnt, "bAlwatsTrust"); - } - } - else g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust"); - } - else g_plugin.delSetting(hContact, "bAlwatsTrust"); - } - - string output(params.out); - { - if (output.find("already in secret keyring") != string::npos) { - MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK); - boost::filesystem::remove(tmp2.c_str()); - return; - } - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - { - char *tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp3, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp3, nullptr); - { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyID", tmp3); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyID", tmp3); - } - else g_plugin.setString(hContact, "KeyID", tmp3); - } - mir_free(tmp3); - } - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(1, toUTF16(output.substr(s, s2 - s)).c_str()); - - s = output.find("“", s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else - s += 3; - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find("”", s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s2 != string::npos && s != string::npos) { - { - char *tmp3 = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp3, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - else g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - mir_free(tmp3); - } - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(2, toUTF16(output.substr(s, s2 - s - 1)).c_str()); - - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - char *tmp3 = (char*)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); - mir_strcpy(tmp3, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyComment", output.substr(s2, s - s2).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", output.substr(s2, s - s2).c_str()); - } - else g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str()); - } - mir_free(tmp3); - s += 3; - s2 = output.find(">", s); - tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp3, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s, s2 - s).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s, s2 - s).c_str()); - } - else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str()); - } - mir_free(tmp3); - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(3, toUTF16(output.substr(s, s2 - s)).c_str()); - } - else { - char *tmp3 = (char*)mir_alloc(output.substr(s2, s - s2).length() + 1); - mir_strcpy(tmp3, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str()); - } - else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - } - mir_free(tmp3); - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(3, toUTF16(output.substr(s2, s - s2)).c_str()); - } - } - } - if (hContact && g_pMainDlg) - g_pMainDlg->SetListAutoSize(); - } - if (!hContact) { - gpg_execution_params params2; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(g_plugin.getMStringW(hContact, "KeyID").c_str()); - if (!gpg_launcher(params2)) - return; - if (params2.result == pxNotFound) - return; - - params2.out.Remove('\r'); - g_plugin.setString(hContact, "GPGPubKey", params2.out.c_str()); - } - MessageBoxA(nullptr, output.c_str(), "", MB_OK); - boost::filesystem::remove(tmp2.c_str()); - } - key_buf.clear(); - if (chk_ENABLE_ENCRYPTION.GetState()) { - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) { - g_plugin.setByte(hcnt, "GPGEncryption", !isContactSecured(hcnt)); - setSrmmIcon(hContact); - } - } - } - else g_plugin.setByte(metaGetMostOnline(hContact), "GPGEncryption", !isContactSecured(hContact)); - } - else g_plugin.setByte(hContact, "GPGEncryption", !isContactSecured(hContact)); - } - } - this->Close(); - } - - void onClick_LOAD_FROM_FILE(CCtrlButton *) - { - ptrW tmp(GetFilePath(TranslateT("Set file containing GPG public key"), L"*", TranslateT("GPG public key file"))); - if (!tmp) - return; - - wfstream f(tmp, std::ios::in | std::ios::ate | std::ios::binary); - if (!f.is_open()) { - MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); - return; - } - if (f.is_open()) { - std::wifstream::pos_type size = f.tellg(); - wchar_t *temp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1]; - f.seekg(0, std::ios::beg); - f.read(temp, size); - temp[size] = '\0'; - key_buf.append(temp); - delete[] temp; - f.close(); - } - if (key_buf.empty()) { - key_buf.clear(); - if (globals.debuglog) - globals.debuglog << "info: Failed to read key file"; - return; - } - ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----"); - ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (ws2 == wstring::npos || ws1 == wstring::npos) { - ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----"); - ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); - } - if (ws2 == wstring::npos || ws1 == wstring::npos) { - MessageBox(nullptr, TranslateT("There is no public or private key."), TranslateT("Info"), MB_OK); - return; - } - ws2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); - edit_PUBLIC_KEY_EDIT.SetText(key_buf.substr(ws1, ws2 - ws1).c_str()); - key_buf.clear(); - } - - void onClick_IMPORT(CCtrlButton *) - { - CDlgImportKey *d = new CDlgImportKey(hContact); - d->Show(); - } -}; - - -void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal) -{ - CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(hContact); - if (bModal) - d->DoModal(); - else - d->Show(); -} - -int GpgOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szGroup.w = LPGENW("Services"); - odp.szTitle.w = _T(MODULENAME); - - odp.szTab.w = LPGENW("Main"); - odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; - odp.pDialog = new COptGpgMainDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("GnuPG Variables"); - odp.pDialog = new COptGpgBinDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("Messages"); - odp.pDialog = new COptGpgMsgDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("Advanced"); - odp.pDialog = new COptGpgAdvDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+globals_s globals;
+
+HWND hwndCurKey_p = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load existing key dialog
+
+class CDlgLoadExistingKey : public CDlgBase
+{
+ wchar_t id[16];
+ CCtrlListView list_EXISTING_KEY_LIST;
+
+public:
+ CDlgLoadExistingKey() :
+ CDlgBase(g_plugin, IDD_LOAD_EXISTING_KEY),
+ list_EXISTING_KEY_LIST(this, IDC_EXISTING_KEY_LIST)
+ {
+ id[0] = 0;
+
+ list_EXISTING_KEY_LIST.OnClick = Callback(this, &CDlgLoadExistingKey::onChange_EXISTING_KEY_LIST);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+
+ list_EXISTING_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50);
+ list_EXISTING_KEY_LIST.AddColumn(1, TranslateT("Email"), 30);
+ list_EXISTING_KEY_LIST.AddColumn(2, TranslateT("Name"), 250);
+ list_EXISTING_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30);
+ list_EXISTING_KEY_LIST.AddColumn(4, TranslateT("Expiration date"), 30);
+ list_EXISTING_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30);
+ list_EXISTING_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
+
+ // parse gpg output
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--list-keys");
+ if (!gpg_launcher(params))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ int i = 1;
+ string out(params.out);
+ string::size_type p = 0, p2 = 0, stop = 0;
+ while (p != string::npos) {
+ if ((p = out.find("pub ", p)) == string::npos)
+ break;
+ p += 5;
+ if (p < stop)
+ break;
+ stop = p;
+ p2 = out.find("/", p) - 1;
+
+ int row = list_EXISTING_KEY_LIST.AddItem(L"", 0);
+ list_EXISTING_KEY_LIST.SetItemText(row, 5, toUTF16(out.substr(p, p2 - p)).c_str());
+
+ p2 += 2;
+ p = out.find(" ", p2);
+ list_EXISTING_KEY_LIST.SetItemText(row, 0, toUTF16(out.substr(p2, p - p2)).c_str());
+
+ p++;
+ p2 = out.find("\n", p);
+ string::size_type p3 = out.substr(p, p2 - p).find("[");
+ if (p3 != string::npos) {
+ p3 += p;
+ p2 = p3;
+ p2--;
+ p3++;
+ p3 += mir_strlen("expires: ");
+ string::size_type p4 = out.find("]", p3);
+ list_EXISTING_KEY_LIST.SetItemText(row, 4, toUTF16(out.substr(p3, p4 - p3)).c_str());
+ }
+ else p2--;
+
+ list_EXISTING_KEY_LIST.SetItemText(row, 3, toUTF16(out.substr(p, p2 - p)).c_str());
+
+ p = out.find("uid ", p);
+ p += mir_strlen("uid ");
+ p2 = out.find("\n", p);
+ p3 = out.substr(p, p2 - p).find("<");
+ if (p3 != string::npos) {
+ p3 += p;
+ p2 = p3;
+ p2--;
+ p3++;
+ string::size_type p4 = out.find(">", p3);
+ list_EXISTING_KEY_LIST.SetItemText(row, 1, toUTF16(out.substr(p3, p4 - p3)).c_str());
+ }
+ else p2--;
+
+ p = out.find_first_not_of(" ", p);
+ list_EXISTING_KEY_LIST.SetItemText(row, 2, toUTF16(out.substr(p, p2 - p)).c_str());
+ i++;
+ }
+
+ if (list_EXISTING_KEY_LIST.GetItemCount()) {
+ list_EXISTING_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int i = list_EXISTING_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return false; //TODO: error message
+
+ list_EXISTING_KEY_LIST.GetItemText(i, 0, id, _countof(id));
+ extern CCtrlEdit *edit_p_PubKeyEdit;
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(id);
+ if (!gpg_launcher(params))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ string out(params.out);
+ size_t p1 = out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (p1 != std::string::npos) {
+ size_t p2 = out.find("-----END PGP PUBLIC KEY BLOCK-----", p1);
+ if (p2 != std::string::npos) {
+ p2 += mir_strlen("-----END PGP PUBLIC KEY BLOCK-----");
+ out = out.substr(p1, p2 - p1);
+ if (edit_p_PubKeyEdit)
+ edit_p_PubKeyEdit->SetText(_A2T(out.c_str()));
+ }
+ else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK);
+ }
+ else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK);
+
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+ }
+
+ void onChange_EXISTING_KEY_LIST(CCtrlListView::TEventInfo *)
+ {
+ EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Import key dialog
+
+class CDlgImportKey : public CDlgBase
+{
+ MCONTACT hContact;
+ CCtrlCombo combo_KEYSERVER;
+ CCtrlButton btn_IMPORT;
+
+public:
+ CDlgImportKey(MCONTACT _hContact) :
+ CDlgBase(g_plugin, IDD_IMPORT_KEY),
+ combo_KEYSERVER(this, IDC_KEYSERVER),
+ btn_IMPORT(this, IDC_IMPORT)
+ {
+ hContact = _hContact;
+ btn_IMPORT.OnClick = Callback(this, &CDlgImportKey::onClick_IMPORT);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow");
+
+ combo_KEYSERVER.AddString(L"subkeys.pgp.net");
+ combo_KEYSERVER.AddString(L"keys.gnupg.net");
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow");
+ }
+
+ void onClick_IMPORT(CCtrlButton *)
+ {
+ gpg_execution_params params;
+ params.addParam(L"--keyserver");
+ params.addParam(combo_KEYSERVER.GetText());
+ params.addParam(L"--recv-keys");
+ params.addParam(toUTF16(globals.hcontact_data[hContact].key_in_prescense));
+ gpg_launcher(params);
+
+ MessageBoxA(nullptr, params.out.c_str(), "GPG output", MB_OK);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// COptGpgMainDlg class
+
+static class COptGpgMainDlg *g_pMainDlg;
+
+class COptGpgMainDlg : public CDlgBase
+{
+ bool old_bFileTransfers = g_plugin.bFileTransfers;
+
+ CCtrlListView list_USERLIST;
+ CCtrlData lbl_CURRENT_KEY;
+ CCtrlEdit edit_LOG_FILE_EDIT;
+ CCtrlCheck check_DEBUG_LOG, check_JABBER_API, check_AUTO_EXCHANGE, check_FILE_TRANSFERS;
+ CCtrlButton btn_DELETE_KEY_BUTTON, btn_SELECT_KEY, btn_SAVE_KEY_BUTTON, btn_COPY_KEY, btn_LOG_FILE_SET;
+
+public:
+ COptGpgMainDlg() : CDlgBase(g_plugin, IDD_OPT_GPG),
+ list_USERLIST(this, IDC_USERLIST), lbl_CURRENT_KEY(this, IDC_CURRENT_KEY), edit_LOG_FILE_EDIT(this, IDC_LOG_FILE_EDIT),
+ check_DEBUG_LOG(this, IDC_DEBUG_LOG), check_JABBER_API(this, IDC_JABBER_API), check_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE), check_FILE_TRANSFERS(this, IDC_FILE_TRANSFERS),
+ btn_DELETE_KEY_BUTTON(this, IDC_DELETE_KEY_BUTTON), btn_SELECT_KEY(this, IDC_SELECT_KEY), btn_SAVE_KEY_BUTTON(this, IDC_SAVE_KEY_BUTTON), btn_COPY_KEY(this, IDC_COPY_KEY), btn_LOG_FILE_SET(this, IDC_LOG_FILE_SET)
+ {
+ btn_DELETE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_DELETE_KEY_BUTTON);
+ btn_SELECT_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_SELECT_KEY);
+ btn_SAVE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_SAVE_KEY_BUTTON);
+ btn_COPY_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_COPY_KEY);
+ btn_LOG_FILE_SET.OnClick = Callback(this, &COptGpgMainDlg::onClick_LOG_FILE_SET);
+
+ check_JABBER_API.OnChange = Callback(this, &COptGpgMainDlg::onChange_JABBER_API);
+
+ list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST);
+
+ CreateLink(check_DEBUG_LOG, g_plugin.bDebugLog);
+ CreateLink(check_JABBER_API, g_plugin.bJabberAPI);
+ CreateLink(check_AUTO_EXCHANGE, g_plugin.bAutoExchange);
+ CreateLink(check_FILE_TRANSFERS, g_plugin.bFileTransfers);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_pMainDlg = this;
+
+ list_USERLIST.AddColumn(0, TranslateT("Contact"), 60);
+ list_USERLIST.AddColumn(1, TranslateT("Key ID"), 50);
+ list_USERLIST.AddColumn(2, TranslateT("Name"), 50);
+ list_USERLIST.AddColumn(3, TranslateT("Email"), 50);
+ list_USERLIST.AddColumn(4, TranslateT("Account"), 60);
+ list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
+
+ for (auto &hContact : Contacts()) {
+ if (!isContactHaveKey(hContact))
+ continue;
+
+ auto *pa = Proto_GetAccount(Proto_GetBaseAccountName(hContact));
+ if (pa == nullptr)
+ continue;
+
+ wchar_t *name = Clist_GetContactDisplayName(hContact);
+
+ int row = list_USERLIST.AddItem(L"", 0, hContact);
+ list_USERLIST.SetItemText(row, 0, name);
+
+ list_USERLIST.SetItemText(row, 4, pa->tszAccountName);
+
+ CMStringW tmp = g_plugin.getMStringW(hContact, "KeyID", L"not set");
+ list_USERLIST.SetItemText(row, 1, tmp);
+
+ tmp = g_plugin.getMStringW(hContact, "KeyMainName", L"not set");
+ list_USERLIST.SetItemText(row, 2, tmp);
+
+ tmp = g_plugin.getMStringW(hContact, "KeyMainEmail", L"not set");
+ list_USERLIST.SetItemText(row, 3, tmp);
+
+ if (g_plugin.getByte(hContact, "GPGEncryption", 0))
+ list_USERLIST.SetCheckState(row, 1);
+ }
+
+ SetListAutoSize();
+
+ edit_LOG_FILE_EDIT.SetText(ptrW(g_plugin.getWStringA("szLogFilePath", L"")));
+
+ check_JABBER_API.Enable();
+ check_AUTO_EXCHANGE.Enable(g_plugin.bJabberAPI);
+
+ lbl_CURRENT_KEY.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("Default private key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get()));
+
+ check_JABBER_API.SetState(g_plugin.getByte("bJabberAPI", 1));
+ check_FILE_TRANSFERS.SetState(g_plugin.getByte("bFileTransfers", 0));
+ check_AUTO_EXCHANGE.SetState(g_plugin.getByte("bAutoExchange", 0));
+
+ //TODO: get rid of following s..t
+ ////////////////
+ hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd();
+ ////////////////
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ globals.debuglog.init();
+
+ if (g_plugin.bFileTransfers != old_bFileTransfers)
+ g_plugin.bSameAction = false;
+
+ g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText()));
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ hwndCurKey_p = nullptr;
+ g_pMainDlg = nullptr;
+ }
+
+ void onClick_DELETE_KEY_BUTTON(CCtrlButton*)
+ {
+ int idx = list_USERLIST.GetSelectionMark();
+ if (idx == -1)
+ return;
+
+ bool keep = false;
+ bool ismetacontact = false;
+ MCONTACT meta = NULL;
+ MCONTACT hContact = list_USERLIST.GetItemData(idx);
+ if (db_mc_isMeta(hContact)) {
+ meta = hContact;
+ hContact = metaGetMostOnline(hContact);
+ ismetacontact = true;
+ }
+ else if ((meta = db_mc_getMeta(hContact)) != NULL) {
+ hContact = metaGetMostOnline(meta);
+ ismetacontact = true;
+ }
+
+ CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID"));
+ for (auto &hcnttmp : Contacts()) {
+ if (hcnttmp != hContact) {
+ ptrA tmp2(g_plugin.getStringA(hcnttmp, "KeyID"));
+ if (!mir_strcmp(tmp, tmp2)) {
+ keep = true;
+ break;
+ }
+ }
+ }
+
+ if (!keep)
+ if (MessageBox(nullptr, TranslateT("This key is not used by any contact. Do you want to remove it from public keyring?"), TranslateT("Key info"), MB_YESNO) == IDYES) {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"--delete-key");
+ params.addParam(_A2T(tmp).get());
+ if (!gpg_launcher(params))
+ return;
+
+ if (params.result == pxNotFound)
+ return;
+
+ if (params.out.Find("--delete-secret-keys") != -1)
+ MessageBox(nullptr, TranslateT("we have secret key for this public key, do not removing from GPG keyring"), TranslateT("info"), MB_OK);
+ else
+ MessageBox(nullptr, TranslateT("Key removed from GPG keyring"), TranslateT("info"), MB_OK);
+ }
+
+ if (ismetacontact) {
+ if (MessageBox(nullptr, TranslateT("Do you want to remove key from entire metacontact (all subcontacts)?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
+ MCONTACT hcnt = NULL;
+ int count = db_mc_getSubCount(meta);
+ for (int i = 0; i < count; i++) {
+ hcnt = db_mc_getSub(meta, i);
+ if (hcnt) {
+ g_plugin.delSetting(hcnt, "KeyID");
+ g_plugin.delSetting(hcnt, "GPGPubKey");
+ g_plugin.delSetting(hcnt, "KeyMainName");
+ g_plugin.delSetting(hcnt, "KeyType");
+ g_plugin.delSetting(hcnt, "KeyMainEmail");
+ g_plugin.delSetting(hcnt, "KeyComment");
+ setSrmmIcon(hcnt);
+ }
+ }
+ }
+ else {
+ g_plugin.delSetting(hContact, "KeyID");
+ g_plugin.delSetting(hContact, "GPGPubKey");
+ g_plugin.delSetting(hContact, "KeyMainName");
+ g_plugin.delSetting(hContact, "KeyType");
+ g_plugin.delSetting(hContact, "KeyMainEmail");
+ g_plugin.delSetting(hContact, "KeyComment");
+ setSrmmIcon(hContact);
+ }
+ }
+ else {
+ g_plugin.delSetting(hContact, "KeyID");
+ g_plugin.delSetting(hContact, "GPGPubKey");
+ g_plugin.delSetting(hContact, "KeyMainName");
+ g_plugin.delSetting(hContact, "KeyType");
+ g_plugin.delSetting(hContact, "KeyMainEmail");
+ g_plugin.delSetting(hContact, "KeyComment");
+ setSrmmIcon(hContact);
+ }
+
+ list_USERLIST.SetItemText(idx, 3, TranslateT("not set"));
+ list_USERLIST.SetItemText(idx, 2, TranslateT("not set"));
+ list_USERLIST.SetItemText(idx, 1, TranslateT("not set"));
+ }
+
+ void onClick_SELECT_KEY(CCtrlButton*)
+ {
+ ShowFirstRunDialog();
+ }
+
+ void onClick_SAVE_KEY_BUTTON(CCtrlButton*)
+ {
+ int idx = list_USERLIST.GetSelectionMark();
+ if (idx == -1)
+ return;
+
+ MCONTACT hContact = list_USERLIST.GetItemData(idx);
+ ptrW tmp(GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true));
+ if (tmp) {
+ CMStringW str(g_plugin.getMStringW(hContact, "GPGPubKey"));
+ str.Replace(L"\r", L"");
+
+ wfstream f(tmp, std::ios::out);
+ f << str.c_str();
+ f.close();
+ }
+ }
+
+ void onClick_COPY_KEY(CCtrlButton *)
+ {
+ CMStringW str(g_plugin.getMStringW("GPGPubKey"));
+ str.Replace(L"\n", L"\r\n");
+ Utils_ClipboardCopy(str);
+ }
+
+ void onClick_LOG_FILE_SET(CCtrlButton*)
+ {
+ edit_LOG_FILE_EDIT.SetText(ptrW(GetFilePath(TranslateT("Set log file"), L"*", TranslateT("LOG files"), 1)));
+ }
+
+ void onChange_JABBER_API(CCtrlCheck *chk)
+ {
+ check_AUTO_EXCHANGE.Enable(chk->GetState());
+ }
+
+ void onItemChanged_USERLIST(CCtrlListView::TEventInfo *ev)
+ {
+ NMLISTVIEW *hdr = ev->nmlv;
+ if (hdr->iItem == -1)
+ return;
+
+ MCONTACT hContact = list_USERLIST.GetItemData(hdr->iItem);
+ if (list_USERLIST.GetCheckState(hdr->iItem))
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ else
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ setSrmmIcon(hContact);
+ }
+
+ void SetLineText(int i, const wchar_t *pwszText)
+ {
+ int idx = list_USERLIST.GetSelectionMark();
+ if (idx != -1)
+ list_USERLIST.SetItemText(idx, i, pwszText);
+ }
+
+ void SetListAutoSize()
+ {
+ if (list_USERLIST.GetItemCount() == 0)
+ return;
+
+ list_USERLIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// COptGpgBinDlg class
+
+class COptGpgBinDlg : public CDlgBase
+{
+ CCtrlEdit edit_BIN_PATH, edit_HOME_DIR;
+ CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR;
+
+public:
+ COptGpgBinDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_BIN),
+ edit_BIN_PATH(this, IDC_BIN_PATH), edit_HOME_DIR(this, IDC_HOME_DIR),
+ btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR)
+ {
+ btn_SET_BIN_PATH.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_BIN_PATH);
+ btn_SET_HOME_DIR.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_HOME_DIR);
+
+ }
+
+ bool OnInitDialog() override
+ {
+ edit_BIN_PATH.SetText(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
+ edit_HOME_DIR.SetText(g_plugin.getMStringW("szHomePath", L"gpg"));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ g_plugin.setWString("szGpgBinPath", ptrW(edit_BIN_PATH.GetText()));
+
+ ptrW wszHomeDir(edit_HOME_DIR.GetText());
+ while (wszHomeDir[mir_wstrlen(wszHomeDir) - 1] == '\\')
+ wszHomeDir[mir_wstrlen(wszHomeDir) - 1] = '\0';
+ g_plugin.setWString("szHomePath", wszHomeDir);
+ return true;
+ }
+
+ void onClick_SET_BIN_PATH(CCtrlButton*)
+ {
+ GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables"));
+ CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
+ edit_BIN_PATH.SetText(tmp);
+ bool gpg_exists = false;
+ {
+ if (_waccess(tmp, 0) != -1)
+ gpg_exists = true;
+ if (gpg_exists) {
+ bool bad_version = false;
+ CMStringW tmp_path = g_plugin.getMStringW("szGpgBinPath", L"");
+ g_plugin.setWString("szGpgBinPath", tmp);
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+
+ bool old_gpg_state = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = old_gpg_state;
+ g_plugin.setWString("szGpgBinPath", tmp_path);
+
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 != string::npos) {
+ p1 += mir_strlen("(GnuPG) ");
+ if (params.out[p1] != '1')
+ bad_version = true;
+ }
+ else {
+ bad_version = false;
+ MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ }
+ }
+ }
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_BIN_PATH.SetText(path);
+ }
+ }
+
+ void onClick_SET_HOME_DIR(CCtrlButton*)
+ {
+ GetFolderPath(TranslateT("Set home directory"));
+ CMStringW tmp(g_plugin.getMStringW("szHomePath", L""));
+ edit_HOME_DIR.SetText(tmp);
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_HOME_DIR.SetText(tmp);
+ }
+ }
+};
+
+class COptGpgMsgDlg : public CDlgBase
+{
+ CCtrlCheck check_APPEND_TAGS, check_STRIP_TAGS;
+ CCtrlEdit edit_IN_OPEN_TAG, edit_IN_CLOSE_TAG, edit_OUT_OPEN_TAG, edit_OUT_CLOSE_TAG;
+
+public:
+ COptGpgMsgDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_MESSAGES),
+ check_APPEND_TAGS(this, IDC_APPEND_TAGS), check_STRIP_TAGS(this, IDC_STRIP_TAGS),
+ edit_IN_OPEN_TAG(this, IDC_IN_OPEN_TAG), edit_IN_CLOSE_TAG(this, IDC_IN_CLOSE_TAG), edit_OUT_OPEN_TAG(this, IDC_OUT_OPEN_TAG), edit_OUT_CLOSE_TAG(this, IDC_OUT_CLOSE_TAG)
+ {
+ CreateLink(check_STRIP_TAGS, g_plugin.bStripTags);
+ CreateLink(check_APPEND_TAGS, g_plugin.bAppendTags);
+ }
+
+ bool OnInitDialog() override
+ {
+ edit_IN_OPEN_TAG.SetText(g_plugin.getMStringW("szInOpenTag", L"<GPGdec>"));
+ edit_IN_CLOSE_TAG.SetText(g_plugin.getMStringW("szInCloseTag", L"</GPGdec>"));
+ edit_OUT_OPEN_TAG.SetText(g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>"));
+ edit_OUT_CLOSE_TAG.SetText(g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>"));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ ptrW tmp(edit_IN_OPEN_TAG.GetText());
+ g_plugin.setWString("szInOpenTag", tmp);
+ globals.wszInopentag = tmp;
+
+ tmp = edit_IN_CLOSE_TAG.GetText();
+ g_plugin.setWString("szInCloseTag", tmp);
+ globals.wszInclosetag = tmp;
+
+ tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText());
+ g_plugin.setWString("szOutOpenTag", tmp);
+ globals.wszOutopentag = tmp;
+
+ tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText());
+ g_plugin.setWString("szOutCloseTag", tmp);
+ globals.wszOutclosetag = tmp;
+ return true;
+ }
+};
+
+class COptGpgAdvDlg : public CDlgBase
+{
+ CCtrlButton btn_EXPORT, btn_IMPORT;
+ CCtrlCheck chkPresenceSub, chkSendErrors;
+
+public:
+ COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED),
+ btn_EXPORT(this, IDC_EXPORT),
+ btn_IMPORT(this, IDC_IMPORT),
+ chkSendErrors(this, IDC_SEND_ERRORS),
+ chkPresenceSub(this, IDC_PRESCENSE_SUBSCRIPTION)
+ {
+ btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT);
+ btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT);
+
+ CreateLink(chkSendErrors, g_plugin.bSendErrorMessages);
+ CreateLink(chkPresenceSub, g_plugin.bPresenceSigning);
+ }
+
+ bool OnInitDialog() override
+ {
+ chkPresenceSub.Enable(g_plugin.bJabberAPI);
+ return true;
+ }
+
+ void onClick_EXPORT(CCtrlButton*)
+ {
+ INT_PTR ExportGpGKeys(WPARAM w, LPARAM l);
+ ExportGpGKeys(NULL, NULL);
+ }
+
+ void onClick_IMPORT(CCtrlButton*)
+ {
+ INT_PTR ImportGpGKeys(WPARAM w, LPARAM l);
+ ImportGpGKeys(NULL, NULL);
+ }
+};
+
+CCtrlEdit *edit_p_PubKeyEdit = nullptr;
+
+static LRESULT CALLBACK editctrl_ctrl_a(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ if (wParam == 0x41 && GetKeyState(VK_CONTROL) < 0)
+ SendMessage(hwndDlg, EM_SETSEL, 0, -1);
+ return 0;
+ }
+ return mir_callNextSubclass(hwndDlg, editctrl_ctrl_a, msg, wParam, lParam);
+}
+
+class CDlgLoadPubKeyDlg : public CDlgBase
+{
+ MCONTACT hContact;
+ wstring key_buf;
+ wstring::size_type ws1 = 0, ws2 = 0;
+ CCtrlCheck chk_ENABLE_ENCRYPTION;
+ CCtrlButton btn_SELECT_EXISTING, btn_OK, btn_LOAD_FROM_FILE, btn_IMPORT;
+ CCtrlEdit edit_PUBLIC_KEY_EDIT;
+
+public:
+ CDlgLoadPubKeyDlg(MCONTACT _p1) :
+ CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY),
+ hContact(_p1),
+ chk_ENABLE_ENCRYPTION(this, IDC_ENABLE_ENCRYPTION),
+ btn_SELECT_EXISTING(this, IDC_SELECT_EXISTING), btn_OK(this, ID_OK), btn_LOAD_FROM_FILE(this, ID_LOAD_FROM_FILE), btn_IMPORT(this, IDC_IMPORT),
+ edit_PUBLIC_KEY_EDIT(this, IDC_PUBLIC_KEY_EDIT)
+ {
+ btn_SELECT_EXISTING.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_SELECT_EXISTING);
+ btn_OK.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_OK);
+ btn_LOAD_FROM_FILE.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_LOAD_FROM_FILE);
+ btn_IMPORT.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_IMPORT);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+
+ mir_subclassWindow(GetDlgItem(m_hwnd, IDC_PUBLIC_KEY_EDIT), editctrl_ctrl_a);
+ MCONTACT hcnt = db_mc_tryMeta(hContact);
+ {
+ wstring msg = TranslateT("Load Public GPG Key for ");
+ msg += Clist_GetContactDisplayName(hcnt, 0);
+ this->SetCaption(msg.c_str());
+ }
+ if (!hcnt) {
+ btn_SELECT_EXISTING.Disable();
+ chk_ENABLE_ENCRYPTION.Disable();
+ }
+ if (isContactSecured(hcnt))
+ chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn off encryption"));
+ else {
+ chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn on encryption"));
+ chk_ENABLE_ENCRYPTION.SetState(1);
+ }
+ if (hcnt) {
+ wstring str = ptrW(g_plugin.getWStringA(hcnt, "GPGPubKey", L""));
+ if (!str.empty()) {
+ wstring::size_type p = 0, stop = 0;
+ for (;;) {
+ if ((p = str.find(L"\n", p + 2)) != wstring::npos) {
+ if (p > stop) {
+ stop = p;
+ str.insert(p, L"\r");
+ }
+ else break;
+ }
+ }
+ }
+
+ if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) {
+ if (g_plugin.getMStringA(hcnt, "KeyID").IsEmpty()) {
+ gpg_execution_params params;
+ params.addParam(L"--export");
+ params.addParam(L"-a");
+ params.addParam(toUTF16(globals.hcontact_data[hcnt].key_in_prescense));
+ gpg_launcher(params); //TODO: handle errors
+
+ if ((params.out.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != -1) && (params.out.Find("-----END PGP PUBLIC KEY BLOCK-----") != -1)) {
+ params.out.Replace("\n", "\r\n");
+
+ wchar_t *tmp3 = mir_a2u(params.out.c_str());
+ str.clear();
+ str.append(tmp3);
+ mir_free(tmp3);
+ string msg = Translate("Load Public GPG Key for ");
+ msg += _T2A(Clist_GetContactDisplayName(hcnt));
+ msg += " (Key ID: ";
+ msg += globals.hcontact_data[hcnt].key_in_prescense;
+ msg += Translate(" found in presence, and exists in keyring.)");
+ SetCaption(toUTF16(msg).c_str());
+ }
+ else {
+ string msg = Translate("Load Public GPG Key (Key ID: ");
+ msg += globals.hcontact_data[hcnt].key_in_prescense;
+ msg += Translate(" found in presence.)");
+ SetCaption(toUTF16(msg).c_str());
+ btn_IMPORT.Enable();
+ }
+ }
+ }
+
+ edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L"");
+ }
+ edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT;
+ return true;
+ }
+
+ virtual void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+ edit_p_PubKeyEdit = nullptr;
+ }
+
+ void onClick_SELECT_EXISTING(CCtrlButton*)
+ {
+ (new CDlgLoadExistingKey())->Show();
+ }
+
+ void onClick_OK(CCtrlButton*)
+ {
+ wchar_t *tmp = mir_wstrdup(edit_PUBLIC_KEY_EDIT.GetText());
+ wchar_t *begin, *end;
+ key_buf.append(tmp);
+ key_buf.append(L"\n"); //no new line at end of file )
+ mir_free(tmp);
+ while ((ws1 = key_buf.find(L"\r", ws1)) != wstring::npos) {
+ key_buf.erase(ws1, 1); //remove windows specific trash
+ }
+ ws1 = 0;
+ if (((ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) {
+ begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PUBLIC KEY BLOCK-----") + 1));
+ mir_wstrcpy(begin, L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----") + 1));
+ mir_wstrcpy(end, L"-----END PGP PUBLIC KEY BLOCK-----");
+ }
+ else if (((ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) {
+ begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PRIVATE KEY BLOCK-----") + 1));
+ mir_wstrcpy(begin, L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
+ end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----") + 1));
+ mir_wstrcpy(end, L"-----END PGP PRIVATE KEY BLOCK-----");
+ }
+ else {
+ MessageBox(nullptr, TranslateT("This is not public or private key"), L"INFO", MB_OK);
+ return;
+ }
+ ws2 += mir_wstrlen(end);
+ bool allsubcontacts = false;
+ {
+ if (db_mc_isMeta(hContact)) {
+ if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
+ allsubcontacts = true;
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setWString(hcnt, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
+ }
+ }
+ else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
+ }
+ else g_plugin.setWString(hContact, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
+ }
+ tmp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (key_buf.length() + 1));
+ mir_wstrcpy(tmp, key_buf.substr(ws1, ws2 - ws1).c_str());
+ { //gpg execute block
+ std::vector<wstring> cmd;
+ CMStringW tmp2;
+ {
+ MCONTACT hcnt = db_mc_tryMeta(hContact);
+ tmp2 = g_plugin.getMStringW("szHomePath");
+ tmp2 += L"\\temporary_exported.asc";
+ boost::filesystem::remove(tmp2.c_str());
+
+ wfstream f(tmp2, std::ios::out);
+ CMStringW str = g_plugin.getMStringW(hcnt, "GPGPubKey");
+ str.Replace(L"\r", L"");
+ f << str.c_str();
+ f.close();
+ }
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ mir_free(begin);
+ mir_free(end);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.delSetting(hcnt, "bAlwatsTrust");
+ }
+ }
+ else g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust");
+ }
+ else g_plugin.delSetting(hContact, "bAlwatsTrust");
+ }
+
+ string output(params.out);
+ {
+ if (output.find("already in secret keyring") != string::npos) {
+ MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK);
+ boost::filesystem::remove(tmp2.c_str());
+ return;
+ }
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ {
+ char *tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp3, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyID", tmp3);
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyID", tmp3);
+ }
+ else g_plugin.setString(hContact, "KeyID", tmp3);
+ }
+ mir_free(tmp3);
+ }
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(1, toUTF16(output.substr(s, s2 - s)).c_str());
+
+ s = output.find("“", s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else
+ s += 3;
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find("”", s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s2 != string::npos && s != string::npos) {
+ {
+ char *tmp3 = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp3, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ mir_free(tmp3);
+ }
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(2, toUTF16(output.substr(s, s2 - s - 1)).c_str());
+
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ char *tmp3 = (char*)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
+ mir_strcpy(tmp3, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyComment", output.substr(s2, s - s2).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", output.substr(s2, s - s2).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str());
+ }
+ mir_free(tmp3);
+ s += 3;
+ s2 = output.find(">", s);
+ tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp3, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ }
+ mir_free(tmp3);
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(3, toUTF16(output.substr(s, s2 - s)).c_str());
+ }
+ else {
+ char *tmp3 = (char*)mir_alloc(output.substr(s2, s - s2).length() + 1);
+ mir_strcpy(tmp3, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ }
+ mir_free(tmp3);
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(3, toUTF16(output.substr(s2, s - s2)).c_str());
+ }
+ }
+ }
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetListAutoSize();
+ }
+ if (!hContact) {
+ gpg_execution_params params2;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(g_plugin.getMStringW(hContact, "KeyID").c_str());
+ if (!gpg_launcher(params2))
+ return;
+ if (params2.result == pxNotFound)
+ return;
+
+ params2.out.Remove('\r');
+ g_plugin.setString(hContact, "GPGPubKey", params2.out.c_str());
+ }
+ MessageBoxA(nullptr, output.c_str(), "", MB_OK);
+ boost::filesystem::remove(tmp2.c_str());
+ }
+ key_buf.clear();
+ if (chk_ENABLE_ENCRYPTION.GetState()) {
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt) {
+ g_plugin.setByte(hcnt, "GPGEncryption", !isContactSecured(hcnt));
+ setSrmmIcon(hContact);
+ }
+ }
+ }
+ else g_plugin.setByte(metaGetMostOnline(hContact), "GPGEncryption", !isContactSecured(hContact));
+ }
+ else g_plugin.setByte(hContact, "GPGEncryption", !isContactSecured(hContact));
+ }
+ }
+ this->Close();
+ }
+
+ void onClick_LOAD_FROM_FILE(CCtrlButton *)
+ {
+ ptrW tmp(GetFilePath(TranslateT("Set file containing GPG public key"), L"*", TranslateT("GPG public key file")));
+ if (!tmp)
+ return;
+
+ wfstream f(tmp, std::ios::in | std::ios::ate | std::ios::binary);
+ if (!f.is_open()) {
+ MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
+ return;
+ }
+ if (f.is_open()) {
+ std::wifstream::pos_type size = f.tellg();
+ wchar_t *temp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1];
+ f.seekg(0, std::ios::beg);
+ f.read(temp, size);
+ temp[size] = '\0';
+ key_buf.append(temp);
+ delete[] temp;
+ f.close();
+ }
+ if (key_buf.empty()) {
+ key_buf.clear();
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to read key file";
+ return;
+ }
+ ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----");
+ ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (ws2 == wstring::npos || ws1 == wstring::npos) {
+ ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----");
+ ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
+ }
+ if (ws2 == wstring::npos || ws1 == wstring::npos) {
+ MessageBox(nullptr, TranslateT("There is no public or private key."), TranslateT("Info"), MB_OK);
+ return;
+ }
+ ws2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
+ edit_PUBLIC_KEY_EDIT.SetText(key_buf.substr(ws1, ws2 - ws1).c_str());
+ key_buf.clear();
+ }
+
+ void onClick_IMPORT(CCtrlButton *)
+ {
+ CDlgImportKey *d = new CDlgImportKey(hContact);
+ d->Show();
+ }
+};
+
+
+void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal)
+{
+ CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(hContact);
+ if (bModal)
+ d->DoModal();
+ else
+ d->Show();
+}
+
+int GpgOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.szGroup.w = LPGENW("Services");
+ odp.szTitle.w = _T(MODULENAME);
+
+ odp.szTab.w = LPGENW("Main");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE;
+ odp.pDialog = new COptGpgMainDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("GnuPG Variables");
+ odp.pDialog = new COptGpgBinDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("Messages");
+ odp.pDialog = new COptGpgMsgDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("Advanced");
+ odp.pDialog = new COptGpgAdvDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/New_GPG/src/options.h b/plugins/New_GPG/src/options.h index 45c92f914a..b7d98935c2 100644 --- a/plugins/New_GPG/src/options.h +++ b/plugins/New_GPG/src/options.h @@ -1,22 +1,22 @@ -// Copyright © 2017-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef OPTIONS_H -#define OPTIONS_H - - - +// Copyright © 2017-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+
+
#endif
\ No newline at end of file diff --git a/plugins/New_GPG/src/srmm.cpp b/plugins/New_GPG/src/srmm.cpp index 589dda598d..ee575c4947 100644 --- a/plugins/New_GPG/src/srmm.cpp +++ b/plugins/New_GPG/src/srmm.cpp @@ -1,78 +1,78 @@ -// Copyright © 2010-22 SecureIM developers (baloo and others), sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -static void ToggleIcon(MCONTACT hContact) -{ - MCONTACT hMeta = NULL; - if (db_mc_isMeta(hContact)) { - hMeta = hContact; - hContact = metaGetMostOnline(hContact); // возьмем тот, через который пойдет сообщение - } - else if (db_mc_isSub(hContact)) - hMeta = db_mc_getMeta(hContact); - - int enc = g_plugin.getByte(hContact, "GPGEncryption"); - if (enc) { - g_plugin.setByte(hContact, "GPGEncryption", 0); - if (hMeta) - g_plugin.setByte(hMeta, "GPGEncryption", 0); - setSrmmIcon(hContact); - } - else if (!enc) { - if (!isContactHaveKey(hContact)) - ShowLoadPublicKeyDialog(hContact, false); - else { - g_plugin.setByte(hContact, "GPGEncryption", 1); - if (hMeta) - g_plugin.setByte(hMeta, "GPGEncryption", 1); - setSrmmIcon(hContact); - return; - } - - if (isContactHaveKey(hContact)) { - g_plugin.setByte(hContact, "GPGEncryption", 1); - if (hMeta) - g_plugin.setByte(hMeta, "GPGEncryption", 1); - setSrmmIcon(hContact); - } - } -} - -int __cdecl onWindowEvent(WPARAM, LPARAM lParam) -{ - MessageWindowEventData *mwd = (MessageWindowEventData *)lParam; - if (mwd->uType == MSG_WINDOW_EVT_OPEN || mwd->uType == MSG_WINDOW_EVT_OPENING) - if (isContactHaveKey(mwd->hContact)) - setSrmmIcon(mwd->hContact); - return 0; -} - -int __cdecl onIconPressed(WPARAM hContact, LPARAM lParam) -{ - StatusIconClickData *sicd = (StatusIconClickData *)lParam; - if (!mir_strcmp(sicd->szModule, MODULENAME)) - ToggleIcon(hContact); - - return 0; -} - -int __cdecl onExtraIconPressed(WPARAM hContact, LPARAM, LPARAM) -{ - ToggleIcon(hContact); - return 0; -} +// Copyright © 2010-23 SecureIM developers (baloo and others), sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+static void ToggleIcon(MCONTACT hContact)
+{
+ MCONTACT hMeta = NULL;
+ if (db_mc_isMeta(hContact)) {
+ hMeta = hContact;
+ hContact = metaGetMostOnline(hContact); // возьмем тот, через который пойдет сообщение
+ }
+ else if (db_mc_isSub(hContact))
+ hMeta = db_mc_getMeta(hContact);
+
+ int enc = g_plugin.getByte(hContact, "GPGEncryption");
+ if (enc) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ if (hMeta)
+ g_plugin.setByte(hMeta, "GPGEncryption", 0);
+ setSrmmIcon(hContact);
+ }
+ else if (!enc) {
+ if (!isContactHaveKey(hContact))
+ ShowLoadPublicKeyDialog(hContact, false);
+ else {
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ if (hMeta)
+ g_plugin.setByte(hMeta, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ return;
+ }
+
+ if (isContactHaveKey(hContact)) {
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ if (hMeta)
+ g_plugin.setByte(hMeta, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ }
+ }
+}
+
+int __cdecl onWindowEvent(WPARAM, LPARAM lParam)
+{
+ MessageWindowEventData *mwd = (MessageWindowEventData *)lParam;
+ if (mwd->uType == MSG_WINDOW_EVT_OPEN || mwd->uType == MSG_WINDOW_EVT_OPENING)
+ if (isContactHaveKey(mwd->hContact))
+ setSrmmIcon(mwd->hContact);
+ return 0;
+}
+
+int __cdecl onIconPressed(WPARAM hContact, LPARAM lParam)
+{
+ StatusIconClickData *sicd = (StatusIconClickData *)lParam;
+ if (!mir_strcmp(sicd->szModule, MODULENAME))
+ ToggleIcon(hContact);
+
+ return 0;
+}
+
+int __cdecl onExtraIconPressed(WPARAM hContact, LPARAM, LPARAM)
+{
+ ToggleIcon(hContact);
+ return 0;
+}
diff --git a/plugins/New_GPG/src/stdafx.cxx b/plugins/New_GPG/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/plugins/New_GPG/src/stdafx.cxx +++ b/plugins/New_GPG/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/plugins/New_GPG/src/stdafx.h b/plugins/New_GPG/src/stdafx.h index 3553a65c41..5abc9c16f7 100644 --- a/plugins/New_GPG/src/stdafx.h +++ b/plugins/New_GPG/src/stdafx.h @@ -1,98 +1,98 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef COMMONHEADERS_H -#define COMMONHEADERS_H - -#pragma warning(disable:4512 4267 4127) - -#define WIN32_LEAN_AND_MEAN -#define _SCL_SECURE_NO_WARNINGS - -#include <io.h> - -// windows -#include <windows.h> -#include <commdlg.h> -#include <shlobj.h> -#include <shlwapi.h> - -// c++ -#include <map> -using std::map; -#include <list> -using std::list; -#include <string> -using std::string; -using std::wstring; -#include <fstream> -using std::wfstream; -using std::fstream; - -// boost -#include <boost/nondet_random.hpp> -#include <boost/random/variate_generator.hpp> -#include <boost/random/uniform_int.hpp> -#include <boost/date_time.hpp> -#include <boost/iostreams/stream.hpp> - -// boost process -#include <boost/process.hpp> -#include <boost/process/windows.hpp> - -// miranda -#include <newpluginapi.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_options.h> -#include <m_langpack.h> -#include <m_skin.h> -#include <m_jabber.h> -#include <m_message.h> -#include <m_clist.h> -#include <m_cluiframes.h> -#include <m_icolib.h> -#include <m_extraicons.h> -#include <m_gui.h> - -#include <m_metacontacts.h> - -struct CMPlugin : public PLUGIN<CMPlugin> -{ - CMOption<bool> bJabberAPI, bPresenceSigning, bFileTransfers, bAutoExchange, bSameAction, bAppendTags, bStripTags, bDebugLog, bSendErrorMessages; - HANDLE hCLIcon = nullptr; - HGENMENU hToggleEncryption = nullptr, hSendKey = nullptr; - - CMPlugin(); - - int Load() override; - int Unload() override; -}; - -// internal -#include "resource.h" -#include "version.h" -#include "constants.h" -#include "log.h" -#include "utilities.h" -#include "gpg_wrapper.h" -#include "jabber_account.h" -#include "metacontacts.h" -#include "ui.h" -#include "options.h" -#include "globals.h" - -#endif +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef COMMONHEADERS_H
+#define COMMONHEADERS_H
+
+#pragma warning(disable:4512 4267 4127)
+
+#define WIN32_LEAN_AND_MEAN
+#define _SCL_SECURE_NO_WARNINGS
+
+#include <io.h>
+
+// windows
+#include <windows.h>
+#include <commdlg.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+
+// c++
+#include <map>
+using std::map;
+#include <list>
+using std::list;
+#include <string>
+using std::string;
+using std::wstring;
+#include <fstream>
+using std::wfstream;
+using std::fstream;
+
+// boost
+#include <boost/nondet_random.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/date_time.hpp>
+#include <boost/iostreams/stream.hpp>
+
+// boost process
+#include <boost/process.hpp>
+#include <boost/process/windows.hpp>
+
+// miranda
+#include <newpluginapi.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_langpack.h>
+#include <m_skin.h>
+#include <m_jabber.h>
+#include <m_message.h>
+#include <m_clist.h>
+#include <m_cluiframes.h>
+#include <m_icolib.h>
+#include <m_extraicons.h>
+#include <m_gui.h>
+
+#include <m_metacontacts.h>
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMOption<bool> bJabberAPI, bPresenceSigning, bFileTransfers, bAutoExchange, bSameAction, bAppendTags, bStripTags, bDebugLog, bSendErrorMessages;
+ HANDLE hCLIcon = nullptr;
+ HGENMENU hToggleEncryption = nullptr, hSendKey = nullptr;
+
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
+// internal
+#include "resource.h"
+#include "version.h"
+#include "constants.h"
+#include "log.h"
+#include "utilities.h"
+#include "gpg_wrapper.h"
+#include "jabber_account.h"
+#include "metacontacts.h"
+#include "ui.h"
+#include "options.h"
+#include "globals.h"
+
+#endif
diff --git a/plugins/New_GPG/src/ui.cpp b/plugins/New_GPG/src/ui.cpp index 78ff38af92..2118ce723a 100644 --- a/plugins/New_GPG/src/ui.cpp +++ b/plugins/New_GPG/src/ui.cpp @@ -1,902 +1,902 @@ -// Copyright (c) 2017-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CDlgEncryptedFileMsgBox::OnInitDialog() -{ - globals.bDecryptFiles = false; - return true; -} - -CDlgEncryptedFileMsgBox::CDlgEncryptedFileMsgBox() : - CDlgBase(g_plugin, IDD_ENCRYPTED_FILE_MSG_BOX), - chk_REMEMBER(this, IDC_REMEMBER), - btn_IGNORE(this, IDC_IGNORE), - btn_DECRYPT(this, IDC_DECRYPT) -{ - btn_IGNORE.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_IGNORE); - btn_DECRYPT.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_DECRYPT); -} - -void CDlgEncryptedFileMsgBox::onClick_IGNORE(CCtrlButton*) -{ - if (chk_REMEMBER.GetState()) - g_plugin.bSameAction = true; - - this->Close(); -} - -void CDlgEncryptedFileMsgBox::onClick_DECRYPT(CCtrlButton*) -{ - globals.bDecryptFiles = true; - if (chk_REMEMBER.GetState()) { - g_plugin.bFileTransfers = true; - g_plugin.bSameAction = false; - } - this->Close(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CDlgExportKeysMsgBox : public CDlgBase -{ - CCtrlCheck chk_PUBLIC, chk_PRIVATE, chk_ALL; - -public: - CDlgExportKeysMsgBox() : - CDlgBase(g_plugin, IDD_EXPORT_TYPE), - chk_PUBLIC(this, IDC_PUBLIC), - chk_PRIVATE(this, IDC_PRIVATE), - chk_ALL(this, IDC_ALL) - { - } - - bool OnInitDialog() override - { - chk_PUBLIC.SetState(true); - return true; - } - - bool OnApply() override - { - if (chk_PUBLIC.GetState()) - ExportGpGKeysFunc(0); - else if (chk_PRIVATE.GetState()) - ExportGpGKeysFunc(1); - else if (chk_ALL.GetState()) - ExportGpGKeysFunc(2); - return true; - } -}; - -INT_PTR ExportGpGKeys(WPARAM, LPARAM) -{ - (new CDlgExportKeysMsgBox())->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CDlgChangePasswdMsgBox : public CDlgBase //always modal -{ - CCtrlEdit edit_NEW_PASSWD1, edit_NEW_PASSWD2, edit_OLD_PASSWD; - -public: - CDlgChangePasswdMsgBox() : - CDlgBase(g_plugin, IDD_CHANGE_PASSWD), - edit_NEW_PASSWD1(this, IDC_NEW_PASSWD1), - edit_NEW_PASSWD2(this, IDC_NEW_PASSWD2), - edit_OLD_PASSWD(this, IDC_OLD_PASSWD) - { - } - - bool OnApply() override - { - //TODO: show some prgress - if (mir_wstrcmp(edit_NEW_PASSWD1.GetText(), edit_NEW_PASSWD2.GetText())) { - MessageBox(m_hwnd, TranslateT("New passwords do not match"), TranslateT("Error"), MB_OK); - return false; - } - - std::string old_pass, new_pass; - new_pass = toUTF8(ptrW(edit_NEW_PASSWD1.GetText()).get()); - old_pass = toUTF8(ptrW(edit_OLD_PASSWD.GetText()).get()); - - bool old_pass_match = false; - if (!mir_strcmp(ptrA(g_plugin.getUStringA("szKeyPassword")), old_pass.c_str())) - old_pass_match = true; - - if (!old_pass_match) { - if (globals.key_id_global[0]) { - string dbsetting = "szKey_"; - dbsetting += toUTF8(globals.key_id_global); - dbsetting += "_Password"; - ptrA pass(g_plugin.getUStringA(dbsetting.c_str())); - if (!mir_strcmp(pass, old_pass.c_str())) - old_pass_match = true; - } - } - - if (!old_pass_match) - if (MessageBox(m_hwnd, TranslateT("Old password does not match, you can continue, but GPG will reject wrong password.\nDo you want to continue?"), TranslateT("Error"), MB_YESNO) == IDNO) - return false; - - gpg_execution_params_pass params(old_pass, new_pass); - params.addParam(L"--edit-key"); - params.addParam(globals.key_id_global); - params.addParam(L"passwd"); - - HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(&pxEexcute_passwd_change_thread, ¶ms); - if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) { - if (params.child) - params.child->terminate(); - if (globals.debuglog) - globals.debuglog << "GPG execution timed out, aborted"; - return true; - } - - return params.result != pxNotFound; - } -}; - -void ShowChangePasswdDlg() -{ - CDlgChangePasswdMsgBox *d = new CDlgChangePasswdMsgBox; - d->DoModal(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// New key generation dialog - -class CDlgKeyGen : public CDlgBase -{ - CCtrlCombo combo_KEY_TYPE; - CCtrlEdit edit_KEY_LENGTH, edit_KEY_PASSWD, edit_KEY_REAL_NAME, edit_KEY_EMAIL, edit_KEY_COMMENT, edit_KEY_EXPIRE_DATE; - CCtrlData lbl_GENERATING_TEXT; - -public: - CDlgKeyGen() : - CDlgBase(g_plugin, IDD_KEY_GEN), - combo_KEY_TYPE(this, IDC_KEY_TYPE), - edit_KEY_LENGTH(this, IDC_KEY_LENGTH), - edit_KEY_PASSWD(this, IDC_KEY_PASSWD), - edit_KEY_REAL_NAME(this, IDC_KEY_REAL_NAME), - edit_KEY_EMAIL(this, IDC_KEY_EMAIL), - edit_KEY_COMMENT(this, IDC_KEY_COMMENT), - edit_KEY_EXPIRE_DATE(this, IDC_KEY_EXPIRE_DATE), - lbl_GENERATING_TEXT(this, IDC_GENERATING_TEXT) - { - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow"); - SetCaption(TranslateT("Key Generation dialog")); - - combo_KEY_TYPE.AddString(L"RSA"); - combo_KEY_TYPE.AddString(L"DSA"); - combo_KEY_TYPE.SelectString(L"RSA"); - edit_KEY_EXPIRE_DATE.SetText(L"0"); - edit_KEY_LENGTH.SetText(L"4096"); - return true; - } - - bool OnApply() override - { - // data sanity checks - ptrW tmp(combo_KEY_TYPE.GetText()); - if (mir_wstrlen(tmp) < 3) { - MessageBox(nullptr, TranslateT("You must set encryption algorithm first"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_LENGTH.GetText(); - int length = _wtoi(tmp); - if (length < 1024 || length > 4096) { - MessageBox(nullptr, TranslateT("Key length must be of length from 1024 to 4096 bits"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_EXPIRE_DATE.GetText(); - if (mir_wstrlen(tmp) != 10 && tmp[0] != '0') { - MessageBox(nullptr, TranslateT("Invalid date"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_REAL_NAME.GetText(); - if (mir_wstrlen(tmp) < 4) { - MessageBox(nullptr, TranslateT("Name must contain at least 4 characters"), TranslateT("Error"), MB_OK); - return false; - } - if (wcschr(tmp, '(') || wcschr(tmp, ')')) { - MessageBox(nullptr, TranslateT("Name cannot contain '(' or ')'"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_EMAIL.GetText(); - if (mir_wstrlen(tmp) < 5 || !wcschr(tmp, '@')) { - MessageBox(nullptr, TranslateT("Invalid Email"), TranslateT("Error"), MB_OK); - return false; - } - - // generating key file - CMStringW path = g_plugin.getMStringW("szHomePath"); - path += L"\\new_key"; - wfstream f(path.c_str(), std::ios::out); - if (!f.is_open()) { - MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); - return false; - } - - f << "Key-Type: "; - char *tmp2 = mir_u2a(combo_KEY_TYPE.GetText()); - char *subkeytype = (char *)mir_alloc(6); - if (strstr(tmp2, "RSA")) - mir_strcpy(subkeytype, "RSA"); - else if (strstr(tmp2, "DSA")) //this is useless check for now, but it will be required if someone add another key types support - mir_strcpy(subkeytype, "ELG-E"); - f << tmp2; - mir_free(tmp2); - f << "\n"; - f << "Key-Length: "; - f << _wtoi(edit_KEY_LENGTH.GetText()); - f << "\n"; - f << "Subkey-Length: "; - f << _wtoi(edit_KEY_LENGTH.GetText()); - f << "\n"; - f << "Subkey-Type: "; - f << subkeytype; - mir_free(subkeytype); - f << "\n"; - if (edit_KEY_PASSWD.GetText()[0]) { - f << "Passphrase: "; - f << toUTF8(edit_KEY_PASSWD.GetText()).c_str(); - f << "\n"; - } - f << "Name-Real: "; - f << toUTF8(edit_KEY_REAL_NAME.GetText()).c_str(); - f << "\n"; - if (edit_KEY_COMMENT.GetText()[0]) { - f << "Name-Comment: "; - f << toUTF8(edit_KEY_COMMENT.GetText()).c_str(); - f << "\n"; - } - f << "Name-Email: "; - f << toUTF8(edit_KEY_EMAIL.GetText()).c_str(); - f << "\n"; - f << "Expire-Date: "; - f << toUTF8(edit_KEY_EXPIRE_DATE.GetText()).c_str(); - f << "\n"; - f.close(); - - lbl_GENERATING_TEXT.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE); - lbl_GENERATING_TEXT.SetText(TranslateT("Generating new key, please wait...")); - combo_KEY_TYPE.Disable(); - edit_KEY_LENGTH.Disable(); - edit_KEY_PASSWD.Disable(); - edit_KEY_REAL_NAME.Disable(); - edit_KEY_EMAIL.Disable(); - edit_KEY_COMMENT.Disable(); - edit_KEY_EXPIRE_DATE.Disable(); - - // gpg execution - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"--gen-key"); - params.addParam(path.c_str()); - params.bNoOutput = true; - if (!gpg_launcher(params, boost::posix_time::minutes(10))) - return false; - if (params.result == pxNotFound) - return false; - - boost::filesystem::remove(path.c_str()); - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow"); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// First run dialog - -class CDlgFirstRun : public CDlgBase -{ - void refresh_key_list() - { - list_KEY_LIST.DeleteAllItems(); - int i = 1; - - // parse gpg output - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--list-secret-keys"); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - wstring::size_type p = 0, p2 = 0, stop = 0; - string out(params.out); - while (p != string::npos) { - if ((p = out.find("sec ", p)) == string::npos) - break; - p += 5; - if (p < stop) - break; - stop = p; - p2 = out.find("/", p) - 1; - wchar_t *key_len = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()), *creation_date = nullptr, *expire_date = nullptr; - p2 += 2; - p = out.find(" ", p2); - std::wstring key_id = toUTF16(out.substr(p2, p - p2)); - p += 1; - p2 = out.find(" ", p); - std::string::size_type p3 = out.find("\n", p); - if ((p2 != std::string::npos) && (p3 < p2)) { - p2 = p3; - creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p - 1)).c_str()); - } - else { - creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - p2 = out.find("[", p2); - p2 = out.find("expires:", p2); - p2 += mir_strlen("expires:"); - if (p2 != std::string::npos) { - p2++; - p = p2; - p2 = out.find("]", p); - expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - //check expiration - bool expired = false; - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - wchar_t buf[5]; - wcsncpy_s(buf, expire_date, _TRUNCATE); - int year = _wtoi(buf); - if (year < now.date().year()) - expired = true; - else if (year == now.date().year()) { - wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); - int month = _wtoi(buf); - if (month < now.date().month()) - expired = true; - else if (month == now.date().month()) { - wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); - unsigned day = _wtoi(buf); - if (day <= now.date().day_number()) - expired = true; - } - } - } - if (expired) { - mir_free(key_len); - mir_free(creation_date); - mir_free(expire_date); - //mimic normal behaviour - p = out.find("uid ", p); - p2 = out.find_first_not_of(" ", p + 5); - p = out.find("<", p2); - p++; - //p2 = out.find(">", p); - // - continue; //does not add to key list - } - } - } - int row = list_KEY_LIST.AddItem(L"", 0); - list_KEY_LIST.SetItemText(row, 3, creation_date); - mir_free(creation_date); - if (expire_date) { - list_KEY_LIST.SetItemText(row, 4, expire_date); - mir_free(expire_date); - } - list_KEY_LIST.SetItemText(row, 5, key_len); - mir_free(key_len); - list_KEY_LIST.SetItemText(row, 0, (wchar_t *)key_id.c_str()); - p = out.find("uid ", p); - p2 = out.find_first_not_of(" ", p + 5); - p = out.find("<", p2); - - wstring tmp = toUTF16(out.substr(p2, p - p2)); - list_KEY_LIST.SetItemText(row, 2, (wchar_t *)tmp.c_str()); - - p++; - p2 = out.find(">", p); - - tmp = toUTF16(out.substr(p, p2 - p)); - list_KEY_LIST.SetItemText(row, 1, (wchar_t *)tmp.c_str()); - - // get accounts - std::wstring accs; - for (auto &pa : Accounts()) { - std::string setting = pa->szModuleName; - setting += "_KeyID"; - ptrW str(g_plugin.getWStringA(setting.c_str(), L"")); - if (key_id == str.get()) { - if (!accs.empty()) - accs += L","; - accs += pa->tszAccountName; - } - } - list_KEY_LIST.SetItemText(row, 6, accs.c_str()); - } - i++; - list_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(6, LVSCW_AUTOSIZE); - } - - CCtrlListView list_KEY_LIST; - CCtrlButton btn_COPY_PUBKEY, btn_EXPORT_PRIVATE, btn_CHANGE_PASSWD, btn_GENERATE_RANDOM, btn_GENERATE_KEY, btn_OTHER, btn_DELETE_KEY, btn_OK; - CCtrlEdit edit_KEY_PASSWORD; - CCtrlCombo combo_ACCOUNT; - CCtrlData lbl_KEY_ID, lbl_GENERATING_KEY; - wchar_t fp[16]; - const char *m_szCurrAcc = nullptr; - -public: - CDlgFirstRun() : - CDlgBase(g_plugin, IDD_FIRST_RUN), - list_KEY_LIST(this, IDC_KEY_LIST), - btn_COPY_PUBKEY(this, IDC_COPY_PUBKEY), - btn_EXPORT_PRIVATE(this, IDC_EXPORT_PRIVATE), - btn_CHANGE_PASSWD(this, IDC_CHANGE_PASSWD), - btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM), - btn_GENERATE_KEY(this, IDC_GENERATE_KEY), - btn_OTHER(this, IDC_OTHER), - btn_DELETE_KEY(this, IDC_DELETE_KEY), - btn_OK(this, ID_OK), - edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD), - combo_ACCOUNT(this, IDC_ACCOUNT), - lbl_KEY_ID(this, IDC_KEY_ID), - lbl_GENERATING_KEY(this, IDC_GENERATING_KEY) - { - fp[0] = 0; - - btn_COPY_PUBKEY.OnClick = Callback(this, &CDlgFirstRun::onClick_COPY_PUBKEY); - btn_EXPORT_PRIVATE.OnClick = Callback(this, &CDlgFirstRun::onClick_EXPORT_PRIVATE); - btn_CHANGE_PASSWD.OnClick = Callback(this, &CDlgFirstRun::onClick_CHANGE_PASSWD); - btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_RANDOM); - btn_GENERATE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_KEY); - btn_OTHER.OnClick = Callback(this, &CDlgFirstRun::onClick_OTHER); - btn_DELETE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_DELETE_KEY); - btn_OK.OnClick = Callback(this, &CDlgFirstRun::onClick_OK); - - combo_ACCOUNT.OnChange = Callback(this, &CDlgFirstRun::onChange_ACCOUNT); - - list_KEY_LIST.OnClick = Callback(this, &CDlgFirstRun::onChange_KEY_LIST); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow"); - SetCaption(TranslateT("Bind own keys to accounts")); - btn_COPY_PUBKEY.Disable(); - btn_EXPORT_PRIVATE.Disable(); - btn_CHANGE_PASSWD.Disable(); - - list_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50); - list_KEY_LIST.AddColumn(1, TranslateT("Email"), 30); - list_KEY_LIST.AddColumn(2, TranslateT("Name"), 250); - list_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30); - list_KEY_LIST.AddColumn(4, TranslateT("Expire date"), 30); - list_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30); - list_KEY_LIST.AddColumn(6, TranslateT("Accounts"), 30); - list_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); - - refresh_key_list(); - - combo_ACCOUNT.AddString(TranslateT("Default")); - - for (auto &pa : Accounts()) { - if (StriStr(pa->szModuleName, "metacontacts")) - continue; - if (StriStr(pa->szModuleName, "weather")) - continue; - - combo_ACCOUNT.AddString(pa->tszAccountName, (LPARAM)pa->szModuleName); - } - combo_ACCOUNT.SetCurSel(0); - - lbl_KEY_ID.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get())); - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow"); - } - - void onClick_COPY_PUBKEY(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - params.out.Remove('\r'); - Utils_ClipboardCopy(params.out); - } - - void onClick_EXPORT_PRIVATE(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - ptrW p(GetFilePath(L"Choose file to export key", L"*", L"Any file", true)); - if (!p || !p[0]) - return; - - std::ofstream file; - file.open(p, std::ios::trunc | std::ios::out); - if (!file.is_open()) - return; //TODO: handle error - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export-secret-keys"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - params.out.Remove('\r'); - file << params.out.c_str(); - if (file.is_open()) - file.close(); - } - - void onClick_CHANGE_PASSWD(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, globals.key_id_global, _countof(globals.key_id_global)); - - // temporary code follows - std::string old_pass, new_pass; - - gpg_execution_params_pass params(old_pass, new_pass); - params.addParam(L"--edit-key"); - params.addParam(globals.key_id_global); - params.addParam(L"passwd"); - - HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(pxEexcute_passwd_change_thread, ¶ms); - if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) { - if (params.child) - params.child->terminate(); - if (globals.debuglog) - globals.debuglog << "GPG execution timed out, aborted"; - this->Close(); - } - } - - void onClick_GENERATE_RANDOM(CCtrlButton *) - { - lbl_GENERATING_KEY.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE); - lbl_GENERATING_KEY.SetText(TranslateT("Generating new random key, please wait")); - btn_GENERATE_KEY.Disable(); - btn_OTHER.Disable(); - btn_DELETE_KEY.Disable(); - list_KEY_LIST.Disable(); - btn_GENERATE_RANDOM.Disable(); - gpg_use_new_random_key(m_szCurrAcc); - this->Close(); - } - - void onClick_GENERATE_KEY(CCtrlButton *) - { - CDlgKeyGen().DoModal(); - refresh_key_list(); - } - - void onClick_OTHER(CCtrlButton *) - { - ShowLoadPublicKeyDialog(0, true); - refresh_key_list(); - } - - void onClick_DELETE_KEY(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--fingerprint"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - int s = params.out.Find("Key fingerprint = "); - s += mir_strlen("Key fingerprint = "); - int s2 = params.out.Find("\n", s); - - CMStringW tmp = params.out.Mid(s, s2 - s); - tmp.Remove(' '); - - gpg_execution_params params2; - params2.addParam(L"--batch"); - params2.addParam(L"--delete-secret-and-public-key"); - params2.addParam(L"--fingerprint"); - params2.addParam(tmp.c_str()); - - if (!gpg_launcher(params2)) - return; - if (params2.result == pxNotFound) - return; - } - - if (m_szCurrAcc == nullptr) { - g_plugin.delSetting("GPGPubKey"); - g_plugin.delSetting("KeyID"); - g_plugin.delSetting("KeyComment"); - g_plugin.delSetting("KeyMainName"); - g_plugin.delSetting("KeyMainEmail"); - g_plugin.delSetting("KeyType"); - } - else { - CMStringA acc_str = m_szCurrAcc; - g_plugin.delSetting(acc_str + "_GPGPubKey"); - g_plugin.delSetting(acc_str + "_KeyMainName"); - g_plugin.delSetting(acc_str + "_KeyID"); - g_plugin.delSetting(acc_str + "_KeyComment"); - g_plugin.delSetting(acc_str + "_KeyMainEmail"); - g_plugin.delSetting(acc_str + "_KeyType"); - } - - list_KEY_LIST.DeleteItem(i); - } - - void onClick_OK(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - wchar_t name[65]; - list_KEY_LIST.GetItemText(i, 2, name, 64); - { - if (wcschr(name, '(')) { - wstring str = name; - wstring::size_type p = str.find(L"(") - 1; - mir_wstrcpy(name, str.substr(0, p).c_str()); - } - } - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - params.out.Remove('\r'); - - if (m_szCurrAcc == nullptr) { - g_plugin.setString("GPGPubKey", params.out.c_str()); - g_plugin.setWString("KeyMainName", name); - g_plugin.setWString("KeyID", fp); - - wstring keyinfo = TranslateT("Default private key ID"); - keyinfo += L": "; - keyinfo += (fp[0]) ? fp : L"not set"; - extern HWND hwndCurKey_p; - SetWindowText(hwndCurKey_p, keyinfo.c_str()); - } - else { - CMStringA acc_str = m_szCurrAcc; - g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str()); - g_plugin.setWString(acc_str + "_KeyMainName", name); - g_plugin.setWString(acc_str + "_KeyID", fp); - } - - ptrW passwd(edit_KEY_PASSWORD.GetText()); - if (mir_wstrlen(passwd)) { - string dbsetting = "szKey_"; - dbsetting += _T2A(fp); - dbsetting += "_Password"; - g_plugin.setWString(dbsetting.c_str(), passwd); - } - - //bAutoExchange = CheckStateStoreDB(hwndDlg, IDC_AUTO_EXCHANGE, "bAutoExchange") != 0; //TODO: check is it just typo, or doing something - globals.gpg_valid = isGPGValid(); - globals.gpg_keyexist = isGPGKeyExist(); - DestroyWindow(m_hwnd); - } - - void onChange_ACCOUNT(CCtrlCombo *pCombo) - { - CMStringW keyinfo = TranslateT("key ID"); - keyinfo += ": "; - - m_szCurrAcc = (const char *)pCombo->GetCurData(); - if (m_szCurrAcc == nullptr) { - keyinfo += g_plugin.getMStringW("KeyID", TranslateT("not set")); - } - else { - std::string acc_str = m_szCurrAcc; - acc_str += "_KeyID"; - keyinfo += g_plugin.getMStringW(acc_str.c_str(), TranslateT("not set")); - } - lbl_KEY_ID.SetText(keyinfo); - } - - void onChange_KEY_LIST(CCtrlListView::TEventInfo *ev) - { - if (ev->nmlv) { - NMLISTVIEW *hdr = ev->nmlv; - - if (hdr->hdr.code == NM_CLICK) { - btn_OK.Enable(); - btn_COPY_PUBKEY.Enable(); - btn_EXPORT_PRIVATE.Enable(); - btn_CHANGE_PASSWD.Enable(); - } - } - } -}; - -void ShowFirstRunDialog() -{ - CDlgFirstRun().DoModal(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CDlgNewKey::CDlgNewKey(MCONTACT _hContact, wstring _new_key) : - CDlgBase(g_plugin, IDD_NEW_KEY), - lbl_KEY_FROM(this, IDC_KEY_FROM), - lbl_MESSAGE(this, IDC_MESSAGE), - btn_IMPORT(this, ID_IMPORT), - btn_IMPORT_AND_USE(this, IDC_IMPORT_AND_USE), - btn_IGNORE_KEY(this, IDC_IGNORE_KEY) -{ - hContact = _hContact; - new_key = _new_key; - btn_IMPORT.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT); - btn_IMPORT_AND_USE.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT_AND_USE); - btn_IGNORE_KEY.OnClick = Callback(this, &CDlgNewKey::onClick_IGNORE_KEY); -} - -bool CDlgNewKey::OnInitDialog() -{ - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow"); - - CMStringW tmp = g_plugin.getMStringW(hContact, "GPGPubKey"); - lbl_MESSAGE.SetText(!tmp.IsEmpty() ? TranslateT("There is existing key for contact, would you like to replace it with new key?") : TranslateT("New public key was received, do you want to import it?")); - btn_IMPORT_AND_USE.Enable(g_plugin.getByte(hContact, "GPGEncryption", 0)); - btn_IMPORT.SetText(!tmp.IsEmpty() ? TranslateT("Replace") : TranslateT("Accept")); - - tmp.Format(TranslateT("Received key from %s"), Clist_GetContactDisplayName(hContact)); - lbl_KEY_FROM.SetText(tmp); - return true; -} - -void CDlgNewKey::OnDestroy() -{ - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow"); -} - -void CDlgNewKey::onClick_IMPORT(CCtrlButton*) -{ - ImportKey(hContact, new_key); - this->Close(); -} - -void CDlgNewKey::onClick_IMPORT_AND_USE(CCtrlButton*) -{ - ImportKey(hContact, new_key); - g_plugin.setByte(hContact, "GPGEncryption", 1); - setSrmmIcon(hContact); - this->Close(); -} - -void CDlgNewKey::onClick_IGNORE_KEY(CCtrlButton*) -{ - this->Close(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CDlgKeyPasswordMsgBox::CDlgKeyPasswordMsgBox(MCONTACT _hContact) : - CDlgBase(g_plugin, IDD_KEY_PASSWD), - lbl_KEYID(this, IDC_KEYID), - edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD), - chk_DEFAULT_PASSWORD(this, IDC_DEFAULT_PASSWORD), - chk_SAVE_PASSWORD(this, IDC_SAVE_PASSWORD) -{ - hContact = _hContact; -} - -bool CDlgKeyPasswordMsgBox::OnInitDialog() -{ - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow"); - - CMStringW questionstr = TranslateT("Please enter password for key with ID: "); - questionstr += g_plugin.getMStringW(hContact, "InKeyID"); - lbl_KEYID.SetText(questionstr.c_str()); - - chk_DEFAULT_PASSWORD.Disable(); - return true; -} - -bool CDlgKeyPasswordMsgBox::OnApply() -{ - ptrW tmp(edit_KEY_PASSWORD.GetText()); - if (tmp && tmp[0]) { - if (chk_SAVE_PASSWORD.GetState()) { - inkeyid = g_plugin.getStringA(hContact, "InKeyID", ""); - if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) { - string dbsetting = "szKey_"; - dbsetting += inkeyid; - dbsetting += "_Password"; - g_plugin.setWString(dbsetting.c_str(), tmp); - } - else g_plugin.setWString("szKeyPassword", tmp); - } - globals.wszPassword = tmp; - } - mir_free(inkeyid); - return true; -} - -void CDlgKeyPasswordMsgBox::OnDestroy() -{ - if (!m_bSucceeded) - globals._terminate = true; - - mir_free(inkeyid); - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow"); -} +// Copyright (c) 2017-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CDlgEncryptedFileMsgBox::OnInitDialog()
+{
+ globals.bDecryptFiles = false;
+ return true;
+}
+
+CDlgEncryptedFileMsgBox::CDlgEncryptedFileMsgBox() :
+ CDlgBase(g_plugin, IDD_ENCRYPTED_FILE_MSG_BOX),
+ chk_REMEMBER(this, IDC_REMEMBER),
+ btn_IGNORE(this, IDC_IGNORE),
+ btn_DECRYPT(this, IDC_DECRYPT)
+{
+ btn_IGNORE.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_IGNORE);
+ btn_DECRYPT.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_DECRYPT);
+}
+
+void CDlgEncryptedFileMsgBox::onClick_IGNORE(CCtrlButton*)
+{
+ if (chk_REMEMBER.GetState())
+ g_plugin.bSameAction = true;
+
+ this->Close();
+}
+
+void CDlgEncryptedFileMsgBox::onClick_DECRYPT(CCtrlButton*)
+{
+ globals.bDecryptFiles = true;
+ if (chk_REMEMBER.GetState()) {
+ g_plugin.bFileTransfers = true;
+ g_plugin.bSameAction = false;
+ }
+ this->Close();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CDlgExportKeysMsgBox : public CDlgBase
+{
+ CCtrlCheck chk_PUBLIC, chk_PRIVATE, chk_ALL;
+
+public:
+ CDlgExportKeysMsgBox() :
+ CDlgBase(g_plugin, IDD_EXPORT_TYPE),
+ chk_PUBLIC(this, IDC_PUBLIC),
+ chk_PRIVATE(this, IDC_PRIVATE),
+ chk_ALL(this, IDC_ALL)
+ {
+ }
+
+ bool OnInitDialog() override
+ {
+ chk_PUBLIC.SetState(true);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (chk_PUBLIC.GetState())
+ ExportGpGKeysFunc(0);
+ else if (chk_PRIVATE.GetState())
+ ExportGpGKeysFunc(1);
+ else if (chk_ALL.GetState())
+ ExportGpGKeysFunc(2);
+ return true;
+ }
+};
+
+INT_PTR ExportGpGKeys(WPARAM, LPARAM)
+{
+ (new CDlgExportKeysMsgBox())->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CDlgChangePasswdMsgBox : public CDlgBase //always modal
+{
+ CCtrlEdit edit_NEW_PASSWD1, edit_NEW_PASSWD2, edit_OLD_PASSWD;
+
+public:
+ CDlgChangePasswdMsgBox() :
+ CDlgBase(g_plugin, IDD_CHANGE_PASSWD),
+ edit_NEW_PASSWD1(this, IDC_NEW_PASSWD1),
+ edit_NEW_PASSWD2(this, IDC_NEW_PASSWD2),
+ edit_OLD_PASSWD(this, IDC_OLD_PASSWD)
+ {
+ }
+
+ bool OnApply() override
+ {
+ //TODO: show some prgress
+ if (mir_wstrcmp(edit_NEW_PASSWD1.GetText(), edit_NEW_PASSWD2.GetText())) {
+ MessageBox(m_hwnd, TranslateT("New passwords do not match"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ std::string old_pass, new_pass;
+ new_pass = toUTF8(ptrW(edit_NEW_PASSWD1.GetText()).get());
+ old_pass = toUTF8(ptrW(edit_OLD_PASSWD.GetText()).get());
+
+ bool old_pass_match = false;
+ if (!mir_strcmp(ptrA(g_plugin.getUStringA("szKeyPassword")), old_pass.c_str()))
+ old_pass_match = true;
+
+ if (!old_pass_match) {
+ if (globals.key_id_global[0]) {
+ string dbsetting = "szKey_";
+ dbsetting += toUTF8(globals.key_id_global);
+ dbsetting += "_Password";
+ ptrA pass(g_plugin.getUStringA(dbsetting.c_str()));
+ if (!mir_strcmp(pass, old_pass.c_str()))
+ old_pass_match = true;
+ }
+ }
+
+ if (!old_pass_match)
+ if (MessageBox(m_hwnd, TranslateT("Old password does not match, you can continue, but GPG will reject wrong password.\nDo you want to continue?"), TranslateT("Error"), MB_YESNO) == IDNO)
+ return false;
+
+ gpg_execution_params_pass params(old_pass, new_pass);
+ params.addParam(L"--edit-key");
+ params.addParam(globals.key_id_global);
+ params.addParam(L"passwd");
+
+ HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(&pxEexcute_passwd_change_thread, ¶ms);
+ if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) {
+ if (params.child)
+ params.child->terminate();
+ if (globals.debuglog)
+ globals.debuglog << "GPG execution timed out, aborted";
+ return true;
+ }
+
+ return params.result != pxNotFound;
+ }
+};
+
+void ShowChangePasswdDlg()
+{
+ CDlgChangePasswdMsgBox *d = new CDlgChangePasswdMsgBox;
+ d->DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// New key generation dialog
+
+class CDlgKeyGen : public CDlgBase
+{
+ CCtrlCombo combo_KEY_TYPE;
+ CCtrlEdit edit_KEY_LENGTH, edit_KEY_PASSWD, edit_KEY_REAL_NAME, edit_KEY_EMAIL, edit_KEY_COMMENT, edit_KEY_EXPIRE_DATE;
+ CCtrlData lbl_GENERATING_TEXT;
+
+public:
+ CDlgKeyGen() :
+ CDlgBase(g_plugin, IDD_KEY_GEN),
+ combo_KEY_TYPE(this, IDC_KEY_TYPE),
+ edit_KEY_LENGTH(this, IDC_KEY_LENGTH),
+ edit_KEY_PASSWD(this, IDC_KEY_PASSWD),
+ edit_KEY_REAL_NAME(this, IDC_KEY_REAL_NAME),
+ edit_KEY_EMAIL(this, IDC_KEY_EMAIL),
+ edit_KEY_COMMENT(this, IDC_KEY_COMMENT),
+ edit_KEY_EXPIRE_DATE(this, IDC_KEY_EXPIRE_DATE),
+ lbl_GENERATING_TEXT(this, IDC_GENERATING_TEXT)
+ {
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow");
+ SetCaption(TranslateT("Key Generation dialog"));
+
+ combo_KEY_TYPE.AddString(L"RSA");
+ combo_KEY_TYPE.AddString(L"DSA");
+ combo_KEY_TYPE.SelectString(L"RSA");
+ edit_KEY_EXPIRE_DATE.SetText(L"0");
+ edit_KEY_LENGTH.SetText(L"4096");
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ // data sanity checks
+ ptrW tmp(combo_KEY_TYPE.GetText());
+ if (mir_wstrlen(tmp) < 3) {
+ MessageBox(nullptr, TranslateT("You must set encryption algorithm first"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_LENGTH.GetText();
+ int length = _wtoi(tmp);
+ if (length < 1024 || length > 4096) {
+ MessageBox(nullptr, TranslateT("Key length must be of length from 1024 to 4096 bits"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_EXPIRE_DATE.GetText();
+ if (mir_wstrlen(tmp) != 10 && tmp[0] != '0') {
+ MessageBox(nullptr, TranslateT("Invalid date"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_REAL_NAME.GetText();
+ if (mir_wstrlen(tmp) < 4) {
+ MessageBox(nullptr, TranslateT("Name must contain at least 4 characters"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+ if (wcschr(tmp, '(') || wcschr(tmp, ')')) {
+ MessageBox(nullptr, TranslateT("Name cannot contain '(' or ')'"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_EMAIL.GetText();
+ if (mir_wstrlen(tmp) < 5 || !wcschr(tmp, '@')) {
+ MessageBox(nullptr, TranslateT("Invalid Email"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ // generating key file
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+ path += L"\\new_key";
+ wfstream f(path.c_str(), std::ios::out);
+ if (!f.is_open()) {
+ MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ f << "Key-Type: ";
+ char *tmp2 = mir_u2a(combo_KEY_TYPE.GetText());
+ char *subkeytype = (char *)mir_alloc(6);
+ if (strstr(tmp2, "RSA"))
+ mir_strcpy(subkeytype, "RSA");
+ else if (strstr(tmp2, "DSA")) //this is useless check for now, but it will be required if someone add another key types support
+ mir_strcpy(subkeytype, "ELG-E");
+ f << tmp2;
+ mir_free(tmp2);
+ f << "\n";
+ f << "Key-Length: ";
+ f << _wtoi(edit_KEY_LENGTH.GetText());
+ f << "\n";
+ f << "Subkey-Length: ";
+ f << _wtoi(edit_KEY_LENGTH.GetText());
+ f << "\n";
+ f << "Subkey-Type: ";
+ f << subkeytype;
+ mir_free(subkeytype);
+ f << "\n";
+ if (edit_KEY_PASSWD.GetText()[0]) {
+ f << "Passphrase: ";
+ f << toUTF8(edit_KEY_PASSWD.GetText()).c_str();
+ f << "\n";
+ }
+ f << "Name-Real: ";
+ f << toUTF8(edit_KEY_REAL_NAME.GetText()).c_str();
+ f << "\n";
+ if (edit_KEY_COMMENT.GetText()[0]) {
+ f << "Name-Comment: ";
+ f << toUTF8(edit_KEY_COMMENT.GetText()).c_str();
+ f << "\n";
+ }
+ f << "Name-Email: ";
+ f << toUTF8(edit_KEY_EMAIL.GetText()).c_str();
+ f << "\n";
+ f << "Expire-Date: ";
+ f << toUTF8(edit_KEY_EXPIRE_DATE.GetText()).c_str();
+ f << "\n";
+ f.close();
+
+ lbl_GENERATING_TEXT.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE);
+ lbl_GENERATING_TEXT.SetText(TranslateT("Generating new key, please wait..."));
+ combo_KEY_TYPE.Disable();
+ edit_KEY_LENGTH.Disable();
+ edit_KEY_PASSWD.Disable();
+ edit_KEY_REAL_NAME.Disable();
+ edit_KEY_EMAIL.Disable();
+ edit_KEY_COMMENT.Disable();
+ edit_KEY_EXPIRE_DATE.Disable();
+
+ // gpg execution
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"--gen-key");
+ params.addParam(path.c_str());
+ params.bNoOutput = true;
+ if (!gpg_launcher(params, boost::posix_time::minutes(10)))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ boost::filesystem::remove(path.c_str());
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow");
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// First run dialog
+
+class CDlgFirstRun : public CDlgBase
+{
+ void refresh_key_list()
+ {
+ list_KEY_LIST.DeleteAllItems();
+ int i = 1;
+
+ // parse gpg output
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--list-secret-keys");
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ wstring::size_type p = 0, p2 = 0, stop = 0;
+ string out(params.out);
+ while (p != string::npos) {
+ if ((p = out.find("sec ", p)) == string::npos)
+ break;
+ p += 5;
+ if (p < stop)
+ break;
+ stop = p;
+ p2 = out.find("/", p) - 1;
+ wchar_t *key_len = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()), *creation_date = nullptr, *expire_date = nullptr;
+ p2 += 2;
+ p = out.find(" ", p2);
+ std::wstring key_id = toUTF16(out.substr(p2, p - p2));
+ p += 1;
+ p2 = out.find(" ", p);
+ std::string::size_type p3 = out.find("\n", p);
+ if ((p2 != std::string::npos) && (p3 < p2)) {
+ p2 = p3;
+ creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p - 1)).c_str());
+ }
+ else {
+ creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ p2 = out.find("[", p2);
+ p2 = out.find("expires:", p2);
+ p2 += mir_strlen("expires:");
+ if (p2 != std::string::npos) {
+ p2++;
+ p = p2;
+ p2 = out.find("]", p);
+ expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ //check expiration
+ bool expired = false;
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ wchar_t buf[5];
+ wcsncpy_s(buf, expire_date, _TRUNCATE);
+ int year = _wtoi(buf);
+ if (year < now.date().year())
+ expired = true;
+ else if (year == now.date().year()) {
+ wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
+ int month = _wtoi(buf);
+ if (month < now.date().month())
+ expired = true;
+ else if (month == now.date().month()) {
+ wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
+ unsigned day = _wtoi(buf);
+ if (day <= now.date().day_number())
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ mir_free(key_len);
+ mir_free(creation_date);
+ mir_free(expire_date);
+ //mimic normal behaviour
+ p = out.find("uid ", p);
+ p2 = out.find_first_not_of(" ", p + 5);
+ p = out.find("<", p2);
+ p++;
+ //p2 = out.find(">", p);
+ //
+ continue; //does not add to key list
+ }
+ }
+ }
+ int row = list_KEY_LIST.AddItem(L"", 0);
+ list_KEY_LIST.SetItemText(row, 3, creation_date);
+ mir_free(creation_date);
+ if (expire_date) {
+ list_KEY_LIST.SetItemText(row, 4, expire_date);
+ mir_free(expire_date);
+ }
+ list_KEY_LIST.SetItemText(row, 5, key_len);
+ mir_free(key_len);
+ list_KEY_LIST.SetItemText(row, 0, (wchar_t *)key_id.c_str());
+ p = out.find("uid ", p);
+ p2 = out.find_first_not_of(" ", p + 5);
+ p = out.find("<", p2);
+
+ wstring tmp = toUTF16(out.substr(p2, p - p2));
+ list_KEY_LIST.SetItemText(row, 2, (wchar_t *)tmp.c_str());
+
+ p++;
+ p2 = out.find(">", p);
+
+ tmp = toUTF16(out.substr(p, p2 - p));
+ list_KEY_LIST.SetItemText(row, 1, (wchar_t *)tmp.c_str());
+
+ // get accounts
+ std::wstring accs;
+ for (auto &pa : Accounts()) {
+ std::string setting = pa->szModuleName;
+ setting += "_KeyID";
+ ptrW str(g_plugin.getWStringA(setting.c_str(), L""));
+ if (key_id == str.get()) {
+ if (!accs.empty())
+ accs += L",";
+ accs += pa->tszAccountName;
+ }
+ }
+ list_KEY_LIST.SetItemText(row, 6, accs.c_str());
+ }
+ i++;
+ list_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(6, LVSCW_AUTOSIZE);
+ }
+
+ CCtrlListView list_KEY_LIST;
+ CCtrlButton btn_COPY_PUBKEY, btn_EXPORT_PRIVATE, btn_CHANGE_PASSWD, btn_GENERATE_RANDOM, btn_GENERATE_KEY, btn_OTHER, btn_DELETE_KEY, btn_OK;
+ CCtrlEdit edit_KEY_PASSWORD;
+ CCtrlCombo combo_ACCOUNT;
+ CCtrlData lbl_KEY_ID, lbl_GENERATING_KEY;
+ wchar_t fp[16];
+ const char *m_szCurrAcc = nullptr;
+
+public:
+ CDlgFirstRun() :
+ CDlgBase(g_plugin, IDD_FIRST_RUN),
+ list_KEY_LIST(this, IDC_KEY_LIST),
+ btn_COPY_PUBKEY(this, IDC_COPY_PUBKEY),
+ btn_EXPORT_PRIVATE(this, IDC_EXPORT_PRIVATE),
+ btn_CHANGE_PASSWD(this, IDC_CHANGE_PASSWD),
+ btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM),
+ btn_GENERATE_KEY(this, IDC_GENERATE_KEY),
+ btn_OTHER(this, IDC_OTHER),
+ btn_DELETE_KEY(this, IDC_DELETE_KEY),
+ btn_OK(this, ID_OK),
+ edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD),
+ combo_ACCOUNT(this, IDC_ACCOUNT),
+ lbl_KEY_ID(this, IDC_KEY_ID),
+ lbl_GENERATING_KEY(this, IDC_GENERATING_KEY)
+ {
+ fp[0] = 0;
+
+ btn_COPY_PUBKEY.OnClick = Callback(this, &CDlgFirstRun::onClick_COPY_PUBKEY);
+ btn_EXPORT_PRIVATE.OnClick = Callback(this, &CDlgFirstRun::onClick_EXPORT_PRIVATE);
+ btn_CHANGE_PASSWD.OnClick = Callback(this, &CDlgFirstRun::onClick_CHANGE_PASSWD);
+ btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_RANDOM);
+ btn_GENERATE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_KEY);
+ btn_OTHER.OnClick = Callback(this, &CDlgFirstRun::onClick_OTHER);
+ btn_DELETE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_DELETE_KEY);
+ btn_OK.OnClick = Callback(this, &CDlgFirstRun::onClick_OK);
+
+ combo_ACCOUNT.OnChange = Callback(this, &CDlgFirstRun::onChange_ACCOUNT);
+
+ list_KEY_LIST.OnClick = Callback(this, &CDlgFirstRun::onChange_KEY_LIST);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow");
+ SetCaption(TranslateT("Bind own keys to accounts"));
+ btn_COPY_PUBKEY.Disable();
+ btn_EXPORT_PRIVATE.Disable();
+ btn_CHANGE_PASSWD.Disable();
+
+ list_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50);
+ list_KEY_LIST.AddColumn(1, TranslateT("Email"), 30);
+ list_KEY_LIST.AddColumn(2, TranslateT("Name"), 250);
+ list_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30);
+ list_KEY_LIST.AddColumn(4, TranslateT("Expire date"), 30);
+ list_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30);
+ list_KEY_LIST.AddColumn(6, TranslateT("Accounts"), 30);
+ list_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
+
+ refresh_key_list();
+
+ combo_ACCOUNT.AddString(TranslateT("Default"));
+
+ for (auto &pa : Accounts()) {
+ if (StriStr(pa->szModuleName, "metacontacts"))
+ continue;
+ if (StriStr(pa->szModuleName, "weather"))
+ continue;
+
+ combo_ACCOUNT.AddString(pa->tszAccountName, (LPARAM)pa->szModuleName);
+ }
+ combo_ACCOUNT.SetCurSel(0);
+
+ lbl_KEY_ID.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get()));
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow");
+ }
+
+ void onClick_COPY_PUBKEY(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ params.out.Remove('\r');
+ Utils_ClipboardCopy(params.out);
+ }
+
+ void onClick_EXPORT_PRIVATE(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ ptrW p(GetFilePath(L"Choose file to export key", L"*", L"Any file", true));
+ if (!p || !p[0])
+ return;
+
+ std::ofstream file;
+ file.open(p, std::ios::trunc | std::ios::out);
+ if (!file.is_open())
+ return; //TODO: handle error
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export-secret-keys");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ params.out.Remove('\r');
+ file << params.out.c_str();
+ if (file.is_open())
+ file.close();
+ }
+
+ void onClick_CHANGE_PASSWD(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, globals.key_id_global, _countof(globals.key_id_global));
+
+ // temporary code follows
+ std::string old_pass, new_pass;
+
+ gpg_execution_params_pass params(old_pass, new_pass);
+ params.addParam(L"--edit-key");
+ params.addParam(globals.key_id_global);
+ params.addParam(L"passwd");
+
+ HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(pxEexcute_passwd_change_thread, ¶ms);
+ if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) {
+ if (params.child)
+ params.child->terminate();
+ if (globals.debuglog)
+ globals.debuglog << "GPG execution timed out, aborted";
+ this->Close();
+ }
+ }
+
+ void onClick_GENERATE_RANDOM(CCtrlButton *)
+ {
+ lbl_GENERATING_KEY.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE);
+ lbl_GENERATING_KEY.SetText(TranslateT("Generating new random key, please wait"));
+ btn_GENERATE_KEY.Disable();
+ btn_OTHER.Disable();
+ btn_DELETE_KEY.Disable();
+ list_KEY_LIST.Disable();
+ btn_GENERATE_RANDOM.Disable();
+ gpg_use_new_random_key(m_szCurrAcc);
+ this->Close();
+ }
+
+ void onClick_GENERATE_KEY(CCtrlButton *)
+ {
+ CDlgKeyGen().DoModal();
+ refresh_key_list();
+ }
+
+ void onClick_OTHER(CCtrlButton *)
+ {
+ ShowLoadPublicKeyDialog(0, true);
+ refresh_key_list();
+ }
+
+ void onClick_DELETE_KEY(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+ {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--fingerprint");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ int s = params.out.Find("Key fingerprint = ");
+ s += mir_strlen("Key fingerprint = ");
+ int s2 = params.out.Find("\n", s);
+
+ CMStringW tmp = params.out.Mid(s, s2 - s);
+ tmp.Remove(' ');
+
+ gpg_execution_params params2;
+ params2.addParam(L"--batch");
+ params2.addParam(L"--delete-secret-and-public-key");
+ params2.addParam(L"--fingerprint");
+ params2.addParam(tmp.c_str());
+
+ if (!gpg_launcher(params2))
+ return;
+ if (params2.result == pxNotFound)
+ return;
+ }
+
+ if (m_szCurrAcc == nullptr) {
+ g_plugin.delSetting("GPGPubKey");
+ g_plugin.delSetting("KeyID");
+ g_plugin.delSetting("KeyComment");
+ g_plugin.delSetting("KeyMainName");
+ g_plugin.delSetting("KeyMainEmail");
+ g_plugin.delSetting("KeyType");
+ }
+ else {
+ CMStringA acc_str = m_szCurrAcc;
+ g_plugin.delSetting(acc_str + "_GPGPubKey");
+ g_plugin.delSetting(acc_str + "_KeyMainName");
+ g_plugin.delSetting(acc_str + "_KeyID");
+ g_plugin.delSetting(acc_str + "_KeyComment");
+ g_plugin.delSetting(acc_str + "_KeyMainEmail");
+ g_plugin.delSetting(acc_str + "_KeyType");
+ }
+
+ list_KEY_LIST.DeleteItem(i);
+ }
+
+ void onClick_OK(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+ wchar_t name[65];
+ list_KEY_LIST.GetItemText(i, 2, name, 64);
+ {
+ if (wcschr(name, '(')) {
+ wstring str = name;
+ wstring::size_type p = str.find(L"(") - 1;
+ mir_wstrcpy(name, str.substr(0, p).c_str());
+ }
+ }
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ params.out.Remove('\r');
+
+ if (m_szCurrAcc == nullptr) {
+ g_plugin.setString("GPGPubKey", params.out.c_str());
+ g_plugin.setWString("KeyMainName", name);
+ g_plugin.setWString("KeyID", fp);
+
+ wstring keyinfo = TranslateT("Default private key ID");
+ keyinfo += L": ";
+ keyinfo += (fp[0]) ? fp : L"not set";
+ extern HWND hwndCurKey_p;
+ SetWindowText(hwndCurKey_p, keyinfo.c_str());
+ }
+ else {
+ CMStringA acc_str = m_szCurrAcc;
+ g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str());
+ g_plugin.setWString(acc_str + "_KeyMainName", name);
+ g_plugin.setWString(acc_str + "_KeyID", fp);
+ }
+
+ ptrW passwd(edit_KEY_PASSWORD.GetText());
+ if (mir_wstrlen(passwd)) {
+ string dbsetting = "szKey_";
+ dbsetting += _T2A(fp);
+ dbsetting += "_Password";
+ g_plugin.setWString(dbsetting.c_str(), passwd);
+ }
+
+ //bAutoExchange = CheckStateStoreDB(hwndDlg, IDC_AUTO_EXCHANGE, "bAutoExchange") != 0; //TODO: check is it just typo, or doing something
+ globals.gpg_valid = isGPGValid();
+ globals.gpg_keyexist = isGPGKeyExist();
+ DestroyWindow(m_hwnd);
+ }
+
+ void onChange_ACCOUNT(CCtrlCombo *pCombo)
+ {
+ CMStringW keyinfo = TranslateT("key ID");
+ keyinfo += ": ";
+
+ m_szCurrAcc = (const char *)pCombo->GetCurData();
+ if (m_szCurrAcc == nullptr) {
+ keyinfo += g_plugin.getMStringW("KeyID", TranslateT("not set"));
+ }
+ else {
+ std::string acc_str = m_szCurrAcc;
+ acc_str += "_KeyID";
+ keyinfo += g_plugin.getMStringW(acc_str.c_str(), TranslateT("not set"));
+ }
+ lbl_KEY_ID.SetText(keyinfo);
+ }
+
+ void onChange_KEY_LIST(CCtrlListView::TEventInfo *ev)
+ {
+ if (ev->nmlv) {
+ NMLISTVIEW *hdr = ev->nmlv;
+
+ if (hdr->hdr.code == NM_CLICK) {
+ btn_OK.Enable();
+ btn_COPY_PUBKEY.Enable();
+ btn_EXPORT_PRIVATE.Enable();
+ btn_CHANGE_PASSWD.Enable();
+ }
+ }
+ }
+};
+
+void ShowFirstRunDialog()
+{
+ CDlgFirstRun().DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CDlgNewKey::CDlgNewKey(MCONTACT _hContact, wstring _new_key) :
+ CDlgBase(g_plugin, IDD_NEW_KEY),
+ lbl_KEY_FROM(this, IDC_KEY_FROM),
+ lbl_MESSAGE(this, IDC_MESSAGE),
+ btn_IMPORT(this, ID_IMPORT),
+ btn_IMPORT_AND_USE(this, IDC_IMPORT_AND_USE),
+ btn_IGNORE_KEY(this, IDC_IGNORE_KEY)
+{
+ hContact = _hContact;
+ new_key = _new_key;
+ btn_IMPORT.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT);
+ btn_IMPORT_AND_USE.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT_AND_USE);
+ btn_IGNORE_KEY.OnClick = Callback(this, &CDlgNewKey::onClick_IGNORE_KEY);
+}
+
+bool CDlgNewKey::OnInitDialog()
+{
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow");
+
+ CMStringW tmp = g_plugin.getMStringW(hContact, "GPGPubKey");
+ lbl_MESSAGE.SetText(!tmp.IsEmpty() ? TranslateT("There is existing key for contact, would you like to replace it with new key?") : TranslateT("New public key was received, do you want to import it?"));
+ btn_IMPORT_AND_USE.Enable(g_plugin.getByte(hContact, "GPGEncryption", 0));
+ btn_IMPORT.SetText(!tmp.IsEmpty() ? TranslateT("Replace") : TranslateT("Accept"));
+
+ tmp.Format(TranslateT("Received key from %s"), Clist_GetContactDisplayName(hContact));
+ lbl_KEY_FROM.SetText(tmp);
+ return true;
+}
+
+void CDlgNewKey::OnDestroy()
+{
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow");
+}
+
+void CDlgNewKey::onClick_IMPORT(CCtrlButton*)
+{
+ ImportKey(hContact, new_key);
+ this->Close();
+}
+
+void CDlgNewKey::onClick_IMPORT_AND_USE(CCtrlButton*)
+{
+ ImportKey(hContact, new_key);
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ this->Close();
+}
+
+void CDlgNewKey::onClick_IGNORE_KEY(CCtrlButton*)
+{
+ this->Close();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CDlgKeyPasswordMsgBox::CDlgKeyPasswordMsgBox(MCONTACT _hContact) :
+ CDlgBase(g_plugin, IDD_KEY_PASSWD),
+ lbl_KEYID(this, IDC_KEYID),
+ edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD),
+ chk_DEFAULT_PASSWORD(this, IDC_DEFAULT_PASSWORD),
+ chk_SAVE_PASSWORD(this, IDC_SAVE_PASSWORD)
+{
+ hContact = _hContact;
+}
+
+bool CDlgKeyPasswordMsgBox::OnInitDialog()
+{
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow");
+
+ CMStringW questionstr = TranslateT("Please enter password for key with ID: ");
+ questionstr += g_plugin.getMStringW(hContact, "InKeyID");
+ lbl_KEYID.SetText(questionstr.c_str());
+
+ chk_DEFAULT_PASSWORD.Disable();
+ return true;
+}
+
+bool CDlgKeyPasswordMsgBox::OnApply()
+{
+ ptrW tmp(edit_KEY_PASSWORD.GetText());
+ if (tmp && tmp[0]) {
+ if (chk_SAVE_PASSWORD.GetState()) {
+ inkeyid = g_plugin.getStringA(hContact, "InKeyID", "");
+ if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) {
+ string dbsetting = "szKey_";
+ dbsetting += inkeyid;
+ dbsetting += "_Password";
+ g_plugin.setWString(dbsetting.c_str(), tmp);
+ }
+ else g_plugin.setWString("szKeyPassword", tmp);
+ }
+ globals.wszPassword = tmp;
+ }
+ mir_free(inkeyid);
+ return true;
+}
+
+void CDlgKeyPasswordMsgBox::OnDestroy()
+{
+ if (!m_bSucceeded)
+ globals._terminate = true;
+
+ mir_free(inkeyid);
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow");
+}
diff --git a/plugins/New_GPG/src/ui.h b/plugins/New_GPG/src/ui.h index 1e30441b83..1e5c936716 100644 --- a/plugins/New_GPG/src/ui.h +++ b/plugins/New_GPG/src/ui.h @@ -1,69 +1,69 @@ -// Copyright © 2017-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef UI_H -#define UI_H - -void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal); -void ShowFirstRunDialog(); - -class CDlgEncryptedFileMsgBox : public CDlgBase -{ - CCtrlCheck chk_REMEMBER; - CCtrlButton btn_IGNORE, btn_DECRYPT; - -public: - CDlgEncryptedFileMsgBox(); - bool OnInitDialog() override; - - void onClick_IGNORE(CCtrlButton*); - void onClick_DECRYPT(CCtrlButton*); -}; - -class CDlgNewKey : public CDlgBase -{ - wstring new_key; - MCONTACT hContact; - CCtrlData lbl_KEY_FROM, lbl_MESSAGE; - CCtrlButton btn_IMPORT, btn_IMPORT_AND_USE, btn_IGNORE_KEY; - -public: - CDlgNewKey(MCONTACT hContact, wstring new_key); - bool OnInitDialog() override; - void OnDestroy() override; - - void onClick_IMPORT(CCtrlButton*); - void onClick_IMPORT_AND_USE(CCtrlButton*); - void onClick_IGNORE_KEY(CCtrlButton*); -}; - -class CDlgKeyPasswordMsgBox : public CDlgBase //always modal -{ - char *inkeyid = nullptr; - MCONTACT hContact; - CCtrlData lbl_KEYID; - CCtrlEdit edit_KEY_PASSWORD; - CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD; - -public: - CDlgKeyPasswordMsgBox(MCONTACT _hContact); - - bool OnInitDialog() override; - bool OnApply() override; - void OnDestroy() override; -}; - +// Copyright © 2017-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef UI_H
+#define UI_H
+
+void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal);
+void ShowFirstRunDialog();
+
+class CDlgEncryptedFileMsgBox : public CDlgBase
+{
+ CCtrlCheck chk_REMEMBER;
+ CCtrlButton btn_IGNORE, btn_DECRYPT;
+
+public:
+ CDlgEncryptedFileMsgBox();
+ bool OnInitDialog() override;
+
+ void onClick_IGNORE(CCtrlButton*);
+ void onClick_DECRYPT(CCtrlButton*);
+};
+
+class CDlgNewKey : public CDlgBase
+{
+ wstring new_key;
+ MCONTACT hContact;
+ CCtrlData lbl_KEY_FROM, lbl_MESSAGE;
+ CCtrlButton btn_IMPORT, btn_IMPORT_AND_USE, btn_IGNORE_KEY;
+
+public:
+ CDlgNewKey(MCONTACT hContact, wstring new_key);
+ bool OnInitDialog() override;
+ void OnDestroy() override;
+
+ void onClick_IMPORT(CCtrlButton*);
+ void onClick_IMPORT_AND_USE(CCtrlButton*);
+ void onClick_IGNORE_KEY(CCtrlButton*);
+};
+
+class CDlgKeyPasswordMsgBox : public CDlgBase //always modal
+{
+ char *inkeyid = nullptr;
+ MCONTACT hContact;
+ CCtrlData lbl_KEYID;
+ CCtrlEdit edit_KEY_PASSWORD;
+ CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD;
+
+public:
+ CDlgKeyPasswordMsgBox(MCONTACT _hContact);
+
+ bool OnInitDialog() override;
+ bool OnApply() override;
+ void OnDestroy() override;
+};
+
#endif // UI_H
\ No newline at end of file diff --git a/plugins/New_GPG/src/utilities.cpp b/plugins/New_GPG/src/utilities.cpp index f6466f89b6..cefc7ade47 100644 --- a/plugins/New_GPG/src/utilities.cpp +++ b/plugins/New_GPG/src/utilities.cpp @@ -1,1428 +1,1428 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -#include "utf8.h" - -void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc) -{ - wchar_t str[MAX_PATH + 2] = {}; - OPENFILENAME ofn = {}; - wchar_t filter[512], *pfilter; - ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName); - ofn.Flags = OFN_EXPLORER; - ofn.lpstrTitle = TranslateW(WindowTittle); - wcsncpy(filter, TranslateW(szExtDesc), _countof(filter) - 1); - pfilter = filter + mir_wstrlen(filter) + 1; - mir_wstrcpy(pfilter, szExt); - pfilter[mir_wstrlen(pfilter) + 1] = '\0'; - pfilter[mir_wstrlen(pfilter) + 2] = '\0'; - ofn.lpstrFilter = filter; - wcsncpy(str, g_plugin.getMStringW(szSetting), _countof(str) - 1); - if (mir_wstrlen(str) < 2) - str[0] = '\0'; - ofn.lpstrFile = str; - ofn.nMaxFile = _MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - if (GetOpenFileName(&ofn)) - g_plugin.setWString(szSetting, str); -} - -wchar_t* GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file) -{ - wchar_t str[MAX_PATH + 1]; - OPENFILENAME ofn = {}; - wchar_t filter[512], *pfilter; - ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName); - ofn.Flags = OFN_EXPLORER; - ofn.lpstrTitle = TranslateW(WindowTittle); - mir_wstrcpy(filter, TranslateW(szExtDesc)); - pfilter = filter + mir_wstrlen(filter) + 1; - mir_wstrcpy(pfilter, szExt); - pfilter[mir_wstrlen(pfilter) + 1] = '\0'; - pfilter[mir_wstrlen(pfilter) + 2] = '\0'; - ofn.lpstrFilter = filter; - mir_wstrcpy(str, L""); - if (mir_wstrlen(str) < 2) - str[0] = '\0'; - ofn.lpstrFile = str; - ofn.nMaxFile = _MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - if (!save_file) { - if (!GetOpenFileName(&ofn)) - return nullptr; - } - else { - if (!GetSaveFileName(&ofn)) - return nullptr; - } - return mir_wstrdup(str); -} - -void GetFolderPath(wchar_t *WindowTittle) -{ - BROWSEINFO pbi = {}; - pbi.lpszTitle = WindowTittle; - pbi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE; - LPITEMIDLIST pidl = SHBrowseForFolder(&pbi); - if (pidl != nullptr) { - wchar_t path[MAX_PATH]; - if (SHGetPathFromIDList(pidl, path)) { - g_plugin.setWString("szHomePath", path); - } - IMalloc * imalloc = nullptr; - if (SUCCEEDED(SHGetMalloc(&imalloc))) { - imalloc->Free(pidl); - imalloc->Release(); - } - } -} - -INT_PTR LoadKey(WPARAM w, LPARAM) -{ - ShowLoadPublicKeyDialog((MCONTACT)w, false); - return 0; -} - -INT_PTR SendKey(WPARAM w, LPARAM) -{ - MCONTACT hContact = db_mc_tryMeta(w); - std::string key_id_str; - - LPSTR proto = Proto_GetBaseAccountName(hContact); - PROTOACCOUNT *acc = Proto_GetAccount(proto); - std::string acc_str; - if (acc) { - acc_str = acc->szModuleName; - key_id_str = acc_str; - key_id_str += "_KeyID"; - acc_str += "_GPGPubKey"; - } - - CMStringA szMessage = g_plugin.getMStringA(acc_str.empty() ? "GPGPubKey" : acc_str.c_str()); - if (szMessage.IsEmpty()) - szMessage = g_plugin.getMStringA("GPGPubKey"); //try to get default key as fallback in any way - - if (!szMessage.IsEmpty()) { - uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - g_plugin.setByte(hContact, "GPGEncryption", 0); - ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)szMessage.c_str()); - std::string msg = "Public key "; - CMStringA keyid = g_plugin.getMStringA(key_id_str.c_str()); - if (keyid.IsEmpty()) - keyid = g_plugin.getMStringA("KeyID"); - msg += keyid; - msg += " sent"; - - HistoryLog(hContact, msg.c_str(), DBEF_SENT); - g_plugin.setByte(hContact, "GPGEncryption", enc); - } - - return 0; -} - -INT_PTR ToggleEncryption(WPARAM w, LPARAM) -{ - MCONTACT hContact = (MCONTACT)w; - uint8_t enc; - if (db_mc_isMeta(hContact)) { - enc = g_plugin.getByte(metaGetMostOnline(hContact), "GPGEncryption"); - if (MessageBox(nullptr, TranslateT("Do you want to toggle encryption for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.getByte(hcnt, "GPGEncryption", enc ? 0 : 1); - } - g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1); - } - } - else { - enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1); - } - setSrmmIcon(hContact); - return 0; -} - -int OnPreBuildContactMenu(WPARAM w, LPARAM) -{ - MCONTACT hContact = db_mc_tryMeta(w); - { - CMenuItem mi2(&g_plugin); - LPSTR proto = Proto_GetBaseAccountName(hContact); - PROTOACCOUNT *acc = Proto_GetAccount(proto); - std::string setting; - if (acc) { - setting = acc->szModuleName; - setting += "_KeyID"; - } - - CMStringA keyid = g_plugin.getMStringA(setting.c_str()); - if (keyid.IsEmpty()) - keyid = g_plugin.getMStringA("KeyID"); - - wchar_t buf[128] = { 0 }; - mir_snwprintf(buf, L"%s: %S", TranslateT("Send public key"), keyid.c_str()); - Menu_ModifyItem(g_plugin.hSendKey, buf); - } - - int flags; - CMStringA tmp = g_plugin.getMStringW(hContact, "GPGPubKey"); - if (tmp.IsEmpty()) { - g_plugin.delSetting(hContact, "GPGEncryption"); - flags = CMIF_GRAYED; - } - else flags = 0; - - if (g_plugin.getByte(hContact, "GPGEncryption")) - Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn off GPG encryption"), g_plugin.getIconHandle(IDI_SECURED), flags); - else - Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn on GPG encryption"), g_plugin.getIconHandle(IDI_UNSECURED), flags); - return 0; -} - -list<wstring> transfers; - -int onProtoAck(WPARAM, LPARAM l) -{ - ACKDATA *ack = (ACKDATA*)l; - if (ack->type == ACKTYPE_FILE) { - switch (ack->result) { - case ACKRESULT_DENIED: case ACKRESULT_FAILED: - break; - case ACKRESULT_SUCCESS: - if (ack->hProcess) { - PROTOFILETRANSFERSTATUS *f = (PROTOFILETRANSFERSTATUS*)ack->hProcess; - if (f == nullptr) - break; - - if ((f->flags & PFTS_SENDING) != PFTS_SENDING) { - wchar_t *filename = nullptr; - if (f->flags & PFTS_UNICODE) { - if (f->szCurrentFile.w && f->szCurrentFile.w[0]) - filename = mir_wstrdup(f->szCurrentFile.w); - if (!filename) - return 0; - } - else { - if (f->szCurrentFile.a && f->szCurrentFile.a[0]) - filename = mir_utf8decodeW(f->szCurrentFile.a); - if (!filename) - return 0; - } - if (wcsstr(filename, L".gpg")) { //decrypt it - //process encrypted file - if (!g_plugin.bFileTransfers && !g_plugin.bSameAction) { - void ShowEncryptedFileMsgBox(); - ShowEncryptedFileMsgBox(); - } - if (!g_plugin.bFileTransfers && g_plugin.bSameAction) - return 0; - if (!globals.bDecryptFiles) - return 0; - HistoryLog(ack->hContact, "Received encrypted file, trying to decrypt"); - if (!boost::filesystem::exists(f->szCurrentFile.w)) - return 0; - - gpg_execution_params params; - params.addParam(L"-o"); - wstring file = filename; - wstring::size_type p1 = file.rfind(L".gpg"); - file.erase(p1, mir_wstrlen(L".gpg")); - if (boost::filesystem::exists(file)) { - if (MessageBox(nullptr, TranslateT("Target file exists, do you want to replace it?"), TranslateT("Warning"), MB_YESNO) == IDNO) - return 0; - } - params.addParam(file); - boost::filesystem::remove(file); - { - // password - CMStringW pass; - CMStringA keyid = g_plugin.getMStringA(ack->hContact, "KeyID"); - if (!keyid.IsEmpty()) { - string dbsetting = "szKey_"; - dbsetting += keyid; - dbsetting += "_Password"; - pass = g_plugin.getMStringW(dbsetting.c_str()); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password in database for key ID: " + string(keyid.c_str()) + ", trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; - } - else { - pass = g_plugin.getMStringW("szKeyPassword"); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; - } - if (!pass.IsEmpty()) { - params.addParam(L"--passphrase"); - params.addParam(pass.c_str()); - } - else if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - else if (globals.debuglog) - globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " without password"; - } - params.addParam(L"-d"); - params.addParam(filename); - if (!gpg_launcher(params, boost::posix_time::minutes(15))) - return 0; - - string out(params.out); - while (out.find("public key decryption failed: bad passphrase") != string::npos) { - if (globals.debuglog) - globals.debuglog << "info: failed to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " password needed, trying to get one"; - if (globals._terminate) - break; - { //save inkey id - string::size_type s = out.find(" encrypted with "); - s = out.find(" ID ", s); - s += mir_strlen(" ID "); - string::size_type s2 = out.find(",", s); - if (db_mc_isMeta(ack->hContact)) - g_plugin.setString(metaGetMostOnline(ack->hContact), "InKeyID", out.substr(s, s2 - s).c_str()); - else - g_plugin.setString(ack->hContact, "InKeyID", out.substr(s, s2 - s).c_str()); - } - - CDlgKeyPasswordMsgBox(ack->hContact).DoModal(); - - if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)); - - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - - if (!gpg_launcher(params, boost::posix_time::seconds(15))) - return 0; - if (params.result == pxNotFound) - return 0; - } - if (params.result == pxSuccess) - boost::filesystem::remove(filename); - } - mir_free(filename); - } - } - break; - } - } - else if (ack->type == ACKTYPE_MESSAGE) { - extern std::list<HANDLE> sent_msgs; - if (!sent_msgs.empty()) { - if (ack->result == ACKRESULT_FAILED) { - std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess); - if (it != sent_msgs.end()) - HistoryLog(ack->hContact, "Failed to send encrypted message"); - } - else if (ack->result == ACKRESULT_SUCCESS) { - std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess); - if (it != sent_msgs.end()) - sent_msgs.erase(it); - } - } - } - return 0; -} - -std::wstring encrypt_file(MCONTACT hContact, wchar_t *filename) -{ - MCONTACT hcnt = db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact; - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--tes"); - params.addParam(L"-r"); - - CMStringW keyid = g_plugin.getMStringW(hcnt, "KeyID"); - wchar_t *name = wcsrchr(filename, '\\'); - if (!name) - name = filename; - else - name++; - wchar_t *file_out = new wchar_t[mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1]; - mir_snwprintf(file_out, mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1, L"%s.gpg", name); - params.addParam(keyid.c_str()); - - if (g_plugin.getByte(hcnt, "bAlwaysTrust")) { - params.addParam(L"--trust-model"); - params.addParam(L"always"); - } - - params.addParam(L"-o"); - wchar_t *temp = _tgetenv(L"TEMP"); - params.addParam(wstring(temp) + L"\\" + file_out); - wstring path_out = temp; - path_out += L"\\"; - path_out += file_out; - boost::filesystem::remove(path_out); - params.addParam(L"-e"); - params.addParam(filename); - delete[] file_out; - - if (!gpg_launcher(params, boost::posix_time::minutes(3))) - return nullptr; - - if (params.out.Find("There is no assurance this key belongs to the named user") != -1) { - if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO)) - return nullptr; - - g_plugin.setByte(hcnt, "bAlwaysTrust", 1); - - params.addParam(L"--trust-model"); - params.addParam(L"always"); - if (!gpg_launcher(params, boost::posix_time::minutes(3))) - return nullptr; - } - return path_out; -} - -//from secureim partially -INT_PTR onSendFile(WPARAM w, LPARAM l) -{ - CCSDATA *ccs = (CCSDATA*)l; - if (!g_plugin.bFileTransfers) - return Proto_ChainSend(w, ccs); - - if (isContactSecured(ccs->hContact)) { - char *proto = Proto_GetBaseAccountName(ccs->hContact); - bool cap_found = false, supported_proto = false; - ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", "")); - if (jid[0]) { - for (auto p : globals.Accounts) { - ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); - if (caps) { - supported_proto = true; - string str; - for (int i = 0;; i++) { - str.push_back(caps[i]); - if (caps[i] == '\0') - if (caps[i + 1] == '\0') - break; - } - - if (str.find("GPG_Encrypted_FileTransfers:0") != string::npos) - cap_found = true; - } - } - } - - if (supported_proto && !cap_found) { - if (MessageBox(nullptr, TranslateT("Capability to decrypt file not found on other side.\nRecipient may be unable to decrypt file(s).\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO) - return Proto_ChainSend(w, ccs); - } - if (!supported_proto) { - if (MessageBox(nullptr, TranslateT("Unable to check encryption support on other side.\nRecipient may be unable to decrypt file(s).\nCurrently capability check supported only for ICQ and Jabber protocols.\nIt will work for any other proto if Miranda with New_GPG is used on other side.\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO) - return Proto_ChainSend(w, ccs); - } - HistoryLog(ccs->hContact, TranslateU("encrypting file for transfer"), DBEF_SENT); - if (StriStr(ccs->szProtoService, "/sendfilew")) { - wchar_t **file = (wchar_t **)ccs->lParam; - for (int i = 0; file[i]; i++) { - if (!boost::filesystem::exists(file[i])) - return 0; //we do not want to send file unencrypted (sometimes ack have wrong info) - if (wcsstr(file[i], L".gpg")) - continue; - std::wstring path_out = encrypt_file(ccs->hContact, file[i]); - mir_free(file[i]); - file[i] = mir_wstrdup(path_out.c_str()); - transfers.push_back(path_out); - } - } - else { - char **file = (char**)ccs->lParam; - for (int i = 0; file[i]; i++) { - if (!boost::filesystem::exists(file[i])) - return 0; //we do not want to send file unencrypted (sometimes ack have wrong info) - if (strstr(file[i], ".gpg")) - continue; - wchar_t *tmp = mir_utf8decodeW(file[i]); - std::wstring path_out = encrypt_file(ccs->hContact, tmp); - mir_free(tmp); - char* tmp2 = mir_utf8encodeW(path_out.c_str()); - mir_free(file[i]); - file[i] = tmp2; - transfers.push_back(path_out); - - } - } - } - return Proto_ChainSend(w, ccs); -} - -void HistoryLog(MCONTACT hContact, const char *msg, uint32_t _time, uint32_t flags) -{ - DBEVENTINFO dbei = {}; - dbei.szModule = MODULENAME; - dbei.flags = DBEF_UTF | flags; - dbei.timestamp = (_time) ? _time : (uint32_t)time(0); - dbei.cbBlob = (uint32_t)mir_strlen(msg) + 1; - dbei.pBlob = (uint8_t*)msg; - db_event_add(hContact, &dbei); -} - -static int ControlAddStringUtf(HWND ctrl, uint32_t msg, const wchar_t *szString) -{ - int item = -1; - item = SendMessage(ctrl, msg, 0, (LPARAM)szString); - return item; -} - -int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data) -{ - int item = ControlAddStringUtf(hCombo, CB_ADDSTRING, szString); - SendMessage(hCombo, CB_SETITEMDATA, item, data); - - return item; -} - -static JABBER_HANDLER_FUNC SendHandler(IJabberInterface *ji, TiXmlElement *node, void*) -{ - TiXmlDocument *pDoc = node->GetDocument(); - - for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) { - LPCSTR str = local_node->GetText(); - LPCSTR nodename = local_node->Name(); - LPCSTR attr = local_node->Attribute("to"); - if (attr) { - MCONTACT hContact = ji->ContactFromJID(attr); - if (hContact) - if (!isContactSecured(hContact)) - break; - } - - if (str == nullptr) - continue; - - // TODO: make following block more readable - if (g_plugin.bPresenceSigning && nodename && strstr(nodename, "status")) { - string path_out = ptrA(g_plugin.getUStringA("szHomePath", "")); - string file = get_random(10); - path_out += "\\tmp\\"; - path_out += file; - boost::filesystem::remove(path_out); - wfstream f(path_out.c_str(), std::ios::out); - f << str; - f.close(); - if (!boost::filesystem::exists(path_out)) { - if (globals.debuglog) - globals.debuglog << "info: Failed to write prescense in file"; - break; - } - - gpg_execution_params params; - { - char setting[64]; - mir_snprintf(setting, sizeof(setting) - 1, "%s_KeyID", ji->GetModuleName()); - CMStringA inkeyid = g_plugin.getMStringA(setting); - if (inkeyid.IsEmpty()) - inkeyid = g_plugin.getMStringA("KeyID"); - - CMStringW pass; - if (!inkeyid.IsEmpty()) { - string dbsetting = "szKey_"; - dbsetting += inkeyid; - dbsetting += "_Password"; - pass = g_plugin.getMStringW(dbsetting.c_str()); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to encrypt message from self with password"; - } - else { - pass = g_plugin.getMStringW("szKeyPassword"); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password for all keys in database, trying to encrypt message from self with password"; - } - if (pass[0]) { - params.addParam(L"--passphrase"); - params.addParam(pass.c_str()); - } - else if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to encrypt message from self with password"; - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - else if (globals.debuglog) - globals.debuglog << "info: passwords not found in database or memory, trying to encrypt message from self without password"; - } - - params.addParam(L"--local-user"); - params.addParam(g_plugin.getMStringW("KeyID").c_str()); - params.addParam(L"--default-key"); - params.addParam(g_plugin.getMStringW("KeyID").c_str()); - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"-abs"); - params.addParam(Utf2T(path_out.c_str()).get()); - gpg_launcher(params, boost::posix_time::seconds(15)); // TODO: handle errors - - boost::filesystem::remove(path_out); - path_out += ".asc"; - f.open(path_out.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - wstring data; - if (f.is_open()) { - std::wifstream::pos_type size = f.tellg(); - wchar_t *tmp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1]; - f.seekg(0, std::ios::beg); - f.read(tmp, size); - tmp[size] = '\0'; - data.append(tmp); - delete[] tmp; - f.close(); - boost::filesystem::remove(path_out); - } - if (data.empty()) { - if (globals.debuglog) - globals.debuglog << "info: Failed to read prescense sign from file"; - break; - } - if (data.find(L"-----BEGIN PGP SIGNATURE-----") != wstring::npos && data.find(L"-----END PGP SIGNATURE-----") != wstring::npos) { - wstring::size_type p1 = data.find(L"-----BEGIN PGP SIGNATURE-----") + mir_wstrlen(L"-----BEGIN PGP SIGNATURE-----"); - if (data.find(L"Version: ", p1) != wstring::npos) { - p1 = data.find(L"Version: ", p1); - p1 = data.find(L"\n", p1); - if (data.find(L"Version: ", p1) != wstring::npos) { - p1 = data.find(L"Version: ", p1); - p1 = data.find(L"\n", p1) + 1; - } - else - p1 += 1; - } - if (data.find(L"Comment: ", p1) != wstring::npos) { - p1 = data.find(L"Comment: ", p1); - p1 = data.find(L"\n", p1); - if (data.find(L"Comment: ", p1) != wstring::npos) { - p1 = data.find(L"Comment: ", p1); - p1 = data.find(L"\n", p1) + 1; - } - else - p1 += 1; - } - else - p1 += 1; - p1++; - wstring::size_type p2 = data.find(L"-----END PGP SIGNATURE-----"); - - std::wstring tmp = data.substr(p1, p2 - p1); - strip_line_term(tmp); - TiXmlElement* encrypted_data = pDoc->NewElement("x"); node->InsertEndChild(encrypted_data); - encrypted_data->SetText(tmp.c_str()); - encrypted_data->SetAttribute("xmlns", "jabber:x:signed"); - } - break; - } - } - return FALSE; -} - -static JABBER_HANDLER_FUNC PresenceHandler(IJabberInterface *ji, TiXmlElement* node, void*) -{ - for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) { - LPCSTR nodename = local_node->Name(); - if (nodename == nullptr) - continue; - - if (!mir_strcmp(nodename, "x")) - continue; - - for (auto *pAttr = local_node->FirstAttribute(); pAttr; pAttr = pAttr->Next()) { - LPCSTR value = pAttr->Value(); - if (!mir_strcmp(value, "jabber:x:signed")) { - std::string status_str; - - for (auto *local_node2 = node->FirstChildElement(); local_node2; local_node2 = local_node2->NextSiblingElement()) { - LPCSTR nodename2 = local_node2->Name(); - if (!mir_strcmp(nodename2, "status")) { - LPCSTR status = local_node2->GetText(); - if (status) - status_str = status; - break; - } - } - - LPCSTR data = local_node->GetText(); - string sign = "-----BEGIN PGP SIGNATURE-----\n\n"; - wstring file = toUTF16(get_random(10)), status_file = toUTF16(get_random(10)); - sign += data; - sign += "\n-----END PGP SIGNATURE-----\n"; - - CMStringW path_out = g_plugin.getMStringW("szHomePath"), status_file_out = path_out; - path_out += L"\\tmp\\"; - path_out += file.c_str(); - path_out += L".sig"; - status_file_out += L"\\tmp\\"; - status_file_out += status_file.c_str(); - status_file_out += L".status"; - - boost::filesystem::remove(path_out.c_str()); - boost::filesystem::remove(status_file_out.c_str()); - wfstream f(path_out.c_str(), std::ios::out); - while (!f.is_open()) - f.open(path_out.c_str(), std::ios::out); - f << sign.c_str(); - f.close(); - f.open(status_file_out.c_str(), std::ios::out); - while (!f.is_open()) - f.open(status_file_out.c_str(), std::ios::out); - f << status_str.c_str(); - f.close(); - if (!boost::filesystem::exists(path_out.c_str())) { - if (globals.debuglog) - globals.debuglog << "info: Failed to write sign in file"; - return FALSE; - } - { - // gpg - gpg_execution_params params; - params.addParam(L"--verify"); - params.addParam(L"-a"); - params.addParam(path_out.c_str()); - params.addParam(status_file_out.c_str()); - if (!gpg_launcher(params, boost::posix_time::seconds(15))) - return FALSE; - if (params.result == pxNotFound) - return FALSE; - - boost::filesystem::remove(path_out.c_str()); - boost::filesystem::remove(status_file_out.c_str()); - - string out(params.out); - if (out.find("key ID ") != string::npos) { - //need to get hcontact here, i can get jid from hxml, and get handle from jid, maybe exists better way ? - string::size_type p1 = out.find("key ID ") + mir_strlen("key ID "); - string::size_type p2 = out.find("\n", p1); - if (p1 != string::npos && p2 != string::npos) { - MCONTACT hContact = ji->ContactFromJID(node->Attribute("from")); - if (hContact) - globals.hcontact_data[hContact].key_in_prescense = out.substr(p1, p2 - p1 - 1).c_str(); - } - } - } - return FALSE; - } - } - } - return FALSE; -} - -static JABBER_HANDLER_FUNC MessageHandler(IJabberInterface*, TiXmlElement*, void*) -{ - return FALSE; -} - -int GetJabberInterface(WPARAM, LPARAM) //get interface for all jabber accounts, options later -{ - list <JabberAccount *>::iterator p; - globals.Accounts.clear(); - - int accNum = 0; - for (auto &pa : Accounts()) { - IJabberInterface *pApi = getJabberApi(pa->szModuleName); - if (pApi == nullptr) - continue; - - auto *pAcc = new JabberAccount(); - pAcc->setJabberInterface(pApi); - if (pa->tszAccountName) - pAcc->setAccountName(mir_wstrdup(pa->tszAccountName)); - else - pAcc->setAccountName(mir_a2u(pa->szModuleName)); - - pAcc->setAccountNumber(accNum++); - pAcc->setSendHandler(pApi->AddSendHandler((JABBER_HANDLER_FUNC)SendHandler)); - pAcc->setPresenceHandler(pApi->AddPresenceHandler((JABBER_HANDLER_FUNC)PresenceHandler)); - - if (g_plugin.bAutoExchange) { - pApi->RegisterFeature("GPG_Key_Auto_Exchange:0", "Indicates that gpg installed and configured to public key auto exchange (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)"); - pApi->AddFeatures("GPG_Key_Auto_Exchange:0\0\0"); - } - if (g_plugin.bFileTransfers) { - pApi->RegisterFeature("GPG_Encrypted_FileTransfers:0", "Indicates that gpg installed and configured to encrypt files (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)"); - pApi->AddFeatures("GPG_Encrypted_FileTransfers:0\0\0"); - } - - globals.Accounts.push_back(pAcc); - } - - return 0; -} - -void RemoveHandlers() -{ - for (auto &it : globals.Accounts) { - auto *pApi = it->getJabberInterface(); - if (pApi == nullptr) - continue; - - if (it->getMessageHandler() != INVALID_HANDLE_VALUE) - pApi->RemoveHandler(it->getMessageHandler()); - if (it->getPresenceHandler() != INVALID_HANDLE_VALUE) - pApi->RemoveHandler(it->getPresenceHandler()); - pApi->RemoveFeatures("GPG_Encrypted_FileTransfers:0\0\0"); - pApi->RemoveFeatures("GPG_Key_Auto_Exchange:0\0\0"); - } -} - -bool isContactSecured(MCONTACT hContact) -{ - uint8_t gpg_enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - if (!gpg_enc) { - if (globals.debuglog) - globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact)); - return false; - } - if (!db_mc_isMeta(hContact)) { - CMStringW key = g_plugin.getMStringW(hContact, "GPGPubKey"); - if (key.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact)); - return false; - } - } - if (globals.debuglog) - globals.debuglog << "encryption is turned on for " + toUTF8(Clist_GetContactDisplayName(hContact)); - return true; -} - -bool isContactHaveKey(MCONTACT hContact) -{ - ptrW key(g_plugin.getWStringA(hContact, "GPGPubKey")); - return (mir_wstrlen(key) > 0); -} - -bool isGPGKeyExist() -{ - CMStringW id(g_plugin.getMStringW("KeyID")); - CMStringA key(g_plugin.getMStringA("GPGPubKey")); - return (!id.IsEmpty() && !key.IsEmpty()); -} - -bool isGPGValid() -{ - ptrW tmp(g_plugin.getWStringA("szGpgBinPath", L"")); - bool gpg_exists = false, is_valid = true; - boost::filesystem::path p(tmp); - - if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) - gpg_exists = true; - else { - wchar_t path[MAX_PATH], mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - - //mir_realloc(path, (mir_wstrlen(path)+64)*sizeof(wchar_t)); - wchar_t gpg_path[MAX_PATH]; - mir_wstrcpy(gpg_path, mir_path); - mir_wstrcat(gpg_path, L"\\GnuPG\\gpg.exe"); - - p = boost::filesystem::path(gpg_path); - if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) { - gpg_exists = true; - mir_wstrcpy(path, L"GnuPG\\gpg.exe"); - } - tmp = mir_wstrdup(path); - } - - if (gpg_exists) { - g_plugin.setWString("szGpgBinPath", tmp); - - gpg_execution_params params; - params.addParam(L"--version"); - bool _gpg_valid = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = _gpg_valid; //TODO: check this - int p1 = params.out.Find("(GnuPG) "); - if (p1 == -1) - is_valid = false; - } - - return is_valid && gpg_exists; -} - -#define NEWTSTR_MALLOC(A) (A==NULL)?NULL:mir_strcpy((char*)mir_alloc(sizeof(char)*(mir_strlen(A)+1)),A) - -const bool StriStr(const char *str, const char *substr) -{ - bool i = false; - char *str_up = NEWTSTR_MALLOC(str); - char *substr_up = NEWTSTR_MALLOC(substr); - - CharUpperBuffA(str_up, (uint32_t)mir_strlen(str_up)); - CharUpperBuffA(substr_up, (uint32_t)mir_strlen(substr_up)); - - if (strstr(str_up, substr_up)) - i = true; - - mir_free(str_up); - mir_free(substr_up); - - return i; -} - -bool IsOnline(MCONTACT hContact) -{ - if (g_plugin.getByte(hContact, "Status", 0) == ID_STATUS_OFFLINE) - return false; - return true; -} - -string toUTF8(wstring str) -{ - string ustr; - try { - utf8::utf16to8(str.begin(), str.end(), back_inserter(ustr)); - } - catch (const utf8::exception& e) { - if (globals.debuglog) - globals.debuglog << std::string("utf8cpp encoding exception: ") + (char*)e.what(); - //TODO - } - return ustr; -} - -wstring toUTF16(string str) //convert as much as possible -{ - wstring ustr; - string tmpstr; - try { - utf8::replace_invalid(str.begin(), str.end(), back_inserter(tmpstr)); - utf8::utf8to16(tmpstr.begin(), tmpstr.end(), back_inserter(ustr)); - } - catch (const utf8::exception& e) { - if (globals.debuglog) - globals.debuglog << std::string("utf8cpp decoding exception: ") + (char*)e.what(); - //TODO - } - return ustr; -} - -string get_random(int length) -{ - string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); - string data; - boost::random_device rng; - boost::variate_generator<boost::random_device&, boost::uniform_int<>> gen(rng, boost::uniform_int<>(0, (int)chars.length() - 1)); - for (int i = 0; i < length; ++i) - data += chars[gen()]; - return data; -} - -void send_encrypted_msgs_thread(void *param) -{ - MCONTACT hContact = (MCONTACT)(DWORD_PTR)param; - while (true) { - while (!isContactSecured(hContact)) - Sleep(1000); - - if (!globals.hcontact_data[hContact].msgs_to_send.empty()) { - Sleep(1000); - list<string>::iterator end = globals.hcontact_data[hContact].msgs_to_send.end(); - extern std::list<HANDLE> sent_msgs; - for (list<string>::iterator p = globals.hcontact_data[hContact].msgs_to_send.begin(); p != end; ++p) { - sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)p->c_str())); - HistoryLog(hContact, p->c_str(), DBEF_SENT); - Sleep(1000); - } - globals.hcontact_data[hContact].msgs_to_send.clear(); - return; - } - else - return; - } -} - -void ExportGpGKeysFunc(int type) -{ - ptrW p(GetFilePath(L"Choose file to export keys", L"*", L"Any file", true)); - if (!p || !p[0]) - return; - - std::ofstream file; - file.open(p, std::ios::trunc | std::ios::out); - if (!file.is_open()) - return; //TODO: handle error - - int exported_keys = 0; - if (!type || type == 2) { - for (auto &hContact : Contacts()) { - CMStringA key = g_plugin.getMStringA(hContact, "GPGPubKey"); - if (key.IsEmpty()) - continue; - - ptrW wszLogin(Contact::GetInfo(CNF_UNIQUEID, 0, Proto_GetBaseAccountName(hContact))), wszContact(Contact::GetInfo(CNF_UNIQUEID, hContact)); - if (wszLogin == nullptr || wszContact == nullptr) - continue; - - std::string id = "Comment: login "; - id += T2Utf(wszLogin).get(); - id += " contact_id "; - id += T2Utf(wszContact).get(); - id += '\n'; - - int p1 = key.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (p1 == -1) - continue; - p1 += mir_strlen("-----BEGIN PGP PUBLIC KEY BLOCK-----"); - p1++; - key.Insert(p1, id.c_str()); - file << key; - file << std::endl; - exported_keys++; - } - } - - if (type == 1 || type == 2) { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--export-secret-keys"); - params.addParam(L"-a"); - gpg_launcher(params); //TODO: handle errors - - file << params.out.c_str(); - file << std::endl; - } - if (file.is_open()) - file.close(); - wchar_t msg[512]; - if (type == 2) - mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys and all private keys."), exported_keys); - else if (type == 1) - mir_snwprintf(msg, TranslateT("We have successfully exported all private keys.")); - else if (!type) - mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys."), exported_keys); - MessageBox(nullptr, msg, TranslateT("Keys export result"), MB_OK); -} - -INT_PTR ImportGpGKeys(WPARAM, LPARAM) -{ - ptrW p(GetFilePath(L"Choose file to import keys from", L"*", L"Any file")); - if (!p || !p[0]) - return 1; - - std::ifstream file; - file.open(p, std::ios::in); - if (!file.is_open()) - return 1; //TODO: handle error - - int processed_keys = 0, processed_private_keys = 0; - - char line[256]; - file.getline(line, 255); - if (!strstr(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && !strstr(line, "-----BEGIN PGP PRIVATE KEY BLOCK-----")) - return 1; //TODO: handle error - - std::string key, login, contact_id; - key += line; - key += '\n'; - while (file.is_open() && !file.eof()) { - file.getline(line, 255); - key += line; - key += '\n'; - if (strstr(line, "-----END PGP PUBLIC KEY BLOCK-----")) { - std::string::size_type p1 = key.rfind("Comment: login "), p2 = 0; - if (p1 == std::string::npos) - { - key.clear(); - continue; //TODO: warning about malformed file - } - p1 += mir_strlen("Comment: login "); - p2 = key.rfind(" contact_id "); - login = key.substr(p1, p2 - p1); - p2 += mir_strlen(" contact_id "); - p1 = key.find("\n", p2); - contact_id = key.substr(p2, p1 - p2); - p1 = key.rfind("Comment: login "); - p2 = key.find("\n", p1); - p2++; - key.erase(p1, p2 - p1); - - PROTOACCOUNT *pFoundAcc = nullptr; - for (auto &pa : Accounts()) { - ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, 0, pa->szModuleName)); - if (wszUniqueId == nullptr) - continue; - - if (login == T2Utf(wszUniqueId).get()) { - pFoundAcc = pa; - break; - } - } - - if (pFoundAcc == nullptr) - continue; - - for (auto &hContact : Contacts(pFoundAcc->szModuleName)) { - ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, hContact, pFoundAcc->szModuleName)); - if (wszUniqueId == nullptr) - continue; - - if (contact_id != T2Utf(wszUniqueId).get()) - continue; - - CMStringW path = g_plugin.getMStringW("szHomePath"); - - gpg_execution_params params; - { - wstring rand = toUTF16(get_random(10)); - path += L"\\"; - path += rand.c_str(); - boost::filesystem::remove(path.c_str()); - wfstream f(path, std::ios::out); - f << toUTF16(key).c_str(); - f.close(); - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(path.c_str()); - } - if (!gpg_launcher(params)) - break; - if (params.result == pxNotFound) - break; - if (params.result == pxSuccess) - processed_keys++; - - string output(params.out); - if (output.find("already in secret keyring") != string::npos) { - MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK); - boost::filesystem::remove(path.c_str()); - break; - } - char *tmp2; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp2, nullptr); - g_plugin.setString(hContact, "KeyID", tmp2); - mir_free(tmp2); - s = output.find("“", s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else - s += 3; - if ((s2 = output.find("(", s)) == string::npos) - s2 = output.find("<", s); - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s2 != string::npos) { - tmp2 = (char *)mir_alloc((output.substr(s, s2 - s - 1).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s, s2 - s - 1).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) { - g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - mir_free(tmp2); - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (output[s] == ')') { - tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) - g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str()); - mir_free(tmp2); - s += 3; - s2 = output.find(">", s); - tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) - g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str()); - mir_free(tmp2); - } - else { - tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) - g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp2); - } - } - g_plugin.setByte(hContact, "GPGEncryption", 1); - g_plugin.setWString(hContact, "GPGPubKey", toUTF16(key).c_str()); - - boost::filesystem::remove(path.c_str()); - break; - } - key.clear(); - } - else if (strstr(line, "-----END PGP PRIVATE KEY BLOCK-----")) { - CMStringW tmp2 = g_plugin.getMStringW("szHomePath"); - tmp2 += L"\\temporary_exported.asc"; - boost::filesystem::remove(tmp2.c_str()); - - wfstream f(tmp2, std::ios::out); - f << toUTF16(key).c_str(); - f.close(); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - break; - if (params.result == pxNotFound) - break; - if (params.result == pxSuccess) - processed_private_keys++; - key.clear(); - } - } - if (file.is_open()) - file.close(); - wchar_t msg[512]; - if (processed_private_keys) - mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys and some private keys."), processed_keys); - else - mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys."), processed_keys); - MessageBox(nullptr, msg, TranslateT("Keys import result"), MB_OK); - return 0; -} - -void SendErrorMessage(MCONTACT hContact) -{ - if (!g_plugin.bSendErrorMessages) - return; - - uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - g_plugin.setByte(hContact, "GPGEncryption", 0); - ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); - HistoryLog(hContact, "Error message sent", DBEF_SENT); - g_plugin.setByte(hContact, "GPGEncryption", enc); -} - -void fix_line_term(std::string &s) -{ - if (s.empty()) - return; - - boost::algorithm::erase_all(s, "\r\r"); - - // unified line endings for unix & windows port - boost::algorithm::replace_all(s, "\r\n", "\n"); - boost::algorithm::replace_all(s, "\n", "\r\n"); -} - -void strip_line_term(std::wstring &s) -{ - if (s.empty()) - return; - boost::algorithm::erase_all(s, L"\r"); - boost::algorithm::erase_all(s, L"\n"); -} - -void strip_line_term(std::string &s) -{ - if (s.empty()) - return; - boost::algorithm::erase_all(s, "\r"); - boost::algorithm::erase_all(s, "\n"); -} - -void strip_tags(std::string &str) -{ - if (str.empty()) - return; - boost::algorithm::erase_all(str, globals.wszInopentag.c_str()); - boost::algorithm::erase_all(str, globals.wszInclosetag.c_str()); - boost::algorithm::erase_all(str, globals.wszOutopentag.c_str()); - boost::algorithm::erase_all(str, globals.wszOutclosetag.c_str()); -} - - -void ShowEncryptedFileMsgBox() -{ - CDlgEncryptedFileMsgBox *d = new CDlgEncryptedFileMsgBox; - d->DoModal(); -} - -void clean_temp_dir() -{ - using namespace boost::filesystem; - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - - CMStringW tmp = mir_path; - tmp += g_plugin.getMStringW("szHomePath"); - tmp += L"\\tmp"; - if (exists(tmp.c_str()) && is_directory(tmp.c_str())) { - boost::filesystem::path p(tmp); - for (directory_iterator i = directory_iterator(p), end = directory_iterator(); i != end; ++i) { - if (boost::filesystem::is_regular_file(i->path())) { - if ((i->path().filename().generic_string().length() == 10 && (i->path().filename().generic_string().find(".") == std::string::npos)) || - i->path().extension() == ".sig" || i->path().extension() == ".asc" || i->path().extension() == ".status") - boost::filesystem::remove(i->path()); - } - } - } -} - -bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path) -{ - wstring tmp = gpg_bin_path; - if (!tmp.empty()) { - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - if (!boost::filesystem::exists(tmp)) { - MessageBox(nullptr, TranslateT("GPG binary does not exist.\nPlease choose another location"), TranslateT("Warning"), MB_OK); - return false; - } - } - else { - MessageBox(nullptr, TranslateT("Please choose GPG binary location"), TranslateT("Warning"), MB_OK); - return false; - } - { - bool bad_version = false; - g_plugin.setWString("szGpgBinPath", tmp.c_str()); - - gpg_execution_params params; - params.addParam(L"--version"); - bool _gpg_valid = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = _gpg_valid; //TODO: check this - g_plugin.delSetting("szGpgBinPath"); - int p1 = params.out.Find("(GnuPG) "); - if (p1 != -1) { - p1 += mir_strlen("(GnuPG) "); - if (params.out[p1] != '1') - bad_version = true; - } - else { - bad_version = false; - MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - return false; - } - if (bad_version) - MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - } - tmp = gpg_home_path; - if (tmp[tmp.length()] == '\\') - tmp.erase(tmp.length()); - if (tmp.empty()) { - MessageBox(nullptr, TranslateT("Please set keyring's home directory"), TranslateT("Warning"), MB_OK); - return false; - } - { - CMStringW path = g_plugin.getMStringW("szHomePath"); - uint32_t dwFileAttr = GetFileAttributes(path); - if (dwFileAttr != INVALID_FILE_ATTRIBUTES) { - dwFileAttr &= ~FILE_ATTRIBUTE_READONLY; - SetFileAttributes(path, dwFileAttr); - } - } - return true; -} - -void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path) -{ - g_plugin.setWString("szGpgBinPath", gpg_bin_path); - g_plugin.setWString("szHomePath", gpg_home_path); -} - -bool gpg_use_new_random_key(const char *account_name) -{ - CMStringW path = g_plugin.getMStringW("szHomePath"); - path += L"\\new_key"; - - // generating key file - wfstream f(path.c_str(), std::ios::out); - if (!f.is_open()) { - MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); - return false; - } - f << "Key-Type: RSA"; - f << "\n"; - f << "Key-Length: 4096"; - f << "\n"; - f << "Subkey-Type: RSA"; - f << "\n"; - f << "Name-Real: "; - f << get_random(6).c_str(); - f << "\n"; - f << "Name-Email: "; - f << get_random(5).c_str(); - f << "@"; - f << get_random(5).c_str(); - f << "."; - f << get_random(3).c_str(); - f << "\n"; - f.close(); - - { - // gpg execution - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"--gen-key"); - params.addParam(path.c_str()); - params.bNoOutput = true; - if (!gpg_launcher(params, boost::posix_time::minutes(10))) - return false; - if (params.result == pxNotFound) - return false; - - boost::filesystem::remove(path.c_str()); - string out(params.out); - int p1 = params.out.Find("key "); - if (p1 != -1) - path = ptrW(mir_utf8decodeW(params.out.Mid(p1 + 4, 8).c_str())); - else - path.Empty(); - } - - if (!path.IsEmpty()) { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(path.c_str()); - if (!gpg_launcher(params)) - return false; - - if (params.result == pxNotFound) - return false; - - params.out.Remove('\r'); - - if (account_name == nullptr) { - g_plugin.setString("GPGPubKey", params.out.c_str()); - g_plugin.setWString("KeyID", path.c_str()); - } - else { - CMStringA acc_str = account_name; - g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str()); - g_plugin.setWString(acc_str + "_KeyID", path.c_str()); - } - } - return true; -} +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+#include "utf8.h"
+
+void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc)
+{
+ wchar_t str[MAX_PATH + 2] = {};
+ OPENFILENAME ofn = {};
+ wchar_t filter[512], *pfilter;
+ ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
+ ofn.Flags = OFN_EXPLORER;
+ ofn.lpstrTitle = TranslateW(WindowTittle);
+ wcsncpy(filter, TranslateW(szExtDesc), _countof(filter) - 1);
+ pfilter = filter + mir_wstrlen(filter) + 1;
+ mir_wstrcpy(pfilter, szExt);
+ pfilter[mir_wstrlen(pfilter) + 1] = '\0';
+ pfilter[mir_wstrlen(pfilter) + 2] = '\0';
+ ofn.lpstrFilter = filter;
+ wcsncpy(str, g_plugin.getMStringW(szSetting), _countof(str) - 1);
+ if (mir_wstrlen(str) < 2)
+ str[0] = '\0';
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (GetOpenFileName(&ofn))
+ g_plugin.setWString(szSetting, str);
+}
+
+wchar_t* GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file)
+{
+ wchar_t str[MAX_PATH + 1];
+ OPENFILENAME ofn = {};
+ wchar_t filter[512], *pfilter;
+ ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
+ ofn.Flags = OFN_EXPLORER;
+ ofn.lpstrTitle = TranslateW(WindowTittle);
+ mir_wstrcpy(filter, TranslateW(szExtDesc));
+ pfilter = filter + mir_wstrlen(filter) + 1;
+ mir_wstrcpy(pfilter, szExt);
+ pfilter[mir_wstrlen(pfilter) + 1] = '\0';
+ pfilter[mir_wstrlen(pfilter) + 2] = '\0';
+ ofn.lpstrFilter = filter;
+ mir_wstrcpy(str, L"");
+ if (mir_wstrlen(str) < 2)
+ str[0] = '\0';
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (!save_file) {
+ if (!GetOpenFileName(&ofn))
+ return nullptr;
+ }
+ else {
+ if (!GetSaveFileName(&ofn))
+ return nullptr;
+ }
+ return mir_wstrdup(str);
+}
+
+void GetFolderPath(wchar_t *WindowTittle)
+{
+ BROWSEINFO pbi = {};
+ pbi.lpszTitle = WindowTittle;
+ pbi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE;
+ LPITEMIDLIST pidl = SHBrowseForFolder(&pbi);
+ if (pidl != nullptr) {
+ wchar_t path[MAX_PATH];
+ if (SHGetPathFromIDList(pidl, path)) {
+ g_plugin.setWString("szHomePath", path);
+ }
+ IMalloc * imalloc = nullptr;
+ if (SUCCEEDED(SHGetMalloc(&imalloc))) {
+ imalloc->Free(pidl);
+ imalloc->Release();
+ }
+ }
+}
+
+INT_PTR LoadKey(WPARAM w, LPARAM)
+{
+ ShowLoadPublicKeyDialog((MCONTACT)w, false);
+ return 0;
+}
+
+INT_PTR SendKey(WPARAM w, LPARAM)
+{
+ MCONTACT hContact = db_mc_tryMeta(w);
+ std::string key_id_str;
+
+ LPSTR proto = Proto_GetBaseAccountName(hContact);
+ PROTOACCOUNT *acc = Proto_GetAccount(proto);
+ std::string acc_str;
+ if (acc) {
+ acc_str = acc->szModuleName;
+ key_id_str = acc_str;
+ key_id_str += "_KeyID";
+ acc_str += "_GPGPubKey";
+ }
+
+ CMStringA szMessage = g_plugin.getMStringA(acc_str.empty() ? "GPGPubKey" : acc_str.c_str());
+ if (szMessage.IsEmpty())
+ szMessage = g_plugin.getMStringA("GPGPubKey"); //try to get default key as fallback in any way
+
+ if (!szMessage.IsEmpty()) {
+ uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)szMessage.c_str());
+ std::string msg = "Public key ";
+ CMStringA keyid = g_plugin.getMStringA(key_id_str.c_str());
+ if (keyid.IsEmpty())
+ keyid = g_plugin.getMStringA("KeyID");
+ msg += keyid;
+ msg += " sent";
+
+ HistoryLog(hContact, msg.c_str(), DBEF_SENT);
+ g_plugin.setByte(hContact, "GPGEncryption", enc);
+ }
+
+ return 0;
+}
+
+INT_PTR ToggleEncryption(WPARAM w, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)w;
+ uint8_t enc;
+ if (db_mc_isMeta(hContact)) {
+ enc = g_plugin.getByte(metaGetMostOnline(hContact), "GPGEncryption");
+ if (MessageBox(nullptr, TranslateT("Do you want to toggle encryption for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.getByte(hcnt, "GPGEncryption", enc ? 0 : 1);
+ }
+ g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1);
+ }
+ }
+ else {
+ enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1);
+ }
+ setSrmmIcon(hContact);
+ return 0;
+}
+
+int OnPreBuildContactMenu(WPARAM w, LPARAM)
+{
+ MCONTACT hContact = db_mc_tryMeta(w);
+ {
+ CMenuItem mi2(&g_plugin);
+ LPSTR proto = Proto_GetBaseAccountName(hContact);
+ PROTOACCOUNT *acc = Proto_GetAccount(proto);
+ std::string setting;
+ if (acc) {
+ setting = acc->szModuleName;
+ setting += "_KeyID";
+ }
+
+ CMStringA keyid = g_plugin.getMStringA(setting.c_str());
+ if (keyid.IsEmpty())
+ keyid = g_plugin.getMStringA("KeyID");
+
+ wchar_t buf[128] = { 0 };
+ mir_snwprintf(buf, L"%s: %S", TranslateT("Send public key"), keyid.c_str());
+ Menu_ModifyItem(g_plugin.hSendKey, buf);
+ }
+
+ int flags;
+ CMStringA tmp = g_plugin.getMStringW(hContact, "GPGPubKey");
+ if (tmp.IsEmpty()) {
+ g_plugin.delSetting(hContact, "GPGEncryption");
+ flags = CMIF_GRAYED;
+ }
+ else flags = 0;
+
+ if (g_plugin.getByte(hContact, "GPGEncryption"))
+ Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn off GPG encryption"), g_plugin.getIconHandle(IDI_SECURED), flags);
+ else
+ Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn on GPG encryption"), g_plugin.getIconHandle(IDI_UNSECURED), flags);
+ return 0;
+}
+
+list<wstring> transfers;
+
+int onProtoAck(WPARAM, LPARAM l)
+{
+ ACKDATA *ack = (ACKDATA*)l;
+ if (ack->type == ACKTYPE_FILE) {
+ switch (ack->result) {
+ case ACKRESULT_DENIED: case ACKRESULT_FAILED:
+ break;
+ case ACKRESULT_SUCCESS:
+ if (ack->hProcess) {
+ PROTOFILETRANSFERSTATUS *f = (PROTOFILETRANSFERSTATUS*)ack->hProcess;
+ if (f == nullptr)
+ break;
+
+ if ((f->flags & PFTS_SENDING) != PFTS_SENDING) {
+ wchar_t *filename = nullptr;
+ if (f->flags & PFTS_UNICODE) {
+ if (f->szCurrentFile.w && f->szCurrentFile.w[0])
+ filename = mir_wstrdup(f->szCurrentFile.w);
+ if (!filename)
+ return 0;
+ }
+ else {
+ if (f->szCurrentFile.a && f->szCurrentFile.a[0])
+ filename = mir_utf8decodeW(f->szCurrentFile.a);
+ if (!filename)
+ return 0;
+ }
+ if (wcsstr(filename, L".gpg")) { //decrypt it
+ //process encrypted file
+ if (!g_plugin.bFileTransfers && !g_plugin.bSameAction) {
+ void ShowEncryptedFileMsgBox();
+ ShowEncryptedFileMsgBox();
+ }
+ if (!g_plugin.bFileTransfers && g_plugin.bSameAction)
+ return 0;
+ if (!globals.bDecryptFiles)
+ return 0;
+ HistoryLog(ack->hContact, "Received encrypted file, trying to decrypt");
+ if (!boost::filesystem::exists(f->szCurrentFile.w))
+ return 0;
+
+ gpg_execution_params params;
+ params.addParam(L"-o");
+ wstring file = filename;
+ wstring::size_type p1 = file.rfind(L".gpg");
+ file.erase(p1, mir_wstrlen(L".gpg"));
+ if (boost::filesystem::exists(file)) {
+ if (MessageBox(nullptr, TranslateT("Target file exists, do you want to replace it?"), TranslateT("Warning"), MB_YESNO) == IDNO)
+ return 0;
+ }
+ params.addParam(file);
+ boost::filesystem::remove(file);
+ {
+ // password
+ CMStringW pass;
+ CMStringA keyid = g_plugin.getMStringA(ack->hContact, "KeyID");
+ if (!keyid.IsEmpty()) {
+ string dbsetting = "szKey_";
+ dbsetting += keyid;
+ dbsetting += "_Password";
+ pass = g_plugin.getMStringW(dbsetting.c_str());
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password in database for key ID: " + string(keyid.c_str()) + ", trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
+ }
+ else {
+ pass = g_plugin.getMStringW("szKeyPassword");
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
+ }
+ if (!pass.IsEmpty()) {
+ params.addParam(L"--passphrase");
+ params.addParam(pass.c_str());
+ }
+ else if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " without password";
+ }
+ params.addParam(L"-d");
+ params.addParam(filename);
+ if (!gpg_launcher(params, boost::posix_time::minutes(15)))
+ return 0;
+
+ string out(params.out);
+ while (out.find("public key decryption failed: bad passphrase") != string::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info: failed to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " password needed, trying to get one";
+ if (globals._terminate)
+ break;
+ { //save inkey id
+ string::size_type s = out.find(" encrypted with ");
+ s = out.find(" ID ", s);
+ s += mir_strlen(" ID ");
+ string::size_type s2 = out.find(",", s);
+ if (db_mc_isMeta(ack->hContact))
+ g_plugin.setString(metaGetMostOnline(ack->hContact), "InKeyID", out.substr(s, s2 - s).c_str());
+ else
+ g_plugin.setString(ack->hContact, "InKeyID", out.substr(s, s2 - s).c_str());
+ }
+
+ CDlgKeyPasswordMsgBox(ack->hContact).DoModal();
+
+ if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact));
+
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+
+ if (!gpg_launcher(params, boost::posix_time::seconds(15)))
+ return 0;
+ if (params.result == pxNotFound)
+ return 0;
+ }
+ if (params.result == pxSuccess)
+ boost::filesystem::remove(filename);
+ }
+ mir_free(filename);
+ }
+ }
+ break;
+ }
+ }
+ else if (ack->type == ACKTYPE_MESSAGE) {
+ extern std::list<HANDLE> sent_msgs;
+ if (!sent_msgs.empty()) {
+ if (ack->result == ACKRESULT_FAILED) {
+ std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess);
+ if (it != sent_msgs.end())
+ HistoryLog(ack->hContact, "Failed to send encrypted message");
+ }
+ else if (ack->result == ACKRESULT_SUCCESS) {
+ std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess);
+ if (it != sent_msgs.end())
+ sent_msgs.erase(it);
+ }
+ }
+ }
+ return 0;
+}
+
+std::wstring encrypt_file(MCONTACT hContact, wchar_t *filename)
+{
+ MCONTACT hcnt = db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact;
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--tes");
+ params.addParam(L"-r");
+
+ CMStringW keyid = g_plugin.getMStringW(hcnt, "KeyID");
+ wchar_t *name = wcsrchr(filename, '\\');
+ if (!name)
+ name = filename;
+ else
+ name++;
+ wchar_t *file_out = new wchar_t[mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1];
+ mir_snwprintf(file_out, mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1, L"%s.gpg", name);
+ params.addParam(keyid.c_str());
+
+ if (g_plugin.getByte(hcnt, "bAlwaysTrust")) {
+ params.addParam(L"--trust-model");
+ params.addParam(L"always");
+ }
+
+ params.addParam(L"-o");
+ wchar_t *temp = _tgetenv(L"TEMP");
+ params.addParam(wstring(temp) + L"\\" + file_out);
+ wstring path_out = temp;
+ path_out += L"\\";
+ path_out += file_out;
+ boost::filesystem::remove(path_out);
+ params.addParam(L"-e");
+ params.addParam(filename);
+ delete[] file_out;
+
+ if (!gpg_launcher(params, boost::posix_time::minutes(3)))
+ return nullptr;
+
+ if (params.out.Find("There is no assurance this key belongs to the named user") != -1) {
+ if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO))
+ return nullptr;
+
+ g_plugin.setByte(hcnt, "bAlwaysTrust", 1);
+
+ params.addParam(L"--trust-model");
+ params.addParam(L"always");
+ if (!gpg_launcher(params, boost::posix_time::minutes(3)))
+ return nullptr;
+ }
+ return path_out;
+}
+
+//from secureim partially
+INT_PTR onSendFile(WPARAM w, LPARAM l)
+{
+ CCSDATA *ccs = (CCSDATA*)l;
+ if (!g_plugin.bFileTransfers)
+ return Proto_ChainSend(w, ccs);
+
+ if (isContactSecured(ccs->hContact)) {
+ char *proto = Proto_GetBaseAccountName(ccs->hContact);
+ bool cap_found = false, supported_proto = false;
+ ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", ""));
+ if (jid[0]) {
+ for (auto p : globals.Accounts) {
+ ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
+ if (caps) {
+ supported_proto = true;
+ string str;
+ for (int i = 0;; i++) {
+ str.push_back(caps[i]);
+ if (caps[i] == '\0')
+ if (caps[i + 1] == '\0')
+ break;
+ }
+
+ if (str.find("GPG_Encrypted_FileTransfers:0") != string::npos)
+ cap_found = true;
+ }
+ }
+ }
+
+ if (supported_proto && !cap_found) {
+ if (MessageBox(nullptr, TranslateT("Capability to decrypt file not found on other side.\nRecipient may be unable to decrypt file(s).\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO)
+ return Proto_ChainSend(w, ccs);
+ }
+ if (!supported_proto) {
+ if (MessageBox(nullptr, TranslateT("Unable to check encryption support on other side.\nRecipient may be unable to decrypt file(s).\nCurrently capability check supported only for ICQ and Jabber protocols.\nIt will work for any other proto if Miranda with New_GPG is used on other side.\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO)
+ return Proto_ChainSend(w, ccs);
+ }
+ HistoryLog(ccs->hContact, TranslateU("encrypting file for transfer"), DBEF_SENT);
+ if (StriStr(ccs->szProtoService, "/sendfilew")) {
+ wchar_t **file = (wchar_t **)ccs->lParam;
+ for (int i = 0; file[i]; i++) {
+ if (!boost::filesystem::exists(file[i]))
+ return 0; //we do not want to send file unencrypted (sometimes ack have wrong info)
+ if (wcsstr(file[i], L".gpg"))
+ continue;
+ std::wstring path_out = encrypt_file(ccs->hContact, file[i]);
+ mir_free(file[i]);
+ file[i] = mir_wstrdup(path_out.c_str());
+ transfers.push_back(path_out);
+ }
+ }
+ else {
+ char **file = (char**)ccs->lParam;
+ for (int i = 0; file[i]; i++) {
+ if (!boost::filesystem::exists(file[i]))
+ return 0; //we do not want to send file unencrypted (sometimes ack have wrong info)
+ if (strstr(file[i], ".gpg"))
+ continue;
+ wchar_t *tmp = mir_utf8decodeW(file[i]);
+ std::wstring path_out = encrypt_file(ccs->hContact, tmp);
+ mir_free(tmp);
+ char* tmp2 = mir_utf8encodeW(path_out.c_str());
+ mir_free(file[i]);
+ file[i] = tmp2;
+ transfers.push_back(path_out);
+
+ }
+ }
+ }
+ return Proto_ChainSend(w, ccs);
+}
+
+void HistoryLog(MCONTACT hContact, const char *msg, uint32_t _time, uint32_t flags)
+{
+ DBEVENTINFO dbei = {};
+ dbei.szModule = MODULENAME;
+ dbei.flags = DBEF_UTF | flags;
+ dbei.timestamp = (_time) ? _time : (uint32_t)time(0);
+ dbei.cbBlob = (uint32_t)mir_strlen(msg) + 1;
+ dbei.pBlob = (uint8_t*)msg;
+ db_event_add(hContact, &dbei);
+}
+
+static int ControlAddStringUtf(HWND ctrl, uint32_t msg, const wchar_t *szString)
+{
+ int item = -1;
+ item = SendMessage(ctrl, msg, 0, (LPARAM)szString);
+ return item;
+}
+
+int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data)
+{
+ int item = ControlAddStringUtf(hCombo, CB_ADDSTRING, szString);
+ SendMessage(hCombo, CB_SETITEMDATA, item, data);
+
+ return item;
+}
+
+static JABBER_HANDLER_FUNC SendHandler(IJabberInterface *ji, TiXmlElement *node, void*)
+{
+ TiXmlDocument *pDoc = node->GetDocument();
+
+ for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) {
+ LPCSTR str = local_node->GetText();
+ LPCSTR nodename = local_node->Name();
+ LPCSTR attr = local_node->Attribute("to");
+ if (attr) {
+ MCONTACT hContact = ji->ContactFromJID(attr);
+ if (hContact)
+ if (!isContactSecured(hContact))
+ break;
+ }
+
+ if (str == nullptr)
+ continue;
+
+ // TODO: make following block more readable
+ if (g_plugin.bPresenceSigning && nodename && strstr(nodename, "status")) {
+ string path_out = ptrA(g_plugin.getUStringA("szHomePath", ""));
+ string file = get_random(10);
+ path_out += "\\tmp\\";
+ path_out += file;
+ boost::filesystem::remove(path_out);
+ wfstream f(path_out.c_str(), std::ios::out);
+ f << str;
+ f.close();
+ if (!boost::filesystem::exists(path_out)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to write prescense in file";
+ break;
+ }
+
+ gpg_execution_params params;
+ {
+ char setting[64];
+ mir_snprintf(setting, sizeof(setting) - 1, "%s_KeyID", ji->GetModuleName());
+ CMStringA inkeyid = g_plugin.getMStringA(setting);
+ if (inkeyid.IsEmpty())
+ inkeyid = g_plugin.getMStringA("KeyID");
+
+ CMStringW pass;
+ if (!inkeyid.IsEmpty()) {
+ string dbsetting = "szKey_";
+ dbsetting += inkeyid;
+ dbsetting += "_Password";
+ pass = g_plugin.getMStringW(dbsetting.c_str());
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to encrypt message from self with password";
+ }
+ else {
+ pass = g_plugin.getMStringW("szKeyPassword");
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password for all keys in database, trying to encrypt message from self with password";
+ }
+ if (pass[0]) {
+ params.addParam(L"--passphrase");
+ params.addParam(pass.c_str());
+ }
+ else if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to encrypt message from self with password";
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: passwords not found in database or memory, trying to encrypt message from self without password";
+ }
+
+ params.addParam(L"--local-user");
+ params.addParam(g_plugin.getMStringW("KeyID").c_str());
+ params.addParam(L"--default-key");
+ params.addParam(g_plugin.getMStringW("KeyID").c_str());
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"-abs");
+ params.addParam(Utf2T(path_out.c_str()).get());
+ gpg_launcher(params, boost::posix_time::seconds(15)); // TODO: handle errors
+
+ boost::filesystem::remove(path_out);
+ path_out += ".asc";
+ f.open(path_out.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ wstring data;
+ if (f.is_open()) {
+ std::wifstream::pos_type size = f.tellg();
+ wchar_t *tmp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1];
+ f.seekg(0, std::ios::beg);
+ f.read(tmp, size);
+ tmp[size] = '\0';
+ data.append(tmp);
+ delete[] tmp;
+ f.close();
+ boost::filesystem::remove(path_out);
+ }
+ if (data.empty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to read prescense sign from file";
+ break;
+ }
+ if (data.find(L"-----BEGIN PGP SIGNATURE-----") != wstring::npos && data.find(L"-----END PGP SIGNATURE-----") != wstring::npos) {
+ wstring::size_type p1 = data.find(L"-----BEGIN PGP SIGNATURE-----") + mir_wstrlen(L"-----BEGIN PGP SIGNATURE-----");
+ if (data.find(L"Version: ", p1) != wstring::npos) {
+ p1 = data.find(L"Version: ", p1);
+ p1 = data.find(L"\n", p1);
+ if (data.find(L"Version: ", p1) != wstring::npos) {
+ p1 = data.find(L"Version: ", p1);
+ p1 = data.find(L"\n", p1) + 1;
+ }
+ else
+ p1 += 1;
+ }
+ if (data.find(L"Comment: ", p1) != wstring::npos) {
+ p1 = data.find(L"Comment: ", p1);
+ p1 = data.find(L"\n", p1);
+ if (data.find(L"Comment: ", p1) != wstring::npos) {
+ p1 = data.find(L"Comment: ", p1);
+ p1 = data.find(L"\n", p1) + 1;
+ }
+ else
+ p1 += 1;
+ }
+ else
+ p1 += 1;
+ p1++;
+ wstring::size_type p2 = data.find(L"-----END PGP SIGNATURE-----");
+
+ std::wstring tmp = data.substr(p1, p2 - p1);
+ strip_line_term(tmp);
+ TiXmlElement* encrypted_data = pDoc->NewElement("x"); node->InsertEndChild(encrypted_data);
+ encrypted_data->SetText(tmp.c_str());
+ encrypted_data->SetAttribute("xmlns", "jabber:x:signed");
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static JABBER_HANDLER_FUNC PresenceHandler(IJabberInterface *ji, TiXmlElement* node, void*)
+{
+ for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) {
+ LPCSTR nodename = local_node->Name();
+ if (nodename == nullptr)
+ continue;
+
+ if (!mir_strcmp(nodename, "x"))
+ continue;
+
+ for (auto *pAttr = local_node->FirstAttribute(); pAttr; pAttr = pAttr->Next()) {
+ LPCSTR value = pAttr->Value();
+ if (!mir_strcmp(value, "jabber:x:signed")) {
+ std::string status_str;
+
+ for (auto *local_node2 = node->FirstChildElement(); local_node2; local_node2 = local_node2->NextSiblingElement()) {
+ LPCSTR nodename2 = local_node2->Name();
+ if (!mir_strcmp(nodename2, "status")) {
+ LPCSTR status = local_node2->GetText();
+ if (status)
+ status_str = status;
+ break;
+ }
+ }
+
+ LPCSTR data = local_node->GetText();
+ string sign = "-----BEGIN PGP SIGNATURE-----\n\n";
+ wstring file = toUTF16(get_random(10)), status_file = toUTF16(get_random(10));
+ sign += data;
+ sign += "\n-----END PGP SIGNATURE-----\n";
+
+ CMStringW path_out = g_plugin.getMStringW("szHomePath"), status_file_out = path_out;
+ path_out += L"\\tmp\\";
+ path_out += file.c_str();
+ path_out += L".sig";
+ status_file_out += L"\\tmp\\";
+ status_file_out += status_file.c_str();
+ status_file_out += L".status";
+
+ boost::filesystem::remove(path_out.c_str());
+ boost::filesystem::remove(status_file_out.c_str());
+ wfstream f(path_out.c_str(), std::ios::out);
+ while (!f.is_open())
+ f.open(path_out.c_str(), std::ios::out);
+ f << sign.c_str();
+ f.close();
+ f.open(status_file_out.c_str(), std::ios::out);
+ while (!f.is_open())
+ f.open(status_file_out.c_str(), std::ios::out);
+ f << status_str.c_str();
+ f.close();
+ if (!boost::filesystem::exists(path_out.c_str())) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to write sign in file";
+ return FALSE;
+ }
+ {
+ // gpg
+ gpg_execution_params params;
+ params.addParam(L"--verify");
+ params.addParam(L"-a");
+ params.addParam(path_out.c_str());
+ params.addParam(status_file_out.c_str());
+ if (!gpg_launcher(params, boost::posix_time::seconds(15)))
+ return FALSE;
+ if (params.result == pxNotFound)
+ return FALSE;
+
+ boost::filesystem::remove(path_out.c_str());
+ boost::filesystem::remove(status_file_out.c_str());
+
+ string out(params.out);
+ if (out.find("key ID ") != string::npos) {
+ //need to get hcontact here, i can get jid from hxml, and get handle from jid, maybe exists better way ?
+ string::size_type p1 = out.find("key ID ") + mir_strlen("key ID ");
+ string::size_type p2 = out.find("\n", p1);
+ if (p1 != string::npos && p2 != string::npos) {
+ MCONTACT hContact = ji->ContactFromJID(node->Attribute("from"));
+ if (hContact)
+ globals.hcontact_data[hContact].key_in_prescense = out.substr(p1, p2 - p1 - 1).c_str();
+ }
+ }
+ }
+ return FALSE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static JABBER_HANDLER_FUNC MessageHandler(IJabberInterface*, TiXmlElement*, void*)
+{
+ return FALSE;
+}
+
+int GetJabberInterface(WPARAM, LPARAM) //get interface for all jabber accounts, options later
+{
+ list <JabberAccount *>::iterator p;
+ globals.Accounts.clear();
+
+ int accNum = 0;
+ for (auto &pa : Accounts()) {
+ IJabberInterface *pApi = getJabberApi(pa->szModuleName);
+ if (pApi == nullptr)
+ continue;
+
+ auto *pAcc = new JabberAccount();
+ pAcc->setJabberInterface(pApi);
+ if (pa->tszAccountName)
+ pAcc->setAccountName(mir_wstrdup(pa->tszAccountName));
+ else
+ pAcc->setAccountName(mir_a2u(pa->szModuleName));
+
+ pAcc->setAccountNumber(accNum++);
+ pAcc->setSendHandler(pApi->AddSendHandler((JABBER_HANDLER_FUNC)SendHandler));
+ pAcc->setPresenceHandler(pApi->AddPresenceHandler((JABBER_HANDLER_FUNC)PresenceHandler));
+
+ if (g_plugin.bAutoExchange) {
+ pApi->RegisterFeature("GPG_Key_Auto_Exchange:0", "Indicates that gpg installed and configured to public key auto exchange (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)");
+ pApi->AddFeatures("GPG_Key_Auto_Exchange:0\0\0");
+ }
+ if (g_plugin.bFileTransfers) {
+ pApi->RegisterFeature("GPG_Encrypted_FileTransfers:0", "Indicates that gpg installed and configured to encrypt files (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)");
+ pApi->AddFeatures("GPG_Encrypted_FileTransfers:0\0\0");
+ }
+
+ globals.Accounts.push_back(pAcc);
+ }
+
+ return 0;
+}
+
+void RemoveHandlers()
+{
+ for (auto &it : globals.Accounts) {
+ auto *pApi = it->getJabberInterface();
+ if (pApi == nullptr)
+ continue;
+
+ if (it->getMessageHandler() != INVALID_HANDLE_VALUE)
+ pApi->RemoveHandler(it->getMessageHandler());
+ if (it->getPresenceHandler() != INVALID_HANDLE_VALUE)
+ pApi->RemoveHandler(it->getPresenceHandler());
+ pApi->RemoveFeatures("GPG_Encrypted_FileTransfers:0\0\0");
+ pApi->RemoveFeatures("GPG_Key_Auto_Exchange:0\0\0");
+ }
+}
+
+bool isContactSecured(MCONTACT hContact)
+{
+ uint8_t gpg_enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ if (!gpg_enc) {
+ if (globals.debuglog)
+ globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact));
+ return false;
+ }
+ if (!db_mc_isMeta(hContact)) {
+ CMStringW key = g_plugin.getMStringW(hContact, "GPGPubKey");
+ if (key.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact));
+ return false;
+ }
+ }
+ if (globals.debuglog)
+ globals.debuglog << "encryption is turned on for " + toUTF8(Clist_GetContactDisplayName(hContact));
+ return true;
+}
+
+bool isContactHaveKey(MCONTACT hContact)
+{
+ ptrW key(g_plugin.getWStringA(hContact, "GPGPubKey"));
+ return (mir_wstrlen(key) > 0);
+}
+
+bool isGPGKeyExist()
+{
+ CMStringW id(g_plugin.getMStringW("KeyID"));
+ CMStringA key(g_plugin.getMStringA("GPGPubKey"));
+ return (!id.IsEmpty() && !key.IsEmpty());
+}
+
+bool isGPGValid()
+{
+ ptrW tmp(g_plugin.getWStringA("szGpgBinPath", L""));
+ bool gpg_exists = false, is_valid = true;
+ boost::filesystem::path p(tmp);
+
+ if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
+ gpg_exists = true;
+ else {
+ wchar_t path[MAX_PATH], mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+
+ //mir_realloc(path, (mir_wstrlen(path)+64)*sizeof(wchar_t));
+ wchar_t gpg_path[MAX_PATH];
+ mir_wstrcpy(gpg_path, mir_path);
+ mir_wstrcat(gpg_path, L"\\GnuPG\\gpg.exe");
+
+ p = boost::filesystem::path(gpg_path);
+ if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) {
+ gpg_exists = true;
+ mir_wstrcpy(path, L"GnuPG\\gpg.exe");
+ }
+ tmp = mir_wstrdup(path);
+ }
+
+ if (gpg_exists) {
+ g_plugin.setWString("szGpgBinPath", tmp);
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+ bool _gpg_valid = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = _gpg_valid; //TODO: check this
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 == -1)
+ is_valid = false;
+ }
+
+ return is_valid && gpg_exists;
+}
+
+#define NEWTSTR_MALLOC(A) (A==NULL)?NULL:mir_strcpy((char*)mir_alloc(sizeof(char)*(mir_strlen(A)+1)),A)
+
+const bool StriStr(const char *str, const char *substr)
+{
+ bool i = false;
+ char *str_up = NEWTSTR_MALLOC(str);
+ char *substr_up = NEWTSTR_MALLOC(substr);
+
+ CharUpperBuffA(str_up, (uint32_t)mir_strlen(str_up));
+ CharUpperBuffA(substr_up, (uint32_t)mir_strlen(substr_up));
+
+ if (strstr(str_up, substr_up))
+ i = true;
+
+ mir_free(str_up);
+ mir_free(substr_up);
+
+ return i;
+}
+
+bool IsOnline(MCONTACT hContact)
+{
+ if (g_plugin.getByte(hContact, "Status", 0) == ID_STATUS_OFFLINE)
+ return false;
+ return true;
+}
+
+string toUTF8(wstring str)
+{
+ string ustr;
+ try {
+ utf8::utf16to8(str.begin(), str.end(), back_inserter(ustr));
+ }
+ catch (const utf8::exception& e) {
+ if (globals.debuglog)
+ globals.debuglog << std::string("utf8cpp encoding exception: ") + (char*)e.what();
+ //TODO
+ }
+ return ustr;
+}
+
+wstring toUTF16(string str) //convert as much as possible
+{
+ wstring ustr;
+ string tmpstr;
+ try {
+ utf8::replace_invalid(str.begin(), str.end(), back_inserter(tmpstr));
+ utf8::utf8to16(tmpstr.begin(), tmpstr.end(), back_inserter(ustr));
+ }
+ catch (const utf8::exception& e) {
+ if (globals.debuglog)
+ globals.debuglog << std::string("utf8cpp decoding exception: ") + (char*)e.what();
+ //TODO
+ }
+ return ustr;
+}
+
+string get_random(int length)
+{
+ string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
+ string data;
+ boost::random_device rng;
+ boost::variate_generator<boost::random_device&, boost::uniform_int<>> gen(rng, boost::uniform_int<>(0, (int)chars.length() - 1));
+ for (int i = 0; i < length; ++i)
+ data += chars[gen()];
+ return data;
+}
+
+void send_encrypted_msgs_thread(void *param)
+{
+ MCONTACT hContact = (MCONTACT)(DWORD_PTR)param;
+ while (true) {
+ while (!isContactSecured(hContact))
+ Sleep(1000);
+
+ if (!globals.hcontact_data[hContact].msgs_to_send.empty()) {
+ Sleep(1000);
+ list<string>::iterator end = globals.hcontact_data[hContact].msgs_to_send.end();
+ extern std::list<HANDLE> sent_msgs;
+ for (list<string>::iterator p = globals.hcontact_data[hContact].msgs_to_send.begin(); p != end; ++p) {
+ sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)p->c_str()));
+ HistoryLog(hContact, p->c_str(), DBEF_SENT);
+ Sleep(1000);
+ }
+ globals.hcontact_data[hContact].msgs_to_send.clear();
+ return;
+ }
+ else
+ return;
+ }
+}
+
+void ExportGpGKeysFunc(int type)
+{
+ ptrW p(GetFilePath(L"Choose file to export keys", L"*", L"Any file", true));
+ if (!p || !p[0])
+ return;
+
+ std::ofstream file;
+ file.open(p, std::ios::trunc | std::ios::out);
+ if (!file.is_open())
+ return; //TODO: handle error
+
+ int exported_keys = 0;
+ if (!type || type == 2) {
+ for (auto &hContact : Contacts()) {
+ CMStringA key = g_plugin.getMStringA(hContact, "GPGPubKey");
+ if (key.IsEmpty())
+ continue;
+
+ ptrW wszLogin(Contact::GetInfo(CNF_UNIQUEID, 0, Proto_GetBaseAccountName(hContact))), wszContact(Contact::GetInfo(CNF_UNIQUEID, hContact));
+ if (wszLogin == nullptr || wszContact == nullptr)
+ continue;
+
+ std::string id = "Comment: login ";
+ id += T2Utf(wszLogin).get();
+ id += " contact_id ";
+ id += T2Utf(wszContact).get();
+ id += '\n';
+
+ int p1 = key.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (p1 == -1)
+ continue;
+ p1 += mir_strlen("-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ p1++;
+ key.Insert(p1, id.c_str());
+ file << key;
+ file << std::endl;
+ exported_keys++;
+ }
+ }
+
+ if (type == 1 || type == 2) {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--export-secret-keys");
+ params.addParam(L"-a");
+ gpg_launcher(params); //TODO: handle errors
+
+ file << params.out.c_str();
+ file << std::endl;
+ }
+ if (file.is_open())
+ file.close();
+ wchar_t msg[512];
+ if (type == 2)
+ mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys and all private keys."), exported_keys);
+ else if (type == 1)
+ mir_snwprintf(msg, TranslateT("We have successfully exported all private keys."));
+ else if (!type)
+ mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys."), exported_keys);
+ MessageBox(nullptr, msg, TranslateT("Keys export result"), MB_OK);
+}
+
+INT_PTR ImportGpGKeys(WPARAM, LPARAM)
+{
+ ptrW p(GetFilePath(L"Choose file to import keys from", L"*", L"Any file"));
+ if (!p || !p[0])
+ return 1;
+
+ std::ifstream file;
+ file.open(p, std::ios::in);
+ if (!file.is_open())
+ return 1; //TODO: handle error
+
+ int processed_keys = 0, processed_private_keys = 0;
+
+ char line[256];
+ file.getline(line, 255);
+ if (!strstr(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && !strstr(line, "-----BEGIN PGP PRIVATE KEY BLOCK-----"))
+ return 1; //TODO: handle error
+
+ std::string key, login, contact_id;
+ key += line;
+ key += '\n';
+ while (file.is_open() && !file.eof()) {
+ file.getline(line, 255);
+ key += line;
+ key += '\n';
+ if (strstr(line, "-----END PGP PUBLIC KEY BLOCK-----")) {
+ std::string::size_type p1 = key.rfind("Comment: login "), p2 = 0;
+ if (p1 == std::string::npos)
+ {
+ key.clear();
+ continue; //TODO: warning about malformed file
+ }
+ p1 += mir_strlen("Comment: login ");
+ p2 = key.rfind(" contact_id ");
+ login = key.substr(p1, p2 - p1);
+ p2 += mir_strlen(" contact_id ");
+ p1 = key.find("\n", p2);
+ contact_id = key.substr(p2, p1 - p2);
+ p1 = key.rfind("Comment: login ");
+ p2 = key.find("\n", p1);
+ p2++;
+ key.erase(p1, p2 - p1);
+
+ PROTOACCOUNT *pFoundAcc = nullptr;
+ for (auto &pa : Accounts()) {
+ ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, 0, pa->szModuleName));
+ if (wszUniqueId == nullptr)
+ continue;
+
+ if (login == T2Utf(wszUniqueId).get()) {
+ pFoundAcc = pa;
+ break;
+ }
+ }
+
+ if (pFoundAcc == nullptr)
+ continue;
+
+ for (auto &hContact : Contacts(pFoundAcc->szModuleName)) {
+ ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, hContact, pFoundAcc->szModuleName));
+ if (wszUniqueId == nullptr)
+ continue;
+
+ if (contact_id != T2Utf(wszUniqueId).get())
+ continue;
+
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+
+ gpg_execution_params params;
+ {
+ wstring rand = toUTF16(get_random(10));
+ path += L"\\";
+ path += rand.c_str();
+ boost::filesystem::remove(path.c_str());
+ wfstream f(path, std::ios::out);
+ f << toUTF16(key).c_str();
+ f.close();
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(path.c_str());
+ }
+ if (!gpg_launcher(params))
+ break;
+ if (params.result == pxNotFound)
+ break;
+ if (params.result == pxSuccess)
+ processed_keys++;
+
+ string output(params.out);
+ if (output.find("already in secret keyring") != string::npos) {
+ MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK);
+ boost::filesystem::remove(path.c_str());
+ break;
+ }
+ char *tmp2;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ g_plugin.setString(hContact, "KeyID", tmp2);
+ mir_free(tmp2);
+ s = output.find("“", s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else
+ s += 3;
+ if ((s2 = output.find("(", s)) == string::npos)
+ s2 = output.find("<", s);
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s2 != string::npos) {
+ tmp2 = (char *)mir_alloc((output.substr(s, s2 - s - 1).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s, s2 - s - 1).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact) {
+ g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ mir_free(tmp2);
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (output[s] == ')') {
+ tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact)
+ g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str());
+ mir_free(tmp2);
+ s += 3;
+ s2 = output.find(">", s);
+ tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact)
+ g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ mir_free(tmp2);
+ }
+ else {
+ tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact)
+ g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp2);
+ }
+ }
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ g_plugin.setWString(hContact, "GPGPubKey", toUTF16(key).c_str());
+
+ boost::filesystem::remove(path.c_str());
+ break;
+ }
+ key.clear();
+ }
+ else if (strstr(line, "-----END PGP PRIVATE KEY BLOCK-----")) {
+ CMStringW tmp2 = g_plugin.getMStringW("szHomePath");
+ tmp2 += L"\\temporary_exported.asc";
+ boost::filesystem::remove(tmp2.c_str());
+
+ wfstream f(tmp2, std::ios::out);
+ f << toUTF16(key).c_str();
+ f.close();
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ break;
+ if (params.result == pxNotFound)
+ break;
+ if (params.result == pxSuccess)
+ processed_private_keys++;
+ key.clear();
+ }
+ }
+ if (file.is_open())
+ file.close();
+ wchar_t msg[512];
+ if (processed_private_keys)
+ mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys and some private keys."), processed_keys);
+ else
+ mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys."), processed_keys);
+ MessageBox(nullptr, msg, TranslateT("Keys import result"), MB_OK);
+ return 0;
+}
+
+void SendErrorMessage(MCONTACT hContact)
+{
+ if (!g_plugin.bSendErrorMessages)
+ return;
+
+ uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message");
+ HistoryLog(hContact, "Error message sent", DBEF_SENT);
+ g_plugin.setByte(hContact, "GPGEncryption", enc);
+}
+
+void fix_line_term(std::string &s)
+{
+ if (s.empty())
+ return;
+
+ boost::algorithm::erase_all(s, "\r\r");
+
+ // unified line endings for unix & windows port
+ boost::algorithm::replace_all(s, "\r\n", "\n");
+ boost::algorithm::replace_all(s, "\n", "\r\n");
+}
+
+void strip_line_term(std::wstring &s)
+{
+ if (s.empty())
+ return;
+ boost::algorithm::erase_all(s, L"\r");
+ boost::algorithm::erase_all(s, L"\n");
+}
+
+void strip_line_term(std::string &s)
+{
+ if (s.empty())
+ return;
+ boost::algorithm::erase_all(s, "\r");
+ boost::algorithm::erase_all(s, "\n");
+}
+
+void strip_tags(std::string &str)
+{
+ if (str.empty())
+ return;
+ boost::algorithm::erase_all(str, globals.wszInopentag.c_str());
+ boost::algorithm::erase_all(str, globals.wszInclosetag.c_str());
+ boost::algorithm::erase_all(str, globals.wszOutopentag.c_str());
+ boost::algorithm::erase_all(str, globals.wszOutclosetag.c_str());
+}
+
+
+void ShowEncryptedFileMsgBox()
+{
+ CDlgEncryptedFileMsgBox *d = new CDlgEncryptedFileMsgBox;
+ d->DoModal();
+}
+
+void clean_temp_dir()
+{
+ using namespace boost::filesystem;
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+
+ CMStringW tmp = mir_path;
+ tmp += g_plugin.getMStringW("szHomePath");
+ tmp += L"\\tmp";
+ if (exists(tmp.c_str()) && is_directory(tmp.c_str())) {
+ boost::filesystem::path p(tmp);
+ for (directory_iterator i = directory_iterator(p), end = directory_iterator(); i != end; ++i) {
+ if (boost::filesystem::is_regular_file(i->path())) {
+ if ((i->path().filename().generic_string().length() == 10 && (i->path().filename().generic_string().find(".") == std::string::npos)) ||
+ i->path().extension() == ".sig" || i->path().extension() == ".asc" || i->path().extension() == ".status")
+ boost::filesystem::remove(i->path());
+ }
+ }
+ }
+}
+
+bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path)
+{
+ wstring tmp = gpg_bin_path;
+ if (!tmp.empty()) {
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+ if (!boost::filesystem::exists(tmp)) {
+ MessageBox(nullptr, TranslateT("GPG binary does not exist.\nPlease choose another location"), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ }
+ else {
+ MessageBox(nullptr, TranslateT("Please choose GPG binary location"), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ {
+ bool bad_version = false;
+ g_plugin.setWString("szGpgBinPath", tmp.c_str());
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+ bool _gpg_valid = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = _gpg_valid; //TODO: check this
+ g_plugin.delSetting("szGpgBinPath");
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 != -1) {
+ p1 += mir_strlen("(GnuPG) ");
+ if (params.out[p1] != '1')
+ bad_version = true;
+ }
+ else {
+ bad_version = false;
+ MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ if (bad_version)
+ MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ }
+ tmp = gpg_home_path;
+ if (tmp[tmp.length()] == '\\')
+ tmp.erase(tmp.length());
+ if (tmp.empty()) {
+ MessageBox(nullptr, TranslateT("Please set keyring's home directory"), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ {
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+ uint32_t dwFileAttr = GetFileAttributes(path);
+ if (dwFileAttr != INVALID_FILE_ATTRIBUTES) {
+ dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
+ SetFileAttributes(path, dwFileAttr);
+ }
+ }
+ return true;
+}
+
+void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path)
+{
+ g_plugin.setWString("szGpgBinPath", gpg_bin_path);
+ g_plugin.setWString("szHomePath", gpg_home_path);
+}
+
+bool gpg_use_new_random_key(const char *account_name)
+{
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+ path += L"\\new_key";
+
+ // generating key file
+ wfstream f(path.c_str(), std::ios::out);
+ if (!f.is_open()) {
+ MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+ f << "Key-Type: RSA";
+ f << "\n";
+ f << "Key-Length: 4096";
+ f << "\n";
+ f << "Subkey-Type: RSA";
+ f << "\n";
+ f << "Name-Real: ";
+ f << get_random(6).c_str();
+ f << "\n";
+ f << "Name-Email: ";
+ f << get_random(5).c_str();
+ f << "@";
+ f << get_random(5).c_str();
+ f << ".";
+ f << get_random(3).c_str();
+ f << "\n";
+ f.close();
+
+ {
+ // gpg execution
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"--gen-key");
+ params.addParam(path.c_str());
+ params.bNoOutput = true;
+ if (!gpg_launcher(params, boost::posix_time::minutes(10)))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ boost::filesystem::remove(path.c_str());
+ string out(params.out);
+ int p1 = params.out.Find("key ");
+ if (p1 != -1)
+ path = ptrW(mir_utf8decodeW(params.out.Mid(p1 + 4, 8).c_str()));
+ else
+ path.Empty();
+ }
+
+ if (!path.IsEmpty()) {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(path.c_str());
+ if (!gpg_launcher(params))
+ return false;
+
+ if (params.result == pxNotFound)
+ return false;
+
+ params.out.Remove('\r');
+
+ if (account_name == nullptr) {
+ g_plugin.setString("GPGPubKey", params.out.c_str());
+ g_plugin.setWString("KeyID", path.c_str());
+ }
+ else {
+ CMStringA acc_str = account_name;
+ g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str());
+ g_plugin.setWString(acc_str + "_KeyID", path.c_str());
+ }
+ }
+ return true;
+}
diff --git a/plugins/New_GPG/src/utilities.h b/plugins/New_GPG/src/utilities.h index 1ac119660d..c6390b61a2 100644 --- a/plugins/New_GPG/src/utilities.h +++ b/plugins/New_GPG/src/utilities.h @@ -1,54 +1,54 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef UTILITIES_H -#define UTILITIES_H - -void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc); -wchar_t *GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file = false); -void GetFolderPath(wchar_t *WindowTittle); - -void setSrmmIcon(MCONTACT); - -void send_encrypted_msgs_thread(void*); - -int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data); -bool isContactSecured(MCONTACT hContact); -bool isContactHaveKey(MCONTACT hContact); -bool isGPGKeyExist(); -bool isGPGValid(); - -void ExportGpGKeysFunc(int type); -void ImportKey(MCONTACT hContact, std::wstring new_key); - -void SendErrorMessage(MCONTACT hContact); - -const bool StriStr(const char *str, const char *substr); -string toUTF8(wstring str); -wstring toUTF16(string str); -string get_random(int length); - -void HistoryLog(MCONTACT, const char *msg, uint32_t _time = 0, uint32_t _flags = 0); -void fix_line_term(std::string &s); -void strip_line_term(std::wstring &s); -void strip_line_term(std::string &s); -void strip_tags(std::string &s); -void clean_temp_dir(); -bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path); -void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path); -bool gpg_use_new_random_key(const char *account_name); - -#endif +// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc);
+wchar_t *GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file = false);
+void GetFolderPath(wchar_t *WindowTittle);
+
+void setSrmmIcon(MCONTACT);
+
+void send_encrypted_msgs_thread(void*);
+
+int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data);
+bool isContactSecured(MCONTACT hContact);
+bool isContactHaveKey(MCONTACT hContact);
+bool isGPGKeyExist();
+bool isGPGValid();
+
+void ExportGpGKeysFunc(int type);
+void ImportKey(MCONTACT hContact, std::wstring new_key);
+
+void SendErrorMessage(MCONTACT hContact);
+
+const bool StriStr(const char *str, const char *substr);
+string toUTF8(wstring str);
+wstring toUTF16(string str);
+string get_random(int length);
+
+void HistoryLog(MCONTACT, const char *msg, uint32_t _time = 0, uint32_t _flags = 0);
+void fix_line_term(std::string &s);
+void strip_line_term(std::wstring &s);
+void strip_line_term(std::string &s);
+void strip_tags(std::string &s);
+void clean_temp_dir();
+bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path);
+void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path);
+bool gpg_use_new_random_key(const char *account_name);
+
+#endif
diff --git a/plugins/New_GPG/src/version.h b/plugins/New_GPG/src/version.h index b0abab9a96..7dff493628 100644 --- a/plugins/New_GPG/src/version.h +++ b/plugins/New_GPG/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "New GPG encryption support plugin, based on code from old GPG plugin and SecureIM."
#define __AUTHOR "sss, Miranda NG Team"
#define __AUTHORWEB "https://miranda-ng.org/p/New_GPG"
-#define __COPYRIGHT "© 2010-22 sss, Miranda NG Team"
+#define __COPYRIGHT "© 2010-23 sss, Miranda NG Team"
diff --git a/plugins/NoHistory/src/stdafx.cxx b/plugins/NoHistory/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/NoHistory/src/stdafx.cxx +++ b/plugins/NoHistory/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/NoHistory/src/version.h b/plugins/NoHistory/src/version.h index c3911a546c..bb3b8226d9 100644 --- a/plugins/NoHistory/src/version.h +++ b/plugins/NoHistory/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Prevent Miranda from storing any history."
#define __AUTHOR "Scott Ellis, NightFox"
#define __AUTHORWEB "https://miranda-ng.org/p/NoHistory"
-#define __COPYRIGHT "© 2005 Scott Ellis, 2010-17 NightFox, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2005 Scott Ellis, 2010-17 NightFox, 2017-23 Miranda NG team"
diff --git a/plugins/NotesAndReminders/src/stdafx.cxx b/plugins/NotesAndReminders/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/plugins/NotesAndReminders/src/stdafx.cxx +++ b/plugins/NotesAndReminders/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/plugins/NotifyAnything/src/stdafx.cxx b/plugins/NotifyAnything/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/NotifyAnything/src/stdafx.cxx +++ b/plugins/NotifyAnything/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Nudge/src/stdafx.cxx b/plugins/Nudge/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Nudge/src/stdafx.cxx +++ b/plugins/Nudge/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/OpenFolder/src/stdafx.cxx b/plugins/OpenFolder/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/OpenFolder/src/stdafx.cxx +++ b/plugins/OpenFolder/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/PackUpdater/Src/Events.cpp b/plugins/PackUpdater/Src/Events.cpp index 85e5a03578..0437123043 100644 --- a/plugins/PackUpdater/Src/Events.cpp +++ b/plugins/PackUpdater/Src/Events.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Notifications.cpp b/plugins/PackUpdater/Src/Notifications.cpp index ba2b4862b1..afd26da397 100644 --- a/plugins/PackUpdater/Src/Notifications.cpp +++ b/plugins/PackUpdater/Src/Notifications.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Notifications.h b/plugins/PackUpdater/Src/Notifications.h index 29c9eba2a8..6fc184f932 100644 --- a/plugins/PackUpdater/Src/Notifications.h +++ b/plugins/PackUpdater/Src/Notifications.h @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Options.cpp b/plugins/PackUpdater/Src/Options.cpp index c6206cbe8c..16be7b7ff8 100644 --- a/plugins/PackUpdater/Src/Options.cpp +++ b/plugins/PackUpdater/Src/Options.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/PackUpdater.cpp b/plugins/PackUpdater/Src/PackUpdater.cpp index eac404ccc8..9b66b38c81 100644 --- a/plugins/PackUpdater/Src/PackUpdater.cpp +++ b/plugins/PackUpdater/Src/PackUpdater.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Utils.cpp b/plugins/PackUpdater/Src/Utils.cpp index e6adf16e9e..57a1c7db82 100644 --- a/plugins/PackUpdater/Src/Utils.cpp +++ b/plugins/PackUpdater/Src/Utils.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/stdafx.cxx b/plugins/PackUpdater/Src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/PackUpdater/Src/stdafx.cxx +++ b/plugins/PackUpdater/Src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/PackUpdater/Src/stdafx.h b/plugins/PackUpdater/Src/stdafx.h index 4e78e8ac6c..5206917ca5 100644 --- a/plugins/PackUpdater/Src/stdafx.h +++ b/plugins/PackUpdater/Src/stdafx.h @@ -1,5 +1,5 @@ /*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/version.h b/plugins/PackUpdater/Src/version.h index 3a9c0f1eb3..e809fb1677 100644 --- a/plugins/PackUpdater/Src/version.h +++ b/plugins/PackUpdater/Src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Simple updater for Miranda NG premodified packs."
#define __AUTHOR "Mataes, ZERO_BiT"
#define __AUTHORWEB "https://miranda-ng.org/p/PackUpdater"
-#define __COPYRIGHT "© 2011-22 Mataes, 2007 ZERO_BiT"
+#define __COPYRIGHT "© 2011-23 Mataes, 2007 ZERO_BiT"
diff --git a/plugins/PasteIt/src/stdafx.cxx b/plugins/PasteIt/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/PasteIt/src/stdafx.cxx +++ b/plugins/PasteIt/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Ping/src/stdafx.cxx b/plugins/Ping/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Ping/src/stdafx.cxx +++ b/plugins/Ping/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/PluginUpdater/pu_stub/src/stdafx.cxx b/plugins/PluginUpdater/pu_stub/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/plugins/PluginUpdater/pu_stub/src/stdafx.cxx +++ b/plugins/PluginUpdater/pu_stub/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/plugins/PluginUpdater/src/stdafx.cxx b/plugins/PluginUpdater/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/PluginUpdater/src/stdafx.cxx +++ b/plugins/PluginUpdater/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/PluginUpdater/src/version.h b/plugins/PluginUpdater/src/version.h index 3697cde49a..acfdd6cb78 100644 --- a/plugins/PluginUpdater/src/version.h +++ b/plugins/PluginUpdater/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Installs and updates plugins and other Miranda NG components."
#define __AUTHOR "Mataes, George Hazan"
#define __AUTHORWEB "https://miranda-ng.org/p/PluginUpdater"
-#define __COPYRIGHT "© 2012-22 Mataes, George Hazan"
+#define __COPYRIGHT "© 2012-23 Mataes, George Hazan"
diff --git a/plugins/Popup/src/stdafx.cxx b/plugins/Popup/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Popup/src/stdafx.cxx +++ b/plugins/Popup/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ProfileManager/src/stdafx.cxx b/plugins/ProfileManager/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ProfileManager/src/stdafx.cxx +++ b/plugins/ProfileManager/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ProxySwitch/src/stdafx.cxx b/plugins/ProxySwitch/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/plugins/ProxySwitch/src/stdafx.cxx +++ b/plugins/ProxySwitch/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/plugins/ProxySwitch/src/version.h b/plugins/ProxySwitch/src/version.h index 49deea3476..de274fc508 100644 --- a/plugins/ProxySwitch/src/version.h +++ b/plugins/ProxySwitch/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Watches IP address changes, displays popups, and adjusts the proxy settings of Miranda, Internet Explorer and Firefox."
#define __AUTHOR "Petr Smejkal"
#define __AUTHORWEB "https://miranda-ng.org/p/ProxySwitch"
-#define __COPYRIGHT "© 2005 Petr Smejkal, 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2005 Petr Smejkal, 2018-23 Miranda NG team"
diff --git a/plugins/QuickContacts/src/dialog.cpp b/plugins/QuickContacts/src/dialog.cpp index 8d2bb44d2e..f52297f4cb 100644 --- a/plugins/QuickContacts/src/dialog.cpp +++ b/plugins/QuickContacts/src/dialog.cpp @@ -1,853 +1,853 @@ -/* -Copyright (C) 2012-22 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 IDC_ICO 12344 - -#define IDC_ENTER 2000 // Pseudo control to handle enter in the main window - -HIMAGELIST hIml; - -long main_dialog_open = 0; -HWND hwndMain = nullptr; - -// array where the contacts are put into -struct c_struct -{ - wchar_t szname[120]; - wchar_t szgroup[50]; - MCONTACT hcontact; - wchar_t proto[20]; - - c_struct() - { - szname[0] = 0; - szgroup[0] = 0; - hcontact = 0; - proto[0] = 0; - } -}; - -LIST<c_struct> contacts(200); -long max_proto_width; - - -// Get the name the contact has in list -// This was not made to be called by more than one thread! -wchar_t tmp_list_name[120]; - -wchar_t *GetListName(c_struct *cs) -{ - if (opts.group_append && cs->szgroup[0] != '\0') { - mir_snwprintf(tmp_list_name, L"%s (%s)", cs->szname, cs->szgroup); - return tmp_list_name; - } - else { - return cs->szname; - } -} - - -int lstreq(wchar_t *a, wchar_t *b, size_t len = -1) -{ - a = CharLower(wcsdup(a)); - b = CharLower(wcsdup(b)); - int ret; - if (len > 0) - ret = wcsncmp(a, b, len); - else - ret = mir_wstrcmp(a, b); - free(a); - free(b); - return ret; -} - - -// simple sorting function to have -// the contact array in alphabetical order -void SortArray(void) -{ - int loop, doop; - c_struct *cs_temp; - - SortedList *sl = (SortedList *)&contacts; - for (loop = 0; loop < contacts.getCount(); loop++) { - for (doop = loop + 1; doop < contacts.getCount(); doop++) { - int cmp = lstreq(contacts[loop]->szname, contacts[doop]->szname); - if (cmp > 0) { - cs_temp = contacts[loop]; - sl->items[loop] = contacts[doop]; - sl->items[doop] = cs_temp; - } - else if (cmp == 0) { - if (lstreq(contacts[loop]->proto, contacts[doop]->proto) > 0) { - cs_temp = contacts[loop]; - sl->items[loop] = contacts[doop]; - sl->items[doop] = cs_temp; - } - } - - } - } -} - - -int GetStatus(MCONTACT hContact, char *proto = nullptr) -{ - if (proto == nullptr) - proto = Proto_GetBaseAccountName(hContact); - - if (proto == nullptr) - return ID_STATUS_OFFLINE; - - return db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE); -} - - -void FreeContacts() -{ - for (auto &it : contacts) - delete it; - contacts.destroy(); -} - - -void LoadContacts(HWND hwndDlg, BOOL show_all) -{ - BOOL metacontactsEnabled = db_mc_isEnabled(); - - // Read last-sent-to contact from db and set handle as window-userdata - HANDLE hlastsent = (HANDLE)g_plugin.getDword("LastSentTo", -1); - SetWindowLongPtr(hwndMain, GWLP_USERDATA, (LONG_PTR)hlastsent); - - // enumerate all contacts and write them to the array - // item data of listbox-strings is the array position - FreeContacts(); - - for (auto &hContact : Contacts()) { - char *pszProto = Proto_GetBaseAccountName(hContact); - if (pszProto == nullptr) - continue; - - // Get meta - MCONTACT hMeta = NULL; - if (metacontactsEnabled) { - if ((!show_all && opts.hide_subcontacts) || opts.group_append) - hMeta = db_mc_getMeta(hContact); - } - else if (!mir_strcmp(META_PROTO, pszProto)) - continue; - - if (!show_all) { - // Check if is offline and have to show - if (GetStatus(hContact, pszProto) <= ID_STATUS_OFFLINE) { - // See if has to show - char setting[128]; - mir_snprintf(setting, "ShowOffline%s", pszProto); - - if (!g_plugin.getByte(setting, FALSE)) - continue; - - // Check if proto offline - else if (opts.hide_from_offline_proto && Proto_GetStatus(pszProto) <= ID_STATUS_OFFLINE) - continue; - - } - - // Check if is subcontact - if (opts.hide_subcontacts && hMeta != NULL) { - if (!opts.keep_subcontacts_from_offline) - continue; - - if (GetStatus(hMeta, META_PROTO) > ID_STATUS_OFFLINE) - continue; - - char setting[128]; - mir_snprintf(setting, "ShowOffline%s", META_PROTO); - if (g_plugin.getByte(setting, FALSE)) - continue; - } - } - - // Add to list - - // Get group - c_struct *contact = new c_struct(); - - if (opts.group_append) { - ptrW wszGroup(Clist_GetGroup(hMeta == NULL ? hContact : hMeta)); - if (wszGroup) - wcsncpy_s(contact->szgroup, wszGroup, _TRUNCATE); - } - - // Make contact name - wchar_t *tmp = (wchar_t *)Clist_GetContactDisplayName(hContact); - mir_wstrncpy(contact->szname, tmp, _countof(contact->szname)); - - PROTOACCOUNT *acc = Proto_GetAccount(pszProto); - if (acc != nullptr) - mir_wstrncpy(contact->proto, acc->tszAccountName, _countof(contact->proto)); - - contact->hcontact = hContact; - contacts.insert(contact); - } - - SortArray(); - - SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_RESETCONTENT, 0, 0); - for (int loop = 0; loop < contacts.getCount(); loop++) - SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_ADDSTRING, 0, (LPARAM)GetListName(contacts[loop])), loop); -} - - -// Enable buttons for the selected contact -void EnableButtons(HWND hwndDlg, MCONTACT hContact) -{ - if (hContact == NULL) { - EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), FALSE); - - SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, 0, 0); - } - else { - // Is a meta? - MCONTACT hSub = db_mc_getMostOnline(hContact); - if (hSub != NULL) - hContact = hSub; - - // Get caps - INT_PTR caps = 0; - - char *pszProto = Proto_GetBaseAccountName(hContact); - if (pszProto != nullptr) - caps = CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0); - - EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), caps & PF1_IMSEND ? TRUE : FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), caps & PF1_FILESEND ? TRUE : FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), TRUE); - EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), TRUE); - EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), TRUE); - - HICON ico = ImageList_GetIcon(hIml, Clist_GetContactIcon(hContact), ILD_IMAGE); - SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, (WPARAM)ico, 0); - } -} - - -// check if the char(s) entered appears in a contacts name -int CheckText(HWND hdlg, wchar_t *sztext, BOOL only_enable = FALSE) -{ - EnableButtons(hwndMain, NULL); - - if (sztext == nullptr || sztext[0] == '\0') - return 0; - - size_t len = mir_wstrlen(sztext); - - if (only_enable) { - for (auto &it : contacts) { - if (lstreq(sztext, it->szname) == 0 || lstreq(sztext, GetListName(it)) == 0) { - EnableButtons(hwndMain, it->hcontact); - return 0; - } - } - } - else { - for (auto &it : contacts) { - if (lstreq(sztext, GetListName(it), len) == 0) { - SetWindowText(hdlg, GetListName(it)); - SendMessage(hdlg, EM_SETSEL, (WPARAM)len, (LPARAM)-1); - EnableButtons(hwndMain, it->hcontact); - return 0; - } - } - } - - EnableButtons(hwndMain, NULL); - return 0; -} - -MCONTACT GetSelectedContact(HWND hwndDlg) -{ - // First try selection - int sel = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0); - - if (sel != CB_ERR) { - int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETITEMDATA, sel, 0); - if (pos != CB_ERR) - return contacts[pos]->hcontact; - } - - // Now try the name - wchar_t cname[120] = L""; - - GetDlgItemText(hwndDlg, IDC_USERNAME, cname, _countof(cname)); - - for (auto &it : contacts) - if (!mir_wstrcmpi(cname, GetListName(it))) - return it->hcontact; - - return NULL; -} - -// get array position from handle -int GetItemPos(MCONTACT hcontact) -{ - for (auto &it : contacts) - if (hcontact == it->hcontact) - return contacts.indexOf(&it); - - return -1; -} - - -// callback function for edit-box of the listbox -// without this the autofill function isn't possible -// this was done like ie does it..as far as spy++ could tell ;) -LRESULT CALLBACK EditProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) -{ - switch (msg) { - case WM_CHAR: - { - if (wparam < 32 && wparam != VK_BACK) - break; - - wchar_t sztext[120] = L""; - uint32_t start; - uint32_t end; - - int ret = SendMessage(hdlg, EM_GETSEL, (WPARAM)&start, (LPARAM)&end); - - GetWindowText(hdlg, sztext, _countof(sztext)); - - BOOL at_end = (mir_wstrlen(sztext) == (int)end); - - if (ret != -1) { - if (wparam == VK_BACK) { - if (start > 0) - SendMessage(hdlg, EM_SETSEL, (WPARAM)start - 1, (LPARAM)end); - - sztext[0] = 0; - } - else { - sztext[0] = wparam; - sztext[1] = 0; - } - - SendMessage(hdlg, EM_REPLACESEL, 0, (LPARAM)sztext); - GetWindowText(hdlg, sztext, _countof(sztext)); - } - - CheckText(hdlg, sztext, !at_end); - - return 1; - } - case WM_KEYUP: - { - wchar_t sztext[120] = L""; - - if (wparam == VK_RETURN) { - switch (SendMessage(GetParent(hdlg), CB_GETDROPPEDSTATE, 0, 0)) { - case FALSE: - SendMessage(GetParent(GetParent(hdlg)), WM_COMMAND, MAKEWPARAM(IDC_ENTER, STN_CLICKED), 0); - break; - - case TRUE: - SendMessage(GetParent(hdlg), CB_SHOWDROPDOWN, FALSE, 0); - break; - } - } - else if (wparam == VK_DELETE) { - GetWindowText(hdlg, sztext, _countof(sztext)); - CheckText(hdlg, sztext, TRUE); - } - - return 0; - } - - case WM_GETDLGCODE: - return DLGC_WANTCHARS | DLGC_WANTARROWS; - } - - return mir_callNextSubclass(hdlg, EditProc, msg, wparam, lparam); -} - -HACCEL hAcct; -HHOOK hHook; - -// This function filters the message queue and translates -// the keyboard accelerators -LRESULT CALLBACK HookProc(int code, WPARAM, LPARAM lparam) -{ - if (code != MSGF_DIALOGBOX) - return 0; - - MSG *msg = (MSG*)lparam; - - int action = Hotkey_Check(msg, "Quick Contacts"); - if (action != 0) { - SendMessage(hwndMain, WM_COMMAND, action, 0); - return 1; - } - - if (msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE) { - switch (SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_GETDROPPEDSTATE, 0, 0)) { - case FALSE: - SendMessage(hwndMain, WM_CLOSE, 0, 0); - break; - - case TRUE: - SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_SHOWDROPDOWN, FALSE, 0); - break; - } - } - - return 0; -} - -BOOL ScreenToClient(HWND hWnd, LPRECT lpRect) -{ - BOOL ret; - - POINT pt; - - pt.x = lpRect->left; - pt.y = lpRect->top; - - ret = ScreenToClient(hWnd, &pt); - - if (!ret) return ret; - - lpRect->left = pt.x; - lpRect->top = pt.y; - - - pt.x = lpRect->right; - pt.y = lpRect->bottom; - - ret = ScreenToClient(hWnd, &pt); - - lpRect->right = pt.x; - lpRect->bottom = pt.y; - - return ret; -} - - -BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint) -{ - return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint); -} - - -static void FillButton(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key, HICON icon) -{ - wchar_t tmp[256]; - wchar_t *full = tmp; - - if (key == nullptr) - full = TranslateW(name); - else - mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key); - - SendDlgItemMessage(hwndDlg, dlgItem, BUTTONSETASFLATBTN, 0, 0); - SendDlgItemMessage(hwndDlg, dlgItem, BUTTONADDTOOLTIP, (LPARAM)full, BATF_UNICODE); - SendDlgItemMessage(hwndDlg, dlgItem, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon); -} - - -static void FillCheckbox(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key) -{ - wchar_t tmp[256]; - wchar_t *full = tmp; - - if (key == nullptr) - full = TranslateW(name); - else - mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key); - - SetDlgItemText(hwndDlg, dlgItem, full); -} - - -static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - RECT rc; - GetWindowRect(GetDlgItem(hwndDlg, IDC_USERNAME), &rc); - ScreenToClient(hwndDlg, &rc); - - CreateWindow(L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE, - rc.left - 20, rc.top + (rc.bottom - rc.top - 16) / 2, 16, 16, hwndDlg, (HMENU)IDC_ICO, - g_plugin.getInst(), nullptr); - - hHook = SetWindowsHookEx(WH_MSGFILTER, HookProc, nullptr, GetCurrentThreadId()); - - // Combo - SendDlgItemMessage(hwndDlg, IDC_USERNAME, EM_LIMITTEXT, (WPARAM)119, 0); - mir_subclassWindow(GetWindow(GetDlgItem(hwndDlg, IDC_USERNAME), GW_CHILD), EditProc); - - // Buttons - FillCheckbox(hwndDlg, IDC_SHOW_ALL_CONTACTS, LPGENW("Show all contacts"), NULL); - FillButton(hwndDlg, IDC_MESSAGE, LPGENW("Send message"), nullptr, Skin_LoadIcon(SKINICON_EVENT_MESSAGE)); - FillButton(hwndDlg, IDC_FILE, LPGENW("Send file"), NULL, Skin_LoadIcon(SKINICON_EVENT_FILE)); - FillButton(hwndDlg, IDC_USERINFO, LPGENW("Open user info"), NULL, Skin_LoadIcon(SKINICON_OTHER_USERDETAILS)); - FillButton(hwndDlg, IDC_HISTORY, LPGENW("Open history"), NULL, Skin_LoadIcon(SKINICON_OTHER_HISTORY)); - FillButton(hwndDlg, IDC_MENU, LPGENW("Open contact menu"), NULL, Skin_LoadIcon(SKINICON_OTHER_DOWNARROW)); - - SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETEXTENDEDUI, TRUE, 0); - - Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, MODULENAME, "window"); - - LoadContacts(hwndDlg, FALSE); - - EnableButtons(hwndDlg, NULL); - if (g_plugin.getByte("EnableLastSentTo", 0)) { - int pos = GetItemPos(g_plugin.getDword("LastSentTo", -1)); - if (pos != -1) { - SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETCURSEL, (WPARAM)pos, 0); - EnableButtons(hwndDlg, contacts[pos]->hcontact); - } - } - - SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); - } - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_USERNAME: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0); - EnableButtons(hwndDlg, pos < contacts.getCount() ? contacts[pos]->hcontact : NULL); - } - break; - - case IDC_ENTER: - { - MCONTACT hContact = GetSelectedContact(hwndDlg); - if (hContact == NULL) - break; - - Clist_ContactDoubleClicked(hContact); - - g_plugin.setDword("LastSentTo", hContact); - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - case IDC_MESSAGE: - { - MCONTACT hContact = GetSelectedContact(hwndDlg); - if (hContact == NULL) { - SetDlgItemText(hwndDlg, IDC_USERNAME, L""); - SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); - break; - } - - // Is button enabled? - if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MESSAGE))) - break; - - CallService(MS_MSG_SENDMESSAGEW, hContact, 0); - - g_plugin.setDword("LastSentTo", hContact); - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - break; - } - - case HOTKEY_FILE: - case IDC_FILE: - { - MCONTACT hContact = GetSelectedContact(hwndDlg); - if (hContact == NULL) { - SetDlgItemText(hwndDlg, IDC_USERNAME, L""); - SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); - break; - } - - // Is button enabled? - if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FILE))) - break; - - CallService(MS_FILE_SENDFILE, hContact, 0); - - g_plugin.setDword("LastSentTo", hContact); - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - - case HOTKEY_INFO: - case IDC_USERINFO: - { - MCONTACT hContact = GetSelectedContact(hwndDlg); - if (hContact == NULL) { - SetDlgItemText(hwndDlg, IDC_USERNAME, L""); - SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); - break; - } - - // Is button enabled? - if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_USERINFO))) - break; - - CallService(MS_USERINFO_SHOWDIALOG, hContact, 0); - - g_plugin.setDword("LastSentTo", hContact); - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - - case HOTKEY_HISTORY: - case IDC_HISTORY: - { - MCONTACT hContact = GetSelectedContact(hwndDlg); - if (hContact == NULL) { - SetDlgItemText(hwndDlg, IDC_USERNAME, L""); - SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); - break; - } - - // Is button enabled? - if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_HISTORY))) - break; - - CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0); - - g_plugin.setDword("LastSentTo", hContact); - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - - case HOTKEY_MENU: - case IDC_MENU: - { - MCONTACT hContact = GetSelectedContact(hwndDlg); - if (hContact == NULL) { - SetDlgItemText(hwndDlg, IDC_USERNAME, L""); - SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); - break; - } - - // Is button enabled? - if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MENU))) - break; - - RECT rc; - GetWindowRect(GetDlgItem(hwndDlg, IDC_MENU), &rc); - HMENU hMenu = Menu_BuildContactMenu(hContact); - int ret = TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, nullptr); - DestroyMenu(hMenu); - - if (ret) { - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - Clist_MenuProcessCommand(LOWORD(ret), MPCF_CONTACTMENU, hContact); - } - - g_plugin.setDword("LastSentTo", (uint32_t)hContact); - } - break; - - case HOTKEY_ALL_CONTACTS: - case IDC_SHOW_ALL_CONTACTS: - { - // Get old text - HWND hEdit = GetWindow(GetWindow(hwndDlg, GW_CHILD), GW_CHILD); - wchar_t sztext[120] = L""; - - if (SendMessage(hEdit, EM_GETSEL, 0, 0) != -1) - SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)L""); - - GetWindowText(hEdit, sztext, _countof(sztext)); - - // Fill combo - BOOL all = IsDlgButtonChecked(hwndDlg, IDC_SHOW_ALL_CONTACTS); - - if (LOWORD(wParam) == HOTKEY_ALL_CONTACTS) { - // Toggle checkbox - all = !all; - CheckDlgButton(hwndDlg, IDC_SHOW_ALL_CONTACTS, all ? BST_CHECKED : BST_UNCHECKED); - } - - LoadContacts(hwndDlg, all); - - // Return selection - CheckText(hEdit, sztext); - } - } - break; - - case WM_CLOSE: - Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "window"); - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - UnhookWindowsHookEx(hHook); - hwndMain = nullptr; - FreeContacts(); - InterlockedExchange(&main_dialog_open, 0); - break; - - case WM_DRAWITEM: - { - // add icons and protocol to listbox - LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; - - // Handle contact menu - if (lpdis->CtlID != IDC_USERNAME) { - if (lpdis->CtlType == ODT_MENU) - return Menu_DrawItem(lParam); - break; - } - - // Handle combo - if (lpdis->itemID == -1) - break; - - TEXTMETRIC tm; - int icon_width = 0, icon_height = 0; - RECT rc; - - GetTextMetrics(lpdis->hDC, &tm); - ImageList_GetIconSize(hIml, &icon_width, &icon_height); - - COLORREF clrfore = SetTextColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); - COLORREF clrback = SetBkColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - - FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - - // Draw icon - rc.left = lpdis->rcItem.left + 5; - rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - icon_height) / 2; - ImageList_Draw(hIml, Clist_GetContactIcon(contacts[lpdis->itemData]->hcontact), lpdis->hDC, rc.left, rc.top, ILD_NORMAL); - - // Make rect for text - rc.left += icon_width + 5; - rc.right = lpdis->rcItem.right - 1; - rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; - rc.bottom = rc.top + tm.tmHeight; - - // Draw Protocol - if (opts.num_protos > 1) { - if (max_proto_width == 0) { - // Has to be done, else the DC isnt the right one - // Dont ask me why - for (auto &it : contacts) { - RECT rcc = { 0, 0, 0x7FFF, 0x7FFF }; - DrawText(lpdis->hDC, it->proto, -1, &rcc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT); - max_proto_width = max(max_proto_width, rcc.right - rcc.left); - } - - // Fix max_proto_width - if (opts.group_append && opts.group_column) - max_proto_width = min(max_proto_width, (rc.right - rc.left) / 5); - else if (opts.group_append) - max_proto_width = min(max_proto_width, (rc.right - rc.left) / 4); - else - max_proto_width = min(max_proto_width, (rc.right - rc.left) / 3); - } - - RECT rc_tmp = rc; - rc_tmp.left = rc_tmp.right - max_proto_width; - DrawText(lpdis->hDC, contacts[lpdis->itemData]->proto, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE); - rc.right = rc_tmp.left - 5; - } - - // Draw group - if (opts.group_append && opts.group_column) { - RECT rc_tmp = rc; - - if (opts.group_column_left) { - rc_tmp.right = rc_tmp.left + (rc.right - rc.left) / 3; - rc.left = rc_tmp.right + 5; - } - else { - rc_tmp.left = rc_tmp.right - (rc.right - rc.left) / 3; - rc.right = rc_tmp.left - 5; - } - - DrawText(lpdis->hDC, contacts[lpdis->itemData]->szgroup, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE); - } - - // Draw text - wchar_t *name; - if (opts.group_append && !opts.group_column) - name = GetListName(contacts[lpdis->itemData]); - else - name = contacts[lpdis->itemData]->szname; - - DrawText(lpdis->hDC, name, -1, &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE); - - // Restore old colors - SetTextColor(lpdis->hDC, clrfore); - SetBkColor(lpdis->hDC, clrback); - } - return TRUE; - - case WM_MEASUREITEM: - { - LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam; - - // Handle contact menu - if (lpmis->CtlID != IDC_USERNAME) { - if (lpmis->CtlType == ODT_MENU) - return Menu_MeasureItem(lParam); - break; - } - - // Handle combo - - TEXTMETRIC tm; - int icon_width = 0, icon_height = 0; - - GetTextMetrics(GetDC(hwndDlg), &tm); - ImageList_GetIconSize(hIml, &icon_width, &icon_height); - - lpmis->itemHeight = max(icon_height, tm.tmHeight); - - return TRUE; - } - } - - return FALSE; -} - -// Show the main dialog -INT_PTR ShowDialog(WPARAM, LPARAM) -{ - // Get the icons for the listbox - hIml = Clist_GetImageList(); - - if (!main_dialog_open) { - InterlockedExchange(&main_dialog_open, 1); - - hwndMain = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_MAIN), nullptr, MainDlgProc); - } - - // Show it - SetForegroundWindow(hwndMain); - SetFocus(hwndMain); - ShowWindow(hwndMain, SW_SHOW); - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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 IDC_ICO 12344
+
+#define IDC_ENTER 2000 // Pseudo control to handle enter in the main window
+
+HIMAGELIST hIml;
+
+long main_dialog_open = 0;
+HWND hwndMain = nullptr;
+
+// array where the contacts are put into
+struct c_struct
+{
+ wchar_t szname[120];
+ wchar_t szgroup[50];
+ MCONTACT hcontact;
+ wchar_t proto[20];
+
+ c_struct()
+ {
+ szname[0] = 0;
+ szgroup[0] = 0;
+ hcontact = 0;
+ proto[0] = 0;
+ }
+};
+
+LIST<c_struct> contacts(200);
+long max_proto_width;
+
+
+// Get the name the contact has in list
+// This was not made to be called by more than one thread!
+wchar_t tmp_list_name[120];
+
+wchar_t *GetListName(c_struct *cs)
+{
+ if (opts.group_append && cs->szgroup[0] != '\0') {
+ mir_snwprintf(tmp_list_name, L"%s (%s)", cs->szname, cs->szgroup);
+ return tmp_list_name;
+ }
+ else {
+ return cs->szname;
+ }
+}
+
+
+int lstreq(wchar_t *a, wchar_t *b, size_t len = -1)
+{
+ a = CharLower(wcsdup(a));
+ b = CharLower(wcsdup(b));
+ int ret;
+ if (len > 0)
+ ret = wcsncmp(a, b, len);
+ else
+ ret = mir_wstrcmp(a, b);
+ free(a);
+ free(b);
+ return ret;
+}
+
+
+// simple sorting function to have
+// the contact array in alphabetical order
+void SortArray(void)
+{
+ int loop, doop;
+ c_struct *cs_temp;
+
+ SortedList *sl = (SortedList *)&contacts;
+ for (loop = 0; loop < contacts.getCount(); loop++) {
+ for (doop = loop + 1; doop < contacts.getCount(); doop++) {
+ int cmp = lstreq(contacts[loop]->szname, contacts[doop]->szname);
+ if (cmp > 0) {
+ cs_temp = contacts[loop];
+ sl->items[loop] = contacts[doop];
+ sl->items[doop] = cs_temp;
+ }
+ else if (cmp == 0) {
+ if (lstreq(contacts[loop]->proto, contacts[doop]->proto) > 0) {
+ cs_temp = contacts[loop];
+ sl->items[loop] = contacts[doop];
+ sl->items[doop] = cs_temp;
+ }
+ }
+
+ }
+ }
+}
+
+
+int GetStatus(MCONTACT hContact, char *proto = nullptr)
+{
+ if (proto == nullptr)
+ proto = Proto_GetBaseAccountName(hContact);
+
+ if (proto == nullptr)
+ return ID_STATUS_OFFLINE;
+
+ return db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE);
+}
+
+
+void FreeContacts()
+{
+ for (auto &it : contacts)
+ delete it;
+ contacts.destroy();
+}
+
+
+void LoadContacts(HWND hwndDlg, BOOL show_all)
+{
+ BOOL metacontactsEnabled = db_mc_isEnabled();
+
+ // Read last-sent-to contact from db and set handle as window-userdata
+ HANDLE hlastsent = (HANDLE)g_plugin.getDword("LastSentTo", -1);
+ SetWindowLongPtr(hwndMain, GWLP_USERDATA, (LONG_PTR)hlastsent);
+
+ // enumerate all contacts and write them to the array
+ // item data of listbox-strings is the array position
+ FreeContacts();
+
+ for (auto &hContact : Contacts()) {
+ char *pszProto = Proto_GetBaseAccountName(hContact);
+ if (pszProto == nullptr)
+ continue;
+
+ // Get meta
+ MCONTACT hMeta = NULL;
+ if (metacontactsEnabled) {
+ if ((!show_all && opts.hide_subcontacts) || opts.group_append)
+ hMeta = db_mc_getMeta(hContact);
+ }
+ else if (!mir_strcmp(META_PROTO, pszProto))
+ continue;
+
+ if (!show_all) {
+ // Check if is offline and have to show
+ if (GetStatus(hContact, pszProto) <= ID_STATUS_OFFLINE) {
+ // See if has to show
+ char setting[128];
+ mir_snprintf(setting, "ShowOffline%s", pszProto);
+
+ if (!g_plugin.getByte(setting, FALSE))
+ continue;
+
+ // Check if proto offline
+ else if (opts.hide_from_offline_proto && Proto_GetStatus(pszProto) <= ID_STATUS_OFFLINE)
+ continue;
+
+ }
+
+ // Check if is subcontact
+ if (opts.hide_subcontacts && hMeta != NULL) {
+ if (!opts.keep_subcontacts_from_offline)
+ continue;
+
+ if (GetStatus(hMeta, META_PROTO) > ID_STATUS_OFFLINE)
+ continue;
+
+ char setting[128];
+ mir_snprintf(setting, "ShowOffline%s", META_PROTO);
+ if (g_plugin.getByte(setting, FALSE))
+ continue;
+ }
+ }
+
+ // Add to list
+
+ // Get group
+ c_struct *contact = new c_struct();
+
+ if (opts.group_append) {
+ ptrW wszGroup(Clist_GetGroup(hMeta == NULL ? hContact : hMeta));
+ if (wszGroup)
+ wcsncpy_s(contact->szgroup, wszGroup, _TRUNCATE);
+ }
+
+ // Make contact name
+ wchar_t *tmp = (wchar_t *)Clist_GetContactDisplayName(hContact);
+ mir_wstrncpy(contact->szname, tmp, _countof(contact->szname));
+
+ PROTOACCOUNT *acc = Proto_GetAccount(pszProto);
+ if (acc != nullptr)
+ mir_wstrncpy(contact->proto, acc->tszAccountName, _countof(contact->proto));
+
+ contact->hcontact = hContact;
+ contacts.insert(contact);
+ }
+
+ SortArray();
+
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_RESETCONTENT, 0, 0);
+ for (int loop = 0; loop < contacts.getCount(); loop++)
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_ADDSTRING, 0, (LPARAM)GetListName(contacts[loop])), loop);
+}
+
+
+// Enable buttons for the selected contact
+void EnableButtons(HWND hwndDlg, MCONTACT hContact)
+{
+ if (hContact == NULL) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), FALSE);
+
+ SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, 0, 0);
+ }
+ else {
+ // Is a meta?
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if (hSub != NULL)
+ hContact = hSub;
+
+ // Get caps
+ INT_PTR caps = 0;
+
+ char *pszProto = Proto_GetBaseAccountName(hContact);
+ if (pszProto != nullptr)
+ caps = CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), caps & PF1_IMSEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), caps & PF1_FILESEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), TRUE);
+
+ HICON ico = ImageList_GetIcon(hIml, Clist_GetContactIcon(hContact), ILD_IMAGE);
+ SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, (WPARAM)ico, 0);
+ }
+}
+
+
+// check if the char(s) entered appears in a contacts name
+int CheckText(HWND hdlg, wchar_t *sztext, BOOL only_enable = FALSE)
+{
+ EnableButtons(hwndMain, NULL);
+
+ if (sztext == nullptr || sztext[0] == '\0')
+ return 0;
+
+ size_t len = mir_wstrlen(sztext);
+
+ if (only_enable) {
+ for (auto &it : contacts) {
+ if (lstreq(sztext, it->szname) == 0 || lstreq(sztext, GetListName(it)) == 0) {
+ EnableButtons(hwndMain, it->hcontact);
+ return 0;
+ }
+ }
+ }
+ else {
+ for (auto &it : contacts) {
+ if (lstreq(sztext, GetListName(it), len) == 0) {
+ SetWindowText(hdlg, GetListName(it));
+ SendMessage(hdlg, EM_SETSEL, (WPARAM)len, (LPARAM)-1);
+ EnableButtons(hwndMain, it->hcontact);
+ return 0;
+ }
+ }
+ }
+
+ EnableButtons(hwndMain, NULL);
+ return 0;
+}
+
+MCONTACT GetSelectedContact(HWND hwndDlg)
+{
+ // First try selection
+ int sel = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
+
+ if (sel != CB_ERR) {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETITEMDATA, sel, 0);
+ if (pos != CB_ERR)
+ return contacts[pos]->hcontact;
+ }
+
+ // Now try the name
+ wchar_t cname[120] = L"";
+
+ GetDlgItemText(hwndDlg, IDC_USERNAME, cname, _countof(cname));
+
+ for (auto &it : contacts)
+ if (!mir_wstrcmpi(cname, GetListName(it)))
+ return it->hcontact;
+
+ return NULL;
+}
+
+// get array position from handle
+int GetItemPos(MCONTACT hcontact)
+{
+ for (auto &it : contacts)
+ if (hcontact == it->hcontact)
+ return contacts.indexOf(&it);
+
+ return -1;
+}
+
+
+// callback function for edit-box of the listbox
+// without this the autofill function isn't possible
+// this was done like ie does it..as far as spy++ could tell ;)
+LRESULT CALLBACK EditProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg) {
+ case WM_CHAR:
+ {
+ if (wparam < 32 && wparam != VK_BACK)
+ break;
+
+ wchar_t sztext[120] = L"";
+ uint32_t start;
+ uint32_t end;
+
+ int ret = SendMessage(hdlg, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
+
+ GetWindowText(hdlg, sztext, _countof(sztext));
+
+ BOOL at_end = (mir_wstrlen(sztext) == (int)end);
+
+ if (ret != -1) {
+ if (wparam == VK_BACK) {
+ if (start > 0)
+ SendMessage(hdlg, EM_SETSEL, (WPARAM)start - 1, (LPARAM)end);
+
+ sztext[0] = 0;
+ }
+ else {
+ sztext[0] = wparam;
+ sztext[1] = 0;
+ }
+
+ SendMessage(hdlg, EM_REPLACESEL, 0, (LPARAM)sztext);
+ GetWindowText(hdlg, sztext, _countof(sztext));
+ }
+
+ CheckText(hdlg, sztext, !at_end);
+
+ return 1;
+ }
+ case WM_KEYUP:
+ {
+ wchar_t sztext[120] = L"";
+
+ if (wparam == VK_RETURN) {
+ switch (SendMessage(GetParent(hdlg), CB_GETDROPPEDSTATE, 0, 0)) {
+ case FALSE:
+ SendMessage(GetParent(GetParent(hdlg)), WM_COMMAND, MAKEWPARAM(IDC_ENTER, STN_CLICKED), 0);
+ break;
+
+ case TRUE:
+ SendMessage(GetParent(hdlg), CB_SHOWDROPDOWN, FALSE, 0);
+ break;
+ }
+ }
+ else if (wparam == VK_DELETE) {
+ GetWindowText(hdlg, sztext, _countof(sztext));
+ CheckText(hdlg, sztext, TRUE);
+ }
+
+ return 0;
+ }
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTCHARS | DLGC_WANTARROWS;
+ }
+
+ return mir_callNextSubclass(hdlg, EditProc, msg, wparam, lparam);
+}
+
+HACCEL hAcct;
+HHOOK hHook;
+
+// This function filters the message queue and translates
+// the keyboard accelerators
+LRESULT CALLBACK HookProc(int code, WPARAM, LPARAM lparam)
+{
+ if (code != MSGF_DIALOGBOX)
+ return 0;
+
+ MSG *msg = (MSG*)lparam;
+
+ int action = Hotkey_Check(msg, "Quick Contacts");
+ if (action != 0) {
+ SendMessage(hwndMain, WM_COMMAND, action, 0);
+ return 1;
+ }
+
+ if (msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE) {
+ switch (SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_GETDROPPEDSTATE, 0, 0)) {
+ case FALSE:
+ SendMessage(hwndMain, WM_CLOSE, 0, 0);
+ break;
+
+ case TRUE:
+ SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_SHOWDROPDOWN, FALSE, 0);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+
+BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint)
+{
+ return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint);
+}
+
+
+static void FillButton(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key, HICON icon)
+{
+ wchar_t tmp[256];
+ wchar_t *full = tmp;
+
+ if (key == nullptr)
+ full = TranslateW(name);
+ else
+ mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key);
+
+ SendDlgItemMessage(hwndDlg, dlgItem, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, dlgItem, BUTTONADDTOOLTIP, (LPARAM)full, BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, dlgItem, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon);
+}
+
+
+static void FillCheckbox(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key)
+{
+ wchar_t tmp[256];
+ wchar_t *full = tmp;
+
+ if (key == nullptr)
+ full = TranslateW(name);
+ else
+ mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key);
+
+ SetDlgItemText(hwndDlg, dlgItem, full);
+}
+
+
+static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_USERNAME), &rc);
+ ScreenToClient(hwndDlg, &rc);
+
+ CreateWindow(L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
+ rc.left - 20, rc.top + (rc.bottom - rc.top - 16) / 2, 16, 16, hwndDlg, (HMENU)IDC_ICO,
+ g_plugin.getInst(), nullptr);
+
+ hHook = SetWindowsHookEx(WH_MSGFILTER, HookProc, nullptr, GetCurrentThreadId());
+
+ // Combo
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, EM_LIMITTEXT, (WPARAM)119, 0);
+ mir_subclassWindow(GetWindow(GetDlgItem(hwndDlg, IDC_USERNAME), GW_CHILD), EditProc);
+
+ // Buttons
+ FillCheckbox(hwndDlg, IDC_SHOW_ALL_CONTACTS, LPGENW("Show all contacts"), NULL);
+ FillButton(hwndDlg, IDC_MESSAGE, LPGENW("Send message"), nullptr, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
+ FillButton(hwndDlg, IDC_FILE, LPGENW("Send file"), NULL, Skin_LoadIcon(SKINICON_EVENT_FILE));
+ FillButton(hwndDlg, IDC_USERINFO, LPGENW("Open user info"), NULL, Skin_LoadIcon(SKINICON_OTHER_USERDETAILS));
+ FillButton(hwndDlg, IDC_HISTORY, LPGENW("Open history"), NULL, Skin_LoadIcon(SKINICON_OTHER_HISTORY));
+ FillButton(hwndDlg, IDC_MENU, LPGENW("Open contact menu"), NULL, Skin_LoadIcon(SKINICON_OTHER_DOWNARROW));
+
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETEXTENDEDUI, TRUE, 0);
+
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, MODULENAME, "window");
+
+ LoadContacts(hwndDlg, FALSE);
+
+ EnableButtons(hwndDlg, NULL);
+ if (g_plugin.getByte("EnableLastSentTo", 0)) {
+ int pos = GetItemPos(g_plugin.getDword("LastSentTo", -1));
+ if (pos != -1) {
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETCURSEL, (WPARAM)pos, 0);
+ EnableButtons(hwndDlg, contacts[pos]->hcontact);
+ }
+ }
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_USERNAME:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
+ EnableButtons(hwndDlg, pos < contacts.getCount() ? contacts[pos]->hcontact : NULL);
+ }
+ break;
+
+ case IDC_ENTER:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ break;
+
+ Clist_ContactDoubleClicked(hContact);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+ case IDC_MESSAGE:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MESSAGE)))
+ break;
+
+ CallService(MS_MSG_SENDMESSAGEW, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+
+ case HOTKEY_FILE:
+ case IDC_FILE:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FILE)))
+ break;
+
+ CallService(MS_FILE_SENDFILE, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case HOTKEY_INFO:
+ case IDC_USERINFO:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_USERINFO)))
+ break;
+
+ CallService(MS_USERINFO_SHOWDIALOG, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case HOTKEY_HISTORY:
+ case IDC_HISTORY:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_HISTORY)))
+ break;
+
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case HOTKEY_MENU:
+ case IDC_MENU:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MENU)))
+ break;
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_MENU), &rc);
+ HMENU hMenu = Menu_BuildContactMenu(hContact);
+ int ret = TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, nullptr);
+ DestroyMenu(hMenu);
+
+ if (ret) {
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ Clist_MenuProcessCommand(LOWORD(ret), MPCF_CONTACTMENU, hContact);
+ }
+
+ g_plugin.setDword("LastSentTo", (uint32_t)hContact);
+ }
+ break;
+
+ case HOTKEY_ALL_CONTACTS:
+ case IDC_SHOW_ALL_CONTACTS:
+ {
+ // Get old text
+ HWND hEdit = GetWindow(GetWindow(hwndDlg, GW_CHILD), GW_CHILD);
+ wchar_t sztext[120] = L"";
+
+ if (SendMessage(hEdit, EM_GETSEL, 0, 0) != -1)
+ SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)L"");
+
+ GetWindowText(hEdit, sztext, _countof(sztext));
+
+ // Fill combo
+ BOOL all = IsDlgButtonChecked(hwndDlg, IDC_SHOW_ALL_CONTACTS);
+
+ if (LOWORD(wParam) == HOTKEY_ALL_CONTACTS) {
+ // Toggle checkbox
+ all = !all;
+ CheckDlgButton(hwndDlg, IDC_SHOW_ALL_CONTACTS, all ? BST_CHECKED : BST_UNCHECKED);
+ }
+
+ LoadContacts(hwndDlg, all);
+
+ // Return selection
+ CheckText(hEdit, sztext);
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "window");
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ UnhookWindowsHookEx(hHook);
+ hwndMain = nullptr;
+ FreeContacts();
+ InterlockedExchange(&main_dialog_open, 0);
+ break;
+
+ case WM_DRAWITEM:
+ {
+ // add icons and protocol to listbox
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+
+ // Handle contact menu
+ if (lpdis->CtlID != IDC_USERNAME) {
+ if (lpdis->CtlType == ODT_MENU)
+ return Menu_DrawItem(lParam);
+ break;
+ }
+
+ // Handle combo
+ if (lpdis->itemID == -1)
+ break;
+
+ TEXTMETRIC tm;
+ int icon_width = 0, icon_height = 0;
+ RECT rc;
+
+ GetTextMetrics(lpdis->hDC, &tm);
+ ImageList_GetIconSize(hIml, &icon_width, &icon_height);
+
+ COLORREF clrfore = SetTextColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
+ COLORREF clrback = SetBkColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+
+ // Draw icon
+ rc.left = lpdis->rcItem.left + 5;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - icon_height) / 2;
+ ImageList_Draw(hIml, Clist_GetContactIcon(contacts[lpdis->itemData]->hcontact), lpdis->hDC, rc.left, rc.top, ILD_NORMAL);
+
+ // Make rect for text
+ rc.left += icon_width + 5;
+ rc.right = lpdis->rcItem.right - 1;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
+ rc.bottom = rc.top + tm.tmHeight;
+
+ // Draw Protocol
+ if (opts.num_protos > 1) {
+ if (max_proto_width == 0) {
+ // Has to be done, else the DC isnt the right one
+ // Dont ask me why
+ for (auto &it : contacts) {
+ RECT rcc = { 0, 0, 0x7FFF, 0x7FFF };
+ DrawText(lpdis->hDC, it->proto, -1, &rcc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT);
+ max_proto_width = max(max_proto_width, rcc.right - rcc.left);
+ }
+
+ // Fix max_proto_width
+ if (opts.group_append && opts.group_column)
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 5);
+ else if (opts.group_append)
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 4);
+ else
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 3);
+ }
+
+ RECT rc_tmp = rc;
+ rc_tmp.left = rc_tmp.right - max_proto_width;
+ DrawText(lpdis->hDC, contacts[lpdis->itemData]->proto, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+ rc.right = rc_tmp.left - 5;
+ }
+
+ // Draw group
+ if (opts.group_append && opts.group_column) {
+ RECT rc_tmp = rc;
+
+ if (opts.group_column_left) {
+ rc_tmp.right = rc_tmp.left + (rc.right - rc.left) / 3;
+ rc.left = rc_tmp.right + 5;
+ }
+ else {
+ rc_tmp.left = rc_tmp.right - (rc.right - rc.left) / 3;
+ rc.right = rc_tmp.left - 5;
+ }
+
+ DrawText(lpdis->hDC, contacts[lpdis->itemData]->szgroup, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+ }
+
+ // Draw text
+ wchar_t *name;
+ if (opts.group_append && !opts.group_column)
+ name = GetListName(contacts[lpdis->itemData]);
+ else
+ name = contacts[lpdis->itemData]->szname;
+
+ DrawText(lpdis->hDC, name, -1, &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+
+ // Restore old colors
+ SetTextColor(lpdis->hDC, clrfore);
+ SetBkColor(lpdis->hDC, clrback);
+ }
+ return TRUE;
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+
+ // Handle contact menu
+ if (lpmis->CtlID != IDC_USERNAME) {
+ if (lpmis->CtlType == ODT_MENU)
+ return Menu_MeasureItem(lParam);
+ break;
+ }
+
+ // Handle combo
+
+ TEXTMETRIC tm;
+ int icon_width = 0, icon_height = 0;
+
+ GetTextMetrics(GetDC(hwndDlg), &tm);
+ ImageList_GetIconSize(hIml, &icon_width, &icon_height);
+
+ lpmis->itemHeight = max(icon_height, tm.tmHeight);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// Show the main dialog
+INT_PTR ShowDialog(WPARAM, LPARAM)
+{
+ // Get the icons for the listbox
+ hIml = Clist_GetImageList();
+
+ if (!main_dialog_open) {
+ InterlockedExchange(&main_dialog_open, 1);
+
+ hwndMain = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_MAIN), nullptr, MainDlgProc);
+ }
+
+ // Show it
+ SetForegroundWindow(hwndMain);
+ SetFocus(hwndMain);
+ ShowWindow(hwndMain, SW_SHOW);
+ return 0;
+}
diff --git a/plugins/QuickContacts/src/stdafx.cxx b/plugins/QuickContacts/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/QuickContacts/src/stdafx.cxx +++ b/plugins/QuickContacts/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/QuickMessages/src/stdafx.cxx b/plugins/QuickMessages/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/QuickMessages/src/stdafx.cxx +++ b/plugins/QuickMessages/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/QuickReplies/src/stdafx.cxx b/plugins/QuickReplies/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/QuickReplies/src/stdafx.cxx +++ b/plugins/QuickReplies/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/QuickSearch/src/main.cpp b/plugins/QuickSearch/src/main.cpp index cf068d7561..632bfb2aff 100644 --- a/plugins/QuickSearch/src/main.cpp +++ b/plugins/QuickSearch/src/main.cpp @@ -1,188 +1,188 @@ -/* -Copyright (C) 2012-22 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; - -HANDLE hTTBButton; - -bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled; - -int OnOptInit(WPARAM, LPARAM); - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C} - {0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}} -}; - -CMPlugin::CMPlugin() : - PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx), - m_columns(1) -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int OnTTBLoaded(WPARAM, LPARAM) -{ - TTBButton ttb = {}; - ttb.dwFlags = TTBBF_VISIBLE; - ttb.pszService = QS_SHOWSERVICE; - ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS); - ttb.name = MODULENAME; - ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search"); - hTTBButton = g_plugin.addTTB(&ttb); - return 0; -} - -static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM) -{ - OpenSrWindow((wchar_t *)wParam); - return 0; -} - -static int OnCheckPlugins(WPARAM, LPARAM) -{ - g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING); - g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW); - g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW); - - return 0; -} - -static int OnModulesLoaded(WPARAM, LPARAM) -{ - HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded); - - CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow); - - // add menu item - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0); - mi.name.a = LPGEN("Quick Search"); - mi.position = 500050000; - mi.pszService = QS_SHOWSERVICE; - mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS); - Menu_AddMainMenuItem(&mi); - - // register hotkey - HOTKEYDESC hkd = {}; - hkd.pszName = "QS_Global"; - hkd.szDescription.a = LPGEN("Open Quick Search window"); - hkd.szSection.a = LPGEN("Quick Search"); - hkd.pszService = QS_SHOWSERVICE; - hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3); - g_plugin.addHotkey(&hkd); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static IconItem iconList[] = -{ - { LPGEN("Quick Search"), "QS", IDI_QS }, - { LPGEN("New Column"), "New", IDI_NEW }, - { LPGEN("Column Up"), "Up", IDI_UP }, - { LPGEN("Column Down"), "Down", IDI_DOWN }, - { LPGEN("Delete Column"), "Delete", IDI_DELETE }, - { LPGEN("Default"), "Default", IDI_DEFAULT}, - { LPGEN("Reload"), "Reload", IDI_RELOAD }, - { LPGEN("Male"), "Male", IDI_MALE }, - { LPGEN("Female"), "Female", IDI_FEMALE }, -}; - -struct -{ - COLORREF defValue; - const char *szSetting, *szDescr; -} -static sttColors[color_max] = { - { 0x00FFFFFF, "back_norm", LPGEN("Normal background") }, - { 0x00000000, "fore_norm", LPGEN("Normal foreground") }, - { 0x00EBE6DE, "back_odd" , LPGEN("Odd background") }, - { 0x00000000, "fore_odd" , LPGEN("Odd foreground") }, - { 0x008080FF, "back_dis" , LPGEN("Disabled account background") }, - { 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") }, - { 0x008000FF, "back_del" , LPGEN("Deleted account background") }, - { 0x00000000, "fore_del" , LPGEN("Deleted account foreground") }, - { 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background") }, - { 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground") }, - { 0x00BAE699, "back_meta", LPGEN("Metacontact background") }, - { 0x00000000, "fore_meta", LPGEN("Metacontact foreground") }, - { 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background") }, - { 0x00000000, "fore_sub" , LPGEN("Subcontact foreground") }, -}; - -static int OnColorReload(WPARAM, LPARAM) -{ - for (int i = 0; i < color_max; i++) - g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szDescr); - return 0; -} - -int CMPlugin::Load() -{ - g_plugin.registerIcon(MODULENAME, iconList); - - ColourID colourid = {}; - strncpy_s(colourid.group, MODULENAME, _TRUNCATE); - strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE); - - for (auto &it : sttColors) { - strncpy_s(colourid.name, it.szDescr, _TRUNCATE); - strncpy_s(colourid.setting, it.szSetting, _TRUNCATE); - colourid.defcolour = it.defValue; - colourid.order = int(&it - sttColors); - g_plugin.addColor(&colourid); - } - OnColorReload(0, 0); - OnCheckPlugins(0, 0); - - HookEvent(ME_COLOUR_RELOAD, OnColorReload); - HookEvent(ME_OPT_INITIALISE, OnOptInit); - HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); - HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins); - HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins); - - if (!LoadColumns(m_columns)) - LoadDefaultColumns(m_columns); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPlugin::Unload() -{ - if (hTTBButton) { - CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0); - hTTBButton = 0; - } - - CloseSrWindow(); - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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;
+
+HANDLE hTTBButton;
+
+bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled;
+
+int OnOptInit(WPARAM, LPARAM);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C}
+ {0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
+ m_columns(1)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int OnTTBLoaded(WPARAM, LPARAM)
+{
+ TTBButton ttb = {};
+ ttb.dwFlags = TTBBF_VISIBLE;
+ ttb.pszService = QS_SHOWSERVICE;
+ ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS);
+ ttb.name = MODULENAME;
+ ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search");
+ hTTBButton = g_plugin.addTTB(&ttb);
+ return 0;
+}
+
+static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM)
+{
+ OpenSrWindow((wchar_t *)wParam);
+ return 0;
+}
+
+static int OnCheckPlugins(WPARAM, LPARAM)
+{
+ g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING);
+ g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW);
+ g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW);
+
+ return 0;
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded);
+
+ CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow);
+
+ // add menu item
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0);
+ mi.name.a = LPGEN("Quick Search");
+ mi.position = 500050000;
+ mi.pszService = QS_SHOWSERVICE;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS);
+ Menu_AddMainMenuItem(&mi);
+
+ // register hotkey
+ HOTKEYDESC hkd = {};
+ hkd.pszName = "QS_Global";
+ hkd.szDescription.a = LPGEN("Open Quick Search window");
+ hkd.szSection.a = LPGEN("Quick Search");
+ hkd.pszService = QS_SHOWSERVICE;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3);
+ g_plugin.addHotkey(&hkd);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static IconItem iconList[] =
+{
+ { LPGEN("Quick Search"), "QS", IDI_QS },
+ { LPGEN("New Column"), "New", IDI_NEW },
+ { LPGEN("Column Up"), "Up", IDI_UP },
+ { LPGEN("Column Down"), "Down", IDI_DOWN },
+ { LPGEN("Delete Column"), "Delete", IDI_DELETE },
+ { LPGEN("Default"), "Default", IDI_DEFAULT},
+ { LPGEN("Reload"), "Reload", IDI_RELOAD },
+ { LPGEN("Male"), "Male", IDI_MALE },
+ { LPGEN("Female"), "Female", IDI_FEMALE },
+};
+
+struct
+{
+ COLORREF defValue;
+ const char *szSetting, *szDescr;
+}
+static sttColors[color_max] = {
+ { 0x00FFFFFF, "back_norm", LPGEN("Normal background") },
+ { 0x00000000, "fore_norm", LPGEN("Normal foreground") },
+ { 0x00EBE6DE, "back_odd" , LPGEN("Odd background") },
+ { 0x00000000, "fore_odd" , LPGEN("Odd foreground") },
+ { 0x008080FF, "back_dis" , LPGEN("Disabled account background") },
+ { 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") },
+ { 0x008000FF, "back_del" , LPGEN("Deleted account background") },
+ { 0x00000000, "fore_del" , LPGEN("Deleted account foreground") },
+ { 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background") },
+ { 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground") },
+ { 0x00BAE699, "back_meta", LPGEN("Metacontact background") },
+ { 0x00000000, "fore_meta", LPGEN("Metacontact foreground") },
+ { 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background") },
+ { 0x00000000, "fore_sub" , LPGEN("Subcontact foreground") },
+};
+
+static int OnColorReload(WPARAM, LPARAM)
+{
+ for (int i = 0; i < color_max; i++)
+ g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szDescr);
+ return 0;
+}
+
+int CMPlugin::Load()
+{
+ g_plugin.registerIcon(MODULENAME, iconList);
+
+ ColourID colourid = {};
+ strncpy_s(colourid.group, MODULENAME, _TRUNCATE);
+ strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE);
+
+ for (auto &it : sttColors) {
+ strncpy_s(colourid.name, it.szDescr, _TRUNCATE);
+ strncpy_s(colourid.setting, it.szSetting, _TRUNCATE);
+ colourid.defcolour = it.defValue;
+ colourid.order = int(&it - sttColors);
+ g_plugin.addColor(&colourid);
+ }
+ OnColorReload(0, 0);
+ OnCheckPlugins(0, 0);
+
+ HookEvent(ME_COLOUR_RELOAD, OnColorReload);
+ HookEvent(ME_OPT_INITIALISE, OnOptInit);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
+ HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins);
+
+ if (!LoadColumns(m_columns))
+ LoadDefaultColumns(m_columns);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+ if (hTTBButton) {
+ CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0);
+ hTTBButton = 0;
+ }
+
+ CloseSrWindow();
+ return 0;
+}
diff --git a/plugins/QuickSearch/src/options.cpp b/plugins/QuickSearch/src/options.cpp index d827416ecc..d74cf745aa 100644 --- a/plugins/QuickSearch/src/options.cpp +++ b/plugins/QuickSearch/src/options.cpp @@ -1,519 +1,519 @@ -/* -Copyright (C) 2012-22 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" - -class COptionsDlg : public CDlgBase -{ - OBJLIST<ColumnItem> m_columns; - - void AddColumn(int idx, ColumnItem *pCol) - { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = idx; - lvi.lParam = LPARAM(pCol); - m_list.InsertItem(&lvi); - } - - void CheckDirection(int iItem) - { - btnUp.Enable(iItem > 0); - btnDown.Enable(iItem < m_list.GetItemCount()-1); - } - - void ClearScreen() - { - // setting - ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE); - - // contact info - ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE); - - // others - ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE); - } - - void DisplayCurInfo(const ColumnItem *pCol) - { - ClearScreen(); - SetupScreen(pCol->setting_type); - - editTitle.SetText(pCol->title); - cmbVarType.SelectData(pCol->setting_type); - - switch (pCol->setting_type) { - case QST_SETTING: - cmbDataType.SelectData(pCol->datatype); - editModule.SetTextA(pCol->module); - editSetting.SetTextA(pCol->setting); - break; - - case QST_SCRIPT: - SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script); - break; - - case QST_CONTACTINFO: - cmbCnfType.SelectData(pCol->cnftype); - break; - - case QST_OTHER: - cmbOther.SelectData(pCol->other); - break; - } - } - - void FillTableLine(int item, ColumnItem *pColumn) - { - m_list.SetItemText(item, 1, pColumn->title); - - switch (pColumn->setting_type) { - case QST_SETTING: - m_list.SetItemText(item, 2, _A2T(pColumn->module)); - m_list.SetItemText(item, 3, _A2T(pColumn->setting)); - break; - - case QST_SCRIPT: - m_list.SetItemText(item, 2, TranslateT("Script")); - break; - - case QST_SERVICE: - m_list.SetItemText(item, 2, TranslateT("Service")); - m_list.SetItemText(item, 3, _A2T(pColumn->svc.service)); - break; - - case QST_CONTACTINFO: - m_list.SetItemText(item, 2, TranslateT("Contact info")); - m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype)); - break; - - case QST_OTHER: - m_list.SetItemText(item, 2, TranslateT("Other")); - if (pColumn->other == QSTO_METACONTACT) - m_list.SetItemText(item, 3, TranslateT("Metacontact")); - break; - } - } - - void InitScreen() - { - // setting - cmbDataType.SetCurSel(0); - editModule.SetText(L""); - editSetting.SetText(L""); - - // contact info - cmbCnfType.SetCurSel(0); - - // others - cmbOther.SetCurSel(0); - } - - void ResizeControl(int id, int width) - { - HWND hwnd = GetDlgItem(m_hwnd, id); - RECT rc; - ::GetWindowRect(hwnd, &rc); - ::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); - } - - void SetupScreen(int type) - { - if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE))) - return; - - // setting - int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE; - ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd); - editModule.Show(cmd == SW_SHOW); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd); - editSetting.Show(cmd == SW_SHOW); - - // contact info - cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE; - ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd); - - // script - cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE; - ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd); - - // others - cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE; - ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd); - } - - void UpdateList() - { - m_list.DeleteAllItems(); - - int cnt = 0; - for (auto &it : m_columns) { - AddColumn(cnt, it); - FillTableLine(cnt, it); - m_list.SetCheckState(cnt, it->bEnabled); - cnt++; - } - - m_list.SetCurSel(0); - } - - CCtrlEdit editTitle, editModule, editSetting; - CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons; - CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType; - CCtrlListView m_list; - CCtrlButton btnSave, btnResize; - CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload; - -public: - COptionsDlg() : - CDlgBase(g_plugin, IDD_OPTIONS), - m_columns(1), - m_list(this, IDC_LIST), - btnSave(this, IDC_SETITEM), - btnResize(this, IDC_B_RESIZE), - btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")), - btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")), - btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")), - btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")), - btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")), - btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")), - editTitle(this, IDC_E_TITLE), - editModule(this, IDC_E_MODULE), - editSetting(this, IDC_E_SETTING), - cmbOther(this, IDC_C_OTHER), - cmbCnfType(this, IDC_C_CNFTYPE), - cmbVarType(this, IDC_C_VARTYPE), - cmbDataType(this, IDC_C_DATATYPE), - chkDrawGrid(this, IDC_CH_DRAWGRID), - chkAutoClose(this, IDC_CH_AUTOCLOSE), - chkSortStatus(this, IDC_CH_SORTSTATUS), - chkSavePattern(this, IDC_CH_SAVEPATTERN), - chkClientIcons(this, IDC_CH_CLIENTICONS), - chkUseToolstyle(this, IDC_CH_USETOOLSTYLE) - { - m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List); - - btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up); - btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New); - btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down); - btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save); - btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete); - btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload); - btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize); - btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default); - - cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var); - } - - bool OnInitDialog() override - { - editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent(); - cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent(); - - m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); - m_list.AddColumn(0, L"#", 20); - m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1", 95)); - m_list.AddColumn(2, TranslateT("Module/Info type"), g_plugin.getWord("col2", 105)); - m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3", 85)); - - cmbVarType.AddString(TranslateT("Database setting"), QST_SETTING); - cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT); - cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO); - cmbVarType.AddString(TranslateT("Other"), QST_OTHER); - cmbVarType.SetCurSel(0); - - cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE); - cmbDataType.AddString(TranslateT("Word"), QSTS_WORD); - cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD); - cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED); - cmbDataType.AddString(TranslateT("Hexadecimal"), QSTS_HEXNUM); - cmbDataType.AddString(TranslateT("String"), QSTS_STRING); - cmbDataType.AddString(TranslateT("Timestamp"), QSTS_TIMESTAMP); - cmbDataType.SetCurSel(0); - - cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN); - cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT); - cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT); - cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT); - cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME); - cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT); - cmbOther.SetCurSel(0); - - for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++) - if (auto *pwszText = cnf2str(i)) - cmbCnfType.AddString(pwszText, i); - cmbCnfType.SetCurSel(0); - - chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0); - chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0); - chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0); - chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0); - chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0); - chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0); - - // make local copy of column descriptions - for (auto &it : g_plugin.m_columns) - m_columns.insert(new ColumnItem(*it)); - - UpdateList(); - onClick_Resize(0); - if (m_columns.getCount()) - DisplayCurInfo(&m_columns[0]); - return true; - } - - bool OnApply() override - { - // checkboxes - g_plugin.m_flags &= ~QSO_MAINOPTIONS; - if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID; - if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE; - if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS; - if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS; - if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN; - if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE; - - int tmpbool = CloseSrWindow(false); - - g_plugin.m_columns.destroy(); - int nCount = m_list.GetItemCount(); - for (int i = 0; i < nCount; i++) { - auto *pCol = (ColumnItem *)m_list.GetItemData(i); - pCol->bEnabled = m_list.GetCheckState(i) != 0; - g_plugin.m_columns.insert(new ColumnItem(*pCol)); - } - - g_plugin.SaveOptions(); - - if (tmpbool) - OpenSrWindow(0); - return true; - } - - void OnDestroy() override - { - m_columns.destroy(); - - g_plugin.setWord("col1", m_list.GetColumnWidth(1)); - g_plugin.setWord("col2", m_list.GetColumnWidth(2)); - g_plugin.setWord("col3", m_list.GetColumnWidth(3)); - } - - void onClick_New(CCtrlButton *) - { - int idx = m_list.GetSelectionMark()+1; - auto *pNew = new ColumnItem(TranslateT("New column")); - m_columns.insert(pNew); - - AddColumn(idx, pNew); - m_list.EnsureVisible(idx, FALSE); - m_list.SetCurSel(idx); - InitScreen(); - CheckDirection(idx); - btnDelete.Enable(); - NotifyChange(); - } - - void onClick_Delete(CCtrlButton *) - { - int idx = m_list.GetSelectionMark(); - auto *pCol = (ColumnItem *)m_list.GetItemData(idx); - - m_list.DeleteItem(idx); - m_columns.remove(pCol); - - int nCount = m_list.GetItemCount(); - if (nCount == 0) { - m_list.Disable(); - InitScreen(); - } - else { - if (nCount == idx) - idx--; - m_list.SetCurSel(idx); - } - CheckDirection(idx); - NotifyChange(); - } - - void onClick_Up(CCtrlButton *) - { - int idx = m_list.GetSelectionMark(); - if (idx > 0) { - CheckDirection(m_list.MoveItem(idx, -1)); - NotifyChange(); - } - } - - void onClick_Down(CCtrlButton *) - { - int idx = m_list.GetSelectionMark(); - if (idx < m_list.GetItemCount() - 1) { - CheckDirection(m_list.MoveItem(idx, 1)); - NotifyChange(); - } - } - - void onClick_Reload(CCtrlButton *) - { - g_plugin.LoadColumns(m_columns); - UpdateList(); - } - - void onClick_Default(CCtrlButton *) - { - LoadDefaultColumns(m_columns); - UpdateList(); - NotifyChange(); - } - - void onClick_Save(CCtrlButton *) - { - if (m_list.GetItemCount() == 0) { - AddColumn(0, new ColumnItem(TranslateT("New column"))); - m_list.SetCurSel(0); - btnDelete.Enable(); - } - - int idx = m_list.GetSelectionMark(); - auto *pCol = (ColumnItem *)m_list.GetItemData(idx); - pCol->dwFlags = 0; - if (m_list.GetCheckState(idx)) - pCol->bEnabled = pCol->bFilter = true; - pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel()); - replaceStrW(pCol->title, editTitle.GetText()); - - switch (pCol->setting_type) { - case QST_SETTING: - pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel()); - pCol->module = mir_u2a(ptrW(editModule.GetText())); - pCol->setting = mir_u2a(ptrW(editSetting.GetText())); - break; - - case QST_CONTACTINFO: - pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel()); - break; - - case QST_OTHER: - pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel()); - break; - } - - FillTableLine(idx, pCol); - NotifyChange(); - } - - void onClick_Resize(CCtrlButton *) - { - wchar_t *pcw; - int dx, rside; - - RECT rc, rc1; - GetClientRect(m_hwnd, &rc); - GetWindowRect(btnResize.GetHwnd(), &rc1); - - POINT pt = { rc1.left, 0 }; - ScreenToClient(m_hwnd, &pt); - if (pt.x < (rc.right - 50)) { - rside = SW_HIDE; - dx = rc.right - (rc1.right - rc1.left) - 4; - pcw = L"<"; - } - else { - rside = SW_SHOW; - - GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc); - pt.x = rc.left; - pt.y = 0; - ScreenToClient(m_hwnd, &pt); - dx = pt.x - (rc1.right - rc1.left) - 4; - pcw = L">"; - } - - btnResize.SetText(pcw); - - // move separator button - SetWindowPos(btnResize.GetHwnd(), 0, dx + 2, 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); - - // resize left side controls - ResizeControl(IDC_LIST, dx); - ResizeControl(IDC_CH_GROUP, dx); - - ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8); - ResizeControl(IDC_CH_DRAWGRID, dx - 8); - ResizeControl(IDC_CH_SAVEPATTERN, dx - 8); - ResizeControl(IDC_CH_AUTOCLOSE, dx - 8); - ResizeControl(IDC_CH_SORTSTATUS, dx - 8); - ResizeControl(IDC_CH_CLIENTICONS, dx - 8); - - // show/hide setting block (ugly, i know!) - ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside); - ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside); - - ClearScreen(); - if (rside == SW_SHOW) - SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel())); - } - - void onItemChanged_List(CCtrlListView::TEventInfo *ev) - { - auto *nmlv = ev->nmlv; - // we got new focus - if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) { - CheckDirection(nmlv->iItem); - InitScreen(); - DisplayCurInfo((ColumnItem*)nmlv->lParam); - } - } - - void onSelChanged_Var(CCtrlCombo *pCombo) - { - SetupScreen(pCombo->GetItemData(pCombo->GetCurSel())); - } -}; - -int OnOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.szGroup.a = LPGEN("Contacts"); - odp.szTitle.a = LPGEN("Quick Search"); - odp.position = 900003000; - odp.pDialog = new COptionsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+class COptionsDlg : public CDlgBase
+{
+ OBJLIST<ColumnItem> m_columns;
+
+ void AddColumn(int idx, ColumnItem *pCol)
+ {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = idx;
+ lvi.lParam = LPARAM(pCol);
+ m_list.InsertItem(&lvi);
+ }
+
+ void CheckDirection(int iItem)
+ {
+ btnUp.Enable(iItem > 0);
+ btnDown.Enable(iItem < m_list.GetItemCount()-1);
+ }
+
+ void ClearScreen()
+ {
+ // setting
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE);
+
+ // contact info
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE);
+
+ // others
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE);
+ }
+
+ void DisplayCurInfo(const ColumnItem *pCol)
+ {
+ ClearScreen();
+ SetupScreen(pCol->setting_type);
+
+ editTitle.SetText(pCol->title);
+ cmbVarType.SelectData(pCol->setting_type);
+
+ switch (pCol->setting_type) {
+ case QST_SETTING:
+ cmbDataType.SelectData(pCol->datatype);
+ editModule.SetTextA(pCol->module);
+ editSetting.SetTextA(pCol->setting);
+ break;
+
+ case QST_SCRIPT:
+ SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script);
+ break;
+
+ case QST_CONTACTINFO:
+ cmbCnfType.SelectData(pCol->cnftype);
+ break;
+
+ case QST_OTHER:
+ cmbOther.SelectData(pCol->other);
+ break;
+ }
+ }
+
+ void FillTableLine(int item, ColumnItem *pColumn)
+ {
+ m_list.SetItemText(item, 1, pColumn->title);
+
+ switch (pColumn->setting_type) {
+ case QST_SETTING:
+ m_list.SetItemText(item, 2, _A2T(pColumn->module));
+ m_list.SetItemText(item, 3, _A2T(pColumn->setting));
+ break;
+
+ case QST_SCRIPT:
+ m_list.SetItemText(item, 2, TranslateT("Script"));
+ break;
+
+ case QST_SERVICE:
+ m_list.SetItemText(item, 2, TranslateT("Service"));
+ m_list.SetItemText(item, 3, _A2T(pColumn->svc.service));
+ break;
+
+ case QST_CONTACTINFO:
+ m_list.SetItemText(item, 2, TranslateT("Contact info"));
+ m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype));
+ break;
+
+ case QST_OTHER:
+ m_list.SetItemText(item, 2, TranslateT("Other"));
+ if (pColumn->other == QSTO_METACONTACT)
+ m_list.SetItemText(item, 3, TranslateT("Metacontact"));
+ break;
+ }
+ }
+
+ void InitScreen()
+ {
+ // setting
+ cmbDataType.SetCurSel(0);
+ editModule.SetText(L"");
+ editSetting.SetText(L"");
+
+ // contact info
+ cmbCnfType.SetCurSel(0);
+
+ // others
+ cmbOther.SetCurSel(0);
+ }
+
+ void ResizeControl(int id, int width)
+ {
+ HWND hwnd = GetDlgItem(m_hwnd, id);
+ RECT rc;
+ ::GetWindowRect(hwnd, &rc);
+ ::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
+ }
+
+ void SetupScreen(int type)
+ {
+ if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE)))
+ return;
+
+ // setting
+ int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd);
+ editModule.Show(cmd == SW_SHOW);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd);
+ editSetting.Show(cmd == SW_SHOW);
+
+ // contact info
+ cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd);
+
+ // script
+ cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd);
+
+ // others
+ cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd);
+ }
+
+ void UpdateList()
+ {
+ m_list.DeleteAllItems();
+
+ int cnt = 0;
+ for (auto &it : m_columns) {
+ AddColumn(cnt, it);
+ FillTableLine(cnt, it);
+ m_list.SetCheckState(cnt, it->bEnabled);
+ cnt++;
+ }
+
+ m_list.SetCurSel(0);
+ }
+
+ CCtrlEdit editTitle, editModule, editSetting;
+ CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons;
+ CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType;
+ CCtrlListView m_list;
+ CCtrlButton btnSave, btnResize;
+ CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload;
+
+public:
+ COptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPTIONS),
+ m_columns(1),
+ m_list(this, IDC_LIST),
+ btnSave(this, IDC_SETITEM),
+ btnResize(this, IDC_B_RESIZE),
+ btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")),
+ btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")),
+ btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")),
+ btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")),
+ btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")),
+ btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")),
+ editTitle(this, IDC_E_TITLE),
+ editModule(this, IDC_E_MODULE),
+ editSetting(this, IDC_E_SETTING),
+ cmbOther(this, IDC_C_OTHER),
+ cmbCnfType(this, IDC_C_CNFTYPE),
+ cmbVarType(this, IDC_C_VARTYPE),
+ cmbDataType(this, IDC_C_DATATYPE),
+ chkDrawGrid(this, IDC_CH_DRAWGRID),
+ chkAutoClose(this, IDC_CH_AUTOCLOSE),
+ chkSortStatus(this, IDC_CH_SORTSTATUS),
+ chkSavePattern(this, IDC_CH_SAVEPATTERN),
+ chkClientIcons(this, IDC_CH_CLIENTICONS),
+ chkUseToolstyle(this, IDC_CH_USETOOLSTYLE)
+ {
+ m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List);
+
+ btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up);
+ btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New);
+ btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down);
+ btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save);
+ btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete);
+ btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload);
+ btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize);
+ btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default);
+
+ cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var);
+ }
+
+ bool OnInitDialog() override
+ {
+ editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent();
+ cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent();
+
+ m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
+ m_list.AddColumn(0, L"#", 20);
+ m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1", 95));
+ m_list.AddColumn(2, TranslateT("Module/Info type"), g_plugin.getWord("col2", 105));
+ m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3", 85));
+
+ cmbVarType.AddString(TranslateT("Database setting"), QST_SETTING);
+ cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT);
+ cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO);
+ cmbVarType.AddString(TranslateT("Other"), QST_OTHER);
+ cmbVarType.SetCurSel(0);
+
+ cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE);
+ cmbDataType.AddString(TranslateT("Word"), QSTS_WORD);
+ cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD);
+ cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED);
+ cmbDataType.AddString(TranslateT("Hexadecimal"), QSTS_HEXNUM);
+ cmbDataType.AddString(TranslateT("String"), QSTS_STRING);
+ cmbDataType.AddString(TranslateT("Timestamp"), QSTS_TIMESTAMP);
+ cmbDataType.SetCurSel(0);
+
+ cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN);
+ cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT);
+ cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT);
+ cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT);
+ cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME);
+ cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT);
+ cmbOther.SetCurSel(0);
+
+ for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++)
+ if (auto *pwszText = cnf2str(i))
+ cmbCnfType.AddString(pwszText, i);
+ cmbCnfType.SetCurSel(0);
+
+ chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0);
+ chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0);
+ chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0);
+ chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0);
+ chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0);
+ chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0);
+
+ // make local copy of column descriptions
+ for (auto &it : g_plugin.m_columns)
+ m_columns.insert(new ColumnItem(*it));
+
+ UpdateList();
+ onClick_Resize(0);
+ if (m_columns.getCount())
+ DisplayCurInfo(&m_columns[0]);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ // checkboxes
+ g_plugin.m_flags &= ~QSO_MAINOPTIONS;
+ if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID;
+ if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE;
+ if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS;
+ if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS;
+ if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN;
+ if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE;
+
+ int tmpbool = CloseSrWindow(false);
+
+ g_plugin.m_columns.destroy();
+ int nCount = m_list.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ auto *pCol = (ColumnItem *)m_list.GetItemData(i);
+ pCol->bEnabled = m_list.GetCheckState(i) != 0;
+ g_plugin.m_columns.insert(new ColumnItem(*pCol));
+ }
+
+ g_plugin.SaveOptions();
+
+ if (tmpbool)
+ OpenSrWindow(0);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ m_columns.destroy();
+
+ g_plugin.setWord("col1", m_list.GetColumnWidth(1));
+ g_plugin.setWord("col2", m_list.GetColumnWidth(2));
+ g_plugin.setWord("col3", m_list.GetColumnWidth(3));
+ }
+
+ void onClick_New(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark()+1;
+ auto *pNew = new ColumnItem(TranslateT("New column"));
+ m_columns.insert(pNew);
+
+ AddColumn(idx, pNew);
+ m_list.EnsureVisible(idx, FALSE);
+ m_list.SetCurSel(idx);
+ InitScreen();
+ CheckDirection(idx);
+ btnDelete.Enable();
+ NotifyChange();
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark();
+ auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
+
+ m_list.DeleteItem(idx);
+ m_columns.remove(pCol);
+
+ int nCount = m_list.GetItemCount();
+ if (nCount == 0) {
+ m_list.Disable();
+ InitScreen();
+ }
+ else {
+ if (nCount == idx)
+ idx--;
+ m_list.SetCurSel(idx);
+ }
+ CheckDirection(idx);
+ NotifyChange();
+ }
+
+ void onClick_Up(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark();
+ if (idx > 0) {
+ CheckDirection(m_list.MoveItem(idx, -1));
+ NotifyChange();
+ }
+ }
+
+ void onClick_Down(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark();
+ if (idx < m_list.GetItemCount() - 1) {
+ CheckDirection(m_list.MoveItem(idx, 1));
+ NotifyChange();
+ }
+ }
+
+ void onClick_Reload(CCtrlButton *)
+ {
+ g_plugin.LoadColumns(m_columns);
+ UpdateList();
+ }
+
+ void onClick_Default(CCtrlButton *)
+ {
+ LoadDefaultColumns(m_columns);
+ UpdateList();
+ NotifyChange();
+ }
+
+ void onClick_Save(CCtrlButton *)
+ {
+ if (m_list.GetItemCount() == 0) {
+ AddColumn(0, new ColumnItem(TranslateT("New column")));
+ m_list.SetCurSel(0);
+ btnDelete.Enable();
+ }
+
+ int idx = m_list.GetSelectionMark();
+ auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
+ pCol->dwFlags = 0;
+ if (m_list.GetCheckState(idx))
+ pCol->bEnabled = pCol->bFilter = true;
+ pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel());
+ replaceStrW(pCol->title, editTitle.GetText());
+
+ switch (pCol->setting_type) {
+ case QST_SETTING:
+ pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel());
+ pCol->module = mir_u2a(ptrW(editModule.GetText()));
+ pCol->setting = mir_u2a(ptrW(editSetting.GetText()));
+ break;
+
+ case QST_CONTACTINFO:
+ pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
+ break;
+
+ case QST_OTHER:
+ pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
+ break;
+ }
+
+ FillTableLine(idx, pCol);
+ NotifyChange();
+ }
+
+ void onClick_Resize(CCtrlButton *)
+ {
+ wchar_t *pcw;
+ int dx, rside;
+
+ RECT rc, rc1;
+ GetClientRect(m_hwnd, &rc);
+ GetWindowRect(btnResize.GetHwnd(), &rc1);
+
+ POINT pt = { rc1.left, 0 };
+ ScreenToClient(m_hwnd, &pt);
+ if (pt.x < (rc.right - 50)) {
+ rside = SW_HIDE;
+ dx = rc.right - (rc1.right - rc1.left) - 4;
+ pcw = L"<";
+ }
+ else {
+ rside = SW_SHOW;
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc);
+ pt.x = rc.left;
+ pt.y = 0;
+ ScreenToClient(m_hwnd, &pt);
+ dx = pt.x - (rc1.right - rc1.left) - 4;
+ pcw = L">";
+ }
+
+ btnResize.SetText(pcw);
+
+ // move separator button
+ SetWindowPos(btnResize.GetHwnd(), 0, dx + 2, 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
+
+ // resize left side controls
+ ResizeControl(IDC_LIST, dx);
+ ResizeControl(IDC_CH_GROUP, dx);
+
+ ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8);
+ ResizeControl(IDC_CH_DRAWGRID, dx - 8);
+ ResizeControl(IDC_CH_SAVEPATTERN, dx - 8);
+ ResizeControl(IDC_CH_AUTOCLOSE, dx - 8);
+ ResizeControl(IDC_CH_SORTSTATUS, dx - 8);
+ ResizeControl(IDC_CH_CLIENTICONS, dx - 8);
+
+ // show/hide setting block (ugly, i know!)
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside);
+
+ ClearScreen();
+ if (rside == SW_SHOW)
+ SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel()));
+ }
+
+ void onItemChanged_List(CCtrlListView::TEventInfo *ev)
+ {
+ auto *nmlv = ev->nmlv;
+ // we got new focus
+ if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) {
+ CheckDirection(nmlv->iItem);
+ InitScreen();
+ DisplayCurInfo((ColumnItem*)nmlv->lParam);
+ }
+ }
+
+ void onSelChanged_Var(CCtrlCombo *pCombo)
+ {
+ SetupScreen(pCombo->GetItemData(pCombo->GetCurSel()));
+ }
+};
+
+int OnOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.szGroup.a = LPGEN("Contacts");
+ odp.szTitle.a = LPGEN("Quick Search");
+ odp.position = 900003000;
+ odp.pDialog = new COptionsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/QuickSearch/src/stdafx.cxx b/plugins/QuickSearch/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/plugins/QuickSearch/src/stdafx.cxx +++ b/plugins/QuickSearch/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/plugins/QuickSearch/src/utils.cpp b/plugins/QuickSearch/src/utils.cpp index 9db7133c3b..2cf1cdbc9f 100644 --- a/plugins/QuickSearch/src/utils.cpp +++ b/plugins/QuickSearch/src/utils.cpp @@ -1,556 +1,556 @@ -/* -Copyright (C) 2012-22 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 ACF_TYPE_NUMBER 0x00 // Parameter is number -#define ACF_TYPE_STRING 0x01 // Parameter is ANSI String -#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string -#define ACF_TYPE_STRUCT 0x03 // Parameter is (result is in) structure -#define ACF_TYPE_PARAM 0x08 // Parameter is Call parameter -#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window -#define ACF_TYPE_RESULT 0x0A // Parameter is previous action result -#define ACF_TYPE_MASK 0x0F // parameter/result type mask - -ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) : - title(mir_wstrdup(pwszTitle)), - width(_width), - setting_type(_setting_type) -{ - bEnabled = true; -} - -ColumnItem::ColumnItem(const ColumnItem &src) -{ - memcpy(this, &src, sizeof(ColumnItem)); - - title = mir_wstrdup(title); - - switch (setting_type) { - case QST_SETTING: - module = mir_strdup(module); - setting = mir_strdup(setting); - break; - - case QST_SCRIPT: - script = mir_wstrdup(script); - break; - - case QST_SERVICE: - svc.service = mir_strdup(svc.service); - switch (svc.wFlags) { - case ACF_TYPE_NUMBER: - case ACF_TYPE_STRING: - case ACF_TYPE_UNICODE: - svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam); - break; - case ACF_TYPE_STRUCT: - svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam); - break; - } - - switch (svc.lFlags) { - case ACF_TYPE_NUMBER: - case ACF_TYPE_STRING: - case ACF_TYPE_UNICODE: - svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam); - break; - case ACF_TYPE_STRUCT: - svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam); - break; - } - break; - } -} - -ColumnItem::~ColumnItem() -{ - mir_free(title); - - switch (setting_type) { - case QST_SETTING: - mir_free(module); - mir_free(setting); - break; - - case QST_SCRIPT: - mir_free(script); - break; - - case QST_SERVICE: - mir_free(svc.service); - switch (svc.wFlags) { - case ACF_TYPE_NUMBER: - case ACF_TYPE_STRING: - case ACF_TYPE_UNICODE: - mir_free((wchar_t *)svc.wParam); - break; - case ACF_TYPE_STRUCT: - mir_free((char *)svc.wParam); - break; - } - - switch (svc.lFlags) { - case ACF_TYPE_NUMBER: - case ACF_TYPE_STRING: - case ACF_TYPE_UNICODE: - mir_free((wchar_t *)svc.lParam); - break; - case ACF_TYPE_STRUCT: - mir_free((char *)svc.lParam); - break; - } - break; - } -} - -void ColumnItem::SetSpecialColumns() -{ - if (setting_type == QST_SETTING) { - if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group")) - isGroup = true; - - else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW")) - isContainer = true; - - else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId")) - isXstatus = true; - - else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled) - isClient = true; - } - else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER) - isGender = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// column functions - -int ListViewToColumn(int col) -{ - for (auto &it : g_plugin.m_columns) { - if (!it->bEnabled) - continue; - - if (col-- <= 0) - return g_plugin.m_columns.indexOf(&it); - } - return -1; -} - -int ColumnToListView(int col) -{ - int res = -1; - for (auto &it : g_plugin.m_columns) { - if (it->bEnabled) - res++; - - if (col-- <= 0) - break; - } - return res; -} - -void LoadDefaultColumns(OBJLIST<ColumnItem> &dst) -{ - dst.destroy(); - - auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER); - pNew->other = QSTO_ACCOUNT; - dst.insert(pNew); - - dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER)); - - pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER); - pNew->bFilter = true; - pNew->other = QSTO_DISPLAYNAME; - - pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING); - pNew->datatype = QSTS_STRING; - pNew->module = mir_strdup("CList"); - pNew->setting = mir_strdup("Group"); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING); - pNew->datatype = QSTS_STRING; - pNew->module = mir_strdup("Tab_SRMsg"); - pNew->setting = mir_strdup("containerW"); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING); - pNew->datatype = QSTS_STRING; - pNew->setting = mir_strdup("MirVer"); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER); - pNew->other = QSTO_LASTSEEN; - pNew->dwFlags = 0; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER); - pNew->other = QSTO_LASTEVENT; - pNew->dwFlags = 0; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING); - pNew->datatype = QSTS_TIMESTAMP; - pNew->setting = mir_strdup("LogonTS"); - pNew->bFilter = true; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER); - pNew->other = QSTO_METACONTACT; - pNew->dwFlags = 0; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER); - pNew->other = QSTO_EVENTCOUNT; - pNew->dwFlags = 0; - dst.insert(pNew); - - pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING); - pNew->datatype = QSTS_TIMESTAMP; - pNew->module = mir_strdup("UserInfo"); - pNew->setting = mir_strdup("ContactAddTime"); - dst.insert(pNew); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// window options - -void CMPlugin::LoadOptWnd() -{ - m_rect.bottom = getDword(so_mbottom); - m_rect.right = getDword(so_mright); - m_rect.left = getDword(so_mleft); - m_rect.top = getDword(so_mtop); - - m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC); - m_sortOrder = getDword(so_columnsort); -} - -void CMPlugin::SaveOptWnd() -{ - setDword(so_mbottom, m_rect.bottom); - setDword(so_mright, m_rect.right); - setDword(so_mleft, m_rect.left); - setDword(so_mtop, m_rect.top); - - setDword(so_flags, m_flags); - setDword(so_columnsort, m_sortOrder); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// load options - -int CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst) -{ - m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC); - int numCols = getWord(so_numcolumns); - - for (int i = 0; i < numCols; i++) { - auto *pNew = new ColumnItem(nullptr); - LoadColumn(i, *pNew); - dst.insert(pNew); - } - - return numCols; -} - -void CMPlugin::LoadColumn(int n, ColumnItem &col) -{ - char buf[127]; - int offset = mir_snprintf(buf, "%s%d_", so_item, n); - - strcpy(buf + offset, so_title); col.title = getWStringA(buf); - strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf); - strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf); - strcpy(buf + offset, so_width); col.width = getWord(buf); - - switch (col.setting_type) { - case QST_SETTING: - strcpy(buf + offset, so_datatype); col.datatype = getWord(buf); - strcpy(buf + offset, so_module); col.module = getStringA(buf); - strcpy(buf + offset, so_setting); col.setting = getStringA(buf); - break; - - case QST_SCRIPT: - strcpy(buf + offset, so_script); col.script = getWStringA(buf); - break; - - case QST_CONTACTINFO: - strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf); - break; - - case QST_SERVICE: - offset = mir_snprintf(buf, "%s%d/service/", so_item, n); - strcpy(buf + offset, so_service); col.svc.service = getStringA(buf); - strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf); - if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) { - col.setting_type = QST_OTHER; - col.other = QSTO_ACCOUNT; - break; - } - - strcpy(buf + offset, so_wparam); - LoadParamValue(buf, col.svc.wFlags, col.svc.wParam); - - strcpy(buf + offset, so_lparam); - LoadParamValue(buf, col.svc.lFlags, col.svc.lParam); - break; - - case QST_OTHER: - strcpy(buf + offset, so_other); col.other = getWord(buf); - break; - } -} - -void CMPlugin::LoadParamValue(char *buf, uint32_t &dwFlags, LPARAM &dwWalue) -{ - char *pEnd = buf + strlen(buf); - strcpy(pEnd, "flags"); dwFlags = getDword(buf); - - strcpy(pEnd, "value"); - switch (dwFlags) { - case ACF_TYPE_NUMBER: - case ACF_TYPE_STRING: - case ACF_TYPE_UNICODE: - dwWalue = LPARAM(getWStringA(buf)); - break; - - case ACF_TYPE_STRUCT: - dwWalue = LPARAM(getStringA(buf)); - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// save options - -static int ListSettings(const char *szSetting, void *param) -{ - if (!memcmp(szSetting, so_item, 4)) { - auto *pList = (LIST<char>*)param; - pList->insert(mir_strdup(szSetting)); - } - return 0; -} - -void CMPlugin::SaveOptions() -{ - // remove old settings - LIST<char> settings(30); - db_enum_settings(0, ListSettings, MODULENAME, &settings); - - for (auto &it : settings) { - delSetting(it); - mir_free(it); - } - - // write new settings - setDword(so_flags, m_flags); - setWord(so_numcolumns, m_columns.getCount()); - - int i = 0; - for (auto &it : m_columns) - SaveColumn(i++, *it); -} - -void CMPlugin::SaveColumn(int n, const ColumnItem &col) -{ - char buf[127]; - int offset = mir_snprintf(buf, "%s%d_", so_item, n); - - strcpy(buf + offset, so_title); setWString(buf, col.title); - strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type); - strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags); - strcpy(buf + offset, so_width); setWord(buf, col.width); - - switch (col.setting_type) { - case QST_SETTING: - strcpy(buf + offset, so_datatype); setWord(buf, col.datatype); - strcpy(buf + offset, so_module); setString(buf, col.module); - strcpy(buf + offset, so_setting); setString(buf, col.setting); - break; - - case QST_SCRIPT: - strcpy(buf + offset, so_script); setWString(buf, col.script); - break; - - case QST_CONTACTINFO: - strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype); - break; - - case QST_SERVICE: - offset = mir_snprintf(buf, "%s%d/service/", so_item, n); - strcpy(buf + offset, so_service); setString(buf, col.svc.service); - strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags); - - strcpy(buf + offset, so_wparam); - SaveParamValue(buf, col.svc.wFlags, col.svc.wParam); - - strcpy(buf + offset, so_lparam); - SaveParamValue(buf, col.svc.lFlags, col.svc.lParam); - break; - - case QST_OTHER: - strcpy(buf + offset, so_other); setWord(buf, col.other); - break; - } -} - -void CMPlugin::SaveParamValue(char *buf, uint32_t flags, LPARAM value) -{ - char *pEnd = buf + strlen(buf); - strcpy(pEnd, "flags"); setDword(buf, flags); - - strcpy(pEnd, "value"); - switch (flags) { - case ACF_TYPE_NUMBER: - case ACF_TYPE_STRING: - case ACF_TYPE_UNICODE: - setWString(buf, (wchar_t *)value); - break; - - case ACF_TYPE_STRUCT: - setString(buf, (char *)value); - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -const wchar_t* cnf2str(int cnf) -{ - switch (cnf) { - case CNF_FIRSTNAME: return TranslateT("First name"); - case CNF_LASTNAME: return TranslateT("Last name"); - case CNF_NICK: return TranslateT("Nick"); - case CNF_CUSTOMNICK: return TranslateT("Custom nick"); - case CNF_EMAIL: return TranslateT("Email"); - case CNF_CITY: return TranslateT("City"); - case CNF_STATE: return TranslateT("State"); - case CNF_COUNTRY: return TranslateT("Country"); - case CNF_PHONE: return TranslateT("Phone"); - case CNF_HOMEPAGE: return TranslateT("Homepage"); - case CNF_ABOUT: return TranslateT("About"); - case CNF_GENDER: return TranslateT("Gender"); - case CNF_AGE: return TranslateT("Age"); - case CNF_FIRSTLAST: return TranslateT("First name/Last name"); - case CNF_UNIQUEID: return TranslateT("Unique ID"); - case CNF_FAX: return TranslateT("Fax"); - case CNF_CELLULAR: return TranslateT("Cellular"); - case CNF_TIMEZONE: return TranslateT("Time zone"); - case CNF_MYNOTES: return TranslateT("My notes"); - case CNF_BIRTHDAY: return TranslateT("Birth day"); - case CNF_BIRTHMONTH: return TranslateT("Birth month"); - case CNF_BIRTHYEAR: return TranslateT("Birth year"); - case CNF_STREET: return TranslateT("Street"); - case CNF_ZIP: return TranslateT("ZIP code"); - case CNF_LANGUAGE1: return TranslateT("Language #1"); - case CNF_LANGUAGE2: return TranslateT("Language #2"); - case CNF_LANGUAGE3: return TranslateT("Language #3"); - case CNF_CONAME: return TranslateT("Company name"); - case CNF_CODEPT: return TranslateT("Company department"); - case CNF_COPOSITION: return TranslateT("Company position"); - case CNF_COSTREET: return TranslateT("Company street"); - case CNF_COCITY: return TranslateT("Company city"); - case CNF_COSTATE: return TranslateT("Company state"); - case CNF_COZIP: return TranslateT("Company ZIP"); - case CNF_COCOUNTRY: return TranslateT("Company country"); - case CNF_COHOMEPAGE: return TranslateT("Company homepage"); - case CNF_DISPLAYUID: return TranslateT("Display ID"); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// formatters - -wchar_t* BuildLastSeenTime(uint32_t ts) -{ - int year = ts / (60 * 24 * 31 * 356); - if (year == 0) - return nullptr; - - year += 1980; ts = ts % (60 * 24 * 31 * 356); - - int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31); - int day = ts / (60 * 24); ts = ts % (60 * 24); - int hours = ts / 60; - int mins = ts % 60; - - return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach(); -} - -uint32_t BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule) -{ - int year = db_get_w(hContact, szModule, "Year"); - if (year == 0) - return 0; - - int day = db_get_w(hContact, szModule, "Day"); - int month = db_get_w(hContact, szModule, "Month"); - int hours = db_get_w(hContact, szModule, "Hours"); - int minutes = db_get_w(hContact, szModule, "Minutes"); - - return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes; -} - -wchar_t* TimeToStrW(uint32_t timestamp) -{ - wchar_t buf[63]; - TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf)); - return mir_wstrdup(buf); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void SnapToScreen(RECT &rc) -{ - int left = GetSystemMetrics(SM_XVIRTUALSCREEN); - int top = GetSystemMetrics(SM_YVIRTUALSCREEN); - int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left; - int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top; - if (rc.right > right) - rc.right = right; - if (rc.bottom > bottom) - rc.bottom = bottom; - if (rc.left < left) - rc.left = left; - if (rc.top < top) - rc.top = top; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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 ACF_TYPE_NUMBER 0x00 // Parameter is number
+#define ACF_TYPE_STRING 0x01 // Parameter is ANSI String
+#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string
+#define ACF_TYPE_STRUCT 0x03 // Parameter is (result is in) structure
+#define ACF_TYPE_PARAM 0x08 // Parameter is Call parameter
+#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window
+#define ACF_TYPE_RESULT 0x0A // Parameter is previous action result
+#define ACF_TYPE_MASK 0x0F // parameter/result type mask
+
+ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) :
+ title(mir_wstrdup(pwszTitle)),
+ width(_width),
+ setting_type(_setting_type)
+{
+ bEnabled = true;
+}
+
+ColumnItem::ColumnItem(const ColumnItem &src)
+{
+ memcpy(this, &src, sizeof(ColumnItem));
+
+ title = mir_wstrdup(title);
+
+ switch (setting_type) {
+ case QST_SETTING:
+ module = mir_strdup(module);
+ setting = mir_strdup(setting);
+ break;
+
+ case QST_SCRIPT:
+ script = mir_wstrdup(script);
+ break;
+
+ case QST_SERVICE:
+ svc.service = mir_strdup(svc.service);
+ switch (svc.wFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam);
+ break;
+ }
+
+ switch (svc.lFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam);
+ break;
+ }
+ break;
+ }
+}
+
+ColumnItem::~ColumnItem()
+{
+ mir_free(title);
+
+ switch (setting_type) {
+ case QST_SETTING:
+ mir_free(module);
+ mir_free(setting);
+ break;
+
+ case QST_SCRIPT:
+ mir_free(script);
+ break;
+
+ case QST_SERVICE:
+ mir_free(svc.service);
+ switch (svc.wFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ mir_free((wchar_t *)svc.wParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ mir_free((char *)svc.wParam);
+ break;
+ }
+
+ switch (svc.lFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ mir_free((wchar_t *)svc.lParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ mir_free((char *)svc.lParam);
+ break;
+ }
+ break;
+ }
+}
+
+void ColumnItem::SetSpecialColumns()
+{
+ if (setting_type == QST_SETTING) {
+ if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group"))
+ isGroup = true;
+
+ else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW"))
+ isContainer = true;
+
+ else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId"))
+ isXstatus = true;
+
+ else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled)
+ isClient = true;
+ }
+ else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER)
+ isGender = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// column functions
+
+int ListViewToColumn(int col)
+{
+ for (auto &it : g_plugin.m_columns) {
+ if (!it->bEnabled)
+ continue;
+
+ if (col-- <= 0)
+ return g_plugin.m_columns.indexOf(&it);
+ }
+ return -1;
+}
+
+int ColumnToListView(int col)
+{
+ int res = -1;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled)
+ res++;
+
+ if (col-- <= 0)
+ break;
+ }
+ return res;
+}
+
+void LoadDefaultColumns(OBJLIST<ColumnItem> &dst)
+{
+ dst.destroy();
+
+ auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER);
+ pNew->other = QSTO_ACCOUNT;
+ dst.insert(pNew);
+
+ dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER));
+
+ pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER);
+ pNew->bFilter = true;
+ pNew->other = QSTO_DISPLAYNAME;
+
+ pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING);
+ pNew->datatype = QSTS_STRING;
+ pNew->module = mir_strdup("CList");
+ pNew->setting = mir_strdup("Group");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING);
+ pNew->datatype = QSTS_STRING;
+ pNew->module = mir_strdup("Tab_SRMsg");
+ pNew->setting = mir_strdup("containerW");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING);
+ pNew->datatype = QSTS_STRING;
+ pNew->setting = mir_strdup("MirVer");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER);
+ pNew->other = QSTO_LASTSEEN;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER);
+ pNew->other = QSTO_LASTEVENT;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING);
+ pNew->datatype = QSTS_TIMESTAMP;
+ pNew->setting = mir_strdup("LogonTS");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER);
+ pNew->other = QSTO_METACONTACT;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER);
+ pNew->other = QSTO_EVENTCOUNT;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING);
+ pNew->datatype = QSTS_TIMESTAMP;
+ pNew->module = mir_strdup("UserInfo");
+ pNew->setting = mir_strdup("ContactAddTime");
+ dst.insert(pNew);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// window options
+
+void CMPlugin::LoadOptWnd()
+{
+ m_rect.bottom = getDword(so_mbottom);
+ m_rect.right = getDword(so_mright);
+ m_rect.left = getDword(so_mleft);
+ m_rect.top = getDword(so_mtop);
+
+ m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
+ m_sortOrder = getDword(so_columnsort);
+}
+
+void CMPlugin::SaveOptWnd()
+{
+ setDword(so_mbottom, m_rect.bottom);
+ setDword(so_mright, m_rect.right);
+ setDword(so_mleft, m_rect.left);
+ setDword(so_mtop, m_rect.top);
+
+ setDword(so_flags, m_flags);
+ setDword(so_columnsort, m_sortOrder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// load options
+
+int CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst)
+{
+ m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
+ int numCols = getWord(so_numcolumns);
+
+ for (int i = 0; i < numCols; i++) {
+ auto *pNew = new ColumnItem(nullptr);
+ LoadColumn(i, *pNew);
+ dst.insert(pNew);
+ }
+
+ return numCols;
+}
+
+void CMPlugin::LoadColumn(int n, ColumnItem &col)
+{
+ char buf[127];
+ int offset = mir_snprintf(buf, "%s%d_", so_item, n);
+
+ strcpy(buf + offset, so_title); col.title = getWStringA(buf);
+ strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf);
+ strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf);
+ strcpy(buf + offset, so_width); col.width = getWord(buf);
+
+ switch (col.setting_type) {
+ case QST_SETTING:
+ strcpy(buf + offset, so_datatype); col.datatype = getWord(buf);
+ strcpy(buf + offset, so_module); col.module = getStringA(buf);
+ strcpy(buf + offset, so_setting); col.setting = getStringA(buf);
+ break;
+
+ case QST_SCRIPT:
+ strcpy(buf + offset, so_script); col.script = getWStringA(buf);
+ break;
+
+ case QST_CONTACTINFO:
+ strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf);
+ break;
+
+ case QST_SERVICE:
+ offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
+ strcpy(buf + offset, so_service); col.svc.service = getStringA(buf);
+ strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf);
+ if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) {
+ col.setting_type = QST_OTHER;
+ col.other = QSTO_ACCOUNT;
+ break;
+ }
+
+ strcpy(buf + offset, so_wparam);
+ LoadParamValue(buf, col.svc.wFlags, col.svc.wParam);
+
+ strcpy(buf + offset, so_lparam);
+ LoadParamValue(buf, col.svc.lFlags, col.svc.lParam);
+ break;
+
+ case QST_OTHER:
+ strcpy(buf + offset, so_other); col.other = getWord(buf);
+ break;
+ }
+}
+
+void CMPlugin::LoadParamValue(char *buf, uint32_t &dwFlags, LPARAM &dwWalue)
+{
+ char *pEnd = buf + strlen(buf);
+ strcpy(pEnd, "flags"); dwFlags = getDword(buf);
+
+ strcpy(pEnd, "value");
+ switch (dwFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ dwWalue = LPARAM(getWStringA(buf));
+ break;
+
+ case ACF_TYPE_STRUCT:
+ dwWalue = LPARAM(getStringA(buf));
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// save options
+
+static int ListSettings(const char *szSetting, void *param)
+{
+ if (!memcmp(szSetting, so_item, 4)) {
+ auto *pList = (LIST<char>*)param;
+ pList->insert(mir_strdup(szSetting));
+ }
+ return 0;
+}
+
+void CMPlugin::SaveOptions()
+{
+ // remove old settings
+ LIST<char> settings(30);
+ db_enum_settings(0, ListSettings, MODULENAME, &settings);
+
+ for (auto &it : settings) {
+ delSetting(it);
+ mir_free(it);
+ }
+
+ // write new settings
+ setDword(so_flags, m_flags);
+ setWord(so_numcolumns, m_columns.getCount());
+
+ int i = 0;
+ for (auto &it : m_columns)
+ SaveColumn(i++, *it);
+}
+
+void CMPlugin::SaveColumn(int n, const ColumnItem &col)
+{
+ char buf[127];
+ int offset = mir_snprintf(buf, "%s%d_", so_item, n);
+
+ strcpy(buf + offset, so_title); setWString(buf, col.title);
+ strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type);
+ strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags);
+ strcpy(buf + offset, so_width); setWord(buf, col.width);
+
+ switch (col.setting_type) {
+ case QST_SETTING:
+ strcpy(buf + offset, so_datatype); setWord(buf, col.datatype);
+ strcpy(buf + offset, so_module); setString(buf, col.module);
+ strcpy(buf + offset, so_setting); setString(buf, col.setting);
+ break;
+
+ case QST_SCRIPT:
+ strcpy(buf + offset, so_script); setWString(buf, col.script);
+ break;
+
+ case QST_CONTACTINFO:
+ strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype);
+ break;
+
+ case QST_SERVICE:
+ offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
+ strcpy(buf + offset, so_service); setString(buf, col.svc.service);
+ strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags);
+
+ strcpy(buf + offset, so_wparam);
+ SaveParamValue(buf, col.svc.wFlags, col.svc.wParam);
+
+ strcpy(buf + offset, so_lparam);
+ SaveParamValue(buf, col.svc.lFlags, col.svc.lParam);
+ break;
+
+ case QST_OTHER:
+ strcpy(buf + offset, so_other); setWord(buf, col.other);
+ break;
+ }
+}
+
+void CMPlugin::SaveParamValue(char *buf, uint32_t flags, LPARAM value)
+{
+ char *pEnd = buf + strlen(buf);
+ strcpy(pEnd, "flags"); setDword(buf, flags);
+
+ strcpy(pEnd, "value");
+ switch (flags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ setWString(buf, (wchar_t *)value);
+ break;
+
+ case ACF_TYPE_STRUCT:
+ setString(buf, (char *)value);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+const wchar_t* cnf2str(int cnf)
+{
+ switch (cnf) {
+ case CNF_FIRSTNAME: return TranslateT("First name");
+ case CNF_LASTNAME: return TranslateT("Last name");
+ case CNF_NICK: return TranslateT("Nick");
+ case CNF_CUSTOMNICK: return TranslateT("Custom nick");
+ case CNF_EMAIL: return TranslateT("Email");
+ case CNF_CITY: return TranslateT("City");
+ case CNF_STATE: return TranslateT("State");
+ case CNF_COUNTRY: return TranslateT("Country");
+ case CNF_PHONE: return TranslateT("Phone");
+ case CNF_HOMEPAGE: return TranslateT("Homepage");
+ case CNF_ABOUT: return TranslateT("About");
+ case CNF_GENDER: return TranslateT("Gender");
+ case CNF_AGE: return TranslateT("Age");
+ case CNF_FIRSTLAST: return TranslateT("First name/Last name");
+ case CNF_UNIQUEID: return TranslateT("Unique ID");
+ case CNF_FAX: return TranslateT("Fax");
+ case CNF_CELLULAR: return TranslateT("Cellular");
+ case CNF_TIMEZONE: return TranslateT("Time zone");
+ case CNF_MYNOTES: return TranslateT("My notes");
+ case CNF_BIRTHDAY: return TranslateT("Birth day");
+ case CNF_BIRTHMONTH: return TranslateT("Birth month");
+ case CNF_BIRTHYEAR: return TranslateT("Birth year");
+ case CNF_STREET: return TranslateT("Street");
+ case CNF_ZIP: return TranslateT("ZIP code");
+ case CNF_LANGUAGE1: return TranslateT("Language #1");
+ case CNF_LANGUAGE2: return TranslateT("Language #2");
+ case CNF_LANGUAGE3: return TranslateT("Language #3");
+ case CNF_CONAME: return TranslateT("Company name");
+ case CNF_CODEPT: return TranslateT("Company department");
+ case CNF_COPOSITION: return TranslateT("Company position");
+ case CNF_COSTREET: return TranslateT("Company street");
+ case CNF_COCITY: return TranslateT("Company city");
+ case CNF_COSTATE: return TranslateT("Company state");
+ case CNF_COZIP: return TranslateT("Company ZIP");
+ case CNF_COCOUNTRY: return TranslateT("Company country");
+ case CNF_COHOMEPAGE: return TranslateT("Company homepage");
+ case CNF_DISPLAYUID: return TranslateT("Display ID");
+ }
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// formatters
+
+wchar_t* BuildLastSeenTime(uint32_t ts)
+{
+ int year = ts / (60 * 24 * 31 * 356);
+ if (year == 0)
+ return nullptr;
+
+ year += 1980; ts = ts % (60 * 24 * 31 * 356);
+
+ int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31);
+ int day = ts / (60 * 24); ts = ts % (60 * 24);
+ int hours = ts / 60;
+ int mins = ts % 60;
+
+ return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach();
+}
+
+uint32_t BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule)
+{
+ int year = db_get_w(hContact, szModule, "Year");
+ if (year == 0)
+ return 0;
+
+ int day = db_get_w(hContact, szModule, "Day");
+ int month = db_get_w(hContact, szModule, "Month");
+ int hours = db_get_w(hContact, szModule, "Hours");
+ int minutes = db_get_w(hContact, szModule, "Minutes");
+
+ return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes;
+}
+
+wchar_t* TimeToStrW(uint32_t timestamp)
+{
+ wchar_t buf[63];
+ TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf));
+ return mir_wstrdup(buf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SnapToScreen(RECT &rc)
+{
+ int left = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ int top = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left;
+ int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top;
+ if (rc.right > right)
+ rc.right = right;
+ if (rc.bottom > bottom)
+ rc.bottom = bottom;
+ if (rc.left < left)
+ rc.left = left;
+ if (rc.top < top)
+ rc.top = top;
+}
diff --git a/plugins/QuickSearch/src/version.h b/plugins/QuickSearch/src/version.h index c054045e77..d1f948c4c1 100644 --- a/plugins/QuickSearch/src/version.h +++ b/plugins/QuickSearch/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "This plugin allows you to quick search for nickname, firstname, lastname, email, uin in your contact list."
#define __AUTHOR "Bethoven, Awkward"
#define __AUTHORWEB "https://miranda-ng.org/p/QuickSearch"
-#define __COPYRIGHT "© 2004-05 Bethoven; 2006-13 Awkward; 2014-22 Miranda NG team"
+#define __COPYRIGHT "© 2004-05 Bethoven; 2006-13 Awkward; 2014-23 Miranda NG team"
diff --git a/plugins/QuickSearch/src/window.cpp b/plugins/QuickSearch/src/window.cpp index 891d39aa92..8624be7d98 100644 --- a/plugins/QuickSearch/src/window.cpp +++ b/plugins/QuickSearch/src/window.cpp @@ -1,876 +1,876 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it &/| -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 | 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 IDM_STAYONTOP (WM_USER+1) - -static QSMainDlg *g_pDlg = 0; - -///////////////////////////////////////////////////////////////////////////////////////// -// edit control window procedure - -static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA); - if (pDlg) - if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam)) - return res; - - return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam); -} - -INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM) -{ - switch (msg) { - case WM_CHAR: - if (wParam == 27) // Escape - Close(); - break; - - case WM_KEYUP: - if (wParam == VK_RETURN) { - if (m_grid.GetSelectedCount() == 1) - ShowContactMsgDlg(GetFocusedContact()); - return 0; - } - break; - - case WM_KEYDOWN: - int count = m_grid.GetItemCount(); - int current = m_grid.GetNextItem(-1, LVNI_FOCUSED); - int next = -1; - if (count > 0) { - switch (wParam) { - case VK_UP: - if (current > 0) - next = current - 1; - break; - - case VK_DOWN: - if (current < count - 1) - next = current + 1; - break; - - case VK_F5: - onClick_Refresh(0); - return 0; - - case VK_NEXT: - case VK_PRIOR: - int perpage = m_grid.GetCountPerPage(); - if (wParam == VK_NEXT) - next = min(current + perpage, count); - else - next = max(current - perpage, 0); - break; - } - } - - if (next >= 0) { - m_grid.SetItemState(-1, 0, LVIS_SELECTED); - m_grid.SetCurSel(next); - m_grid.EnsureVisible(next, FALSE); - } - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// list header window procedure - -static void MakeColumnMenu() -{ - HMENU hMenu = CreatePopupMenu(); - - for (auto &it : g_plugin.m_columns) { - int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED; - AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title)); - } - - POINT pt; - GetCursorPos(&pt); - - int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0); - if (id >= 100) - g_pDlg->ToggleColumn(id - 100); - - DestroyMenu(hMenu); -} - -void QSMainDlg::ToggleColumn(int col) -{ - auto &pCol = g_plugin.m_columns[col]; - - if (!pCol.bEnabled) { // show column - pCol.bEnabled = true; - - if (!pCol.bInit) { - for (auto &it : m_rows) - it->pValues[col].LoadOneItem(it->hContact, pCol, this); - pCol.bInit = true; - } - - // screen - int lvcol = ColumnToListView(col); - AddColumn(lvcol, &pCol); - - int nCount = m_grid.GetItemCount(); - for (int i = 0; i < nCount; i++) { - auto *pRow = GetRow(i); - - LV_ITEMW li; - li.iItem = i; - li.iSubItem = lvcol; - li.mask = LVIF_TEXT; - li.pszText = pRow->pValues[col].text; - if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus) - li.mask |= LVIF_IMAGE; - m_grid.SetItem(&li); - } - } - else { // hide column - int cnt = 0; - for (auto &it : g_plugin.m_columns) - if (it->bEnabled) - cnt++; - - // keep at least one visible column (1 + this) - if (cnt > 2) { - m_grid.DeleteColumn(col); - pCol.bEnabled = false; - } - } -} - -static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_RBUTTONUP: - return 0; - - case WM_RBUTTONDOWN: - MakeColumnMenu(); - break; - } - - return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// grid list window procedure - -static int OldHSubItem = 0, OldHItem = 0; - -static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA); - if (pDlg) - if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam)) - return res; - - return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam); -} - -INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - LV_HITTESTINFO pinfo; - - switch (msg) { - case WM_CHAR: - switch (wParam) { // ESC - case 27: - Close(); - break; - - case 1: // Cltr+A - m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED); - break; - - case 3: // Ctrl-C - CopyMultiLines(); - break; - - case 8: // backspace - edtFilter.SendMsg(msg, wParam, lParam); - break; - } - - if (wParam >= 32 && wParam <= 127) // letters - edtFilter.SendMsg(msg, wParam, lParam); - break; - - case WM_MOUSEMOVE: - pinfo.pt.x = LOWORD(lParam); - pinfo.pt.y = HIWORD(lParam); - pinfo.flags = 0; - if (m_grid.SubItemHitTest(&pinfo) == -1) - break; - - if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) { - OldHSubItem = pinfo.iSubItem; - OldHItem = pinfo.iItem; - - if (g_bTipperInstalled) { - if (TTShowed) { - TTShowed = false; - Tipper_Hide(); - } - m_hover.Stop(); - - if (OldHSubItem == 0) - m_hover.Start(450); - } - - TOOLINFOW ti = {}; - ti.cbSize = sizeof(ti); - ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND; - ti.hwnd = m_hwnd; - ti.uId = LPARAM(m_hwnd); - - int num = ListViewToColumn(OldHSubItem); - auto &pCol = g_plugin.m_columns[num]; - if (pCol.isXstatus || pCol.isGender) { - auto *pRow = GetRow(OldHItem); - if (pCol.isGender) { - switch (pRow->pValues[num].data) { - case 'M': ti.lpszText = TranslateT("Male"); break; - case 'F': ti.lpszText = TranslateT("Female"); break; - default: ti.lpszText = TranslateT("Unknown"); break; - } - } - else { - wchar_t buf[256]; - mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf)); - int iStatus = _wtoi(buf); - - CUSTOM_STATUS ics = {}; - ics.cbSize = sizeof(ics); - ics.status = &iStatus; - ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE; - ics.pwszName = buf; - CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics); - ti.lpszText = TranslateW(buf); - } - } - - SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti)); - } - break; - - case WM_KEYUP: - switch (wParam) { - case VK_RETURN: - if (m_grid.GetSelectedCount() == 1) - ShowContactMsgDlg(GetFocusedContact()); - break; - - case VK_INSERT: - CallService(MS_FINDADD_FINDADD, 0, 0); - break; - - case VK_DELETE: - lParam = m_grid.GetSelectedCount(); - if (lParam > 1) - DeleteByList(); - else if (lParam == 1) - DeleteOneContact(GetFocusedContact()); - break; - - case VK_F5: - onClick_Refresh(0); - break; - } - break; - - case WM_NOTIFY: - if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) { - NMHEADER *pdhr = (NMHEADER *)lParam; - if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) { - int i = ListViewToColumn(pdhr->iItem); - auto &pCol = g_plugin.m_columns[i]; - - if (pdhr->pitem->fmt & HDF_CHECKED) { - pCol.bFilter = false; - pdhr->pitem->fmt &= ~HDF_CHECKED; - } - else { - pCol.bFilter = true; - pdhr->pitem->fmt |= HDF_CHECKED; - } - - SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem)); - FillGrid(); - return TRUE; - } - } - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// QSMainDlg class implementation - -static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2) -{ - if (p1->bAccDel != p2->bAccDel) - return (p1->bAccDel) ? 1 : -1; - - if (p1->bAccOff != p2->bAccOff) - return (p1->bAccOff) ? 1 : -1; - - return mir_strcmp(p1->szProto, p2->szProto); -} - -QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) : - CDlgBase(g_plugin, IDD_MAIN), - m_rows(50), - m_patterns(1), - m_sbdata(10, CompareSb), - m_grid(this, IDC_LIST), - m_hover(this, 10), - cmbProto(this, IDC_CB_PROTOCOLS), - edtFilter(this, IDC_E_SEARCHTEXT), - btnRefresh(this, IDC_REFRESH), - chkColorize(this, IDC_CH_COLORIZE), - chkShowOffline(this, IDC_CH_SHOWOFFLINE) -{ - SetMinSize(300, 160); - - if (pwszPattern) - m_wszPatternBuf = mir_wstrdup(pwszPattern); - else if (g_plugin.m_flags & QSO_SAVEPATTERN) - m_wszPatternBuf = g_plugin.getWStringA("pattern"); - - m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover); - - m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid); - m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid); - m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid); - m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid); - - btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh); - - cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto); - - edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter); - chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize); - chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline); -} - -bool QSMainDlg::OnInitDialog() -{ - g_pDlg = this; - mnuhandle = 0; - - SetCaption(TranslateT("Quick Search")); - - hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR); - - HMENU smenu = GetSystemMenu(m_hwnd, false); - InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr); - InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top")); - - if (g_plugin.m_flags & QSO_STAYONTOP) { - CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED); - SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } - - chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0); - - szFilterProto = nullptr; // display all protocols - if (g_plugin.m_flags & QSO_SHOWOFFLINE) - bShowOffline = true; - - chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0); - - // Window - INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE); - if (g_plugin.m_flags & QSO_TOOLSTYLE) - tmp |= WS_EX_TOOLWINDOW; - else - tmp &= ~WS_EX_TOOLWINDOW; - SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp); - - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS)); - - // ListView - m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL); - - tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP | - LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER; - if (g_plugin.m_flags & QSO_DRAWGRID) - tmp |= LVS_EX_GRIDLINES; - m_grid.SetExtendedListViewStyle(tmp); - - // ListView header - HWND header = m_grid.GetHeader(); - SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES); - - mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc); - - SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc); - - SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc); - - FillProtoCombo(); - - PrepareTable(); - - if (m_wszPatternBuf) { - edtFilter.SetText(m_wszPatternBuf); - MakePattern(m_wszPatternBuf); - } - FillGrid(); - - // Show sorting column - HDITEM hdi = {}; - hdi.mask = HDI_FORMAT; - SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi)); - if (g_plugin.m_flags & QSO_SORTASC) - hdi.fmt |= HDF_SORTUP; - else - hdi.fmt |= HDF_SORTDOWN; - SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi)); - - RECT rc = g_plugin.m_rect; - ::SnapToScreen(rc); - ::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false); - - HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0); - - TOOLINFOW ti; - ti.cbSize = sizeof(ti); - ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND; - ti.hwnd = m_hwnd; - ti.uId = UINT_PTR(m_grid.GetHwnd()); - SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti)); - - hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED); - hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED); - hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED); - return true; -} - -void QSMainDlg::OnDestroy() -{ - if (mnuhandle) - Menu_RemoveItem(mnuhandle); - - UnhookEvent(hAdd); - UnhookEvent(hDelete); - UnhookEvent(hChange); - - g_pDlg = nullptr; - - RECT rc; - GetWindowRect(m_hwnd, &rc); - CopyRect(&g_plugin.m_rect, &rc); - - // save column width/order - SaveColumnOrder(); - - g_plugin.SaveOptWnd(); - - m_grid.SetImageList(0, LVSIL_SMALL); - - if (g_plugin.m_flags & QSO_SAVEPATTERN) - g_plugin.setWString("pattern", ptrW(edtFilter.GetText())); - - m_rows.destroy(); - m_patterns.destroy(); -} - -int QSMainDlg::Resizer(UTILRESIZECONTROL *urc) -{ - switch (urc->wId) { - case IDC_REFRESH: - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - case IDC_E_SEARCHTEXT: - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - case IDC_LIST: - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - - case IDC_STATUSBAR: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// event handlers - -INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM) -{ - auto *pRow = new CRowItem(hContact, this); - m_rows.insert(pRow); - AddContactToList(hContact, pRow); - ProcessLine(pRow); - Sort(); - UpdateSB(); - return 0; -} - -INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM) -{ - int idx = -1; - CRowItem *pRow = 0; - - for (auto &it : m_rows) - if (it->hContact == hContact) { - pRow = it; - idx = m_rows.indexOf(&it); - break; - } - - if (idx == -1) - return 0; - - int iItem = FindItem(pRow); - if (iItem != -1) - m_grid.DeleteItem(iItem); - - m_rows.remove(idx); - UpdateSB(); - return 0; -} - -INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam) -{ - auto *pRow = FindRow(hContact); - if (pRow == nullptr) - return 0; - - int oldStatus = pRow->status; - int newStatus = Contact::GetStatus(hContact); - pRow->status = newStatus; - - if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE) - ChangeStatusPicture(pRow, hContact, lParam); - else if (oldStatus != ID_STATUS_OFFLINE) { - if (g_plugin.m_flags & QSO_SHOWOFFLINE) - ChangeStatusPicture(pRow, hContact, lParam); - else - ProcessLine(pRow, true); - } - else if (newStatus != ID_STATUS_OFFLINE) { - if (g_plugin.m_flags & QSO_SHOWOFFLINE) - ChangeStatusPicture(pRow, hContact, lParam); - else { - pRow->bActive = false; - m_grid.DeleteItem(FindItem(pRow)); - } - } - - if (g_plugin.m_flags & QSO_SORTBYSTATUS) - Sort(); - - UpdateSB(); - return 0; -} - -INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM) -{ - if (wParam == IDM_STAYONTOP) { - int h; HWND w; - if (g_plugin.m_flags & QSO_STAYONTOP) { - h = MF_BYCOMMAND | MF_UNCHECKED; - w = HWND_NOTOPMOST; - } - else { - h = MF_BYCOMMAND | MF_CHECKED; - w = HWND_TOPMOST; - } - CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h); - SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - g_plugin.m_flags ^= QSO_STAYONTOP; - } - return 0; -} - -INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam) -{ - if (g_bTipperInstalled) { - RECT rc; - GetWindowRect(m_grid.GetHwnd(), &rc); - - POINT pt = { LOWORD(lParam), HIWORD(lParam) }; - ClientToScreen(m_hwnd, &pt); - if (!PtInRect(&rc, pt)) { - if (TTShowed) { - TTShowed = false; - CallService(MS_TIPPER_HIDETIP, 0, 0); - } - } - - m_hover.Stop(); - } - return 0; -} - -INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM) -{ - if (wParam == VK_F5) - PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0); - return 0; -} - -void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos) -{ - int w = m_grid.GetSelectedCount(); - if (w > 1) - ShowMultiPopup(w); - else - ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y)); -} - -void QSMainDlg::onSelChange_Proto(CCtrlCombo *) -{ - LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel()); - if (lParam == -1 || lParam == 0) - szFilterProto = nullptr; - else - szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName; - - AdvancedFilter(); -} - -void QSMainDlg::onChange_Filter(CCtrlEdit *) -{ - if (!m_bInitialized) - return; - - MakePattern(ptrW(edtFilter.GetText())); - FillGrid(); -} - -void QSMainDlg::onChange_ShowOffline(CCtrlCheck *) -{ - if (chkShowOffline.IsChecked()) { - g_plugin.m_flags |= QSO_SHOWOFFLINE; - bShowOffline = true; - } - else { - g_plugin.m_flags &= ~QSO_SHOWOFFLINE; - bShowOffline = false; - } - - AdvancedFilter(); -} - -void QSMainDlg::onChange_Colorize(CCtrlCheck *) -{ - if (chkColorize.IsChecked()) - g_plugin.m_flags |= QSO_COLORIZE; - else - g_plugin.m_flags &= ~QSO_COLORIZE; - RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE); -} - -void QSMainDlg::onClick_Refresh(CCtrlButton *) -{ - m_rows.destroy(); - PrepareToFill(); - PrepareTable(true); - FillGrid(); -} - -void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev) -{ - HWND header = m_grid.GetHeader(); - - // clear sort mark - HDITEM hdi = {}; - hdi.mask = HDI_FORMAT; - SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi)); - hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); - SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi)); - - if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) { - g_plugin.m_flags |= QSO_SORTASC; - g_plugin.m_sortOrder = ev->nmlv->iSubItem; - } - else g_plugin.m_flags ^= QSO_SORTASC;; - - // set new sort mark - SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi)); - if ((g_plugin.m_flags & QSO_SORTASC) == 0) - hdi.fmt |= HDF_SORTDOWN; - else - hdi.fmt &= ~HDF_SORTUP; - SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi)); - - Sort(); -} - -void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*) -{ - ShowContactMsgDlg(GetFocusedContact()); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev) -{ - LPNMLVCUSTOMDRAW lplvcd = ev->nmcd; - HICON h; - RECT rc; - auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam; - - int result = CDRF_DODEFAULT; - switch (lplvcd->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - result = CDRF_NOTIFYITEMDRAW; - break; - - case CDDS_ITEMPREPAINT: - pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText); - result = CDRF_NOTIFYSUBITEMDRAW; - break; - - case CDDS_SUBITEM + CDDS_ITEMPREPAINT: - pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText); - { - int sub = ListViewToColumn(lplvcd->iSubItem); - auto *pCol = &g_plugin.m_columns[sub]; - if (pCol == nullptr) - break; - - if (pCol->isGender) { - m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc); - - switch (pRow->pValues[sub].data) { - case 'F': h = g_plugin.getIcon(IDI_FEMALE); break; - case 'M': h = g_plugin.getIcon(IDI_MALE); break; - default: h = 0; - } - - if (h) - DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL); - result = CDRF_SKIPDEFAULT; - } - else if (pCol->isXstatus) { - int j = _wtoi(pRow->pValues[sub].text); - if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) { - h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED); - m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc); - DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL); - } - result = CDRF_SKIPDEFAULT; - } - else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient) - result = CDRF_NOTIFYPOSTPAINT; - } - break; - - case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT: - { - int sub = ListViewToColumn(lplvcd->iSubItem); - auto *pCol = &g_plugin.m_columns[sub]; - if (pCol == nullptr) - break; - - if (pCol->isClient) { - auto *MirVerW = pRow->pValues[sub].text; - if (MirVerW && *MirVerW && g_bFingerInstalled) { - h = Finger_GetClientIcon(MirVerW, FALSE); - m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc); - DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL); - DestroyIcon(h); - } - } - result = CDRF_SKIPDEFAULT; - } - break; - } - - SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result); -} - -void QSMainDlg::onTimer_Hover(CTimer *pTimer) -{ - pTimer->Stop(); - - if (GetForegroundWindow() != m_hwnd) - return; - - auto *pRow = GetRow(OldHItem); - if (pRow == 0) - return; - - POINT pt; - GetCursorPos(&pt); - - RECT rcItem; - m_grid.GetItemRect(OldHItem, &rcItem, 0); - ScreenToClient(m_grid.GetHwnd(), &pt); - if (!PtInRect(&rcItem, pt)) - return; - - CLCINFOTIP info = {}; - info.cbSize = sizeof(info); - info.hItem = HANDLE(pRow->hContact); - Tipper_ShowTip(0, &info); - TTShowed = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CloseSrWindow(bool) -{ - if (!g_pDlg) - return false; - - g_pDlg->Close(); - return true; -} - -int OpenSrWindow(const wchar_t *pwszPattern) -{ - if (g_pDlg) { - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(g_pDlg->GetHwnd(), &wp); - if (wp.showCmd == SW_SHOWMINIMIZED) - g_pDlg->Show(SW_RESTORE); - SetForegroundWindow(g_pDlg->GetHwnd()); - return true; - } - - int count = 0; - for (auto &it : g_plugin.m_columns) - if (it->bEnabled) - count++; - - // no even one visible column - if (count == 0) - return true; - - g_plugin.LoadOptWnd(); - - auto *pDlg = new QSMainDlg(pwszPattern); - if (pDlg->PrepareToFill()) - pDlg->Create(); - else - delete pDlg; - - return true; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+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 | 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 IDM_STAYONTOP (WM_USER+1)
+
+static QSMainDlg *g_pDlg = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// edit control window procedure
+
+static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+ if (pDlg)
+ if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
+ return res;
+
+ return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_CHAR:
+ if (wParam == 27) // Escape
+ Close();
+ break;
+
+ case WM_KEYUP:
+ if (wParam == VK_RETURN) {
+ if (m_grid.GetSelectedCount() == 1)
+ ShowContactMsgDlg(GetFocusedContact());
+ return 0;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ int count = m_grid.GetItemCount();
+ int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
+ int next = -1;
+ if (count > 0) {
+ switch (wParam) {
+ case VK_UP:
+ if (current > 0)
+ next = current - 1;
+ break;
+
+ case VK_DOWN:
+ if (current < count - 1)
+ next = current + 1;
+ break;
+
+ case VK_F5:
+ onClick_Refresh(0);
+ return 0;
+
+ case VK_NEXT:
+ case VK_PRIOR:
+ int perpage = m_grid.GetCountPerPage();
+ if (wParam == VK_NEXT)
+ next = min(current + perpage, count);
+ else
+ next = max(current - perpage, 0);
+ break;
+ }
+ }
+
+ if (next >= 0) {
+ m_grid.SetItemState(-1, 0, LVIS_SELECTED);
+ m_grid.SetCurSel(next);
+ m_grid.EnsureVisible(next, FALSE);
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// list header window procedure
+
+static void MakeColumnMenu()
+{
+ HMENU hMenu = CreatePopupMenu();
+
+ for (auto &it : g_plugin.m_columns) {
+ int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
+ AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
+ }
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
+ if (id >= 100)
+ g_pDlg->ToggleColumn(id - 100);
+
+ DestroyMenu(hMenu);
+}
+
+void QSMainDlg::ToggleColumn(int col)
+{
+ auto &pCol = g_plugin.m_columns[col];
+
+ if (!pCol.bEnabled) { // show column
+ pCol.bEnabled = true;
+
+ if (!pCol.bInit) {
+ for (auto &it : m_rows)
+ it->pValues[col].LoadOneItem(it->hContact, pCol, this);
+ pCol.bInit = true;
+ }
+
+ // screen
+ int lvcol = ColumnToListView(col);
+ AddColumn(lvcol, &pCol);
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ auto *pRow = GetRow(i);
+
+ LV_ITEMW li;
+ li.iItem = i;
+ li.iSubItem = lvcol;
+ li.mask = LVIF_TEXT;
+ li.pszText = pRow->pValues[col].text;
+ if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
+ li.mask |= LVIF_IMAGE;
+ m_grid.SetItem(&li);
+ }
+ }
+ else { // hide column
+ int cnt = 0;
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ cnt++;
+
+ // keep at least one visible column (1 + this)
+ if (cnt > 2) {
+ m_grid.DeleteColumn(col);
+ pCol.bEnabled = false;
+ }
+ }
+}
+
+static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_RBUTTONUP:
+ return 0;
+
+ case WM_RBUTTONDOWN:
+ MakeColumnMenu();
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid list window procedure
+
+static int OldHSubItem = 0, OldHItem = 0;
+
+static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+ if (pDlg)
+ if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
+ return res;
+
+ return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LV_HITTESTINFO pinfo;
+
+ switch (msg) {
+ case WM_CHAR:
+ switch (wParam) { // ESC
+ case 27:
+ Close();
+ break;
+
+ case 1: // Cltr+A
+ m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
+ break;
+
+ case 3: // Ctrl-C
+ CopyMultiLines();
+ break;
+
+ case 8: // backspace
+ edtFilter.SendMsg(msg, wParam, lParam);
+ break;
+ }
+
+ if (wParam >= 32 && wParam <= 127) // letters
+ edtFilter.SendMsg(msg, wParam, lParam);
+ break;
+
+ case WM_MOUSEMOVE:
+ pinfo.pt.x = LOWORD(lParam);
+ pinfo.pt.y = HIWORD(lParam);
+ pinfo.flags = 0;
+ if (m_grid.SubItemHitTest(&pinfo) == -1)
+ break;
+
+ if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
+ OldHSubItem = pinfo.iSubItem;
+ OldHItem = pinfo.iItem;
+
+ if (g_bTipperInstalled) {
+ if (TTShowed) {
+ TTShowed = false;
+ Tipper_Hide();
+ }
+ m_hover.Stop();
+
+ if (OldHSubItem == 0)
+ m_hover.Start(450);
+ }
+
+ TOOLINFOW ti = {};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+ ti.hwnd = m_hwnd;
+ ti.uId = LPARAM(m_hwnd);
+
+ int num = ListViewToColumn(OldHSubItem);
+ auto &pCol = g_plugin.m_columns[num];
+ if (pCol.isXstatus || pCol.isGender) {
+ auto *pRow = GetRow(OldHItem);
+ if (pCol.isGender) {
+ switch (pRow->pValues[num].data) {
+ case 'M': ti.lpszText = TranslateT("Male"); break;
+ case 'F': ti.lpszText = TranslateT("Female"); break;
+ default: ti.lpszText = TranslateT("Unknown"); break;
+ }
+ }
+ else {
+ wchar_t buf[256];
+ mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
+ int iStatus = _wtoi(buf);
+
+ CUSTOM_STATUS ics = {};
+ ics.cbSize = sizeof(ics);
+ ics.status = &iStatus;
+ ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
+ ics.pwszName = buf;
+ CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
+ ti.lpszText = TranslateW(buf);
+ }
+ }
+
+ SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
+ }
+ break;
+
+ case WM_KEYUP:
+ switch (wParam) {
+ case VK_RETURN:
+ if (m_grid.GetSelectedCount() == 1)
+ ShowContactMsgDlg(GetFocusedContact());
+ break;
+
+ case VK_INSERT:
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ break;
+
+ case VK_DELETE:
+ lParam = m_grid.GetSelectedCount();
+ if (lParam > 1)
+ DeleteByList();
+ else if (lParam == 1)
+ DeleteOneContact(GetFocusedContact());
+ break;
+
+ case VK_F5:
+ onClick_Refresh(0);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
+ NMHEADER *pdhr = (NMHEADER *)lParam;
+ if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
+ int i = ListViewToColumn(pdhr->iItem);
+ auto &pCol = g_plugin.m_columns[i];
+
+ if (pdhr->pitem->fmt & HDF_CHECKED) {
+ pCol.bFilter = false;
+ pdhr->pitem->fmt &= ~HDF_CHECKED;
+ }
+ else {
+ pCol.bFilter = true;
+ pdhr->pitem->fmt |= HDF_CHECKED;
+ }
+
+ SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
+ FillGrid();
+ return TRUE;
+ }
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// QSMainDlg class implementation
+
+static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
+{
+ if (p1->bAccDel != p2->bAccDel)
+ return (p1->bAccDel) ? 1 : -1;
+
+ if (p1->bAccOff != p2->bAccOff)
+ return (p1->bAccOff) ? 1 : -1;
+
+ return mir_strcmp(p1->szProto, p2->szProto);
+}
+
+QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
+ CDlgBase(g_plugin, IDD_MAIN),
+ m_rows(50),
+ m_patterns(1),
+ m_sbdata(10, CompareSb),
+ m_grid(this, IDC_LIST),
+ m_hover(this, 10),
+ cmbProto(this, IDC_CB_PROTOCOLS),
+ edtFilter(this, IDC_E_SEARCHTEXT),
+ btnRefresh(this, IDC_REFRESH),
+ chkColorize(this, IDC_CH_COLORIZE),
+ chkShowOffline(this, IDC_CH_SHOWOFFLINE)
+{
+ SetMinSize(300, 160);
+
+ if (pwszPattern)
+ m_wszPatternBuf = mir_wstrdup(pwszPattern);
+ else if (g_plugin.m_flags & QSO_SAVEPATTERN)
+ m_wszPatternBuf = g_plugin.getWStringA("pattern");
+
+ m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
+
+ m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
+ m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
+ m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
+ m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
+
+ btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
+
+ cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
+
+ edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
+ chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
+ chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
+}
+
+bool QSMainDlg::OnInitDialog()
+{
+ g_pDlg = this;
+ mnuhandle = 0;
+
+ SetCaption(TranslateT("Quick Search"));
+
+ hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
+
+ HMENU smenu = GetSystemMenu(m_hwnd, false);
+ InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+ InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
+
+ if (g_plugin.m_flags & QSO_STAYONTOP) {
+ CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
+ SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
+
+ szFilterProto = nullptr; // display all protocols
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ bShowOffline = true;
+
+ chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
+
+ // Window
+ INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
+ if (g_plugin.m_flags & QSO_TOOLSTYLE)
+ tmp |= WS_EX_TOOLWINDOW;
+ else
+ tmp &= ~WS_EX_TOOLWINDOW;
+ SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
+
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
+
+ // ListView
+ m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
+
+ tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
+ LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
+ if (g_plugin.m_flags & QSO_DRAWGRID)
+ tmp |= LVS_EX_GRIDLINES;
+ m_grid.SetExtendedListViewStyle(tmp);
+
+ // ListView header
+ HWND header = m_grid.GetHeader();
+ SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
+
+ mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
+
+ SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
+
+ SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
+
+ FillProtoCombo();
+
+ PrepareTable();
+
+ if (m_wszPatternBuf) {
+ edtFilter.SetText(m_wszPatternBuf);
+ MakePattern(m_wszPatternBuf);
+ }
+ FillGrid();
+
+ // Show sorting column
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+ SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ if (g_plugin.m_flags & QSO_SORTASC)
+ hdi.fmt |= HDF_SORTUP;
+ else
+ hdi.fmt |= HDF_SORTDOWN;
+ SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ RECT rc = g_plugin.m_rect;
+ ::SnapToScreen(rc);
+ ::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
+
+ HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
+
+ TOOLINFOW ti;
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+ ti.hwnd = m_hwnd;
+ ti.uId = UINT_PTR(m_grid.GetHwnd());
+ SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
+
+ hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
+ hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
+ hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
+ return true;
+}
+
+void QSMainDlg::OnDestroy()
+{
+ if (mnuhandle)
+ Menu_RemoveItem(mnuhandle);
+
+ UnhookEvent(hAdd);
+ UnhookEvent(hDelete);
+ UnhookEvent(hChange);
+
+ g_pDlg = nullptr;
+
+ RECT rc;
+ GetWindowRect(m_hwnd, &rc);
+ CopyRect(&g_plugin.m_rect, &rc);
+
+ // save column width/order
+ SaveColumnOrder();
+
+ g_plugin.SaveOptWnd();
+
+ m_grid.SetImageList(0, LVSIL_SMALL);
+
+ if (g_plugin.m_flags & QSO_SAVEPATTERN)
+ g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
+
+ m_rows.destroy();
+ m_patterns.destroy();
+}
+
+int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId) {
+ case IDC_REFRESH:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ case IDC_E_SEARCHTEXT:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ case IDC_LIST:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDC_STATUSBAR:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// event handlers
+
+INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
+{
+ auto *pRow = new CRowItem(hContact, this);
+ m_rows.insert(pRow);
+ AddContactToList(hContact, pRow);
+ ProcessLine(pRow);
+ Sort();
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
+{
+ int idx = -1;
+ CRowItem *pRow = 0;
+
+ for (auto &it : m_rows)
+ if (it->hContact == hContact) {
+ pRow = it;
+ idx = m_rows.indexOf(&it);
+ break;
+ }
+
+ if (idx == -1)
+ return 0;
+
+ int iItem = FindItem(pRow);
+ if (iItem != -1)
+ m_grid.DeleteItem(iItem);
+
+ m_rows.remove(idx);
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
+{
+ auto *pRow = FindRow(hContact);
+ if (pRow == nullptr)
+ return 0;
+
+ int oldStatus = pRow->status;
+ int newStatus = Contact::GetStatus(hContact);
+ pRow->status = newStatus;
+
+ if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else if (oldStatus != ID_STATUS_OFFLINE) {
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else
+ ProcessLine(pRow, true);
+ }
+ else if (newStatus != ID_STATUS_OFFLINE) {
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else {
+ pRow->bActive = false;
+ m_grid.DeleteItem(FindItem(pRow));
+ }
+ }
+
+ if (g_plugin.m_flags & QSO_SORTBYSTATUS)
+ Sort();
+
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
+{
+ if (wParam == IDM_STAYONTOP) {
+ int h; HWND w;
+ if (g_plugin.m_flags & QSO_STAYONTOP) {
+ h = MF_BYCOMMAND | MF_UNCHECKED;
+ w = HWND_NOTOPMOST;
+ }
+ else {
+ h = MF_BYCOMMAND | MF_CHECKED;
+ w = HWND_TOPMOST;
+ }
+ CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
+ SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ g_plugin.m_flags ^= QSO_STAYONTOP;
+ }
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
+{
+ if (g_bTipperInstalled) {
+ RECT rc;
+ GetWindowRect(m_grid.GetHwnd(), &rc);
+
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ ClientToScreen(m_hwnd, &pt);
+ if (!PtInRect(&rc, pt)) {
+ if (TTShowed) {
+ TTShowed = false;
+ CallService(MS_TIPPER_HIDETIP, 0, 0);
+ }
+ }
+
+ m_hover.Stop();
+ }
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
+{
+ if (wParam == VK_F5)
+ PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
+ return 0;
+}
+
+void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
+{
+ int w = m_grid.GetSelectedCount();
+ if (w > 1)
+ ShowMultiPopup(w);
+ else
+ ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
+}
+
+void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
+{
+ LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
+ if (lParam == -1 || lParam == 0)
+ szFilterProto = nullptr;
+ else
+ szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
+
+ AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Filter(CCtrlEdit *)
+{
+ if (!m_bInitialized)
+ return;
+
+ MakePattern(ptrW(edtFilter.GetText()));
+ FillGrid();
+}
+
+void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
+{
+ if (chkShowOffline.IsChecked()) {
+ g_plugin.m_flags |= QSO_SHOWOFFLINE;
+ bShowOffline = true;
+ }
+ else {
+ g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
+ bShowOffline = false;
+ }
+
+ AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Colorize(CCtrlCheck *)
+{
+ if (chkColorize.IsChecked())
+ g_plugin.m_flags |= QSO_COLORIZE;
+ else
+ g_plugin.m_flags &= ~QSO_COLORIZE;
+ RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
+}
+
+void QSMainDlg::onClick_Refresh(CCtrlButton *)
+{
+ m_rows.destroy();
+ PrepareToFill();
+ PrepareTable(true);
+ FillGrid();
+}
+
+void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
+{
+ HWND header = m_grid.GetHeader();
+
+ // clear sort mark
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+ SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
+ SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
+ g_plugin.m_flags |= QSO_SORTASC;
+ g_plugin.m_sortOrder = ev->nmlv->iSubItem;
+ }
+ else g_plugin.m_flags ^= QSO_SORTASC;;
+
+ // set new sort mark
+ SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ if ((g_plugin.m_flags & QSO_SORTASC) == 0)
+ hdi.fmt |= HDF_SORTDOWN;
+ else
+ hdi.fmt &= ~HDF_SORTUP;
+ SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ Sort();
+}
+
+void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
+{
+ ShowContactMsgDlg(GetFocusedContact());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
+{
+ LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
+ HICON h;
+ RECT rc;
+ auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
+
+ int result = CDRF_DODEFAULT;
+ switch (lplvcd->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ result = CDRF_NOTIFYITEMDRAW;
+ break;
+
+ case CDDS_ITEMPREPAINT:
+ pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+ result = CDRF_NOTIFYSUBITEMDRAW;
+ break;
+
+ case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
+ pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+ {
+ int sub = ListViewToColumn(lplvcd->iSubItem);
+ auto *pCol = &g_plugin.m_columns[sub];
+ if (pCol == nullptr)
+ break;
+
+ if (pCol->isGender) {
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+
+ switch (pRow->pValues[sub].data) {
+ case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
+ case 'M': h = g_plugin.getIcon(IDI_MALE); break;
+ default: h = 0;
+ }
+
+ if (h)
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ result = CDRF_SKIPDEFAULT;
+ }
+ else if (pCol->isXstatus) {
+ int j = _wtoi(pRow->pValues[sub].text);
+ if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
+ h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ }
+ result = CDRF_SKIPDEFAULT;
+ }
+ else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
+ result = CDRF_NOTIFYPOSTPAINT;
+ }
+ break;
+
+ case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
+ {
+ int sub = ListViewToColumn(lplvcd->iSubItem);
+ auto *pCol = &g_plugin.m_columns[sub];
+ if (pCol == nullptr)
+ break;
+
+ if (pCol->isClient) {
+ auto *MirVerW = pRow->pValues[sub].text;
+ if (MirVerW && *MirVerW && g_bFingerInstalled) {
+ h = Finger_GetClientIcon(MirVerW, FALSE);
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ DestroyIcon(h);
+ }
+ }
+ result = CDRF_SKIPDEFAULT;
+ }
+ break;
+ }
+
+ SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
+}
+
+void QSMainDlg::onTimer_Hover(CTimer *pTimer)
+{
+ pTimer->Stop();
+
+ if (GetForegroundWindow() != m_hwnd)
+ return;
+
+ auto *pRow = GetRow(OldHItem);
+ if (pRow == 0)
+ return;
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rcItem;
+ m_grid.GetItemRect(OldHItem, &rcItem, 0);
+ ScreenToClient(m_grid.GetHwnd(), &pt);
+ if (!PtInRect(&rcItem, pt))
+ return;
+
+ CLCINFOTIP info = {};
+ info.cbSize = sizeof(info);
+ info.hItem = HANDLE(pRow->hContact);
+ Tipper_ShowTip(0, &info);
+ TTShowed = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CloseSrWindow(bool)
+{
+ if (!g_pDlg)
+ return false;
+
+ g_pDlg->Close();
+ return true;
+}
+
+int OpenSrWindow(const wchar_t *pwszPattern)
+{
+ if (g_pDlg) {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
+ if (wp.showCmd == SW_SHOWMINIMIZED)
+ g_pDlg->Show(SW_RESTORE);
+ SetForegroundWindow(g_pDlg->GetHwnd());
+ return true;
+ }
+
+ int count = 0;
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ count++;
+
+ // no even one visible column
+ if (count == 0)
+ return true;
+
+ g_plugin.LoadOptWnd();
+
+ auto *pDlg = new QSMainDlg(pwszPattern);
+ if (pDlg->PrepareToFill())
+ pDlg->Create();
+ else
+ delete pDlg;
+
+ return true;
+}
diff --git a/plugins/QuickSearch/src/window_misc.cpp b/plugins/QuickSearch/src/window_misc.cpp index e6b5e23341..58d96561fc 100644 --- a/plugins/QuickSearch/src/window_misc.cpp +++ b/plugins/QuickSearch/src/window_misc.cpp @@ -1,875 +1,875 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it &/| -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 | 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" - -///////////////////////////////////////////////////////////////////////////////////////// -// patterns - -bool QSMainDlg::CheckPattern(CRowItem *pRow) -{ - if (m_patterns.getCount() == 0) - return true; - - for (auto &p : m_patterns) - p->res = 0; - - int i = 0; - for (auto &it : g_plugin.m_columns) { - if (it->bEnabled && it->bFilter && pRow->pValues[i].text != nullptr) { - CMStringW buf(pRow->pValues[i].text); - buf.MakeLower(); - - for (auto &p : m_patterns) - if (!p->res && buf.Find(p->str) != -1) - p->res = true; - } - i++; - } - - bool result = true; - for (auto &p : m_patterns) - result = result && p->res; - return result; -} - -void QSMainDlg::MakePattern(const wchar_t *pwszPattern) -{ - m_patterns.destroy(); - if (mir_wstrlen(pwszPattern) == 0) - return; - - // m_wszPatternBuf works as a storage for patterns, we store pointers to it in m_patterns - m_wszPatternBuf = mir_wstrdup(pwszPattern); - CharLowerW(m_wszPatternBuf); - - for (wchar_t *p = m_wszPatternBuf; *p; ) { - auto *pWord = wcspbrk(p, L" \""); - if (pWord == nullptr) { - m_patterns.insert(new Pattern(p)); - return; - } - - bool isSpace = pWord[0] == ' '; - - // there's some valuable info between p and pWord - if (pWord != p) { - *pWord = 0; - m_patterns.insert(new Pattern(p)); - } - - if (isSpace) { - p = ltrimpw(pWord + 1); // skip all spaces - } - else { - auto *pEnd = wcschr(++pWord, '\"'); - - // treat the rest of line as one pattern - if (pEnd == nullptr) { - m_patterns.insert(new Pattern(pWord)); - return; - } - - *pEnd = 0; - m_patterns.insert(new Pattern(pWord)); - p = ltrimpw(pEnd + 1); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void QSMainDlg::AddColumn(int idx, ColumnItem *pCol) -{ - LV_COLUMN lvcol = {}; - lvcol.mask = LVCF_TEXT | LVCF_WIDTH; - lvcol.pszText = TranslateW(pCol->title); - lvcol.cx = pCol->width; - m_grid.InsertColumn(idx, &lvcol); - - HDITEM hdi; - hdi.mask = HDI_FORMAT; - if (pCol->bFilter) - hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED; - else - hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX; - SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi)); -} - -void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow) -{ - LV_ITEMW li = {}; - li.mask = LVIF_IMAGE | LVIF_PARAM; - li.iItem = 100000; - li.iImage = Clist_GetContactIcon(hContact); - li.lParam = LPARAM(pRow); - - li.iItem = m_grid.InsertItem(&li); - li.iImage = 0; - li.iSubItem = 0; - - for (int i = 0; i < g_plugin.m_columns.getCount(); i++) { - auto &col = g_plugin.m_columns[i]; - if (!col.bEnabled) - continue; - - // Client icons preprocess - li.pszText = pRow->pValues[i].text; - li.mask = LVIF_TEXT; - if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender) - li.mask |= LVIF_IMAGE; - m_grid.SetItem(&li); - li.iSubItem++; - } -} - -void QSMainDlg::AdvancedFilter() -{ - m_grid.SetDraw(false); - - for (auto &it : m_rows) { - bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto); - if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE) - bShow = false; - - if (it->bPattern) { - if (bShow) { - if (!it->bActive) - ProcessLine(it, false); - } - else { - it->bActive = false; - m_grid.DeleteItem(FindItem(it)); - } - } - } - - m_grid.SetDraw(true); - InvalidateRect(m_grid.GetHwnd(), 0, false); - - Sort(); - UpdateSB(); -} - -void QSMainDlg::ChangeStatusPicture(CRowItem *pRow, MCONTACT, LPARAM lParam) -{ - int idx = FindItem(pRow); - if (idx == -1) - return; - - LV_ITEMW li = {}; - li.iItem = idx; - li.mask = LVIF_IMAGE; - li.iImage = lParam; //CallService(MS_CLIST_GETCONTACTICON,hContact,0); - m_grid.SetItem(&li); -} - -void QSMainDlg::CopyMultiLines() -{ - CMStringW buf; - - int i = 0; - for (auto &it : g_plugin.m_columns) { - if (it->bEnabled) { - it->width = m_grid.GetColumnWidth(i); - if (it->width >= 10) - buf.AppendFormat(L"%s\t", it->title); - } - i++; - } - buf.Append(L"\r\n"); - - int nRows = m_grid.GetItemCount(); - int nSelected = m_grid.GetSelectedCount(); - - for (int j = 0; j < nRows; j++) { - if (nSelected > 1 && !m_grid.GetItemState(j, LVIS_SELECTED)) - continue; - - auto *pRow = GetRow(j); - - i = 0; - for (auto &it : g_plugin.m_columns) { - if (it->bEnabled && it->width >= 10) - buf.AppendFormat(L"%s\t", pRow->pValues[i].getText()); - i++; - } - buf.Append(L"\r\n"); - } - - Utils_ClipboardCopy(buf); -} - -void QSMainDlg::DeleteByList() -{ - if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts?"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING)) - return; - - m_grid.SetDraw(false); - - for (int i = m_grid.GetItemCount() - 1; i >= 0; i--) - if (m_grid.GetItemState(i, LVIS_SELECTED)) - db_delete_contact(GetRow(i)->hContact); - - m_grid.SetDraw(true); -} - -void QSMainDlg::DeleteOneContact(MCONTACT hContact) -{ - if (ServiceExists(MS_CLIST_DELETECONTACT)) - CallService(MS_CLIST_DELETECONTACT, hContact, 0); - else - db_delete_contact(hContact); -} - -wchar_t* QSMainDlg::DoMeta(MCONTACT hContact) -{ - for (auto &it : m_rows) { - if (it->hContact != hContact) - continue; - - if (it->bIsMeta) { - if (it->wparam == 0) - it->wparam = ++hLastMeta; - } - else if (it->bIsSub) - it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam); - - if (it->wparam > 0) { - CMStringW tmp(FORMAT, L"[%d]", int(it->wparam)); - if (it->lparam > 0) - tmp.AppendFormat(L" %d", int(it->lparam)); - return tmp.Detach(); - } - break; - } - - return nullptr; -} - -void QSMainDlg::DrawSB() -{ - CStatusBarItem global(0, 0); - for (auto &it : m_sbdata) { - global.found += it->found; - global.liston += it->liston; - global.online += it->online; - global.total += it->total; - } - - CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online); - - RECT rc; - HDC hdc = GetDC(hwndStatusBar); - DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT); - ReleaseDC(hwndStatusBar, hdc); - - int all = rc.right - rc.left, i = 1; - - mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2))); - parts[0] = all; - for (auto &it : m_sbdata) { - UNREFERENCED_PARAMETER(it); - all += 55; - parts[i++] = all; - } - parts[i] = -1; - SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get())); - SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str())); - - i = 1; - for (auto &it : m_sbdata) { - HICON hIcon; - wchar_t c, *pc; - if (it->bAccDel) { - c = '!'; - pc = TranslateT("deleted"); - hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE); - } - else if (it->bAccOff) { - c = '?'; - pc = TranslateT("disabled"); - hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE); - } - else { - c = ' '; - pc = TranslateT("active"); - hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE); - } - - SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon); - - buf.Format(L"%c %d", c, it->found); - SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str())); - - auto *pa = Proto_GetAccount(it->szProto); - buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online); - SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str())); - i++; - } -} - -void QSMainDlg::FillGrid() -{ - m_grid.SetDraw(false); - - for (auto &it: m_rows) - ProcessLine(it); - - m_grid.SetDraw(true); - InvalidateRect(m_grid.GetHwnd(), 0, FALSE); - - Sort(); - UpdateSB(); - AdvancedFilter(); - - m_grid.SetCurSel(0); -} - -void QSMainDlg::FillProtoCombo() -{ - cmbProto.ResetContent(); - cmbProto.AddString(TranslateT("All")); - - for (auto &it : Accounts()) - cmbProto.AddString(it->tszAccountName, (LPARAM)it); - - cmbProto.SetCurSel(0); -} - -int QSMainDlg::FindItem(CRowItem *pRow) -{ - if (pRow == nullptr) - return -1; - - LV_FINDINFO fi = {}; - fi.flags = LVFI_PARAM; - fi.lParam = LPARAM(pRow); - return m_grid.FindItem(-1, &fi); -} - -int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum) -{ - for (auto &it : m_rows) { - if (it->hContact != hMeta) - continue; - - // new meta - if (it->wparam == 0) { - it->wparam = ++hLastMeta; - it->lparam = 0; - } - metaNum = it->wparam; - it->lparam++; - return it->lparam; - } - - return 0; -} - -CRowItem* QSMainDlg::FindRow(MCONTACT hContact) -{ - for (auto &it : m_rows) - if (it->hContact == hContact) - return it; - - return nullptr; -} - -MCONTACT QSMainDlg::GetFocusedContact() -{ - int idx = m_grid.GetSelectionMark(); - if (idx == -1) - return -1; - - INT_PTR data = m_grid.GetItemData(idx); - return (data == -1) ? -1 : ((CRowItem *)data)->hContact; -} - -int QSMainDlg::GetLVSubItem(int x, int y) -{ - LV_HITTESTINFO info = {}; - info.pt.x = x; - info.pt.y = y; - ScreenToClient(m_grid.GetHwnd(), &info.pt); - if (m_grid.SubItemHitTest(&info) == -1) - return -1; - - return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1; -} - -void QSMainDlg::PrepareTable(bool bReset) -{ - m_grid.DeleteAllItems(); - - HDITEM hdi = {}; - hdi.mask = HDI_FORMAT; - - int old = tableColumns; - tableColumns = 0; - - LV_COLUMN lvc = {}; - lvc.mask = LVCF_TEXT | LVCF_WIDTH; - for (auto &it : g_plugin.m_columns) { - if (it->bEnabled) - AddColumn(tableColumns++, it); - - it->SetSpecialColumns(); - } - - if (bReset) - for (int i = old + tableColumns - 1; i >= tableColumns; i--) - m_grid.DeleteColumn(i); -} - -bool QSMainDlg::PrepareToFill() -{ - if (g_plugin.m_columns.getCount() == 0) - return false; - - for (auto &it : g_plugin.m_columns) - if (it->bEnabled) - it->bInit = true; - - hLastMeta = 0; - - m_rows.destroy(); - for (auto &hContact : Contacts()) - m_rows.insert(new CRowItem(hContact, this)); - - return m_rows.getCount() != 0; -} - -void QSMainDlg::ProcessLine(CRowItem *pRow, bool test) -{ - if (pRow->bDeleted) - return; - - if (test) - pRow->bPattern = CheckPattern(pRow); - - if (pRow->bPattern) { - if (!pRow->bActive) { - if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) { - // check for proto in combo - if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) { - pRow->bActive = true; - AddContactToList(pRow->hContact, pRow); - } - } - } - } - else if (pRow->bActive) { - pRow->bActive = false; - m_grid.DeleteItem(FindItem(pRow)); - } -} - -void QSMainDlg::SaveColumnOrder() -{ - int idx = 0, col = 0; - for (auto &it : g_plugin.m_columns) { - if (it->bEnabled) { - it->width = m_grid.GetColumnWidth(col++); - g_plugin.SaveColumn(idx, *it); - } - idx++; - } -} - -void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact) -{ - if (hContact) { - Clist_ContactDoubleClicked(hContact); - if (g_plugin.m_flags & QSO_AUTOCLOSE) - Close(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// contact menu - -static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param) -{ - ((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param); - return 0; -} - -void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col) -{ - auto &pCol = g_plugin.m_columns[col]; - - auto *pRow = FindRow(hContact); - if (pRow == nullptr) - return; - - const char *szModule = pCol.module; - if (szModule == nullptr) - szModule = pRow->szProto; - - auto &pVal = pRow->pValues[col]; - CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title); - - ENTER_STRING es = {}; - es.szModuleName = MODULENAME; - es.caption = TranslateT("Enter new cell value"); - es.ptszInitVal = pVal.text; - if (!EnterString(&es)) - return; - - replaceStrW(pVal.text, es.ptszResult); - if (pCol.datatype != QSTS_STRING) - pVal.data = _wtoi(pVal.text); - - switch (pCol.datatype) { - case QSTS_BYTE: - db_set_b(hContact, szModule, pCol.setting, pVal.data); - break; - case QSTS_WORD: - db_set_w(hContact, szModule, pCol.setting, pVal.data); - break; - case QSTS_DWORD: - case QSTS_SIGNED: - case QSTS_HEXNUM: - db_set_dw(hContact, szModule, pCol.setting, pVal.data); - break; - - case QSTS_STRING: - db_set_ws(hContact, szModule, pCol.setting, pVal.text); - break; - } - - UpdateLVCell(FindItem(pRow), col, pVal.text); -} - -void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col) -{ - if (hContact == 0) - return; - - HANDLE srvhandle = 0; - - bool bDoit = false; - if (col >= 0) { - if ((col = ListViewToColumn(col)) == -1) - return; - - auto &pCol = g_plugin.m_columns[col]; - if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) { - bDoit = true; - - srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col); - - if (mnuhandle == nullptr) { - CMenuItem mi(&g_plugin); - SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81); - mi.name.a = LPGEN("Change setting through QS"); - mi.pszService = "QS/Dummy"; - mnuhandle = Menu_AddContactMenuItem(&mi); - } - else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0); - } - } - - POINT pt; - GetCursorPos(&pt); - HMENU hMenu = Menu_BuildContactMenu(hContact); - if (hMenu) { - int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0); - if (iCmd) { - if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) { - if (g_plugin.m_flags & QSO_AUTOCLOSE) - CloseSrWindow(); - } - } - - ::DestroyMenu(hMenu); - } - - if (srvhandle) - DestroyServiceFunction(srvhandle); - if (bDoit) - Menu_ShowItem(mnuhandle, false); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// muptiple selection popup menu - -static HMENU MakeContainerMenu() -{ - HMENU hMenu = CreatePopupMenu(); - - for (int i = 0;; i++) { - char setting[10]; - _itoa_s(i, setting, 10); - ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting)); - if (wszName != nullptr) - AppendMenuW(hMenu, MF_STRING, 300 + i, wszName); - else - break; - } - - return hMenu; -} - -void QSMainDlg::ShowMultiPopup(int cnt) -{ - HMENU hMenu = CreatePopupMenu(); - AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt)); - AppendMenuW(hMenu, MF_SEPARATOR, 0, 0); - AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete")); - AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy")); - AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta")); - - HMENU cntmenu = MakeContainerMenu(); - AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container")); - - if (HMENU grpmenu = Clist_GroupBuildMenu(400)) - AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group")); - - POINT pt; - GetCursorPos(&pt); - - int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0); - switch (iRes) { - case 101: - DeleteByList(); - break; - - case 102: - CopyMultiLines(); - break; - - case 103: - ConvertToMeta(); - break; - } - - if (iRes >= 300 && iRes <= 399) { - wchar_t buf[100]; - if (iRes == 300) // default container, just delete setting - buf[0] = 0; - else - GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND); - - MoveToContainer(buf); - } - else if (iRes >= 400 && iRes <= 499) - MoveToGroup(Clist_GroupGetName(iRes - 400)); -} - -void QSMainDlg::ConvertToMeta() -{ - MCONTACT hMeta = 0; - - int nCount = m_grid.GetItemCount(); - for (int i = 0; i < nCount; i++) { - if (!m_grid.GetItemState(i, LVIS_SELECTED)) - continue; - - auto *pRow = GetRow(i); - if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) { - if (hMeta == 0) - hMeta = tmp; - else if (hMeta != tmp) { - MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR); - return; - } - } - } - - if (hMeta != 0) - if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING)) - return; - - for (int i = 0; i < nCount; i++) { - if (!m_grid.GetItemState(i, LVIS_SELECTED)) - continue; - - auto *pRow = GetRow(i); - if (hMeta) - db_mc_addToMeta(pRow->hContact, hMeta); - else - db_mc_convertToMeta(pRow->hContact); - } -} - -void QSMainDlg::MoveToContainer(const wchar_t *pwszName) -{ - int grcol = -1; - for (auto &it : g_plugin.m_columns) { - if (it->isContainer) { - if (it->bEnabled) - grcol = g_plugin.m_columns.indexOf(&it); - else - it->bInit = false; - } - } - - int nCount = m_grid.GetItemCount(); - for (int i = 0; i < nCount; i++) { - if (!m_grid.GetItemState(i, LVIS_SELECTED)) - continue; - - auto *pRow = GetRow(i); - if (*pwszName == 0) - db_unset(pRow->hContact, "Tab_SRMsg", "containerW"); - else - db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName); - - if (grcol != -1) { - auto &pVal = pRow->pValues[grcol]; - replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr); - UpdateLVCell(i, grcol, pwszName); - } - } -} - -void QSMainDlg::MoveToGroup(const wchar_t *pwszName) -{ - int grcol = -1; - for (auto &it : g_plugin.m_columns) { - if (it->isGroup) { - if (it->bEnabled) - grcol = g_plugin.m_columns.indexOf(&it); - else - it->bInit = false; - } - } - - int nCount = m_grid.GetItemCount(); - for (int i = 0; i < nCount; i++) { - if (!m_grid.GetItemState(i, LVIS_SELECTED)) - continue; - - auto *pRow = GetRow(i); - Clist_SetGroup(pRow->hContact, pwszName); - - if (grcol != -1) { - auto &pVal = pRow->pValues[grcol]; - replaceStrW(pVal.text, pwszName); - UpdateLVCell(i, grcol, pwszName); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// grid sorting - -static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type) -{ - bool typ1, typ2; - UINT_PTR i1, i2; - int result = 0; - CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2; - - if (type == StatusSort) { - i1 = r1->status, i2 = r2->status; - if (i1 == ID_STATUS_OFFLINE) i1 += 64; - if (i2 == ID_STATUS_OFFLINE) i2 += 64; - typ1 = typ2 = false; - } - else { - auto &res1 = r1->pValues[type], &res2 = r2->pValues[type]; - i1 = res1.data, i2 = res2.data; - typ1 = i1 == -1; typ2 = i1 == -1; - - if (typ1 && typ2) { // two strings - if (res1.text == 0 && res2.text == 0) - result = 0; - else if (res2.text == 0) - result = 1; - else if (res1.text == 0) - result = -1; - else - result = lstrcmpiW(res1.text, res2.text); - } - else if (typ1 || typ2) // string & num - result = (typ1) ? 1 : -1; - } - - if (!typ1 && !typ2) { // not strings - if (i1 > i2) - result = 1; - else if (i1 < i2) - result = -1; - else - result = 0; - } - - if (g_plugin.m_flags & QSO_SORTASC) - result = -result; - return result; -} - -void QSMainDlg::Sort() -{ - if (g_plugin.m_sortOrder >= tableColumns) - g_plugin.m_sortOrder = StatusSort; - m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder)); - - if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS)) - m_grid.SortItems(&CompareItem, StatusSort); -} - -void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText) -{ - auto &pCol = g_plugin.m_columns[column]; - auto *pRow = GetRow(item); - - if (pwszText == nullptr) { - auto &pVal = pRow->pValues[column]; - replaceStrW(pVal.text, 0); - pVal.LoadOneItem(pRow->hContact, pCol, this); - pwszText = pVal.text; - } - - m_grid.SetItemText(item, ColumnToListView(column), pwszText); - - if (pCol.bFilter) - ProcessLine(pRow, true); - if (g_plugin.m_sortOrder == column) - Sort(); -} - -void QSMainDlg::UpdateSB() -{ - m_sbdata.destroy(); - - for (auto &it : m_rows) { - if (it->szProto == nullptr) - continue; - - CStatusBarItem tmp(it->szProto, it->flags); - auto *pItem = m_sbdata.find(&tmp); - if (pItem == nullptr) - m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags)); - - pItem->total++; - - if (it->bActive) - pItem->found++; - - if (it->status != ID_STATUS_OFFLINE) { - pItem->online++; - if (it->bActive) - pItem->liston++; - } - } - - DrawSB(); -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+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 | 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"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// patterns
+
+bool QSMainDlg::CheckPattern(CRowItem *pRow)
+{
+ if (m_patterns.getCount() == 0)
+ return true;
+
+ for (auto &p : m_patterns)
+ p->res = 0;
+
+ int i = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled && it->bFilter && pRow->pValues[i].text != nullptr) {
+ CMStringW buf(pRow->pValues[i].text);
+ buf.MakeLower();
+
+ for (auto &p : m_patterns)
+ if (!p->res && buf.Find(p->str) != -1)
+ p->res = true;
+ }
+ i++;
+ }
+
+ bool result = true;
+ for (auto &p : m_patterns)
+ result = result && p->res;
+ return result;
+}
+
+void QSMainDlg::MakePattern(const wchar_t *pwszPattern)
+{
+ m_patterns.destroy();
+ if (mir_wstrlen(pwszPattern) == 0)
+ return;
+
+ // m_wszPatternBuf works as a storage for patterns, we store pointers to it in m_patterns
+ m_wszPatternBuf = mir_wstrdup(pwszPattern);
+ CharLowerW(m_wszPatternBuf);
+
+ for (wchar_t *p = m_wszPatternBuf; *p; ) {
+ auto *pWord = wcspbrk(p, L" \"");
+ if (pWord == nullptr) {
+ m_patterns.insert(new Pattern(p));
+ return;
+ }
+
+ bool isSpace = pWord[0] == ' ';
+
+ // there's some valuable info between p and pWord
+ if (pWord != p) {
+ *pWord = 0;
+ m_patterns.insert(new Pattern(p));
+ }
+
+ if (isSpace) {
+ p = ltrimpw(pWord + 1); // skip all spaces
+ }
+ else {
+ auto *pEnd = wcschr(++pWord, '\"');
+
+ // treat the rest of line as one pattern
+ if (pEnd == nullptr) {
+ m_patterns.insert(new Pattern(pWord));
+ return;
+ }
+
+ *pEnd = 0;
+ m_patterns.insert(new Pattern(pWord));
+ p = ltrimpw(pEnd + 1);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::AddColumn(int idx, ColumnItem *pCol)
+{
+ LV_COLUMN lvcol = {};
+ lvcol.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvcol.pszText = TranslateW(pCol->title);
+ lvcol.cx = pCol->width;
+ m_grid.InsertColumn(idx, &lvcol);
+
+ HDITEM hdi;
+ hdi.mask = HDI_FORMAT;
+ if (pCol->bFilter)
+ hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED;
+ else
+ hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX;
+ SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi));
+}
+
+void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow)
+{
+ LV_ITEMW li = {};
+ li.mask = LVIF_IMAGE | LVIF_PARAM;
+ li.iItem = 100000;
+ li.iImage = Clist_GetContactIcon(hContact);
+ li.lParam = LPARAM(pRow);
+
+ li.iItem = m_grid.InsertItem(&li);
+ li.iImage = 0;
+ li.iSubItem = 0;
+
+ for (int i = 0; i < g_plugin.m_columns.getCount(); i++) {
+ auto &col = g_plugin.m_columns[i];
+ if (!col.bEnabled)
+ continue;
+
+ // Client icons preprocess
+ li.pszText = pRow->pValues[i].text;
+ li.mask = LVIF_TEXT;
+ if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender)
+ li.mask |= LVIF_IMAGE;
+ m_grid.SetItem(&li);
+ li.iSubItem++;
+ }
+}
+
+void QSMainDlg::AdvancedFilter()
+{
+ m_grid.SetDraw(false);
+
+ for (auto &it : m_rows) {
+ bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto);
+ if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE)
+ bShow = false;
+
+ if (it->bPattern) {
+ if (bShow) {
+ if (!it->bActive)
+ ProcessLine(it, false);
+ }
+ else {
+ it->bActive = false;
+ m_grid.DeleteItem(FindItem(it));
+ }
+ }
+ }
+
+ m_grid.SetDraw(true);
+ InvalidateRect(m_grid.GetHwnd(), 0, false);
+
+ Sort();
+ UpdateSB();
+}
+
+void QSMainDlg::ChangeStatusPicture(CRowItem *pRow, MCONTACT, LPARAM lParam)
+{
+ int idx = FindItem(pRow);
+ if (idx == -1)
+ return;
+
+ LV_ITEMW li = {};
+ li.iItem = idx;
+ li.mask = LVIF_IMAGE;
+ li.iImage = lParam; //CallService(MS_CLIST_GETCONTACTICON,hContact,0);
+ m_grid.SetItem(&li);
+}
+
+void QSMainDlg::CopyMultiLines()
+{
+ CMStringW buf;
+
+ int i = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled) {
+ it->width = m_grid.GetColumnWidth(i);
+ if (it->width >= 10)
+ buf.AppendFormat(L"%s\t", it->title);
+ }
+ i++;
+ }
+ buf.Append(L"\r\n");
+
+ int nRows = m_grid.GetItemCount();
+ int nSelected = m_grid.GetSelectedCount();
+
+ for (int j = 0; j < nRows; j++) {
+ if (nSelected > 1 && !m_grid.GetItemState(j, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(j);
+
+ i = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled && it->width >= 10)
+ buf.AppendFormat(L"%s\t", pRow->pValues[i].getText());
+ i++;
+ }
+ buf.Append(L"\r\n");
+ }
+
+ Utils_ClipboardCopy(buf);
+}
+
+void QSMainDlg::DeleteByList()
+{
+ if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts?"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING))
+ return;
+
+ m_grid.SetDraw(false);
+
+ for (int i = m_grid.GetItemCount() - 1; i >= 0; i--)
+ if (m_grid.GetItemState(i, LVIS_SELECTED))
+ db_delete_contact(GetRow(i)->hContact);
+
+ m_grid.SetDraw(true);
+}
+
+void QSMainDlg::DeleteOneContact(MCONTACT hContact)
+{
+ if (ServiceExists(MS_CLIST_DELETECONTACT))
+ CallService(MS_CLIST_DELETECONTACT, hContact, 0);
+ else
+ db_delete_contact(hContact);
+}
+
+wchar_t* QSMainDlg::DoMeta(MCONTACT hContact)
+{
+ for (auto &it : m_rows) {
+ if (it->hContact != hContact)
+ continue;
+
+ if (it->bIsMeta) {
+ if (it->wparam == 0)
+ it->wparam = ++hLastMeta;
+ }
+ else if (it->bIsSub)
+ it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam);
+
+ if (it->wparam > 0) {
+ CMStringW tmp(FORMAT, L"[%d]", int(it->wparam));
+ if (it->lparam > 0)
+ tmp.AppendFormat(L" %d", int(it->lparam));
+ return tmp.Detach();
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+void QSMainDlg::DrawSB()
+{
+ CStatusBarItem global(0, 0);
+ for (auto &it : m_sbdata) {
+ global.found += it->found;
+ global.liston += it->liston;
+ global.online += it->online;
+ global.total += it->total;
+ }
+
+ CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online);
+
+ RECT rc;
+ HDC hdc = GetDC(hwndStatusBar);
+ DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT);
+ ReleaseDC(hwndStatusBar, hdc);
+
+ int all = rc.right - rc.left, i = 1;
+
+ mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2)));
+ parts[0] = all;
+ for (auto &it : m_sbdata) {
+ UNREFERENCED_PARAMETER(it);
+ all += 55;
+ parts[i++] = all;
+ }
+ parts[i] = -1;
+ SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get()));
+ SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str()));
+
+ i = 1;
+ for (auto &it : m_sbdata) {
+ HICON hIcon;
+ wchar_t c, *pc;
+ if (it->bAccDel) {
+ c = '!';
+ pc = TranslateT("deleted");
+ hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
+ }
+ else if (it->bAccOff) {
+ c = '?';
+ pc = TranslateT("disabled");
+ hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
+ }
+ else {
+ c = ' ';
+ pc = TranslateT("active");
+ hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE);
+ }
+
+ SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon);
+
+ buf.Format(L"%c %d", c, it->found);
+ SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str()));
+
+ auto *pa = Proto_GetAccount(it->szProto);
+ buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online);
+ SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str()));
+ i++;
+ }
+}
+
+void QSMainDlg::FillGrid()
+{
+ m_grid.SetDraw(false);
+
+ for (auto &it: m_rows)
+ ProcessLine(it);
+
+ m_grid.SetDraw(true);
+ InvalidateRect(m_grid.GetHwnd(), 0, FALSE);
+
+ Sort();
+ UpdateSB();
+ AdvancedFilter();
+
+ m_grid.SetCurSel(0);
+}
+
+void QSMainDlg::FillProtoCombo()
+{
+ cmbProto.ResetContent();
+ cmbProto.AddString(TranslateT("All"));
+
+ for (auto &it : Accounts())
+ cmbProto.AddString(it->tszAccountName, (LPARAM)it);
+
+ cmbProto.SetCurSel(0);
+}
+
+int QSMainDlg::FindItem(CRowItem *pRow)
+{
+ if (pRow == nullptr)
+ return -1;
+
+ LV_FINDINFO fi = {};
+ fi.flags = LVFI_PARAM;
+ fi.lParam = LPARAM(pRow);
+ return m_grid.FindItem(-1, &fi);
+}
+
+int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum)
+{
+ for (auto &it : m_rows) {
+ if (it->hContact != hMeta)
+ continue;
+
+ // new meta
+ if (it->wparam == 0) {
+ it->wparam = ++hLastMeta;
+ it->lparam = 0;
+ }
+ metaNum = it->wparam;
+ it->lparam++;
+ return it->lparam;
+ }
+
+ return 0;
+}
+
+CRowItem* QSMainDlg::FindRow(MCONTACT hContact)
+{
+ for (auto &it : m_rows)
+ if (it->hContact == hContact)
+ return it;
+
+ return nullptr;
+}
+
+MCONTACT QSMainDlg::GetFocusedContact()
+{
+ int idx = m_grid.GetSelectionMark();
+ if (idx == -1)
+ return -1;
+
+ INT_PTR data = m_grid.GetItemData(idx);
+ return (data == -1) ? -1 : ((CRowItem *)data)->hContact;
+}
+
+int QSMainDlg::GetLVSubItem(int x, int y)
+{
+ LV_HITTESTINFO info = {};
+ info.pt.x = x;
+ info.pt.y = y;
+ ScreenToClient(m_grid.GetHwnd(), &info.pt);
+ if (m_grid.SubItemHitTest(&info) == -1)
+ return -1;
+
+ return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1;
+}
+
+void QSMainDlg::PrepareTable(bool bReset)
+{
+ m_grid.DeleteAllItems();
+
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+
+ int old = tableColumns;
+ tableColumns = 0;
+
+ LV_COLUMN lvc = {};
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled)
+ AddColumn(tableColumns++, it);
+
+ it->SetSpecialColumns();
+ }
+
+ if (bReset)
+ for (int i = old + tableColumns - 1; i >= tableColumns; i--)
+ m_grid.DeleteColumn(i);
+}
+
+bool QSMainDlg::PrepareToFill()
+{
+ if (g_plugin.m_columns.getCount() == 0)
+ return false;
+
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ it->bInit = true;
+
+ hLastMeta = 0;
+
+ m_rows.destroy();
+ for (auto &hContact : Contacts())
+ m_rows.insert(new CRowItem(hContact, this));
+
+ return m_rows.getCount() != 0;
+}
+
+void QSMainDlg::ProcessLine(CRowItem *pRow, bool test)
+{
+ if (pRow->bDeleted)
+ return;
+
+ if (test)
+ pRow->bPattern = CheckPattern(pRow);
+
+ if (pRow->bPattern) {
+ if (!pRow->bActive) {
+ if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) {
+ // check for proto in combo
+ if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) {
+ pRow->bActive = true;
+ AddContactToList(pRow->hContact, pRow);
+ }
+ }
+ }
+ }
+ else if (pRow->bActive) {
+ pRow->bActive = false;
+ m_grid.DeleteItem(FindItem(pRow));
+ }
+}
+
+void QSMainDlg::SaveColumnOrder()
+{
+ int idx = 0, col = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled) {
+ it->width = m_grid.GetColumnWidth(col++);
+ g_plugin.SaveColumn(idx, *it);
+ }
+ idx++;
+ }
+}
+
+void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact)
+{
+ if (hContact) {
+ Clist_ContactDoubleClicked(hContact);
+ if (g_plugin.m_flags & QSO_AUTOCLOSE)
+ Close();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// contact menu
+
+static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param)
+{
+ ((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param);
+ return 0;
+}
+
+void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col)
+{
+ auto &pCol = g_plugin.m_columns[col];
+
+ auto *pRow = FindRow(hContact);
+ if (pRow == nullptr)
+ return;
+
+ const char *szModule = pCol.module;
+ if (szModule == nullptr)
+ szModule = pRow->szProto;
+
+ auto &pVal = pRow->pValues[col];
+ CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title);
+
+ ENTER_STRING es = {};
+ es.szModuleName = MODULENAME;
+ es.caption = TranslateT("Enter new cell value");
+ es.ptszInitVal = pVal.text;
+ if (!EnterString(&es))
+ return;
+
+ replaceStrW(pVal.text, es.ptszResult);
+ if (pCol.datatype != QSTS_STRING)
+ pVal.data = _wtoi(pVal.text);
+
+ switch (pCol.datatype) {
+ case QSTS_BYTE:
+ db_set_b(hContact, szModule, pCol.setting, pVal.data);
+ break;
+ case QSTS_WORD:
+ db_set_w(hContact, szModule, pCol.setting, pVal.data);
+ break;
+ case QSTS_DWORD:
+ case QSTS_SIGNED:
+ case QSTS_HEXNUM:
+ db_set_dw(hContact, szModule, pCol.setting, pVal.data);
+ break;
+
+ case QSTS_STRING:
+ db_set_ws(hContact, szModule, pCol.setting, pVal.text);
+ break;
+ }
+
+ UpdateLVCell(FindItem(pRow), col, pVal.text);
+}
+
+void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col)
+{
+ if (hContact == 0)
+ return;
+
+ HANDLE srvhandle = 0;
+
+ bool bDoit = false;
+ if (col >= 0) {
+ if ((col = ListViewToColumn(col)) == -1)
+ return;
+
+ auto &pCol = g_plugin.m_columns[col];
+ if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) {
+ bDoit = true;
+
+ srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col);
+
+ if (mnuhandle == nullptr) {
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81);
+ mi.name.a = LPGEN("Change setting through QS");
+ mi.pszService = "QS/Dummy";
+ mnuhandle = Menu_AddContactMenuItem(&mi);
+ }
+ else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0);
+ }
+ }
+
+ POINT pt;
+ GetCursorPos(&pt);
+ HMENU hMenu = Menu_BuildContactMenu(hContact);
+ if (hMenu) {
+ int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0);
+ if (iCmd) {
+ if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) {
+ if (g_plugin.m_flags & QSO_AUTOCLOSE)
+ CloseSrWindow();
+ }
+ }
+
+ ::DestroyMenu(hMenu);
+ }
+
+ if (srvhandle)
+ DestroyServiceFunction(srvhandle);
+ if (bDoit)
+ Menu_ShowItem(mnuhandle, false);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// muptiple selection popup menu
+
+static HMENU MakeContainerMenu()
+{
+ HMENU hMenu = CreatePopupMenu();
+
+ for (int i = 0;; i++) {
+ char setting[10];
+ _itoa_s(i, setting, 10);
+ ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting));
+ if (wszName != nullptr)
+ AppendMenuW(hMenu, MF_STRING, 300 + i, wszName);
+ else
+ break;
+ }
+
+ return hMenu;
+}
+
+void QSMainDlg::ShowMultiPopup(int cnt)
+{
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt));
+ AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
+ AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete"));
+ AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy"));
+ AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta"));
+
+ HMENU cntmenu = MakeContainerMenu();
+ AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container"));
+
+ if (HMENU grpmenu = Clist_GroupBuildMenu(400))
+ AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group"));
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0);
+ switch (iRes) {
+ case 101:
+ DeleteByList();
+ break;
+
+ case 102:
+ CopyMultiLines();
+ break;
+
+ case 103:
+ ConvertToMeta();
+ break;
+ }
+
+ if (iRes >= 300 && iRes <= 399) {
+ wchar_t buf[100];
+ if (iRes == 300) // default container, just delete setting
+ buf[0] = 0;
+ else
+ GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND);
+
+ MoveToContainer(buf);
+ }
+ else if (iRes >= 400 && iRes <= 499)
+ MoveToGroup(Clist_GroupGetName(iRes - 400));
+}
+
+void QSMainDlg::ConvertToMeta()
+{
+ MCONTACT hMeta = 0;
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) {
+ if (hMeta == 0)
+ hMeta = tmp;
+ else if (hMeta != tmp) {
+ MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR);
+ return;
+ }
+ }
+ }
+
+ if (hMeta != 0)
+ if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING))
+ return;
+
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ if (hMeta)
+ db_mc_addToMeta(pRow->hContact, hMeta);
+ else
+ db_mc_convertToMeta(pRow->hContact);
+ }
+}
+
+void QSMainDlg::MoveToContainer(const wchar_t *pwszName)
+{
+ int grcol = -1;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->isContainer) {
+ if (it->bEnabled)
+ grcol = g_plugin.m_columns.indexOf(&it);
+ else
+ it->bInit = false;
+ }
+ }
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ if (*pwszName == 0)
+ db_unset(pRow->hContact, "Tab_SRMsg", "containerW");
+ else
+ db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName);
+
+ if (grcol != -1) {
+ auto &pVal = pRow->pValues[grcol];
+ replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr);
+ UpdateLVCell(i, grcol, pwszName);
+ }
+ }
+}
+
+void QSMainDlg::MoveToGroup(const wchar_t *pwszName)
+{
+ int grcol = -1;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->isGroup) {
+ if (it->bEnabled)
+ grcol = g_plugin.m_columns.indexOf(&it);
+ else
+ it->bInit = false;
+ }
+ }
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ Clist_SetGroup(pRow->hContact, pwszName);
+
+ if (grcol != -1) {
+ auto &pVal = pRow->pValues[grcol];
+ replaceStrW(pVal.text, pwszName);
+ UpdateLVCell(i, grcol, pwszName);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid sorting
+
+static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type)
+{
+ bool typ1, typ2;
+ UINT_PTR i1, i2;
+ int result = 0;
+ CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2;
+
+ if (type == StatusSort) {
+ i1 = r1->status, i2 = r2->status;
+ if (i1 == ID_STATUS_OFFLINE) i1 += 64;
+ if (i2 == ID_STATUS_OFFLINE) i2 += 64;
+ typ1 = typ2 = false;
+ }
+ else {
+ auto &res1 = r1->pValues[type], &res2 = r2->pValues[type];
+ i1 = res1.data, i2 = res2.data;
+ typ1 = i1 == -1; typ2 = i1 == -1;
+
+ if (typ1 && typ2) { // two strings
+ if (res1.text == 0 && res2.text == 0)
+ result = 0;
+ else if (res2.text == 0)
+ result = 1;
+ else if (res1.text == 0)
+ result = -1;
+ else
+ result = lstrcmpiW(res1.text, res2.text);
+ }
+ else if (typ1 || typ2) // string & num
+ result = (typ1) ? 1 : -1;
+ }
+
+ if (!typ1 && !typ2) { // not strings
+ if (i1 > i2)
+ result = 1;
+ else if (i1 < i2)
+ result = -1;
+ else
+ result = 0;
+ }
+
+ if (g_plugin.m_flags & QSO_SORTASC)
+ result = -result;
+ return result;
+}
+
+void QSMainDlg::Sort()
+{
+ if (g_plugin.m_sortOrder >= tableColumns)
+ g_plugin.m_sortOrder = StatusSort;
+ m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder));
+
+ if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS))
+ m_grid.SortItems(&CompareItem, StatusSort);
+}
+
+void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText)
+{
+ auto &pCol = g_plugin.m_columns[column];
+ auto *pRow = GetRow(item);
+
+ if (pwszText == nullptr) {
+ auto &pVal = pRow->pValues[column];
+ replaceStrW(pVal.text, 0);
+ pVal.LoadOneItem(pRow->hContact, pCol, this);
+ pwszText = pVal.text;
+ }
+
+ m_grid.SetItemText(item, ColumnToListView(column), pwszText);
+
+ if (pCol.bFilter)
+ ProcessLine(pRow, true);
+ if (g_plugin.m_sortOrder == column)
+ Sort();
+}
+
+void QSMainDlg::UpdateSB()
+{
+ m_sbdata.destroy();
+
+ for (auto &it : m_rows) {
+ if (it->szProto == nullptr)
+ continue;
+
+ CStatusBarItem tmp(it->szProto, it->flags);
+ auto *pItem = m_sbdata.find(&tmp);
+ if (pItem == nullptr)
+ m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags));
+
+ pItem->total++;
+
+ if (it->bActive)
+ pItem->found++;
+
+ if (it->status != ID_STATUS_OFFLINE) {
+ pItem->online++;
+ if (it->bActive)
+ pItem->liston++;
+ }
+ }
+
+ DrawSB();
+}
diff --git a/plugins/QuickSearch/src/window_row.cpp b/plugins/QuickSearch/src/window_row.cpp index d05ddb0c43..991dcafd68 100644 --- a/plugins/QuickSearch/src/window_row.cpp +++ b/plugins/QuickSearch/src/window_row.cpp @@ -1,224 +1,224 @@ -/* -Copyright (C) 2012-22 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" - -CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) : - hContact(_1) -{ - auto *pa = Proto_GetContactAccount(hContact); - if (pa != nullptr) { - szProto = pa->szModuleName; - if (!pa->IsEnabled()) - bAccOff = true; - - if (db_mc_isMeta(hContact)) - bIsMeta = true; - else if (db_mc_isSub(hContact)) - bIsSub = true; - } - else { - szProto = nullptr; - bAccDel = true; - } - - if (bAccDel || bAccOff) - status = ID_STATUS_OFFLINE; - else - status = Contact::GetStatus(hContact); - - if (int nCount = g_plugin.m_columns.getCount()) { - pValues = new Val[nCount]; - for (int i = 0; i < nCount; i++) - pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg); - } - else pValues = nullptr; -} - -CRowItem::~CRowItem() -{ - delete[] pValues; -} - -void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText) -{ - if (g_plugin.m_flags & QSO_COLORIZE) { - if (bAccDel) { - clrBack = g_plugin.m_colors[bkg_del]; - clrText = g_plugin.m_colors[fgr_del]; - return; - } - if (bAccOff) { - clrBack = g_plugin.m_colors[bkg_dis]; - clrText = g_plugin.m_colors[fgr_dis]; - return; - } - if (bIsMeta) { - clrBack = g_plugin.m_colors[bkg_meta]; - clrText = g_plugin.m_colors[fgr_meta]; - return; - } - if (bIsSub) { - clrBack = g_plugin.m_colors[bkg_sub]; - clrText = g_plugin.m_colors[fgr_sub]; - return; - } - if (bInList) { - clrBack = g_plugin.m_colors[bkg_hid]; - clrText = g_plugin.m_colors[fgr_hid]; - return; - } - } - - if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) { - clrBack = g_plugin.m_colors[bkg_odd]; - clrText = g_plugin.m_colors[fgr_odd]; - } - else { - clrBack = g_plugin.m_colors[bkg_norm]; - clrText = g_plugin.m_colors[fgr_norm]; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static wchar_t* int2strw(uint32_t num) -{ - wchar_t buf[64]; - _itow_s(num, buf, 10); - return mir_wstrdup(buf); -} - -static wchar_t* hex2strw(uint32_t num) -{ - wchar_t buf[64]; - _itow_s(num, buf, 16); - return mir_wstrdup(buf); -} - -void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg) -{ - data = UINT_PTR(-1); - replaceStrW(text, nullptr); - - switch (pCol.setting_type) { - case QST_SCRIPT: - { - VARSW vars(pCol.script); - if (g_bVarsInstalled) - text = variables_parse(vars, 0, hContact); - else - text = vars.detach(); - } - break; - - case QST_SERVICE: - // !!!!!!!!!!!!!!!!!!! not implemented - break; - - case QST_CONTACTINFO: - text = Contact::GetInfo(pCol.cnftype, hContact); - if (text) - data = _wtoi(text); - break; - - case QST_OTHER: - switch (pCol.other) { - case QSTO_ACCOUNT: - if (auto *pa = Proto_GetContactAccount(hContact)) - text = mir_wstrdup(pa->tszAccountName); - break; - - case QSTO_LASTSEEN: - data = BuildLastSeenTimeInt(hContact, "SeenModule"); - text = BuildLastSeenTime(data); - break; - - case QSTO_DISPLAYNAME: - text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0)); - break; - - case QSTO_LASTEVENT: - if (MEVENT hDbEvent = db_event_last(hContact)) { - DBEVENTINFO dbei = {}; - db_event_get(hDbEvent, &dbei); - data = dbei.timestamp; - text = TimeToStrW(data); - } - else text = 0; - break; - - case QSTO_METACONTACT: - text = pDlg->DoMeta(hContact); - break; - - case QSTO_EVENTCOUNT: - data = db_event_count(hContact); - text = int2strw(data); - break; - } - break; - - case QST_SETTING: - auto *szNodule = pCol.module; - if (!mir_strlen(szNodule)) - szNodule = Proto_GetBaseAccountName(hContact); - - switch (pCol.datatype) { - case QSTS_STRING: - text = db_get_wsa(hContact, szNodule, pCol.setting); - break; - - case QSTS_BYTE: - data = db_get_b(hContact, szNodule, pCol.setting); - text = int2strw(data); - break; - - case QSTS_WORD: - data = db_get_w(hContact, szNodule, pCol.setting); - text = int2strw(data); - break; - - case QSTS_DWORD: - if (pCol.setting == nullptr) { - data = hContact; - text = hex2strw(data); - } - else { - data = db_get_dw(hContact, szNodule, pCol.setting); - text = int2strw(data); - } - break; - - case QSTS_SIGNED: - data = db_get_dw(hContact, szNodule, pCol.setting); - text = int2strw(data); - break; - - case QSTS_HEXNUM: - data = db_get_dw(hContact, szNodule, pCol.setting); - text = hex2strw(data); - break; - - case QSTS_TIMESTAMP: - data = db_get_dw(hContact, szNodule, pCol.setting); - if (data != 0) - text = TimeToStrW(data); - break; - } - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) :
+ hContact(_1)
+{
+ auto *pa = Proto_GetContactAccount(hContact);
+ if (pa != nullptr) {
+ szProto = pa->szModuleName;
+ if (!pa->IsEnabled())
+ bAccOff = true;
+
+ if (db_mc_isMeta(hContact))
+ bIsMeta = true;
+ else if (db_mc_isSub(hContact))
+ bIsSub = true;
+ }
+ else {
+ szProto = nullptr;
+ bAccDel = true;
+ }
+
+ if (bAccDel || bAccOff)
+ status = ID_STATUS_OFFLINE;
+ else
+ status = Contact::GetStatus(hContact);
+
+ if (int nCount = g_plugin.m_columns.getCount()) {
+ pValues = new Val[nCount];
+ for (int i = 0; i < nCount; i++)
+ pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg);
+ }
+ else pValues = nullptr;
+}
+
+CRowItem::~CRowItem()
+{
+ delete[] pValues;
+}
+
+void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText)
+{
+ if (g_plugin.m_flags & QSO_COLORIZE) {
+ if (bAccDel) {
+ clrBack = g_plugin.m_colors[bkg_del];
+ clrText = g_plugin.m_colors[fgr_del];
+ return;
+ }
+ if (bAccOff) {
+ clrBack = g_plugin.m_colors[bkg_dis];
+ clrText = g_plugin.m_colors[fgr_dis];
+ return;
+ }
+ if (bIsMeta) {
+ clrBack = g_plugin.m_colors[bkg_meta];
+ clrText = g_plugin.m_colors[fgr_meta];
+ return;
+ }
+ if (bIsSub) {
+ clrBack = g_plugin.m_colors[bkg_sub];
+ clrText = g_plugin.m_colors[fgr_sub];
+ return;
+ }
+ if (bInList) {
+ clrBack = g_plugin.m_colors[bkg_hid];
+ clrText = g_plugin.m_colors[fgr_hid];
+ return;
+ }
+ }
+
+ if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) {
+ clrBack = g_plugin.m_colors[bkg_odd];
+ clrText = g_plugin.m_colors[fgr_odd];
+ }
+ else {
+ clrBack = g_plugin.m_colors[bkg_norm];
+ clrText = g_plugin.m_colors[fgr_norm];
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static wchar_t* int2strw(uint32_t num)
+{
+ wchar_t buf[64];
+ _itow_s(num, buf, 10);
+ return mir_wstrdup(buf);
+}
+
+static wchar_t* hex2strw(uint32_t num)
+{
+ wchar_t buf[64];
+ _itow_s(num, buf, 16);
+ return mir_wstrdup(buf);
+}
+
+void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg)
+{
+ data = UINT_PTR(-1);
+ replaceStrW(text, nullptr);
+
+ switch (pCol.setting_type) {
+ case QST_SCRIPT:
+ {
+ VARSW vars(pCol.script);
+ if (g_bVarsInstalled)
+ text = variables_parse(vars, 0, hContact);
+ else
+ text = vars.detach();
+ }
+ break;
+
+ case QST_SERVICE:
+ // !!!!!!!!!!!!!!!!!!! not implemented
+ break;
+
+ case QST_CONTACTINFO:
+ text = Contact::GetInfo(pCol.cnftype, hContact);
+ if (text)
+ data = _wtoi(text);
+ break;
+
+ case QST_OTHER:
+ switch (pCol.other) {
+ case QSTO_ACCOUNT:
+ if (auto *pa = Proto_GetContactAccount(hContact))
+ text = mir_wstrdup(pa->tszAccountName);
+ break;
+
+ case QSTO_LASTSEEN:
+ data = BuildLastSeenTimeInt(hContact, "SeenModule");
+ text = BuildLastSeenTime(data);
+ break;
+
+ case QSTO_DISPLAYNAME:
+ text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0));
+ break;
+
+ case QSTO_LASTEVENT:
+ if (MEVENT hDbEvent = db_event_last(hContact)) {
+ DBEVENTINFO dbei = {};
+ db_event_get(hDbEvent, &dbei);
+ data = dbei.timestamp;
+ text = TimeToStrW(data);
+ }
+ else text = 0;
+ break;
+
+ case QSTO_METACONTACT:
+ text = pDlg->DoMeta(hContact);
+ break;
+
+ case QSTO_EVENTCOUNT:
+ data = db_event_count(hContact);
+ text = int2strw(data);
+ break;
+ }
+ break;
+
+ case QST_SETTING:
+ auto *szNodule = pCol.module;
+ if (!mir_strlen(szNodule))
+ szNodule = Proto_GetBaseAccountName(hContact);
+
+ switch (pCol.datatype) {
+ case QSTS_STRING:
+ text = db_get_wsa(hContact, szNodule, pCol.setting);
+ break;
+
+ case QSTS_BYTE:
+ data = db_get_b(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ break;
+
+ case QSTS_WORD:
+ data = db_get_w(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ break;
+
+ case QSTS_DWORD:
+ if (pCol.setting == nullptr) {
+ data = hContact;
+ text = hex2strw(data);
+ }
+ else {
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ }
+ break;
+
+ case QSTS_SIGNED:
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ break;
+
+ case QSTS_HEXNUM:
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ text = hex2strw(data);
+ break;
+
+ case QSTS_TIMESTAMP:
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ if (data != 0)
+ text = TimeToStrW(data);
+ break;
+ }
+ }
+}
diff --git a/plugins/Rate/src/stdafx.cxx b/plugins/Rate/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Rate/src/stdafx.cxx +++ b/plugins/Rate/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/RecentContacts/src/stdafx.cxx b/plugins/RecentContacts/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/RecentContacts/src/stdafx.cxx +++ b/plugins/RecentContacts/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/RemovePersonalSettings/src/rps.cpp b/plugins/RemovePersonalSettings/src/rps.cpp index fe68e8d6cb..1c6385f2ab 100644 --- a/plugins/RemovePersonalSettings/src/rps.cpp +++ b/plugins/RemovePersonalSettings/src/rps.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (c) 2000-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/RemovePersonalSettings/src/stdafx.cxx b/plugins/RemovePersonalSettings/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/RemovePersonalSettings/src/stdafx.cxx +++ b/plugins/RemovePersonalSettings/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Restart/src/stdafx.cxx b/plugins/Restart/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Restart/src/stdafx.cxx +++ b/plugins/Restart/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Scriver/src/stdafx.cxx b/plugins/Scriver/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Scriver/src/stdafx.cxx +++ b/plugins/Scriver/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Scriver/src/version.h b/plugins/Scriver/src/version.h index 42c116f4fa..d6cf1cca6b 100644 --- a/plugins/Scriver/src/version.h +++ b/plugins/Scriver/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Scriver - send and receive instant messages."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Scriver"
-#define __COPYRIGHT "© 2000-2012 Miranda IM project, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2000-2012 Miranda IM project, 2012-23 Miranda NG team"
diff --git a/plugins/SecureIM/src/stdafx.cpp b/plugins/SecureIM/src/stdafx.cpp index b1991ac69e..73ac0be178 100644 --- a/plugins/SecureIM/src/stdafx.cpp +++ b/plugins/SecureIM/src/stdafx.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SeenPlugin/src/stdafx.cxx b/plugins/SeenPlugin/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SeenPlugin/src/stdafx.cxx +++ b/plugins/SeenPlugin/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SendScreenshotPlus/src/CSend.cpp b/plugins/SendScreenshotPlus/src/CSend.cpp index 5a5e72e03e..3e83d4b6c6 100644 --- a/plugins/SendScreenshotPlus/src/CSend.cpp +++ b/plugins/SendScreenshotPlus/src/CSend.cpp @@ -1,613 +1,613 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#define CSEND_DIALOG 8800 - -///////////////////////////////////////////////////////////////////////////////////////// - -CSend::CSend(HWND /*Owner*/, MCONTACT hContact, bool bAsync, bool bSilent) : - m_bDeleteAfterSend(false), - m_bAsync(bAsync), - m_bSilent(bSilent), - m_pszFile(nullptr), - m_pszFileDesc(nullptr), - m_pszSendTyp(nullptr), - m_pszProto(nullptr), - m_EnableItem(0), - m_ChatRoom(0), - m_cbEventMsg(0), - m_hSend(nullptr), - m_hOnSend(nullptr), - m_ErrorMsg(nullptr), - m_ErrorTitle(nullptr) -{ - SetContact(hContact); -} - -CSend::~CSend() -{ - mir_free(m_pszFile); - mir_free(m_pszFileDesc); - mir_free(m_ErrorMsg); - mir_free(m_ErrorTitle); - if (m_hOnSend) UnhookEvent(m_hOnSend); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSend::SetContact(MCONTACT hContact) -{ - m_hContact = hContact; - if (hContact) { - m_pszProto = Proto_GetBaseAccountName(hContact); - m_ChatRoom = Contact::IsGroupChat(hContact, m_pszProto); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CALLBACK CSend::ResultDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - Window_SetIcon_IcoLib(hwndDlg, GetIconHandle(ICO_MAIN)); - { - CSend *self = (CSend*)lParam; - SetDlgItemText(hwndDlg, IDC_HEADERBAR, CMStringW(TranslateT("Resulting URL from\n")) + self->m_pszSendTyp); - - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIconBtn(ICO_BTN_ARROWR)); - SetDlgItemTextA(hwndDlg, ID_edtURL, self->m_URL); - if (self->m_URLthumb) { - SetDlgItemTextA(hwndDlg, ID_edtURLthumb, self->m_URLthumb); - } - else { - SetDlgItemTextA(hwndDlg, ID_edtURLthumb, "-"); - for (int i = ID_btnThumbCopy; i <= ID_edtURLthumb; ++i) { - EnableWindow(GetDlgItem(hwndDlg, i), FALSE); - } - } - if (!self->m_pszFileDesc) - SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_ErrorTitle); - else - SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_pszFileDesc); - SendDlgItemMessage(hwndDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY)); - SendDlgItemMessage(hwndDlg, IDOK, BUTTONTRANSLATE, 0, 0); - SendDlgItemMessage(hwndDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_CANCEL)); - SendDlgItemMessage(hwndDlg, IDCANCEL, BUTTONTRANSLATE, 0, 0); - for (int i = ID_btnCopy; i <= ID_btnThumbBBC2; ++i) { - SendDlgItemMessage(hwndDlg, i, BUTTONSETASTHEMEDBTN, 0, 0); - SendDlgItemMessage(hwndDlg, i, BUTTONSETASFLATBTN, 1, 0); - switch (i) { - case ID_btnCopy: - case ID_btnThumbCopy: - SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY)); - SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy"), BATF_UNICODE); - break; - case ID_btnBBC: - case ID_btnThumbBBC: - SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBC)); - SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode"), BATF_UNICODE); - break; - default: - SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBCLNK)); - SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode w/ link"), BATF_UNICODE); - } - } - } - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - DestroyWindow(hwndDlg); - return TRUE; - - case IDOK: - case ID_btnCopy: - case ID_btnThumbCopy: - case ID_btnBBC: - case ID_btnThumbBBC: - case ID_btnThumbBBC2: - wchar_t tmp[2048]; - int edtID = ID_edtURL; - int bbc = 0; - switch (LOWORD(wParam)) { - case ID_btnThumbBBC2: ++bbc; - case ID_btnThumbBBC: ++bbc; - case ID_btnThumbCopy: - edtID = ID_edtURLthumb; - break; - case ID_btnBBC: ++bbc; - break; - } - size_t len; - if (bbc) { - if (bbc == 1) { - memcpy(tmp, L"[img]", 5 * sizeof(wchar_t)); len = 5; - len += GetDlgItemText(hwndDlg, edtID, tmp + len, 2048 - 11); - memcpy(tmp + len, L"[/img]", 7 * sizeof(wchar_t)); len += 7; - } - else { - memcpy(tmp, L"[url=", 5 * sizeof(wchar_t)); len = 5; - len += GetDlgItemText(hwndDlg, ID_edtURL, tmp + len, 1024); - memcpy(tmp + len, L"][img]", 6 * sizeof(wchar_t)); len += 6; - len += GetDlgItemText(hwndDlg, edtID, tmp + len, 1024); - memcpy(tmp + len, L"[/img][/url]", 13 * sizeof(wchar_t)); len += 12; - } - } - else len = GetDlgItemText(hwndDlg, edtID, tmp, _countof(tmp)); - - Utils_ClipboardCopy(CMStringW(tmp, len + 1)); - - if (LOWORD(wParam) == IDOK) - DestroyWindow(hwndDlg); - return TRUE; - } - } - return FALSE; -} - -void CSend::svcSendMsgExit(const char* szMessage) -{ - if (m_bSilent) { - Exit(ACKRESULT_SUCCESS); return; - } - if (!m_hContact) { - if (!m_pszFileDesc) - m_pszFileDesc = mir_a2u(szMessage); - Exit(CSEND_DIALOG); return; - } - - if (m_ChatRoom) { - CMStringW tmp(szMessage); - if (m_pszFileDesc) { - tmp.Append(L"\r\n"); - tmp.Append(m_pszFileDesc); - } - - int res = GC_RESULT_NOSESSION; - int cnt = g_chatApi.SM_GetCount(m_pszProto); - - // loop on all gc session to get the right (save) ptszID for the chatroom from m_hContact - GC_INFO gci = { 0 }; - gci.pszModule = m_pszProto; - for (int i = 0; i < cnt; i++) { - gci.iItem = i; - gci.Flags = GCF_BYINDEX | GCF_HCONTACT | GCF_ID; - Chat_GetInfo(&gci); - if (gci.hContact == m_hContact) { - Chat_SendUserMessage(m_pszProto, gci.pszID, tmp); - res = 200; - break; - } - } - Exit(res); return; - } - else { - m_szEventMsg = szMessage; - if (m_pszFileDesc && m_pszFileDesc[0] != NULL) { - m_szEventMsg.Append("\r\n"); - m_szEventMsg.Append(_T2A(m_pszFileDesc)); - m_cbEventMsg = m_szEventMsg.GetLength() + 1; - } - - // create a HookEventObj on ME_PROTO_ACK - if (!m_hOnSend) - m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this); - - // start PSS_MESSAGE service - m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_MESSAGE, NULL, ptrA(mir_utf8encode(m_szEventMsg))); - - // check we actually got an ft handle back from the protocol - if (!m_hSend) { - Unhook(); - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); return; - } - } -} - -void CSend::svcSendFileExit() -{ - // szMessage should be encoded as the File followed by the description, the - // separator being a single nul (\0). If there is no description, do not forget - // to end the File with two nuls. - if (m_bSilent) { - Exit(ACKRESULT_SUCCESS); return; - } - - if (!m_hContact) { - Error(LPGENW("%s requires a valid contact!"), m_pszSendTyp); - Exit(ACKRESULT_FAILED); return; - } - - m_szEventMsg = _T2A(m_pszFile); - - if (m_pszFileDesc && m_pszFileDesc[0] != NULL) { - m_szEventMsg.AppendChar(0); - m_szEventMsg.Append(_T2A(m_pszFileDesc)); - } - - m_cbEventMsg = m_szEventMsg.GetLength() + 1; - - // Сreate a HookEventObj on ME_PROTO_ACK - if (!m_hOnSend) { - m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this); - } - - // Start miranda PSS_FILE based on mir ver (T) - wchar_t* ppFile[2] = { nullptr, nullptr }; - wchar_t* pDesc = mir_wstrdup(m_pszFileDesc); - ppFile[0] = mir_wstrdup(m_pszFile); - ppFile[1] = nullptr; - m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_FILE, (WPARAM)pDesc, (LPARAM)ppFile); - mir_free(pDesc); - mir_free(ppFile[0]); - - // check we actually got an ft handle back from the protocol - if (!m_hSend) { - Unhook(); - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); return; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSend::OnSend(void *obj, WPARAM, LPARAM lParam) -{ - CSend* self = (CSend*)obj; - ACKDATA *ack = (ACKDATA*)lParam; - if (ack->hProcess != self->m_hSend) - return 0; - - switch (ack->result) { - case ACKRESULT_INITIALISING: // SetFtStatus(hwndDlg, LPGENW("Initialising..."), FTS_TEXT); break; - case ACKRESULT_CONNECTING: // SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break; - case ACKRESULT_CONNECTPROXY: // SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break; - case ACKRESULT_LISTENING: // SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break; - case ACKRESULT_CONNECTED: // SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break; - case ACKRESULT_SENTREQUEST: // SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break; - case ACKRESULT_NEXTFILE: // SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT); - case ACKRESULT_FILERESUME: - case ACKRESULT_DATA: // transfer is on progress - break; - case ACKRESULT_DENIED: - self->Unhook(); - self->Exit(ack->result); - break; - case ACKRESULT_FAILED: - self->Unhook(); - self->Exit(ack->result); - // type=ACKTYPE_MESSAGE, result=success/failure, (char*)lParam=error message or NULL. - // type=ACKTYPE_FILE, result=ACKRESULT_FAILED then lParam=(LPARAM)(const char*)szReason - break; - case ACKRESULT_SUCCESS: - self->Unhook(); - switch (ack->type) { - case ACKTYPE_CHAT: - break; - case ACKTYPE_MESSAGE: - self->DB_EventAdd((uint16_t)EVENTTYPE_MESSAGE); - break; - case ACKTYPE_FILE: - self->m_szEventMsg.Insert(0, "aaaa"); - self->m_cbEventMsg += sizeof(uint32_t); - self->DB_EventAdd((uint16_t)EVENTTYPE_FILE); - break; - } - self->Exit(ack->result); - break; - } - return 0; -} - -void CSend::DB_EventAdd(uint16_t EventType) -{ - DBEVENTINFO dbei = {}; - dbei.szModule = m_pszProto; - dbei.eventType = EventType; - dbei.flags = DBEF_SENT; - dbei.timestamp = time(0); - dbei.flags |= DBEF_UTF; - dbei.cbBlob = m_cbEventMsg; - dbei.pBlob = (uint8_t*)m_szEventMsg.GetString(); - db_event_add(m_hContact, &dbei); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSend::Error(LPCTSTR pszFormat, ...) -{ - wchar_t tszMsg[MAX_SECONDLINE]; - - mir_snwprintf(tszMsg, L"%s - %s", _A2W(MODULENAME), TranslateT("Error")); - mir_free(m_ErrorTitle), m_ErrorTitle = mir_wstrdup(tszMsg); - - va_list vl; - va_start(vl, pszFormat); - mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl); - va_end(vl); - mir_free(m_ErrorMsg), m_ErrorMsg = mir_wstrdup(tszMsg); - - memset(&m_box, 0, sizeof(MSGBOX)); - m_box.cbSize = sizeof(MSGBOX); - m_box.hParent = nullptr; - m_box.hiLogo = GetIcon(ICO_MAIN); - m_box.hiMsg = nullptr; - m_box.ptszTitle = m_ErrorTitle; - m_box.ptszMsg = m_ErrorMsg; - m_box.uType = MB_OK | MB_ICON_ERROR; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSend::Exit(unsigned int Result) -{ - if (!m_bSilent) { - bool err = true; - switch (Result) { - case CSEND_DIALOG: - Skin_PlaySound("FileDone"); - DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UResultForm), nullptr, ResultDialogProc, (LPARAM)this); - err = false; - break; - case ACKRESULT_SUCCESS: - case GC_RESULT_SUCCESS: - Skin_PlaySound("FileDone"); - err = false; - break; - case ACKRESULT_DENIED: - Skin_PlaySound("FileDenied"); - Error(L"%s (%i):\nFile transfer denied.", TranslateW(m_pszSendTyp), Result); - MsgBoxService(NULL, (LPARAM)&m_box); - err = false; - break; - case GC_RESULT_WRONGVER: // You appear to be using the wrong version of GC API. - Error(L"%s (%i):\nYou appear to be using the wrong version of GC API", TranslateT("GCHAT error"), Result); - break; - case GC_RESULT_ERROR: // An internal GC error occurred. - Error(L"%s (%i):\nAn internal GC error occurred.", TranslateT("GCHAT error"), Result); - break; - case GC_RESULT_NOSESSION: // contact has no open GC session - Error(L"%s (%i):\nContact has no open GC session.", TranslateT("GCHAT error"), Result); - break; - case ACKRESULT_FAILED: - default: - break; - } - if (err) { - Skin_PlaySound("FileFailed"); - if (m_ErrorMsg) MsgBoxService(NULL, (LPARAM)&m_box); - else MsgErr(nullptr, LPGENW("An unknown error has occurred.")); - } - } - if (m_pszFile && *m_pszFile && m_bDeleteAfterSend && m_EnableItem&SS_DLG_DELETEAFTERSSEND) { - DeleteFile(m_pszFile), m_pszFile = nullptr; - } - if (m_bAsync) - delete this; // deletes derived class since destructor is virtual (which also auto-calls base dtor) -} - -///////////////////////////////////////////////////////////////////////////////////////// -// helper functions used for HTTP uploads - -#define snprintf _snprintf - -const char* CSend::GetHTMLContent(char* str, const char* startTag, const char* endTag) -{ - char* begin = strstr(str, startTag); - if (!begin) return nullptr; - begin += mir_strlen(startTag) - 1; - for (; *begin != '>' && *begin; ++begin); - if (*begin) { - char* end = strstr(++begin, endTag); - if (end) *end = 0; - } - return begin; -} - -static void HTTPFormAppendData(NETLIBHTTPREQUEST* nlhr, size_t* dataMax, char** dataPos, const char* data, size_t len) -{ - nlhr->dataLength = (*dataPos - nlhr->pData); - if (nlhr->dataLength + len >= *dataMax) { - *dataPos = nlhr->pData; - *dataMax += 0x1000 + 0x1000 * (len >> 12); - nlhr->pData = (char*)mir_realloc(nlhr->pData, *dataMax); - if (!nlhr->pData) mir_free(*dataPos); - *dataPos = nlhr->pData; - if (!*dataPos) - return; - *dataPos += nlhr->dataLength; - } - if (data) { - memcpy(*dataPos, data, sizeof(char)*len); *dataPos += len; - nlhr->dataLength += (int)len; // not necessary - } -} - -void CSend::HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr) -{ - mir_free(nlhr->headers[0].szValue), nlhr->headers[0].szValue = nullptr; - mir_free(nlhr->headers), nlhr->headers = nullptr; - mir_free(nlhr->pData), nlhr->pData = nullptr; -} - -int CSend::HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum) -{ - char boundary[16]; - memcpy(boundary, "--M461C/", 8); - { - union - { - uint32_t num; - unsigned char cr[4]; - }; num = GetTickCount() ^ 0x8000; - for (int i = 0; i < 4; ++i) { - unsigned char chcode = cr[i] >> 4; - boundary[8 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode; - chcode = cr[i] & 0x0f; - boundary[9 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode; - } - } - nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); - nlhr->requestType = requestType; - nlhr->flags = NLHRF_HTTP11; - if (!strncmp(url, "https://", 8)) nlhr->flags |= NLHRF_SSL; - nlhr->szUrl = (char*)url; - nlhr->headersCount = 3; - for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) { - if (!(iter->flags&HTTPFF_HEADER)) break; - ++nlhr->headersCount; - } - nlhr->headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER)*nlhr->headersCount); - { - char* contenttype = (char*)mir_alloc(sizeof(char)*(30 + 1 + sizeof(boundary))); - memcpy(contenttype, "multipart/form-data; boundary=", 30); - memcpy(contenttype + 30, boundary, sizeof(boundary)); - contenttype[30 + sizeof(boundary)] = '\0'; - nlhr->headers[0].szName = "Content-Type"; - nlhr->headers[0].szValue = contenttype; - nlhr->headers[1].szName = "User-Agent"; - nlhr->headers[1].szValue = __USER_AGENT_STRING; - nlhr->headers[2].szName = "Accept-Language"; - nlhr->headers[2].szValue = "en-us,en;q=0.8"; - int i = 3; - for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) { - if (!(iter->flags&HTTPFF_HEADER)) break; - nlhr->headers[i].szName = (char*)iter->name; - nlhr->headers[i++].szValue = (char*)iter->value_str; - } - } - char* dataPos = nlhr->pData; - size_t dataMax = 0; - for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) { - if (iter->flags&HTTPFF_HEADER) continue; - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 40); - memset(dataPos, '-', 2); dataPos += 2; - memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary); - memcpy(dataPos, "\r\nContent-Disposition: form-data; name=\"", 40); dataPos += 40; - size_t namelen = mir_strlen(iter->name), valuelen = 0; - if (!(iter->flags&HTTPFF_INT)) - valuelen = mir_strlen(iter->value_str); - if (iter->flags&HTTPFF_FILE) { - const char* filename = strrchr(iter->value_str, '\\'); - if (!filename) filename = strrchr(iter->value_str, '/'); - if (!filename) filename = iter->value_str; - else ++filename; - valuelen = mir_strlen(filename); - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 13 + valuelen + 17); - memcpy(dataPos, iter->name, namelen); dataPos += namelen; - memcpy(dataPos, "\"; filename=\"", 13); dataPos += 13; - memcpy(dataPos, filename, valuelen); dataPos += valuelen; - memcpy(dataPos, "\"\r\nContent-Type: ", 17); dataPos += 17; - /// add mime type - const char* mime = "application/octet-stream"; - const char* fileext = strrchr(filename, '.'); - if (fileext) { - if (!mir_strcmp(fileext, ".jpg") || !mir_strcmp(fileext, ".jpeg") || !mir_strcmp(fileext, ".jpe")) - mime = "image/jpeg"; - else if (!mir_strcmp(fileext, ".bmp")) - mime = "image/bmp"; - else if (!mir_strcmp(fileext, ".png")) - mime = "image/png"; - else if (!mir_strcmp(fileext, ".gif")) - mime = "image/gif"; - else if (!mir_strcmp(fileext, ".tif") || !mir_strcmp(fileext, ".tiff")) - mime = "image/tiff"; - } - HTTPFormAppendData(nlhr, &dataMax, &dataPos, mime, mir_strlen(mime)); - HTTPFormAppendData(nlhr, &dataMax, &dataPos, "\r\n\r\n", 4); - /// add file content - size_t filesize = 0; - FILE* fp = fopen(iter->value_str, "rb"); - if (fp) { - fseek(fp, 0, SEEK_END); - filesize = ftell(fp); fseek(fp, 0, SEEK_SET); - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, filesize + 2); - if (fread(dataPos, 1, filesize, fp) != filesize) { - fclose(fp), fp = nullptr; - } - } - if (!fp) { - HTTPFormDestroy(nlhr); - Error(L"Error occurred when opening local file.\nAborting file upload..."); - Exit(ACKRESULT_FAILED); - return 1; - } - else - fclose(fp); - dataPos += filesize; - memcpy(dataPos, "\r\n", 2); dataPos += 2; - } - else if (iter->flags&HTTPFF_8BIT) { - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 38 + valuelen + 2); - memcpy(dataPos, iter->name, namelen); dataPos += namelen; - memcpy(dataPos, "\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 38); dataPos += 38; - memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen; - memcpy(dataPos, "\r\n", 2); dataPos += 2; - } - else if (iter->flags&HTTPFF_INT) { - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + 17/*max numbers*/ + 2); - memcpy(dataPos, iter->name, namelen); dataPos += namelen; - memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5; - int ret = snprintf(dataPos, 17, "%Id", iter->value_int); - if (ret < 17 && ret>0) dataPos += ret; - memcpy(dataPos, "\r\n", 2); dataPos += 2; - } - else { - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + valuelen + 2); - memcpy(dataPos, iter->name, namelen); dataPos += namelen; - memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5; - memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen; - memcpy(dataPos, "\r\n", 2); dataPos += 2; - } - } - HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 4); - memset(dataPos, '-', 2); dataPos += 2; - memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary); - memcpy(dataPos, "--\r\n", 4); dataPos += 4; - nlhr->dataLength = dataPos - nlhr->pData; -#ifdef _DEBUG /// print request content to "_sendss_tmp" file for debugging - { - FILE* fp = fopen("_sendss_tmp", "wb"); - if (fp) { - fprintf(fp, "--Target-- %s\n", nlhr->szUrl); - for (int i = 0; i < nlhr->headersCount; ++i) { - fprintf(fp, "%s: %s\n", nlhr->headers[i].szName, nlhr->headers[i].szValue); - } - fprintf(fp, "\n\n"); - fwrite(nlhr->pData, 1, nlhr->dataLength, fp); - fclose(fp); - } - } -#endif // _DEBUG - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#define CSEND_DIALOG 8800
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSend::CSend(HWND /*Owner*/, MCONTACT hContact, bool bAsync, bool bSilent) :
+ m_bDeleteAfterSend(false),
+ m_bAsync(bAsync),
+ m_bSilent(bSilent),
+ m_pszFile(nullptr),
+ m_pszFileDesc(nullptr),
+ m_pszSendTyp(nullptr),
+ m_pszProto(nullptr),
+ m_EnableItem(0),
+ m_ChatRoom(0),
+ m_cbEventMsg(0),
+ m_hSend(nullptr),
+ m_hOnSend(nullptr),
+ m_ErrorMsg(nullptr),
+ m_ErrorTitle(nullptr)
+{
+ SetContact(hContact);
+}
+
+CSend::~CSend()
+{
+ mir_free(m_pszFile);
+ mir_free(m_pszFileDesc);
+ mir_free(m_ErrorMsg);
+ mir_free(m_ErrorTitle);
+ if (m_hOnSend) UnhookEvent(m_hOnSend);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSend::SetContact(MCONTACT hContact)
+{
+ m_hContact = hContact;
+ if (hContact) {
+ m_pszProto = Proto_GetBaseAccountName(hContact);
+ m_ChatRoom = Contact::IsGroupChat(hContact, m_pszProto);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CALLBACK CSend::ResultDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, GetIconHandle(ICO_MAIN));
+ {
+ CSend *self = (CSend*)lParam;
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, CMStringW(TranslateT("Resulting URL from\n")) + self->m_pszSendTyp);
+
+ SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIconBtn(ICO_BTN_ARROWR));
+ SetDlgItemTextA(hwndDlg, ID_edtURL, self->m_URL);
+ if (self->m_URLthumb) {
+ SetDlgItemTextA(hwndDlg, ID_edtURLthumb, self->m_URLthumb);
+ }
+ else {
+ SetDlgItemTextA(hwndDlg, ID_edtURLthumb, "-");
+ for (int i = ID_btnThumbCopy; i <= ID_edtURLthumb; ++i) {
+ EnableWindow(GetDlgItem(hwndDlg, i), FALSE);
+ }
+ }
+ if (!self->m_pszFileDesc)
+ SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_ErrorTitle);
+ else
+ SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_pszFileDesc);
+ SendDlgItemMessage(hwndDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY));
+ SendDlgItemMessage(hwndDlg, IDOK, BUTTONTRANSLATE, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_CANCEL));
+ SendDlgItemMessage(hwndDlg, IDCANCEL, BUTTONTRANSLATE, 0, 0);
+ for (int i = ID_btnCopy; i <= ID_btnThumbBBC2; ++i) {
+ SendDlgItemMessage(hwndDlg, i, BUTTONSETASTHEMEDBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, i, BUTTONSETASFLATBTN, 1, 0);
+ switch (i) {
+ case ID_btnCopy:
+ case ID_btnThumbCopy:
+ SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY));
+ SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy"), BATF_UNICODE);
+ break;
+ case ID_btnBBC:
+ case ID_btnThumbBBC:
+ SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBC));
+ SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode"), BATF_UNICODE);
+ break;
+ default:
+ SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBCLNK));
+ SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode w/ link"), BATF_UNICODE);
+ }
+ }
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+
+ case IDOK:
+ case ID_btnCopy:
+ case ID_btnThumbCopy:
+ case ID_btnBBC:
+ case ID_btnThumbBBC:
+ case ID_btnThumbBBC2:
+ wchar_t tmp[2048];
+ int edtID = ID_edtURL;
+ int bbc = 0;
+ switch (LOWORD(wParam)) {
+ case ID_btnThumbBBC2: ++bbc;
+ case ID_btnThumbBBC: ++bbc;
+ case ID_btnThumbCopy:
+ edtID = ID_edtURLthumb;
+ break;
+ case ID_btnBBC: ++bbc;
+ break;
+ }
+ size_t len;
+ if (bbc) {
+ if (bbc == 1) {
+ memcpy(tmp, L"[img]", 5 * sizeof(wchar_t)); len = 5;
+ len += GetDlgItemText(hwndDlg, edtID, tmp + len, 2048 - 11);
+ memcpy(tmp + len, L"[/img]", 7 * sizeof(wchar_t)); len += 7;
+ }
+ else {
+ memcpy(tmp, L"[url=", 5 * sizeof(wchar_t)); len = 5;
+ len += GetDlgItemText(hwndDlg, ID_edtURL, tmp + len, 1024);
+ memcpy(tmp + len, L"][img]", 6 * sizeof(wchar_t)); len += 6;
+ len += GetDlgItemText(hwndDlg, edtID, tmp + len, 1024);
+ memcpy(tmp + len, L"[/img][/url]", 13 * sizeof(wchar_t)); len += 12;
+ }
+ }
+ else len = GetDlgItemText(hwndDlg, edtID, tmp, _countof(tmp));
+
+ Utils_ClipboardCopy(CMStringW(tmp, len + 1));
+
+ if (LOWORD(wParam) == IDOK)
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void CSend::svcSendMsgExit(const char* szMessage)
+{
+ if (m_bSilent) {
+ Exit(ACKRESULT_SUCCESS); return;
+ }
+ if (!m_hContact) {
+ if (!m_pszFileDesc)
+ m_pszFileDesc = mir_a2u(szMessage);
+ Exit(CSEND_DIALOG); return;
+ }
+
+ if (m_ChatRoom) {
+ CMStringW tmp(szMessage);
+ if (m_pszFileDesc) {
+ tmp.Append(L"\r\n");
+ tmp.Append(m_pszFileDesc);
+ }
+
+ int res = GC_RESULT_NOSESSION;
+ int cnt = g_chatApi.SM_GetCount(m_pszProto);
+
+ // loop on all gc session to get the right (save) ptszID for the chatroom from m_hContact
+ GC_INFO gci = { 0 };
+ gci.pszModule = m_pszProto;
+ for (int i = 0; i < cnt; i++) {
+ gci.iItem = i;
+ gci.Flags = GCF_BYINDEX | GCF_HCONTACT | GCF_ID;
+ Chat_GetInfo(&gci);
+ if (gci.hContact == m_hContact) {
+ Chat_SendUserMessage(m_pszProto, gci.pszID, tmp);
+ res = 200;
+ break;
+ }
+ }
+ Exit(res); return;
+ }
+ else {
+ m_szEventMsg = szMessage;
+ if (m_pszFileDesc && m_pszFileDesc[0] != NULL) {
+ m_szEventMsg.Append("\r\n");
+ m_szEventMsg.Append(_T2A(m_pszFileDesc));
+ m_cbEventMsg = m_szEventMsg.GetLength() + 1;
+ }
+
+ // create a HookEventObj on ME_PROTO_ACK
+ if (!m_hOnSend)
+ m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this);
+
+ // start PSS_MESSAGE service
+ m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_MESSAGE, NULL, ptrA(mir_utf8encode(m_szEventMsg)));
+
+ // check we actually got an ft handle back from the protocol
+ if (!m_hSend) {
+ Unhook();
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+ }
+}
+
+void CSend::svcSendFileExit()
+{
+ // szMessage should be encoded as the File followed by the description, the
+ // separator being a single nul (\0). If there is no description, do not forget
+ // to end the File with two nuls.
+ if (m_bSilent) {
+ Exit(ACKRESULT_SUCCESS); return;
+ }
+
+ if (!m_hContact) {
+ Error(LPGENW("%s requires a valid contact!"), m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+
+ m_szEventMsg = _T2A(m_pszFile);
+
+ if (m_pszFileDesc && m_pszFileDesc[0] != NULL) {
+ m_szEventMsg.AppendChar(0);
+ m_szEventMsg.Append(_T2A(m_pszFileDesc));
+ }
+
+ m_cbEventMsg = m_szEventMsg.GetLength() + 1;
+
+ // Сreate a HookEventObj on ME_PROTO_ACK
+ if (!m_hOnSend) {
+ m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this);
+ }
+
+ // Start miranda PSS_FILE based on mir ver (T)
+ wchar_t* ppFile[2] = { nullptr, nullptr };
+ wchar_t* pDesc = mir_wstrdup(m_pszFileDesc);
+ ppFile[0] = mir_wstrdup(m_pszFile);
+ ppFile[1] = nullptr;
+ m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_FILE, (WPARAM)pDesc, (LPARAM)ppFile);
+ mir_free(pDesc);
+ mir_free(ppFile[0]);
+
+ // check we actually got an ft handle back from the protocol
+ if (!m_hSend) {
+ Unhook();
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSend::OnSend(void *obj, WPARAM, LPARAM lParam)
+{
+ CSend* self = (CSend*)obj;
+ ACKDATA *ack = (ACKDATA*)lParam;
+ if (ack->hProcess != self->m_hSend)
+ return 0;
+
+ switch (ack->result) {
+ case ACKRESULT_INITIALISING: // SetFtStatus(hwndDlg, LPGENW("Initialising..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTING: // SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTPROXY: // SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break;
+ case ACKRESULT_LISTENING: // SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTED: // SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break;
+ case ACKRESULT_SENTREQUEST: // SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break;
+ case ACKRESULT_NEXTFILE: // SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT);
+ case ACKRESULT_FILERESUME:
+ case ACKRESULT_DATA: // transfer is on progress
+ break;
+ case ACKRESULT_DENIED:
+ self->Unhook();
+ self->Exit(ack->result);
+ break;
+ case ACKRESULT_FAILED:
+ self->Unhook();
+ self->Exit(ack->result);
+ // type=ACKTYPE_MESSAGE, result=success/failure, (char*)lParam=error message or NULL.
+ // type=ACKTYPE_FILE, result=ACKRESULT_FAILED then lParam=(LPARAM)(const char*)szReason
+ break;
+ case ACKRESULT_SUCCESS:
+ self->Unhook();
+ switch (ack->type) {
+ case ACKTYPE_CHAT:
+ break;
+ case ACKTYPE_MESSAGE:
+ self->DB_EventAdd((uint16_t)EVENTTYPE_MESSAGE);
+ break;
+ case ACKTYPE_FILE:
+ self->m_szEventMsg.Insert(0, "aaaa");
+ self->m_cbEventMsg += sizeof(uint32_t);
+ self->DB_EventAdd((uint16_t)EVENTTYPE_FILE);
+ break;
+ }
+ self->Exit(ack->result);
+ break;
+ }
+ return 0;
+}
+
+void CSend::DB_EventAdd(uint16_t EventType)
+{
+ DBEVENTINFO dbei = {};
+ dbei.szModule = m_pszProto;
+ dbei.eventType = EventType;
+ dbei.flags = DBEF_SENT;
+ dbei.timestamp = time(0);
+ dbei.flags |= DBEF_UTF;
+ dbei.cbBlob = m_cbEventMsg;
+ dbei.pBlob = (uint8_t*)m_szEventMsg.GetString();
+ db_event_add(m_hContact, &dbei);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSend::Error(LPCTSTR pszFormat, ...)
+{
+ wchar_t tszMsg[MAX_SECONDLINE];
+
+ mir_snwprintf(tszMsg, L"%s - %s", _A2W(MODULENAME), TranslateT("Error"));
+ mir_free(m_ErrorTitle), m_ErrorTitle = mir_wstrdup(tszMsg);
+
+ va_list vl;
+ va_start(vl, pszFormat);
+ mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl);
+ va_end(vl);
+ mir_free(m_ErrorMsg), m_ErrorMsg = mir_wstrdup(tszMsg);
+
+ memset(&m_box, 0, sizeof(MSGBOX));
+ m_box.cbSize = sizeof(MSGBOX);
+ m_box.hParent = nullptr;
+ m_box.hiLogo = GetIcon(ICO_MAIN);
+ m_box.hiMsg = nullptr;
+ m_box.ptszTitle = m_ErrorTitle;
+ m_box.ptszMsg = m_ErrorMsg;
+ m_box.uType = MB_OK | MB_ICON_ERROR;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSend::Exit(unsigned int Result)
+{
+ if (!m_bSilent) {
+ bool err = true;
+ switch (Result) {
+ case CSEND_DIALOG:
+ Skin_PlaySound("FileDone");
+ DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UResultForm), nullptr, ResultDialogProc, (LPARAM)this);
+ err = false;
+ break;
+ case ACKRESULT_SUCCESS:
+ case GC_RESULT_SUCCESS:
+ Skin_PlaySound("FileDone");
+ err = false;
+ break;
+ case ACKRESULT_DENIED:
+ Skin_PlaySound("FileDenied");
+ Error(L"%s (%i):\nFile transfer denied.", TranslateW(m_pszSendTyp), Result);
+ MsgBoxService(NULL, (LPARAM)&m_box);
+ err = false;
+ break;
+ case GC_RESULT_WRONGVER: // You appear to be using the wrong version of GC API.
+ Error(L"%s (%i):\nYou appear to be using the wrong version of GC API", TranslateT("GCHAT error"), Result);
+ break;
+ case GC_RESULT_ERROR: // An internal GC error occurred.
+ Error(L"%s (%i):\nAn internal GC error occurred.", TranslateT("GCHAT error"), Result);
+ break;
+ case GC_RESULT_NOSESSION: // contact has no open GC session
+ Error(L"%s (%i):\nContact has no open GC session.", TranslateT("GCHAT error"), Result);
+ break;
+ case ACKRESULT_FAILED:
+ default:
+ break;
+ }
+ if (err) {
+ Skin_PlaySound("FileFailed");
+ if (m_ErrorMsg) MsgBoxService(NULL, (LPARAM)&m_box);
+ else MsgErr(nullptr, LPGENW("An unknown error has occurred."));
+ }
+ }
+ if (m_pszFile && *m_pszFile && m_bDeleteAfterSend && m_EnableItem&SS_DLG_DELETEAFTERSSEND) {
+ DeleteFile(m_pszFile), m_pszFile = nullptr;
+ }
+ if (m_bAsync)
+ delete this; // deletes derived class since destructor is virtual (which also auto-calls base dtor)
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// helper functions used for HTTP uploads
+
+#define snprintf _snprintf
+
+const char* CSend::GetHTMLContent(char* str, const char* startTag, const char* endTag)
+{
+ char* begin = strstr(str, startTag);
+ if (!begin) return nullptr;
+ begin += mir_strlen(startTag) - 1;
+ for (; *begin != '>' && *begin; ++begin);
+ if (*begin) {
+ char* end = strstr(++begin, endTag);
+ if (end) *end = 0;
+ }
+ return begin;
+}
+
+static void HTTPFormAppendData(NETLIBHTTPREQUEST* nlhr, size_t* dataMax, char** dataPos, const char* data, size_t len)
+{
+ nlhr->dataLength = (*dataPos - nlhr->pData);
+ if (nlhr->dataLength + len >= *dataMax) {
+ *dataPos = nlhr->pData;
+ *dataMax += 0x1000 + 0x1000 * (len >> 12);
+ nlhr->pData = (char*)mir_realloc(nlhr->pData, *dataMax);
+ if (!nlhr->pData) mir_free(*dataPos);
+ *dataPos = nlhr->pData;
+ if (!*dataPos)
+ return;
+ *dataPos += nlhr->dataLength;
+ }
+ if (data) {
+ memcpy(*dataPos, data, sizeof(char)*len); *dataPos += len;
+ nlhr->dataLength += (int)len; // not necessary
+ }
+}
+
+void CSend::HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr)
+{
+ mir_free(nlhr->headers[0].szValue), nlhr->headers[0].szValue = nullptr;
+ mir_free(nlhr->headers), nlhr->headers = nullptr;
+ mir_free(nlhr->pData), nlhr->pData = nullptr;
+}
+
+int CSend::HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum)
+{
+ char boundary[16];
+ memcpy(boundary, "--M461C/", 8);
+ {
+ union
+ {
+ uint32_t num;
+ unsigned char cr[4];
+ }; num = GetTickCount() ^ 0x8000;
+ for (int i = 0; i < 4; ++i) {
+ unsigned char chcode = cr[i] >> 4;
+ boundary[8 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode;
+ chcode = cr[i] & 0x0f;
+ boundary[9 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode;
+ }
+ }
+ nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+ nlhr->requestType = requestType;
+ nlhr->flags = NLHRF_HTTP11;
+ if (!strncmp(url, "https://", 8)) nlhr->flags |= NLHRF_SSL;
+ nlhr->szUrl = (char*)url;
+ nlhr->headersCount = 3;
+ for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
+ if (!(iter->flags&HTTPFF_HEADER)) break;
+ ++nlhr->headersCount;
+ }
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER)*nlhr->headersCount);
+ {
+ char* contenttype = (char*)mir_alloc(sizeof(char)*(30 + 1 + sizeof(boundary)));
+ memcpy(contenttype, "multipart/form-data; boundary=", 30);
+ memcpy(contenttype + 30, boundary, sizeof(boundary));
+ contenttype[30 + sizeof(boundary)] = '\0';
+ nlhr->headers[0].szName = "Content-Type";
+ nlhr->headers[0].szValue = contenttype;
+ nlhr->headers[1].szName = "User-Agent";
+ nlhr->headers[1].szValue = __USER_AGENT_STRING;
+ nlhr->headers[2].szName = "Accept-Language";
+ nlhr->headers[2].szValue = "en-us,en;q=0.8";
+ int i = 3;
+ for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
+ if (!(iter->flags&HTTPFF_HEADER)) break;
+ nlhr->headers[i].szName = (char*)iter->name;
+ nlhr->headers[i++].szValue = (char*)iter->value_str;
+ }
+ }
+ char* dataPos = nlhr->pData;
+ size_t dataMax = 0;
+ for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
+ if (iter->flags&HTTPFF_HEADER) continue;
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 40);
+ memset(dataPos, '-', 2); dataPos += 2;
+ memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary);
+ memcpy(dataPos, "\r\nContent-Disposition: form-data; name=\"", 40); dataPos += 40;
+ size_t namelen = mir_strlen(iter->name), valuelen = 0;
+ if (!(iter->flags&HTTPFF_INT))
+ valuelen = mir_strlen(iter->value_str);
+ if (iter->flags&HTTPFF_FILE) {
+ const char* filename = strrchr(iter->value_str, '\\');
+ if (!filename) filename = strrchr(iter->value_str, '/');
+ if (!filename) filename = iter->value_str;
+ else ++filename;
+ valuelen = mir_strlen(filename);
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 13 + valuelen + 17);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"; filename=\"", 13); dataPos += 13;
+ memcpy(dataPos, filename, valuelen); dataPos += valuelen;
+ memcpy(dataPos, "\"\r\nContent-Type: ", 17); dataPos += 17;
+ /// add mime type
+ const char* mime = "application/octet-stream";
+ const char* fileext = strrchr(filename, '.');
+ if (fileext) {
+ if (!mir_strcmp(fileext, ".jpg") || !mir_strcmp(fileext, ".jpeg") || !mir_strcmp(fileext, ".jpe"))
+ mime = "image/jpeg";
+ else if (!mir_strcmp(fileext, ".bmp"))
+ mime = "image/bmp";
+ else if (!mir_strcmp(fileext, ".png"))
+ mime = "image/png";
+ else if (!mir_strcmp(fileext, ".gif"))
+ mime = "image/gif";
+ else if (!mir_strcmp(fileext, ".tif") || !mir_strcmp(fileext, ".tiff"))
+ mime = "image/tiff";
+ }
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, mime, mir_strlen(mime));
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, "\r\n\r\n", 4);
+ /// add file content
+ size_t filesize = 0;
+ FILE* fp = fopen(iter->value_str, "rb");
+ if (fp) {
+ fseek(fp, 0, SEEK_END);
+ filesize = ftell(fp); fseek(fp, 0, SEEK_SET);
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, filesize + 2);
+ if (fread(dataPos, 1, filesize, fp) != filesize) {
+ fclose(fp), fp = nullptr;
+ }
+ }
+ if (!fp) {
+ HTTPFormDestroy(nlhr);
+ Error(L"Error occurred when opening local file.\nAborting file upload...");
+ Exit(ACKRESULT_FAILED);
+ return 1;
+ }
+ else
+ fclose(fp);
+ dataPos += filesize;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ else if (iter->flags&HTTPFF_8BIT) {
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 38 + valuelen + 2);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 38); dataPos += 38;
+ memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ else if (iter->flags&HTTPFF_INT) {
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + 17/*max numbers*/ + 2);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5;
+ int ret = snprintf(dataPos, 17, "%Id", iter->value_int);
+ if (ret < 17 && ret>0) dataPos += ret;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ else {
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + valuelen + 2);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5;
+ memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ }
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 4);
+ memset(dataPos, '-', 2); dataPos += 2;
+ memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary);
+ memcpy(dataPos, "--\r\n", 4); dataPos += 4;
+ nlhr->dataLength = dataPos - nlhr->pData;
+#ifdef _DEBUG /// print request content to "_sendss_tmp" file for debugging
+ {
+ FILE* fp = fopen("_sendss_tmp", "wb");
+ if (fp) {
+ fprintf(fp, "--Target-- %s\n", nlhr->szUrl);
+ for (int i = 0; i < nlhr->headersCount; ++i) {
+ fprintf(fp, "%s: %s\n", nlhr->headers[i].szName, nlhr->headers[i].szValue);
+ }
+ fprintf(fp, "\n\n");
+ fwrite(nlhr->pData, 1, nlhr->dataLength, fp);
+ fclose(fp);
+ }
+ }
+#endif // _DEBUG
+ return 0;
+}
diff --git a/plugins/SendScreenshotPlus/src/CSend.h b/plugins/SendScreenshotPlus/src/CSend.h index 630222b69e..9301811f17 100644 --- a/plugins/SendScreenshotPlus/src/CSend.h +++ b/plugins/SendScreenshotPlus/src/CSend.h @@ -1,137 +1,137 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_H -#define _CSEND_H - -///////////////////////////////////////////////////////////////////////////////////////// - -#define SS_AUTOSEND 1 -#define SS_DELETEAFTERSSEND 2 - -#define SS_DLG_AUTOSEND 1 // Button_Enable(GetDlgItem(Owner, ID_chkEmulateClick), TRUE); -#define SS_DLG_DELETEAFTERSSEND 2 // Button_Enable(GetDlgItem(Owner, ID_chkDeleteAfterSend), TRUE); -#define SS_DLG_DESCRIPTION 4 // Button_Enable(GetDlgItem(Owner, ID_chkDesc), TRUE); - -#define GC_RESULT_SUCCESS 200 -#define GC_RESULT_WRONGVER 201 -#define GC_RESULT_ERROR 202 -#define GC_RESULT_NOSESSION 209 - -const wchar_t SS_ERR_INIT[] = LPGENW("Unable to initiate %s."); -const wchar_t SS_ERR_MAPI[] = LPGENW("MAPI error (%i):\n%s."); -const wchar_t SS_ERR_RESPONSE[] = LPGENW("Unknown response from %s (%i)"); -const wchar_t SS_ERR_NORESPONSE[] = LPGENW("Got no response from %s (%i)"); - -///////////////////////////////////////////////////////////////////////////////////////// - -class CSend -{ -public: - CSend(HWND Owner, MCONTACT hContact, bool bAsync, bool bSilent=false); // oder (TfrmMain & Owner) - virtual ~CSend(); - - virtual int Send() = 0; // returns 1 if sent (you must delete class) and 0 when still sending (class deletes itself) - int SendSilent() { m_bAsync = m_bSilent = true; return Send(); } - - void SetFile(const wchar_t* file) { replaceStrW(m_pszFile, file); } - void SetFile(const char* file) { mir_free(m_pszFile), m_pszFile=mir_a2u(file); } - void SetDescription(const wchar_t* descr){ replaceStrW(m_pszFileDesc, descr); } - void SetContact(MCONTACT hContact); - const char* GetURL() { return m_URL; } - const char* GetURLthumbnail() {return m_URLthumb; } - uint8_t GetEnableItem() {return m_EnableItem;}; - wchar_t* GetErrorMsg() {return m_ErrorMsg;}; - - bool m_bDeleteAfterSend; - -protected: - bool m_bAsync; - bool m_bSilent; - wchar_t* m_pszFile; - wchar_t* m_pszFileDesc; - CMStringA m_URL; - CMStringA m_URLthumb; - static int OnSend(void *obj, WPARAM wParam, LPARAM lParam); - wchar_t* m_pszSendTyp; // hold string for error mess - char* m_pszProto; // Contact Proto Module - MCONTACT m_hContact; // Contact handle - uint8_t m_EnableItem; // hold flag for send type - uint8_t m_ChatRoom; // is Contact chatroom - - void Error(LPCTSTR pszFormat, ...); - void svcSendFileExit(); - void svcSendMsgExit(const char* szMessage); - void Exit(unsigned int Result); - - uint32_t m_cbEventMsg; // sizeof EventMsg(T) buffer - CMStringA m_szEventMsg; // EventMsg char* - HANDLE m_hSend; // protocol send handle - HANDLE m_hOnSend; // HookEventObj on ME_PROTO_ACK - - MSGBOX m_box; - wchar_t* m_ErrorMsg; - wchar_t* m_ErrorTitle; - - void Unhook(){if(m_hOnSend) {UnhookEvent(m_hOnSend);m_hOnSend = nullptr;}} - void DB_EventAdd(uint16_t EventType); - - static INT_PTR CALLBACK ResultDialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam); - - /// HTTP upload helper stuff - enum HTTPFormFlags - { - HTTPFF_HEADER = 0x80, - HTTPFF_TEXT = 0x00, - HTTPFF_8BIT = 0x01, - HTTPFF_FILE = 0x02, - HTTPFF_INT = 0x04, - }; - - #define HTTPFORM_HEADER(str) str,HTTPFF_HEADER - #define HTTPFORM_TEXT(str) str,HTTPFF_TEXT - #define HTTPFORM_8BIT(str) str,HTTPFF_8BIT - #define HTTPFORM_FILE(str) str,HTTPFF_FILE - #define HTTPFORM_INT(int) (const char*)(int),HTTPFF_INT - - struct HTTPFormData - { - const char *name; - union{ - const char* value_str; - intptr_t value_int; - }; - int flags; - }; - - static const char* GetHTMLContent(char* str, const char* startTag, const char* endTag); // changes "str", can be successfully used only once - void HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr); // use to free data inside "nlhr" created by HTTPFormCreate - int HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum); // returns "0" on success, Exit() will be called on failure (stop processing) -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_H
+#define _CSEND_H
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define SS_AUTOSEND 1
+#define SS_DELETEAFTERSSEND 2
+
+#define SS_DLG_AUTOSEND 1 // Button_Enable(GetDlgItem(Owner, ID_chkEmulateClick), TRUE);
+#define SS_DLG_DELETEAFTERSSEND 2 // Button_Enable(GetDlgItem(Owner, ID_chkDeleteAfterSend), TRUE);
+#define SS_DLG_DESCRIPTION 4 // Button_Enable(GetDlgItem(Owner, ID_chkDesc), TRUE);
+
+#define GC_RESULT_SUCCESS 200
+#define GC_RESULT_WRONGVER 201
+#define GC_RESULT_ERROR 202
+#define GC_RESULT_NOSESSION 209
+
+const wchar_t SS_ERR_INIT[] = LPGENW("Unable to initiate %s.");
+const wchar_t SS_ERR_MAPI[] = LPGENW("MAPI error (%i):\n%s.");
+const wchar_t SS_ERR_RESPONSE[] = LPGENW("Unknown response from %s (%i)");
+const wchar_t SS_ERR_NORESPONSE[] = LPGENW("Got no response from %s (%i)");
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CSend
+{
+public:
+ CSend(HWND Owner, MCONTACT hContact, bool bAsync, bool bSilent=false); // oder (TfrmMain & Owner)
+ virtual ~CSend();
+
+ virtual int Send() = 0; // returns 1 if sent (you must delete class) and 0 when still sending (class deletes itself)
+ int SendSilent() { m_bAsync = m_bSilent = true; return Send(); }
+
+ void SetFile(const wchar_t* file) { replaceStrW(m_pszFile, file); }
+ void SetFile(const char* file) { mir_free(m_pszFile), m_pszFile=mir_a2u(file); }
+ void SetDescription(const wchar_t* descr){ replaceStrW(m_pszFileDesc, descr); }
+ void SetContact(MCONTACT hContact);
+ const char* GetURL() { return m_URL; }
+ const char* GetURLthumbnail() {return m_URLthumb; }
+ uint8_t GetEnableItem() {return m_EnableItem;};
+ wchar_t* GetErrorMsg() {return m_ErrorMsg;};
+
+ bool m_bDeleteAfterSend;
+
+protected:
+ bool m_bAsync;
+ bool m_bSilent;
+ wchar_t* m_pszFile;
+ wchar_t* m_pszFileDesc;
+ CMStringA m_URL;
+ CMStringA m_URLthumb;
+ static int OnSend(void *obj, WPARAM wParam, LPARAM lParam);
+ wchar_t* m_pszSendTyp; // hold string for error mess
+ char* m_pszProto; // Contact Proto Module
+ MCONTACT m_hContact; // Contact handle
+ uint8_t m_EnableItem; // hold flag for send type
+ uint8_t m_ChatRoom; // is Contact chatroom
+
+ void Error(LPCTSTR pszFormat, ...);
+ void svcSendFileExit();
+ void svcSendMsgExit(const char* szMessage);
+ void Exit(unsigned int Result);
+
+ uint32_t m_cbEventMsg; // sizeof EventMsg(T) buffer
+ CMStringA m_szEventMsg; // EventMsg char*
+ HANDLE m_hSend; // protocol send handle
+ HANDLE m_hOnSend; // HookEventObj on ME_PROTO_ACK
+
+ MSGBOX m_box;
+ wchar_t* m_ErrorMsg;
+ wchar_t* m_ErrorTitle;
+
+ void Unhook(){if(m_hOnSend) {UnhookEvent(m_hOnSend);m_hOnSend = nullptr;}}
+ void DB_EventAdd(uint16_t EventType);
+
+ static INT_PTR CALLBACK ResultDialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);
+
+ /// HTTP upload helper stuff
+ enum HTTPFormFlags
+ {
+ HTTPFF_HEADER = 0x80,
+ HTTPFF_TEXT = 0x00,
+ HTTPFF_8BIT = 0x01,
+ HTTPFF_FILE = 0x02,
+ HTTPFF_INT = 0x04,
+ };
+
+ #define HTTPFORM_HEADER(str) str,HTTPFF_HEADER
+ #define HTTPFORM_TEXT(str) str,HTTPFF_TEXT
+ #define HTTPFORM_8BIT(str) str,HTTPFF_8BIT
+ #define HTTPFORM_FILE(str) str,HTTPFF_FILE
+ #define HTTPFORM_INT(int) (const char*)(int),HTTPFF_INT
+
+ struct HTTPFormData
+ {
+ const char *name;
+ union{
+ const char* value_str;
+ intptr_t value_int;
+ };
+ int flags;
+ };
+
+ static const char* GetHTMLContent(char* str, const char* startTag, const char* endTag); // changes "str", can be successfully used only once
+ void HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr); // use to free data inside "nlhr" created by HTTPFormCreate
+ int HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum); // returns "0" on success, Exit() will be called on failure (stop processing)
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendCloduFile.h b/plugins/SendScreenshotPlus/src/CSendCloduFile.h index 663bbf526c..f5f0c221db 100644 --- a/plugins/SendScreenshotPlus/src/CSendCloduFile.h +++ b/plugins/SendScreenshotPlus/src/CSendCloduFile.h @@ -1,50 +1,50 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_CLOUD_FILE_H -#define _CSEND_CLOUD_FILE_H - -#include "Utils.h" - -class CSendCloudFile : public CSend -{ - -public: - CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service); - ~CSendCloudFile(); - - int Send() override; - -protected: - const char *m_service; - - void SendThread(); - static void SendThreadWrapper(void *Obj); -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_CLOUD_FILE_H
+#define _CSEND_CLOUD_FILE_H
+
+#include "Utils.h"
+
+class CSendCloudFile : public CSend
+{
+
+public:
+ CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service);
+ ~CSendCloudFile();
+
+ int Send() override;
+
+protected:
+ const char *m_service;
+
+ void SendThread();
+ static void SendThreadWrapper(void *Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp b/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp index ddcdb3b0a5..88f5da4dac 100644 --- a/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp +++ b/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp @@ -1,78 +1,78 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -CSendCloudFile::CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service) - : CSend(Owner, hContact, bAsync), m_service(service) -{ - // @todo : re-enable SS_DLG_DELETEAFTERSSEND with full implemention of Dropbox upload with progress, msg and sounds - m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND; - m_pszSendTyp = TranslateT("CloudFile transfer"); -} - -CSendCloudFile::~CSendCloudFile() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendCloudFile::Send() -{ - mir_forkthread(&CSendCloudFile::SendThreadWrapper, this); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSendCloudFile::SendThread() -{ - // @todo : SS_DLG_DESCRIPTION and SS_DLG_DELETEAFTERSSEND are of no use as of now since we don't track upload progress - - CFUPLOADDATA ud = { m_service, m_pszFile, L"SendSS" }; - CFUPLOADRESULT ur = { }; - - if (CallService(MS_CLOUDFILE_UPLOAD, (WPARAM)&ud, (LPARAM)&ur)) { - Error(LPGENW("%s (%i):\nCould not add a share to the CloudFile plugin."), TranslateW(m_pszSendTyp), 0); - Exit(ACKRESULT_FAILED); - return; - } - - m_URL = ur.link; - if (m_URL) - svcSendMsgExit(m_URL); - else - Exit(ACKRESULT_FAILED); -} - -void CSendCloudFile::SendThreadWrapper(void * Obj) -{ - reinterpret_cast<CSendCloudFile*>(Obj)->SendThread(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendCloudFile::CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service)
+ : CSend(Owner, hContact, bAsync), m_service(service)
+{
+ // @todo : re-enable SS_DLG_DELETEAFTERSSEND with full implemention of Dropbox upload with progress, msg and sounds
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = TranslateT("CloudFile transfer");
+}
+
+CSendCloudFile::~CSendCloudFile()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendCloudFile::Send()
+{
+ mir_forkthread(&CSendCloudFile::SendThreadWrapper, this);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSendCloudFile::SendThread()
+{
+ // @todo : SS_DLG_DESCRIPTION and SS_DLG_DELETEAFTERSSEND are of no use as of now since we don't track upload progress
+
+ CFUPLOADDATA ud = { m_service, m_pszFile, L"SendSS" };
+ CFUPLOADRESULT ur = { };
+
+ if (CallService(MS_CLOUDFILE_UPLOAD, (WPARAM)&ud, (LPARAM)&ur)) {
+ Error(LPGENW("%s (%i):\nCould not add a share to the CloudFile plugin."), TranslateW(m_pszSendTyp), 0);
+ Exit(ACKRESULT_FAILED);
+ return;
+ }
+
+ m_URL = ur.link;
+ if (m_URL)
+ svcSendMsgExit(m_URL);
+ else
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendCloudFile::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendCloudFile*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendEmail.cpp b/plugins/SendScreenshotPlus/src/CSendEmail.cpp index 8490004f21..2af6b0b174 100644 --- a/plugins/SendScreenshotPlus/src/CSendEmail.cpp +++ b/plugins/SendScreenshotPlus/src/CSendEmail.cpp @@ -1,201 +1,201 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -CSendEmail::CSendEmail(HWND Owner, MCONTACT hContact, bool /*bAsync*/) - : CSend(Owner, hContact, true) -{ - m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_DELETEAFTERSSEND; // SS_DLG_AUTOSEND | ; - m_pszSendTyp = LPGENW("Email transfer"); - m_pszFileA = nullptr; - m_pszFileName = nullptr; - m_Email = nullptr; - m_FriendlyName = nullptr; - m_Subject = nullptr; -} - -CSendEmail::~CSendEmail() -{ - mir_free(m_pszFileA); - mir_free(m_pszFileName); - mir_free(m_Email); - mir_free(m_FriendlyName); - mir_free(m_Subject); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendEmail::Send() -{ - if (!m_hContact) return 1; - mir_free(m_pszFileName); - m_pszFileName = GetFileNameA(m_pszFile); - - mir_free(m_pszFileA); - m_pszFileA = mir_u2a(m_pszFile); - - m_Email = mir_u2a(ptrW(Contact::GetInfo(CNF_EMAIL, m_hContact, m_pszProto))); - m_FriendlyName = mir_u2a(ptrW(Contact::GetInfo(CNF_DISPLAY, m_hContact, m_pszProto))); - m_Subject = mir_u2a(m_pszFileDesc); - - // SendByEmail(m_pszFileA, "", m_FriendlyName, m_Email, m_Subject); - - // start Send thread - mir_forkthread(&CSendEmail::SendThreadWrapper, this); - return 0; -} - -void CSendEmail::SendThread() -{ - // This code based on SentTo.exe application. - // The default mail client for Simple MAPI or MAPI calls is defined by the - // HKLM\Software\Clients\Mail::(default) registry value. - - MapiFileDesc arrfileDesc[1]; - - typedef ULONG(FAR PASCAL *MAPIFUNC)(LHANDLE, ULONG, lpMapiMessage, FLAGS, ULONG); - MapiMessage Msg; - MAPIFUNC lpMAPISendMail; - - HINSTANCE hMAPILib = ::LoadLibrary(L"MAPI32.DLL"); - if (hMAPILib == nullptr) { - // return -1; - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); return; - } - - lpMAPISendMail = (MAPIFUNC)GetProcAddress(hMAPILib, "MAPISendMail"); - if (lpMAPISendMail == nullptr) { - ::FreeLibrary(hMAPILib); - // return -2; - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); return; - } - - memset(&Msg, 0, sizeof(Msg)); - - arrfileDesc[0].ulReserved = 0; - arrfileDesc[0].flFlags = 0; - arrfileDesc[0].lpFileType = nullptr; - arrfileDesc[0].nPosition = -1; - arrfileDesc[0].lpszPathName = m_pszFileA; - arrfileDesc[0].lpszFileName = nullptr; - - Msg.nFileCount = 1; - Msg.lpFiles = arrfileDesc; - Msg.lpszNoteText = ""; // body - Msg.lpszSubject = m_Subject; // subject - - Msg.nRecipCount = 1; - MapiRecipDesc recip; - recip.ulReserved = 0; - recip.ulRecipClass = MAPI_TO; - - if (m_FriendlyName && m_FriendlyName[0] != NULL) { - recip.lpszName = m_FriendlyName; // friendly name set to contact's name - } - else { - recip.lpszName = m_Email; // friendly name set to contact's email - } - - recip.lpszAddress = m_Email; // email - recip.ulEIDSize = 0; - recip.lpEntryID = nullptr; - Msg.lpRecips = &recip; - - try { - int res = lpMAPISendMail(NULL, NULL, &Msg, MAPI_LOGON_UI | MAPI_DIALOG, 0); - ::FreeLibrary(hMAPILib); - - wchar_t* err; - switch (res) { - case SUCCESS_SUCCESS: - // The call succeeded and the message was sent. - Exit(ACKRESULT_SUCCESS); return; - // No message was sent - case MAPI_E_AMBIGUOUS_RECIPIENT: - err = LPGENW("A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set"); - break; - case MAPI_E_ATTACHMENT_NOT_FOUND: - err = LPGENW("The specified attachment was not found"); - break; - case MAPI_E_ATTACHMENT_OPEN_FAILURE: - err = LPGENW("The specified attachment could not be opened"); - break; - case MAPI_E_BAD_RECIPTYPE: - err = LPGENW("The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC"); - break; - case MAPI_E_FAILURE: - err = LPGENW("One or more unspecified errors occurred"); - break; - case MAPI_E_INSUFFICIENT_MEMORY: - err = LPGENW("There was insufficient memory to proceed"); - break; - case MAPI_E_INVALID_RECIPS: - err = LPGENW("One or more recipients were invalid or did not resolve to any address"); - break; - case MAPI_E_LOGIN_FAILURE: - err = LPGENW("There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed"); - break; - case MAPI_E_TEXT_TOO_LARGE: - err = LPGENW("The text in the message was too large"); - break; - case MAPI_E_TOO_MANY_FILES: - err = LPGENW("There were too many file attachments"); - break; - case MAPI_E_TOO_MANY_RECIPIENTS: - err = LPGENW("There were too many recipients"); - break; - case MAPI_E_UNKNOWN_RECIPIENT: - err = LPGENW("A recipient did not appear in the address list"); - break; - case MAPI_E_USER_ABORT: - err = LPGENW("The user canceled one of the dialog boxes"); - break; - default: - err = LPGENW("Unknown Error"); - break; - } - Error(SS_ERR_MAPI, res, err); - Exit(ACKRESULT_FAILED); - } - catch (...) { - ::FreeLibrary(hMAPILib); - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); - return; - } -} - -void CSendEmail::SendThreadWrapper(void * Obj) -{ - reinterpret_cast<CSendEmail*>(Obj)->SendThread(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendEmail::CSendEmail(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_DELETEAFTERSSEND; // SS_DLG_AUTOSEND | ;
+ m_pszSendTyp = LPGENW("Email transfer");
+ m_pszFileA = nullptr;
+ m_pszFileName = nullptr;
+ m_Email = nullptr;
+ m_FriendlyName = nullptr;
+ m_Subject = nullptr;
+}
+
+CSendEmail::~CSendEmail()
+{
+ mir_free(m_pszFileA);
+ mir_free(m_pszFileName);
+ mir_free(m_Email);
+ mir_free(m_FriendlyName);
+ mir_free(m_Subject);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendEmail::Send()
+{
+ if (!m_hContact) return 1;
+ mir_free(m_pszFileName);
+ m_pszFileName = GetFileNameA(m_pszFile);
+
+ mir_free(m_pszFileA);
+ m_pszFileA = mir_u2a(m_pszFile);
+
+ m_Email = mir_u2a(ptrW(Contact::GetInfo(CNF_EMAIL, m_hContact, m_pszProto)));
+ m_FriendlyName = mir_u2a(ptrW(Contact::GetInfo(CNF_DISPLAY, m_hContact, m_pszProto)));
+ m_Subject = mir_u2a(m_pszFileDesc);
+
+ // SendByEmail(m_pszFileA, "", m_FriendlyName, m_Email, m_Subject);
+
+ // start Send thread
+ mir_forkthread(&CSendEmail::SendThreadWrapper, this);
+ return 0;
+}
+
+void CSendEmail::SendThread()
+{
+ // This code based on SentTo.exe application.
+ // The default mail client for Simple MAPI or MAPI calls is defined by the
+ // HKLM\Software\Clients\Mail::(default) registry value.
+
+ MapiFileDesc arrfileDesc[1];
+
+ typedef ULONG(FAR PASCAL *MAPIFUNC)(LHANDLE, ULONG, lpMapiMessage, FLAGS, ULONG);
+ MapiMessage Msg;
+ MAPIFUNC lpMAPISendMail;
+
+ HINSTANCE hMAPILib = ::LoadLibrary(L"MAPI32.DLL");
+ if (hMAPILib == nullptr) {
+ // return -1;
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+
+ lpMAPISendMail = (MAPIFUNC)GetProcAddress(hMAPILib, "MAPISendMail");
+ if (lpMAPISendMail == nullptr) {
+ ::FreeLibrary(hMAPILib);
+ // return -2;
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+
+ memset(&Msg, 0, sizeof(Msg));
+
+ arrfileDesc[0].ulReserved = 0;
+ arrfileDesc[0].flFlags = 0;
+ arrfileDesc[0].lpFileType = nullptr;
+ arrfileDesc[0].nPosition = -1;
+ arrfileDesc[0].lpszPathName = m_pszFileA;
+ arrfileDesc[0].lpszFileName = nullptr;
+
+ Msg.nFileCount = 1;
+ Msg.lpFiles = arrfileDesc;
+ Msg.lpszNoteText = ""; // body
+ Msg.lpszSubject = m_Subject; // subject
+
+ Msg.nRecipCount = 1;
+ MapiRecipDesc recip;
+ recip.ulReserved = 0;
+ recip.ulRecipClass = MAPI_TO;
+
+ if (m_FriendlyName && m_FriendlyName[0] != NULL) {
+ recip.lpszName = m_FriendlyName; // friendly name set to contact's name
+ }
+ else {
+ recip.lpszName = m_Email; // friendly name set to contact's email
+ }
+
+ recip.lpszAddress = m_Email; // email
+ recip.ulEIDSize = 0;
+ recip.lpEntryID = nullptr;
+ Msg.lpRecips = &recip;
+
+ try {
+ int res = lpMAPISendMail(NULL, NULL, &Msg, MAPI_LOGON_UI | MAPI_DIALOG, 0);
+ ::FreeLibrary(hMAPILib);
+
+ wchar_t* err;
+ switch (res) {
+ case SUCCESS_SUCCESS:
+ // The call succeeded and the message was sent.
+ Exit(ACKRESULT_SUCCESS); return;
+ // No message was sent
+ case MAPI_E_AMBIGUOUS_RECIPIENT:
+ err = LPGENW("A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set");
+ break;
+ case MAPI_E_ATTACHMENT_NOT_FOUND:
+ err = LPGENW("The specified attachment was not found");
+ break;
+ case MAPI_E_ATTACHMENT_OPEN_FAILURE:
+ err = LPGENW("The specified attachment could not be opened");
+ break;
+ case MAPI_E_BAD_RECIPTYPE:
+ err = LPGENW("The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC");
+ break;
+ case MAPI_E_FAILURE:
+ err = LPGENW("One or more unspecified errors occurred");
+ break;
+ case MAPI_E_INSUFFICIENT_MEMORY:
+ err = LPGENW("There was insufficient memory to proceed");
+ break;
+ case MAPI_E_INVALID_RECIPS:
+ err = LPGENW("One or more recipients were invalid or did not resolve to any address");
+ break;
+ case MAPI_E_LOGIN_FAILURE:
+ err = LPGENW("There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed");
+ break;
+ case MAPI_E_TEXT_TOO_LARGE:
+ err = LPGENW("The text in the message was too large");
+ break;
+ case MAPI_E_TOO_MANY_FILES:
+ err = LPGENW("There were too many file attachments");
+ break;
+ case MAPI_E_TOO_MANY_RECIPIENTS:
+ err = LPGENW("There were too many recipients");
+ break;
+ case MAPI_E_UNKNOWN_RECIPIENT:
+ err = LPGENW("A recipient did not appear in the address list");
+ break;
+ case MAPI_E_USER_ABORT:
+ err = LPGENW("The user canceled one of the dialog boxes");
+ break;
+ default:
+ err = LPGENW("Unknown Error");
+ break;
+ }
+ Error(SS_ERR_MAPI, res, err);
+ Exit(ACKRESULT_FAILED);
+ }
+ catch (...) {
+ ::FreeLibrary(hMAPILib);
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return;
+ }
+}
+
+void CSendEmail::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendEmail*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendEmail.h b/plugins/SendScreenshotPlus/src/CSendEmail.h index 7e765f747f..1bc852975e 100644 --- a/plugins/SendScreenshotPlus/src/CSendEmail.h +++ b/plugins/SendScreenshotPlus/src/CSendEmail.h @@ -1,53 +1,53 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_EMAIL_H -#define _CSEND_EMAIL_H - -class CSendEmail : public CSend -{ - -public: - // Deklaration Standardkonstruktor/Standarddestructor - CSendEmail(HWND Owner, MCONTACT hContact, bool bAsync); - ~CSendEmail(); - - int Send() override; - -protected: - char* m_pszFileA; - char* m_pszFileName; - char* m_Email; - char* m_FriendlyName; - char* m_Subject; - - void SendThread(); - static void SendThreadWrapper(void * Obj); -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_EMAIL_H
+#define _CSEND_EMAIL_H
+
+class CSendEmail : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendEmail(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendEmail();
+
+ int Send() override;
+
+protected:
+ char* m_pszFileA;
+ char* m_pszFileName;
+ char* m_Email;
+ char* m_FriendlyName;
+ char* m_Subject;
+
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp b/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp index e4f5cc11d1..1e4ae57e2c 100644 --- a/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp +++ b/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp @@ -1,93 +1,93 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -CSendFTPFile::CSendFTPFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/) - : CSend(Owner, hContact, true) -{ - m_EnableItem = 0; //SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND; - m_pszSendTyp = LPGENW("FTPFile transfer"); - m_pszFileName = nullptr; -} - -CSendFTPFile::~CSendFTPFile() -{ - mir_free(m_pszFileName); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendFTPFile::Send() -{ - if (!m_hContact) return 1; - /********************************************************************************************* - * Send file (files) to the FTP server and copy file URL - * to message log or clipboard (according to plugin setting) - * wParam = (HANDLE)hContact - * lParam = (char *)filename - * Filename format is same as GetOpenFileName (OPENFILENAME.lpstrFile) returns, - * see http://msdn2.microsoft.com/en-us/library/ms646839.aspx - * Returns 0 on success or nonzero on failure - * if (!wParam || !lParam) return 1 - ********************************************************************************************/ - mir_free(m_pszFileName); - m_pszFileName = GetFileNameA(m_pszFile); - size_t size = sizeof(char)*(mir_strlen(m_pszFileName) + 2); - m_pszFileName = (char*)mir_realloc(m_pszFileName, size); - m_pszFileName[size - 1] = NULL; - - // start Send thread - mir_forkthread(&CSendFTPFile::SendThreadWrapper, this); - return 0; -} - -void CSendFTPFile::SendThread() -{ - - INT_PTR ret = FTPFileUploadA(m_hContact, FNUM_DEFAULT, FMODE_RAWFILE, &m_pszFileName, 1); - if (ret != 0) { - Error(LPGENW("%s (%i):\nCould not add a share to the FTP File plugin."), TranslateW(m_pszSendTyp), ret); - Exit(ret); return; - } - - // Can't delete the file since FTP File plugin will use it - m_bDeleteAfterSend = false; - - if (m_URL && *m_URL) {/// @fixme : m_URL never set - svcSendMsgExit(m_URL); return; - } - Exit(ACKRESULT_FAILED); -} - -void CSendFTPFile::SendThreadWrapper(void * Obj) -{ - reinterpret_cast<CSendFTPFile*>(Obj)->SendThread(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendFTPFile::CSendFTPFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = 0; //SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("FTPFile transfer");
+ m_pszFileName = nullptr;
+}
+
+CSendFTPFile::~CSendFTPFile()
+{
+ mir_free(m_pszFileName);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendFTPFile::Send()
+{
+ if (!m_hContact) return 1;
+ /*********************************************************************************************
+ * Send file (files) to the FTP server and copy file URL
+ * to message log or clipboard (according to plugin setting)
+ * wParam = (HANDLE)hContact
+ * lParam = (char *)filename
+ * Filename format is same as GetOpenFileName (OPENFILENAME.lpstrFile) returns,
+ * see http://msdn2.microsoft.com/en-us/library/ms646839.aspx
+ * Returns 0 on success or nonzero on failure
+ * if (!wParam || !lParam) return 1
+ ********************************************************************************************/
+ mir_free(m_pszFileName);
+ m_pszFileName = GetFileNameA(m_pszFile);
+ size_t size = sizeof(char)*(mir_strlen(m_pszFileName) + 2);
+ m_pszFileName = (char*)mir_realloc(m_pszFileName, size);
+ m_pszFileName[size - 1] = NULL;
+
+ // start Send thread
+ mir_forkthread(&CSendFTPFile::SendThreadWrapper, this);
+ return 0;
+}
+
+void CSendFTPFile::SendThread()
+{
+
+ INT_PTR ret = FTPFileUploadA(m_hContact, FNUM_DEFAULT, FMODE_RAWFILE, &m_pszFileName, 1);
+ if (ret != 0) {
+ Error(LPGENW("%s (%i):\nCould not add a share to the FTP File plugin."), TranslateW(m_pszSendTyp), ret);
+ Exit(ret); return;
+ }
+
+ // Can't delete the file since FTP File plugin will use it
+ m_bDeleteAfterSend = false;
+
+ if (m_URL && *m_URL) {/// @fixme : m_URL never set
+ svcSendMsgExit(m_URL); return;
+ }
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendFTPFile::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendFTPFile*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendFTPFile.h b/plugins/SendScreenshotPlus/src/CSendFTPFile.h index 9094f3f4f7..b57f9243b2 100644 --- a/plugins/SendScreenshotPlus/src/CSendFTPFile.h +++ b/plugins/SendScreenshotPlus/src/CSendFTPFile.h @@ -1,48 +1,48 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_FTP_FILE_H -#define _CSEND_FTP_FILE_H - -class CSendFTPFile : public CSend -{ - -public: - // Deklaration Standardkonstruktor/Standarddestructor - CSendFTPFile(HWND Owner, MCONTACT hContact, bool bAsync); - ~CSendFTPFile(); - - int Send() override; - -protected: - char* m_pszFileName; - void SendThread(); - static void SendThreadWrapper(void * Obj); -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_FTP_FILE_H
+#define _CSEND_FTP_FILE_H
+
+class CSendFTPFile : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendFTPFile(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendFTPFile();
+
+ int Send() override;
+
+protected:
+ char* m_pszFileName;
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendFile.cpp b/plugins/SendScreenshotPlus/src/CSendFile.cpp index 550ad4bda4..ed1500a03f 100644 --- a/plugins/SendScreenshotPlus/src/CSendFile.cpp +++ b/plugins/SendScreenshotPlus/src/CSendFile.cpp @@ -1,50 +1,50 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -CSendFile::CSendFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/) - : CSend(Owner, hContact, true) -{ - m_EnableItem = SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND | SS_DLG_DESCRIPTION; - m_pszSendTyp = LPGENW("File transfer"); -} - -CSendFile::~CSendFile() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendFile::Send() -{ - svcSendFileExit(); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendFile::CSendFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND | SS_DLG_DESCRIPTION;
+ m_pszSendTyp = LPGENW("File transfer");
+}
+
+CSendFile::~CSendFile()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendFile::Send()
+{
+ svcSendFileExit();
+ return 0;
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendFile.h b/plugins/SendScreenshotPlus/src/CSendFile.h index b30a1ce6e3..e2b7462725 100644 --- a/plugins/SendScreenshotPlus/src/CSendFile.h +++ b/plugins/SendScreenshotPlus/src/CSendFile.h @@ -1,43 +1,43 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_FILE_H -#define _CSEND_FILE_H - -class CSendFile : public CSend -{ - -public: - // Deklaration Standardkonstruktor/Standarddestructor - CSendFile(HWND Owner, MCONTACT hContact, bool bAsync); - ~CSendFile(); - - int Send() override; -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_FILE_H
+#define _CSEND_FILE_H
+
+class CSendFile : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendFile(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendFile();
+
+ int Send() override;
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp b/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp index 9075d70951..7815d49bfb 100644 --- a/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp +++ b/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp @@ -1,117 +1,117 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -INT_PTR(*g_MirCallService)(const char *, WPARAM, LPARAM) = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// - -CSendHTTPServer::CSendHTTPServer(HWND Owner, MCONTACT hContact, bool /*bAsync*/) - : CSend(Owner, hContact, true) -{ - m_EnableItem = SS_DLG_DESCRIPTION; //| SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND; - m_pszSendTyp = LPGENW("HTTPServer transfer"); - m_pszFileName = nullptr; - m_fsi_pszRealPath = nullptr; -} - -CSendHTTPServer::~CSendHTTPServer() -{ - mir_free(m_pszFileName); - mir_free(m_fsi_pszRealPath); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendHTTPServer::Send() -{ - if (!m_hContact) return 1; - if (CallService(MS_HTTP_ACCEPT_CONNECTIONS, TRUE, 0) != 0) { - Error(LPGENW("Could not start the HTTP Server plugin.")); - Exit(ACKRESULT_FAILED); - return !m_bAsync; - } - - if (!m_pszFileName) { - m_pszFileName = GetFileNameA(m_pszFile); - } - - m_fsi_pszSrvPath.Empty(); - m_fsi_pszSrvPath.AppendChar('/'); - m_fsi_pszSrvPath.Append(m_pszFileName); - - replaceStr(m_fsi_pszRealPath, _T2A(m_pszFile)); - - memset(&m_fsi, 0, sizeof(m_fsi)); - m_fsi.lStructSize = sizeof(STFileShareInfo); - m_fsi.nMaxDownloads = -1; // -1 = infinite - m_fsi.pszRealPath = m_fsi_pszRealPath; - - // start Send thread - mir_forkthread(&CSendHTTPServer::SendThreadWrapper, this); - return 0; -} - -void CSendHTTPServer::SendThread() -{ - INT_PTR ret; - - if (ServiceExists(MS_HTTP_GET_LINK)) { - // patched plugin version - ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi); - if (!ret) { - m_URL = ptrA((char*)CallService(MS_HTTP_GET_LINK, (WPARAM)m_fsi.pszSrvPath, 0)); - } - } - else { - // original plugin - m_fsi.dwOptions = OPT_SEND_LINK; - - // send DATA and wait for reply - ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi); - } - - if (ret != 0) { - Error(LPGENW("%s (%i):\nCould not add a share to the HTTP Server plugin."), TranslateW(m_pszSendTyp), ret); - Exit(ret); return; - } - - // Share the file by HTTP Server plugin, SendSS does not own the file anymore = auto-delete won't work - m_bDeleteAfterSend = false; - - if (m_URL && *m_URL) { - svcSendMsgExit(m_URL); return; - } - Exit(ACKRESULT_FAILED); -} - -void CSendHTTPServer::SendThreadWrapper(void * Obj) -{ - reinterpret_cast<CSendHTTPServer*>(Obj)->SendThread(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+INT_PTR(*g_MirCallService)(const char *, WPARAM, LPARAM) = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendHTTPServer::CSendHTTPServer(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION; //| SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("HTTPServer transfer");
+ m_pszFileName = nullptr;
+ m_fsi_pszRealPath = nullptr;
+}
+
+CSendHTTPServer::~CSendHTTPServer()
+{
+ mir_free(m_pszFileName);
+ mir_free(m_fsi_pszRealPath);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendHTTPServer::Send()
+{
+ if (!m_hContact) return 1;
+ if (CallService(MS_HTTP_ACCEPT_CONNECTIONS, TRUE, 0) != 0) {
+ Error(LPGENW("Could not start the HTTP Server plugin."));
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+
+ if (!m_pszFileName) {
+ m_pszFileName = GetFileNameA(m_pszFile);
+ }
+
+ m_fsi_pszSrvPath.Empty();
+ m_fsi_pszSrvPath.AppendChar('/');
+ m_fsi_pszSrvPath.Append(m_pszFileName);
+
+ replaceStr(m_fsi_pszRealPath, _T2A(m_pszFile));
+
+ memset(&m_fsi, 0, sizeof(m_fsi));
+ m_fsi.lStructSize = sizeof(STFileShareInfo);
+ m_fsi.nMaxDownloads = -1; // -1 = infinite
+ m_fsi.pszRealPath = m_fsi_pszRealPath;
+
+ // start Send thread
+ mir_forkthread(&CSendHTTPServer::SendThreadWrapper, this);
+ return 0;
+}
+
+void CSendHTTPServer::SendThread()
+{
+ INT_PTR ret;
+
+ if (ServiceExists(MS_HTTP_GET_LINK)) {
+ // patched plugin version
+ ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi);
+ if (!ret) {
+ m_URL = ptrA((char*)CallService(MS_HTTP_GET_LINK, (WPARAM)m_fsi.pszSrvPath, 0));
+ }
+ }
+ else {
+ // original plugin
+ m_fsi.dwOptions = OPT_SEND_LINK;
+
+ // send DATA and wait for reply
+ ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi);
+ }
+
+ if (ret != 0) {
+ Error(LPGENW("%s (%i):\nCould not add a share to the HTTP Server plugin."), TranslateW(m_pszSendTyp), ret);
+ Exit(ret); return;
+ }
+
+ // Share the file by HTTP Server plugin, SendSS does not own the file anymore = auto-delete won't work
+ m_bDeleteAfterSend = false;
+
+ if (m_URL && *m_URL) {
+ svcSendMsgExit(m_URL); return;
+ }
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendHTTPServer::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendHTTPServer*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHTTPServer.h b/plugins/SendScreenshotPlus/src/CSendHTTPServer.h index e11ac3fae0..aa5dec4030 100644 --- a/plugins/SendScreenshotPlus/src/CSendHTTPServer.h +++ b/plugins/SendScreenshotPlus/src/CSendHTTPServer.h @@ -1,55 +1,55 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_HTTP_SERVER_H -#define _CSEND_HTTP_SERVER_H - -class CSendHTTPServer : public CSend -{ -public: - // Deklaration Standardkonstruktor/Standarddestructor - CSendHTTPServer(HWND Owner, MCONTACT hContact, bool bAsync); - ~CSendHTTPServer(); - - int Send() override; - -protected: - char* m_pszFileName; - CMStringA m_fsi_pszSrvPath; - char* m_fsi_pszRealPath; - - STFileShareInfo m_fsi; - - void SendThread(); - static void SendThreadWrapper(void * Obj); - - typedef std::map<HANDLE, CSendHTTPServer *> CContactMapping; - static CContactMapping _CContactMapping; -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_HTTP_SERVER_H
+#define _CSEND_HTTP_SERVER_H
+
+class CSendHTTPServer : public CSend
+{
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendHTTPServer(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendHTTPServer();
+
+ int Send() override;
+
+protected:
+ char* m_pszFileName;
+ CMStringA m_fsi_pszSrvPath;
+ char* m_fsi_pszRealPath;
+
+ STFileShareInfo m_fsi;
+
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+
+ typedef std::map<HANDLE, CSendHTTPServer *> CContactMapping;
+ static CContactMapping _CContactMapping;
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp index 7940487e88..5a2a4d8a34 100644 --- a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp +++ b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp @@ -1,118 +1,118 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -CSendHost_ImageShack::CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync) - : CSend(Owner, hContact, bAsync) -{ - m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND; - m_pszSendTyp = LPGENW("Image upload"); -} - -CSendHost_ImageShack::~CSendHost_ImageShack() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendHost_ImageShack::Send() -{ - if (!g_hNetlibUser) { // check Netlib - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); - return !m_bAsync; - } - memset(&m_nlhr, 0, sizeof(m_nlhr)); - char* tmp; tmp = mir_u2a(m_pszFile); - HTTPFormData frm[] = { - // { "Referer", HTTPFORM_HEADER("http://www.imageshack.us/upload_api.php") }, - { "fileupload", HTTPFORM_FILE(tmp) }, - // { "rembar", "yes" },// no info bar on thumb - { "public", "no" }, - { "key", HTTPFORM_8BIT(DEVKEY_IMAGESHACK) }, - }; - int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "http://imageshack.us/upload_api.php", frm, sizeof(frm) / sizeof(HTTPFormData)); - mir_free(tmp); - if (error) - return !m_bAsync; - // start upload thread - if (m_bAsync) { - mir_forkthread(&CSendHost_ImageShack::SendThreadWrapper, this); - return 0; - } - SendThread(); - return 1; -} - -void CSendHost_ImageShack::SendThread() -{ - // send DATA and wait for m_nlreply - NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &m_nlhr)); - HTTPFormDestroy(&m_nlhr); - if (reply) { - if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) { - reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated - const char* url = nullptr; - url = GetHTMLContent(reply->pData, "<image_link>", "</image_link>"); - if (url && *url) { - m_URLthumb = m_URL = url; - - int idx = m_URLthumb.ReverseFind('.'); - if (idx != -1 && m_URLthumb.GetLength() - idx > 2) - m_URLthumb.Insert(idx + 1, "th"); - else - m_URLthumb.Empty(); - - svcSendMsgExit(url); - return; - } - - url = GetHTMLContent(reply->pData, "<error ", "</error>"); - wchar_t* err = nullptr; - if (url) err = mir_a2u(url); - if (!err || !*err) { // fallback to server response mess - mir_free(err); - err = mir_a2u(reply->pData); - } - Error(L"%s", err); - mir_free(err); - } - else Error(SS_ERR_RESPONSE, m_pszSendTyp, reply->resultCode); - } - else Error(SS_ERR_NORESPONSE, m_pszSendTyp, m_nlhr.resultCode); - - Exit(ACKRESULT_FAILED); -} - -void CSendHost_ImageShack::SendThreadWrapper(void * Obj) -{ - reinterpret_cast<CSendHost_ImageShack*>(Obj)->SendThread(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendHost_ImageShack::CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync)
+ : CSend(Owner, hContact, bAsync)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("Image upload");
+}
+
+CSendHost_ImageShack::~CSendHost_ImageShack()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendHost_ImageShack::Send()
+{
+ if (!g_hNetlibUser) { // check Netlib
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+ memset(&m_nlhr, 0, sizeof(m_nlhr));
+ char* tmp; tmp = mir_u2a(m_pszFile);
+ HTTPFormData frm[] = {
+ // { "Referer", HTTPFORM_HEADER("http://www.imageshack.us/upload_api.php") },
+ { "fileupload", HTTPFORM_FILE(tmp) },
+ // { "rembar", "yes" },// no info bar on thumb
+ { "public", "no" },
+ { "key", HTTPFORM_8BIT(DEVKEY_IMAGESHACK) },
+ };
+ int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "http://imageshack.us/upload_api.php", frm, sizeof(frm) / sizeof(HTTPFormData));
+ mir_free(tmp);
+ if (error)
+ return !m_bAsync;
+ // start upload thread
+ if (m_bAsync) {
+ mir_forkthread(&CSendHost_ImageShack::SendThreadWrapper, this);
+ return 0;
+ }
+ SendThread();
+ return 1;
+}
+
+void CSendHost_ImageShack::SendThread()
+{
+ // send DATA and wait for m_nlreply
+ NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &m_nlhr));
+ HTTPFormDestroy(&m_nlhr);
+ if (reply) {
+ if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) {
+ reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated
+ const char* url = nullptr;
+ url = GetHTMLContent(reply->pData, "<image_link>", "</image_link>");
+ if (url && *url) {
+ m_URLthumb = m_URL = url;
+
+ int idx = m_URLthumb.ReverseFind('.');
+ if (idx != -1 && m_URLthumb.GetLength() - idx > 2)
+ m_URLthumb.Insert(idx + 1, "th");
+ else
+ m_URLthumb.Empty();
+
+ svcSendMsgExit(url);
+ return;
+ }
+
+ url = GetHTMLContent(reply->pData, "<error ", "</error>");
+ wchar_t* err = nullptr;
+ if (url) err = mir_a2u(url);
+ if (!err || !*err) { // fallback to server response mess
+ mir_free(err);
+ err = mir_a2u(reply->pData);
+ }
+ Error(L"%s", err);
+ mir_free(err);
+ }
+ else Error(SS_ERR_RESPONSE, m_pszSendTyp, reply->resultCode);
+ }
+ else Error(SS_ERR_NORESPONSE, m_pszSendTyp, m_nlhr.resultCode);
+
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendHost_ImageShack::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendHost_ImageShack*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h index a03647c063..7c58094aac 100644 --- a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h +++ b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h @@ -1,49 +1,49 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _CSEND_IMAGESHACK_H -#define _CSEND_IMAGESHACK_H - -class CSendHost_ImageShack : public CSend -{ - -public: - // Deklaration Standardkonstruktor/Standarddestructor - CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync); - ~CSendHost_ImageShack(); - - int Send() override; - -protected: - NETLIBHTTPREQUEST m_nlhr; - - void SendThread(); - static void SendThreadWrapper(void * Obj); -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_IMAGESHACK_H
+#define _CSEND_IMAGESHACK_H
+
+class CSendHost_ImageShack : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendHost_ImageShack();
+
+ int Send() override;
+
+protected:
+ NETLIBHTTPREQUEST m_nlhr;
+
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp b/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp index 4aa5ccc44f..bde9f158bb 100644 --- a/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp +++ b/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp @@ -1,86 +1,86 @@ -/* - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org) - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. -*/ -#include "stdafx.h" - -CSendHost_Imgur::CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync) - : CSend(Owner, hContact, bAsync) -{ - m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND; - m_pszSendTyp = LPGENW("Image upload"); -} - -CSendHost_Imgur::~CSendHost_Imgur() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CSendHost_Imgur::Send() -{ - if (!g_hNetlibUser) { // check Netlib - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); - return !m_bAsync; - } - memset(&m_nlhr, 0, sizeof(m_nlhr)); - char* tmp; tmp = mir_u2a(m_pszFile); - HTTPFormData frm[] = { - { "Authorization", HTTPFORM_HEADER("Client-ID 2a7303d78abe041") }, - { "image", HTTPFORM_FILE(tmp) }, - }; - - int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "https://api.imgur.com/3/image", frm, _countof(frm)); - mir_free(tmp); - if (error) - return !m_bAsync; - // start upload thread - if (m_bAsync) { - mir_forkthread(&CSendHost_Imgur::SendThread, this); - return 0; - } - SendThread(this); - return 1; -} - -void CSendHost_Imgur::SendThread(void* obj) -{ - CSendHost_Imgur *self = (CSendHost_Imgur*)obj; - // send DATA and wait for m_nlreply - NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr)); - self->HTTPFormDestroy(&self->m_nlhr); - if (reply) { - if (reply->dataLength) { - JSONROOT root(reply->pData); - if (root) { - if ((*root)["success"].as_bool()) { - self->m_URL = (*root)["data"]["link"].as_mstring(); - int idx = self->m_URL.ReverseFind('.'); - if (idx != -1) { - self->m_URLthumb = self->m_URL; - self->m_URLthumb.Insert(idx, 'm'); - } - self->svcSendMsgExit(self->m_URL); return; - } - else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, (*root)["status"].as_int(), 0); - } - else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode); - } - else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode); - } - else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode); - - self->Exit(ACKRESULT_FAILED); -} +/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#include "stdafx.h"
+
+CSendHost_Imgur::CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync)
+ : CSend(Owner, hContact, bAsync)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("Image upload");
+}
+
+CSendHost_Imgur::~CSendHost_Imgur()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendHost_Imgur::Send()
+{
+ if (!g_hNetlibUser) { // check Netlib
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+ memset(&m_nlhr, 0, sizeof(m_nlhr));
+ char* tmp; tmp = mir_u2a(m_pszFile);
+ HTTPFormData frm[] = {
+ { "Authorization", HTTPFORM_HEADER("Client-ID 2a7303d78abe041") },
+ { "image", HTTPFORM_FILE(tmp) },
+ };
+
+ int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "https://api.imgur.com/3/image", frm, _countof(frm));
+ mir_free(tmp);
+ if (error)
+ return !m_bAsync;
+ // start upload thread
+ if (m_bAsync) {
+ mir_forkthread(&CSendHost_Imgur::SendThread, this);
+ return 0;
+ }
+ SendThread(this);
+ return 1;
+}
+
+void CSendHost_Imgur::SendThread(void* obj)
+{
+ CSendHost_Imgur *self = (CSendHost_Imgur*)obj;
+ // send DATA and wait for m_nlreply
+ NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr));
+ self->HTTPFormDestroy(&self->m_nlhr);
+ if (reply) {
+ if (reply->dataLength) {
+ JSONROOT root(reply->pData);
+ if (root) {
+ if ((*root)["success"].as_bool()) {
+ self->m_URL = (*root)["data"]["link"].as_mstring();
+ int idx = self->m_URL.ReverseFind('.');
+ if (idx != -1) {
+ self->m_URLthumb = self->m_URL;
+ self->m_URLthumb.Insert(idx, 'm');
+ }
+ self->svcSendMsgExit(self->m_URL); return;
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, (*root)["status"].as_int(), 0);
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
+ }
+ else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode);
+
+ self->Exit(ACKRESULT_FAILED);
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_imgur.h b/plugins/SendScreenshotPlus/src/CSendHost_imgur.h index b64b118391..544afecd53 100644 --- a/plugins/SendScreenshotPlus/src/CSendHost_imgur.h +++ b/plugins/SendScreenshotPlus/src/CSendHost_imgur.h @@ -1,30 +1,30 @@ -/* - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org) - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. -*/ -#ifndef _CSEND_HOST_IMGUR_H -#define _CSEND_HOST_IMGUR_H -class CSendHost_Imgur : public CSend { -// API: http://api.imgur.com/endpoints/image - public: - CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync); - ~CSendHost_Imgur(); - - int Send() override; - - protected: - NETLIBHTTPREQUEST m_nlhr; - static void SendThread(void* obj); -}; -#endif +/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#ifndef _CSEND_HOST_IMGUR_H
+#define _CSEND_HOST_IMGUR_H
+class CSendHost_Imgur : public CSend {
+// API: http://api.imgur.com/endpoints/image
+ public:
+ CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendHost_Imgur();
+
+ int Send() override;
+
+ protected:
+ NETLIBHTTPREQUEST m_nlhr;
+ static void SendThread(void* obj);
+};
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp index 0b6c6c2311..58651ae85d 100644 --- a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp +++ b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp @@ -1,105 +1,105 @@ -/* - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org) - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. -*/ -#include "stdafx.h" - -CSendHost_UploadPie::CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire) - : m_expire(expire), CSend(Owner, hContact, bAsync) -{ - m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND; - m_pszSendTyp = LPGENW("Image upload"); -} - -CSendHost_UploadPie::~CSendHost_UploadPie() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static const char kHostURL[] = "https://uploadpie.com/"; - -int CSendHost_UploadPie::Send() -{ - if (!g_hNetlibUser) { // check Netlib - Error(SS_ERR_INIT, m_pszSendTyp); - Exit(ACKRESULT_FAILED); - return !m_bAsync; - } - memset(&m_nlhr, 0, sizeof(m_nlhr)); - char* tmp; tmp = mir_u2a(m_pszFile); - HTTPFormData frm[] = { - { "MAX_FILE_SIZE", HTTPFORM_INT(3145728) }, - { "upload", HTTPFORM_INT(1) }, - { "uploadedfile", HTTPFORM_FILE(tmp) }, - { "expire", HTTPFORM_INT(m_expire) }, - }; - - int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, kHostURL, frm, _countof(frm)); - mir_free(tmp); - if (error) - return !m_bAsync; - - // start upload thread - if (m_bAsync) { - mir_forkthread(&CSendHost_UploadPie::SendThread, this); - return 0; - } - SendThread(this); - return 1; -} - -void CSendHost_UploadPie::SendThread(void* obj) -{ - CSendHost_UploadPie* self = (CSendHost_UploadPie*)obj; - // send DATA and wait for m_nlreply - NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr)); - self->HTTPFormDestroy(&self->m_nlhr); - if (reply) { - if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) { - reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated - char* url = reply->pData; - do { - char* pos; - if ((url = strstr(url, kHostURL))) { - for (pos = url + _countof(kHostURL)-1; (*pos >= '0'&&*pos <= '9') || (*pos >= 'a'&&*pos <= 'z') || (*pos >= 'A'&&*pos <= 'Z') || *pos == '_' || *pos == '-' || *pos == '"' || *pos == '\''; ++pos) { - if (*pos == '"' || *pos == '\'') break; - } - if (url + _countof(kHostURL)-1 != pos && (*pos == '"' || *pos == '\'')) { - *pos = '\0'; - break; - } - ++url; - } - } while (url); - - if (url) { - self->m_URL = url; - self->svcSendMsgExit(url); return; - } - else { // check error mess from server - const char* err = GetHTMLContent(reply->pData, "<p id=\"error\"", "</p>"); - wchar_t* werr; - if (err) werr = mir_a2u(err); - else werr = mir_a2u(reply->pData); - self->Error(L"%s", werr); - mir_free(werr); - } - } - else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode); - } - else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode); - - self->Exit(ACKRESULT_FAILED); -} +/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#include "stdafx.h"
+
+CSendHost_UploadPie::CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire)
+ : m_expire(expire), CSend(Owner, hContact, bAsync)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("Image upload");
+}
+
+CSendHost_UploadPie::~CSendHost_UploadPie()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static const char kHostURL[] = "https://uploadpie.com/";
+
+int CSendHost_UploadPie::Send()
+{
+ if (!g_hNetlibUser) { // check Netlib
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+ memset(&m_nlhr, 0, sizeof(m_nlhr));
+ char* tmp; tmp = mir_u2a(m_pszFile);
+ HTTPFormData frm[] = {
+ { "MAX_FILE_SIZE", HTTPFORM_INT(3145728) },
+ { "upload", HTTPFORM_INT(1) },
+ { "uploadedfile", HTTPFORM_FILE(tmp) },
+ { "expire", HTTPFORM_INT(m_expire) },
+ };
+
+ int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, kHostURL, frm, _countof(frm));
+ mir_free(tmp);
+ if (error)
+ return !m_bAsync;
+
+ // start upload thread
+ if (m_bAsync) {
+ mir_forkthread(&CSendHost_UploadPie::SendThread, this);
+ return 0;
+ }
+ SendThread(this);
+ return 1;
+}
+
+void CSendHost_UploadPie::SendThread(void* obj)
+{
+ CSendHost_UploadPie* self = (CSendHost_UploadPie*)obj;
+ // send DATA and wait for m_nlreply
+ NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr));
+ self->HTTPFormDestroy(&self->m_nlhr);
+ if (reply) {
+ if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) {
+ reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated
+ char* url = reply->pData;
+ do {
+ char* pos;
+ if ((url = strstr(url, kHostURL))) {
+ for (pos = url + _countof(kHostURL)-1; (*pos >= '0'&&*pos <= '9') || (*pos >= 'a'&&*pos <= 'z') || (*pos >= 'A'&&*pos <= 'Z') || *pos == '_' || *pos == '-' || *pos == '"' || *pos == '\''; ++pos) {
+ if (*pos == '"' || *pos == '\'') break;
+ }
+ if (url + _countof(kHostURL)-1 != pos && (*pos == '"' || *pos == '\'')) {
+ *pos = '\0';
+ break;
+ }
+ ++url;
+ }
+ } while (url);
+
+ if (url) {
+ self->m_URL = url;
+ self->svcSendMsgExit(url); return;
+ }
+ else { // check error mess from server
+ const char* err = GetHTMLContent(reply->pData, "<p id=\"error\"", "</p>");
+ wchar_t* werr;
+ if (err) werr = mir_a2u(err);
+ else werr = mir_a2u(reply->pData);
+ self->Error(L"%s", werr);
+ mir_free(werr);
+ }
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
+ }
+ else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode);
+
+ self->Exit(ACKRESULT_FAILED);
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h index b1defe20c2..94cfdf1fbc 100644 --- a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h +++ b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h @@ -1,30 +1,30 @@ -/* - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org) - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. -*/ -#ifndef _CSEND_HOST_UPLOADPIE_H -#define _CSEND_HOST_UPLOADPIE_H -class CSendHost_UploadPie : public CSend { - public: - CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire); - ~CSendHost_UploadPie(); - - int Send() override; - - protected: - int m_expire; - NETLIBHTTPREQUEST m_nlhr; - static void SendThread(void* obj); -}; -#endif +/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#ifndef _CSEND_HOST_UPLOADPIE_H
+#define _CSEND_HOST_UPLOADPIE_H
+class CSendHost_UploadPie : public CSend {
+ public:
+ CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire);
+ ~CSendHost_UploadPie();
+
+ int Send() override;
+
+ protected:
+ int m_expire;
+ NETLIBHTTPREQUEST m_nlhr;
+ static void SendThread(void* obj);
+};
+#endif
diff --git a/plugins/SendScreenshotPlus/src/Main.cpp b/plugins/SendScreenshotPlus/src/Main.cpp index e3223f43cc..f2455932b2 100644 --- a/plugins/SendScreenshotPlus/src/Main.cpp +++ b/plugins/SendScreenshotPlus/src/Main.cpp @@ -1,336 +1,336 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original source code -(c) 2004-2006 Sérgio Vieira Rolanski (ported from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -// Prototypes /////////////////////////////////////////////////////////////////////////// - -CMPlugin g_plugin; -HGENMENU g_hMenu1, g_hMenu2; - -ATOM g_clsTargetHighlighter = 0; -MGLOBAL g_myGlobals; -HNETLIBUSER g_hNetlibUser; - -IconItem ICONS[ICO_END_] = -{ - { LPGEN("Main Icon"), "main", IDI_MAIN, 32 }, - { LPGEN("Main Icon"), "mainxs", IDI_MAIN }, - { LPGEN("Target Cursor"), "target", IDI_TARGET, 32 }, - { LPGEN("Target Desktop"), "monitor", IDI_MONITOR, 32 }, -}; - -IconItem ICONS_BTN[ICO_BTN_END_] = -{ - { LPGEN("Help"), "help", IDI_HELP }, - { LPGEN("Open Folder"), "folder", IDI_FOLDER }, - { LPGEN("Description off"), "desc", IDI_DESC }, - { LPGEN("Description on"), "descon", IDI_DESCON }, - { LPGEN("Delete off"), "del", IDI_DEL }, - { LPGEN("Delete on"), "delon", IDI_DELON }, - { LPGEN("Prev"), "arrowl", IDI_ARROWL }, - { LPGEN("Next"), "arrowr", IDI_ARROWR }, - { LPGEN("Update"), "update", IDI_UPDATE }, - { LPGEN("OK"), "ok", IDI_OK }, - { LPGEN("Cancel"), "cancel", IDI_CANCEL }, - { LPGEN("Edit"), "edit", IDI_EDIT }, - { LPGEN("Edit on"), "editon", IDI_EDITON }, - { LPGEN("Copy"), "copy", IDI_COPY }, - { LPGEN("BBCode"), "bbc", IDI_BBC }, - { LPGEN("BBCode link"), "bbclnk", IDI_BBC2 }, - { LPGEN("Down arrow"), "downarrow", IDI_DOWNARROW }, -}; - -static HANDLE m_hFolderScreenshot = nullptr; -wchar_t* GetCustomPath() -{ - wchar_t *pszPath = Utils_ReplaceVarsW(L"%miranda_userdata%\\Screenshots"); - if (m_hFolderScreenshot) { - wchar_t szPath[1024] = { 0 }; - FoldersGetCustomPathW(m_hFolderScreenshot, szPath, 1024, pszPath); - mir_free(pszPath); - pszPath = mir_wstrdup(szPath); - } - if (!pszPath) { - MessageBox(nullptr, L"Can not retrieve screenshot path.", L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL); - return nullptr; - } - int result = CreateDirectoryTreeW(pszPath); - if (result) { - wchar_t szError[MAX_PATH]; - mir_snwprintf(szError, MAX_PATH, TranslateT("Could not create screenshot folder (error code: %d):\n%s\nDo you have write permissions?"), result, pszPath); - MessageBox(nullptr, szError, L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL); - mir_free(pszPath); - return nullptr; - } - return pszPath; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Callback function of service for contact menu and main menu -// wParam = contact handle -// lParam = 0 (or 0xFFFF to preselect window under cursor) - -INT_PTR service_OpenCaptureDialog(WPARAM wParam, LPARAM lParam) -{ - TfrmMain *frmMain = new TfrmMain(); - if (!frmMain) { - MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL); - return -1; - } - wchar_t *pszPath = GetCustomPath(); - if (!pszPath) { - delete frmMain; - return -1; - } - frmMain->Init(pszPath, wParam); - mir_free(pszPath); - if (lParam == 0xFFFF) { - frmMain->SetTargetWindow(nullptr); - } - frmMain->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Callback function of service -// 1. Send a screenshot of the desktop to the selected contact -// wParam = contact handle -// lParam = 0 -// 2. Open the capture dialog in take screenshot only mode (it will not be sent) -// wParam = 0 -// lParam = anything but 0 - -INT_PTR service_SendDesktop(WPARAM wParam, LPARAM) -{ - TfrmMain *frmMain = new TfrmMain(); - if (!frmMain) { - MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL); - return -1; - } - wchar_t *pszPath = GetCustomPath(); - if (!pszPath) { - delete frmMain; - return -1; - } - - MCONTACT hContact = (MCONTACT)wParam; - frmMain->m_opt_chkTimed = false; - frmMain->m_opt_tabCapture = 1; - frmMain->m_opt_cboxDesktop = 0; - frmMain->m_opt_chkEditor = false; - frmMain->m_opt_cboxSendBy = Contact::IsGroupChat(hContact) ? SS_IMAGESHACK : SS_FILESEND; - frmMain->Init(pszPath, hContact); // this method create the window hidden. - mir_free(pszPath); - frmMain->btnCaptureClick(); // this method will call Close() - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Callback function of service for sending image to imageshack.us -// wParam = (char*)filename -// lParam = (HANDLE)contact (can be null) - -INT_PTR service_Send2ImageShack(WPARAM wParam, LPARAM lParam) -{ - char *result = nullptr; - CSendHost_ImageShack *cSend = new CSendHost_ImageShack(nullptr, lParam, false); - cSend->m_bDeleteAfterSend = false; - cSend->SetFile((char *)wParam); - if (lParam != NULL) { - if (cSend->Send()) delete cSend; - return NULL; - } - cSend->SendSilent(); - if (cSend->GetURL()) { - result = mir_strdup(cSend->GetURL()); - } - else { - result = mir_u2a(cSend->GetErrorMsg()); - } - delete cSend; - return (INT_PTR)result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static PLUGININFOEX pluginInfoEx = -{ - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {ED39AF7C-BECD-404E-9499-4D04F711B9CB} - { 0xed39af7c, 0xbecd, 0x404e, { 0x94, 0x99, 0x4d, 0x04, 0xf7, 0x11, 0xb9, 0xcb } } -}; - -CMPlugin::CMPlugin() : - PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// hooks - -int hook_ModulesLoaded(WPARAM, LPARAM) -{ - g_myGlobals.PluginHTTPExist = ServiceExists(MS_HTTP_ACCEPT_CONNECTIONS); - g_myGlobals.PluginFTPExist = ServiceExists(MS_FTPFILE_UPLOAD); - g_myGlobals.PluginCloudFileExist = ServiceExists(MS_CLOUDFILE_UPLOAD); - - // Netlib register - NETLIBUSER nlu = {}; - nlu.szSettingsModule = __PLUGIN_NAME; - nlu.szDescriptiveName.w = TranslateT("SendSS"); - nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; //|NUF_NOHTTPSOPTION; - g_hNetlibUser = Netlib_RegisterUser(&nlu); - - // load my button class / or use UInfoEx - CtrlButtonLoadModule(); - - // Folders plugin support - m_hFolderScreenshot = FoldersRegisterCustomPathW(LPGEN("SendSS"), LPGEN("Screenshots"), - PROFILE_PATHW L"\\" CURRENT_PROFILEW L"\\Screenshots"); - return 0; -} - -int hook_SystemPreShutdown(WPARAM, LPARAM) -{ - TfrmMain::Unload(); - - // Netlib unregister - Netlib_CloseHandle(g_hNetlibUser); - - // uninitialize classes - CtrlButtonUnloadModule(); - return 0; -} - -int hook_PrebuildContactMenu(WPARAM hContact, LPARAM) -{ - INT_PTR flags = CallProtoService(Proto_GetBaseAccountName(hContact), PS_GETCAPS, PFLAGNUM_1, 0); - bool bEnabled = (flags != CALLSERVICE_NOTFOUND) && (flags & PF1_FILE) != 0; - Menu_ShowItem(g_hMenu1, bEnabled); - Menu_ShowItem(g_hMenu2, bEnabled); - return 0; -} - -static int TabsrmmButtonsInit(WPARAM, LPARAM) -{ - // SRMM toolbar button - BBButton bbd = {}; - bbd.pszModuleName = MODULENAME; - bbd.dwButtonID = 1; - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON; - bbd.dwDefPos = 201; - bbd.hIcon = GetIconHandle(ICO_MAINXS); - Srmm_AddButton(&bbd, &g_plugin); - return 0; -} - -static int TabsrmmButtonPressed(WPARAM hContact, LPARAM lParam) -{ - CustomButtonClickData *cbcd = (CustomButtonClickData *)lParam; - if (!mir_strcmp(cbcd->pszModule, MODULENAME) && cbcd->dwButtonId == 1) - CallService(MS_SENDSS_OPENDIALOG, hContact, 0); - - return 0; -} - -int CMPlugin::Load() -{ - // hook events - HookEvent(ME_SYSTEM_MODULESLOADED, hook_ModulesLoaded); - HookEvent(ME_SYSTEM_PRESHUTDOWN, hook_SystemPreShutdown); - HookEvent(ME_CLIST_PREBUILDCONTACTMENU, hook_PrebuildContactMenu); - - HookEvent(ME_MSG_BUTTONPRESSED, TabsrmmButtonPressed); - HookTemporaryEvent(ME_MSG_TOOLBARLOADED, TabsrmmButtonsInit); - - // icons - g_plugin.registerIcon(MODULENAME, ICONS, MODULENAME); - g_plugin.registerIcon(MODULENAME "/" LPGEN("Buttons"), ICONS_BTN, MODULENAME); - - // services -#define srv_reg(name) CreateServiceFunction(MODULENAME "/" #name, service_##name); - srv_reg(OpenCaptureDialog); - srv_reg(SendDesktop); - srv_reg(Send2ImageShack); - - // menu items - CMenuItem mi(&g_plugin); - mi.flags = CMIF_UNICODE; - mi.hIcolibItem = GetIconHandle(ICO_MAINXS); - - SET_UID(mi, 0xa559a22e, 0xd0f9, 0x4553, 0x8e, 0x68, 0x55, 0xb3, 0xae, 0xc4, 0x5d, 0x93); - mi.name.w = LPGENW("Take a screenshot"); - mi.pszService = MS_SENDSS_OPENDIALOG; - mi.position = 1000001; - Menu_AddMainMenuItem(&mi); - - SET_UID(mi, 0xfea0a84, 0x1767, 0x4605, 0x99, 0xf0, 0xa9, 0x48, 0x1a, 0xa6, 0x6f, 0xce); - mi.name.w = LPGENW("Send screenshot"); - mi.pszService = MS_SENDSS_OPENDIALOG; - mi.position = 1000000; - g_hMenu1 = Menu_AddContactMenuItem(&mi); - - SET_UID(mi, 0x8d5b0d9a, 0x68d4, 0x4594, 0x9f, 0x41, 0x0, 0x64, 0x20, 0xe7, 0xf8, 0x9f); - mi.name.w = LPGENW("Send desktop screenshot"); - mi.pszService = MS_SENDSS_SENDDESKTOP; - mi.position = 1000001; - g_hMenu2 = Menu_AddContactMenuItem(&mi); - - // hotkey's - HOTKEYDESC hkd = {}; - hkd.pszName = "Open SendSS+"; - hkd.szDescription.w = LPGENW("Open SendSS+"); - hkd.szSection.w = L"SendSS+"; - hkd.pszService = MS_SENDSS_OPENDIALOG; - hkd.lParam = 0xFFFF; - hkd.dwFlags = HKD_UNICODE; - g_plugin.addHotkey(&hkd); - - // register highlighter window class - HBRUSH brush = CreateSolidBrush(0x0000FF00); // owned by class - WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, DefWindowProc, 0, 0, g_plugin.getInst(), nullptr, nullptr, brush, nullptr, L"SendSSHighlighter" }; - g_clsTargetHighlighter = RegisterClass(&wndclass); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Prepare the plugin to stop - -int CMPlugin::Unload() -{ - if (g_clsTargetHighlighter) - UnregisterClass((wchar_t *)g_clsTargetHighlighter, g_plugin.getInst()), g_clsTargetHighlighter = 0; - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original source code
+(c) 2004-2006 Sérgio Vieira Rolanski (ported from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+CMPlugin g_plugin;
+HGENMENU g_hMenu1, g_hMenu2;
+
+ATOM g_clsTargetHighlighter = 0;
+MGLOBAL g_myGlobals;
+HNETLIBUSER g_hNetlibUser;
+
+IconItem ICONS[ICO_END_] =
+{
+ { LPGEN("Main Icon"), "main", IDI_MAIN, 32 },
+ { LPGEN("Main Icon"), "mainxs", IDI_MAIN },
+ { LPGEN("Target Cursor"), "target", IDI_TARGET, 32 },
+ { LPGEN("Target Desktop"), "monitor", IDI_MONITOR, 32 },
+};
+
+IconItem ICONS_BTN[ICO_BTN_END_] =
+{
+ { LPGEN("Help"), "help", IDI_HELP },
+ { LPGEN("Open Folder"), "folder", IDI_FOLDER },
+ { LPGEN("Description off"), "desc", IDI_DESC },
+ { LPGEN("Description on"), "descon", IDI_DESCON },
+ { LPGEN("Delete off"), "del", IDI_DEL },
+ { LPGEN("Delete on"), "delon", IDI_DELON },
+ { LPGEN("Prev"), "arrowl", IDI_ARROWL },
+ { LPGEN("Next"), "arrowr", IDI_ARROWR },
+ { LPGEN("Update"), "update", IDI_UPDATE },
+ { LPGEN("OK"), "ok", IDI_OK },
+ { LPGEN("Cancel"), "cancel", IDI_CANCEL },
+ { LPGEN("Edit"), "edit", IDI_EDIT },
+ { LPGEN("Edit on"), "editon", IDI_EDITON },
+ { LPGEN("Copy"), "copy", IDI_COPY },
+ { LPGEN("BBCode"), "bbc", IDI_BBC },
+ { LPGEN("BBCode link"), "bbclnk", IDI_BBC2 },
+ { LPGEN("Down arrow"), "downarrow", IDI_DOWNARROW },
+};
+
+static HANDLE m_hFolderScreenshot = nullptr;
+wchar_t* GetCustomPath()
+{
+ wchar_t *pszPath = Utils_ReplaceVarsW(L"%miranda_userdata%\\Screenshots");
+ if (m_hFolderScreenshot) {
+ wchar_t szPath[1024] = { 0 };
+ FoldersGetCustomPathW(m_hFolderScreenshot, szPath, 1024, pszPath);
+ mir_free(pszPath);
+ pszPath = mir_wstrdup(szPath);
+ }
+ if (!pszPath) {
+ MessageBox(nullptr, L"Can not retrieve screenshot path.", L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ return nullptr;
+ }
+ int result = CreateDirectoryTreeW(pszPath);
+ if (result) {
+ wchar_t szError[MAX_PATH];
+ mir_snwprintf(szError, MAX_PATH, TranslateT("Could not create screenshot folder (error code: %d):\n%s\nDo you have write permissions?"), result, pszPath);
+ MessageBox(nullptr, szError, L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ mir_free(pszPath);
+ return nullptr;
+ }
+ return pszPath;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callback function of service for contact menu and main menu
+// wParam = contact handle
+// lParam = 0 (or 0xFFFF to preselect window under cursor)
+
+INT_PTR service_OpenCaptureDialog(WPARAM wParam, LPARAM lParam)
+{
+ TfrmMain *frmMain = new TfrmMain();
+ if (!frmMain) {
+ MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ return -1;
+ }
+ wchar_t *pszPath = GetCustomPath();
+ if (!pszPath) {
+ delete frmMain;
+ return -1;
+ }
+ frmMain->Init(pszPath, wParam);
+ mir_free(pszPath);
+ if (lParam == 0xFFFF) {
+ frmMain->SetTargetWindow(nullptr);
+ }
+ frmMain->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callback function of service
+// 1. Send a screenshot of the desktop to the selected contact
+// wParam = contact handle
+// lParam = 0
+// 2. Open the capture dialog in take screenshot only mode (it will not be sent)
+// wParam = 0
+// lParam = anything but 0
+
+INT_PTR service_SendDesktop(WPARAM wParam, LPARAM)
+{
+ TfrmMain *frmMain = new TfrmMain();
+ if (!frmMain) {
+ MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ return -1;
+ }
+ wchar_t *pszPath = GetCustomPath();
+ if (!pszPath) {
+ delete frmMain;
+ return -1;
+ }
+
+ MCONTACT hContact = (MCONTACT)wParam;
+ frmMain->m_opt_chkTimed = false;
+ frmMain->m_opt_tabCapture = 1;
+ frmMain->m_opt_cboxDesktop = 0;
+ frmMain->m_opt_chkEditor = false;
+ frmMain->m_opt_cboxSendBy = Contact::IsGroupChat(hContact) ? SS_IMAGESHACK : SS_FILESEND;
+ frmMain->Init(pszPath, hContact); // this method create the window hidden.
+ mir_free(pszPath);
+ frmMain->btnCaptureClick(); // this method will call Close()
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callback function of service for sending image to imageshack.us
+// wParam = (char*)filename
+// lParam = (HANDLE)contact (can be null)
+
+INT_PTR service_Send2ImageShack(WPARAM wParam, LPARAM lParam)
+{
+ char *result = nullptr;
+ CSendHost_ImageShack *cSend = new CSendHost_ImageShack(nullptr, lParam, false);
+ cSend->m_bDeleteAfterSend = false;
+ cSend->SetFile((char *)wParam);
+ if (lParam != NULL) {
+ if (cSend->Send()) delete cSend;
+ return NULL;
+ }
+ cSend->SendSilent();
+ if (cSend->GetURL()) {
+ result = mir_strdup(cSend->GetURL());
+ }
+ else {
+ result = mir_u2a(cSend->GetErrorMsg());
+ }
+ delete cSend;
+ return (INT_PTR)result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {ED39AF7C-BECD-404E-9499-4D04F711B9CB}
+ { 0xed39af7c, 0xbecd, 0x404e, { 0x94, 0x99, 0x4d, 0x04, 0xf7, 0x11, 0xb9, 0xcb } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// hooks
+
+int hook_ModulesLoaded(WPARAM, LPARAM)
+{
+ g_myGlobals.PluginHTTPExist = ServiceExists(MS_HTTP_ACCEPT_CONNECTIONS);
+ g_myGlobals.PluginFTPExist = ServiceExists(MS_FTPFILE_UPLOAD);
+ g_myGlobals.PluginCloudFileExist = ServiceExists(MS_CLOUDFILE_UPLOAD);
+
+ // Netlib register
+ NETLIBUSER nlu = {};
+ nlu.szSettingsModule = __PLUGIN_NAME;
+ nlu.szDescriptiveName.w = TranslateT("SendSS");
+ nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; //|NUF_NOHTTPSOPTION;
+ g_hNetlibUser = Netlib_RegisterUser(&nlu);
+
+ // load my button class / or use UInfoEx
+ CtrlButtonLoadModule();
+
+ // Folders plugin support
+ m_hFolderScreenshot = FoldersRegisterCustomPathW(LPGEN("SendSS"), LPGEN("Screenshots"),
+ PROFILE_PATHW L"\\" CURRENT_PROFILEW L"\\Screenshots");
+ return 0;
+}
+
+int hook_SystemPreShutdown(WPARAM, LPARAM)
+{
+ TfrmMain::Unload();
+
+ // Netlib unregister
+ Netlib_CloseHandle(g_hNetlibUser);
+
+ // uninitialize classes
+ CtrlButtonUnloadModule();
+ return 0;
+}
+
+int hook_PrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ INT_PTR flags = CallProtoService(Proto_GetBaseAccountName(hContact), PS_GETCAPS, PFLAGNUM_1, 0);
+ bool bEnabled = (flags != CALLSERVICE_NOTFOUND) && (flags & PF1_FILE) != 0;
+ Menu_ShowItem(g_hMenu1, bEnabled);
+ Menu_ShowItem(g_hMenu2, bEnabled);
+ return 0;
+}
+
+static int TabsrmmButtonsInit(WPARAM, LPARAM)
+{
+ // SRMM toolbar button
+ BBButton bbd = {};
+ bbd.pszModuleName = MODULENAME;
+ bbd.dwButtonID = 1;
+ bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON;
+ bbd.dwDefPos = 201;
+ bbd.hIcon = GetIconHandle(ICO_MAINXS);
+ Srmm_AddButton(&bbd, &g_plugin);
+ return 0;
+}
+
+static int TabsrmmButtonPressed(WPARAM hContact, LPARAM lParam)
+{
+ CustomButtonClickData *cbcd = (CustomButtonClickData *)lParam;
+ if (!mir_strcmp(cbcd->pszModule, MODULENAME) && cbcd->dwButtonId == 1)
+ CallService(MS_SENDSS_OPENDIALOG, hContact, 0);
+
+ return 0;
+}
+
+int CMPlugin::Load()
+{
+ // hook events
+ HookEvent(ME_SYSTEM_MODULESLOADED, hook_ModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, hook_SystemPreShutdown);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, hook_PrebuildContactMenu);
+
+ HookEvent(ME_MSG_BUTTONPRESSED, TabsrmmButtonPressed);
+ HookTemporaryEvent(ME_MSG_TOOLBARLOADED, TabsrmmButtonsInit);
+
+ // icons
+ g_plugin.registerIcon(MODULENAME, ICONS, MODULENAME);
+ g_plugin.registerIcon(MODULENAME "/" LPGEN("Buttons"), ICONS_BTN, MODULENAME);
+
+ // services
+#define srv_reg(name) CreateServiceFunction(MODULENAME "/" #name, service_##name);
+ srv_reg(OpenCaptureDialog);
+ srv_reg(SendDesktop);
+ srv_reg(Send2ImageShack);
+
+ // menu items
+ CMenuItem mi(&g_plugin);
+ mi.flags = CMIF_UNICODE;
+ mi.hIcolibItem = GetIconHandle(ICO_MAINXS);
+
+ SET_UID(mi, 0xa559a22e, 0xd0f9, 0x4553, 0x8e, 0x68, 0x55, 0xb3, 0xae, 0xc4, 0x5d, 0x93);
+ mi.name.w = LPGENW("Take a screenshot");
+ mi.pszService = MS_SENDSS_OPENDIALOG;
+ mi.position = 1000001;
+ Menu_AddMainMenuItem(&mi);
+
+ SET_UID(mi, 0xfea0a84, 0x1767, 0x4605, 0x99, 0xf0, 0xa9, 0x48, 0x1a, 0xa6, 0x6f, 0xce);
+ mi.name.w = LPGENW("Send screenshot");
+ mi.pszService = MS_SENDSS_OPENDIALOG;
+ mi.position = 1000000;
+ g_hMenu1 = Menu_AddContactMenuItem(&mi);
+
+ SET_UID(mi, 0x8d5b0d9a, 0x68d4, 0x4594, 0x9f, 0x41, 0x0, 0x64, 0x20, 0xe7, 0xf8, 0x9f);
+ mi.name.w = LPGENW("Send desktop screenshot");
+ mi.pszService = MS_SENDSS_SENDDESKTOP;
+ mi.position = 1000001;
+ g_hMenu2 = Menu_AddContactMenuItem(&mi);
+
+ // hotkey's
+ HOTKEYDESC hkd = {};
+ hkd.pszName = "Open SendSS+";
+ hkd.szDescription.w = LPGENW("Open SendSS+");
+ hkd.szSection.w = L"SendSS+";
+ hkd.pszService = MS_SENDSS_OPENDIALOG;
+ hkd.lParam = 0xFFFF;
+ hkd.dwFlags = HKD_UNICODE;
+ g_plugin.addHotkey(&hkd);
+
+ // register highlighter window class
+ HBRUSH brush = CreateSolidBrush(0x0000FF00); // owned by class
+ WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, DefWindowProc, 0, 0, g_plugin.getInst(), nullptr, nullptr, brush, nullptr, L"SendSSHighlighter" };
+ g_clsTargetHighlighter = RegisterClass(&wndclass);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Prepare the plugin to stop
+
+int CMPlugin::Unload()
+{
+ if (g_clsTargetHighlighter)
+ UnregisterClass((wchar_t *)g_clsTargetHighlighter, g_plugin.getInst()), g_clsTargetHighlighter = 0;
+ return 0;
+}
diff --git a/plugins/SendScreenshotPlus/src/UMainForm.cpp b/plugins/SendScreenshotPlus/src/UMainForm.cpp index 86b53ffef7..d67ebe3de0 100644 --- a/plugins/SendScreenshotPlus/src/UMainForm.cpp +++ b/plugins/SendScreenshotPlus/src/UMainForm.cpp @@ -1,1124 +1,1124 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include <list> - -void TfrmMain::Unload() -{ - std::list<TfrmMain*> lst; - for (auto &it : _HandleMapping) - lst.push_back(it.second); // we can't delete inside loop.. not MT compatible - - while (!lst.empty()) { - DestroyWindow(lst.front()->m_hWnd); // deletes class - lst.pop_front(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CALLBACK TfrmMain::DlgProc_CaptureTabPage(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - // main message handling is done inside TfrmMain::DlgTfrmMain - switch (uMsg) { - case WM_INITDIALOG: - switch (lParam) { - case IDD_UMain_CaptureWindow: - Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_TARGET)); - SetDlgItemText(hDlg, ID_edtCaption, TranslateT("Drag&Drop the target on the desired window.")); - break; - case IDD_UMain_CaptureDesktop: - Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MONITOR)); - break; - case IDD_UMain_CaptureFile: - Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MAIN)); - break; - } - SetFocus(GetDlgItem(hDlg, ID_imgTarget)); - return FALSE; - - case WM_CTLCOLORDLG: - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT)); - return (INT_PTR)GetStockObject(WHITE_BRUSH); - - case WM_COMMAND: - if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_btnExplore) { // local file tab - OPENFILENAME ofn = { sizeof(OPENFILENAME) }; - wchar_t filename[MAX_PATH]; - GetDlgItemText(hDlg, ID_edtSize, filename, _countof(filename)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = hDlg; - ofn.lpstrFilter = L"Images\0*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tif;*.tiff\0"; - ofn.nFilterIndex = 1; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY; - if (GetOpenFileName(&ofn)) { - SetDlgItemText(hDlg, ID_edtSize, filename); - } - break; - } - SendMessage(GetParent(hDlg), uMsg, wParam, lParam); - break; - case WM_NOTIFY: - SendMessage(GetParent(hDlg), uMsg, wParam, lParam); - break; - case WM_DESTROY: - break; - } - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -TfrmMain::CHandleMapping TfrmMain::_HandleMapping; - -INT_PTR CALLBACK TfrmMain::DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORSTATIC) { - switch (GetWindowLongPtr((HWND)lParam, GWL_ID)) { - case IDC_HEADERBAR: - SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT)); - break; - default: - return 0; - } - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetStockObject(WHITE_BRUSH); - } - - CHandleMapping::iterator wnd; - if (msg == WM_INITDIALOG) { - wnd = _HandleMapping.insert(CHandleMapping::value_type(hWnd, reinterpret_cast<TfrmMain*>(lParam))).first; - wnd->second->m_hWnd = hWnd; - wnd->second->wmInitdialog(wParam, lParam); - return 0; - } - wnd = _HandleMapping.find(hWnd); - if (wnd == _HandleMapping.end()) - return 0; - - switch (msg) { - case WM_DROPFILES: - // Drag&Drop of local files - { - wchar_t filename[MAX_PATH]; - if (!DragQueryFile((HDROP)wParam, 0, filename, MAX_PATH)) - *filename = '\0'; - DragFinish((HDROP)wParam); - if (wnd->second->m_hwndTabPage) - ShowWindow(wnd->second->m_hwndTabPage, SW_HIDE); - - wnd->second->m_opt_tabCapture = 2; // activate file tab - TabCtrl_SetCurSel(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture); - - TAB_INFO itab = { TCIF_PARAM }; - TabCtrl_GetItem(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture, &itab); - wnd->second->m_hwndTabPage = itab.hwndTabPage; - - ShowWindow(wnd->second->m_hwndTabPage, SW_SHOW); - SetDlgItemText(wnd->second->m_hwndTabPage, ID_edtSize, filename); - } - break; - case WM_COMMAND: - wnd->second->wmCommand(wParam, lParam); - break; - case WM_CLOSE: - wnd->second->wmClose(wParam, lParam); - break; - case WM_DESTROY: - delete wnd->second; - break; - case WM_NOTIFY: - wnd->second->wmNotify(wParam, lParam); - break; - case WM_TIMER: - wnd->second->wmTimer(wParam, lParam); - break; - case UM_EVENT: - wnd->second->UMevent(wParam, lParam); - break; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// WM_INITDIALOG: - -int EnumCloudFileServices(const CFSERVICEINFO *serviceInfo, void *param) -{ - HWND hCtrl = (HWND)param; - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, serviceInfo->userName), new UPLOAD_INFO(SS_CLOUDFILE, (void*)serviceInfo->accountName)); - return 0; -} - -void TfrmMain::wmInitdialog(WPARAM, LPARAM) -{ - HWND hCtrl; - // Taskbar and Window icon - Window_SetIcon_IcoLib(m_hWnd, GetIconHandle(ICO_MAIN)); - - wchar_t *pt = mir_wstrdup(Clist_GetContactDisplayName(m_hContact)); - if (pt && (m_hContact != 0)) { - CMStringW string; - string.AppendFormat(TranslateT("Send screenshot to %s"), pt); - SetWindowText(m_hWnd, string); - } - mir_free(pt); - - // Headerbar - SendDlgItemMessage(m_hWnd, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIcon(ICO_MAIN)); - - // Timed controls - CheckDlgButton(m_hWnd, ID_chkTimed, m_opt_chkTimed ? BST_CHECKED : BST_UNCHECKED); - SetDlgItemInt(m_hWnd, ID_edtTimed, (UINT)m_opt_edtTimed, FALSE); - SendDlgItemMessage(m_hWnd, ID_upTimed, UDM_SETRANGE, 0, (LPARAM)MAKELONG(250, 1)); - chkTimedClick(); // enable disable Timed controls - - // create Image list for tab control - if (!m_himlTab) { - m_himlTab = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - ImageList_AddIcon(m_himlTab, GetIcon(ICO_TARGET)); - ImageList_AddIcon(m_himlTab, GetIcon(ICO_MONITOR)); - ImageList_AddIcon(m_himlTab, GetIconBtn(ICO_BTN_FOLDER)); - } - - // create the tab control. - { - m_hwndTab = GetDlgItem(m_hWnd, IDC_CAPTURETAB); - TabCtrl_SetImageList(m_hwndTab, m_himlTab); - TabCtrl_SetItemExtra(m_hwndTab, sizeof(TAB_INFO) - sizeof(TCITEMHEADER)); - RECT rcTab; - TAB_INFO itab; - itab.hwndMain = m_hWnd; - itab.hwndTab = m_hwndTab; - itab.tcih.mask = TCIF_PARAM | TCIF_TEXT | TCIF_IMAGE; - - // Add a tab for each of the three child dialog boxes. - itab.tcih.pszText = TranslateT("Window"); - itab.tcih.iImage = 0; - itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureWindow), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureWindow); - TabCtrl_InsertItem(m_hwndTab, 0, &itab); - - // get tab boundaries (required after 1st tab) - GetClientRect(m_hwndTab, &rcTab); - MapWindowPoints(m_hwndTab, m_hWnd, (POINT*)&rcTab, 2); - TabCtrl_AdjustRect(m_hwndTab, 0, &rcTab); - rcTab.bottom -= rcTab.top; rcTab.right -= rcTab.left; - - SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0); - CheckDlgButton(itab.hwndTabPage, ID_chkIndirectCapture, m_opt_chkIndirectCapture ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(itab.hwndTabPage, ID_chkClientArea, m_opt_chkClientArea ? BST_CHECKED : BST_UNCHECKED); - - itab.tcih.pszText = TranslateT("Desktop"); - itab.tcih.iImage = 1; - itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureDesktop), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureDesktop); - TabCtrl_InsertItem(m_hwndTab, 1, &itab); - SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0); - - hCtrl = GetDlgItem(itab.hwndTabPage, ID_edtCaption); - ComboBox_ResetContent(hCtrl); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Entire Desktop>")), 0); - ComboBox_SetCurSel(hCtrl, 0); - if (m_MonitorCount > 1) { - wchar_t tszTemp[120]; - for (size_t mon = 0; mon < m_MonitorCount; ++mon) { // @todo : fix format for non MSVC compilers - mir_snwprintf(tszTemp, L"%Iu. %s%s", - mon + 1, TranslateT("Monitor"), - (m_Monitors[mon].dwFlags & MONITORINFOF_PRIMARY) ? TranslateT(" (primary)") : L"" - ); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, tszTemp), mon + 1); - } - ComboBox_SelectItem(hCtrl, m_opt_cboxDesktop); - } - PostMessage(m_hWnd, WM_COMMAND, MAKEWPARAM(ID_edtCaption, CBN_SELCHANGE), (LPARAM)hCtrl); - - itab.tcih.pszText = TranslateT("File"); - itab.tcih.iImage = 2; - itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureFile), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureFile); - TabCtrl_InsertItem(m_hwndTab, 2, &itab); - SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0); - - // select tab and set m_hwndTabPage - TabCtrl_SetCurSel(m_hwndTab, m_opt_tabCapture); - itab.tcih.mask = TCIF_PARAM; - TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab); - m_hwndTabPage = itab.hwndTabPage; - ShowWindow(m_hwndTabPage, SW_SHOW); - - // enable Drag&Drop for local file pane - typedef BOOL(WINAPI *ChangeWindowMessageFilterEx_t)(HWND hwnd, UINT message, uint32_t action, PCHANGEFILTERSTRUCT pChangeFilterStruct); - ChangeWindowMessageFilterEx_t pChangeWindowMessageFilterEx; - pChangeWindowMessageFilterEx = (ChangeWindowMessageFilterEx_t)GetProcAddress(GetModuleHandleA("user32"), "ChangeWindowMessageFilterEx"); - if (pChangeWindowMessageFilterEx) { // Win7+, UAC fix - pChangeWindowMessageFilterEx(m_hWnd, WM_DROPFILES, MSGFLT_ALLOW, nullptr); - pChangeWindowMessageFilterEx(m_hWnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr); - pChangeWindowMessageFilterEx(m_hWnd, 0x0049/*WM_COPYGLOBALDATA*/, MSGFLT_ALLOW, nullptr); - } - DragAcceptFiles(m_hWnd, 1); - } - - // init Format combo box - { - hCtrl = GetDlgItem(m_hWnd, ID_cboxFormat); - ComboBox_ResetContent(hCtrl); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"PNG"), 0); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"JPG"), 1); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"BMP"), 2); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"TIF"), 3); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"GIF"), 4); - ComboBox_SelectItem(hCtrl, m_opt_cboxFormat); - } - - // init SendBy combo box - UPLOAD_INFO *pDefault = nullptr; - { - hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy); - ComboBox_ResetContent(hCtrl); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Only save>")), new UPLOAD_INFO(SS_JUSTSAVE)); - if (m_hContact) { - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("File Transfer")), new UPLOAD_INFO(SS_FILESEND)); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("E-mail")), new UPLOAD_INFO(SS_EMAIL)); - if (g_myGlobals.PluginHTTPExist) { - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"HTTP Server"), new UPLOAD_INFO(SS_HTTPSERVER)); - } - else if (m_opt_cboxSendBy == SS_HTTPSERVER) { - m_opt_cboxSendBy = SS_IMAGESHACK; - } - if (g_myGlobals.PluginFTPExist) { - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("FTP File")), new UPLOAD_INFO(SS_FTPFILE)); - } - else if (m_opt_cboxSendBy == SS_FTPFILE) { - m_opt_cboxSendBy = SS_IMAGESHACK; - } - } - else if (m_opt_cboxSendBy == SS_FILESEND || m_opt_cboxSendBy == SS_EMAIL || m_opt_cboxSendBy == SS_HTTPSERVER || m_opt_cboxSendBy == SS_FTPFILE) { - m_opt_cboxSendBy = SS_IMAGESHACK; - } - if (g_myGlobals.PluginCloudFileExist) { - CallService(MS_CLOUDFILE_ENUMSERVICES, (WPARAM)EnumCloudFileServices, (LPARAM)hCtrl); - } - else if (m_opt_cboxSendBy == SS_CLOUDFILE) { - m_opt_cboxSendBy = SS_IMAGESHACK; - } - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"ImageShack"), new UPLOAD_INFO(SS_IMAGESHACK)); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (30m)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)1)); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1d)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)4)); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1w)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)5)); - ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"Imgur"), new UPLOAD_INFO(SS_IMGUR)); - - for (int i = 0; i < ComboBox_GetCount(hCtrl); i++) { - UPLOAD_INFO *p = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i); - if (p && p->sendBy == m_opt_cboxSendBy) { - pDefault = p; - ComboBox_SetCurSel(hCtrl, i); - break; - } - } - } - - // init footer options - CheckDlgButton(m_hWnd, ID_chkOpenAgain, m_opt_chkOpenAgain ? BST_CHECKED : BST_UNCHECKED); - - if (hCtrl = GetDlgItem(m_hWnd, ID_btnExplore)) { - SendDlgItemMessage(m_hWnd, ID_btnExplore, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open Folder"), MBBF_TCHAR); - HICON hIcon = GetIconBtn(ICO_BTN_FOLDER); - SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SetWindowText(hCtrl, hIcon ? L"" : L"..."); - } - - if (hCtrl = GetDlgItem(m_hWnd, ID_chkDesc)) { - SendDlgItemMessage(m_hWnd, ID_chkDesc, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Fill description textbox."), MBBF_TCHAR); - HICON hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC); - SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SetWindowText(hCtrl, hIcon ? L"" : L"D"); - SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDesc ? BST_CHECKED : BST_UNCHECKED, NULL); - } - - if (hCtrl = GetDlgItem(m_hWnd, ID_chkDeleteAfterSend)) { - SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete after send"), MBBF_TCHAR); - HICON hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL); - SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SetWindowText(hCtrl, hIcon ? L"" : L"X"); - SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDeleteAfterSend ? BST_CHECKED : BST_UNCHECKED, NULL); - } - - if (hCtrl = GetDlgItem(m_hWnd, ID_chkEditor)) { - SendDlgItemMessage(m_hWnd, ID_chkEditor, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open editor before sending"), MBBF_TCHAR); - HICON hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT); - SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SetWindowText(hCtrl, hIcon ? L"" : L"E"); - SendMessage(hCtrl, BM_SETCHECK, m_opt_chkEditor ? BST_CHECKED : BST_UNCHECKED, NULL); - } - - if (hCtrl = GetDlgItem(m_hWnd, ID_btnCapture)) { - SendMessage(hCtrl, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Capture"), MBBF_TCHAR); - HICON hIcon = GetIconBtn(ICO_BTN_OK); - SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SetWindowText(hCtrl, TranslateT("&Capture")); - SendMessage(hCtrl, BUTTONSETDEFAULT, 1, NULL); - } - cboxSendByChange((pDefault) ? pDefault->param : nullptr); // enable disable controls - - TranslateDialogDefault(m_hWnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// WM_COMMAND: - -void TfrmMain::wmCommand(WPARAM wParam, LPARAM lParam) -{ - HICON hIcon; - - int IDControl = LOWORD(wParam); - switch (HIWORD(wParam)) { - case BN_CLICKED: // Button controls - switch (IDControl) { - case IDCANCEL: // ESC pressed - this->Close(); - break; - case ID_chkTimed: - m_opt_chkTimed = (uint8_t)Button_GetCheck((HWND)lParam); - TfrmMain::chkTimedClick(); - break; - case ID_chkIndirectCapture: - m_opt_chkIndirectCapture = (uint8_t)Button_GetCheck((HWND)lParam); - break; - case ID_chkClientArea: - m_opt_chkClientArea = (uint8_t)Button_GetCheck((HWND)lParam); - if (m_hTargetWindow) - edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, GetParent((HWND)lParam), ID_edtSize); - break; - case ID_imgTarget: - if (m_opt_tabCapture != 0) break; - m_hLastWin = nullptr; - SetTimer(m_hWnd, ID_imgTarget, BUTTON_POLLDELAY, nullptr); - break; - case ID_btnExplore: - TfrmMain::btnExploreClick(); - break; - case ID_chkDesc: - m_opt_btnDesc = !m_opt_btnDesc; - hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC); - SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - break; - case ID_chkDeleteAfterSend: - m_opt_btnDeleteAfterSend = !m_opt_btnDeleteAfterSend; - hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL); - SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - if (m_cSend) m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend; - break; - case ID_chkEditor: - m_opt_chkEditor = !m_opt_chkEditor; - hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT); - SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - break; - case ID_chkOpenAgain: - m_opt_chkOpenAgain = Button_GetCheck((HWND)lParam); - break; - case ID_btnCapture: - TfrmMain::btnCaptureClick(); - break; - } - break; - - case CBN_SELCHANGE: // ComboBox controls - switch (IDControl) { // lParam = Handle to the control - case ID_cboxFormat: // not finish - m_opt_cboxFormat = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam)); - break; - case ID_cboxSendBy: - { - UPLOAD_INFO *upload = (UPLOAD_INFO*)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam)); - m_opt_cboxSendBy = upload->sendBy; - cboxSendByChange(upload->param); - } - break; - - case ID_edtCaption: // cboxDesktopChange - m_opt_cboxDesktop = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam)); - m_hTargetWindow = nullptr; - if (m_opt_cboxDesktop > 0) { - edtSizeUpdate(m_Monitors[m_opt_cboxDesktop - 1].rcMonitor, GetParent((HWND)lParam), ID_edtSize); - } - else { - edtSizeUpdate(m_VirtualScreen, GetParent((HWND)lParam), ID_edtSize); - } - break; - } - break; - - case EN_CHANGE: // Edit controls - switch (IDControl) { // lParam = Handle to the control - case ID_edtQuality: - m_opt_edtQuality = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtQuality, nullptr, FALSE); - break; - case ID_edtTimed: - m_opt_edtTimed = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtTimed, nullptr, FALSE); - break; - } - break; - } -} - -// WM_CLOSE: -void TfrmMain::wmClose(WPARAM, LPARAM) -{ - HWND hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy); - size_t count = ComboBox_GetCount(hCtrl); - for (size_t i = 0; i < count; i++) { - UPLOAD_INFO *ui = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i); - delete ui; - } - DestroyWindow(m_hWnd); - return; -} - -// WM_TIMER: -const int g_iTargetBorder = 7; -void TfrmMain::SetTargetWindow(HWND hwnd) -{ - if (!hwnd) { - POINT point; GetCursorPos(&point); - hwnd = WindowFromPoint(point); - for (HWND hTMP; (hTMP = GetParent(hwnd)); hwnd = hTMP) - ; - } - m_hTargetWindow = hwnd; - int len = GetWindowTextLength(m_hTargetWindow) + 1; - wchar_t *lpTitle; - if (len > 1) { - lpTitle = (wchar_t*)mir_alloc(len*sizeof(wchar_t)); - GetWindowText(m_hTargetWindow, lpTitle, len); - } - else { // no WindowText present, use WindowClass - lpTitle = (wchar_t*)mir_alloc(64 * sizeof(wchar_t)); - RealGetWindowClass(m_hTargetWindow, lpTitle, 64); - } - SetDlgItemText(m_hwndTabPage, ID_edtCaption, lpTitle); - mir_free(lpTitle); - edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, m_hwndTabPage, ID_edtSize); -} - -void TfrmMain::wmTimer(WPARAM wParam, LPARAM) -{ - if (wParam == ID_imgTarget) { // Timer for Target selector - static int primarymouse; - if (!m_hTargetHighlighter) { - primarymouse = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON; - m_hTargetHighlighter = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (wchar_t*)g_clsTargetHighlighter, nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, g_plugin.getInst(), nullptr); - if (!m_hTargetHighlighter) return; - SetLayeredWindowAttributes(m_hTargetHighlighter, 0, 123, LWA_ALPHA); - SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_IBEAM); // text cursor - SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_NORMAL); - SetActiveWindow(m_hTargetHighlighter); // activate highlighter to fix focus problems with UAC (unelevated GetAsyncKeyState() fails if an elevated app got focus) - Hide(); - } - if (!(GetAsyncKeyState(primarymouse) & 0x8000)) { - KillTimer(m_hWnd, ID_imgTarget); - SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0); - DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr; - SetTargetWindow(m_hLastWin); - Show(); - return; - } - POINT point; GetCursorPos(&point); - HWND hwnd = WindowFromPoint(point); - if (!((GetAsyncKeyState(VK_SHIFT) | GetAsyncKeyState(VK_MENU)) & 0x8000)) - for (HWND hTMP; (hTMP = GetAncestor(hwnd, GA_PARENT)) && IsChild(hTMP, hwnd); hwnd = hTMP); - else { - ScreenToClient(hwnd, &point); - HWND hTMP; if ((hTMP = RealChildWindowFromPoint(hwnd, point))) - hwnd = hTMP; - } - if (hwnd != m_hLastWin) { - m_hLastWin = hwnd; - RECT rect; - if (m_opt_chkClientArea) { - GetClientRect(hwnd, &rect); - ClientToScreen(hwnd, (POINT*)&rect); - rect.right = rect.left + rect.right; - rect.bottom = rect.top + rect.bottom; - } - else - GetWindowRect(hwnd, &rect); - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - if (g_iTargetBorder) { - SetWindowPos(m_hTargetHighlighter, nullptr, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE); - if (width > g_iTargetBorder * 2 && height > g_iTargetBorder * 2) { - HRGN hRegnNew = CreateRectRgn(0, 0, width, height); - HRGN hRgnHole = CreateRectRgn(g_iTargetBorder, g_iTargetBorder, width - g_iTargetBorder, height - g_iTargetBorder); - CombineRgn(hRegnNew, hRegnNew, hRgnHole, RGN_XOR); - DeleteObject(hRgnHole); - SetWindowRgn(m_hTargetHighlighter, hRegnNew, FALSE); // cleans up hRegnNew - } - else SetWindowRgn(m_hTargetHighlighter, nullptr, FALSE); - } - SetWindowPos(m_hTargetHighlighter, HWND_TOPMOST, rect.left, rect.top, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE); - } - return; - } - if (wParam == ID_chkTimed) { // Timer for Screenshot -#ifdef _DEBUG - OutputDebugStringA("SS Bitmap Timer Start\r\n"); -#endif - if (!m_bCapture) { // only start once - if (m_Screenshot) { - FreeImage_Unload(m_Screenshot); - m_Screenshot = nullptr; - } - m_bCapture = true; - switch (m_opt_tabCapture) { - case 0: - m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture); - break; - case 1: - m_Screenshot = CaptureMonitor((m_opt_cboxDesktop > 0) ? m_Monitors[m_opt_cboxDesktop - 1].szDevice : nullptr); - break; - case 2: // edge case, existing local file - break; -#ifdef _DEBUG - default: - OutputDebugStringA("SS Bitmap Timer Stop (no tabCapture)\r\n"); -#endif - } - m_bCapture = false; - if (m_Screenshot || m_opt_tabCapture == 2) { // @note : test without "if" - KillTimer(m_hWnd, ID_chkTimed); -#ifdef _DEBUG - OutputDebugStringA("SS Bitmap Timer Stop (CaptureDone)\r\n"); -#endif - SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone); - } - } - } -} - -// WM_NOTIFY: -void TfrmMain::wmNotify(WPARAM, LPARAM lParam) -{ - switch (((LPNMHDR)lParam)->idFrom) { - case IDC_CAPTURETAB: - // HWND hwndFrom; = member is handle to the tab control - // UINT_PTR idFrom; = member is the child window identifier of the tab control. - // UINT code; = member is TCN_SELCHANGE - switch (((LPNMHDR)lParam)->code) { - case TCN_SELCHANGING: - if (m_hwndTabPage) { - ShowWindow(m_hwndTabPage, SW_HIDE); - m_hwndTabPage = nullptr; - } - break; - - case TCN_SELCHANGE: - { - TAB_INFO itab = { TCIF_PARAM }; - m_opt_tabCapture = TabCtrl_GetCurSel(m_hwndTab); - TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab); - m_hwndTabPage = itab.hwndTabPage; - } - ShowWindow(m_hwndTabPage, SW_SHOW); - break; - } - break; - } -} - -// UM_EVENT: -void TfrmMain::UMevent(WPARAM, LPARAM lParam) -{ - // HWND hWnd = (HWND)wParam; - switch (lParam) { - case EVT_CaptureDone: - if (!m_Screenshot && m_opt_tabCapture != 2) { - wchar_t *err = TranslateT("Couldn't take a screenshot"); - MessageBox(nullptr, err, ERROR_TITLE, MB_OK | MB_ICONWARNING); - Show(); - return; - } - FormClose(); - break; - - case EVT_SendFileDone: - break; - - case EVT_CheckOpenAgain: - if (m_opt_chkOpenAgain) { - if (m_Screenshot) { - FreeImage_Unload(m_Screenshot); - m_Screenshot = nullptr; - } - Show(); - } - else {// Saving Options and close - SaveOptions(); - Close(); - } - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Standard konstruktor/destruktor - -TfrmMain::TfrmMain() -{ - /* m_opt_XXX */ - m_bOnExitSave = TRUE; - - m_hWnd = nullptr; - m_hContact = NULL; - m_hTargetWindow = m_hLastWin = nullptr; - m_hTargetHighlighter = nullptr; - m_FDestFolder = m_pszFile = nullptr; - m_Screenshot = nullptr; - /* m_AlphaColor */ - m_cSend = nullptr; - - m_Monitors = nullptr; - m_MonitorCount = MonitorInfoEnum(m_Monitors, m_VirtualScreen); - /* m_opt_XXX */ LoadOptions(); - m_bCapture = false; - /* m_hwndTab,m_hwndTabPage */ - m_himlTab = nullptr; -} - -TfrmMain::~TfrmMain() -{ - _HandleMapping.erase(m_hWnd); - mir_free(m_pszFile); - mir_free(m_FDestFolder); - mir_free(m_Monitors); - if (m_Screenshot) FreeImage_Unload(m_Screenshot); - if (m_cSend) delete m_cSend; - if (m_hTargetHighlighter) { - DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr; - SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Load / Saving options from miranda's database - -void TfrmMain::LoadOptions(void) -{ - uint32_t rgb = g_plugin.getDword("AlphaColor", 16777215); - m_AlphaColor.rgbRed = GetRValue(rgb); - m_AlphaColor.rgbGreen = GetGValue(rgb); - m_AlphaColor.rgbBlue = GetBValue(rgb); - m_AlphaColor.rgbReserved = 0; - - m_opt_edtQuality = g_plugin.getByte("JpegQuality", 75); - - m_opt_tabCapture = g_plugin.getByte("Capture", 0); - m_opt_chkIndirectCapture = g_plugin.getByte("IndirectCapture", 0); - m_opt_chkClientArea = g_plugin.getByte("ClientArea", 0); - m_opt_cboxDesktop = g_plugin.getByte("Desktop", 0); - - m_opt_chkTimed = g_plugin.getByte("TimedCap", 0); - m_opt_edtTimed = g_plugin.getByte("CapTime", 3); - m_opt_cboxFormat = g_plugin.getByte("OutputFormat", 0); - m_opt_cboxSendBy = g_plugin.getByte("SendBy", 0); - - m_opt_btnDesc = g_plugin.getByte("AutoDescription", 1); - m_opt_btnDeleteAfterSend = g_plugin.getByte("DelAfterSend", 1) != 0; - m_opt_chkEditor = g_plugin.getByte("Preview", 0); - m_opt_chkOpenAgain = g_plugin.getByte("OpenAgain", 0); -} - -void TfrmMain::SaveOptions(void) -{ - if (m_bOnExitSave) { - g_plugin.setDword("AlphaColor", - (uint32_t)RGB(m_AlphaColor.rgbRed, m_AlphaColor.rgbGreen, m_AlphaColor.rgbBlue)); - - g_plugin.setByte("JpegQuality", m_opt_edtQuality); - - g_plugin.setByte("Capture", m_opt_tabCapture); - g_plugin.setByte("IndirectCapture", m_opt_chkIndirectCapture); - g_plugin.setByte("ClientArea", m_opt_chkClientArea); - g_plugin.setByte("Desktop", m_opt_cboxDesktop); - - g_plugin.setByte("TimedCap", m_opt_chkTimed); - g_plugin.setByte("CapTime", m_opt_edtTimed); - g_plugin.setByte("OutputFormat", m_opt_cboxFormat); - g_plugin.setByte("SendBy", m_opt_cboxSendBy); - - g_plugin.setByte("AutoDescription", m_opt_btnDesc); - g_plugin.setByte("DelAfterSend", m_opt_btnDeleteAfterSend); - g_plugin.setByte("Preview", m_opt_chkEditor); - g_plugin.setByte("OpenAgain", m_opt_chkOpenAgain); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::Init(wchar_t *DestFolder, MCONTACT Contact) -{ - m_FDestFolder = mir_wstrdup(DestFolder); - m_hContact = Contact; - - // create window - m_hWnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMainForm), nullptr, DlgTfrmMain, (LPARAM)this); - - // register object - _HandleMapping.insert(CHandleMapping::value_type(m_hWnd, this)); - - // check Contact - if (m_cSend) - m_cSend->SetContact(Contact); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::btnCaptureClick() -{ - if (m_opt_tabCapture == 1) m_hTargetWindow = GetDesktopWindow(); - else if (m_opt_tabCapture == 2) { - wchar_t filename[MAX_PATH]; - GetDlgItemText(m_hwndTabPage, ID_edtSize, filename, _countof(filename)); - FILE *fp = _wfopen(filename, L"rb"); - if (!fp) { - wchar_t *err = TranslateT("Select a file"); - MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING); - return; - } - fclose(fp); - mir_free(m_pszFile); m_pszFile = mir_wstrdup(filename); - } - else if (!m_hTargetWindow) { - wchar_t *err = TranslateT("Select a target window."); - MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING); - return; - } - TfrmMain::Hide(); - - if (m_opt_chkTimed) { - SetTimer(m_hWnd, ID_chkTimed, m_opt_edtTimed ? m_opt_edtTimed * 1000 : 500, nullptr); // calls EVT_CaptureDone - return; - } - if (m_opt_tabCapture == 1) { // desktop needs always time to update from TfrmMain::Hide() - SetTimer(m_hWnd, ID_chkTimed, 500, nullptr); // calls EVT_CaptureDone - return; - } - if (m_opt_tabCapture != 2) { - m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture); - } - SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::chkTimedClick() -{ - Button_Enable(GetDlgItem(m_hWnd, ID_edtTimedLabel), (BOOL)m_opt_chkTimed); - Button_Enable(GetDlgItem(m_hWnd, ID_edtTimed), (BOOL)m_opt_chkTimed); - Button_Enable(GetDlgItem(m_hWnd, ID_upTimed), (BOOL)m_opt_chkTimed); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::cboxSendByChange(void *param) -{ - BOOL bState; - HICON hIcon; - uint8_t itemFlag = SS_DLG_DESCRIPTION; - if (m_cSend) - delete m_cSend; - switch (m_opt_cboxSendBy) { - case SS_FILESEND: // "File Transfer" - m_cSend = new CSendFile(m_hWnd, m_hContact, true); - break; - case SS_EMAIL: // "E-mail" - m_cSend = new CSendEmail(m_hWnd, m_hContact, true); - break; - case SS_HTTPSERVER: // "HTTP Server" - m_cSend = new CSendHTTPServer(m_hWnd, m_hContact, true); - break; - case SS_FTPFILE: // "FTP File" - m_cSend = new CSendFTPFile(m_hWnd, m_hContact, true); - break; - case SS_CLOUDFILE: // "CloudFile" - m_cSend = new CSendCloudFile(m_hWnd, m_hContact, false, (char*)param); - break; - case SS_IMAGESHACK: // "ImageShack" - m_cSend = new CSendHost_ImageShack(m_hWnd, m_hContact, true); - break; - case SS_UPLOADPIE: // "Upload Pie" - m_cSend = new CSendHost_UploadPie(m_hWnd, m_hContact, true, (INT_PTR)param); - break; - case SS_IMGUR: - m_cSend = new CSendHost_Imgur(m_hWnd, m_hContact, true); - break; - default: - m_cSend = nullptr; - break; - } - if (m_cSend) { - itemFlag = m_cSend->GetEnableItem(); - m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend; - } - bState = (itemFlag & SS_DLG_DELETEAFTERSSEND); - hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL); - SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr)); - Button_Enable(GetDlgItem(m_hWnd, ID_chkDeleteAfterSend), bState); - - bState = (itemFlag & SS_DLG_DESCRIPTION); - hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC); - SendDlgItemMessage(m_hWnd, ID_chkDesc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr)); - Button_Enable(GetDlgItem(m_hWnd, ID_chkDesc), bState); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::btnExploreClick() -{ - if (m_FDestFolder) - ShellExecute(nullptr, L"explore", m_FDestFolder, nullptr, nullptr, SW_SHOW); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl) -{ - // get window dimensions - RECT rect = { 0 }; - RECT cliRect = { 0 }; - wchar_t B[33], H[16]; - GetWindowRect(hWnd, &rect); - if (ClientArea) { - POINT pt = { 0 }; - GetClientRect(hWnd, &cliRect); - pt.x = cliRect.left; - pt.y = cliRect.top; - ClientToScreen(hWnd, &pt); - pt.x = pt.x - rect.left; // offset x for client area - pt.y = pt.y - rect.top; // offset y for client area - rect = cliRect; - } - - _itow(rect.right - rect.left, B, 10); - _itow(rect.bottom - rect.top, H, 10); - mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B)); - mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B)); - SetDlgItemText(hTarget, Ctrl, B); -} - -void TfrmMain::edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl) -{ - wchar_t B[33], H[16]; - _itow(ABS(rect.right - rect.left), B, 10); - _itow(ABS(rect.bottom - rect.top), H, 10); - mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B)); - mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B)); - SetDlgItemText(hTarget, Ctrl, B); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR TfrmMain::SaveScreenshot(FIBITMAP *dib) -{ - if (!dib) - return 1; - - // generate file name - unsigned FileNumber = g_plugin.getDword("FileNumber", 0) + 1; - if (FileNumber > 99999) - FileNumber = 1; - - CMStringW wszFileName(m_FDestFolder); - if (wszFileName.Right(1) != L"\\") - wszFileName.Append(L"\\"); - wszFileName.AppendFormat(L"shot%.5u", FileNumber); - - // generate a description according to the screenshot - wchar_t winText[1024]; - GetDlgItemText(m_hwndTabPage, ID_edtCaption, winText, _countof(winText)); - - CMStringW wszFileDesc; - if (m_opt_tabCapture) - wszFileDesc.Format(TranslateT("Screenshot of \"%s\""), winText); - else { - if (m_opt_chkClientArea) - wszFileDesc.Format(TranslateT("Screenshot for client area of \"%s\" window"), winText); - else - wszFileDesc.Format(TranslateT("Screenshot of \"%s\" window"), winText); - } - - // convert to 32Bits (make sure it is 32bit) - FIBITMAP *dib_new = FreeImage_ConvertTo32Bits(dib); - FreeImage_SetTransparent(dib_new, TRUE); - - BOOL ret = FALSE; - HWND hwndCombo = GetDlgItem(m_hWnd, ID_cboxFormat); - switch (ComboBox_GetItemData(hwndCombo, ComboBox_GetCurSel(hwndCombo))) { - case 0: // PNG - wszFileName.Append(L".png"); - ret = FreeImage_SaveU(FIF_PNG, dib_new, wszFileName, 0); - break; - - case 1: // JPG - wszFileName.Append(L".jpg"); - { - FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr); - FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32); - FreeImage_Unload(dib32); - ret = FreeImage_SaveU(FIF_JPEG, dib24, wszFileName, 0); - FreeImage_Unload(dib24); - } - break; - - case 2: // BMP - wszFileName.Append(L".bmp"); - { - FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr); - FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32); - FreeImage_Unload(dib32); - ret = FreeImage_SaveU(FIF_BMP, dib24, wszFileName, 0); - FreeImage_Unload(dib24); - } - break; - - case 3: // TIFF (miranda freeimage interface do not support save tiff, we udse GDI+) - wszFileName.Append(L".tif"); - { - FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr); - FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32); - FreeImage_Unload(dib32); - - HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib24); - FreeImage_Unload(dib24); - SaveTIF(hBmp, wszFileName); - DeleteObject(hBmp); - } - ret = TRUE; - break; - - case 4: // GIF - wszFileName.Append(L".gif"); - { - HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib_new); - SaveGIF(hBmp, wszFileName); - DeleteObject(hBmp); - } - ret = TRUE; - break; - } - - FreeImage_Unload(dib_new); - - if (!ret) - return 1; - - g_plugin.setDword("FileNumber", FileNumber); - replaceStrW(m_pszFile, wszFileName); - - if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc) - wszFileDesc.Empty(); - - if (m_cSend) { - m_cSend->SetFile(m_pszFile); - m_cSend->SetDescription(wszFileDesc); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void TfrmMain::FormClose() -{ - bool bCanDelete = m_opt_btnDeleteAfterSend; - if (m_opt_tabCapture == 2) { // existing file - wchar_t description[1024]; - GetDlgItemText(m_hwndTabPage, ID_edtCaption, description, _countof(description)); - if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc) - *description = '\0'; - if (m_cSend) { - m_cSend->m_bDeleteAfterSend = false; // well... guess it's better to not delete existing files for now... - m_cSend->SetFile(m_pszFile); - m_cSend->SetDescription(description); - } - bCanDelete = false; - } - else if (SaveScreenshot(m_Screenshot)) { // Saving the screenshot - Show(); // Error from SaveScreenshot - return; - } - - bool send = true; - if (m_opt_chkEditor) { - SHELLEXECUTEINFO shex = { sizeof(SHELLEXECUTEINFO) }; - shex.fMask = SEE_MASK_NOCLOSEPROCESS; - shex.lpVerb = L"edit"; - shex.lpFile = m_pszFile; - shex.nShow = SW_SHOWNORMAL; - ShellExecuteEx(&shex); - if (shex.hProcess) { - uint32_t res; - MSG msg; - do { - // wait for editor exit or messages/input - res = MsgWaitForMultipleObjects(1, &shex.hProcess, 0, INFINITE, QS_ALLINPUT); - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - res = WAIT_OBJECT_0; - PostMessage(nullptr, WM_QUIT, 0, 0); // forward for outer message loops - break; - } - - // process dialog messages (of unknown dialogs) - HWND hwndDlgModeless = GetActiveWindow(); - if (hwndDlgModeless != nullptr && IsDialogMessage(hwndDlgModeless, &msg)) /* Wine fix. */ - continue; - - // process messages - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } while (res == WAIT_OBJECT_0 + 1); - CloseHandle(shex.hProcess); - } - if (MessageBox(m_hWnd, TranslateT("Send screenshot?"), L"SendSS", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL) != IDYES) - send = false; - } - - if (send && m_cSend && m_pszFile) { - if (!m_cSend->Send()) // not sent now, class deletes itself later - m_cSend = nullptr; - cboxSendByChange(nullptr); - } - else if (!send && bCanDelete) - DeleteFile(m_pszFile); - - SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CheckOpenAgain); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include <list>
+
+void TfrmMain::Unload()
+{
+ std::list<TfrmMain*> lst;
+ for (auto &it : _HandleMapping)
+ lst.push_back(it.second); // we can't delete inside loop.. not MT compatible
+
+ while (!lst.empty()) {
+ DestroyWindow(lst.front()->m_hWnd); // deletes class
+ lst.pop_front();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CALLBACK TfrmMain::DlgProc_CaptureTabPage(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ // main message handling is done inside TfrmMain::DlgTfrmMain
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ switch (lParam) {
+ case IDD_UMain_CaptureWindow:
+ Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_TARGET));
+ SetDlgItemText(hDlg, ID_edtCaption, TranslateT("Drag&Drop the target on the desired window."));
+ break;
+ case IDD_UMain_CaptureDesktop:
+ Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MONITOR));
+ break;
+ case IDD_UMain_CaptureFile:
+ Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MAIN));
+ break;
+ }
+ SetFocus(GetDlgItem(hDlg, ID_imgTarget));
+ return FALSE;
+
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+ case WM_COMMAND:
+ if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_btnExplore) { // local file tab
+ OPENFILENAME ofn = { sizeof(OPENFILENAME) };
+ wchar_t filename[MAX_PATH];
+ GetDlgItemText(hDlg, ID_edtSize, filename, _countof(filename));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hDlg;
+ ofn.lpstrFilter = L"Images\0*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tif;*.tiff\0";
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY;
+ if (GetOpenFileName(&ofn)) {
+ SetDlgItemText(hDlg, ID_edtSize, filename);
+ }
+ break;
+ }
+ SendMessage(GetParent(hDlg), uMsg, wParam, lParam);
+ break;
+ case WM_NOTIFY:
+ SendMessage(GetParent(hDlg), uMsg, wParam, lParam);
+ break;
+ case WM_DESTROY:
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+TfrmMain::CHandleMapping TfrmMain::_HandleMapping;
+
+INT_PTR CALLBACK TfrmMain::DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORSTATIC) {
+ switch (GetWindowLongPtr((HWND)lParam, GWL_ID)) {
+ case IDC_HEADERBAR:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ break;
+ default:
+ return 0;
+ }
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+
+ CHandleMapping::iterator wnd;
+ if (msg == WM_INITDIALOG) {
+ wnd = _HandleMapping.insert(CHandleMapping::value_type(hWnd, reinterpret_cast<TfrmMain*>(lParam))).first;
+ wnd->second->m_hWnd = hWnd;
+ wnd->second->wmInitdialog(wParam, lParam);
+ return 0;
+ }
+ wnd = _HandleMapping.find(hWnd);
+ if (wnd == _HandleMapping.end())
+ return 0;
+
+ switch (msg) {
+ case WM_DROPFILES:
+ // Drag&Drop of local files
+ {
+ wchar_t filename[MAX_PATH];
+ if (!DragQueryFile((HDROP)wParam, 0, filename, MAX_PATH))
+ *filename = '\0';
+ DragFinish((HDROP)wParam);
+ if (wnd->second->m_hwndTabPage)
+ ShowWindow(wnd->second->m_hwndTabPage, SW_HIDE);
+
+ wnd->second->m_opt_tabCapture = 2; // activate file tab
+ TabCtrl_SetCurSel(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture);
+
+ TAB_INFO itab = { TCIF_PARAM };
+ TabCtrl_GetItem(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture, &itab);
+ wnd->second->m_hwndTabPage = itab.hwndTabPage;
+
+ ShowWindow(wnd->second->m_hwndTabPage, SW_SHOW);
+ SetDlgItemText(wnd->second->m_hwndTabPage, ID_edtSize, filename);
+ }
+ break;
+ case WM_COMMAND:
+ wnd->second->wmCommand(wParam, lParam);
+ break;
+ case WM_CLOSE:
+ wnd->second->wmClose(wParam, lParam);
+ break;
+ case WM_DESTROY:
+ delete wnd->second;
+ break;
+ case WM_NOTIFY:
+ wnd->second->wmNotify(wParam, lParam);
+ break;
+ case WM_TIMER:
+ wnd->second->wmTimer(wParam, lParam);
+ break;
+ case UM_EVENT:
+ wnd->second->UMevent(wParam, lParam);
+ break;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WM_INITDIALOG:
+
+int EnumCloudFileServices(const CFSERVICEINFO *serviceInfo, void *param)
+{
+ HWND hCtrl = (HWND)param;
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, serviceInfo->userName), new UPLOAD_INFO(SS_CLOUDFILE, (void*)serviceInfo->accountName));
+ return 0;
+}
+
+void TfrmMain::wmInitdialog(WPARAM, LPARAM)
+{
+ HWND hCtrl;
+ // Taskbar and Window icon
+ Window_SetIcon_IcoLib(m_hWnd, GetIconHandle(ICO_MAIN));
+
+ wchar_t *pt = mir_wstrdup(Clist_GetContactDisplayName(m_hContact));
+ if (pt && (m_hContact != 0)) {
+ CMStringW string;
+ string.AppendFormat(TranslateT("Send screenshot to %s"), pt);
+ SetWindowText(m_hWnd, string);
+ }
+ mir_free(pt);
+
+ // Headerbar
+ SendDlgItemMessage(m_hWnd, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIcon(ICO_MAIN));
+
+ // Timed controls
+ CheckDlgButton(m_hWnd, ID_chkTimed, m_opt_chkTimed ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemInt(m_hWnd, ID_edtTimed, (UINT)m_opt_edtTimed, FALSE);
+ SendDlgItemMessage(m_hWnd, ID_upTimed, UDM_SETRANGE, 0, (LPARAM)MAKELONG(250, 1));
+ chkTimedClick(); // enable disable Timed controls
+
+ // create Image list for tab control
+ if (!m_himlTab) {
+ m_himlTab = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ ImageList_AddIcon(m_himlTab, GetIcon(ICO_TARGET));
+ ImageList_AddIcon(m_himlTab, GetIcon(ICO_MONITOR));
+ ImageList_AddIcon(m_himlTab, GetIconBtn(ICO_BTN_FOLDER));
+ }
+
+ // create the tab control.
+ {
+ m_hwndTab = GetDlgItem(m_hWnd, IDC_CAPTURETAB);
+ TabCtrl_SetImageList(m_hwndTab, m_himlTab);
+ TabCtrl_SetItemExtra(m_hwndTab, sizeof(TAB_INFO) - sizeof(TCITEMHEADER));
+ RECT rcTab;
+ TAB_INFO itab;
+ itab.hwndMain = m_hWnd;
+ itab.hwndTab = m_hwndTab;
+ itab.tcih.mask = TCIF_PARAM | TCIF_TEXT | TCIF_IMAGE;
+
+ // Add a tab for each of the three child dialog boxes.
+ itab.tcih.pszText = TranslateT("Window");
+ itab.tcih.iImage = 0;
+ itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureWindow), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureWindow);
+ TabCtrl_InsertItem(m_hwndTab, 0, &itab);
+
+ // get tab boundaries (required after 1st tab)
+ GetClientRect(m_hwndTab, &rcTab);
+ MapWindowPoints(m_hwndTab, m_hWnd, (POINT*)&rcTab, 2);
+ TabCtrl_AdjustRect(m_hwndTab, 0, &rcTab);
+ rcTab.bottom -= rcTab.top; rcTab.right -= rcTab.left;
+
+ SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
+ CheckDlgButton(itab.hwndTabPage, ID_chkIndirectCapture, m_opt_chkIndirectCapture ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(itab.hwndTabPage, ID_chkClientArea, m_opt_chkClientArea ? BST_CHECKED : BST_UNCHECKED);
+
+ itab.tcih.pszText = TranslateT("Desktop");
+ itab.tcih.iImage = 1;
+ itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureDesktop), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureDesktop);
+ TabCtrl_InsertItem(m_hwndTab, 1, &itab);
+ SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
+
+ hCtrl = GetDlgItem(itab.hwndTabPage, ID_edtCaption);
+ ComboBox_ResetContent(hCtrl);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Entire Desktop>")), 0);
+ ComboBox_SetCurSel(hCtrl, 0);
+ if (m_MonitorCount > 1) {
+ wchar_t tszTemp[120];
+ for (size_t mon = 0; mon < m_MonitorCount; ++mon) { // @todo : fix format for non MSVC compilers
+ mir_snwprintf(tszTemp, L"%Iu. %s%s",
+ mon + 1, TranslateT("Monitor"),
+ (m_Monitors[mon].dwFlags & MONITORINFOF_PRIMARY) ? TranslateT(" (primary)") : L""
+ );
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, tszTemp), mon + 1);
+ }
+ ComboBox_SelectItem(hCtrl, m_opt_cboxDesktop);
+ }
+ PostMessage(m_hWnd, WM_COMMAND, MAKEWPARAM(ID_edtCaption, CBN_SELCHANGE), (LPARAM)hCtrl);
+
+ itab.tcih.pszText = TranslateT("File");
+ itab.tcih.iImage = 2;
+ itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureFile), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureFile);
+ TabCtrl_InsertItem(m_hwndTab, 2, &itab);
+ SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
+
+ // select tab and set m_hwndTabPage
+ TabCtrl_SetCurSel(m_hwndTab, m_opt_tabCapture);
+ itab.tcih.mask = TCIF_PARAM;
+ TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab);
+ m_hwndTabPage = itab.hwndTabPage;
+ ShowWindow(m_hwndTabPage, SW_SHOW);
+
+ // enable Drag&Drop for local file pane
+ typedef BOOL(WINAPI *ChangeWindowMessageFilterEx_t)(HWND hwnd, UINT message, uint32_t action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
+ ChangeWindowMessageFilterEx_t pChangeWindowMessageFilterEx;
+ pChangeWindowMessageFilterEx = (ChangeWindowMessageFilterEx_t)GetProcAddress(GetModuleHandleA("user32"), "ChangeWindowMessageFilterEx");
+ if (pChangeWindowMessageFilterEx) { // Win7+, UAC fix
+ pChangeWindowMessageFilterEx(m_hWnd, WM_DROPFILES, MSGFLT_ALLOW, nullptr);
+ pChangeWindowMessageFilterEx(m_hWnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr);
+ pChangeWindowMessageFilterEx(m_hWnd, 0x0049/*WM_COPYGLOBALDATA*/, MSGFLT_ALLOW, nullptr);
+ }
+ DragAcceptFiles(m_hWnd, 1);
+ }
+
+ // init Format combo box
+ {
+ hCtrl = GetDlgItem(m_hWnd, ID_cboxFormat);
+ ComboBox_ResetContent(hCtrl);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"PNG"), 0);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"JPG"), 1);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"BMP"), 2);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"TIF"), 3);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"GIF"), 4);
+ ComboBox_SelectItem(hCtrl, m_opt_cboxFormat);
+ }
+
+ // init SendBy combo box
+ UPLOAD_INFO *pDefault = nullptr;
+ {
+ hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy);
+ ComboBox_ResetContent(hCtrl);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Only save>")), new UPLOAD_INFO(SS_JUSTSAVE));
+ if (m_hContact) {
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("File Transfer")), new UPLOAD_INFO(SS_FILESEND));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("E-mail")), new UPLOAD_INFO(SS_EMAIL));
+ if (g_myGlobals.PluginHTTPExist) {
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"HTTP Server"), new UPLOAD_INFO(SS_HTTPSERVER));
+ }
+ else if (m_opt_cboxSendBy == SS_HTTPSERVER) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ if (g_myGlobals.PluginFTPExist) {
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("FTP File")), new UPLOAD_INFO(SS_FTPFILE));
+ }
+ else if (m_opt_cboxSendBy == SS_FTPFILE) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ }
+ else if (m_opt_cboxSendBy == SS_FILESEND || m_opt_cboxSendBy == SS_EMAIL || m_opt_cboxSendBy == SS_HTTPSERVER || m_opt_cboxSendBy == SS_FTPFILE) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ if (g_myGlobals.PluginCloudFileExist) {
+ CallService(MS_CLOUDFILE_ENUMSERVICES, (WPARAM)EnumCloudFileServices, (LPARAM)hCtrl);
+ }
+ else if (m_opt_cboxSendBy == SS_CLOUDFILE) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"ImageShack"), new UPLOAD_INFO(SS_IMAGESHACK));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (30m)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)1));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1d)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)4));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1w)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)5));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"Imgur"), new UPLOAD_INFO(SS_IMGUR));
+
+ for (int i = 0; i < ComboBox_GetCount(hCtrl); i++) {
+ UPLOAD_INFO *p = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i);
+ if (p && p->sendBy == m_opt_cboxSendBy) {
+ pDefault = p;
+ ComboBox_SetCurSel(hCtrl, i);
+ break;
+ }
+ }
+ }
+
+ // init footer options
+ CheckDlgButton(m_hWnd, ID_chkOpenAgain, m_opt_chkOpenAgain ? BST_CHECKED : BST_UNCHECKED);
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_btnExplore)) {
+ SendDlgItemMessage(m_hWnd, ID_btnExplore, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open Folder"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(ICO_BTN_FOLDER);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"...");
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_chkDesc)) {
+ SendDlgItemMessage(m_hWnd, ID_chkDesc, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Fill description textbox."), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"D");
+ SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDesc ? BST_CHECKED : BST_UNCHECKED, NULL);
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_chkDeleteAfterSend)) {
+ SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete after send"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"X");
+ SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDeleteAfterSend ? BST_CHECKED : BST_UNCHECKED, NULL);
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_chkEditor)) {
+ SendDlgItemMessage(m_hWnd, ID_chkEditor, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open editor before sending"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"E");
+ SendMessage(hCtrl, BM_SETCHECK, m_opt_chkEditor ? BST_CHECKED : BST_UNCHECKED, NULL);
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_btnCapture)) {
+ SendMessage(hCtrl, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Capture"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(ICO_BTN_OK);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, TranslateT("&Capture"));
+ SendMessage(hCtrl, BUTTONSETDEFAULT, 1, NULL);
+ }
+ cboxSendByChange((pDefault) ? pDefault->param : nullptr); // enable disable controls
+
+ TranslateDialogDefault(m_hWnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WM_COMMAND:
+
+void TfrmMain::wmCommand(WPARAM wParam, LPARAM lParam)
+{
+ HICON hIcon;
+
+ int IDControl = LOWORD(wParam);
+ switch (HIWORD(wParam)) {
+ case BN_CLICKED: // Button controls
+ switch (IDControl) {
+ case IDCANCEL: // ESC pressed
+ this->Close();
+ break;
+ case ID_chkTimed:
+ m_opt_chkTimed = (uint8_t)Button_GetCheck((HWND)lParam);
+ TfrmMain::chkTimedClick();
+ break;
+ case ID_chkIndirectCapture:
+ m_opt_chkIndirectCapture = (uint8_t)Button_GetCheck((HWND)lParam);
+ break;
+ case ID_chkClientArea:
+ m_opt_chkClientArea = (uint8_t)Button_GetCheck((HWND)lParam);
+ if (m_hTargetWindow)
+ edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, GetParent((HWND)lParam), ID_edtSize);
+ break;
+ case ID_imgTarget:
+ if (m_opt_tabCapture != 0) break;
+ m_hLastWin = nullptr;
+ SetTimer(m_hWnd, ID_imgTarget, BUTTON_POLLDELAY, nullptr);
+ break;
+ case ID_btnExplore:
+ TfrmMain::btnExploreClick();
+ break;
+ case ID_chkDesc:
+ m_opt_btnDesc = !m_opt_btnDesc;
+ hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
+ SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ break;
+ case ID_chkDeleteAfterSend:
+ m_opt_btnDeleteAfterSend = !m_opt_btnDeleteAfterSend;
+ hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
+ SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ if (m_cSend) m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend;
+ break;
+ case ID_chkEditor:
+ m_opt_chkEditor = !m_opt_chkEditor;
+ hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT);
+ SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ break;
+ case ID_chkOpenAgain:
+ m_opt_chkOpenAgain = Button_GetCheck((HWND)lParam);
+ break;
+ case ID_btnCapture:
+ TfrmMain::btnCaptureClick();
+ break;
+ }
+ break;
+
+ case CBN_SELCHANGE: // ComboBox controls
+ switch (IDControl) { // lParam = Handle to the control
+ case ID_cboxFormat: // not finish
+ m_opt_cboxFormat = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ break;
+ case ID_cboxSendBy:
+ {
+ UPLOAD_INFO *upload = (UPLOAD_INFO*)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ m_opt_cboxSendBy = upload->sendBy;
+ cboxSendByChange(upload->param);
+ }
+ break;
+
+ case ID_edtCaption: // cboxDesktopChange
+ m_opt_cboxDesktop = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ m_hTargetWindow = nullptr;
+ if (m_opt_cboxDesktop > 0) {
+ edtSizeUpdate(m_Monitors[m_opt_cboxDesktop - 1].rcMonitor, GetParent((HWND)lParam), ID_edtSize);
+ }
+ else {
+ edtSizeUpdate(m_VirtualScreen, GetParent((HWND)lParam), ID_edtSize);
+ }
+ break;
+ }
+ break;
+
+ case EN_CHANGE: // Edit controls
+ switch (IDControl) { // lParam = Handle to the control
+ case ID_edtQuality:
+ m_opt_edtQuality = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtQuality, nullptr, FALSE);
+ break;
+ case ID_edtTimed:
+ m_opt_edtTimed = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtTimed, nullptr, FALSE);
+ break;
+ }
+ break;
+ }
+}
+
+// WM_CLOSE:
+void TfrmMain::wmClose(WPARAM, LPARAM)
+{
+ HWND hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy);
+ size_t count = ComboBox_GetCount(hCtrl);
+ for (size_t i = 0; i < count; i++) {
+ UPLOAD_INFO *ui = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i);
+ delete ui;
+ }
+ DestroyWindow(m_hWnd);
+ return;
+}
+
+// WM_TIMER:
+const int g_iTargetBorder = 7;
+void TfrmMain::SetTargetWindow(HWND hwnd)
+{
+ if (!hwnd) {
+ POINT point; GetCursorPos(&point);
+ hwnd = WindowFromPoint(point);
+ for (HWND hTMP; (hTMP = GetParent(hwnd)); hwnd = hTMP)
+ ;
+ }
+ m_hTargetWindow = hwnd;
+ int len = GetWindowTextLength(m_hTargetWindow) + 1;
+ wchar_t *lpTitle;
+ if (len > 1) {
+ lpTitle = (wchar_t*)mir_alloc(len*sizeof(wchar_t));
+ GetWindowText(m_hTargetWindow, lpTitle, len);
+ }
+ else { // no WindowText present, use WindowClass
+ lpTitle = (wchar_t*)mir_alloc(64 * sizeof(wchar_t));
+ RealGetWindowClass(m_hTargetWindow, lpTitle, 64);
+ }
+ SetDlgItemText(m_hwndTabPage, ID_edtCaption, lpTitle);
+ mir_free(lpTitle);
+ edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, m_hwndTabPage, ID_edtSize);
+}
+
+void TfrmMain::wmTimer(WPARAM wParam, LPARAM)
+{
+ if (wParam == ID_imgTarget) { // Timer for Target selector
+ static int primarymouse;
+ if (!m_hTargetHighlighter) {
+ primarymouse = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
+ m_hTargetHighlighter = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (wchar_t*)g_clsTargetHighlighter, nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, g_plugin.getInst(), nullptr);
+ if (!m_hTargetHighlighter) return;
+ SetLayeredWindowAttributes(m_hTargetHighlighter, 0, 123, LWA_ALPHA);
+ SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_IBEAM); // text cursor
+ SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_NORMAL);
+ SetActiveWindow(m_hTargetHighlighter); // activate highlighter to fix focus problems with UAC (unelevated GetAsyncKeyState() fails if an elevated app got focus)
+ Hide();
+ }
+ if (!(GetAsyncKeyState(primarymouse) & 0x8000)) {
+ KillTimer(m_hWnd, ID_imgTarget);
+ SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0);
+ DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr;
+ SetTargetWindow(m_hLastWin);
+ Show();
+ return;
+ }
+ POINT point; GetCursorPos(&point);
+ HWND hwnd = WindowFromPoint(point);
+ if (!((GetAsyncKeyState(VK_SHIFT) | GetAsyncKeyState(VK_MENU)) & 0x8000))
+ for (HWND hTMP; (hTMP = GetAncestor(hwnd, GA_PARENT)) && IsChild(hTMP, hwnd); hwnd = hTMP);
+ else {
+ ScreenToClient(hwnd, &point);
+ HWND hTMP; if ((hTMP = RealChildWindowFromPoint(hwnd, point)))
+ hwnd = hTMP;
+ }
+ if (hwnd != m_hLastWin) {
+ m_hLastWin = hwnd;
+ RECT rect;
+ if (m_opt_chkClientArea) {
+ GetClientRect(hwnd, &rect);
+ ClientToScreen(hwnd, (POINT*)&rect);
+ rect.right = rect.left + rect.right;
+ rect.bottom = rect.top + rect.bottom;
+ }
+ else
+ GetWindowRect(hwnd, &rect);
+ int width = rect.right - rect.left;
+ int height = rect.bottom - rect.top;
+ if (g_iTargetBorder) {
+ SetWindowPos(m_hTargetHighlighter, nullptr, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE);
+ if (width > g_iTargetBorder * 2 && height > g_iTargetBorder * 2) {
+ HRGN hRegnNew = CreateRectRgn(0, 0, width, height);
+ HRGN hRgnHole = CreateRectRgn(g_iTargetBorder, g_iTargetBorder, width - g_iTargetBorder, height - g_iTargetBorder);
+ CombineRgn(hRegnNew, hRegnNew, hRgnHole, RGN_XOR);
+ DeleteObject(hRgnHole);
+ SetWindowRgn(m_hTargetHighlighter, hRegnNew, FALSE); // cleans up hRegnNew
+ }
+ else SetWindowRgn(m_hTargetHighlighter, nullptr, FALSE);
+ }
+ SetWindowPos(m_hTargetHighlighter, HWND_TOPMOST, rect.left, rect.top, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
+ }
+ return;
+ }
+ if (wParam == ID_chkTimed) { // Timer for Screenshot
+#ifdef _DEBUG
+ OutputDebugStringA("SS Bitmap Timer Start\r\n");
+#endif
+ if (!m_bCapture) { // only start once
+ if (m_Screenshot) {
+ FreeImage_Unload(m_Screenshot);
+ m_Screenshot = nullptr;
+ }
+ m_bCapture = true;
+ switch (m_opt_tabCapture) {
+ case 0:
+ m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture);
+ break;
+ case 1:
+ m_Screenshot = CaptureMonitor((m_opt_cboxDesktop > 0) ? m_Monitors[m_opt_cboxDesktop - 1].szDevice : nullptr);
+ break;
+ case 2: // edge case, existing local file
+ break;
+#ifdef _DEBUG
+ default:
+ OutputDebugStringA("SS Bitmap Timer Stop (no tabCapture)\r\n");
+#endif
+ }
+ m_bCapture = false;
+ if (m_Screenshot || m_opt_tabCapture == 2) { // @note : test without "if"
+ KillTimer(m_hWnd, ID_chkTimed);
+#ifdef _DEBUG
+ OutputDebugStringA("SS Bitmap Timer Stop (CaptureDone)\r\n");
+#endif
+ SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone);
+ }
+ }
+ }
+}
+
+// WM_NOTIFY:
+void TfrmMain::wmNotify(WPARAM, LPARAM lParam)
+{
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_CAPTURETAB:
+ // HWND hwndFrom; = member is handle to the tab control
+ // UINT_PTR idFrom; = member is the child window identifier of the tab control.
+ // UINT code; = member is TCN_SELCHANGE
+ switch (((LPNMHDR)lParam)->code) {
+ case TCN_SELCHANGING:
+ if (m_hwndTabPage) {
+ ShowWindow(m_hwndTabPage, SW_HIDE);
+ m_hwndTabPage = nullptr;
+ }
+ break;
+
+ case TCN_SELCHANGE:
+ {
+ TAB_INFO itab = { TCIF_PARAM };
+ m_opt_tabCapture = TabCtrl_GetCurSel(m_hwndTab);
+ TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab);
+ m_hwndTabPage = itab.hwndTabPage;
+ }
+ ShowWindow(m_hwndTabPage, SW_SHOW);
+ break;
+ }
+ break;
+ }
+}
+
+// UM_EVENT:
+void TfrmMain::UMevent(WPARAM, LPARAM lParam)
+{
+ // HWND hWnd = (HWND)wParam;
+ switch (lParam) {
+ case EVT_CaptureDone:
+ if (!m_Screenshot && m_opt_tabCapture != 2) {
+ wchar_t *err = TranslateT("Couldn't take a screenshot");
+ MessageBox(nullptr, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
+ Show();
+ return;
+ }
+ FormClose();
+ break;
+
+ case EVT_SendFileDone:
+ break;
+
+ case EVT_CheckOpenAgain:
+ if (m_opt_chkOpenAgain) {
+ if (m_Screenshot) {
+ FreeImage_Unload(m_Screenshot);
+ m_Screenshot = nullptr;
+ }
+ Show();
+ }
+ else {// Saving Options and close
+ SaveOptions();
+ Close();
+ }
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Standard konstruktor/destruktor
+
+TfrmMain::TfrmMain()
+{
+ /* m_opt_XXX */
+ m_bOnExitSave = TRUE;
+
+ m_hWnd = nullptr;
+ m_hContact = NULL;
+ m_hTargetWindow = m_hLastWin = nullptr;
+ m_hTargetHighlighter = nullptr;
+ m_FDestFolder = m_pszFile = nullptr;
+ m_Screenshot = nullptr;
+ /* m_AlphaColor */
+ m_cSend = nullptr;
+
+ m_Monitors = nullptr;
+ m_MonitorCount = MonitorInfoEnum(m_Monitors, m_VirtualScreen);
+ /* m_opt_XXX */ LoadOptions();
+ m_bCapture = false;
+ /* m_hwndTab,m_hwndTabPage */
+ m_himlTab = nullptr;
+}
+
+TfrmMain::~TfrmMain()
+{
+ _HandleMapping.erase(m_hWnd);
+ mir_free(m_pszFile);
+ mir_free(m_FDestFolder);
+ mir_free(m_Monitors);
+ if (m_Screenshot) FreeImage_Unload(m_Screenshot);
+ if (m_cSend) delete m_cSend;
+ if (m_hTargetHighlighter) {
+ DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr;
+ SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load / Saving options from miranda's database
+
+void TfrmMain::LoadOptions(void)
+{
+ uint32_t rgb = g_plugin.getDword("AlphaColor", 16777215);
+ m_AlphaColor.rgbRed = GetRValue(rgb);
+ m_AlphaColor.rgbGreen = GetGValue(rgb);
+ m_AlphaColor.rgbBlue = GetBValue(rgb);
+ m_AlphaColor.rgbReserved = 0;
+
+ m_opt_edtQuality = g_plugin.getByte("JpegQuality", 75);
+
+ m_opt_tabCapture = g_plugin.getByte("Capture", 0);
+ m_opt_chkIndirectCapture = g_plugin.getByte("IndirectCapture", 0);
+ m_opt_chkClientArea = g_plugin.getByte("ClientArea", 0);
+ m_opt_cboxDesktop = g_plugin.getByte("Desktop", 0);
+
+ m_opt_chkTimed = g_plugin.getByte("TimedCap", 0);
+ m_opt_edtTimed = g_plugin.getByte("CapTime", 3);
+ m_opt_cboxFormat = g_plugin.getByte("OutputFormat", 0);
+ m_opt_cboxSendBy = g_plugin.getByte("SendBy", 0);
+
+ m_opt_btnDesc = g_plugin.getByte("AutoDescription", 1);
+ m_opt_btnDeleteAfterSend = g_plugin.getByte("DelAfterSend", 1) != 0;
+ m_opt_chkEditor = g_plugin.getByte("Preview", 0);
+ m_opt_chkOpenAgain = g_plugin.getByte("OpenAgain", 0);
+}
+
+void TfrmMain::SaveOptions(void)
+{
+ if (m_bOnExitSave) {
+ g_plugin.setDword("AlphaColor",
+ (uint32_t)RGB(m_AlphaColor.rgbRed, m_AlphaColor.rgbGreen, m_AlphaColor.rgbBlue));
+
+ g_plugin.setByte("JpegQuality", m_opt_edtQuality);
+
+ g_plugin.setByte("Capture", m_opt_tabCapture);
+ g_plugin.setByte("IndirectCapture", m_opt_chkIndirectCapture);
+ g_plugin.setByte("ClientArea", m_opt_chkClientArea);
+ g_plugin.setByte("Desktop", m_opt_cboxDesktop);
+
+ g_plugin.setByte("TimedCap", m_opt_chkTimed);
+ g_plugin.setByte("CapTime", m_opt_edtTimed);
+ g_plugin.setByte("OutputFormat", m_opt_cboxFormat);
+ g_plugin.setByte("SendBy", m_opt_cboxSendBy);
+
+ g_plugin.setByte("AutoDescription", m_opt_btnDesc);
+ g_plugin.setByte("DelAfterSend", m_opt_btnDeleteAfterSend);
+ g_plugin.setByte("Preview", m_opt_chkEditor);
+ g_plugin.setByte("OpenAgain", m_opt_chkOpenAgain);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::Init(wchar_t *DestFolder, MCONTACT Contact)
+{
+ m_FDestFolder = mir_wstrdup(DestFolder);
+ m_hContact = Contact;
+
+ // create window
+ m_hWnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMainForm), nullptr, DlgTfrmMain, (LPARAM)this);
+
+ // register object
+ _HandleMapping.insert(CHandleMapping::value_type(m_hWnd, this));
+
+ // check Contact
+ if (m_cSend)
+ m_cSend->SetContact(Contact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::btnCaptureClick()
+{
+ if (m_opt_tabCapture == 1) m_hTargetWindow = GetDesktopWindow();
+ else if (m_opt_tabCapture == 2) {
+ wchar_t filename[MAX_PATH];
+ GetDlgItemText(m_hwndTabPage, ID_edtSize, filename, _countof(filename));
+ FILE *fp = _wfopen(filename, L"rb");
+ if (!fp) {
+ wchar_t *err = TranslateT("Select a file");
+ MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
+ return;
+ }
+ fclose(fp);
+ mir_free(m_pszFile); m_pszFile = mir_wstrdup(filename);
+ }
+ else if (!m_hTargetWindow) {
+ wchar_t *err = TranslateT("Select a target window.");
+ MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
+ return;
+ }
+ TfrmMain::Hide();
+
+ if (m_opt_chkTimed) {
+ SetTimer(m_hWnd, ID_chkTimed, m_opt_edtTimed ? m_opt_edtTimed * 1000 : 500, nullptr); // calls EVT_CaptureDone
+ return;
+ }
+ if (m_opt_tabCapture == 1) { // desktop needs always time to update from TfrmMain::Hide()
+ SetTimer(m_hWnd, ID_chkTimed, 500, nullptr); // calls EVT_CaptureDone
+ return;
+ }
+ if (m_opt_tabCapture != 2) {
+ m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture);
+ }
+ SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::chkTimedClick()
+{
+ Button_Enable(GetDlgItem(m_hWnd, ID_edtTimedLabel), (BOOL)m_opt_chkTimed);
+ Button_Enable(GetDlgItem(m_hWnd, ID_edtTimed), (BOOL)m_opt_chkTimed);
+ Button_Enable(GetDlgItem(m_hWnd, ID_upTimed), (BOOL)m_opt_chkTimed);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::cboxSendByChange(void *param)
+{
+ BOOL bState;
+ HICON hIcon;
+ uint8_t itemFlag = SS_DLG_DESCRIPTION;
+ if (m_cSend)
+ delete m_cSend;
+ switch (m_opt_cboxSendBy) {
+ case SS_FILESEND: // "File Transfer"
+ m_cSend = new CSendFile(m_hWnd, m_hContact, true);
+ break;
+ case SS_EMAIL: // "E-mail"
+ m_cSend = new CSendEmail(m_hWnd, m_hContact, true);
+ break;
+ case SS_HTTPSERVER: // "HTTP Server"
+ m_cSend = new CSendHTTPServer(m_hWnd, m_hContact, true);
+ break;
+ case SS_FTPFILE: // "FTP File"
+ m_cSend = new CSendFTPFile(m_hWnd, m_hContact, true);
+ break;
+ case SS_CLOUDFILE: // "CloudFile"
+ m_cSend = new CSendCloudFile(m_hWnd, m_hContact, false, (char*)param);
+ break;
+ case SS_IMAGESHACK: // "ImageShack"
+ m_cSend = new CSendHost_ImageShack(m_hWnd, m_hContact, true);
+ break;
+ case SS_UPLOADPIE: // "Upload Pie"
+ m_cSend = new CSendHost_UploadPie(m_hWnd, m_hContact, true, (INT_PTR)param);
+ break;
+ case SS_IMGUR:
+ m_cSend = new CSendHost_Imgur(m_hWnd, m_hContact, true);
+ break;
+ default:
+ m_cSend = nullptr;
+ break;
+ }
+ if (m_cSend) {
+ itemFlag = m_cSend->GetEnableItem();
+ m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend;
+ }
+ bState = (itemFlag & SS_DLG_DELETEAFTERSSEND);
+ hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
+ SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr));
+ Button_Enable(GetDlgItem(m_hWnd, ID_chkDeleteAfterSend), bState);
+
+ bState = (itemFlag & SS_DLG_DESCRIPTION);
+ hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
+ SendDlgItemMessage(m_hWnd, ID_chkDesc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr));
+ Button_Enable(GetDlgItem(m_hWnd, ID_chkDesc), bState);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::btnExploreClick()
+{
+ if (m_FDestFolder)
+ ShellExecute(nullptr, L"explore", m_FDestFolder, nullptr, nullptr, SW_SHOW);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl)
+{
+ // get window dimensions
+ RECT rect = { 0 };
+ RECT cliRect = { 0 };
+ wchar_t B[33], H[16];
+ GetWindowRect(hWnd, &rect);
+ if (ClientArea) {
+ POINT pt = { 0 };
+ GetClientRect(hWnd, &cliRect);
+ pt.x = cliRect.left;
+ pt.y = cliRect.top;
+ ClientToScreen(hWnd, &pt);
+ pt.x = pt.x - rect.left; // offset x for client area
+ pt.y = pt.y - rect.top; // offset y for client area
+ rect = cliRect;
+ }
+
+ _itow(rect.right - rect.left, B, 10);
+ _itow(rect.bottom - rect.top, H, 10);
+ mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B));
+ mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B));
+ SetDlgItemText(hTarget, Ctrl, B);
+}
+
+void TfrmMain::edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl)
+{
+ wchar_t B[33], H[16];
+ _itow(ABS(rect.right - rect.left), B, 10);
+ _itow(ABS(rect.bottom - rect.top), H, 10);
+ mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B));
+ mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B));
+ SetDlgItemText(hTarget, Ctrl, B);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR TfrmMain::SaveScreenshot(FIBITMAP *dib)
+{
+ if (!dib)
+ return 1;
+
+ // generate file name
+ unsigned FileNumber = g_plugin.getDword("FileNumber", 0) + 1;
+ if (FileNumber > 99999)
+ FileNumber = 1;
+
+ CMStringW wszFileName(m_FDestFolder);
+ if (wszFileName.Right(1) != L"\\")
+ wszFileName.Append(L"\\");
+ wszFileName.AppendFormat(L"shot%.5u", FileNumber);
+
+ // generate a description according to the screenshot
+ wchar_t winText[1024];
+ GetDlgItemText(m_hwndTabPage, ID_edtCaption, winText, _countof(winText));
+
+ CMStringW wszFileDesc;
+ if (m_opt_tabCapture)
+ wszFileDesc.Format(TranslateT("Screenshot of \"%s\""), winText);
+ else {
+ if (m_opt_chkClientArea)
+ wszFileDesc.Format(TranslateT("Screenshot for client area of \"%s\" window"), winText);
+ else
+ wszFileDesc.Format(TranslateT("Screenshot of \"%s\" window"), winText);
+ }
+
+ // convert to 32Bits (make sure it is 32bit)
+ FIBITMAP *dib_new = FreeImage_ConvertTo32Bits(dib);
+ FreeImage_SetTransparent(dib_new, TRUE);
+
+ BOOL ret = FALSE;
+ HWND hwndCombo = GetDlgItem(m_hWnd, ID_cboxFormat);
+ switch (ComboBox_GetItemData(hwndCombo, ComboBox_GetCurSel(hwndCombo))) {
+ case 0: // PNG
+ wszFileName.Append(L".png");
+ ret = FreeImage_SaveU(FIF_PNG, dib_new, wszFileName, 0);
+ break;
+
+ case 1: // JPG
+ wszFileName.Append(L".jpg");
+ {
+ FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
+ FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
+ FreeImage_Unload(dib32);
+ ret = FreeImage_SaveU(FIF_JPEG, dib24, wszFileName, 0);
+ FreeImage_Unload(dib24);
+ }
+ break;
+
+ case 2: // BMP
+ wszFileName.Append(L".bmp");
+ {
+ FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
+ FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
+ FreeImage_Unload(dib32);
+ ret = FreeImage_SaveU(FIF_BMP, dib24, wszFileName, 0);
+ FreeImage_Unload(dib24);
+ }
+ break;
+
+ case 3: // TIFF (miranda freeimage interface do not support save tiff, we udse GDI+)
+ wszFileName.Append(L".tif");
+ {
+ FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
+ FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
+ FreeImage_Unload(dib32);
+
+ HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib24);
+ FreeImage_Unload(dib24);
+ SaveTIF(hBmp, wszFileName);
+ DeleteObject(hBmp);
+ }
+ ret = TRUE;
+ break;
+
+ case 4: // GIF
+ wszFileName.Append(L".gif");
+ {
+ HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib_new);
+ SaveGIF(hBmp, wszFileName);
+ DeleteObject(hBmp);
+ }
+ ret = TRUE;
+ break;
+ }
+
+ FreeImage_Unload(dib_new);
+
+ if (!ret)
+ return 1;
+
+ g_plugin.setDword("FileNumber", FileNumber);
+ replaceStrW(m_pszFile, wszFileName);
+
+ if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc)
+ wszFileDesc.Empty();
+
+ if (m_cSend) {
+ m_cSend->SetFile(m_pszFile);
+ m_cSend->SetDescription(wszFileDesc);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::FormClose()
+{
+ bool bCanDelete = m_opt_btnDeleteAfterSend;
+ if (m_opt_tabCapture == 2) { // existing file
+ wchar_t description[1024];
+ GetDlgItemText(m_hwndTabPage, ID_edtCaption, description, _countof(description));
+ if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc)
+ *description = '\0';
+ if (m_cSend) {
+ m_cSend->m_bDeleteAfterSend = false; // well... guess it's better to not delete existing files for now...
+ m_cSend->SetFile(m_pszFile);
+ m_cSend->SetDescription(description);
+ }
+ bCanDelete = false;
+ }
+ else if (SaveScreenshot(m_Screenshot)) { // Saving the screenshot
+ Show(); // Error from SaveScreenshot
+ return;
+ }
+
+ bool send = true;
+ if (m_opt_chkEditor) {
+ SHELLEXECUTEINFO shex = { sizeof(SHELLEXECUTEINFO) };
+ shex.fMask = SEE_MASK_NOCLOSEPROCESS;
+ shex.lpVerb = L"edit";
+ shex.lpFile = m_pszFile;
+ shex.nShow = SW_SHOWNORMAL;
+ ShellExecuteEx(&shex);
+ if (shex.hProcess) {
+ uint32_t res;
+ MSG msg;
+ do {
+ // wait for editor exit or messages/input
+ res = MsgWaitForMultipleObjects(1, &shex.hProcess, 0, INFINITE, QS_ALLINPUT);
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT) {
+ res = WAIT_OBJECT_0;
+ PostMessage(nullptr, WM_QUIT, 0, 0); // forward for outer message loops
+ break;
+ }
+
+ // process dialog messages (of unknown dialogs)
+ HWND hwndDlgModeless = GetActiveWindow();
+ if (hwndDlgModeless != nullptr && IsDialogMessage(hwndDlgModeless, &msg)) /* Wine fix. */
+ continue;
+
+ // process messages
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ } while (res == WAIT_OBJECT_0 + 1);
+ CloseHandle(shex.hProcess);
+ }
+ if (MessageBox(m_hWnd, TranslateT("Send screenshot?"), L"SendSS", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL) != IDYES)
+ send = false;
+ }
+
+ if (send && m_cSend && m_pszFile) {
+ if (!m_cSend->Send()) // not sent now, class deletes itself later
+ m_cSend = nullptr;
+ cboxSendByChange(nullptr);
+ }
+ else if (!send && bCanDelete)
+ DeleteFile(m_pszFile);
+
+ SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CheckOpenAgain);
+}
diff --git a/plugins/SendScreenshotPlus/src/UMainForm.h b/plugins/SendScreenshotPlus/src/UMainForm.h index 55b75e2373..fbe8a6a50b 100644 --- a/plugins/SendScreenshotPlus/src/UMainForm.h +++ b/plugins/SendScreenshotPlus/src/UMainForm.h @@ -1,146 +1,146 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef UMainFormH -#define UMainFormH - -#define SS_JUSTSAVE 0 -#define SS_FILESEND 1 -#define SS_EMAIL 2 -#define SS_HTTPSERVER 3 -#define SS_FTPFILE 4 -#define SS_CLOUDFILE 5 -#define SS_IMAGESHACK 6 -#define SS_UPLOADPIE 7 -#define SS_IMGUR 8 - -struct UPLOAD_INFO -{ - uint8_t sendBy; //SS_* - void *param; - - UPLOAD_INFO(uint8_t sb) : sendBy(sb), param(nullptr) { } - UPLOAD_INFO(uint8_t sb, void *p) : sendBy(sb), param(p) { } -}; - -// Used for our own cheap TrackMouseEvent -#define BUTTON_POLLDELAY 50 - -// User Events -#define EVT_CaptureDone 1 -#define EVT_SendFileDone 2 -#define EVT_CheckOpenAgain 3 - -struct TAB_INFO -{ - TCITEMHEADER tcih; - HWND hwndMain; // main window - HWND hwndTab; // tab control - HWND hwndTabPage; // current child dialog box -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -class TfrmMain -{ - -public: - // Deklaration Standardkonstruktor/Standarddestructor - TfrmMain(); - ~TfrmMain(); - - uint8_t m_opt_tabCapture; // capture tab page - uint8_t m_opt_cboxDesktop; // TRadioButton *rbtnDesktop; - uint8_t m_opt_chkTimed; // TCheckBox *chkTimed; - uint8_t m_opt_cboxSendBy; // TComboBox *cboxSendBy; - uint8_t m_opt_btnDesc; // TCheckBox *chkDesc; - uint8_t m_opt_chkEditor; // TCheckBox *chkEditor; - bool m_bOnExitSave; - - static void Unload(); - void Init(wchar_t* DestFolder, MCONTACT Contact); - void Close(){ SendMessage(m_hWnd, WM_CLOSE, 0, 0); } - void Show(){ ShowWindow(m_hWnd, SW_SHOW); } - void Hide(){ ShowWindow(m_hWnd, SW_HIDE); } - void SetTargetWindow(HWND hwnd = nullptr); - void btnCaptureClick(); - void cboxSendByChange(void *param); - -private: - HWND m_hWnd; - MCONTACT m_hContact; - HWND m_hTargetWindow, m_hLastWin; - HWND m_hTargetHighlighter; - wchar_t* m_FDestFolder; - wchar_t* m_pszFile; - FIBITMAP* m_Screenshot; - RGBQUAD m_AlphaColor; - CSend* m_cSend; - - void chkTimedClick(); - void btnExploreClick(); - void LoadOptions(void); - void SaveOptions(void); - INT_PTR SaveScreenshot(FIBITMAP* dib); - void FormClose(); - static void edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl); - static void edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl); - -protected: - MONITORINFOEX* m_Monitors; - size_t m_MonitorCount; - RECT m_VirtualScreen; - - uint8_t m_opt_chkOpenAgain; // TCheckBox *chkOpenAgain; - uint8_t m_opt_chkIndirectCapture; // TCheckBox *chkIndirectCapture; - uint8_t m_opt_chkClientArea; // TCheckBox *chkClientArea; - uint8_t m_opt_edtQuality; // TLabeledEdit *edtQuality; - bool m_opt_btnDeleteAfterSend; // TCheckBox *chkDeleteAfterSend; - uint8_t m_opt_cboxFormat; // TComboBox *cboxFormat; - uint8_t m_opt_edtTimed; // TLabeledEdit *edtTimed; - bool m_bCapture; // is capture active - HWND m_hwndTab; // TabControl handle - HWND m_hwndTabPage; // TabControl activ page handle - HIMAGELIST m_himlTab; // TabControl imagelist - - typedef std::map<HWND, TfrmMain *> CHandleMapping; - static CHandleMapping _HandleMapping; - static INT_PTR CALLBACK DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - - void wmInitdialog(WPARAM wParam, LPARAM lParam); - void wmCommand(WPARAM wParam, LPARAM lParam); - void wmClose(WPARAM wParam, LPARAM lParam); - void wmNotify(WPARAM wParam, LPARAM lParam); - void wmTimer(WPARAM wParam, LPARAM lParam); - - void UMevent(WPARAM wParam, LPARAM lParam); - - static INT_PTR CALLBACK DlgProc_CaptureTabPage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef UMainFormH
+#define UMainFormH
+
+#define SS_JUSTSAVE 0
+#define SS_FILESEND 1
+#define SS_EMAIL 2
+#define SS_HTTPSERVER 3
+#define SS_FTPFILE 4
+#define SS_CLOUDFILE 5
+#define SS_IMAGESHACK 6
+#define SS_UPLOADPIE 7
+#define SS_IMGUR 8
+
+struct UPLOAD_INFO
+{
+ uint8_t sendBy; //SS_*
+ void *param;
+
+ UPLOAD_INFO(uint8_t sb) : sendBy(sb), param(nullptr) { }
+ UPLOAD_INFO(uint8_t sb, void *p) : sendBy(sb), param(p) { }
+};
+
+// Used for our own cheap TrackMouseEvent
+#define BUTTON_POLLDELAY 50
+
+// User Events
+#define EVT_CaptureDone 1
+#define EVT_SendFileDone 2
+#define EVT_CheckOpenAgain 3
+
+struct TAB_INFO
+{
+ TCITEMHEADER tcih;
+ HWND hwndMain; // main window
+ HWND hwndTab; // tab control
+ HWND hwndTabPage; // current child dialog box
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class TfrmMain
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ TfrmMain();
+ ~TfrmMain();
+
+ uint8_t m_opt_tabCapture; // capture tab page
+ uint8_t m_opt_cboxDesktop; // TRadioButton *rbtnDesktop;
+ uint8_t m_opt_chkTimed; // TCheckBox *chkTimed;
+ uint8_t m_opt_cboxSendBy; // TComboBox *cboxSendBy;
+ uint8_t m_opt_btnDesc; // TCheckBox *chkDesc;
+ uint8_t m_opt_chkEditor; // TCheckBox *chkEditor;
+ bool m_bOnExitSave;
+
+ static void Unload();
+ void Init(wchar_t* DestFolder, MCONTACT Contact);
+ void Close(){ SendMessage(m_hWnd, WM_CLOSE, 0, 0); }
+ void Show(){ ShowWindow(m_hWnd, SW_SHOW); }
+ void Hide(){ ShowWindow(m_hWnd, SW_HIDE); }
+ void SetTargetWindow(HWND hwnd = nullptr);
+ void btnCaptureClick();
+ void cboxSendByChange(void *param);
+
+private:
+ HWND m_hWnd;
+ MCONTACT m_hContact;
+ HWND m_hTargetWindow, m_hLastWin;
+ HWND m_hTargetHighlighter;
+ wchar_t* m_FDestFolder;
+ wchar_t* m_pszFile;
+ FIBITMAP* m_Screenshot;
+ RGBQUAD m_AlphaColor;
+ CSend* m_cSend;
+
+ void chkTimedClick();
+ void btnExploreClick();
+ void LoadOptions(void);
+ void SaveOptions(void);
+ INT_PTR SaveScreenshot(FIBITMAP* dib);
+ void FormClose();
+ static void edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl);
+ static void edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl);
+
+protected:
+ MONITORINFOEX* m_Monitors;
+ size_t m_MonitorCount;
+ RECT m_VirtualScreen;
+
+ uint8_t m_opt_chkOpenAgain; // TCheckBox *chkOpenAgain;
+ uint8_t m_opt_chkIndirectCapture; // TCheckBox *chkIndirectCapture;
+ uint8_t m_opt_chkClientArea; // TCheckBox *chkClientArea;
+ uint8_t m_opt_edtQuality; // TLabeledEdit *edtQuality;
+ bool m_opt_btnDeleteAfterSend; // TCheckBox *chkDeleteAfterSend;
+ uint8_t m_opt_cboxFormat; // TComboBox *cboxFormat;
+ uint8_t m_opt_edtTimed; // TLabeledEdit *edtTimed;
+ bool m_bCapture; // is capture active
+ HWND m_hwndTab; // TabControl handle
+ HWND m_hwndTabPage; // TabControl activ page handle
+ HIMAGELIST m_himlTab; // TabControl imagelist
+
+ typedef std::map<HWND, TfrmMain *> CHandleMapping;
+ static CHandleMapping _HandleMapping;
+ static INT_PTR CALLBACK DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ void wmInitdialog(WPARAM wParam, LPARAM lParam);
+ void wmCommand(WPARAM wParam, LPARAM lParam);
+ void wmClose(WPARAM wParam, LPARAM lParam);
+ void wmNotify(WPARAM wParam, LPARAM lParam);
+ void wmTimer(WPARAM wParam, LPARAM lParam);
+
+ void UMevent(WPARAM wParam, LPARAM lParam);
+
+ static INT_PTR CALLBACK DlgProc_CaptureTabPage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/Utils.cpp b/plugins/SendScreenshotPlus/src/Utils.cpp index 1d0d92f5ea..bc543d1024 100644 --- a/plugins/SendScreenshotPlus/src/Utils.cpp +++ b/plugins/SendScreenshotPlus/src/Utils.cpp @@ -1,412 +1,412 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -void ComboBox_SelectItem(HWND hCombo, LPARAM data) -{ - for (int i = 0;; i++) { - LPARAM itemData = ComboBox_GetItemData(hCombo, i); - if (itemData == data) { - ComboBox_SetCurSel(hCombo, i); - return; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// MonitorInfoEnum - -static BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) -{ - MONITORS* monitors = (MONITORS*)dwData; - ++monitors->count; - monitors->info = (MONITORINFOEX*)mir_realloc(monitors->info, sizeof(MONITORINFOEX)*monitors->count); - monitors->info[monitors->count - 1].cbSize = sizeof(MONITORINFOEX); - if (!GetMonitorInfo(hMonitor, (LPMONITORINFO)(monitors->info + monitors->count - 1))) - return FALSE; // stop enumeration if error - - return TRUE; -} - -size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen) -{ - MONITORS tmp = { 0, nullptr }; - if (EnumDisplayMonitors(nullptr, nullptr, MonitorInfoEnumProc, (LPARAM)&tmp)) { - myMonitors = tmp.info; - memset(&virtualScreen, 0, sizeof(virtualScreen)); - for (size_t i = 0; i < tmp.count; ++i) { - UnionRect(&virtualScreen, &virtualScreen, &tmp.info[i].rcMonitor); - } - return tmp.count; - } - - mir_free(tmp.info); - return 0; -} - -FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture = nullptr); - -///////////////////////////////////////////////////////////////////////////////////////// -// capture window as FIBITMAP - caller must FreeImage_Unload(dib) - -FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture) -{ - FIBITMAP* dib; - HWND hForegroundWin; - RECT rect; // cropping rect - - if (!hCapture || !IsWindow(hCapture)) - return nullptr; - - hForegroundWin = GetForegroundWindow(); // old foreground window - SetForegroundWindow(hCapture); // force target foreground - BringWindowToTop(hCapture); // bring it to top as well - - // redraw window to prevent runtime artifacts in picture - UpdateWindow(hCapture); - - HWND hParent = GetAncestor(hCapture, GA_PARENT); - if (hParent && !IsChild(hParent, hCapture)) - hParent = nullptr; - if (bIndirectCapture) { - intptr_t wastopmost = GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_TOPMOST; - if (!wastopmost) - SetWindowPos(hCapture, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - if (bClientArea) { - GetClientRect(hCapture, &rect); - ClientToScreen(hCapture, (POINT*)&rect); - rect.right += rect.left; rect.bottom += rect.top; - } - else - GetWindowRect(hCapture, &rect); - dib = CaptureMonitor(nullptr, &rect); - if (!wastopmost) - SetWindowPos(hCapture, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } - else { - HDC hDCsrc; - GetWindowRect(hCapture, &rect); - if (hParent) - hDCsrc = GetDC(hCapture); // hCapture is part of a window, capture that - else - hDCsrc = GetWindowDC(hCapture); // entire window w/ title bar - rect.right = ABS(rect.right - rect.left); - rect.bottom = ABS(rect.bottom - rect.top); - rect.left = rect.top = 0; - // capture window and get FIBITMAP - dib = CreateDIBFromDC(hDCsrc, &rect, hCapture); - ReleaseDC(hCapture, hDCsrc); - - // we could capture directly, but doing so breaks GetWindowRgn() and also includes artifacts... - if (bClientArea) { - GetWindowRect(hCapture, &rect); - RECT rectCA; GetClientRect(hCapture, &rectCA); - ClientToScreen(hCapture, (POINT*)&rectCA); - rectCA.left = ABS(rectCA.left - rect.left); - rectCA.top = ABS(rectCA.top - rect.top); - rectCA.right += rectCA.left; rectCA.bottom += rectCA.top; - - // crop the window to ClientArea - FIBITMAP* dibClient = FreeImage_Copy(dib, rectCA.left, rectCA.top, rectCA.right, rectCA.bottom); - FreeImage_Unload(dib); - dib = dibClient; - } - } - - // restore previous foreground window - if (hForegroundWin) { - SetForegroundWindow(hForegroundWin); - BringWindowToTop(hForegroundWin); - } - return dib; -} - -FIBITMAP* CaptureMonitor(const wchar_t* szDevice, const RECT* cropRect/*=NULL*/) -{ - HDC hScrDC; - RECT rect; - - // get screen resolution - if (!szDevice) { - hScrDC = CreateDC(L"DISPLAY", nullptr, nullptr, nullptr); - rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); - rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); - rect.right = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN); - rect.bottom = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN); - } - else { - hScrDC = CreateDC(szDevice, nullptr, nullptr, nullptr); - rect.left = rect.top = 0; - rect.right = GetDeviceCaps(hScrDC, HORZRES); - rect.bottom = GetDeviceCaps(hScrDC, VERTRES); - } - if (cropRect) { - if (cropRect->left > rect.left) rect.left = cropRect->left; - if (cropRect->top > rect.top) rect.top = cropRect->top; - if (cropRect->right < rect.right) rect.right = cropRect->right; - if (cropRect->bottom < rect.bottom) rect.bottom = cropRect->bottom; - } - - FIBITMAP *dib = CreateDIBFromDC(hScrDC, &rect); - ReleaseDC(nullptr, hScrDC); - return dib; -} - -FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture/*=NULL*/) -{ - long width = rect->right - rect->left; - long height = rect->bottom - rect->top; - - // create a DC for the screen and create - // a memory DC compatible to screen DC - HDC hScrDC = hDC; - if (!hScrDC) - hScrDC = GetDC(hCapture); - HDC hMemDC = CreateCompatibleDC(hScrDC); - // create a bitmap compatible with the screen DC - HBITMAP hBitmap = CreateCompatibleBitmap(hScrDC, width, height); - // select new bitmap into memory DC - SelectObject(hMemDC, hBitmap); - - if (hCapture && hDC) - PrintWindow(hCapture, hMemDC, 0); - else // bitblt screen DC to memory DC - BitBlt(hMemDC, 0, 0, width, height, hScrDC, rect->left, rect->top, CAPTUREBLT | SRCCOPY); - - FIBITMAP *dib = FreeImage_CreateDIBFromHBITMAP(hBitmap); - - // alpha channel from window is always wrong and sometimes even for desktop (Win7, no aero) - // coz GDI do not draw all in alpha mode. - // we have to create our own new alpha channel. - bool bFixAlpha = true; - bool bInvert = false; - HBRUSH hBr = CreateSolidBrush(RGB(255, 255, 255)); // Create a SolidBrush object for non transparent area - HBITMAP hMask = CreateBitmap(width, height, 1, 1, nullptr); // Create monochrome (1 bit) B+W mask bitmap. - HDC hMaskDC = CreateCompatibleDC(nullptr); - SelectBitmap(hMaskDC, hMask); - HRGN hRgn = CreateRectRgn(0, 0, 0, 0); - if (hCapture && GetWindowRgn(hCapture, hRgn) == ERROR) { - if ((GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_LAYERED)) { - uint8_t bAlpha = 0; - COLORREF crKey = 0x00000000; - DWORD dwFlags = 0; - if (GetLayeredWindowAttributes(hCapture, &crKey, &bAlpha, &dwFlags)) { - // per window transparency (like fading in a whole window) - if ((dwFlags&LWA_COLORKEY)) { - SetBkColor(hMemDC, crKey); - BitBlt(hMaskDC, 0, 0, width, height, hMemDC, rect->left, rect->top, SRCCOPY); - bInvert = true; - } - else if ((dwFlags&LWA_ALPHA)) { - bFixAlpha = false; - } - } - else { // per-pixel transparency (won't use the WM_PAINT) - bFixAlpha = false; - } - } - else { // not layered - fill the window region - SetRectRgn(hRgn, 0, 0, width, height); - FillRgn(hMaskDC, hRgn, hBr); - } - } - else { - if (!hCapture) SetRectRgn(hRgn, 0, 0, width, height); // client area only, no transparency - FillRgn(hMaskDC, hRgn, hBr); - } - DeleteObject(hRgn); - if (bFixAlpha) { - FIBITMAP* dibMask = FreeImage_CreateDIBFromHBITMAP(hMask); - if (bInvert) FreeImage_Invert(dibMask); - FIBITMAP* dib8 = FreeImage_ConvertTo8Bits(dibMask); - // copy the dib8 alpha mask to dib32 main bitmap - FreeImage_SetChannel(dib, dib8, FICC_ALPHA); - FreeImage_Unload(dibMask); - FreeImage_Unload(dib8); - } - DeleteDC(hMaskDC); - DeleteObject(hMask); - DeleteObject(hBr); - - // clean up - DeleteDC(hMemDC); - DeleteObject(hBitmap); - if (!hDC) - ReleaseDC(nullptr, hScrDC); - -#ifdef _DEBUG - switch (FreeImage_GetImageType(dib)) { - case FIT_UNKNOWN: - OutputDebugStringA("FIBITMAP Type: FIT_UNKNOWN\r\n"); - break; - case FIT_BITMAP: - OutputDebugStringA("FIBITMAP Type: FIT_BITMAP\r\n"); - break; - case FIT_UINT16: - OutputDebugStringA("FIBITMAP Type: FIT_UINT16\r\n"); - break; - case FIT_INT16: - OutputDebugStringA("FIBITMAP Type: FIT_INT16\r\n"); - break; - case FIT_UINT32: - OutputDebugStringA("FIBITMAP Type: FIT_UINT32\r\n"); - break; - case FIT_INT32: - OutputDebugStringA("FIBITMAP Type: FIT_INT32\r\n"); - break; - case FIT_FLOAT: - OutputDebugStringA("FIBITMAP Type: FIT_FLOAT\r\n"); - break; - case FIT_DOUBLE: - OutputDebugStringA("FIBITMAP Type: FIT_DOUBLE\r\n"); - break; - case FIT_COMPLEX: - OutputDebugStringA("FIBITMAP Type: FIT_COMPLEX\r\n"); - break; - case FIT_RGB16: - OutputDebugStringA("FIBITMAP Type: FIT_RGB16\r\n"); - break; - case FIT_RGBA16: - OutputDebugStringA("FIBITMAP Type: FIT_RGBA16\r\n"); - break; - case FIT_RGBF: - OutputDebugStringA("FIBITMAP Type: FIT_RGBF\r\n"); - break; - case FIT_RGBAF: - OutputDebugStringA("FIBITMAP Type: FIT_RGBAF\r\n"); - break; - default: - OutputDebugStringA("FIBITMAP Type: non detectable image type (error)\r\n"); - break; - } - BOOL inf = FreeImage_IsTransparent(dib); - OutputDebugStringA(inf ? "FIBITMAP Transparent: true\r\n" : "FIBITMAP Transparent: false\r\n"); -#endif - return dib; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* GetFileNameA(const wchar_t* pszPath) -{ - const wchar_t* slash = wcsrchr(pszPath, '\\'); - if (!slash) slash = wcsrchr(pszPath, '/'); - if (slash) - return mir_u2a(slash + 1); - else - return mir_u2a(pszPath); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder) -{ - UINT uiNum = 0; - UINT uiSize = 0; - BOOL bOk = FALSE; - Gdiplus::GetImageEncodersSize(&uiNum, &uiSize); - if (uiSize > 0) { - Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)mir_alloc(uiSize); - if (pImageCodecInfo) { - Gdiplus::GetImageEncoders(uiNum, uiSize, pImageCodecInfo); - for (UINT i = 0; i < uiNum; ++i) { - if (!mir_wstrcmp(pImageCodecInfo[i].MimeType, wchMimeType)) { - clsidEncoder = pImageCodecInfo[i].Clsid; - bOk = TRUE; - } - } - mir_free(pImageCodecInfo); - } - } - return bOk; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void SaveGIF(HBITMAP hBmp, const wchar_t *szFilename) -{ - Gdiplus::GdiplusStartupInput gdiplusStartupInput; - ULONG_PTR gdiplusToken; - Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); - - Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE)); - if (pBitmap) { - // Get the CLSID of the GIF encoder. - CLSID clsidEncoder; - if (GetEncoderClsid(L"image/gif", clsidEncoder)) { - LPWSTR pswFile = mir_wstrdup(szFilename); - pBitmap->Save((const wchar_t*)pswFile, &clsidEncoder, nullptr); - mir_free(pswFile); - } - delete pBitmap; - } - Gdiplus::GdiplusShutdown(gdiplusToken); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void SaveTIF(HBITMAP hBmp, const wchar_t *szFilename) -{ - // http://www.codeproject.com/Messages/1406708/How-to-reduce-the-size-of-an-Image-using-GDIplus.aspx - ULONG_PTR gdiplusToken; - Gdiplus::GdiplusStartupInput gdiplusStartupInput; - Gdiplus::Status stat; - Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); - - Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE)); - if (pBitmap) { - // Get the CLSID of the GIF encoder. - CLSID EncCLSID; - if (GetEncoderClsid(L"image/tiff", EncCLSID)) { - //--- Create a 2-parameter array, for Compression and for Color Bit depth - Gdiplus::EncoderParameters* EncParams = (Gdiplus::EncoderParameters*) malloc(sizeof(Gdiplus::EncoderParameters) + 1 * sizeof(Gdiplus::EncoderParameter)); - // Gdiplus::EncoderParameters pEncoderParameters; - //--- Use LZW Compression instead of Group 4, since it works for color and G4 doesn't - ULONG ulCompression = Gdiplus::EncoderValueCompressionLZW; - ULONG ulColorDepth = 24L; - - EncParams->Count = 2; - EncParams->Parameter[0].Guid = Gdiplus::EncoderCompression; - EncParams->Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong; - EncParams->Parameter[0].NumberOfValues = 1; - EncParams->Parameter[0].Value = &ulCompression; - EncParams->Parameter[1].Guid = Gdiplus::EncoderColorDepth; - EncParams->Parameter[1].Type = Gdiplus::EncoderParameterValueTypeLong; - EncParams->Parameter[1].NumberOfValues = 1; - EncParams->Parameter[1].Value = &ulColorDepth; - - LPWSTR pswFile = mir_wstrdup(szFilename); - stat = pBitmap->Save((const wchar_t*)pswFile, &EncCLSID, EncParams); - mir_free(pswFile); - free(EncParams); - } - delete pBitmap; - } - Gdiplus::GdiplusShutdown(gdiplusToken); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+void ComboBox_SelectItem(HWND hCombo, LPARAM data)
+{
+ for (int i = 0;; i++) {
+ LPARAM itemData = ComboBox_GetItemData(hCombo, i);
+ if (itemData == data) {
+ ComboBox_SetCurSel(hCombo, i);
+ return;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MonitorInfoEnum
+
+static BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ MONITORS* monitors = (MONITORS*)dwData;
+ ++monitors->count;
+ monitors->info = (MONITORINFOEX*)mir_realloc(monitors->info, sizeof(MONITORINFOEX)*monitors->count);
+ monitors->info[monitors->count - 1].cbSize = sizeof(MONITORINFOEX);
+ if (!GetMonitorInfo(hMonitor, (LPMONITORINFO)(monitors->info + monitors->count - 1)))
+ return FALSE; // stop enumeration if error
+
+ return TRUE;
+}
+
+size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen)
+{
+ MONITORS tmp = { 0, nullptr };
+ if (EnumDisplayMonitors(nullptr, nullptr, MonitorInfoEnumProc, (LPARAM)&tmp)) {
+ myMonitors = tmp.info;
+ memset(&virtualScreen, 0, sizeof(virtualScreen));
+ for (size_t i = 0; i < tmp.count; ++i) {
+ UnionRect(&virtualScreen, &virtualScreen, &tmp.info[i].rcMonitor);
+ }
+ return tmp.count;
+ }
+
+ mir_free(tmp.info);
+ return 0;
+}
+
+FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture = nullptr);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// capture window as FIBITMAP - caller must FreeImage_Unload(dib)
+
+FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture)
+{
+ FIBITMAP* dib;
+ HWND hForegroundWin;
+ RECT rect; // cropping rect
+
+ if (!hCapture || !IsWindow(hCapture))
+ return nullptr;
+
+ hForegroundWin = GetForegroundWindow(); // old foreground window
+ SetForegroundWindow(hCapture); // force target foreground
+ BringWindowToTop(hCapture); // bring it to top as well
+
+ // redraw window to prevent runtime artifacts in picture
+ UpdateWindow(hCapture);
+
+ HWND hParent = GetAncestor(hCapture, GA_PARENT);
+ if (hParent && !IsChild(hParent, hCapture))
+ hParent = nullptr;
+ if (bIndirectCapture) {
+ intptr_t wastopmost = GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_TOPMOST;
+ if (!wastopmost)
+ SetWindowPos(hCapture, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ if (bClientArea) {
+ GetClientRect(hCapture, &rect);
+ ClientToScreen(hCapture, (POINT*)&rect);
+ rect.right += rect.left; rect.bottom += rect.top;
+ }
+ else
+ GetWindowRect(hCapture, &rect);
+ dib = CaptureMonitor(nullptr, &rect);
+ if (!wastopmost)
+ SetWindowPos(hCapture, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ else {
+ HDC hDCsrc;
+ GetWindowRect(hCapture, &rect);
+ if (hParent)
+ hDCsrc = GetDC(hCapture); // hCapture is part of a window, capture that
+ else
+ hDCsrc = GetWindowDC(hCapture); // entire window w/ title bar
+ rect.right = ABS(rect.right - rect.left);
+ rect.bottom = ABS(rect.bottom - rect.top);
+ rect.left = rect.top = 0;
+ // capture window and get FIBITMAP
+ dib = CreateDIBFromDC(hDCsrc, &rect, hCapture);
+ ReleaseDC(hCapture, hDCsrc);
+
+ // we could capture directly, but doing so breaks GetWindowRgn() and also includes artifacts...
+ if (bClientArea) {
+ GetWindowRect(hCapture, &rect);
+ RECT rectCA; GetClientRect(hCapture, &rectCA);
+ ClientToScreen(hCapture, (POINT*)&rectCA);
+ rectCA.left = ABS(rectCA.left - rect.left);
+ rectCA.top = ABS(rectCA.top - rect.top);
+ rectCA.right += rectCA.left; rectCA.bottom += rectCA.top;
+
+ // crop the window to ClientArea
+ FIBITMAP* dibClient = FreeImage_Copy(dib, rectCA.left, rectCA.top, rectCA.right, rectCA.bottom);
+ FreeImage_Unload(dib);
+ dib = dibClient;
+ }
+ }
+
+ // restore previous foreground window
+ if (hForegroundWin) {
+ SetForegroundWindow(hForegroundWin);
+ BringWindowToTop(hForegroundWin);
+ }
+ return dib;
+}
+
+FIBITMAP* CaptureMonitor(const wchar_t* szDevice, const RECT* cropRect/*=NULL*/)
+{
+ HDC hScrDC;
+ RECT rect;
+
+ // get screen resolution
+ if (!szDevice) {
+ hScrDC = CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
+ rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ rect.right = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ rect.bottom = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ }
+ else {
+ hScrDC = CreateDC(szDevice, nullptr, nullptr, nullptr);
+ rect.left = rect.top = 0;
+ rect.right = GetDeviceCaps(hScrDC, HORZRES);
+ rect.bottom = GetDeviceCaps(hScrDC, VERTRES);
+ }
+ if (cropRect) {
+ if (cropRect->left > rect.left) rect.left = cropRect->left;
+ if (cropRect->top > rect.top) rect.top = cropRect->top;
+ if (cropRect->right < rect.right) rect.right = cropRect->right;
+ if (cropRect->bottom < rect.bottom) rect.bottom = cropRect->bottom;
+ }
+
+ FIBITMAP *dib = CreateDIBFromDC(hScrDC, &rect);
+ ReleaseDC(nullptr, hScrDC);
+ return dib;
+}
+
+FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture/*=NULL*/)
+{
+ long width = rect->right - rect->left;
+ long height = rect->bottom - rect->top;
+
+ // create a DC for the screen and create
+ // a memory DC compatible to screen DC
+ HDC hScrDC = hDC;
+ if (!hScrDC)
+ hScrDC = GetDC(hCapture);
+ HDC hMemDC = CreateCompatibleDC(hScrDC);
+ // create a bitmap compatible with the screen DC
+ HBITMAP hBitmap = CreateCompatibleBitmap(hScrDC, width, height);
+ // select new bitmap into memory DC
+ SelectObject(hMemDC, hBitmap);
+
+ if (hCapture && hDC)
+ PrintWindow(hCapture, hMemDC, 0);
+ else // bitblt screen DC to memory DC
+ BitBlt(hMemDC, 0, 0, width, height, hScrDC, rect->left, rect->top, CAPTUREBLT | SRCCOPY);
+
+ FIBITMAP *dib = FreeImage_CreateDIBFromHBITMAP(hBitmap);
+
+ // alpha channel from window is always wrong and sometimes even for desktop (Win7, no aero)
+ // coz GDI do not draw all in alpha mode.
+ // we have to create our own new alpha channel.
+ bool bFixAlpha = true;
+ bool bInvert = false;
+ HBRUSH hBr = CreateSolidBrush(RGB(255, 255, 255)); // Create a SolidBrush object for non transparent area
+ HBITMAP hMask = CreateBitmap(width, height, 1, 1, nullptr); // Create monochrome (1 bit) B+W mask bitmap.
+ HDC hMaskDC = CreateCompatibleDC(nullptr);
+ SelectBitmap(hMaskDC, hMask);
+ HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
+ if (hCapture && GetWindowRgn(hCapture, hRgn) == ERROR) {
+ if ((GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_LAYERED)) {
+ uint8_t bAlpha = 0;
+ COLORREF crKey = 0x00000000;
+ DWORD dwFlags = 0;
+ if (GetLayeredWindowAttributes(hCapture, &crKey, &bAlpha, &dwFlags)) {
+ // per window transparency (like fading in a whole window)
+ if ((dwFlags&LWA_COLORKEY)) {
+ SetBkColor(hMemDC, crKey);
+ BitBlt(hMaskDC, 0, 0, width, height, hMemDC, rect->left, rect->top, SRCCOPY);
+ bInvert = true;
+ }
+ else if ((dwFlags&LWA_ALPHA)) {
+ bFixAlpha = false;
+ }
+ }
+ else { // per-pixel transparency (won't use the WM_PAINT)
+ bFixAlpha = false;
+ }
+ }
+ else { // not layered - fill the window region
+ SetRectRgn(hRgn, 0, 0, width, height);
+ FillRgn(hMaskDC, hRgn, hBr);
+ }
+ }
+ else {
+ if (!hCapture) SetRectRgn(hRgn, 0, 0, width, height); // client area only, no transparency
+ FillRgn(hMaskDC, hRgn, hBr);
+ }
+ DeleteObject(hRgn);
+ if (bFixAlpha) {
+ FIBITMAP* dibMask = FreeImage_CreateDIBFromHBITMAP(hMask);
+ if (bInvert) FreeImage_Invert(dibMask);
+ FIBITMAP* dib8 = FreeImage_ConvertTo8Bits(dibMask);
+ // copy the dib8 alpha mask to dib32 main bitmap
+ FreeImage_SetChannel(dib, dib8, FICC_ALPHA);
+ FreeImage_Unload(dibMask);
+ FreeImage_Unload(dib8);
+ }
+ DeleteDC(hMaskDC);
+ DeleteObject(hMask);
+ DeleteObject(hBr);
+
+ // clean up
+ DeleteDC(hMemDC);
+ DeleteObject(hBitmap);
+ if (!hDC)
+ ReleaseDC(nullptr, hScrDC);
+
+#ifdef _DEBUG
+ switch (FreeImage_GetImageType(dib)) {
+ case FIT_UNKNOWN:
+ OutputDebugStringA("FIBITMAP Type: FIT_UNKNOWN\r\n");
+ break;
+ case FIT_BITMAP:
+ OutputDebugStringA("FIBITMAP Type: FIT_BITMAP\r\n");
+ break;
+ case FIT_UINT16:
+ OutputDebugStringA("FIBITMAP Type: FIT_UINT16\r\n");
+ break;
+ case FIT_INT16:
+ OutputDebugStringA("FIBITMAP Type: FIT_INT16\r\n");
+ break;
+ case FIT_UINT32:
+ OutputDebugStringA("FIBITMAP Type: FIT_UINT32\r\n");
+ break;
+ case FIT_INT32:
+ OutputDebugStringA("FIBITMAP Type: FIT_INT32\r\n");
+ break;
+ case FIT_FLOAT:
+ OutputDebugStringA("FIBITMAP Type: FIT_FLOAT\r\n");
+ break;
+ case FIT_DOUBLE:
+ OutputDebugStringA("FIBITMAP Type: FIT_DOUBLE\r\n");
+ break;
+ case FIT_COMPLEX:
+ OutputDebugStringA("FIBITMAP Type: FIT_COMPLEX\r\n");
+ break;
+ case FIT_RGB16:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGB16\r\n");
+ break;
+ case FIT_RGBA16:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGBA16\r\n");
+ break;
+ case FIT_RGBF:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGBF\r\n");
+ break;
+ case FIT_RGBAF:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGBAF\r\n");
+ break;
+ default:
+ OutputDebugStringA("FIBITMAP Type: non detectable image type (error)\r\n");
+ break;
+ }
+ BOOL inf = FreeImage_IsTransparent(dib);
+ OutputDebugStringA(inf ? "FIBITMAP Transparent: true\r\n" : "FIBITMAP Transparent: false\r\n");
+#endif
+ return dib;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* GetFileNameA(const wchar_t* pszPath)
+{
+ const wchar_t* slash = wcsrchr(pszPath, '\\');
+ if (!slash) slash = wcsrchr(pszPath, '/');
+ if (slash)
+ return mir_u2a(slash + 1);
+ else
+ return mir_u2a(pszPath);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder)
+{
+ UINT uiNum = 0;
+ UINT uiSize = 0;
+ BOOL bOk = FALSE;
+ Gdiplus::GetImageEncodersSize(&uiNum, &uiSize);
+ if (uiSize > 0) {
+ Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)mir_alloc(uiSize);
+ if (pImageCodecInfo) {
+ Gdiplus::GetImageEncoders(uiNum, uiSize, pImageCodecInfo);
+ for (UINT i = 0; i < uiNum; ++i) {
+ if (!mir_wstrcmp(pImageCodecInfo[i].MimeType, wchMimeType)) {
+ clsidEncoder = pImageCodecInfo[i].Clsid;
+ bOk = TRUE;
+ }
+ }
+ mir_free(pImageCodecInfo);
+ }
+ }
+ return bOk;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SaveGIF(HBITMAP hBmp, const wchar_t *szFilename)
+{
+ Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ ULONG_PTR gdiplusToken;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+
+ Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE));
+ if (pBitmap) {
+ // Get the CLSID of the GIF encoder.
+ CLSID clsidEncoder;
+ if (GetEncoderClsid(L"image/gif", clsidEncoder)) {
+ LPWSTR pswFile = mir_wstrdup(szFilename);
+ pBitmap->Save((const wchar_t*)pswFile, &clsidEncoder, nullptr);
+ mir_free(pswFile);
+ }
+ delete pBitmap;
+ }
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SaveTIF(HBITMAP hBmp, const wchar_t *szFilename)
+{
+ // http://www.codeproject.com/Messages/1406708/How-to-reduce-the-size-of-an-Image-using-GDIplus.aspx
+ ULONG_PTR gdiplusToken;
+ Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ Gdiplus::Status stat;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+
+ Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE));
+ if (pBitmap) {
+ // Get the CLSID of the GIF encoder.
+ CLSID EncCLSID;
+ if (GetEncoderClsid(L"image/tiff", EncCLSID)) {
+ //--- Create a 2-parameter array, for Compression and for Color Bit depth
+ Gdiplus::EncoderParameters* EncParams = (Gdiplus::EncoderParameters*) malloc(sizeof(Gdiplus::EncoderParameters) + 1 * sizeof(Gdiplus::EncoderParameter));
+ // Gdiplus::EncoderParameters pEncoderParameters;
+ //--- Use LZW Compression instead of Group 4, since it works for color and G4 doesn't
+ ULONG ulCompression = Gdiplus::EncoderValueCompressionLZW;
+ ULONG ulColorDepth = 24L;
+
+ EncParams->Count = 2;
+ EncParams->Parameter[0].Guid = Gdiplus::EncoderCompression;
+ EncParams->Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
+ EncParams->Parameter[0].NumberOfValues = 1;
+ EncParams->Parameter[0].Value = &ulCompression;
+ EncParams->Parameter[1].Guid = Gdiplus::EncoderColorDepth;
+ EncParams->Parameter[1].Type = Gdiplus::EncoderParameterValueTypeLong;
+ EncParams->Parameter[1].NumberOfValues = 1;
+ EncParams->Parameter[1].Value = &ulColorDepth;
+
+ LPWSTR pswFile = mir_wstrdup(szFilename);
+ stat = pBitmap->Save((const wchar_t*)pswFile, &EncCLSID, EncParams);
+ mir_free(pswFile);
+ free(EncParams);
+ }
+ delete pBitmap;
+ }
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
diff --git a/plugins/SendScreenshotPlus/src/Utils.h b/plugins/SendScreenshotPlus/src/Utils.h index 89dd2f614e..df736da08f 100644 --- a/plugins/SendScreenshotPlus/src/Utils.h +++ b/plugins/SendScreenshotPlus/src/Utils.h @@ -1,73 +1,73 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef UTILSH -#define UTILSH - -#define SPP_USERPANE 1 - -#define ABS(x) ((x)<0?-(x):(x)) - -struct MONITORS -{ - size_t count; - MONITORINFOEX* info; -}; - -extern HWND g_hCapture; -extern HBITMAP g_hBitmap, g_hbmMask; - -///////////////////////////////////////////////////////////////////////////////////////// - -size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen); - -FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture); -FIBITMAP* CaptureMonitor(const wchar_t *pwszDevice, const RECT *cropRect = nullptr); - -char* GetFileNameA(const wchar_t *pwszPath); - -BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder); - -void SaveGIF(HBITMAP hBmp, const wchar_t *pwszFilename); -void SaveTIF(HBITMAP hBmp, const wchar_t *pwszFilename); - -///////////////////////////////////////////////////////////////////////////////////////// - -class EventHandle -{ - HANDLE _hEvent; -public: - inline EventHandle() { _hEvent = CreateEvent(nullptr, 0, 0, nullptr); } - inline ~EventHandle() { CloseHandle(_hEvent); } - inline void Set() { SetEvent(_hEvent); } - inline void Wait() { WaitForSingleObject(_hEvent, INFINITE); } - inline void Wait(uint32_t dwMilliseconds) { WaitForSingleObject(_hEvent, dwMilliseconds); } - inline operator HANDLE() { return _hEvent; } -}; - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef UTILSH
+#define UTILSH
+
+#define SPP_USERPANE 1
+
+#define ABS(x) ((x)<0?-(x):(x))
+
+struct MONITORS
+{
+ size_t count;
+ MONITORINFOEX* info;
+};
+
+extern HWND g_hCapture;
+extern HBITMAP g_hBitmap, g_hbmMask;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen);
+
+FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture);
+FIBITMAP* CaptureMonitor(const wchar_t *pwszDevice, const RECT *cropRect = nullptr);
+
+char* GetFileNameA(const wchar_t *pwszPath);
+
+BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder);
+
+void SaveGIF(HBITMAP hBmp, const wchar_t *pwszFilename);
+void SaveTIF(HBITMAP hBmp, const wchar_t *pwszFilename);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class EventHandle
+{
+ HANDLE _hEvent;
+public:
+ inline EventHandle() { _hEvent = CreateEvent(nullptr, 0, 0, nullptr); }
+ inline ~EventHandle() { CloseHandle(_hEvent); }
+ inline void Set() { SetEvent(_hEvent); }
+ inline void Wait() { WaitForSingleObject(_hEvent, INFINITE); }
+ inline void Wait(uint32_t dwMilliseconds) { WaitForSingleObject(_hEvent, dwMilliseconds); }
+ inline operator HANDLE() { return _hEvent; }
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/ctrl_button.h b/plugins/SendScreenshotPlus/src/ctrl_button.h index 42ac4762a7..d2672c79d7 100644 --- a/plugins/SendScreenshotPlus/src/ctrl_button.h +++ b/plugins/SendScreenshotPlus/src/ctrl_button.h @@ -1,34 +1,34 @@ -/* -imported from UserinfoEx plugin for Miranda NG -Copyright: -© 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef _UINFOEX_BOTTONS_H_INCLUDED_ -#define _UINFOEX_BOTTONS_H_INCLUDED_ 1 - -void CtrlButtonLoadModule(); -void CtrlButtonUnloadModule(); - +/*
+imported from UserinfoEx plugin for Miranda NG
+Copyright:
+© 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _UINFOEX_BOTTONS_H_INCLUDED_
+#define _UINFOEX_BOTTONS_H_INCLUDED_ 1
+
+void CtrlButtonLoadModule();
+void CtrlButtonUnloadModule();
+
#endif /* _UINFOEX_BOTTONS_H_INCLUDED_ */
\ No newline at end of file diff --git a/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp b/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp index e64e686215..b0aa0bc04d 100644 --- a/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp +++ b/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp @@ -1,714 +1,714 @@ -/* -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright: -© 2012-22 Miranda NG team (https://miranda-ng.org) -© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -// SendSS compatibility: -#include "stdafx.h" -#define ghInst g_plugin.getInst() -#define myGlobals g_myGlobals -#define MODNAME MODULENAME -#define ICO_COMMON_MAIN 0xFFFF -#define ICO_DLG_DETAILS 0xFFFF - -HICON Skin_GetIcon_SendSS(unsigned short id) -{ - if(id==0xFFFF) - return GetIcon(ICO_MAIN); - return GetIconBtn(id); -} -#define IcoLib_GetIcon Skin_GetIcon_SendSS -// original UserInfoEx codebase (almost): - -typedef struct _MSGPOPUPDATA -{ - POPUPACTION pa[3]; - HWND hDialog; -} MSGPOPUPDATA, *LPMSGPOPUPDATA; - -/** - * This helper function moves and resizes a dialog box's control element. - * - * @param hDlg - the dialog box's window handle - * @param idCtrl - the identication number of the control to move - * @param dx -´number of pixels to horizontal move the control - * @param dy - number of pixels to vertical move the control - * @param dw - number of pixels to horizontal resize the control - * @param dh - number of pixels to vertical resize the control - * - * @return nothing - **/ -static FORCEINLINE void MoveCtrl(HWND hDlg, int idCtrl, int dx, int dy, int dw, int dh) -{ - RECT ws; - HWND hCtrl = GetDlgItem(hDlg, idCtrl); - GetWindowRect(hCtrl, &ws); - OffsetRect(&ws, dx, dy); - MoveWindow(hCtrl, ws.left, ws.top, ws.right - ws.left + dw, ws.bottom - ws.top + dh, FALSE); -} - -/** -* This function loads the icon to display for the current message. -* -* @param pMsgBox - pointer to a MSGBOX structure, with information about the message to display. -* -* @retval HICON - The function returns an icon to display with the message. -* @retval NULL - There is no icon for the message. -**/ -static HICON MsgLoadIcon(LPMSGBOX pMsgBox) -{ - HICON hIcon; - - // load the desired status icon - switch (pMsgBox->uType & MB_ICONMASK) { - case MB_ICON_OTHER: // custom icon defined by caller function - hIcon = pMsgBox->hiMsg; - break; - - // default windows icons - case MB_ICON_ERROR: - case MB_ICON_QUESTION: - case MB_ICON_WARNING: - case MB_ICON_INFO: - { - LPCTSTR ico[] = { nullptr, IDI_ERROR, IDI_QUESTION, IDI_WARNING, IDI_INFORMATION }; - hIcon = LoadIcon(nullptr, ico[MB_ICON_INDEX(pMsgBox->uType)]); - } - break; - - // no icon - default: - hIcon = nullptr; - } - return hIcon; -} - -/** - * This function fills a given POPUPACTION structure with the data of a given message id, - * which is normally used by the message box. This is required to let the user interact - * with a popup in the same way as with a normal message dialog box. - * - * @param pa - reference to a POPUPACTION structure to fill - * @param id - the message id - * @param result - This parameter is passed to the POPUPACTION structure as is. - * - * @return nothing - **/ -static void MakePopupAction(POPUPACTION &pa, int id) -{ - pa.cbSize = sizeof(POPUPACTION); - pa.flags = PAF_ENABLED; - pa.wParam = MAKEWORD(id, BN_CLICKED); - pa.lParam = 0; - - switch (id) { - case IDOK: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK); - mir_strcpy(pa.lpzTitle, MODNAME"/Ok"); - break; - - case IDCLOSE: - case IDCANCEL: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL); - mir_strcpy(pa.lpzTitle, MODNAME"/Cancel"); - break; - - case IDABORT: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL); - mir_strcpy(pa.lpzTitle, MODNAME"/Abort"); - break; - - case IDRETRY: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE); - mir_strcpy(pa.lpzTitle, MODNAME"/Retry"); - break; - - case IDIGNORE: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK); - mir_strcpy(pa.lpzTitle, MODNAME"/Ignore"); - break; - - case IDYES: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK); - mir_strcpy(pa.lpzTitle, MODNAME"/Yes"); - break; - - case IDNO: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL); - mir_strcpy(pa.lpzTitle, MODNAME"/No"); - break; - - case IDHELP: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL); - mir_strcpy(pa.lpzTitle, MODNAME"/Help"); - break; - - case IDALL: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK); - mir_strcpy(pa.lpzTitle, MODNAME"/All"); - break; - - case IDNONE: - pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL); - mir_strcpy(pa.lpzTitle, MODNAME"/None"); - } -} - -/** - * This is the message procedure for my nice looking message box - * - * @param hDlg - window handle - * @param uMsg - message to handle - * @param wParam - message specific parameter - * @param lParam - message specific parameter - * - * @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL - **/ -static INT_PTR CALLBACK MsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - static int retOk = IDOK; - static int retAll = IDALL; - static int retNon = IDNONE; - static int retCancel = IDCANCEL; - - switch (uMsg) { - case WM_INITDIALOG: - { - LPMSGBOX pMsgBox = (LPMSGBOX)lParam; - if (PtrIsValid(pMsgBox)) { - int icoWidth = 0; - int InfoBarHeight = 0; - HFONT hNormalFont; - - hNormalFont = (HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0); - if (pMsgBox->uType & MB_INFOBAR) { - LOGFONT lf; - - // set bold font for name in description area - GetObject(hNormalFont, sizeof(lf), &lf); - lf.lfWeight = FW_BOLD; - hNormalFont = CreateFontIndirect(&lf); - - // set infobar's textfont - SendDlgItemMessage(hDlg, TXT_NAME, WM_SETFONT, (WPARAM)hNormalFont, 0); - - // set infobar's logo icon - SendDlgItemMessage(hDlg, ICO_DLGLOGO, STM_SETIMAGE, IMAGE_ICON, - (pMsgBox->hiLogo ? (LPARAM)pMsgBox->hiLogo : (LPARAM)IcoLib_GetIcon(ICO_DLG_DETAILS))); - - // enable headerbar - ShowWindow(GetDlgItem(hDlg, TXT_NAME), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, ICO_DLGLOGO), SW_SHOW); - } - else { - RECT rc; - GetClientRect(GetDlgItem(hDlg, TXT_NAME), &rc); - InfoBarHeight = rc.bottom; - - if (pMsgBox->hiLogo) - SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)pMsgBox->hiLogo); - } - - // draw the desired status icon - HICON hIcon = MsgLoadIcon(pMsgBox); - if (hIcon) - SendDlgItemMessage(hDlg, ICO_MSGDLG, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - else { - RECT ws; - GetWindowRect(GetDlgItem(hDlg, ICO_MSGDLG), &ws); - icoWidth = ws.right - ws.left; - ShowWindow(GetDlgItem(hDlg, ICO_MSGDLG), SW_HIDE); - } - - // resize the messagebox and reorganize the buttons - if (HDC hDC = GetDC(hDlg)) { - POINT mpt = { 0, 0 }; - RECT ws = { 0, 0, 0, 0 }; - int txtWidth = 0, txtHeight = 0, needX, needY; - RECT rcDlg; - SIZE ts; - LPTSTR h, rs; - - SelectObject(hDC, hNormalFont); - // get message text width and height - if (pMsgBox->ptszMsg) for (rs = h = pMsgBox->ptszMsg;; ++h) { - if (*h == '\n' || !*h) { - GetTextExtentPoint32(hDC, rs, h - rs, &ts); - if (ts.cx > txtWidth) - txtWidth = ts.cx; - txtHeight += ts.cy; - if (!*h) - break; - rs = h + 1; - } - } - // increase width if info text requires more - if ((pMsgBox->uType&MB_INFOBAR) && pMsgBox->ptszInfoText && *pMsgBox->ptszInfoText) { - int multiline = 0; - RECT rcico; GetClientRect(GetDlgItem(hDlg, ICO_DLGLOGO), &rcico); - rcico.right = rcico.right * 100 / 66; // padding - for (rs = h = pMsgBox->ptszInfoText;; ++h) { - if (*h == '\n' || !*h) { - GetTextExtentPoint32(hDC, rs, h - rs, &ts); - ts.cx += rcico.right; - if (ts.cx > txtWidth) - txtWidth = ts.cx; - if (!*h) - break; - rs = h + 1; - ++multiline; - } - } - if (!multiline) - SetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE) | SS_CENTERIMAGE); - } - ReleaseDC(hDlg, hDC); - - // calc new dialog size - GetWindowRect(hDlg, &rcDlg); - GetWindowRect(GetDlgItem(hDlg, TXT_MESSAGE), &ws); - needX = txtWidth - (ws.right - ws.left) - icoWidth; - needY = max(0, txtHeight - (ws.bottom - ws.top) + 5); - rcDlg.left -= needX / 2; rcDlg.right += needX / 2; - rcDlg.top -= (needY - InfoBarHeight) / 2; rcDlg.bottom += (needY - InfoBarHeight) / 2; - - // resize dialog window - MoveWindow(hDlg, rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, FALSE); - ClientToScreen(hDlg, &mpt); - - MoveCtrl(hDlg, STATIC_WHITERECT, -mpt.x, -mpt.y, needX, needY - InfoBarHeight); - MoveCtrl(hDlg, TXT_NAME, -mpt.x, -mpt.y, needX, 0); - MoveCtrl(hDlg, ICO_DLGLOGO, -mpt.x + needX, -mpt.y, 0, 0); - MoveCtrl(hDlg, ICO_MSGDLG, -mpt.x, -mpt.y - InfoBarHeight, 0, 0); - MoveCtrl(hDlg, TXT_MESSAGE, -mpt.x - icoWidth, -mpt.y - InfoBarHeight, needX, needY); - MoveCtrl(hDlg, STATIC_LINE2, -mpt.x, -mpt.y + needY - InfoBarHeight, needX, 0); - - // Do pushbutton positioning - RECT rcOk, rcAll, rcNone, rcCancel; - - // get button rectangles - GetWindowRect(GetDlgItem(hDlg, IDOK), &rcOk); - OffsetRect(&rcOk, -mpt.x, -mpt.y + needY - InfoBarHeight); - - GetWindowRect(GetDlgItem(hDlg, IDALL), &rcAll); - OffsetRect(&rcAll, -mpt.x, -mpt.y + needY - InfoBarHeight); - - GetWindowRect(GetDlgItem(hDlg, IDNONE), &rcNone); - OffsetRect(&rcNone, -mpt.x, -mpt.y + needY - InfoBarHeight); - - GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel); - OffsetRect(&rcCancel, -mpt.x, -mpt.y + needY - InfoBarHeight); - - LONG okWidth = rcOk.right - rcOk.left; - LONG allWidth = rcAll.right - rcAll.left; - LONG noneWidth = rcNone.right - rcNone.left; - LONG caWidth = rcCancel.right - rcCancel.left; - LONG dlgMid = (rcDlg.right - rcDlg.left) / 2; - - // load button configuration - switch (MB_TYPE(pMsgBox->uType)) { - case MB_OK: - rcOk.left = dlgMid - (okWidth / 2); - rcOk.right = rcOk.left + okWidth; - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - break; - - case MB_OKCANCEL: - retOk = IDRETRY; - SetDlgItemText(hDlg, IDOK, LPGENW("OK")); - retCancel = IDCANCEL; - SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel")); - rcOk.left = dlgMid - okWidth - 10; - rcOk.right = rcOk.left + okWidth; - rcCancel.left = dlgMid + 10; - rcCancel.right = rcCancel.left + caWidth; - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW); - break; - - case MB_RETRYCANCEL: - retOk = IDRETRY; - SetDlgItemText(hDlg, IDOK, LPGENW("Retry")); - retCancel = IDCANCEL; - SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel")); - rcOk.left = dlgMid - okWidth - 10; - rcOk.right = rcOk.left + okWidth; - rcCancel.left = dlgMid + 10; - rcCancel.right = rcCancel.left + caWidth; - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW); - break; - - case MB_YESNO: - retOk = IDYES; - SetDlgItemText(hDlg, IDOK, LPGENW("Yes")); - retCancel = IDNO; - SetDlgItemText(hDlg, IDCANCEL, LPGENW("No")); - rcOk.left = dlgMid - okWidth - 10; - rcOk.right = rcOk.left + okWidth; - rcCancel.left = dlgMid + 10; - rcCancel.right = rcCancel.left + caWidth; - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW); - break; - - case MB_ABORTRETRYIGNORE: - retOk = IDABORT; - SetDlgItemText(hDlg, IDOK, LPGENW("Abort")); - retAll = IDABORT; - SetDlgItemText(hDlg, IDALL, LPGENW("Retry")); - retCancel = IDCANCEL; - SetDlgItemText(hDlg, IDCANCEL, LPGENW("Ignore")); - rcAll.left = dlgMid - (allWidth / 2); - rcAll.right = rcAll.left + allWidth; - rcOk.left = rcAll.left - okWidth - 5; - rcOk.right = rcOk.left + okWidth; - rcCancel.left = rcAll.right + 5; - rcCancel.right = rcCancel.left + caWidth; - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW); - break; - - case MB_YESNOCANCEL: - retOk = IDYES; - SetDlgItemText(hDlg, IDOK, LPGENW("Yes")); - retAll = IDNO; - SetDlgItemText(hDlg, IDALL, LPGENW("No")); - retCancel = IDCANCEL; - SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel")); - rcAll.left = dlgMid - (allWidth / 2); - rcAll.right = rcAll.left + allWidth; - rcOk.left = rcAll.left - okWidth - 5; - rcOk.right = rcOk.left + okWidth; - rcCancel.left = rcAll.right + 5; - rcCancel.right = rcCancel.left + caWidth; - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW); - break; - - case MB_YESALLNO: - retOk = IDYES; - SetDlgItemText(hDlg, IDOK, LPGENW("Yes")); - retAll = IDALL; - SetDlgItemText(hDlg, IDALL, LPGENW("All")); - SetDlgItemText(hDlg, IDNONE, LPGENW("None")); - retCancel = IDNO; - SetDlgItemText(hDlg, IDCANCEL, LPGENW("No")); - rcCancel.right = rcDlg.right - rcDlg.left - 10; - rcCancel.left = rcCancel.right - caWidth; - rcNone.right = rcCancel.left - 5; - rcNone.left = rcNone.right - noneWidth; - rcAll.right = rcNone.left - 5; - rcAll.left = rcAll.right - allWidth; - rcOk.right = rcAll.left - 5; - rcOk.left = rcOk.right - okWidth; - // show buttons - ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDNONE), SW_SHOW); - ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW); - break; - - default: - rcOk.left = dlgMid - (okWidth / 2); - rcOk.right = rcOk.left + okWidth; - } - - MoveWindow(GetDlgItem(hDlg, IDOK), rcOk.left, rcOk.top, rcOk.right - rcOk.left, rcOk.bottom - rcOk.top, FALSE); - MoveWindow(GetDlgItem(hDlg, IDALL), rcAll.left, rcAll.top, rcAll.right - rcAll.left, rcAll.bottom - rcAll.top, FALSE); - MoveWindow(GetDlgItem(hDlg, IDNONE), rcNone.left, rcNone.top, rcNone.right - rcNone.left, rcNone.bottom - rcNone.top, FALSE); - MoveWindow(GetDlgItem(hDlg, IDCANCEL), rcCancel.left, rcCancel.top, rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top, FALSE); - } - - // set text's - SetWindowText(hDlg, pMsgBox->ptszTitle); - SetDlgItemText(hDlg, TXT_NAME, pMsgBox->ptszInfoText); - SetDlgItemText(hDlg, TXT_MESSAGE, pMsgBox->ptszMsg); - - TranslateDialogDefault(hDlg); - return TRUE; - } - } - break; - - case WM_CTLCOLORSTATIC: - switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) { - case STATIC_WHITERECT: - case ICO_DLGLOGO: - case ICO_MSGDLG: - case TXT_MESSAGE: - case TXT_NAME: - SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT)); - return GetSysColor(COLOR_WINDOW); - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - EndDialog(hDlg, retOk); - break; - case IDCANCEL: - EndDialog(hDlg, retCancel); - break; - case IDALL: - EndDialog(hDlg, retAll); - break; - case IDNONE: - EndDialog(hDlg, retNon); - } - break; - - case WM_DESTROY: - DeleteObject((HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0)); - break; - } - return FALSE; -} - -/** -* Dummi modal MsgBox for popup, -* this set call function in wait stait and do not freece miranda main thread -* the window is outside the desktop -*/ -static INT_PTR CALLBACK MsgBoxPop(HWND hDlg, UINT uMsg, WPARAM, LPARAM lParam) -{ - switch (uMsg) { - case WM_INITDIALOG: - LPMSGBOX pMsgBox = (LPMSGBOX)lParam; - - MoveWindow(hDlg, -10, -10, 0, 0, FALSE); - LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)mir_alloc(sizeof(MSGPOPUPDATA)); - if (pmpd) { - POPUPDATAW ppd; - ppd.lchContact = NULL; // (HANDLE)wParam; - // icon - ppd.lchIcon = MsgLoadIcon(pMsgBox); - mir_wstrncpy(ppd.lpwzContactName, pMsgBox->ptszTitle, _countof(ppd.lpwzContactName)); - mir_wstrncpy(ppd.lpwzText, pMsgBox->ptszMsg, _countof(ppd.lpwzText)); - - // CALLBAC Proc - ppd.PluginWindowProc = PopupProc; - ppd.PluginData = pmpd; - ppd.iSeconds = -1; - ppd.lpActions = pmpd->pa; - - // set color of popup - switch (pMsgBox->uType & MB_ICONMASK) { - case MB_ICON_ERROR: - ppd.colorBack = RGB(200, 10, 0); - ppd.colorText = RGB(255, 255, 255); - break; - - case MB_ICON_WARNING: - ppd.colorBack = RGB(200, 100, 0); - ppd.colorText = RGB(255, 255, 255); - break; - - default: - if (pMsgBox->uType & MB_CUSTOMCOLOR) { - ppd.colorBack = pMsgBox->colorBack; - ppd.colorText = pMsgBox->colorText; - } - } - - // handle for MakePopupAction - pmpd->hDialog = hDlg; - - // active buttons - switch (MB_TYPE(pMsgBox->uType)) { - case MB_OK: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK); - break; - - case MB_OKCANCEL: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL); - break; - - case MB_RETRYCANCEL: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL); - break; - - case MB_YESNO: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO); - break; - - case MB_ABORTRETRYIGNORE: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDABORT); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDIGNORE); - break; - - case MB_YESNOCANCEL: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL); - break; - - case MB_YESALLNO: - MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDALL); - MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO); - break; - } - - // create popup - PUAddPopupW(&ppd); - if (MB_TYPE(pMsgBox->uType) == MB_OK) - EndDialog(hDlg, IDOK); - } - break; - } - return FALSE; -} - -/** -* This is the message procedure for popup -* -* @param hDlg - window handle -* @param uMsg - message to handle -* @param wParam - message specific parameter -* @param lParam - message specific parameter -* -* @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL -**/ - -LRESULT CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case UM_POPUPACTION: - if (HIWORD(wParam) == BN_CLICKED) { - LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg); - if (pmpd) { - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - case IDABORT: - case IDRETRY: - case IDIGNORE: - case IDYES: - case IDNO: - case IDALL: - case IDNONE: - if (IsWindow(pmpd->hDialog)) - EndDialog(pmpd->hDialog, LOWORD(wParam)); - break; - - default: - if (IsWindow(pmpd->hDialog)) - EndDialog(pmpd->hDialog, IDCANCEL); - } - } - PUDeletePopup(hDlg); - } - break; - - case UM_FREEPLUGINDATA: - LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg); - if (pmpd > 0) - MIR_FREE(pmpd); - return TRUE; - } - return DefWindowProc(hDlg, uMsg, wParam, lParam); -} - -/** -* This is the service function for external plugins to use the nice messagebox -* -* @param wParam - MCONTACT hContact which can display an avatar for popups -* @param lParam - MSGBOX structure holding parameters -* -* @return The function returns the ID of the clicked button (IDOK, IDCANCEL, ...) -* or -1 on error. -**/ -INT_PTR MsgBoxService(WPARAM, LPARAM lParam) -{ - LPMSGBOX pMsgBox = (LPMSGBOX)lParam; - - // check input - if (PtrIsValid(pMsgBox) && pMsgBox->cbSize == sizeof(MSGBOX)) { - // Shall the MessageBox displayed as popup? - if (!(pMsgBox->uType & (MB_INFOBAR | MB_NOPOPUP)) // message box can be a popup? - && myGlobals.PopupActionsExist == 1 // popup support ext stuct? - && (db_get_dw(0, "Popup", "Actions", 0) & 1) // popup++ actions on? - && db_get_b(0, MODNAME, SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX)) // user likes popups? - return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOXDUMMI), pMsgBox->hParent, MsgBoxPop, lParam); - - return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOX), pMsgBox->hParent, MsgBoxProc, lParam); - } - return -1; -} - -/** -* name: MsgBox -* desc: calls a messagebox -* param: -**/ -INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPCTSTR pszTitle, LPCTSTR pszInfo, LPCTSTR pszFormat, ...) -{ - wchar_t tszMsg[MAX_SECONDLINE]; - - va_list vl; - va_start(vl, pszFormat); - mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl); - va_end(vl); - - MSGBOX mb = { 0 }; - mb.cbSize = sizeof(MSGBOX); - mb.hParent = hParent; - mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN); - mb.hiMsg = nullptr; - mb.ptszTitle = TranslateW(pszTitle); - mb.ptszInfoText = TranslateW(pszInfo); - mb.ptszMsg = tszMsg; - mb.uType = uType; - return MsgBoxService(NULL, (LPARAM)&mb); -} - -/** -* name: MsgErr -* desc: calls a messagebox -* param: -**/ -INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...) -{ - wchar_t tszTitle[MAX_SECONDLINE], tszMsg[MAX_SECONDLINE]; - mir_snwprintf(tszTitle, L"%s - %s", _A2W(MODNAME), TranslateT("Error")); - - va_list vl; - va_start(vl, pszFormat); - mir_vsnwprintf(tszMsg, TranslateW(pszFormat), vl); - va_end(vl); - - MSGBOX mb = { 0 }; - mb.cbSize = sizeof(MSGBOX); - mb.hParent = hParent; - mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN); - mb.hiMsg = nullptr; - mb.ptszTitle = tszTitle; - mb.ptszMsg = tszMsg; - mb.uType = MB_OK | MB_ICON_ERROR; - return MsgBoxService(NULL, (LPARAM)&mb); -} +/*
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright:
+© 2012-23 Miranda NG team (https://miranda-ng.org)
+© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// SendSS compatibility:
+#include "stdafx.h"
+#define ghInst g_plugin.getInst()
+#define myGlobals g_myGlobals
+#define MODNAME MODULENAME
+#define ICO_COMMON_MAIN 0xFFFF
+#define ICO_DLG_DETAILS 0xFFFF
+
+HICON Skin_GetIcon_SendSS(unsigned short id)
+{
+ if(id==0xFFFF)
+ return GetIcon(ICO_MAIN);
+ return GetIconBtn(id);
+}
+#define IcoLib_GetIcon Skin_GetIcon_SendSS
+// original UserInfoEx codebase (almost):
+
+typedef struct _MSGPOPUPDATA
+{
+ POPUPACTION pa[3];
+ HWND hDialog;
+} MSGPOPUPDATA, *LPMSGPOPUPDATA;
+
+/**
+ * This helper function moves and resizes a dialog box's control element.
+ *
+ * @param hDlg - the dialog box's window handle
+ * @param idCtrl - the identication number of the control to move
+ * @param dx -´number of pixels to horizontal move the control
+ * @param dy - number of pixels to vertical move the control
+ * @param dw - number of pixels to horizontal resize the control
+ * @param dh - number of pixels to vertical resize the control
+ *
+ * @return nothing
+ **/
+static FORCEINLINE void MoveCtrl(HWND hDlg, int idCtrl, int dx, int dy, int dw, int dh)
+{
+ RECT ws;
+ HWND hCtrl = GetDlgItem(hDlg, idCtrl);
+ GetWindowRect(hCtrl, &ws);
+ OffsetRect(&ws, dx, dy);
+ MoveWindow(hCtrl, ws.left, ws.top, ws.right - ws.left + dw, ws.bottom - ws.top + dh, FALSE);
+}
+
+/**
+* This function loads the icon to display for the current message.
+*
+* @param pMsgBox - pointer to a MSGBOX structure, with information about the message to display.
+*
+* @retval HICON - The function returns an icon to display with the message.
+* @retval NULL - There is no icon for the message.
+**/
+static HICON MsgLoadIcon(LPMSGBOX pMsgBox)
+{
+ HICON hIcon;
+
+ // load the desired status icon
+ switch (pMsgBox->uType & MB_ICONMASK) {
+ case MB_ICON_OTHER: // custom icon defined by caller function
+ hIcon = pMsgBox->hiMsg;
+ break;
+
+ // default windows icons
+ case MB_ICON_ERROR:
+ case MB_ICON_QUESTION:
+ case MB_ICON_WARNING:
+ case MB_ICON_INFO:
+ {
+ LPCTSTR ico[] = { nullptr, IDI_ERROR, IDI_QUESTION, IDI_WARNING, IDI_INFORMATION };
+ hIcon = LoadIcon(nullptr, ico[MB_ICON_INDEX(pMsgBox->uType)]);
+ }
+ break;
+
+ // no icon
+ default:
+ hIcon = nullptr;
+ }
+ return hIcon;
+}
+
+/**
+ * This function fills a given POPUPACTION structure with the data of a given message id,
+ * which is normally used by the message box. This is required to let the user interact
+ * with a popup in the same way as with a normal message dialog box.
+ *
+ * @param pa - reference to a POPUPACTION structure to fill
+ * @param id - the message id
+ * @param result - This parameter is passed to the POPUPACTION structure as is.
+ *
+ * @return nothing
+ **/
+static void MakePopupAction(POPUPACTION &pa, int id)
+{
+ pa.cbSize = sizeof(POPUPACTION);
+ pa.flags = PAF_ENABLED;
+ pa.wParam = MAKEWORD(id, BN_CLICKED);
+ pa.lParam = 0;
+
+ switch (id) {
+ case IDOK:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Ok");
+ break;
+
+ case IDCLOSE:
+ case IDCANCEL:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Cancel");
+ break;
+
+ case IDABORT:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Abort");
+ break;
+
+ case IDRETRY:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Retry");
+ break;
+
+ case IDIGNORE:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Ignore");
+ break;
+
+ case IDYES:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Yes");
+ break;
+
+ case IDNO:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/No");
+ break;
+
+ case IDHELP:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Help");
+ break;
+
+ case IDALL:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/All");
+ break;
+
+ case IDNONE:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/None");
+ }
+}
+
+/**
+ * This is the message procedure for my nice looking message box
+ *
+ * @param hDlg - window handle
+ * @param uMsg - message to handle
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+ **/
+static INT_PTR CALLBACK MsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static int retOk = IDOK;
+ static int retAll = IDALL;
+ static int retNon = IDNONE;
+ static int retCancel = IDCANCEL;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+ if (PtrIsValid(pMsgBox)) {
+ int icoWidth = 0;
+ int InfoBarHeight = 0;
+ HFONT hNormalFont;
+
+ hNormalFont = (HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0);
+ if (pMsgBox->uType & MB_INFOBAR) {
+ LOGFONT lf;
+
+ // set bold font for name in description area
+ GetObject(hNormalFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hNormalFont = CreateFontIndirect(&lf);
+
+ // set infobar's textfont
+ SendDlgItemMessage(hDlg, TXT_NAME, WM_SETFONT, (WPARAM)hNormalFont, 0);
+
+ // set infobar's logo icon
+ SendDlgItemMessage(hDlg, ICO_DLGLOGO, STM_SETIMAGE, IMAGE_ICON,
+ (pMsgBox->hiLogo ? (LPARAM)pMsgBox->hiLogo : (LPARAM)IcoLib_GetIcon(ICO_DLG_DETAILS)));
+
+ // enable headerbar
+ ShowWindow(GetDlgItem(hDlg, TXT_NAME), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, ICO_DLGLOGO), SW_SHOW);
+ }
+ else {
+ RECT rc;
+ GetClientRect(GetDlgItem(hDlg, TXT_NAME), &rc);
+ InfoBarHeight = rc.bottom;
+
+ if (pMsgBox->hiLogo)
+ SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)pMsgBox->hiLogo);
+ }
+
+ // draw the desired status icon
+ HICON hIcon = MsgLoadIcon(pMsgBox);
+ if (hIcon)
+ SendDlgItemMessage(hDlg, ICO_MSGDLG, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ else {
+ RECT ws;
+ GetWindowRect(GetDlgItem(hDlg, ICO_MSGDLG), &ws);
+ icoWidth = ws.right - ws.left;
+ ShowWindow(GetDlgItem(hDlg, ICO_MSGDLG), SW_HIDE);
+ }
+
+ // resize the messagebox and reorganize the buttons
+ if (HDC hDC = GetDC(hDlg)) {
+ POINT mpt = { 0, 0 };
+ RECT ws = { 0, 0, 0, 0 };
+ int txtWidth = 0, txtHeight = 0, needX, needY;
+ RECT rcDlg;
+ SIZE ts;
+ LPTSTR h, rs;
+
+ SelectObject(hDC, hNormalFont);
+ // get message text width and height
+ if (pMsgBox->ptszMsg) for (rs = h = pMsgBox->ptszMsg;; ++h) {
+ if (*h == '\n' || !*h) {
+ GetTextExtentPoint32(hDC, rs, h - rs, &ts);
+ if (ts.cx > txtWidth)
+ txtWidth = ts.cx;
+ txtHeight += ts.cy;
+ if (!*h)
+ break;
+ rs = h + 1;
+ }
+ }
+ // increase width if info text requires more
+ if ((pMsgBox->uType&MB_INFOBAR) && pMsgBox->ptszInfoText && *pMsgBox->ptszInfoText) {
+ int multiline = 0;
+ RECT rcico; GetClientRect(GetDlgItem(hDlg, ICO_DLGLOGO), &rcico);
+ rcico.right = rcico.right * 100 / 66; // padding
+ for (rs = h = pMsgBox->ptszInfoText;; ++h) {
+ if (*h == '\n' || !*h) {
+ GetTextExtentPoint32(hDC, rs, h - rs, &ts);
+ ts.cx += rcico.right;
+ if (ts.cx > txtWidth)
+ txtWidth = ts.cx;
+ if (!*h)
+ break;
+ rs = h + 1;
+ ++multiline;
+ }
+ }
+ if (!multiline)
+ SetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE) | SS_CENTERIMAGE);
+ }
+ ReleaseDC(hDlg, hDC);
+
+ // calc new dialog size
+ GetWindowRect(hDlg, &rcDlg);
+ GetWindowRect(GetDlgItem(hDlg, TXT_MESSAGE), &ws);
+ needX = txtWidth - (ws.right - ws.left) - icoWidth;
+ needY = max(0, txtHeight - (ws.bottom - ws.top) + 5);
+ rcDlg.left -= needX / 2; rcDlg.right += needX / 2;
+ rcDlg.top -= (needY - InfoBarHeight) / 2; rcDlg.bottom += (needY - InfoBarHeight) / 2;
+
+ // resize dialog window
+ MoveWindow(hDlg, rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, FALSE);
+ ClientToScreen(hDlg, &mpt);
+
+ MoveCtrl(hDlg, STATIC_WHITERECT, -mpt.x, -mpt.y, needX, needY - InfoBarHeight);
+ MoveCtrl(hDlg, TXT_NAME, -mpt.x, -mpt.y, needX, 0);
+ MoveCtrl(hDlg, ICO_DLGLOGO, -mpt.x + needX, -mpt.y, 0, 0);
+ MoveCtrl(hDlg, ICO_MSGDLG, -mpt.x, -mpt.y - InfoBarHeight, 0, 0);
+ MoveCtrl(hDlg, TXT_MESSAGE, -mpt.x - icoWidth, -mpt.y - InfoBarHeight, needX, needY);
+ MoveCtrl(hDlg, STATIC_LINE2, -mpt.x, -mpt.y + needY - InfoBarHeight, needX, 0);
+
+ // Do pushbutton positioning
+ RECT rcOk, rcAll, rcNone, rcCancel;
+
+ // get button rectangles
+ GetWindowRect(GetDlgItem(hDlg, IDOK), &rcOk);
+ OffsetRect(&rcOk, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDALL), &rcAll);
+ OffsetRect(&rcAll, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDNONE), &rcNone);
+ OffsetRect(&rcNone, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel);
+ OffsetRect(&rcCancel, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ LONG okWidth = rcOk.right - rcOk.left;
+ LONG allWidth = rcAll.right - rcAll.left;
+ LONG noneWidth = rcNone.right - rcNone.left;
+ LONG caWidth = rcCancel.right - rcCancel.left;
+ LONG dlgMid = (rcDlg.right - rcDlg.left) / 2;
+
+ // load button configuration
+ switch (MB_TYPE(pMsgBox->uType)) {
+ case MB_OK:
+ rcOk.left = dlgMid - (okWidth / 2);
+ rcOk.right = rcOk.left + okWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ break;
+
+ case MB_OKCANCEL:
+ retOk = IDRETRY;
+ SetDlgItemText(hDlg, IDOK, LPGENW("OK"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_RETRYCANCEL:
+ retOk = IDRETRY;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Retry"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_YESNO:
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
+ retCancel = IDNO;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("No"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ retOk = IDABORT;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Abort"));
+ retAll = IDABORT;
+ SetDlgItemText(hDlg, IDALL, LPGENW("Retry"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Ignore"));
+ rcAll.left = dlgMid - (allWidth / 2);
+ rcAll.right = rcAll.left + allWidth;
+ rcOk.left = rcAll.left - okWidth - 5;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = rcAll.right + 5;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_YESNOCANCEL:
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
+ retAll = IDNO;
+ SetDlgItemText(hDlg, IDALL, LPGENW("No"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
+ rcAll.left = dlgMid - (allWidth / 2);
+ rcAll.right = rcAll.left + allWidth;
+ rcOk.left = rcAll.left - okWidth - 5;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = rcAll.right + 5;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_YESALLNO:
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
+ retAll = IDALL;
+ SetDlgItemText(hDlg, IDALL, LPGENW("All"));
+ SetDlgItemText(hDlg, IDNONE, LPGENW("None"));
+ retCancel = IDNO;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("No"));
+ rcCancel.right = rcDlg.right - rcDlg.left - 10;
+ rcCancel.left = rcCancel.right - caWidth;
+ rcNone.right = rcCancel.left - 5;
+ rcNone.left = rcNone.right - noneWidth;
+ rcAll.right = rcNone.left - 5;
+ rcAll.left = rcAll.right - allWidth;
+ rcOk.right = rcAll.left - 5;
+ rcOk.left = rcOk.right - okWidth;
+ // show buttons
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDNONE), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ default:
+ rcOk.left = dlgMid - (okWidth / 2);
+ rcOk.right = rcOk.left + okWidth;
+ }
+
+ MoveWindow(GetDlgItem(hDlg, IDOK), rcOk.left, rcOk.top, rcOk.right - rcOk.left, rcOk.bottom - rcOk.top, FALSE);
+ MoveWindow(GetDlgItem(hDlg, IDALL), rcAll.left, rcAll.top, rcAll.right - rcAll.left, rcAll.bottom - rcAll.top, FALSE);
+ MoveWindow(GetDlgItem(hDlg, IDNONE), rcNone.left, rcNone.top, rcNone.right - rcNone.left, rcNone.bottom - rcNone.top, FALSE);
+ MoveWindow(GetDlgItem(hDlg, IDCANCEL), rcCancel.left, rcCancel.top, rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top, FALSE);
+ }
+
+ // set text's
+ SetWindowText(hDlg, pMsgBox->ptszTitle);
+ SetDlgItemText(hDlg, TXT_NAME, pMsgBox->ptszInfoText);
+ SetDlgItemText(hDlg, TXT_MESSAGE, pMsgBox->ptszMsg);
+
+ TranslateDialogDefault(hDlg);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_CTLCOLORSTATIC:
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
+ case STATIC_WHITERECT:
+ case ICO_DLGLOGO:
+ case ICO_MSGDLG:
+ case TXT_MESSAGE:
+ case TXT_NAME:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return GetSysColor(COLOR_WINDOW);
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ EndDialog(hDlg, retOk);
+ break;
+ case IDCANCEL:
+ EndDialog(hDlg, retCancel);
+ break;
+ case IDALL:
+ EndDialog(hDlg, retAll);
+ break;
+ case IDNONE:
+ EndDialog(hDlg, retNon);
+ }
+ break;
+
+ case WM_DESTROY:
+ DeleteObject((HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0));
+ break;
+ }
+ return FALSE;
+}
+
+/**
+* Dummi modal MsgBox for popup,
+* this set call function in wait stait and do not freece miranda main thread
+* the window is outside the desktop
+*/
+static INT_PTR CALLBACK MsgBoxPop(HWND hDlg, UINT uMsg, WPARAM, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ MoveWindow(hDlg, -10, -10, 0, 0, FALSE);
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)mir_alloc(sizeof(MSGPOPUPDATA));
+ if (pmpd) {
+ POPUPDATAW ppd;
+ ppd.lchContact = NULL; // (HANDLE)wParam;
+ // icon
+ ppd.lchIcon = MsgLoadIcon(pMsgBox);
+ mir_wstrncpy(ppd.lpwzContactName, pMsgBox->ptszTitle, _countof(ppd.lpwzContactName));
+ mir_wstrncpy(ppd.lpwzText, pMsgBox->ptszMsg, _countof(ppd.lpwzText));
+
+ // CALLBAC Proc
+ ppd.PluginWindowProc = PopupProc;
+ ppd.PluginData = pmpd;
+ ppd.iSeconds = -1;
+ ppd.lpActions = pmpd->pa;
+
+ // set color of popup
+ switch (pMsgBox->uType & MB_ICONMASK) {
+ case MB_ICON_ERROR:
+ ppd.colorBack = RGB(200, 10, 0);
+ ppd.colorText = RGB(255, 255, 255);
+ break;
+
+ case MB_ICON_WARNING:
+ ppd.colorBack = RGB(200, 100, 0);
+ ppd.colorText = RGB(255, 255, 255);
+ break;
+
+ default:
+ if (pMsgBox->uType & MB_CUSTOMCOLOR) {
+ ppd.colorBack = pMsgBox->colorBack;
+ ppd.colorText = pMsgBox->colorText;
+ }
+ }
+
+ // handle for MakePopupAction
+ pmpd->hDialog = hDlg;
+
+ // active buttons
+ switch (MB_TYPE(pMsgBox->uType)) {
+ case MB_OK:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK);
+ break;
+
+ case MB_OKCANCEL:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
+ break;
+
+ case MB_RETRYCANCEL:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
+ break;
+
+ case MB_YESNO:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDABORT);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDIGNORE);
+ break;
+
+ case MB_YESNOCANCEL:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
+ break;
+
+ case MB_YESALLNO:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDALL);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
+ break;
+ }
+
+ // create popup
+ PUAddPopupW(&ppd);
+ if (MB_TYPE(pMsgBox->uType) == MB_OK)
+ EndDialog(hDlg, IDOK);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**
+* This is the message procedure for popup
+*
+* @param hDlg - window handle
+* @param uMsg - message to handle
+* @param wParam - message specific parameter
+* @param lParam - message specific parameter
+*
+* @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+**/
+
+LRESULT CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case UM_POPUPACTION:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+ if (pmpd) {
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ case IDABORT:
+ case IDRETRY:
+ case IDIGNORE:
+ case IDYES:
+ case IDNO:
+ case IDALL:
+ case IDNONE:
+ if (IsWindow(pmpd->hDialog))
+ EndDialog(pmpd->hDialog, LOWORD(wParam));
+ break;
+
+ default:
+ if (IsWindow(pmpd->hDialog))
+ EndDialog(pmpd->hDialog, IDCANCEL);
+ }
+ }
+ PUDeletePopup(hDlg);
+ }
+ break;
+
+ case UM_FREEPLUGINDATA:
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+ if (pmpd > 0)
+ MIR_FREE(pmpd);
+ return TRUE;
+ }
+ return DefWindowProc(hDlg, uMsg, wParam, lParam);
+}
+
+/**
+* This is the service function for external plugins to use the nice messagebox
+*
+* @param wParam - MCONTACT hContact which can display an avatar for popups
+* @param lParam - MSGBOX structure holding parameters
+*
+* @return The function returns the ID of the clicked button (IDOK, IDCANCEL, ...)
+* or -1 on error.
+**/
+INT_PTR MsgBoxService(WPARAM, LPARAM lParam)
+{
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ // check input
+ if (PtrIsValid(pMsgBox) && pMsgBox->cbSize == sizeof(MSGBOX)) {
+ // Shall the MessageBox displayed as popup?
+ if (!(pMsgBox->uType & (MB_INFOBAR | MB_NOPOPUP)) // message box can be a popup?
+ && myGlobals.PopupActionsExist == 1 // popup support ext stuct?
+ && (db_get_dw(0, "Popup", "Actions", 0) & 1) // popup++ actions on?
+ && db_get_b(0, MODNAME, SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX)) // user likes popups?
+ return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOXDUMMI), pMsgBox->hParent, MsgBoxPop, lParam);
+
+ return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOX), pMsgBox->hParent, MsgBoxProc, lParam);
+ }
+ return -1;
+}
+
+/**
+* name: MsgBox
+* desc: calls a messagebox
+* param:
+**/
+INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPCTSTR pszTitle, LPCTSTR pszInfo, LPCTSTR pszFormat, ...)
+{
+ wchar_t tszMsg[MAX_SECONDLINE];
+
+ va_list vl;
+ va_start(vl, pszFormat);
+ mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl);
+ va_end(vl);
+
+ MSGBOX mb = { 0 };
+ mb.cbSize = sizeof(MSGBOX);
+ mb.hParent = hParent;
+ mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN);
+ mb.hiMsg = nullptr;
+ mb.ptszTitle = TranslateW(pszTitle);
+ mb.ptszInfoText = TranslateW(pszInfo);
+ mb.ptszMsg = tszMsg;
+ mb.uType = uType;
+ return MsgBoxService(NULL, (LPARAM)&mb);
+}
+
+/**
+* name: MsgErr
+* desc: calls a messagebox
+* param:
+**/
+INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...)
+{
+ wchar_t tszTitle[MAX_SECONDLINE], tszMsg[MAX_SECONDLINE];
+ mir_snwprintf(tszTitle, L"%s - %s", _A2W(MODNAME), TranslateT("Error"));
+
+ va_list vl;
+ va_start(vl, pszFormat);
+ mir_vsnwprintf(tszMsg, TranslateW(pszFormat), vl);
+ va_end(vl);
+
+ MSGBOX mb = { 0 };
+ mb.cbSize = sizeof(MSGBOX);
+ mb.hParent = hParent;
+ mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN);
+ mb.hiMsg = nullptr;
+ mb.ptszTitle = tszTitle;
+ mb.ptszMsg = tszMsg;
+ mb.uType = MB_OK | MB_ICON_ERROR;
+ return MsgBoxService(NULL, (LPARAM)&mb);
+}
diff --git a/plugins/SendScreenshotPlus/src/dlg_msgbox.h b/plugins/SendScreenshotPlus/src/dlg_msgbox.h index 68f40ea6b3..d1b31e6538 100644 --- a/plugins/SendScreenshotPlus/src/dlg_msgbox.h +++ b/plugins/SendScreenshotPlus/src/dlg_msgbox.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
+© 2012-23 Miranda NG team (https://miranda-ng.org)
© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
This program is free software; you can redistribute it and/or
diff --git a/plugins/SendScreenshotPlus/src/stdafx.cxx b/plugins/SendScreenshotPlus/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SendScreenshotPlus/src/stdafx.cxx +++ b/plugins/SendScreenshotPlus/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SendScreenshotPlus/src/stdafx.h b/plugins/SendScreenshotPlus/src/stdafx.h index bdf13cef1f..ecad2333c8 100644 --- a/plugins/SendScreenshotPlus/src/stdafx.h +++ b/plugins/SendScreenshotPlus/src/stdafx.h @@ -1,186 +1,186 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-09 Miranda ICQ/IM project, - -This file is part of Send Screenshot Plus, a Miranda IM plugin. -Copyright (c) 2010 Ing.U.Horn - -Parts of this file based on original sorce code -(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ -#ifndef _GLOBAL_H_ -#define _GLOBAL_H_ - -#define OEMRESOURCE - -#include <windows.h> -#include <Windowsx.h> -#include <commctrl.h> -#include <time.h> -#include <Shlwapi.h> -#include <gdiplus.h> -#include <mapi.h> -#include <UxTheme.h> - -#include <map> -#include <string> -using namespace std; - -#include <msapi/vsstyle.h> -#include <msapi/vssym32.h> - -#include <newpluginapi.h> -#include <m_button.h> -#include <m_chat_int.h> -#include <m_clist.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_hotkeys.h> -#include <m_imgsrvc.h> -#include <m_langpack.h> -#include <m_netlib.h> -#include <m_protosvc.h> -#include <m_skin.h> -#include <m_json.h> -#include <m_popup.h> -#include <m_icolib.h> -#include <m_message.h> - -#include <m_folders.h> -#include <m_HTTPServer.h> -#include <m_ftpfile.h> -#include <m_sendss.h> -#include <m_userinfoex.h> -#include <m_cloudfile.h> - -#include "ctrl_button.h" -#include "dlg_msgbox.h" -#include "resource.h" -#include "version.h" -#include "CSend.h" -#include "CSendEmail.h" -#include "CSendFile.h" -#include "CSendFTPFile.h" -#include "CSendHTTPServer.h" -#include "CSendCloduFile.h" -#include "CSendHost_ImageShack.h" -#include "CSendHost_uploadpie.h" -#include "CSendHost_imgur.h" -#include "DevKey.h" -#include "UMainForm.h" -#include "Utils.h" - -#define UM_CLOSING WM_USER+1 -#define UM_EVENT WM_USER+2 - -// Generic Message Box for Errors -#define MSGERROR(text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONERROR) -#define MSGINFO (text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONINFORMATION) - -typedef struct _MGLOBAL { - uint32_t mirandaVersion; // mirandaVersion - BOOLEAN PopupActionsExist : 1; // Popup+ or MS_POPUP_REGISTERACTIONS exist - BOOLEAN PluginHTTPExist : 1; // HTTPServer or MS_HTTP_ACCEPT_CONNECTIONS exist - BOOLEAN PluginFTPExist : 1; // FTPFile or MS_FTPFILE_UPLOAD exist - BOOLEAN PluginCloudFileExist: 1; // CloudFile or MS_CLOUDFILE_UPLOAD exists - -} MGLOBAL, *LPMGLOBAL; - -///////////////////////////////////////////////////////////////////////////////////////// - -#define ERROR_TITLE TranslateT("SendScreenshot - Error") - -// Miranda Database Key -#define MODULENAME "SendSS" - -struct CMPlugin : public PLUGIN<CMPlugin> -{ - CMPlugin(); - - int Load() override; - int Unload() override; -}; - -extern ATOM g_clsTargetHighlighter; -extern MGLOBAL g_myGlobals; -extern HNETLIBUSER g_hNetlibUser; - -enum -{ - ICO_MAIN = 0, - ICO_MAINXS, - ICO_TARGET, - ICO_MONITOR, - ICO_END_, - ICO_BTN_HELP = 0, - ICO_BTN_FOLDER, - ICO_BTN_DESC, - ICO_BTN_DESCON, - ICO_BTN_DEL, - ICO_BTN_DELON, - ICO_BTN_ARROWL, - ICO_BTN_ARROWR, - ICO_BTN_UPDATE, - ICO_BTN_OK, - ICO_BTN_CANCEL, - ICO_BTN_EDIT, - ICO_BTN_EDITON, - ICO_BTN_COPY, - ICO_BTN_BBC, - ICO_BTN_BBCLNK, - ICO_BTN_DOWNARROW, - ICO_BTN_END_, -}; -#define GetIconHandle(ico) ICONS[ico].hIcolib -#define GetIcon(ico) IcoLib_GetIconByHandle(GetIconHandle(ico)) -extern IconItem ICONS[ICO_END_]; -#define GetIconBtnHandle(ico) ICONS_BTN[ico].hIcolib -#define GetIconBtn(ico) IcoLib_GetIconByHandle(GetIconBtnHandle(ico)) -extern IconItem ICONS_BTN[ICO_BTN_END_]; - -#define PtrIsValid(p) (((p)!=0)&&(((HANDLE)(p))!=INVALID_HANDLE_VALUE)) -#define MIR_FREE(p) {if (PtrIsValid(p)){mir_free((void*)p);(p)=NULL;}} -#ifdef _DEBUG -# define DBGMSG(str,...) do{char tmp[1024];sprintf(tmp,str,##__VA_ARGS__);OutputDebugStringA(tmp);}while(0) -#else -# define DBGMSG(str,...) -#endif - -void ComboBox_SelectItem(HWND hCombo, LPARAM data); - -template<class _Elem> -std::basic_string<_Elem> replace(const std::basic_string<_Elem> & Origninal, const std::basic_string<_Elem> & What, const std::basic_string<_Elem> & With) -{ - std::basic_string<_Elem> res; - size_t l = 0; - for (size_t p = Origninal.find(What.c_str(), 0); p != std::basic_string<_Elem>::npos; p = Origninal.find(What.c_str(), l)) - { - if (l != p) - res.append(Origninal.c_str() + l, p - l); - res.append(With); - l = p + What.length(); - } - if (l < Origninal.length()) - res.append(Origninal.c_str() + l); - - return res; -} - -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _GLOBAL_H_
+#define _GLOBAL_H_
+
+#define OEMRESOURCE
+
+#include <windows.h>
+#include <Windowsx.h>
+#include <commctrl.h>
+#include <time.h>
+#include <Shlwapi.h>
+#include <gdiplus.h>
+#include <mapi.h>
+#include <UxTheme.h>
+
+#include <map>
+#include <string>
+using namespace std;
+
+#include <msapi/vsstyle.h>
+#include <msapi/vssym32.h>
+
+#include <newpluginapi.h>
+#include <m_button.h>
+#include <m_chat_int.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_hotkeys.h>
+#include <m_imgsrvc.h>
+#include <m_langpack.h>
+#include <m_netlib.h>
+#include <m_protosvc.h>
+#include <m_skin.h>
+#include <m_json.h>
+#include <m_popup.h>
+#include <m_icolib.h>
+#include <m_message.h>
+
+#include <m_folders.h>
+#include <m_HTTPServer.h>
+#include <m_ftpfile.h>
+#include <m_sendss.h>
+#include <m_userinfoex.h>
+#include <m_cloudfile.h>
+
+#include "ctrl_button.h"
+#include "dlg_msgbox.h"
+#include "resource.h"
+#include "version.h"
+#include "CSend.h"
+#include "CSendEmail.h"
+#include "CSendFile.h"
+#include "CSendFTPFile.h"
+#include "CSendHTTPServer.h"
+#include "CSendCloduFile.h"
+#include "CSendHost_ImageShack.h"
+#include "CSendHost_uploadpie.h"
+#include "CSendHost_imgur.h"
+#include "DevKey.h"
+#include "UMainForm.h"
+#include "Utils.h"
+
+#define UM_CLOSING WM_USER+1
+#define UM_EVENT WM_USER+2
+
+// Generic Message Box for Errors
+#define MSGERROR(text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONERROR)
+#define MSGINFO (text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONINFORMATION)
+
+typedef struct _MGLOBAL {
+ uint32_t mirandaVersion; // mirandaVersion
+ BOOLEAN PopupActionsExist : 1; // Popup+ or MS_POPUP_REGISTERACTIONS exist
+ BOOLEAN PluginHTTPExist : 1; // HTTPServer or MS_HTTP_ACCEPT_CONNECTIONS exist
+ BOOLEAN PluginFTPExist : 1; // FTPFile or MS_FTPFILE_UPLOAD exist
+ BOOLEAN PluginCloudFileExist: 1; // CloudFile or MS_CLOUDFILE_UPLOAD exists
+
+} MGLOBAL, *LPMGLOBAL;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define ERROR_TITLE TranslateT("SendScreenshot - Error")
+
+// Miranda Database Key
+#define MODULENAME "SendSS"
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
+extern ATOM g_clsTargetHighlighter;
+extern MGLOBAL g_myGlobals;
+extern HNETLIBUSER g_hNetlibUser;
+
+enum
+{
+ ICO_MAIN = 0,
+ ICO_MAINXS,
+ ICO_TARGET,
+ ICO_MONITOR,
+ ICO_END_,
+ ICO_BTN_HELP = 0,
+ ICO_BTN_FOLDER,
+ ICO_BTN_DESC,
+ ICO_BTN_DESCON,
+ ICO_BTN_DEL,
+ ICO_BTN_DELON,
+ ICO_BTN_ARROWL,
+ ICO_BTN_ARROWR,
+ ICO_BTN_UPDATE,
+ ICO_BTN_OK,
+ ICO_BTN_CANCEL,
+ ICO_BTN_EDIT,
+ ICO_BTN_EDITON,
+ ICO_BTN_COPY,
+ ICO_BTN_BBC,
+ ICO_BTN_BBCLNK,
+ ICO_BTN_DOWNARROW,
+ ICO_BTN_END_,
+};
+#define GetIconHandle(ico) ICONS[ico].hIcolib
+#define GetIcon(ico) IcoLib_GetIconByHandle(GetIconHandle(ico))
+extern IconItem ICONS[ICO_END_];
+#define GetIconBtnHandle(ico) ICONS_BTN[ico].hIcolib
+#define GetIconBtn(ico) IcoLib_GetIconByHandle(GetIconBtnHandle(ico))
+extern IconItem ICONS_BTN[ICO_BTN_END_];
+
+#define PtrIsValid(p) (((p)!=0)&&(((HANDLE)(p))!=INVALID_HANDLE_VALUE))
+#define MIR_FREE(p) {if (PtrIsValid(p)){mir_free((void*)p);(p)=NULL;}}
+#ifdef _DEBUG
+# define DBGMSG(str,...) do{char tmp[1024];sprintf(tmp,str,##__VA_ARGS__);OutputDebugStringA(tmp);}while(0)
+#else
+# define DBGMSG(str,...)
+#endif
+
+void ComboBox_SelectItem(HWND hCombo, LPARAM data);
+
+template<class _Elem>
+std::basic_string<_Elem> replace(const std::basic_string<_Elem> & Origninal, const std::basic_string<_Elem> & What, const std::basic_string<_Elem> & With)
+{
+ std::basic_string<_Elem> res;
+ size_t l = 0;
+ for (size_t p = Origninal.find(What.c_str(), 0); p != std::basic_string<_Elem>::npos; p = Origninal.find(What.c_str(), l))
+ {
+ if (l != p)
+ res.append(Origninal.c_str() + l, p - l);
+ res.append(With);
+ l = p + What.length();
+ }
+ if (l < Origninal.length())
+ res.append(Origninal.c_str() + l);
+
+ return res;
+}
+
+#endif
diff --git a/plugins/Sessions/Src/Import.cpp b/plugins/Sessions/Src/Import.cpp index 47b2d80159..6eac8c639e 100644 --- a/plugins/Sessions/Src/Import.cpp +++ b/plugins/Sessions/Src/Import.cpp @@ -1,134 +1,134 @@ -/* -Copyright (C) 2012-22 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" - -bool LoadContactsFromMask(MCONTACT hContact, int mode, int count) -{ - CMStringA szValue; - if (mode == 0) - szValue = g_plugin.getMStringA(hContact, "LastSessionsMarks"); - else if (mode == 1) - szValue = g_plugin.getMStringA(hContact, "UserSessionsMarks"); - - if (szValue.IsEmpty()) - return false; - - return szValue[count] == '1'; -} - -int GetInSessionOrder(MCONTACT hContact, int mode, int count) -{ - char szTemp[3] = { 0, 0, 0 }; - count *= 2; - - if (mode == 0) { - CMStringA szValue(g_plugin.getMStringA(hContact, "LastSessionsOrder")); - if (!szValue.IsEmpty() && count < szValue.GetLength()) { - memcpy(szTemp, szValue.c_str() + count, 2); - return atoi(szTemp); - } - } - else if (mode == 1) { - CMStringA szValue(g_plugin.getMStringA(hContact, "UserSessionsOrder")); - if (!szValue.IsEmpty() && count < szValue.GetLength()) { - memcpy(szTemp, szValue.c_str() + count, 2); - return atoi(szTemp); - } - } - return 0; -} - -void CMPlugin::CheckImport() -{ - if (db_get_b(0, "Compatibility", MODULENAME) > 0) - return; - - MCONTACT tmp[255]; - - for (int i = 0;; i++) { - char szSetting[100]; - mir_snprintf(szSetting, "SessionDate_%d", i); - CMStringW wszName(getMStringW(szSetting)); - if (wszName.IsEmpty()) - break; - - delSetting(szSetting); - - memset(tmp, 0, sizeof(tmp)); - for (auto &hContact : Contacts()) { - if (LoadContactsFromMask(hContact, 0, i)) { - int idx = GetInSessionOrder(hContact, 0, i); - if (idx < _countof(tmp)) - tmp[idx] = hContact; - } - } - if (tmp[0] == 0) - continue; - - CSession *pNew = new CSession(); - pNew->id = 255 - i; - pNew->wszName = wszName; - for (int k = 0; tmp[k]; k++) - pNew->contacts.push_back(tmp[k]); - - pNew->save(); - } - - for (int i = 0;; i++) { - char szSetting[100]; - mir_snprintf(szSetting, "UserSessionDsc_%d", i); - CMStringW wszName(getMStringW(szSetting)); - if (wszName.IsEmpty()) - break; - - delSetting(szSetting); - - memset(tmp, 0, sizeof(tmp)); - for (auto &hContact : Contacts()) { - if (LoadContactsFromMask(hContact, 1, i)) { - int idx = GetInSessionOrder(hContact, 1, i); - if (idx < _countof(tmp)) - tmp[idx] = hContact; - } - } - if (tmp[0] == 0) - continue; - - CSession *pNew = new CSession(); - pNew->id = 255 - i; - pNew->bIsUser = true; - pNew->wszName = wszName; - - mir_snprintf(szSetting, "FavUserSession_%d", i); - pNew->bIsFavorite = getBool(szSetting); - delSetting(szSetting); - - for (int k = 0; tmp[k]; k++) - pNew->contacts.push_back(tmp[k]); - - pNew->save(); - } - - delSetting("UserSessionsCount"); - - for (auto &hContact : Contacts()) - db_delete_module(hContact, MODULENAME); - - g_lastDateId = g_lastUserId = 256; - db_set_b(0, "Compatibility", MODULENAME, 1); -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+bool LoadContactsFromMask(MCONTACT hContact, int mode, int count)
+{
+ CMStringA szValue;
+ if (mode == 0)
+ szValue = g_plugin.getMStringA(hContact, "LastSessionsMarks");
+ else if (mode == 1)
+ szValue = g_plugin.getMStringA(hContact, "UserSessionsMarks");
+
+ if (szValue.IsEmpty())
+ return false;
+
+ return szValue[count] == '1';
+}
+
+int GetInSessionOrder(MCONTACT hContact, int mode, int count)
+{
+ char szTemp[3] = { 0, 0, 0 };
+ count *= 2;
+
+ if (mode == 0) {
+ CMStringA szValue(g_plugin.getMStringA(hContact, "LastSessionsOrder"));
+ if (!szValue.IsEmpty() && count < szValue.GetLength()) {
+ memcpy(szTemp, szValue.c_str() + count, 2);
+ return atoi(szTemp);
+ }
+ }
+ else if (mode == 1) {
+ CMStringA szValue(g_plugin.getMStringA(hContact, "UserSessionsOrder"));
+ if (!szValue.IsEmpty() && count < szValue.GetLength()) {
+ memcpy(szTemp, szValue.c_str() + count, 2);
+ return atoi(szTemp);
+ }
+ }
+ return 0;
+}
+
+void CMPlugin::CheckImport()
+{
+ if (db_get_b(0, "Compatibility", MODULENAME) > 0)
+ return;
+
+ MCONTACT tmp[255];
+
+ for (int i = 0;; i++) {
+ char szSetting[100];
+ mir_snprintf(szSetting, "SessionDate_%d", i);
+ CMStringW wszName(getMStringW(szSetting));
+ if (wszName.IsEmpty())
+ break;
+
+ delSetting(szSetting);
+
+ memset(tmp, 0, sizeof(tmp));
+ for (auto &hContact : Contacts()) {
+ if (LoadContactsFromMask(hContact, 0, i)) {
+ int idx = GetInSessionOrder(hContact, 0, i);
+ if (idx < _countof(tmp))
+ tmp[idx] = hContact;
+ }
+ }
+ if (tmp[0] == 0)
+ continue;
+
+ CSession *pNew = new CSession();
+ pNew->id = 255 - i;
+ pNew->wszName = wszName;
+ for (int k = 0; tmp[k]; k++)
+ pNew->contacts.push_back(tmp[k]);
+
+ pNew->save();
+ }
+
+ for (int i = 0;; i++) {
+ char szSetting[100];
+ mir_snprintf(szSetting, "UserSessionDsc_%d", i);
+ CMStringW wszName(getMStringW(szSetting));
+ if (wszName.IsEmpty())
+ break;
+
+ delSetting(szSetting);
+
+ memset(tmp, 0, sizeof(tmp));
+ for (auto &hContact : Contacts()) {
+ if (LoadContactsFromMask(hContact, 1, i)) {
+ int idx = GetInSessionOrder(hContact, 1, i);
+ if (idx < _countof(tmp))
+ tmp[idx] = hContact;
+ }
+ }
+ if (tmp[0] == 0)
+ continue;
+
+ CSession *pNew = new CSession();
+ pNew->id = 255 - i;
+ pNew->bIsUser = true;
+ pNew->wszName = wszName;
+
+ mir_snprintf(szSetting, "FavUserSession_%d", i);
+ pNew->bIsFavorite = getBool(szSetting);
+ delSetting(szSetting);
+
+ for (int k = 0; tmp[k]; k++)
+ pNew->contacts.push_back(tmp[k]);
+
+ pNew->save();
+ }
+
+ delSetting("UserSessionsCount");
+
+ for (auto &hContact : Contacts())
+ db_delete_module(hContact, MODULENAME);
+
+ g_lastDateId = g_lastUserId = 256;
+ db_set_b(0, "Compatibility", MODULENAME, 1);
+}
diff --git a/plugins/Sessions/Src/LoadSessions.cpp b/plugins/Sessions/Src/LoadSessions.cpp index de9e428d0a..690ff664d2 100644 --- a/plugins/Sessions/Src/LoadSessions.cpp +++ b/plugins/Sessions/Src/LoadSessions.cpp @@ -1,227 +1,227 @@ -/* -Copyright (C) 2012-22 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" - -static bool g_bDontShow, g_bStartup; - -///////////////////////////////////////////////////////////////////////////////////////// - -class CLoadSessionDlg : public CDlgBase -{ - CSession *pSession = nullptr; - - CTimer timerShow, timerLoad; - CCtrlCombo m_list; - CCtrlButton btnDelete; - -public: - CLoadSessionDlg() : - CDlgBase(g_plugin, IDD_WLCMDIALOG), - m_list(this, IDC_LIST), - btnDelete(this, IDC_SESSDEL), - timerLoad(this, TIMERID_LOAD), - timerShow(this, TIMERID_SHOW) - { - btnDelete.OnClick = Callback(this, &CLoadSessionDlg::onClick_Delete); - - m_list.OnSelChanged = Callback(this, &CLoadSessionDlg::onSelChange_List); - - timerLoad.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Load); - timerShow.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Show); - } - - bool OnInitDialog() override - { - g_hDlg = m_hwnd; - - LoadSessionToCombobox(m_list, false); - LoadSessionToCombobox(m_list, true); - m_list.SetCurSel(0); - pSession = (CSession *)m_list.GetItemData(0); - - int iDelay = g_plugin.getWord("StartupModeDelay", 1500); - if (g_bDontShow == TRUE) - timerLoad.Start(iDelay); - else { - LoadPosition(m_hwnd, "LoadDlg"); - if (g_bStartup) - timerShow.Start(iDelay); - else - Show(); - } - - return true; - } - - bool OnApply() override - { - if (!LoadSession(pSession)) - return true; - - return false; - } - - void OnDestroy() override - { - SavePosition(m_hwnd, "LoadDlg"); - g_hDlg = nullptr; - } - - void onTimer_Load(CTimer *pTimer) - { - pTimer->Stop(); - LoadSession(pSession); - g_bDontShow = g_bStartup = false; - Close(); - } - - void onTimer_Show(CTimer *pTimer) - { - pTimer->Stop(); - Show(); - g_bStartup = FALSE; - } - - void onSelChange_List(CCtrlCombo *) - { - int index = m_list.GetCurSel(); - if (index != CB_ERR) - pSession = (CSession*)m_list.GetItemData(index); - } - - void onClick_Delete(CCtrlButton *) - { - if (pSession == nullptr) { - if (session_list_recovered[0]) { - for (int i = 0; session_list_recovered[i]; i++) - g_plugin.setByte(session_list_recovered[i], "wasInLastSession", 0); - memset(session_list_recovered, 0, sizeof(session_list_recovered)); - } - g_bIncompletedSave = 0; - } - else pSession->remove(); - - m_list.ResetContent(); - LoadSessionToCombobox(m_list, false); - LoadSessionToCombobox(m_list, true); - - m_list.SetCurSel(0); - btnDelete.Enable(m_list.GetCount() != 0); - } -}; - -INT_PTR OpenSessionsManagerWindow(WPARAM, LPARAM) -{ - if (g_hDlg) { - ShowWindow(g_hDlg, SW_SHOW); - return 0; - } - - if (g_bIncompletedSave || g_arDateSessions.getCount() || g_arUserSessions.getCount()) { - (new CLoadSessionDlg())->Create(); - return 0; - } - - if (g_bOtherWarnings) - MessageBox(nullptr, TranslateT("No sessions to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING); - return 1; -} - -void CALLBACK LaunchSessions() -{ - int startup = g_plugin.getByte("StartupMode", 3); - if (startup == 1 || (startup == 3 && g_bLastSessionPresent)) { - g_bStartup = true; - (new CLoadSessionDlg())->Create(); - } - else if (startup == 2 && g_bLastSessionPresent) { - g_bDontShow = true; - (new CLoadSessionDlg())->Create(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR LoadLastSession(WPARAM, LPARAM) -{ - int cnt = g_arDateSessions.getCount(); - if (g_bLastSessionPresent && cnt) - return LoadSession(&g_arDateSessions[cnt-1]); - - if (g_bOtherWarnings) - MessageBox(nullptr, TranslateT("Last Sessions is empty"), TranslateT("Sessions Manager"), MB_OK); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Load session dialog - -int LoadSession(CSession *pSession) -{ - int dup = 0; - int hidden[255] = { '0' }; - - MCONTACT session_list_t[255] = {}; - if (session_list_recovered[0] && pSession == nullptr) - memcpy(session_list_t, session_list_recovered, sizeof(session_list_t)); - else { - int i = 0; - for (auto &cc : pSession->contacts) - session_list_t[i++] = cc; - } - - int i = 0, j = 0; - // TODO: change to "switch" - while (session_list_t[i] != 0) { - if (CheckForDuplicate(session_list, session_list_t[i]) == -1) - Clist_ContactDoubleClicked(session_list_t[i]); - else if (g_bWarnOnHidden) { - if (!CheckContactVisibility(session_list_t[i])) { - hidden[j] = i + 1; - j++; - } - dup++; - } - i++; - } - - if (i == 0) { - if (g_bOtherWarnings) - MessageBox(nullptr, TranslateT("No contacts to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING); - return 1; - } - - if (dup == i) { - if (!hidden[i]) { - if (g_bOtherWarnings) - MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING); - return 1; - } - if (!g_bWarnOnHidden && g_bOtherWarnings) { - MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING); - return 1; - } - if (g_bWarnOnHidden) { - if (IDYES == MessageBox(nullptr, TranslateT("This Session already opened (but probably hidden).\nDo you want to show hidden contacts?"), TranslateT("Sessions Manager"), MB_YESNO | MB_ICONQUESTION)) - for (j = 0; hidden[j] != 0; j++) - Clist_ContactDoubleClicked(session_list_t[hidden[j] - 1]); - } - } - - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+static bool g_bDontShow, g_bStartup;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CLoadSessionDlg : public CDlgBase
+{
+ CSession *pSession = nullptr;
+
+ CTimer timerShow, timerLoad;
+ CCtrlCombo m_list;
+ CCtrlButton btnDelete;
+
+public:
+ CLoadSessionDlg() :
+ CDlgBase(g_plugin, IDD_WLCMDIALOG),
+ m_list(this, IDC_LIST),
+ btnDelete(this, IDC_SESSDEL),
+ timerLoad(this, TIMERID_LOAD),
+ timerShow(this, TIMERID_SHOW)
+ {
+ btnDelete.OnClick = Callback(this, &CLoadSessionDlg::onClick_Delete);
+
+ m_list.OnSelChanged = Callback(this, &CLoadSessionDlg::onSelChange_List);
+
+ timerLoad.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Load);
+ timerShow.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Show);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_hDlg = m_hwnd;
+
+ LoadSessionToCombobox(m_list, false);
+ LoadSessionToCombobox(m_list, true);
+ m_list.SetCurSel(0);
+ pSession = (CSession *)m_list.GetItemData(0);
+
+ int iDelay = g_plugin.getWord("StartupModeDelay", 1500);
+ if (g_bDontShow == TRUE)
+ timerLoad.Start(iDelay);
+ else {
+ LoadPosition(m_hwnd, "LoadDlg");
+ if (g_bStartup)
+ timerShow.Start(iDelay);
+ else
+ Show();
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (!LoadSession(pSession))
+ return true;
+
+ return false;
+ }
+
+ void OnDestroy() override
+ {
+ SavePosition(m_hwnd, "LoadDlg");
+ g_hDlg = nullptr;
+ }
+
+ void onTimer_Load(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ LoadSession(pSession);
+ g_bDontShow = g_bStartup = false;
+ Close();
+ }
+
+ void onTimer_Show(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ Show();
+ g_bStartup = FALSE;
+ }
+
+ void onSelChange_List(CCtrlCombo *)
+ {
+ int index = m_list.GetCurSel();
+ if (index != CB_ERR)
+ pSession = (CSession*)m_list.GetItemData(index);
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ if (pSession == nullptr) {
+ if (session_list_recovered[0]) {
+ for (int i = 0; session_list_recovered[i]; i++)
+ g_plugin.setByte(session_list_recovered[i], "wasInLastSession", 0);
+ memset(session_list_recovered, 0, sizeof(session_list_recovered));
+ }
+ g_bIncompletedSave = 0;
+ }
+ else pSession->remove();
+
+ m_list.ResetContent();
+ LoadSessionToCombobox(m_list, false);
+ LoadSessionToCombobox(m_list, true);
+
+ m_list.SetCurSel(0);
+ btnDelete.Enable(m_list.GetCount() != 0);
+ }
+};
+
+INT_PTR OpenSessionsManagerWindow(WPARAM, LPARAM)
+{
+ if (g_hDlg) {
+ ShowWindow(g_hDlg, SW_SHOW);
+ return 0;
+ }
+
+ if (g_bIncompletedSave || g_arDateSessions.getCount() || g_arUserSessions.getCount()) {
+ (new CLoadSessionDlg())->Create();
+ return 0;
+ }
+
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("No sessions to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+}
+
+void CALLBACK LaunchSessions()
+{
+ int startup = g_plugin.getByte("StartupMode", 3);
+ if (startup == 1 || (startup == 3 && g_bLastSessionPresent)) {
+ g_bStartup = true;
+ (new CLoadSessionDlg())->Create();
+ }
+ else if (startup == 2 && g_bLastSessionPresent) {
+ g_bDontShow = true;
+ (new CLoadSessionDlg())->Create();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR LoadLastSession(WPARAM, LPARAM)
+{
+ int cnt = g_arDateSessions.getCount();
+ if (g_bLastSessionPresent && cnt)
+ return LoadSession(&g_arDateSessions[cnt-1]);
+
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("Last Sessions is empty"), TranslateT("Sessions Manager"), MB_OK);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load session dialog
+
+int LoadSession(CSession *pSession)
+{
+ int dup = 0;
+ int hidden[255] = { '0' };
+
+ MCONTACT session_list_t[255] = {};
+ if (session_list_recovered[0] && pSession == nullptr)
+ memcpy(session_list_t, session_list_recovered, sizeof(session_list_t));
+ else {
+ int i = 0;
+ for (auto &cc : pSession->contacts)
+ session_list_t[i++] = cc;
+ }
+
+ int i = 0, j = 0;
+ // TODO: change to "switch"
+ while (session_list_t[i] != 0) {
+ if (CheckForDuplicate(session_list, session_list_t[i]) == -1)
+ Clist_ContactDoubleClicked(session_list_t[i]);
+ else if (g_bWarnOnHidden) {
+ if (!CheckContactVisibility(session_list_t[i])) {
+ hidden[j] = i + 1;
+ j++;
+ }
+ dup++;
+ }
+ i++;
+ }
+
+ if (i == 0) {
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("No contacts to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+ }
+
+ if (dup == i) {
+ if (!hidden[i]) {
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+ }
+ if (!g_bWarnOnHidden && g_bOtherWarnings) {
+ MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+ }
+ if (g_bWarnOnHidden) {
+ if (IDYES == MessageBox(nullptr, TranslateT("This Session already opened (but probably hidden).\nDo you want to show hidden contacts?"), TranslateT("Sessions Manager"), MB_YESNO | MB_ICONQUESTION))
+ for (j = 0; hidden[j] != 0; j++)
+ Clist_ContactDoubleClicked(session_list_t[hidden[j] - 1]);
+ }
+ }
+
+ return 0;
+}
diff --git a/plugins/Sessions/Src/SaveSessions.cpp b/plugins/Sessions/Src/SaveSessions.cpp index 4d195b4c83..c9792d8ee2 100644 --- a/plugins/Sessions/Src/SaveSessions.cpp +++ b/plugins/Sessions/Src/SaveSessions.cpp @@ -1,194 +1,194 @@ -/* -Copyright (C) 2012-22 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" - -HWND g_hSDlg; - -bool bSC = false; - -///////////////////////////////////////////////////////////////////////////////////////// - -static int SaveUserSessionName(MCONTACT *contacts, wchar_t *szUSessionName) -{ - if (contacts[0] == 0) - return 1; - - CSession *pSession = nullptr; - for (auto &it : g_arUserSessions) - if (!it->wszName.CompareNoCase(szUSessionName)) - pSession = it; - - if (pSession) - pSession->contacts.clear(); - else { - g_plugin.g_lastUserId = g_plugin.g_lastUserId + 1; - - pSession = new CSession(); - pSession->id = g_plugin.g_lastUserId; - pSession->bIsUser = true; - pSession->wszName = szUSessionName; - g_arUserSessions.insert(pSession); - } - - for (int i = 0; contacts[i]; i++) - pSession->contacts.push_back(contacts[i]); - - pSession->save(); - - while (g_arUserSessions.getCount() > g_ses_limit) { - g_arUserSessions[0].remove(); - g_arUserSessions.remove(int(0)); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Save session dialog - -class CSessionDlg : public CDlgBase -{ - CCtrlClc m_clist; - CCtrlCombo m_sessions; - CCtrlCheck chkSelContacts, chkSaveAndClose; - - MCONTACT user_session_list[255]; - -public: - CSessionDlg() : - CDlgBase(g_plugin, IDD_SAVEDIALOG), - m_clist(this, IDC_CLIST), - m_sessions(this, IDC_LIST), - chkSelContacts(this, IDC_SELCONTACTS), - chkSaveAndClose(this, IDC_SANDCCHECK) - { - memset(user_session_list, 0, sizeof(user_session_list)); - - chkSelContacts.OnChange = Callback(this, &CSessionDlg::onChange_SelContacts); - - m_clist.OnCheckChanged = Callback(this, &CSessionDlg::onCheckChanged_Clist); - } - - bool OnInitDialog() override - { - g_hSDlg = m_hwnd; - LoadSessionToCombobox(m_sessions, true); - - SetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE, - GetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE) | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES); - m_clist.SetExStyle(CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT); - m_clist.AutoRebuild(); - - LoadPosition(m_hwnd, "SaveDlg"); - return true; - } - - bool OnApply() override - { - wchar_t szUserSessionName[MAX_PATH]; - m_sessions.GetText(szUserSessionName, _countof(szUserSessionName)); - rtrimw(szUserSessionName); - if (szUserSessionName[0] == 0) { - MessageBox(nullptr, TranslateT("Session name is empty, enter the name and try again"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING); - return false; - } - - if (chkSelContacts.IsChecked() && bSC) { - int i = 0; - for (auto &hContact : Contacts()) { - uint8_t res = m_clist.GetCheck(m_clist.FindContact(hContact)); - if (res) { - user_session_list[i] = hContact; - i++; - } - } - - SaveUserSessionName(user_session_list, szUserSessionName); - return true; - } - - if (!SaveUserSessionName(session_list, szUserSessionName)) { - if (chkSaveAndClose.IsChecked()) - CloseCurrentSession(0, 0); - return true; - } - - MessageBox(nullptr, TranslateT("Current session is empty!"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING); - return false; - } - - void OnDestroy() override - { - SavePosition(m_hwnd, "SaveDlg"); - g_hSDlg = nullptr; - } - - void onCheckChanged_Clist(CCtrlClc*) - { - bSC = TRUE; - memcpy(user_session_list, session_list, sizeof(user_session_list)); - } - - void onChange_SelContacts(CCtrlCheck *) - { - if (!m_bInitialized) - return; - - RECT rWnd; - GetWindowRect(m_hwnd, &rWnd); - - int x = rWnd.right - rWnd.left, y = rWnd.bottom - rWnd.top, dy, dx, dd; - - if (chkSelContacts.IsChecked()) { - chkSaveAndClose.Disable(); - dy = 20; - dx = 150; - dd = 5; - m_clist.Show(); - } - else { - chkSaveAndClose.Enable(); - dy = -20; - dx = -150; - dd = 5; - m_clist.Hide(); - } - - SetWindowPos(m_hwnd, nullptr, rWnd.left, rWnd.top, x + dx, y + (dx / 3), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE); - SetWindowPos(m_clist.GetHwnd(), nullptr, x - dd, dd, dx - dd, y + (dx / 12), SWP_NOZORDER); - - for (int i = 0; session_list[i] > 0; i++) - m_clist.SetCheck(m_clist.FindContact(session_list[i]), 1); - - OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_LIST), 0, dy); - OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_STATIC), 0, dy); - OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_SANDCCHECK), 0, dy); - OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDOK), 0, dy); - OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDCANCEL), 0, dy); - } -}; - -INT_PTR SaveUserSessionHandles(WPARAM, LPARAM) -{ - if (g_hSDlg) { - ShowWindow(g_hSDlg, SW_SHOW); - return 1; - } - - (new CSessionDlg())->Show(); - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+HWND g_hSDlg;
+
+bool bSC = false;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int SaveUserSessionName(MCONTACT *contacts, wchar_t *szUSessionName)
+{
+ if (contacts[0] == 0)
+ return 1;
+
+ CSession *pSession = nullptr;
+ for (auto &it : g_arUserSessions)
+ if (!it->wszName.CompareNoCase(szUSessionName))
+ pSession = it;
+
+ if (pSession)
+ pSession->contacts.clear();
+ else {
+ g_plugin.g_lastUserId = g_plugin.g_lastUserId + 1;
+
+ pSession = new CSession();
+ pSession->id = g_plugin.g_lastUserId;
+ pSession->bIsUser = true;
+ pSession->wszName = szUSessionName;
+ g_arUserSessions.insert(pSession);
+ }
+
+ for (int i = 0; contacts[i]; i++)
+ pSession->contacts.push_back(contacts[i]);
+
+ pSession->save();
+
+ while (g_arUserSessions.getCount() > g_ses_limit) {
+ g_arUserSessions[0].remove();
+ g_arUserSessions.remove(int(0));
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Save session dialog
+
+class CSessionDlg : public CDlgBase
+{
+ CCtrlClc m_clist;
+ CCtrlCombo m_sessions;
+ CCtrlCheck chkSelContacts, chkSaveAndClose;
+
+ MCONTACT user_session_list[255];
+
+public:
+ CSessionDlg() :
+ CDlgBase(g_plugin, IDD_SAVEDIALOG),
+ m_clist(this, IDC_CLIST),
+ m_sessions(this, IDC_LIST),
+ chkSelContacts(this, IDC_SELCONTACTS),
+ chkSaveAndClose(this, IDC_SANDCCHECK)
+ {
+ memset(user_session_list, 0, sizeof(user_session_list));
+
+ chkSelContacts.OnChange = Callback(this, &CSessionDlg::onChange_SelContacts);
+
+ m_clist.OnCheckChanged = Callback(this, &CSessionDlg::onCheckChanged_Clist);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_hSDlg = m_hwnd;
+ LoadSessionToCombobox(m_sessions, true);
+
+ SetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE) | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
+ m_clist.SetExStyle(CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT);
+ m_clist.AutoRebuild();
+
+ LoadPosition(m_hwnd, "SaveDlg");
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ wchar_t szUserSessionName[MAX_PATH];
+ m_sessions.GetText(szUserSessionName, _countof(szUserSessionName));
+ rtrimw(szUserSessionName);
+ if (szUserSessionName[0] == 0) {
+ MessageBox(nullptr, TranslateT("Session name is empty, enter the name and try again"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return false;
+ }
+
+ if (chkSelContacts.IsChecked() && bSC) {
+ int i = 0;
+ for (auto &hContact : Contacts()) {
+ uint8_t res = m_clist.GetCheck(m_clist.FindContact(hContact));
+ if (res) {
+ user_session_list[i] = hContact;
+ i++;
+ }
+ }
+
+ SaveUserSessionName(user_session_list, szUserSessionName);
+ return true;
+ }
+
+ if (!SaveUserSessionName(session_list, szUserSessionName)) {
+ if (chkSaveAndClose.IsChecked())
+ CloseCurrentSession(0, 0);
+ return true;
+ }
+
+ MessageBox(nullptr, TranslateT("Current session is empty!"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return false;
+ }
+
+ void OnDestroy() override
+ {
+ SavePosition(m_hwnd, "SaveDlg");
+ g_hSDlg = nullptr;
+ }
+
+ void onCheckChanged_Clist(CCtrlClc*)
+ {
+ bSC = TRUE;
+ memcpy(user_session_list, session_list, sizeof(user_session_list));
+ }
+
+ void onChange_SelContacts(CCtrlCheck *)
+ {
+ if (!m_bInitialized)
+ return;
+
+ RECT rWnd;
+ GetWindowRect(m_hwnd, &rWnd);
+
+ int x = rWnd.right - rWnd.left, y = rWnd.bottom - rWnd.top, dy, dx, dd;
+
+ if (chkSelContacts.IsChecked()) {
+ chkSaveAndClose.Disable();
+ dy = 20;
+ dx = 150;
+ dd = 5;
+ m_clist.Show();
+ }
+ else {
+ chkSaveAndClose.Enable();
+ dy = -20;
+ dx = -150;
+ dd = 5;
+ m_clist.Hide();
+ }
+
+ SetWindowPos(m_hwnd, nullptr, rWnd.left, rWnd.top, x + dx, y + (dx / 3), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE);
+ SetWindowPos(m_clist.GetHwnd(), nullptr, x - dd, dd, dx - dd, y + (dx / 12), SWP_NOZORDER);
+
+ for (int i = 0; session_list[i] > 0; i++)
+ m_clist.SetCheck(m_clist.FindContact(session_list[i]), 1);
+
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_LIST), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_STATIC), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_SANDCCHECK), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDOK), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDCANCEL), 0, dy);
+ }
+};
+
+INT_PTR SaveUserSessionHandles(WPARAM, LPARAM)
+{
+ if (g_hSDlg) {
+ ShowWindow(g_hSDlg, SW_SHOW);
+ return 1;
+ }
+
+ (new CSessionDlg())->Show();
+ return 0;
+}
diff --git a/plugins/Sessions/Src/stdafx.cxx b/plugins/Sessions/Src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Sessions/Src/stdafx.cxx +++ b/plugins/Sessions/Src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ShellExt/src/stdafx.cxx b/plugins/ShellExt/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ShellExt/src/stdafx.cxx +++ b/plugins/ShellExt/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SimpleAR/src/stdafx.cxx b/plugins/SimpleAR/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SimpleAR/src/stdafx.cxx +++ b/plugins/SimpleAR/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SimpleStatusMsg/src/awaymsg.cpp b/plugins/SimpleStatusMsg/src/awaymsg.cpp index e034dc3009..61320cfb44 100644 --- a/plugins/SimpleStatusMsg/src/awaymsg.cpp +++ b/plugins/SimpleStatusMsg/src/awaymsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-10 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/SimpleStatusMsg/src/stdafx.cxx b/plugins/SimpleStatusMsg/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SimpleStatusMsg/src/stdafx.cxx +++ b/plugins/SimpleStatusMsg/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SkypeStatusChange/src/stdafx.cxx b/plugins/SkypeStatusChange/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SkypeStatusChange/src/stdafx.cxx +++ b/plugins/SkypeStatusChange/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SmileyAdd/src/options.cpp b/plugins/SmileyAdd/src/options.cpp index 324506dd8d..92e3e8676c 100644 --- a/plugins/SmileyAdd/src/options.cpp +++ b/plugins/SmileyAdd/src/options.cpp @@ -1,6 +1,6 @@ /*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/options.h b/plugins/SmileyAdd/src/options.h index ee7bc61ba7..299ef72f2e 100644 --- a/plugins/SmileyAdd/src/options.h +++ b/plugins/SmileyAdd/src/options.h @@ -1,6 +1,6 @@ /*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/services.cpp b/plugins/SmileyAdd/src/services.cpp index 01ecc57786..abc5e3cb93 100644 --- a/plugins/SmileyAdd/src/services.cpp +++ b/plugins/SmileyAdd/src/services.cpp @@ -1,6 +1,6 @@ /*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/smileys.cpp b/plugins/SmileyAdd/src/smileys.cpp index 3c3dd69b33..a9f69a1c5c 100644 --- a/plugins/SmileyAdd/src/smileys.cpp +++ b/plugins/SmileyAdd/src/smileys.cpp @@ -1,977 +1,977 @@ -/* -Miranda NG SmileyAdd Plugin -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) -Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved -Copyright (C) 2003-04 Rein-Peter de Boer - -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" - -SmileyPackListType g_SmileyPacks; -SmileyCategoryListType g_SmileyCategories; - -static HWND hwndHidden = nullptr; - -static void CALLBACK timerProc(HWND, UINT, UINT_PTR param, DWORD) -{ - SmileyType *pType = (SmileyType*)param; - pType->MoveToNextFrame(); -} - -// these two functions must be called from the main thread -static void CALLBACK sttStartTimer(PVOID obj) -{ - if (hwndHidden == nullptr) - hwndHidden = CreateWindowEx(0, L"STATIC", nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr); - - SmileyType *pType = (SmileyType*)obj; - pType->SetFrameDelay(); -} - -static void CALLBACK sttStopTimer(PVOID obj) -{ - KillTimer(hwndHidden, (DWORD_PTR)obj); -} - -// -// SmileyType -// - -SmileyType::SmileyType(void) : - m_arSmileys(10, PtrKeySortT) -{ - m_SmileyIcon = nullptr; - m_xepimg = nullptr; - m_flags = 0; - m_index = 0; - m_size.cx = 0; - m_size.cy = 0; -} - -SmileyType::~SmileyType() -{ - if (m_xepimg) { - m_xepimg->Release(); - m_xepimg = nullptr; - } - - if (m_SmileyIcon != nullptr) { - DestroyIcon(m_SmileyIcon); - m_SmileyIcon = nullptr; - } -} - -void SmileyType::AddObject(ISmileyBase *pObject) -{ - if (m_arSmileys.getCount() == 0) { - if (m_xepimg == nullptr) - m_xepimg = AddCacheImage(m_filepath, m_index); - CallFunctionAsync(sttStartTimer, this); - } - - m_arSmileys.insert(pObject); -} - -void SmileyType::RemoveObject(ISmileyBase *pObject) -{ - int idx = m_arSmileys.getIndex(pObject); - if (idx == -1) - return; - - m_arSmileys.remove(idx); - if (m_arSmileys.getCount() == 0) - CallFunctionAsync(sttStopTimer, this); -} - -void SmileyType::SetFrameDelay() -{ - int iFrameDelay = (m_xepimg == nullptr) ? 0 : m_xepimg->GetFrameDelay(); - if (iFrameDelay <= 0) - KillTimer(hwndHidden, (DWORD_PTR)this); - else - SetTimer(hwndHidden, (DWORD_PTR)this, iFrameDelay*10, timerProc); -} - -void SmileyType::MoveToNextFrame() -{ - m_index = m_xepimg->SelectNextFrame(m_index); - - for (auto &it : m_arSmileys.rev_iter()) - it->Draw(); - - SetFrameDelay(); // reset timer -} - -HICON SmileyType::GetIcon(void) -{ - if (m_SmileyIcon == nullptr) { - ImageBase *img = CreateCachedImage(); - if (!img) - return nullptr; - - img->SelectFrame(m_index); - m_SmileyIcon = img->GetIcon(); - img->Release(); - } - return m_SmileyIcon; -} - -HICON SmileyType::GetIconDup(void) -{ - if (ImageBase *img = CreateCachedImage()) { - img->SelectFrame(m_index); - HICON hIcon = img->GetIcon(); - img->Release(); - return hIcon; - } - return nullptr; -} - -bool SmileyType::LoadFromImage(IStream *pStream) -{ - if (m_xepimg) - m_xepimg->Release(); - - CMStringW name; - m_xepimg = new ImageType(0, name, pStream); - return true; -} - -bool SmileyType::LoadFromResource(const CMStringW &file, const int index) -{ - m_index = index; - m_filepath = file; - return true; -} - -void SmileyType::GetSize(SIZE &size) -{ - if (m_size.cy == 0) { - ImageBase *img = CreateCachedImage(); - if (img) { - img->GetSize(m_size); - img->Release(); - } - } - size = m_size; -} - -ImageBase* SmileyType::CreateCachedImage(void) -{ - if (m_xepimg) { - m_xepimg->AddRef(); - return m_xepimg; - } - return AddCacheImage(m_filepath, m_index); -} - -void SmileyType::SetImList(HIMAGELIST hImLst, long i) -{ - if (m_xepimg) m_xepimg->Release(); - m_xepimg = new ImageListItemType(0, hImLst, i); -} - -HBITMAP SmileyType::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY) -{ - ImageBase *img = CreateCachedImage(); - if (!img) return nullptr; - img->SelectFrame(m_index); - HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY); - img->Release(); - - return hBmp; -} - -// -// SmileyPackType -// - -SmileyPackType::SmileyPackType() -{ - m_hSmList = nullptr; - errorFound = false; -} - -SmileyType* SmileyPackType::GetSmiley(unsigned index) -{ - return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : nullptr; -} - -SmileyPackType::~SmileyPackType() -{ - if (m_hSmList != nullptr) ImageList_Destroy(m_hSmList); -} - -static const wchar_t urlRegEx[] = L"(?:ftp|https|http|file|aim|webcal|irc|msnim|xmpp|gopher|mailto|news|nntp|telnet|wais|prospero)://?[\\w.?%:/$+;]*"; -static const wchar_t pathRegEx[] = L"[\\s\"][a-zA-Z]:[\\\\/][\\w.\\-\\\\/]*"; -static const wchar_t timeRegEx[] = L"\\d{1,2}:\\d{2}:\\d{2}|\\d{1,2}:\\d{2}"; - -void SmileyPackType::AddTriggersToSmileyLookup(void) -{ - CMStringW emptystr; - m_SmileyLookup.insert(new SmileyLookup(urlRegEx, true, -1, emptystr)); - m_SmileyLookup.insert(new SmileyLookup(pathRegEx, true, -1, emptystr)); - m_SmileyLookup.insert(new SmileyLookup(timeRegEx, true, -1, emptystr)); - - for (int dist = 0; dist < m_SmileyList.getCount(); dist++) { - auto &p = m_SmileyList[dist]; - if (p.IsRegEx()) { - SmileyLookup *dats = new SmileyLookup(p.GetTriggerText(), true, dist, GetFilename()); - if (dats->IsValid()) - m_SmileyLookup.insert(dats); - else - errorFound = true; - if (p.m_InsertText.IsEmpty()) - p.m_InsertText = p.m_ToolText; - } - else if (!p.IsService()) { - bool first = true; - const CMStringW &text = p.GetTriggerText(); - int iStart = 0; - while (true) { - CMStringW wszWord = text.Tokenize(L" \t", iStart); - if (iStart == -1) - break; - - ReplaceAllSpecials(wszWord, wszWord); - SmileyLookup *dats = new SmileyLookup(wszWord, false, dist, GetFilename()); - if (dats->IsValid()) { - m_SmileyLookup.insert(dats); - if (first) { - p.m_InsertText = wszWord; - first = false; - } - } - else delete dats; - } - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static MRegexp16 isCode(L"\\&\\#(\\d*)\\;"); - -static void replaceCodes(CMStringW &str) -{ - if (isCode.match(str) < 0) - return; - - str.Delete(isCode.getPos(), isCode.getLength()); - - uint32_t iCode = _wtoi(isCode.getGroup(1)); - wchar_t tmp[3] = { 0, 0, 0 }; - if (iCode < 0x10000) - tmp[0] = LOWORD(iCode), tmp[1] = HIWORD(iCode); - else { - iCode -= 0x10000; - tmp[0] = 0xD800 + (iCode >> 10); - tmp[1] = 0xDC00 + (iCode & 0x3FF); - } - str.Insert(isCode.getPos(), tmp); -} - -void SmileyPackType::ReplaceAllSpecials(const CMStringW &Input, CMStringW &Output) -{ - Output = Input; - Output.Replace(L"%%_%%", L" "); - Output.Replace(L"%%__%%", L" "); - Output.Replace(L"%%''%%", L"\""); - replaceCodes(Output); -} - -void SmileyPackType::Clear(void) -{ - m_SmileyList.destroy(); - m_SmileyLookup.destroy(); - if (m_hSmList != nullptr) { ImageList_Destroy(m_hSmList); m_hSmList = nullptr; } - m_Filename.Empty(); - m_Name.Empty(); - m_Date.Empty(); - m_Version.Empty(); - m_Author.Empty(); - m_VisibleCount = 0; - m_ButtonSmiley.Empty(); - errorFound = false; -} - -bool SmileyPackType::LoadSmileyFile(const CMStringW &filename, const CMStringW &packname, bool onlyInfo, bool noerr) -{ - Clear(); - - if (filename.IsEmpty()) { - m_Name = L"Nothing loaded"; - return false; - } - - wchar_t wszTmp[MAX_PATH]; - CMStringW modpath = VARSW(filename); - if (_waccess(modpath, 4) != 0) { - PathToAbsoluteW(filename, wszTmp, g_plugin.wszDefaultPath); - if (_waccess(wszTmp, 4) != 0) { - if (!noerr) { - static const wchar_t errmsg[] = LPGENW("Smiley pack %s for category \"%s\" not found.\nSelect correct smiley pack in the Options -> Customize -> Smileys."); - wchar_t msgtxt[1024]; - mir_snwprintf(msgtxt, TranslateW(errmsg), modpath.c_str(), packname.c_str()); - ReportError(msgtxt); - } - - m_Name = L"Nothing loaded"; - return false; - } - } - else PathToAbsoluteW(modpath, wszTmp, g_plugin.wszDefaultPath); - - modpath = wszTmp; - m_Filename = filename; - - // Load file - bool res; - if (filename.Find(L".xep") == -1) - res = LoadSmileyFileMSL(modpath, onlyInfo, modpath); - else - res = LoadSmileyFileXEP(modpath, onlyInfo); - - if (errorFound) - ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee network log for details.")); - - return res; -} - -static IStream* DecodeBase64Data(const char *pString) -{ - if (pString == nullptr) - return nullptr; - - size_t dataLen; - ptrA data((char*)mir_base64_decode(pString, &dataLen)); - if (data == nullptr) - return nullptr; - - // Read image list - HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, dataLen); - if (!hBuffer) - return nullptr; - - void *dst = GlobalLock(hBuffer); - memcpy(dst, data, dataLen); - GlobalUnlock(hBuffer); - - IStream *pStream = nullptr; - CreateStreamOnHGlobal(hBuffer, TRUE, &pStream); - return pStream; -} - -static CMStringW FilterQuotes(const char *pStr) -{ - CMStringW res(pStr); - int iStart = res.Find('\"', 0); - if (iStart != -1) { - int iEnd = res.Find('\"', ++iStart); - if (iEnd != -1) - res = res.Mid(iStart, iEnd - iStart); - } - - return res.Trim(); -} - -bool SmileyPackType::LoadSmileyFileXEP(const CMStringW &fileName, bool onlyInfo) -{ - FILE *in = _wfopen(fileName, L"rb"); - if (in == nullptr) - return false; - - TiXmlDocument doc; - int ret = doc.LoadFile(in); - fclose(in); - if (ret != 0) - return false; - - auto *pSettings = doc.FirstChildElement("settings"); - if (pSettings != nullptr) { - if (auto *pNode = pSettings->FirstChildElement("DataBaseName")) - m_Name = CMStringW(Utf2T(pNode->GetText())).Trim(); - - if (auto *pNode = pSettings->FirstChildElement("PackageAuthor")) - m_Author = CMStringW(Utf2T(pNode->GetText())).Trim(); - } - - if (!onlyInfo) { - auto *pImages = TiXmlConst(&doc)["lists"]["images"].ToElement(); - if (pImages) { - IStream *pStream = DecodeBase64Data(pImages->GetText()); - if (pStream) { - if (m_hSmList != nullptr) - ImageList_Destroy(m_hSmList); - m_hSmList = ImageList_Read(pStream); - pStream->Release(); - } - } - - for (auto *nRec : TiXmlFilter(doc.FirstChildElement("dataroot"), "record")) { - int idx = nRec->IntAttribute("ImageIndex", -1); - if (idx == -1) - continue; - - SmileyType *dat = new SmileyType; - dat->SetRegEx(true); - dat->SetImList(m_hSmList, idx); - dat->m_ToolText = FilterQuotes(nRec->GetText()); - - if (auto *pNode = nRec->FirstChildElement("Expression")) - dat->m_TriggerText = FilterQuotes(pNode->GetText()); - if (auto *pNode = nRec->FirstChildElement("PasteText")) - dat->m_InsertText = FilterQuotes(pNode->GetText()); - - dat->SetHidden(dat->m_InsertText.IsEmpty()); - - if (auto *pNode = nRec->FirstChildElement("Image")) { - IStream *pStream = DecodeBase64Data(pNode->GetText()); - if (pStream) { - dat->LoadFromImage(pStream); - pStream->Release(); - } - } - - m_SmileyList.insert(dat); - } - } - - m_VisibleCount = m_SmileyList.getCount(); - AddTriggersToSmileyLookup(); - - selec.x = selec.y = win.x = win.y = 0; - return true; -} - -bool SmileyPackType::LoadSmileyFileMSL(const CMStringW &filename, bool onlyInfo, CMStringW &modpath) -{ - int fh = _wopen(filename.c_str(), _O_BINARY | _O_RDONLY); - if (fh == -1) - return false; - - // Find file size - const long flen = _filelength(fh); - - // Allocate file buffer - char *buf = new char[flen + sizeof(wchar_t)]; - - // Read file in - int len = _read(fh, buf, flen); - *(wchar_t*)(buf + len) = 0; - - // Close file - _close(fh); - - CMStringW tbuf; - if (len > 2 && *(wchar_t*)buf == 0xfeff) - tbuf = ((wchar_t*)buf + 1); - else if (len > 3 && buf[0] == '\xef' && buf[1] == '\xbb' && buf[2] == '\xbf') - tbuf = _A2T(buf + 3, CP_UTF8); - else - tbuf = _A2T(buf); - - delete[] buf; - - CMStringW pathstr, packstr; - { - MRegexp16 pathsplit(L"(.*\\\\)(.*)\\.|$"); - pathsplit.match(modpath); - - pathstr = pathsplit.getGroup(1); - packstr = pathsplit.getGroup(2); - } - - if (!onlyInfo) - selec.x = selec.y = win.x = win.y = 0; - - int iStart = 0; - MRegexp16 otherf(L"^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\""); - MRegexp16 size(L"^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)"); - MRegexp16 smiley( - L"^\\s*Smiley(\\*)?\\s*=" // Is Hidden - L"(?:\\s*\"(.*)\")" // Smiley file name - L"(?:[\\s,]+(\\-?\\d+))" // Icon resource id - L"(?:[\\s,]+(R|S)?\"(.*?)\")" // Trigger text - L"(?:[\\s,]+\"(.*?)\")?" // Tooltip or insert text - L"(?:[\\s,]+\"(.*?)\")?"); // Tooltip text - - SmileyVectorType hiddenSmileys; - unsigned smnum = 0; - - while (true) { - CMStringW line = tbuf.Tokenize(L"\r\n", iStart); - if (iStart == -1) - break; - - if (line.IsEmpty() || line[0] == ';') - continue; - - if (otherf.match(line) >= 0) { - CMStringW key(otherf.getGroup(1)), value(otherf.getGroup(2)); - if (key == L"Name") - m_Name = value; - else if (key == L"Author") - m_Author = value; - else if (key == L"Date") - m_Date = value; - else if (key == L"Version") - m_Version = value; - else if (key == L"ButtonSmiley") - m_ButtonSmiley = value; - continue; - } - - if (onlyInfo) - continue; - - if (size.match(line) >= 0) { - POINT tpt; - tpt.x = _wtol(size.getGroup(2)); - tpt.y = _wtol(size.getGroup(3)); - - if (size.getGroup(1) == L"Selection") - selec = tpt; - else if (size.getGroup(1) == L"Window") - win = tpt; - continue; - } - - if (smiley.match(line)) { - CMStringW resname = smiley.getGroup(2); - if (resname.Find(L"http://") != -1) { - if (GetSmileyFile(resname, packstr)) - continue; - } - else if (!resname.IsEmpty()) - resname.Insert(0, pathstr); - - SmileyType *dat = new SmileyType; - - const int iconIndex = _wtol(smiley.getGroup(3)); - - dat->SetHidden(!smiley.getGroup(1).IsEmpty()); - - CMStringW wszGrp4(smiley.getGroup(4)); - if (!wszGrp4.IsEmpty()) { - dat->SetRegEx(wszGrp4 == L"R"); - dat->SetService(wszGrp4 == L"S"); - } - - dat->m_TriggerText = smiley.getGroup(5); - - CMStringW wszGrp6(smiley.getGroup(6)), wszGrp7(smiley.getGroup(7)); - if (dat->IsRegEx()) { - if (!wszGrp6.IsEmpty()) - ReplaceAllSpecials(wszGrp6, dat->m_InsertText); - - if (!wszGrp7.IsEmpty()) - ReplaceAllSpecials(wszGrp7, dat->m_ToolText); - else - dat->m_ToolText = dat->m_InsertText; - } - else { - if (!wszGrp6.IsEmpty()) - ReplaceAllSpecials(wszGrp6, dat->m_ToolText); - else - ReplaceAllSpecials(dat->m_TriggerText, dat->m_ToolText); - } - - bool noerr; - if (resname.IsEmpty()) { - dat->SetHidden(true); - dat->SetText(true); - noerr = true; - } - else noerr = dat->LoadFromResource(resname, iconIndex); - - if (dat->IsHidden()) - hiddenSmileys.insert(dat); - else - m_SmileyList.insert(dat); - - if (!noerr) { - static const wchar_t errmsg[] = LPGENW("Smiley #%u in file %s for smiley pack %s not found."); - wchar_t msgtxt[1024]; - mir_snwprintf(msgtxt, TranslateW(errmsg), smnum, resname.c_str(), modpath.c_str()); - Netlib_LogW(hNetlibUser, msgtxt); - errorFound = true; - } - smnum++; - } - } - - m_VisibleCount = m_SmileyList.getCount(); - m_SmileyList.splice(hiddenSmileys); - AddTriggersToSmileyLookup(); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// SmileyPackListType - -bool SmileyPackListType::AddSmileyPack(CMStringW &filename, CMStringW &packname) -{ - bool res = true; - if (GetSmileyPack(filename) == nullptr) { //not exist yet, so add - SmileyPackType *smileyPack = new SmileyPackType; - - res = smileyPack->LoadSmileyFile(filename, packname, FALSE); - if (res) - m_SmileyPacks.insert(smileyPack); - else - delete smileyPack; - } - return res; -} - -SmileyPackType* SmileyPackListType::GetSmileyPack(CMStringW &filename) -{ - CMStringW modpath = VARSW(filename); - - for (auto &it : m_SmileyPacks) { - CMStringW modpath1(VARSW(it->GetFilename())); - if (mir_wstrcmpi(modpath.c_str(), modpath1.c_str()) == 0) - return it; - } - return nullptr; -} - -void SmileyPackListType::ClearAndFreeAll() -{ - m_SmileyPacks.destroy(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// SmileyCategoryType - -SmileyCategoryType::SmileyCategoryType(SmileyPackListType *pSPS, const CMStringW &name, - const CMStringW &displayName, const CMStringW &defaultFilename, SmcType typ) -{ - m_pSmileyPackStore = pSPS; - type = typ; - m_Name = name; - m_DisplayName = displayName; - visible = true; - - opt.ReadPackFileName(m_Filename, m_Name, defaultFilename); -} - -void SmileyCategoryType::Load(void) -{ - bool bVisibleCat = opt.UsePhysProto ? !IsAcc() : !IsPhysProto(); - bool bVisible = opt.UseOneForAll ? !IsProto() : bVisibleCat; - if (bVisible && !m_Filename.IsEmpty()) { - bool loaded = m_pSmileyPackStore->AddSmileyPack(m_Filename, m_DisplayName); - if (!loaded) { - ClearFilename(); - SaveSettings(); - } - } -} - -SmileyPackType* SmileyCategoryType::GetSmileyPack(void) -{ - return m_pSmileyPackStore->GetSmileyPack(m_Filename); -} - -void SmileyCategoryType::SaveSettings(void) -{ - opt.WritePackFileName(m_Filename, m_Name); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// SmileyCategoryListType - -void SmileyCategoryListType::ClearAndLoadAll(void) -{ - m_pSmileyPackStore->ClearAndFreeAll(); - - for (auto &it : m_SmileyCategories) - it->Load(); -} - -SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const CMStringW &name) -{ - for (auto &it : m_SmileyCategories) - if (name.CompareNoCase(it->GetName()) == 0) - return it; - - return nullptr; -} - -SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index) -{ - return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : nullptr; -} - -SmileyPackType* SmileyCategoryListType::GetSmileyPack(const CMStringW &categoryname) -{ - SmileyCategoryType *smc = GetSmileyCategory(categoryname); - return smc != nullptr ? smc->GetSmileyPack() : nullptr; -} - -void SmileyCategoryListType::SaveSettings(void) -{ - CMStringW catstr; - for (auto &it : m_SmileyCategories) { - it->SaveSettings(); - if (it->IsCustom()) { - if (!catstr.IsEmpty()) - catstr += '#'; - catstr += it->GetName(); - } - } - opt.WriteCustomCategories(catstr); -} - -void SmileyCategoryListType::AddAndLoad(const CMStringW &name, const CMStringW &displayName) -{ - if (GetSmileyCategory(name) != nullptr) - return; - - AddCategory(name, displayName, smcExt); - - // Load only if other smileys have been loaded already - if (m_SmileyCategories.getCount() > 1) - m_SmileyCategories[m_SmileyCategories.getCount() - 1].Load(); -} - -void SmileyCategoryListType::AddCategory(const CMStringW &name, const CMStringW &displayName, SmcType typ, const CMStringW &defaultFilename) -{ - if (GetSmileyCategory(name) == nullptr) - m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name, displayName, defaultFilename, typ)); -} - -bool SmileyCategoryListType::DeleteCustomCategory(int index) -{ - if (index < m_SmileyCategories.getCount()) { - if (m_SmileyCategories[index].IsCustom()) { - m_SmileyCategories.remove(index); - return true; - } - } - return false; -} - -void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const CMStringW &defaultFile) -{ - if (acc->IsEnabled() && acc->szProtoName && IsSmileyProto(acc->szModuleName)) { - CMStringW displayName(acc->tszAccountName ? acc->tszAccountName : _A2T(acc->szModuleName)); - CMStringW PhysProtoName, paths; - DBVARIANT dbv; - - if (db_get_ws(0, acc->szModuleName, "AM_BaseProto", &dbv) == 0) { - PhysProtoName = L"AllProto"; - PhysProtoName += dbv.pwszVal; - db_free(&dbv); - } - - if (!PhysProtoName.IsEmpty()) - paths = g_SmileyCategories.GetSmileyCategory(PhysProtoName) ? g_SmileyCategories.GetSmileyCategory(PhysProtoName)->GetFilename() : L""; - - // assemble default path - if (paths.IsEmpty()) { - const char *packnam = acc->szProtoName; - if (mir_strcmp(packnam, "JABBER") == 0) - packnam = "JGMail"; - - wchar_t path[MAX_PATH]; - mir_snwprintf(path, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam); - if (_waccess(path, 0) != 0) - paths = defaultFile; - } - - CMStringW tname(_A2T(acc->szModuleName)); - AddCategory(tname, displayName, acc->bIsVirtual ? smcVirtualProto : smcProto, paths); - } -} - -void SmileyCategoryListType::AddProtoAsCategory(char *acc, const CMStringW &defaultFile) -{ - if (acc == nullptr) - return; - - const char *packnam = acc; - if (mir_strcmp(packnam, "JABBER") == 0) - packnam = "JGMail"; - - // assemble default path - CMStringW paths(FORMAT, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam); - paths = VARSW(paths); - if (_waccess(paths.c_str(), 0) != 0) - paths = defaultFile; - - CMStringW dName(acc), displayName; - displayName.AppendFormat(TranslateT("%s global smiley pack"), dName.GetBuffer()); - CMStringW tname("AllProto"); - tname += _A2T(acc); - AddCategory(tname, displayName, smcPhysProto, paths); -} - -void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc) -{ - CMStringW tname(_A2T(acc->szModuleName)); - - for (auto &hContact : Contacts()) { - char *proto = Proto_GetBaseAccountName(hContact); - if (proto == nullptr) - continue; - - DBVARIANT dbv; - if (!db_get_ws(hContact, proto, "Transport", &dbv)) { - bool found = (tname.CompareNoCase(dbv.pwszVal) == 0); - db_free(&dbv); - if (found) - return; - } - } - - for (auto &it : m_SmileyCategories) { - if (tname.CompareNoCase(it->GetName()) == 0) { - m_SmileyCategories.removeItem(&it); - break; - } - } -} - -void SmileyCategoryListType::AddContactTransportAsCategory(MCONTACT hContact, const CMStringW &defaultFile) -{ - char *proto = Proto_GetBaseAccountName(hContact); - if (proto == nullptr) - return; - - DBVARIANT dbv; - if (!db_get_ws(hContact, proto, "Transport", &dbv)) { - if (dbv.pwszVal[0] == '\0') { - db_free(&dbv); - return; - } - char *trsp = mir_strdup(_T2A(dbv.pwszVal)); - _strlwr(trsp); - - const char *packname = nullptr; - if (strstr(trsp, "icq") != nullptr) - packname = "icq"; - - mir_free(trsp); - - CMStringW paths, displayName(dbv.pwszVal); - if (packname != nullptr) { - paths.Format(L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packname); - paths = VARSW(paths); - if (_waccess(paths.c_str(), 0) != 0) - paths = defaultFile; - } - else paths = defaultFile; - - AddCategory(displayName, displayName, smcTransportProto, defaultFile); - - db_free(&dbv); - } -} - -void SmileyCategoryListType::AddAllProtocolsAsCategory(void) -{ - CMStringW displayName = TranslateT("Standard"); - CMStringW tname = L"Standard"; - AddCategory(tname, displayName, smcStd); - - const CMStringW &defaultFile = GetSmileyCategory(tname)->GetFilename(); - - PROTOCOLDESCRIPTOR **proto; - int protoCount = 0; - Proto_EnumProtocols(&protoCount, &proto); - - for (int i = 0; i < protoCount; i++) { - PROTOCOLDESCRIPTOR *pd = proto[i]; - if (pd->type == PROTOTYPE_PROTOWITHACCS) - AddProtoAsCategory(pd->szName, defaultFile); - } - - for (auto &pa : Accounts()) - AddAccountAsCategory(pa, defaultFile); - - for (auto &hContact : Contacts()) - AddContactTransportAsCategory(hContact, defaultFile); - - CMStringW cats; - opt.ReadCustomCategories(cats); - - int cppv = 0; - for (;;) { - int cp = cats.Find('#', cppv); - if (cp == -1) - break; - - displayName = cats.Mid(cppv, cp - cppv); - AddCategory(displayName, displayName, smcCustom, defaultFile); - cppv = cp + 1; - } - - if (cppv != cats.GetLength()) { - displayName = cats.Mid(cppv); - AddCategory(displayName, displayName, smcCustom, defaultFile); - } -} - -static const CMStringW testString(L"Test String"); - -SmileyLookup::SmileyLookup(const CMStringW &str, const bool regexs, const int ind, const CMStringW &smpt) -{ - m_ind = ind; - if (regexs) { - m_pattern.compile(str); - m_valid = m_pattern.isValid(); - if (!m_valid) { - wchar_t msgtxt[1024]; - mir_snwprintf(msgtxt, TranslateT("Regular expression \"%s\" in smiley pack \"%s\" malformed."), str.c_str(), smpt.c_str()); - Netlib_LogW(hNetlibUser, msgtxt); - } - } - else { - m_text = str; - replaceCodes(m_text); - m_valid = !str.IsEmpty(); - } -} - -SmileyLookup::~SmileyLookup() -{ -} - -void SmileyLookup::Find(const CMStringW &str, SmileyLocVecType &smlcur, bool firstOnly) -{ - if (!m_valid) return; - - if (m_text.IsEmpty()) { - while (m_pattern.nextMatch(str) >= 0) { - CMStringW wszMatch(m_pattern.getMatch()); - smlcur.insert(new SmileyLocType(m_pattern.getPos(), wszMatch.GetLength())); - if (firstOnly && m_ind != -1) - return; - } - } - else { - const wchar_t *pos = str.c_str(); - while ((pos = wcsstr(pos, m_text.c_str())) != nullptr) { - smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.GetLength())); - pos += m_text.GetLength(); - if (firstOnly && m_ind != -1) - return; - } - } -} +/*
+Miranda NG SmileyAdd Plugin
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
+Copyright (C) 2003-04 Rein-Peter de Boer
+
+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"
+
+SmileyPackListType g_SmileyPacks;
+SmileyCategoryListType g_SmileyCategories;
+
+static HWND hwndHidden = nullptr;
+
+static void CALLBACK timerProc(HWND, UINT, UINT_PTR param, DWORD)
+{
+ SmileyType *pType = (SmileyType*)param;
+ pType->MoveToNextFrame();
+}
+
+// these two functions must be called from the main thread
+static void CALLBACK sttStartTimer(PVOID obj)
+{
+ if (hwndHidden == nullptr)
+ hwndHidden = CreateWindowEx(0, L"STATIC", nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
+
+ SmileyType *pType = (SmileyType*)obj;
+ pType->SetFrameDelay();
+}
+
+static void CALLBACK sttStopTimer(PVOID obj)
+{
+ KillTimer(hwndHidden, (DWORD_PTR)obj);
+}
+
+//
+// SmileyType
+//
+
+SmileyType::SmileyType(void) :
+ m_arSmileys(10, PtrKeySortT)
+{
+ m_SmileyIcon = nullptr;
+ m_xepimg = nullptr;
+ m_flags = 0;
+ m_index = 0;
+ m_size.cx = 0;
+ m_size.cy = 0;
+}
+
+SmileyType::~SmileyType()
+{
+ if (m_xepimg) {
+ m_xepimg->Release();
+ m_xepimg = nullptr;
+ }
+
+ if (m_SmileyIcon != nullptr) {
+ DestroyIcon(m_SmileyIcon);
+ m_SmileyIcon = nullptr;
+ }
+}
+
+void SmileyType::AddObject(ISmileyBase *pObject)
+{
+ if (m_arSmileys.getCount() == 0) {
+ if (m_xepimg == nullptr)
+ m_xepimg = AddCacheImage(m_filepath, m_index);
+ CallFunctionAsync(sttStartTimer, this);
+ }
+
+ m_arSmileys.insert(pObject);
+}
+
+void SmileyType::RemoveObject(ISmileyBase *pObject)
+{
+ int idx = m_arSmileys.getIndex(pObject);
+ if (idx == -1)
+ return;
+
+ m_arSmileys.remove(idx);
+ if (m_arSmileys.getCount() == 0)
+ CallFunctionAsync(sttStopTimer, this);
+}
+
+void SmileyType::SetFrameDelay()
+{
+ int iFrameDelay = (m_xepimg == nullptr) ? 0 : m_xepimg->GetFrameDelay();
+ if (iFrameDelay <= 0)
+ KillTimer(hwndHidden, (DWORD_PTR)this);
+ else
+ SetTimer(hwndHidden, (DWORD_PTR)this, iFrameDelay*10, timerProc);
+}
+
+void SmileyType::MoveToNextFrame()
+{
+ m_index = m_xepimg->SelectNextFrame(m_index);
+
+ for (auto &it : m_arSmileys.rev_iter())
+ it->Draw();
+
+ SetFrameDelay(); // reset timer
+}
+
+HICON SmileyType::GetIcon(void)
+{
+ if (m_SmileyIcon == nullptr) {
+ ImageBase *img = CreateCachedImage();
+ if (!img)
+ return nullptr;
+
+ img->SelectFrame(m_index);
+ m_SmileyIcon = img->GetIcon();
+ img->Release();
+ }
+ return m_SmileyIcon;
+}
+
+HICON SmileyType::GetIconDup(void)
+{
+ if (ImageBase *img = CreateCachedImage()) {
+ img->SelectFrame(m_index);
+ HICON hIcon = img->GetIcon();
+ img->Release();
+ return hIcon;
+ }
+ return nullptr;
+}
+
+bool SmileyType::LoadFromImage(IStream *pStream)
+{
+ if (m_xepimg)
+ m_xepimg->Release();
+
+ CMStringW name;
+ m_xepimg = new ImageType(0, name, pStream);
+ return true;
+}
+
+bool SmileyType::LoadFromResource(const CMStringW &file, const int index)
+{
+ m_index = index;
+ m_filepath = file;
+ return true;
+}
+
+void SmileyType::GetSize(SIZE &size)
+{
+ if (m_size.cy == 0) {
+ ImageBase *img = CreateCachedImage();
+ if (img) {
+ img->GetSize(m_size);
+ img->Release();
+ }
+ }
+ size = m_size;
+}
+
+ImageBase* SmileyType::CreateCachedImage(void)
+{
+ if (m_xepimg) {
+ m_xepimg->AddRef();
+ return m_xepimg;
+ }
+ return AddCacheImage(m_filepath, m_index);
+}
+
+void SmileyType::SetImList(HIMAGELIST hImLst, long i)
+{
+ if (m_xepimg) m_xepimg->Release();
+ m_xepimg = new ImageListItemType(0, hImLst, i);
+}
+
+HBITMAP SmileyType::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY)
+{
+ ImageBase *img = CreateCachedImage();
+ if (!img) return nullptr;
+ img->SelectFrame(m_index);
+ HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY);
+ img->Release();
+
+ return hBmp;
+}
+
+//
+// SmileyPackType
+//
+
+SmileyPackType::SmileyPackType()
+{
+ m_hSmList = nullptr;
+ errorFound = false;
+}
+
+SmileyType* SmileyPackType::GetSmiley(unsigned index)
+{
+ return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : nullptr;
+}
+
+SmileyPackType::~SmileyPackType()
+{
+ if (m_hSmList != nullptr) ImageList_Destroy(m_hSmList);
+}
+
+static const wchar_t urlRegEx[] = L"(?:ftp|https|http|file|aim|webcal|irc|msnim|xmpp|gopher|mailto|news|nntp|telnet|wais|prospero)://?[\\w.?%:/$+;]*";
+static const wchar_t pathRegEx[] = L"[\\s\"][a-zA-Z]:[\\\\/][\\w.\\-\\\\/]*";
+static const wchar_t timeRegEx[] = L"\\d{1,2}:\\d{2}:\\d{2}|\\d{1,2}:\\d{2}";
+
+void SmileyPackType::AddTriggersToSmileyLookup(void)
+{
+ CMStringW emptystr;
+ m_SmileyLookup.insert(new SmileyLookup(urlRegEx, true, -1, emptystr));
+ m_SmileyLookup.insert(new SmileyLookup(pathRegEx, true, -1, emptystr));
+ m_SmileyLookup.insert(new SmileyLookup(timeRegEx, true, -1, emptystr));
+
+ for (int dist = 0; dist < m_SmileyList.getCount(); dist++) {
+ auto &p = m_SmileyList[dist];
+ if (p.IsRegEx()) {
+ SmileyLookup *dats = new SmileyLookup(p.GetTriggerText(), true, dist, GetFilename());
+ if (dats->IsValid())
+ m_SmileyLookup.insert(dats);
+ else
+ errorFound = true;
+ if (p.m_InsertText.IsEmpty())
+ p.m_InsertText = p.m_ToolText;
+ }
+ else if (!p.IsService()) {
+ bool first = true;
+ const CMStringW &text = p.GetTriggerText();
+ int iStart = 0;
+ while (true) {
+ CMStringW wszWord = text.Tokenize(L" \t", iStart);
+ if (iStart == -1)
+ break;
+
+ ReplaceAllSpecials(wszWord, wszWord);
+ SmileyLookup *dats = new SmileyLookup(wszWord, false, dist, GetFilename());
+ if (dats->IsValid()) {
+ m_SmileyLookup.insert(dats);
+ if (first) {
+ p.m_InsertText = wszWord;
+ first = false;
+ }
+ }
+ else delete dats;
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static MRegexp16 isCode(L"\\&\\#(\\d*)\\;");
+
+static void replaceCodes(CMStringW &str)
+{
+ if (isCode.match(str) < 0)
+ return;
+
+ str.Delete(isCode.getPos(), isCode.getLength());
+
+ uint32_t iCode = _wtoi(isCode.getGroup(1));
+ wchar_t tmp[3] = { 0, 0, 0 };
+ if (iCode < 0x10000)
+ tmp[0] = LOWORD(iCode), tmp[1] = HIWORD(iCode);
+ else {
+ iCode -= 0x10000;
+ tmp[0] = 0xD800 + (iCode >> 10);
+ tmp[1] = 0xDC00 + (iCode & 0x3FF);
+ }
+ str.Insert(isCode.getPos(), tmp);
+}
+
+void SmileyPackType::ReplaceAllSpecials(const CMStringW &Input, CMStringW &Output)
+{
+ Output = Input;
+ Output.Replace(L"%%_%%", L" ");
+ Output.Replace(L"%%__%%", L" ");
+ Output.Replace(L"%%''%%", L"\"");
+ replaceCodes(Output);
+}
+
+void SmileyPackType::Clear(void)
+{
+ m_SmileyList.destroy();
+ m_SmileyLookup.destroy();
+ if (m_hSmList != nullptr) { ImageList_Destroy(m_hSmList); m_hSmList = nullptr; }
+ m_Filename.Empty();
+ m_Name.Empty();
+ m_Date.Empty();
+ m_Version.Empty();
+ m_Author.Empty();
+ m_VisibleCount = 0;
+ m_ButtonSmiley.Empty();
+ errorFound = false;
+}
+
+bool SmileyPackType::LoadSmileyFile(const CMStringW &filename, const CMStringW &packname, bool onlyInfo, bool noerr)
+{
+ Clear();
+
+ if (filename.IsEmpty()) {
+ m_Name = L"Nothing loaded";
+ return false;
+ }
+
+ wchar_t wszTmp[MAX_PATH];
+ CMStringW modpath = VARSW(filename);
+ if (_waccess(modpath, 4) != 0) {
+ PathToAbsoluteW(filename, wszTmp, g_plugin.wszDefaultPath);
+ if (_waccess(wszTmp, 4) != 0) {
+ if (!noerr) {
+ static const wchar_t errmsg[] = LPGENW("Smiley pack %s for category \"%s\" not found.\nSelect correct smiley pack in the Options -> Customize -> Smileys.");
+ wchar_t msgtxt[1024];
+ mir_snwprintf(msgtxt, TranslateW(errmsg), modpath.c_str(), packname.c_str());
+ ReportError(msgtxt);
+ }
+
+ m_Name = L"Nothing loaded";
+ return false;
+ }
+ }
+ else PathToAbsoluteW(modpath, wszTmp, g_plugin.wszDefaultPath);
+
+ modpath = wszTmp;
+ m_Filename = filename;
+
+ // Load file
+ bool res;
+ if (filename.Find(L".xep") == -1)
+ res = LoadSmileyFileMSL(modpath, onlyInfo, modpath);
+ else
+ res = LoadSmileyFileXEP(modpath, onlyInfo);
+
+ if (errorFound)
+ ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee network log for details."));
+
+ return res;
+}
+
+static IStream* DecodeBase64Data(const char *pString)
+{
+ if (pString == nullptr)
+ return nullptr;
+
+ size_t dataLen;
+ ptrA data((char*)mir_base64_decode(pString, &dataLen));
+ if (data == nullptr)
+ return nullptr;
+
+ // Read image list
+ HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, dataLen);
+ if (!hBuffer)
+ return nullptr;
+
+ void *dst = GlobalLock(hBuffer);
+ memcpy(dst, data, dataLen);
+ GlobalUnlock(hBuffer);
+
+ IStream *pStream = nullptr;
+ CreateStreamOnHGlobal(hBuffer, TRUE, &pStream);
+ return pStream;
+}
+
+static CMStringW FilterQuotes(const char *pStr)
+{
+ CMStringW res(pStr);
+ int iStart = res.Find('\"', 0);
+ if (iStart != -1) {
+ int iEnd = res.Find('\"', ++iStart);
+ if (iEnd != -1)
+ res = res.Mid(iStart, iEnd - iStart);
+ }
+
+ return res.Trim();
+}
+
+bool SmileyPackType::LoadSmileyFileXEP(const CMStringW &fileName, bool onlyInfo)
+{
+ FILE *in = _wfopen(fileName, L"rb");
+ if (in == nullptr)
+ return false;
+
+ TiXmlDocument doc;
+ int ret = doc.LoadFile(in);
+ fclose(in);
+ if (ret != 0)
+ return false;
+
+ auto *pSettings = doc.FirstChildElement("settings");
+ if (pSettings != nullptr) {
+ if (auto *pNode = pSettings->FirstChildElement("DataBaseName"))
+ m_Name = CMStringW(Utf2T(pNode->GetText())).Trim();
+
+ if (auto *pNode = pSettings->FirstChildElement("PackageAuthor"))
+ m_Author = CMStringW(Utf2T(pNode->GetText())).Trim();
+ }
+
+ if (!onlyInfo) {
+ auto *pImages = TiXmlConst(&doc)["lists"]["images"].ToElement();
+ if (pImages) {
+ IStream *pStream = DecodeBase64Data(pImages->GetText());
+ if (pStream) {
+ if (m_hSmList != nullptr)
+ ImageList_Destroy(m_hSmList);
+ m_hSmList = ImageList_Read(pStream);
+ pStream->Release();
+ }
+ }
+
+ for (auto *nRec : TiXmlFilter(doc.FirstChildElement("dataroot"), "record")) {
+ int idx = nRec->IntAttribute("ImageIndex", -1);
+ if (idx == -1)
+ continue;
+
+ SmileyType *dat = new SmileyType;
+ dat->SetRegEx(true);
+ dat->SetImList(m_hSmList, idx);
+ dat->m_ToolText = FilterQuotes(nRec->GetText());
+
+ if (auto *pNode = nRec->FirstChildElement("Expression"))
+ dat->m_TriggerText = FilterQuotes(pNode->GetText());
+ if (auto *pNode = nRec->FirstChildElement("PasteText"))
+ dat->m_InsertText = FilterQuotes(pNode->GetText());
+
+ dat->SetHidden(dat->m_InsertText.IsEmpty());
+
+ if (auto *pNode = nRec->FirstChildElement("Image")) {
+ IStream *pStream = DecodeBase64Data(pNode->GetText());
+ if (pStream) {
+ dat->LoadFromImage(pStream);
+ pStream->Release();
+ }
+ }
+
+ m_SmileyList.insert(dat);
+ }
+ }
+
+ m_VisibleCount = m_SmileyList.getCount();
+ AddTriggersToSmileyLookup();
+
+ selec.x = selec.y = win.x = win.y = 0;
+ return true;
+}
+
+bool SmileyPackType::LoadSmileyFileMSL(const CMStringW &filename, bool onlyInfo, CMStringW &modpath)
+{
+ int fh = _wopen(filename.c_str(), _O_BINARY | _O_RDONLY);
+ if (fh == -1)
+ return false;
+
+ // Find file size
+ const long flen = _filelength(fh);
+
+ // Allocate file buffer
+ char *buf = new char[flen + sizeof(wchar_t)];
+
+ // Read file in
+ int len = _read(fh, buf, flen);
+ *(wchar_t*)(buf + len) = 0;
+
+ // Close file
+ _close(fh);
+
+ CMStringW tbuf;
+ if (len > 2 && *(wchar_t*)buf == 0xfeff)
+ tbuf = ((wchar_t*)buf + 1);
+ else if (len > 3 && buf[0] == '\xef' && buf[1] == '\xbb' && buf[2] == '\xbf')
+ tbuf = _A2T(buf + 3, CP_UTF8);
+ else
+ tbuf = _A2T(buf);
+
+ delete[] buf;
+
+ CMStringW pathstr, packstr;
+ {
+ MRegexp16 pathsplit(L"(.*\\\\)(.*)\\.|$");
+ pathsplit.match(modpath);
+
+ pathstr = pathsplit.getGroup(1);
+ packstr = pathsplit.getGroup(2);
+ }
+
+ if (!onlyInfo)
+ selec.x = selec.y = win.x = win.y = 0;
+
+ int iStart = 0;
+ MRegexp16 otherf(L"^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\"");
+ MRegexp16 size(L"^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)");
+ MRegexp16 smiley(
+ L"^\\s*Smiley(\\*)?\\s*=" // Is Hidden
+ L"(?:\\s*\"(.*)\")" // Smiley file name
+ L"(?:[\\s,]+(\\-?\\d+))" // Icon resource id
+ L"(?:[\\s,]+(R|S)?\"(.*?)\")" // Trigger text
+ L"(?:[\\s,]+\"(.*?)\")?" // Tooltip or insert text
+ L"(?:[\\s,]+\"(.*?)\")?"); // Tooltip text
+
+ SmileyVectorType hiddenSmileys;
+ unsigned smnum = 0;
+
+ while (true) {
+ CMStringW line = tbuf.Tokenize(L"\r\n", iStart);
+ if (iStart == -1)
+ break;
+
+ if (line.IsEmpty() || line[0] == ';')
+ continue;
+
+ if (otherf.match(line) >= 0) {
+ CMStringW key(otherf.getGroup(1)), value(otherf.getGroup(2));
+ if (key == L"Name")
+ m_Name = value;
+ else if (key == L"Author")
+ m_Author = value;
+ else if (key == L"Date")
+ m_Date = value;
+ else if (key == L"Version")
+ m_Version = value;
+ else if (key == L"ButtonSmiley")
+ m_ButtonSmiley = value;
+ continue;
+ }
+
+ if (onlyInfo)
+ continue;
+
+ if (size.match(line) >= 0) {
+ POINT tpt;
+ tpt.x = _wtol(size.getGroup(2));
+ tpt.y = _wtol(size.getGroup(3));
+
+ if (size.getGroup(1) == L"Selection")
+ selec = tpt;
+ else if (size.getGroup(1) == L"Window")
+ win = tpt;
+ continue;
+ }
+
+ if (smiley.match(line)) {
+ CMStringW resname = smiley.getGroup(2);
+ if (resname.Find(L"http://") != -1) {
+ if (GetSmileyFile(resname, packstr))
+ continue;
+ }
+ else if (!resname.IsEmpty())
+ resname.Insert(0, pathstr);
+
+ SmileyType *dat = new SmileyType;
+
+ const int iconIndex = _wtol(smiley.getGroup(3));
+
+ dat->SetHidden(!smiley.getGroup(1).IsEmpty());
+
+ CMStringW wszGrp4(smiley.getGroup(4));
+ if (!wszGrp4.IsEmpty()) {
+ dat->SetRegEx(wszGrp4 == L"R");
+ dat->SetService(wszGrp4 == L"S");
+ }
+
+ dat->m_TriggerText = smiley.getGroup(5);
+
+ CMStringW wszGrp6(smiley.getGroup(6)), wszGrp7(smiley.getGroup(7));
+ if (dat->IsRegEx()) {
+ if (!wszGrp6.IsEmpty())
+ ReplaceAllSpecials(wszGrp6, dat->m_InsertText);
+
+ if (!wszGrp7.IsEmpty())
+ ReplaceAllSpecials(wszGrp7, dat->m_ToolText);
+ else
+ dat->m_ToolText = dat->m_InsertText;
+ }
+ else {
+ if (!wszGrp6.IsEmpty())
+ ReplaceAllSpecials(wszGrp6, dat->m_ToolText);
+ else
+ ReplaceAllSpecials(dat->m_TriggerText, dat->m_ToolText);
+ }
+
+ bool noerr;
+ if (resname.IsEmpty()) {
+ dat->SetHidden(true);
+ dat->SetText(true);
+ noerr = true;
+ }
+ else noerr = dat->LoadFromResource(resname, iconIndex);
+
+ if (dat->IsHidden())
+ hiddenSmileys.insert(dat);
+ else
+ m_SmileyList.insert(dat);
+
+ if (!noerr) {
+ static const wchar_t errmsg[] = LPGENW("Smiley #%u in file %s for smiley pack %s not found.");
+ wchar_t msgtxt[1024];
+ mir_snwprintf(msgtxt, TranslateW(errmsg), smnum, resname.c_str(), modpath.c_str());
+ Netlib_LogW(hNetlibUser, msgtxt);
+ errorFound = true;
+ }
+ smnum++;
+ }
+ }
+
+ m_VisibleCount = m_SmileyList.getCount();
+ m_SmileyList.splice(hiddenSmileys);
+ AddTriggersToSmileyLookup();
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SmileyPackListType
+
+bool SmileyPackListType::AddSmileyPack(CMStringW &filename, CMStringW &packname)
+{
+ bool res = true;
+ if (GetSmileyPack(filename) == nullptr) { //not exist yet, so add
+ SmileyPackType *smileyPack = new SmileyPackType;
+
+ res = smileyPack->LoadSmileyFile(filename, packname, FALSE);
+ if (res)
+ m_SmileyPacks.insert(smileyPack);
+ else
+ delete smileyPack;
+ }
+ return res;
+}
+
+SmileyPackType* SmileyPackListType::GetSmileyPack(CMStringW &filename)
+{
+ CMStringW modpath = VARSW(filename);
+
+ for (auto &it : m_SmileyPacks) {
+ CMStringW modpath1(VARSW(it->GetFilename()));
+ if (mir_wstrcmpi(modpath.c_str(), modpath1.c_str()) == 0)
+ return it;
+ }
+ return nullptr;
+}
+
+void SmileyPackListType::ClearAndFreeAll()
+{
+ m_SmileyPacks.destroy();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SmileyCategoryType
+
+SmileyCategoryType::SmileyCategoryType(SmileyPackListType *pSPS, const CMStringW &name,
+ const CMStringW &displayName, const CMStringW &defaultFilename, SmcType typ)
+{
+ m_pSmileyPackStore = pSPS;
+ type = typ;
+ m_Name = name;
+ m_DisplayName = displayName;
+ visible = true;
+
+ opt.ReadPackFileName(m_Filename, m_Name, defaultFilename);
+}
+
+void SmileyCategoryType::Load(void)
+{
+ bool bVisibleCat = opt.UsePhysProto ? !IsAcc() : !IsPhysProto();
+ bool bVisible = opt.UseOneForAll ? !IsProto() : bVisibleCat;
+ if (bVisible && !m_Filename.IsEmpty()) {
+ bool loaded = m_pSmileyPackStore->AddSmileyPack(m_Filename, m_DisplayName);
+ if (!loaded) {
+ ClearFilename();
+ SaveSettings();
+ }
+ }
+}
+
+SmileyPackType* SmileyCategoryType::GetSmileyPack(void)
+{
+ return m_pSmileyPackStore->GetSmileyPack(m_Filename);
+}
+
+void SmileyCategoryType::SaveSettings(void)
+{
+ opt.WritePackFileName(m_Filename, m_Name);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SmileyCategoryListType
+
+void SmileyCategoryListType::ClearAndLoadAll(void)
+{
+ m_pSmileyPackStore->ClearAndFreeAll();
+
+ for (auto &it : m_SmileyCategories)
+ it->Load();
+}
+
+SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const CMStringW &name)
+{
+ for (auto &it : m_SmileyCategories)
+ if (name.CompareNoCase(it->GetName()) == 0)
+ return it;
+
+ return nullptr;
+}
+
+SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index)
+{
+ return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : nullptr;
+}
+
+SmileyPackType* SmileyCategoryListType::GetSmileyPack(const CMStringW &categoryname)
+{
+ SmileyCategoryType *smc = GetSmileyCategory(categoryname);
+ return smc != nullptr ? smc->GetSmileyPack() : nullptr;
+}
+
+void SmileyCategoryListType::SaveSettings(void)
+{
+ CMStringW catstr;
+ for (auto &it : m_SmileyCategories) {
+ it->SaveSettings();
+ if (it->IsCustom()) {
+ if (!catstr.IsEmpty())
+ catstr += '#';
+ catstr += it->GetName();
+ }
+ }
+ opt.WriteCustomCategories(catstr);
+}
+
+void SmileyCategoryListType::AddAndLoad(const CMStringW &name, const CMStringW &displayName)
+{
+ if (GetSmileyCategory(name) != nullptr)
+ return;
+
+ AddCategory(name, displayName, smcExt);
+
+ // Load only if other smileys have been loaded already
+ if (m_SmileyCategories.getCount() > 1)
+ m_SmileyCategories[m_SmileyCategories.getCount() - 1].Load();
+}
+
+void SmileyCategoryListType::AddCategory(const CMStringW &name, const CMStringW &displayName, SmcType typ, const CMStringW &defaultFilename)
+{
+ if (GetSmileyCategory(name) == nullptr)
+ m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name, displayName, defaultFilename, typ));
+}
+
+bool SmileyCategoryListType::DeleteCustomCategory(int index)
+{
+ if (index < m_SmileyCategories.getCount()) {
+ if (m_SmileyCategories[index].IsCustom()) {
+ m_SmileyCategories.remove(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const CMStringW &defaultFile)
+{
+ if (acc->IsEnabled() && acc->szProtoName && IsSmileyProto(acc->szModuleName)) {
+ CMStringW displayName(acc->tszAccountName ? acc->tszAccountName : _A2T(acc->szModuleName));
+ CMStringW PhysProtoName, paths;
+ DBVARIANT dbv;
+
+ if (db_get_ws(0, acc->szModuleName, "AM_BaseProto", &dbv) == 0) {
+ PhysProtoName = L"AllProto";
+ PhysProtoName += dbv.pwszVal;
+ db_free(&dbv);
+ }
+
+ if (!PhysProtoName.IsEmpty())
+ paths = g_SmileyCategories.GetSmileyCategory(PhysProtoName) ? g_SmileyCategories.GetSmileyCategory(PhysProtoName)->GetFilename() : L"";
+
+ // assemble default path
+ if (paths.IsEmpty()) {
+ const char *packnam = acc->szProtoName;
+ if (mir_strcmp(packnam, "JABBER") == 0)
+ packnam = "JGMail";
+
+ wchar_t path[MAX_PATH];
+ mir_snwprintf(path, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
+ if (_waccess(path, 0) != 0)
+ paths = defaultFile;
+ }
+
+ CMStringW tname(_A2T(acc->szModuleName));
+ AddCategory(tname, displayName, acc->bIsVirtual ? smcVirtualProto : smcProto, paths);
+ }
+}
+
+void SmileyCategoryListType::AddProtoAsCategory(char *acc, const CMStringW &defaultFile)
+{
+ if (acc == nullptr)
+ return;
+
+ const char *packnam = acc;
+ if (mir_strcmp(packnam, "JABBER") == 0)
+ packnam = "JGMail";
+
+ // assemble default path
+ CMStringW paths(FORMAT, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
+ paths = VARSW(paths);
+ if (_waccess(paths.c_str(), 0) != 0)
+ paths = defaultFile;
+
+ CMStringW dName(acc), displayName;
+ displayName.AppendFormat(TranslateT("%s global smiley pack"), dName.GetBuffer());
+ CMStringW tname("AllProto");
+ tname += _A2T(acc);
+ AddCategory(tname, displayName, smcPhysProto, paths);
+}
+
+void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc)
+{
+ CMStringW tname(_A2T(acc->szModuleName));
+
+ for (auto &hContact : Contacts()) {
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (proto == nullptr)
+ continue;
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
+ bool found = (tname.CompareNoCase(dbv.pwszVal) == 0);
+ db_free(&dbv);
+ if (found)
+ return;
+ }
+ }
+
+ for (auto &it : m_SmileyCategories) {
+ if (tname.CompareNoCase(it->GetName()) == 0) {
+ m_SmileyCategories.removeItem(&it);
+ break;
+ }
+ }
+}
+
+void SmileyCategoryListType::AddContactTransportAsCategory(MCONTACT hContact, const CMStringW &defaultFile)
+{
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (proto == nullptr)
+ return;
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
+ if (dbv.pwszVal[0] == '\0') {
+ db_free(&dbv);
+ return;
+ }
+ char *trsp = mir_strdup(_T2A(dbv.pwszVal));
+ _strlwr(trsp);
+
+ const char *packname = nullptr;
+ if (strstr(trsp, "icq") != nullptr)
+ packname = "icq";
+
+ mir_free(trsp);
+
+ CMStringW paths, displayName(dbv.pwszVal);
+ if (packname != nullptr) {
+ paths.Format(L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packname);
+ paths = VARSW(paths);
+ if (_waccess(paths.c_str(), 0) != 0)
+ paths = defaultFile;
+ }
+ else paths = defaultFile;
+
+ AddCategory(displayName, displayName, smcTransportProto, defaultFile);
+
+ db_free(&dbv);
+ }
+}
+
+void SmileyCategoryListType::AddAllProtocolsAsCategory(void)
+{
+ CMStringW displayName = TranslateT("Standard");
+ CMStringW tname = L"Standard";
+ AddCategory(tname, displayName, smcStd);
+
+ const CMStringW &defaultFile = GetSmileyCategory(tname)->GetFilename();
+
+ PROTOCOLDESCRIPTOR **proto;
+ int protoCount = 0;
+ Proto_EnumProtocols(&protoCount, &proto);
+
+ for (int i = 0; i < protoCount; i++) {
+ PROTOCOLDESCRIPTOR *pd = proto[i];
+ if (pd->type == PROTOTYPE_PROTOWITHACCS)
+ AddProtoAsCategory(pd->szName, defaultFile);
+ }
+
+ for (auto &pa : Accounts())
+ AddAccountAsCategory(pa, defaultFile);
+
+ for (auto &hContact : Contacts())
+ AddContactTransportAsCategory(hContact, defaultFile);
+
+ CMStringW cats;
+ opt.ReadCustomCategories(cats);
+
+ int cppv = 0;
+ for (;;) {
+ int cp = cats.Find('#', cppv);
+ if (cp == -1)
+ break;
+
+ displayName = cats.Mid(cppv, cp - cppv);
+ AddCategory(displayName, displayName, smcCustom, defaultFile);
+ cppv = cp + 1;
+ }
+
+ if (cppv != cats.GetLength()) {
+ displayName = cats.Mid(cppv);
+ AddCategory(displayName, displayName, smcCustom, defaultFile);
+ }
+}
+
+static const CMStringW testString(L"Test String");
+
+SmileyLookup::SmileyLookup(const CMStringW &str, const bool regexs, const int ind, const CMStringW &smpt)
+{
+ m_ind = ind;
+ if (regexs) {
+ m_pattern.compile(str);
+ m_valid = m_pattern.isValid();
+ if (!m_valid) {
+ wchar_t msgtxt[1024];
+ mir_snwprintf(msgtxt, TranslateT("Regular expression \"%s\" in smiley pack \"%s\" malformed."), str.c_str(), smpt.c_str());
+ Netlib_LogW(hNetlibUser, msgtxt);
+ }
+ }
+ else {
+ m_text = str;
+ replaceCodes(m_text);
+ m_valid = !str.IsEmpty();
+ }
+}
+
+SmileyLookup::~SmileyLookup()
+{
+}
+
+void SmileyLookup::Find(const CMStringW &str, SmileyLocVecType &smlcur, bool firstOnly)
+{
+ if (!m_valid) return;
+
+ if (m_text.IsEmpty()) {
+ while (m_pattern.nextMatch(str) >= 0) {
+ CMStringW wszMatch(m_pattern.getMatch());
+ smlcur.insert(new SmileyLocType(m_pattern.getPos(), wszMatch.GetLength()));
+ if (firstOnly && m_ind != -1)
+ return;
+ }
+ }
+ else {
+ const wchar_t *pos = str.c_str();
+ while ((pos = wcsstr(pos, m_text.c_str())) != nullptr) {
+ smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.GetLength()));
+ pos += m_text.GetLength();
+ if (firstOnly && m_ind != -1)
+ return;
+ }
+ }
+}
diff --git a/plugins/SmileyAdd/src/smileys.h b/plugins/SmileyAdd/src/smileys.h index b6f793ac45..da80c4b924 100644 --- a/plugins/SmileyAdd/src/smileys.h +++ b/plugins/SmileyAdd/src/smileys.h @@ -1,6 +1,6 @@ /*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/stdafx.cxx b/plugins/SmileyAdd/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SmileyAdd/src/stdafx.cxx +++ b/plugins/SmileyAdd/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SmileyAdd/src/version.h b/plugins/SmileyAdd/src/version.h index 354db970ef..b5cf594032 100644 --- a/plugins/SmileyAdd/src/version.h +++ b/plugins/SmileyAdd/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Smiley support for Miranda NG."
#define __AUTHOR "Peacow, nightwish, bid, borkra"
#define __AUTHORWEB "https://miranda-ng.org/p/SmileyAdd"
-#define __COPYRIGHT "© 2012-22 Miranda NG team, 2004-12 Boris Krasnovskiy, portions by Rein-Peter de Boer"
+#define __COPYRIGHT "© 2012-23 Miranda NG team, 2004-12 Boris Krasnovskiy, portions by Rein-Peter de Boer"
diff --git a/plugins/Spamotron/src/stdafx.cxx b/plugins/Spamotron/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Spamotron/src/stdafx.cxx +++ b/plugins/Spamotron/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SpellChecker/src/stdafx.cxx b/plugins/SpellChecker/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SpellChecker/src/stdafx.cxx +++ b/plugins/SpellChecker/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/SplashScreen/src/stdafx.cxx b/plugins/SplashScreen/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/SplashScreen/src/stdafx.cxx +++ b/plugins/SplashScreen/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StartPosition/src/main.cpp b/plugins/StartPosition/src/main.cpp index f6a3eddb6b..d9c5014c36 100644 --- a/plugins/StartPosition/src/main.cpp +++ b/plugins/StartPosition/src/main.cpp @@ -5,7 +5,7 @@ Copyright (C) 2005-2008 Felipe Brahm - souFrag ICQ#50566818
http://www.soufrag.cl
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StartPosition/src/options.h b/plugins/StartPosition/src/options.h index c8b85af8fe..e9311851df 100644 --- a/plugins/StartPosition/src/options.h +++ b/plugins/StartPosition/src/options.h @@ -1,93 +1,93 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - -#pragma once - -#include "stdafx.h" - -enum ClistAlign : uint8_t -{ - left, - right -}; - -enum ClistState : uint8_t -{ - hidden, - minimized, - normal -}; - -struct ClistOptions -{ - CMOption<uint8_t> isDocked; - CMOption<uint8_t> state; - - CMOption<uint32_t> x; - CMOption<uint32_t> y; - CMOption<uint32_t> width; - CMOption<uint32_t> height; - - ClistOptions() : - isDocked(CLIST_MODULE_NAME, "Docked", 0), - state(CLIST_MODULE_NAME, "State", ClistState::normal), - x(CLIST_MODULE_NAME, "x", 0), - y(CLIST_MODULE_NAME, "y", 0), - width(CLIST_MODULE_NAME, "Width", 150), - height(CLIST_MODULE_NAME, "Height", 350) - { } -}; - -struct StartPositionOptions -{ - CMOption<uint8_t> setTopPosition; - CMOption<uint8_t> setBottomPosition; - CMOption<uint8_t> setSidePosition; - CMOption<uint8_t> clistAlign; - CMOption<uint8_t> setClistWidth; - CMOption<uint8_t> setClistStartState; - CMOption<uint8_t> clistState; - - CMOption<uint32_t> pixelsFromTop; - CMOption<uint32_t> pixelsFromBottom; - CMOption<uint32_t> pixelsFromSide; - CMOption<uint32_t> clistWidth; - - StartPositionOptions(); -}; - -class COptionsDlg : public CDlgBase -{ - CCtrlCheck chkPositionTop, chkPositionBottom, chkPositionSide, chkFromLeft, chkFromRight, chkWidth; - CCtrlEdit edtPositionTop, edtPositionBottom, edtPositionSide, edtWidth; - CCtrlCheck chkStartState, chkStartHidden, chkStartNormal; - -public: - COptionsDlg(); - - bool OnInitDialog() override; - bool OnApply() override; - -private: - void removeOldSettings(); - - void onCheck_PositionTop(CCtrlCheck*); - void onCheck_PositionBottom(CCtrlCheck*); - void onCheck_PositionSide(CCtrlCheck*); - void onCheck_Width(CCtrlCheck*); - void onCheck_StartState(CCtrlCheck*); -}; +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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/>.
+*/
+
+#pragma once
+
+#include "stdafx.h"
+
+enum ClistAlign : uint8_t
+{
+ left,
+ right
+};
+
+enum ClistState : uint8_t
+{
+ hidden,
+ minimized,
+ normal
+};
+
+struct ClistOptions
+{
+ CMOption<uint8_t> isDocked;
+ CMOption<uint8_t> state;
+
+ CMOption<uint32_t> x;
+ CMOption<uint32_t> y;
+ CMOption<uint32_t> width;
+ CMOption<uint32_t> height;
+
+ ClistOptions() :
+ isDocked(CLIST_MODULE_NAME, "Docked", 0),
+ state(CLIST_MODULE_NAME, "State", ClistState::normal),
+ x(CLIST_MODULE_NAME, "x", 0),
+ y(CLIST_MODULE_NAME, "y", 0),
+ width(CLIST_MODULE_NAME, "Width", 150),
+ height(CLIST_MODULE_NAME, "Height", 350)
+ { }
+};
+
+struct StartPositionOptions
+{
+ CMOption<uint8_t> setTopPosition;
+ CMOption<uint8_t> setBottomPosition;
+ CMOption<uint8_t> setSidePosition;
+ CMOption<uint8_t> clistAlign;
+ CMOption<uint8_t> setClistWidth;
+ CMOption<uint8_t> setClistStartState;
+ CMOption<uint8_t> clistState;
+
+ CMOption<uint32_t> pixelsFromTop;
+ CMOption<uint32_t> pixelsFromBottom;
+ CMOption<uint32_t> pixelsFromSide;
+ CMOption<uint32_t> clistWidth;
+
+ StartPositionOptions();
+};
+
+class COptionsDlg : public CDlgBase
+{
+ CCtrlCheck chkPositionTop, chkPositionBottom, chkPositionSide, chkFromLeft, chkFromRight, chkWidth;
+ CCtrlEdit edtPositionTop, edtPositionBottom, edtPositionSide, edtWidth;
+ CCtrlCheck chkStartState, chkStartHidden, chkStartNormal;
+
+public:
+ COptionsDlg();
+
+ bool OnInitDialog() override;
+ bool OnApply() override;
+
+private:
+ void removeOldSettings();
+
+ void onCheck_PositionTop(CCtrlCheck*);
+ void onCheck_PositionBottom(CCtrlCheck*);
+ void onCheck_PositionSide(CCtrlCheck*);
+ void onCheck_Width(CCtrlCheck*);
+ void onCheck_StartState(CCtrlCheck*);
+};
diff --git a/plugins/StartPosition/src/startposition.h b/plugins/StartPosition/src/startposition.h index 09e652aa5c..75c159e972 100644 --- a/plugins/StartPosition/src/startposition.h +++ b/plugins/StartPosition/src/startposition.h @@ -1,34 +1,34 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - -#pragma once - -#include "stdafx.h" - -struct CMPlugin : public PLUGIN<CMPlugin> -{ - CMPlugin(); - - int Load() override; - - void Init(); - - int __cdecl OnOptionsInit(WPARAM, LPARAM); - void positionClist(); - - StartPositionOptions spOptions; -}; +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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/>.
+*/
+
+#pragma once
+
+#include "stdafx.h"
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+
+ void Init();
+
+ int __cdecl OnOptionsInit(WPARAM, LPARAM);
+ void positionClist();
+
+ StartPositionOptions spOptions;
+};
diff --git a/plugins/StartPosition/src/stdafx.cxx b/plugins/StartPosition/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/StartPosition/src/stdafx.cxx +++ b/plugins/StartPosition/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StartPosition/src/stdafx.h b/plugins/StartPosition/src/stdafx.h index 5c7e573b7f..6dbc8d5f9a 100644 --- a/plugins/StartPosition/src/stdafx.h +++ b/plugins/StartPosition/src/stdafx.h @@ -5,7 +5,7 @@ Copyright (C) 2005-2008 Felipe Brahm - souFrag ICQ#50566818
http://www.soufrag.cl
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StartPosition/src/version.h b/plugins/StartPosition/src/version.h index c1b334483d..7eb14073ef 100644 --- a/plugins/StartPosition/src/version.h +++ b/plugins/StartPosition/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Plugin supports starting position of Contact List relative to the taskbar and screen side."
#define __AUTHOR "Felipe Brahm - souFrag"
#define __AUTHORWEB "https://miranda-ng.org/p/StartPosition"
-#define __COPYRIGHT "© 2005-2008 Felipe Brahm - souFrag, © 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2005-2008 Felipe Brahm - souFrag, © 2012-23 Miranda NG team"
diff --git a/plugins/StartupSilence/src/main.cpp b/plugins/StartupSilence/src/main.cpp index b7035946d2..77b67af8a1 100644 --- a/plugins/StartupSilence/src/main.cpp +++ b/plugins/StartupSilence/src/main.cpp @@ -1,6 +1,6 @@ /*
Copyright (c) 2012-13 Vladimir Lyubimov
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/StartupSilence/src/stdafx.cxx b/plugins/StartupSilence/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/StartupSilence/src/stdafx.cxx +++ b/plugins/StartupSilence/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StartupSilence/src/version.h b/plugins/StartupSilence/src/version.h index 99a753db30..33b20e8aea 100644 --- a/plugins/StartupSilence/src/version.h +++ b/plugins/StartupSilence/src/version.h @@ -16,7 +16,7 @@ #define __DESCRIPTION "Suppresses popups and mutes sounds for 10-300 sec at Miranda NG startup, then sets them back to predefined state (Automatically per computer settings)."
#define __AUTHOR "Vladimir Lyubimov"
#define __AUTHORWEB "https://miranda-ng.org/"
-#define __COPYRIGHT "© 2012-22 Vladimir Lyubimov"
+#define __COPYRIGHT "© 2012-23 Vladimir Lyubimov"
// other stuff for Version resource
#include <stdver.h>
diff --git a/plugins/StatusChange/src/stdafx.cxx b/plugins/StatusChange/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/StatusChange/src/stdafx.cxx +++ b/plugins/StatusChange/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StatusManager/src/stdafx.cxx b/plugins/StatusManager/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/plugins/StatusManager/src/stdafx.cxx +++ b/plugins/StatusManager/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/plugins/StatusManager/src/version.h b/plugins/StatusManager/src/version.h index 98d537c9fe..e2c084386a 100644 --- a/plugins/StatusManager/src/version.h +++ b/plugins/StatusManager/src/version.h @@ -13,4 +13,4 @@ #define __DESCRIPTION "A connection checker and auto away module. Also allows you to define the status Miranda should set on startup, configurable per protocol."
#define __AUTHOR "P Boon"
#define __AUTHORWEB "https://miranda-ng.org/p/StatusManager"
-#define __COPYRIGHT "© 2003-08 P. Boon, 2008-22 George Hazan"
+#define __COPYRIGHT "© 2003-08 P. Boon, 2008-23 George Hazan"
diff --git a/plugins/StopSpamMod/src/stdafx.cxx b/plugins/StopSpamMod/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/StopSpamMod/src/stdafx.cxx +++ b/plugins/StopSpamMod/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/StopSpamMod/src/version.h b/plugins/StopSpamMod/src/version.h index 4dda7de950..c2551e9567 100644 --- a/plugins/StopSpamMod/src/version.h +++ b/plugins/StopSpamMod/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Anti-spam plugin for Miranda NG."
#define __AUTHOR "Roman Miklashevsky, sss, Elzor"
#define __AUTHORWEB "https://miranda-ng.org/p/StopSpamMod"
-#define __COPYRIGHT "© 2004-22 Roman Miklashevsky, A. Petkevich, Kosh&chka, sss, Elzor"
+#define __COPYRIGHT "© 2004-23 Roman Miklashevsky, A. Petkevich, Kosh&chka, sss, Elzor"
diff --git a/plugins/StopSpamPlus/src/stdafx.cxx b/plugins/StopSpamPlus/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/StopSpamPlus/src/stdafx.cxx +++ b/plugins/StopSpamPlus/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/TabSRMM/TabSRMM_icons/version.h b/plugins/TabSRMM/TabSRMM_icons/version.h index c825c52d39..94741dbf2e 100644 --- a/plugins/TabSRMM/TabSRMM_icons/version.h +++ b/plugins/TabSRMM/TabSRMM_icons/version.h @@ -1,11 +1,11 @@ -#define __MAJOR_VERSION 3 -#define __MINOR_VERSION 1 -#define __RELEASE_NUM 99 -#define __BUILD_NUM 8 - -#include <stdver.h> - -#define __PLUGIN_NAME "TabSRMM icons" -#define __FILENAME "TabSRMM_icons.dll" -#define __DESCRIPTION "Iconpack for TabSRMM plugin of Miranda NG." -#define __COPYRIGHT "© 2012-22 Miranda NG team, 2000-2010 Miranda Project and contributors." +#define __MAJOR_VERSION 3
+#define __MINOR_VERSION 1
+#define __RELEASE_NUM 99
+#define __BUILD_NUM 8
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "TabSRMM icons"
+#define __FILENAME "TabSRMM_icons.dll"
+#define __DESCRIPTION "Iconpack for TabSRMM plugin of Miranda NG."
+#define __COPYRIGHT "© 2012-23 Miranda NG team, 2000-2010 Miranda Project and contributors."
diff --git a/plugins/TabSRMM/src/ImageDataObject.cpp b/plugins/TabSRMM/src/ImageDataObject.cpp index 82c7756cef..6da7a5fda6 100644 --- a/plugins/TabSRMM/src/ImageDataObject.cpp +++ b/plugins/TabSRMM/src/ImageDataObject.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/TSButton.cpp b/plugins/TabSRMM/src/TSButton.cpp index 7ab8576319..444787e681 100644 --- a/plugins/TabSRMM/src/TSButton.cpp +++ b/plugins/TabSRMM/src/TSButton.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat.h b/plugins/TabSRMM/src/chat.h index 42df2909a2..75ceea90cf 100644 --- a/plugins/TabSRMM/src/chat.h +++ b/plugins/TabSRMM/src/chat.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_log.cpp b/plugins/TabSRMM/src/chat_log.cpp index e25c14e6fa..73884d4e84 100644 --- a/plugins/TabSRMM/src/chat_log.cpp +++ b/plugins/TabSRMM/src/chat_log.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_main.cpp b/plugins/TabSRMM/src/chat_main.cpp index c9662c5dbc..b398955f21 100644 --- a/plugins/TabSRMM/src/chat_main.cpp +++ b/plugins/TabSRMM/src/chat_main.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_manager.cpp b/plugins/TabSRMM/src/chat_manager.cpp index dab25a0ddf..bc540bed44 100644 --- a/plugins/TabSRMM/src/chat_manager.cpp +++ b/plugins/TabSRMM/src/chat_manager.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_options.cpp b/plugins/TabSRMM/src/chat_options.cpp index f8c1c99341..710a46a0af 100644 --- a/plugins/TabSRMM/src/chat_options.cpp +++ b/plugins/TabSRMM/src/chat_options.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_tools.cpp b/plugins/TabSRMM/src/chat_tools.cpp index d2d7fdc31f..78a316ea6f 100644 --- a/plugins/TabSRMM/src/chat_tools.cpp +++ b/plugins/TabSRMM/src/chat_tools.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/contactcache.cpp b/plugins/TabSRMM/src/contactcache.cpp index 88b06217ca..2071555d94 100644 --- a/plugins/TabSRMM/src/contactcache.cpp +++ b/plugins/TabSRMM/src/contactcache.cpp @@ -1,478 +1,478 @@ -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda NG: the free IM client for Microsoft* Windows* -// -// Copyright (C) 2012-22 Miranda NG team, -// Copyright (c) 2000-09 Miranda ICQ/IM project, -// all portions of this codebase are copyrighted to the people -// listed in contributors.txt. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// you should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// -// part of tabSRMM messaging plugin for Miranda. -// -// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors -// -// contact cache implementation -// -// the contact cache provides various services to the message window(s) -// it also abstracts meta contacts. - -#include "stdafx.h" - -static OBJLIST<CContactCache> arContacts(50, NumericKeySortT); - -static DBCachedContact ccInvalid; - -CContactCache::CContactCache(MCONTACT hContact) : - m_hContact(hContact), - m_history(10) -{ - if (hContact) { - if ((cc = db_get_contact(hContact)) != nullptr) { - initPhaseTwo(); - return; - } - } - - cc = &ccInvalid; - m_szAccount = C_INVALID_ACCOUNT; - m_isMeta = false; - m_isValid = false; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// 2nd part of the object initialization that must be callable during the -// object's lifetime (not only on construction). - -void CContactCache::initPhaseTwo() -{ - m_szAccount = nullptr; - if (cc->szProto) { - PROTOACCOUNT *acc = Proto_GetAccount(cc->szProto); - if (acc && acc->tszAccountName) - m_szAccount = acc->tszAccountName; - } - - m_isValid = (cc->szProto != nullptr && m_szAccount != nullptr) ? true : false; - if (m_isValid) { - m_iStatus = db_get_w(m_hContact, cc->szProto, "Status", ID_STATUS_OFFLINE); - m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here - if (m_isMeta) - updateMeta(); - updateNick(); - } - else { - m_szAccount = C_INVALID_ACCOUNT; - m_isMeta = false; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// reset meta contact information.Used when meta contacts are disabled -// on user's request. - -void CContactCache::resetMeta() -{ - m_isMeta = false; - m_szMetaProto = nullptr; - m_iMetaStatus = ID_STATUS_OFFLINE; - initPhaseTwo(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// if the contact has an open message window, close it. -// window procedure will use setWindowData() to reset m_hwnd to 0. - -void CContactCache::closeWindow() -{ - if (m_dat) { - m_dat->m_bForcedClose = true; - m_dat->Close(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update private copy of the nick name.Use contact list name cache -// -// @return bool: true if nick has changed. - -bool CContactCache::updateNick() -{ - bool fChanged = false; - if (m_isValid) { - wchar_t *tszNick = Clist_GetContactDisplayName(getActiveContact()); - if (tszNick && mir_wstrcmp(m_szNick, tszNick)) - fChanged = true; - wcsncpy_s(m_szNick, (tszNick ? tszNick : L"<undef>"), _TRUNCATE); - } - return fChanged; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update meta(subcontact and - protocol) status.This runs when the -// MC protocol fires one of its events OR when a relevant database value changes -// in the master contact. - -void CContactCache::updateMeta() -{ - if (m_isValid) { - MCONTACT hOldSub = m_hSub; - m_hSub = db_mc_getSrmmSub(cc->contactID); - m_szMetaProto = Proto_GetBaseAccountName(m_hSub); - m_iMetaStatus = (uint16_t)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE); - PROTOACCOUNT *pa = Proto_GetAccount(m_szMetaProto); - if (pa) - m_szAccount = pa->tszAccountName; - - if (hOldSub != m_hSub) { - updateNick(); - updateUIN(); - } - } - else { - m_hSub = 0; - m_szMetaProto = nullptr; - m_iMetaStatus = ID_STATUS_OFFLINE; - m_xStatus = 0; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// obtain the UIN.This is only maintained for open message windows -// it also run when the subcontact for a MC changes. - -bool CContactCache::updateUIN() -{ - m_szUIN[0] = 0; - - if (m_isValid) { - ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, getActiveContact(), getActiveProto())); - if (uid != nullptr) - wcsncpy_s(m_szUIN, uid, _TRUNCATE); - } - - return false; -} - -void CContactCache::updateStats(int iType, size_t value) -{ - if (m_stats == nullptr) - m_stats = new TSessionStats(); - - switch (iType) { - case TSessionStats::UPDATE_WITH_LAST_RCV: - if (!m_stats->lastReceivedChars) - break; - m_stats->iReceived++; - m_stats->messageCount++; - m_stats->iReceivedBytes += m_stats->lastReceivedChars; - m_stats->lastReceivedChars = 0; - break; - case TSessionStats::INIT_TIMER: - m_stats->started = time(0); - break; - case TSessionStats::SET_LAST_RCV: - m_stats->lastReceivedChars = (unsigned int)value; - break; - case TSessionStats::BYTES_SENT: - m_stats->iSent++; - m_stats->messageCount++; - m_stats->iSentBytes += (unsigned int)value; - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -//set the window data for this contact.The window procedure of the message -// dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache -// that a message window is open for this contact. -// -// @param dat: CMsgDialog* - window data structure - -void CContactCache::setWindowData(CMsgDialog *dat) -{ - m_dat = dat; - - if (dat) { - updateStatusMsg(); - } - else { - // release memory - not needed when window isn't open - replaceStrW(m_szStatusMsg, nullptr); - replaceStrW(m_ListeningInfo, nullptr); - replaceStrW(m_xStatusMsg, nullptr); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// saves message to the input history. -// it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history. - -void CContactCache::saveHistory() -{ - if (m_dat == nullptr) - return; - - CCtrlRichEdit &pEntry = m_dat->GetEntry(); - ptrA szFromStream(pEntry.GetRichTextRtf()); - if (szFromStream != nullptr) { - m_iHistoryCurrent = -1; - m_history.insert(szFromStream.detach()); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// handle the input history scrolling for the message input area -// @param wParam: VK_ keyboard code (VK_UP or VK_DOWN) - -void CContactCache::inputHistoryEvent(WPARAM wParam) -{ - if (m_dat == nullptr) - return; - - CCtrlRichEdit &pEntry = m_dat->GetEntry(); - if (m_history.getCount() > 0) { - char *pszText; - if (wParam == VK_UP) { - if (m_iHistoryCurrent == 0) - return; - - if (m_iHistoryCurrent < 0) - m_iHistoryCurrent = m_history.getCount()-1; - else - m_iHistoryCurrent--; - pszText = m_history[m_iHistoryCurrent]; - } - else { - if (m_iHistoryCurrent == -1) - return; - - if (m_iHistoryCurrent == m_history.getCount() - 1) { - m_iHistoryCurrent = -1; - pszText = ""; - } - else { - m_iHistoryCurrent++; - pszText = m_history[m_iHistoryCurrent]; - } - } - - SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 }; - pEntry.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pszText); - pEntry.SendMsg(EM_SETSEL, -1, -1); - } - - pEntry.OnChange(&pEntry); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// release additional memory resources - -void CContactCache::releaseAlloced() -{ - if (m_stats) { - delete m_stats; - m_stats = nullptr; - } - - for (auto &it : m_history) - mir_free(it); - m_history.destroy(); - - mir_free(m_szStatusMsg); - m_szStatusMsg = nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// when a contact is deleted, mark it as invalid in the cache and release -// all memory it has allocated. - -void CContactCache::deletedHandler() -{ - cc = &ccInvalid; - m_isValid = false; - if (m_dat) { - m_dat->m_bForcedClose = true; - - // this message must be sent async to allow a contact to rest in peace before window gets closed - ::PostMessage(m_dat->GetHwnd(), WM_CLOSE, 1, 2); - } - - releaseAlloced(); - m_hContact = INVALID_CONTACT_ID; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update all or only the given status message information from the database -// -// @param szKey: char* database key name or 0 to reload all messages - -void CContactCache::updateStatusMsg(const char *szKey) -{ - if (!m_isValid) - return; - - MCONTACT hContact = getActiveContact(); - - if (szKey == nullptr || (szKey && !mir_strcmp("StatusMsg", szKey))) { - if (m_szStatusMsg) - mir_free(m_szStatusMsg); - m_szStatusMsg = nullptr; - ptrW szStatus(db_get_wsa(hContact, "CList", "StatusMsg")); - if (szStatus != 0) - m_szStatusMsg = (mir_wstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : nullptr); - } - if (szKey == nullptr || (szKey && !mir_strcmp("ListeningTo", szKey))) { - if (m_ListeningInfo) - mir_free(m_ListeningInfo); - m_ListeningInfo = nullptr; - ptrW szListeningTo(db_get_wsa(hContact, cc->szProto, "ListeningTo")); - if (szListeningTo != 0 && *szListeningTo) - m_ListeningInfo = szListeningTo.detach(); - } - if (szKey == nullptr || (szKey && !mir_strcmp("XStatusMsg", szKey))) { - if (m_xStatusMsg) - mir_free(m_xStatusMsg); - m_xStatusMsg = nullptr; - ptrW szXStatusMsg(db_get_wsa(hContact, cc->szProto, "XStatusMsg")); - if (szXStatusMsg != 0 && *szXStatusMsg) - m_xStatusMsg = szXStatusMsg.detach(); - } - m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// retrieve contact cache entry for the given contact.It _never_ returns zero, for a hContact -// 0, it retrieves a dummy object. -// Non-existing cache entries are created on demand. -// -// @param hContact: contact handle -// @return CContactCache* pointer to the cache entry for this contact - -CContactCache* CContactCache::getContactCache(MCONTACT hContact) -{ - CContactCache *cc = arContacts.find((CContactCache*)&hContact); - if (cc == nullptr) { - cc = new CContactCache(hContact); - arContacts.insert(cc); - } - return cc; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// when the state of the meta contacts protocol changes from enabled to disabled -// (or vice versa), this updates the contact cache -// -// it is ONLY called from the DBSettingChanged() event handler when the relevant -// database value is touched. - -int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM) -{ - for (auto &c : arContacts) { - if (c->isMeta() && !bMetaEnabled) { - c->closeWindow(); - c->resetMeta(); - } - - // meta contacts are enabled, but current contact is a subcontact - > close window - if (bMetaEnabled && c->isSubContact()) - c->closeWindow(); - - // reset meta contact information, if metacontacts protocol became avail - if (bMetaEnabled && !c->cc->IsMeta()) - c->resetMeta(); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// normalize the status message with proper cr / lf sequences. -// @param src wchar_t*: original status message -// @param fStripAll bool: strip all cr/lf sequences and replace them with spaces (use for title bar) -// @return wchar_t*: converted status message. CALLER is responsible to mir_free it, MUST use mir_free() - -wchar_t* CContactCache::getNormalizedStatusMsg(const wchar_t *src, bool fStripAll) -{ - if (src == nullptr || mir_wstrlen(src) < 2) - return nullptr; - - CMStringW dest; - - for (int i = 0; src[i] != 0; i++) { - if (src[i] == 0x0d || src[i] == '\t') - continue; - if (i && src[i] == (wchar_t)0x0a) { - if (fStripAll) { - dest.AppendChar(' '); - continue; - } - dest.AppendChar('\n'); - continue; - } - dest.AppendChar(src[i]); - } - - return mir_wstrndup(dest, dest.GetLength()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// retrieve the tab / title icon for the corresponding session. - -HICON CContactCache::getIcon(int &iSize) const -{ - if (!m_dat) - return Skin_LoadProtoIcon(cc->szProto, getStatus()); - - if (m_dat->m_bErrorState) - return PluginConfig.g_iconErr; - if (m_dat->m_bCanFlashTab) - return m_dat->m_iFlashIcon; - - if (m_dat->isChat() && m_dat->m_iFlashIcon) { - int sizeX, sizeY; - Utils::getIconSize(m_dat->m_iFlashIcon, sizeX, sizeY); - iSize = sizeX; - return m_dat->m_iFlashIcon; - } - if (m_dat->m_hTabIcon == m_dat->m_hTabStatusIcon && m_dat->m_hXStatusIcon) - return m_dat->m_hXStatusIcon; - return m_dat->m_hTabIcon; -} - -size_t CContactCache::getMaxMessageLength() -{ - if (m_nMax == 0) { - MCONTACT hContact = getActiveContact(); - LPCSTR szProto = getActiveProto(); - if (szProto) { - m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact); - if (m_nMax) { - if (M.GetByte("autosplit", 0)) - m_nMax = 20000; - } - else m_nMax = 20000; - - m_dat->LimitMessageText(m_nMax); - } - } - return m_nMax; -} - -bool CContactCache::updateStatus(int iStatus) -{ - m_iOldStatus = m_iStatus; - m_iStatus = iStatus; - return m_iOldStatus != iStatus; -} +/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// contact cache implementation
+//
+// the contact cache provides various services to the message window(s)
+// it also abstracts meta contacts.
+
+#include "stdafx.h"
+
+static OBJLIST<CContactCache> arContacts(50, NumericKeySortT);
+
+static DBCachedContact ccInvalid;
+
+CContactCache::CContactCache(MCONTACT hContact) :
+ m_hContact(hContact),
+ m_history(10)
+{
+ if (hContact) {
+ if ((cc = db_get_contact(hContact)) != nullptr) {
+ initPhaseTwo();
+ return;
+ }
+ }
+
+ cc = &ccInvalid;
+ m_szAccount = C_INVALID_ACCOUNT;
+ m_isMeta = false;
+ m_isValid = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 2nd part of the object initialization that must be callable during the
+// object's lifetime (not only on construction).
+
+void CContactCache::initPhaseTwo()
+{
+ m_szAccount = nullptr;
+ if (cc->szProto) {
+ PROTOACCOUNT *acc = Proto_GetAccount(cc->szProto);
+ if (acc && acc->tszAccountName)
+ m_szAccount = acc->tszAccountName;
+ }
+
+ m_isValid = (cc->szProto != nullptr && m_szAccount != nullptr) ? true : false;
+ if (m_isValid) {
+ m_iStatus = db_get_w(m_hContact, cc->szProto, "Status", ID_STATUS_OFFLINE);
+ m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here
+ if (m_isMeta)
+ updateMeta();
+ updateNick();
+ }
+ else {
+ m_szAccount = C_INVALID_ACCOUNT;
+ m_isMeta = false;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reset meta contact information.Used when meta contacts are disabled
+// on user's request.
+
+void CContactCache::resetMeta()
+{
+ m_isMeta = false;
+ m_szMetaProto = nullptr;
+ m_iMetaStatus = ID_STATUS_OFFLINE;
+ initPhaseTwo();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// if the contact has an open message window, close it.
+// window procedure will use setWindowData() to reset m_hwnd to 0.
+
+void CContactCache::closeWindow()
+{
+ if (m_dat) {
+ m_dat->m_bForcedClose = true;
+ m_dat->Close();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update private copy of the nick name.Use contact list name cache
+//
+// @return bool: true if nick has changed.
+
+bool CContactCache::updateNick()
+{
+ bool fChanged = false;
+ if (m_isValid) {
+ wchar_t *tszNick = Clist_GetContactDisplayName(getActiveContact());
+ if (tszNick && mir_wstrcmp(m_szNick, tszNick))
+ fChanged = true;
+ wcsncpy_s(m_szNick, (tszNick ? tszNick : L"<undef>"), _TRUNCATE);
+ }
+ return fChanged;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update meta(subcontact and - protocol) status.This runs when the
+// MC protocol fires one of its events OR when a relevant database value changes
+// in the master contact.
+
+void CContactCache::updateMeta()
+{
+ if (m_isValid) {
+ MCONTACT hOldSub = m_hSub;
+ m_hSub = db_mc_getSrmmSub(cc->contactID);
+ m_szMetaProto = Proto_GetBaseAccountName(m_hSub);
+ m_iMetaStatus = (uint16_t)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE);
+ PROTOACCOUNT *pa = Proto_GetAccount(m_szMetaProto);
+ if (pa)
+ m_szAccount = pa->tszAccountName;
+
+ if (hOldSub != m_hSub) {
+ updateNick();
+ updateUIN();
+ }
+ }
+ else {
+ m_hSub = 0;
+ m_szMetaProto = nullptr;
+ m_iMetaStatus = ID_STATUS_OFFLINE;
+ m_xStatus = 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// obtain the UIN.This is only maintained for open message windows
+// it also run when the subcontact for a MC changes.
+
+bool CContactCache::updateUIN()
+{
+ m_szUIN[0] = 0;
+
+ if (m_isValid) {
+ ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, getActiveContact(), getActiveProto()));
+ if (uid != nullptr)
+ wcsncpy_s(m_szUIN, uid, _TRUNCATE);
+ }
+
+ return false;
+}
+
+void CContactCache::updateStats(int iType, size_t value)
+{
+ if (m_stats == nullptr)
+ m_stats = new TSessionStats();
+
+ switch (iType) {
+ case TSessionStats::UPDATE_WITH_LAST_RCV:
+ if (!m_stats->lastReceivedChars)
+ break;
+ m_stats->iReceived++;
+ m_stats->messageCount++;
+ m_stats->iReceivedBytes += m_stats->lastReceivedChars;
+ m_stats->lastReceivedChars = 0;
+ break;
+ case TSessionStats::INIT_TIMER:
+ m_stats->started = time(0);
+ break;
+ case TSessionStats::SET_LAST_RCV:
+ m_stats->lastReceivedChars = (unsigned int)value;
+ break;
+ case TSessionStats::BYTES_SENT:
+ m_stats->iSent++;
+ m_stats->messageCount++;
+ m_stats->iSentBytes += (unsigned int)value;
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//set the window data for this contact.The window procedure of the message
+// dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache
+// that a message window is open for this contact.
+//
+// @param dat: CMsgDialog* - window data structure
+
+void CContactCache::setWindowData(CMsgDialog *dat)
+{
+ m_dat = dat;
+
+ if (dat) {
+ updateStatusMsg();
+ }
+ else {
+ // release memory - not needed when window isn't open
+ replaceStrW(m_szStatusMsg, nullptr);
+ replaceStrW(m_ListeningInfo, nullptr);
+ replaceStrW(m_xStatusMsg, nullptr);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// saves message to the input history.
+// it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history.
+
+void CContactCache::saveHistory()
+{
+ if (m_dat == nullptr)
+ return;
+
+ CCtrlRichEdit &pEntry = m_dat->GetEntry();
+ ptrA szFromStream(pEntry.GetRichTextRtf());
+ if (szFromStream != nullptr) {
+ m_iHistoryCurrent = -1;
+ m_history.insert(szFromStream.detach());
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// handle the input history scrolling for the message input area
+// @param wParam: VK_ keyboard code (VK_UP or VK_DOWN)
+
+void CContactCache::inputHistoryEvent(WPARAM wParam)
+{
+ if (m_dat == nullptr)
+ return;
+
+ CCtrlRichEdit &pEntry = m_dat->GetEntry();
+ if (m_history.getCount() > 0) {
+ char *pszText;
+ if (wParam == VK_UP) {
+ if (m_iHistoryCurrent == 0)
+ return;
+
+ if (m_iHistoryCurrent < 0)
+ m_iHistoryCurrent = m_history.getCount()-1;
+ else
+ m_iHistoryCurrent--;
+ pszText = m_history[m_iHistoryCurrent];
+ }
+ else {
+ if (m_iHistoryCurrent == -1)
+ return;
+
+ if (m_iHistoryCurrent == m_history.getCount() - 1) {
+ m_iHistoryCurrent = -1;
+ pszText = "";
+ }
+ else {
+ m_iHistoryCurrent++;
+ pszText = m_history[m_iHistoryCurrent];
+ }
+ }
+
+ SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
+ pEntry.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pszText);
+ pEntry.SendMsg(EM_SETSEL, -1, -1);
+ }
+
+ pEntry.OnChange(&pEntry);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// release additional memory resources
+
+void CContactCache::releaseAlloced()
+{
+ if (m_stats) {
+ delete m_stats;
+ m_stats = nullptr;
+ }
+
+ for (auto &it : m_history)
+ mir_free(it);
+ m_history.destroy();
+
+ mir_free(m_szStatusMsg);
+ m_szStatusMsg = nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// when a contact is deleted, mark it as invalid in the cache and release
+// all memory it has allocated.
+
+void CContactCache::deletedHandler()
+{
+ cc = &ccInvalid;
+ m_isValid = false;
+ if (m_dat) {
+ m_dat->m_bForcedClose = true;
+
+ // this message must be sent async to allow a contact to rest in peace before window gets closed
+ ::PostMessage(m_dat->GetHwnd(), WM_CLOSE, 1, 2);
+ }
+
+ releaseAlloced();
+ m_hContact = INVALID_CONTACT_ID;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update all or only the given status message information from the database
+//
+// @param szKey: char* database key name or 0 to reload all messages
+
+void CContactCache::updateStatusMsg(const char *szKey)
+{
+ if (!m_isValid)
+ return;
+
+ MCONTACT hContact = getActiveContact();
+
+ if (szKey == nullptr || (szKey && !mir_strcmp("StatusMsg", szKey))) {
+ if (m_szStatusMsg)
+ mir_free(m_szStatusMsg);
+ m_szStatusMsg = nullptr;
+ ptrW szStatus(db_get_wsa(hContact, "CList", "StatusMsg"));
+ if (szStatus != 0)
+ m_szStatusMsg = (mir_wstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : nullptr);
+ }
+ if (szKey == nullptr || (szKey && !mir_strcmp("ListeningTo", szKey))) {
+ if (m_ListeningInfo)
+ mir_free(m_ListeningInfo);
+ m_ListeningInfo = nullptr;
+ ptrW szListeningTo(db_get_wsa(hContact, cc->szProto, "ListeningTo"));
+ if (szListeningTo != 0 && *szListeningTo)
+ m_ListeningInfo = szListeningTo.detach();
+ }
+ if (szKey == nullptr || (szKey && !mir_strcmp("XStatusMsg", szKey))) {
+ if (m_xStatusMsg)
+ mir_free(m_xStatusMsg);
+ m_xStatusMsg = nullptr;
+ ptrW szXStatusMsg(db_get_wsa(hContact, cc->szProto, "XStatusMsg"));
+ if (szXStatusMsg != 0 && *szXStatusMsg)
+ m_xStatusMsg = szXStatusMsg.detach();
+ }
+ m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve contact cache entry for the given contact.It _never_ returns zero, for a hContact
+// 0, it retrieves a dummy object.
+// Non-existing cache entries are created on demand.
+//
+// @param hContact: contact handle
+// @return CContactCache* pointer to the cache entry for this contact
+
+CContactCache* CContactCache::getContactCache(MCONTACT hContact)
+{
+ CContactCache *cc = arContacts.find((CContactCache*)&hContact);
+ if (cc == nullptr) {
+ cc = new CContactCache(hContact);
+ arContacts.insert(cc);
+ }
+ return cc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// when the state of the meta contacts protocol changes from enabled to disabled
+// (or vice versa), this updates the contact cache
+//
+// it is ONLY called from the DBSettingChanged() event handler when the relevant
+// database value is touched.
+
+int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM)
+{
+ for (auto &c : arContacts) {
+ if (c->isMeta() && !bMetaEnabled) {
+ c->closeWindow();
+ c->resetMeta();
+ }
+
+ // meta contacts are enabled, but current contact is a subcontact - > close window
+ if (bMetaEnabled && c->isSubContact())
+ c->closeWindow();
+
+ // reset meta contact information, if metacontacts protocol became avail
+ if (bMetaEnabled && !c->cc->IsMeta())
+ c->resetMeta();
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// normalize the status message with proper cr / lf sequences.
+// @param src wchar_t*: original status message
+// @param fStripAll bool: strip all cr/lf sequences and replace them with spaces (use for title bar)
+// @return wchar_t*: converted status message. CALLER is responsible to mir_free it, MUST use mir_free()
+
+wchar_t* CContactCache::getNormalizedStatusMsg(const wchar_t *src, bool fStripAll)
+{
+ if (src == nullptr || mir_wstrlen(src) < 2)
+ return nullptr;
+
+ CMStringW dest;
+
+ for (int i = 0; src[i] != 0; i++) {
+ if (src[i] == 0x0d || src[i] == '\t')
+ continue;
+ if (i && src[i] == (wchar_t)0x0a) {
+ if (fStripAll) {
+ dest.AppendChar(' ');
+ continue;
+ }
+ dest.AppendChar('\n');
+ continue;
+ }
+ dest.AppendChar(src[i]);
+ }
+
+ return mir_wstrndup(dest, dest.GetLength());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve the tab / title icon for the corresponding session.
+
+HICON CContactCache::getIcon(int &iSize) const
+{
+ if (!m_dat)
+ return Skin_LoadProtoIcon(cc->szProto, getStatus());
+
+ if (m_dat->m_bErrorState)
+ return PluginConfig.g_iconErr;
+ if (m_dat->m_bCanFlashTab)
+ return m_dat->m_iFlashIcon;
+
+ if (m_dat->isChat() && m_dat->m_iFlashIcon) {
+ int sizeX, sizeY;
+ Utils::getIconSize(m_dat->m_iFlashIcon, sizeX, sizeY);
+ iSize = sizeX;
+ return m_dat->m_iFlashIcon;
+ }
+ if (m_dat->m_hTabIcon == m_dat->m_hTabStatusIcon && m_dat->m_hXStatusIcon)
+ return m_dat->m_hXStatusIcon;
+ return m_dat->m_hTabIcon;
+}
+
+size_t CContactCache::getMaxMessageLength()
+{
+ if (m_nMax == 0) {
+ MCONTACT hContact = getActiveContact();
+ LPCSTR szProto = getActiveProto();
+ if (szProto) {
+ m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact);
+ if (m_nMax) {
+ if (M.GetByte("autosplit", 0))
+ m_nMax = 20000;
+ }
+ else m_nMax = 20000;
+
+ m_dat->LimitMessageText(m_nMax);
+ }
+ }
+ return m_nMax;
+}
+
+bool CContactCache::updateStatus(int iStatus)
+{
+ m_iOldStatus = m_iStatus;
+ m_iStatus = iStatus;
+ return m_iOldStatus != iStatus;
+}
diff --git a/plugins/TabSRMM/src/contactcache.h b/plugins/TabSRMM/src/contactcache.h index 80a7ff18b2..1168b60258 100644 --- a/plugins/TabSRMM/src/contactcache.h +++ b/plugins/TabSRMM/src/contactcache.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/container.cpp b/plugins/TabSRMM/src/container.cpp index c26ddddb23..2c6c218631 100644 --- a/plugins/TabSRMM/src/container.cpp +++ b/plugins/TabSRMM/src/container.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/containeroptions.cpp b/plugins/TabSRMM/src/containeroptions.cpp index f956233387..96eaf7480d 100644 --- a/plugins/TabSRMM/src/containeroptions.cpp +++ b/plugins/TabSRMM/src/containeroptions.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/controls.cpp b/plugins/TabSRMM/src/controls.cpp index 4e72e2c8e2..1ac916f6cc 100644 --- a/plugins/TabSRMM/src/controls.cpp +++ b/plugins/TabSRMM/src/controls.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/controls.h b/plugins/TabSRMM/src/controls.h index c3ff022fd1..b878f347d4 100644 --- a/plugins/TabSRMM/src/controls.h +++ b/plugins/TabSRMM/src/controls.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/eventpopups.cpp b/plugins/TabSRMM/src/eventpopups.cpp index 37731f2f44..3b35900a1a 100644 --- a/plugins/TabSRMM/src/eventpopups.cpp +++ b/plugins/TabSRMM/src/eventpopups.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/functions.h b/plugins/TabSRMM/src/functions.h index e8c57f4476..dc0bb47c5f 100644 --- a/plugins/TabSRMM/src/functions.h +++ b/plugins/TabSRMM/src/functions.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/generic_msghandlers.cpp b/plugins/TabSRMM/src/generic_msghandlers.cpp index 9399fdc313..f704717da6 100644 --- a/plugins/TabSRMM/src/generic_msghandlers.cpp +++ b/plugins/TabSRMM/src/generic_msghandlers.cpp @@ -1,1358 +1,1358 @@ -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda NG: the free IM client for Microsoft* Windows* -// -// Copyright (C) 2012-22 Miranda NG team, -// Copyright (c) 2000-09 Miranda ICQ/IM project, -// all portions of this codebase are copyrighted to the people -// listed in contributors.txt. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// you should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// -// part of tabSRMM messaging plugin for Miranda. -// -// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors -// -// these are generic message handlers which are used by the message dialog window procedure. -// calling them directly instead of using SendMessage() is faster. -// also contains various callback functions for custom buttons - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Save message log for given session as RTF document - -/* -void CMsgDialog::DM_SaveLogAsRTF() const -{ - if (m_hwndIEView != nullptr) { - IEVIEWEVENT event = { sizeof(event) }; - event.hwnd = m_hwndIEView; - event.hContact = m_hContact; - event.iType = IEE_SAVE_DOCUMENT; - CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event); - } - else { - wchar_t szFilter[MAX_PATH], szFilename[MAX_PATH]; - mir_snwprintf(szFilter, L"%s%c*.rtf%c%c", TranslateT("Rich Edit file"), 0, 0, 0); - mir_snwprintf(szFilename, L"%s.rtf", m_cache->getNick()); - - Utils::sanitizeFilename(szFilename); - - wchar_t szInitialDir[MAX_PATH + 2]; - mir_snwprintf(szInitialDir, L"%s%s\\", M.getDataPath(), L"\\Saved message logs"); - CreateDirectoryTreeW(szInitialDir); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFile = szFilename; - ofn.lpstrFilter = szFilter; - ofn.lpstrInitialDir = szInitialDir; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_HIDEREADONLY; - ofn.lpstrDefExt = L"rtf"; - if (GetSaveFileName(&ofn)) { - EDITSTREAM stream = { 0 }; - stream.dwCookie = (DWORD_PTR)szFilename; - stream.dwError = 0; - stream.pfnCallback = Utils::StreamOut; - m_rtf.SendMsg(EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM)&stream); - } - } -} -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// checks if the balloon tooltip can be dismissed (usually called by WM_MOUSEMOVE events) - -void CMsgDialog::DM_DismissTip(const POINT& pt) -{ - if (!IsWindowVisible(m_hwndTip)) - return; - - RECT rc; - GetWindowRect(m_hwndTip, &rc); - if (PtInRect(&rc, pt)) - return; - - if (abs(pt.x - m_ptTipActivation.x) > 5 || abs(pt.y - m_ptTipActivation.y) > 5) { - SendMessage(m_hwndTip, TTM_TRACKACTIVATE, FALSE, 0); - m_ptTipActivation.x = m_ptTipActivation.y = 0; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// initialize the balloon tooltip for message window notifications - -void CMsgDialog::DM_InitTip() -{ - m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, nullptr, g_plugin.getInst(), (LPVOID)nullptr); - - memset(&ti, 0, sizeof(ti)); - ti.cbSize = sizeof(ti); - ti.lpszText = TranslateT("No status message"); - ti.hinst = g_plugin.getInst(); - ti.hwnd = m_hwnd; - ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT; - ti.uId = (UINT_PTR)m_hwnd; - SendMessage(m_hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti); - - SetWindowPos(m_hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// checks generic hotkeys valid for both IM and MUC sessions -// -// returns 1 for handled hotkeys, 0 otherwise. - -bool CMsgDialog::DM_GenericHotkeysCheck(MSG *message) -{ - LRESULT mim_hotkey_check = Hotkey_Check(message, TABSRMM_HK_SECTION_GENERIC); - - switch (mim_hotkey_check) { - case TABSRMM_HK_PASTEANDSEND: - HandlePasteAndSend(); - return true; - - case TABSRMM_HK_HISTORY: - m_btnHistory.Click(); - return true; - - case TABSRMM_HK_CONTAINEROPTIONS: - m_pContainer->OptionsDialog(); - return true; - - case TABSRMM_HK_TOGGLEINFOPANEL: - m_pPanel.setActive(!m_pPanel.isActive()); - m_pPanel.showHide(); - return true; - - case TABSRMM_HK_TOGGLETOOLBAR: - SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLETOOLBAR, 0); - return true; - - case TABSRMM_HK_CLEARLOG: - tabClearLog(); - return true; - - case TABSRMM_HK_TOGGLESIDEBAR: - if (m_pContainer->m_pSideBar->isActive()) - SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0); - return true; - - case TABSRMM_HK_CLOSE_OTHER: - CloseOtherTabs(m_pContainer->m_hwndTabs, *this); - return true; - } - return false; -} - -LRESULT CMsgDialog::DM_MsgWindowCmdHandler(UINT cmd, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - int iSelection; - HMENU submenu; - - switch (cmd) { - case IDC_SRMM_BOLD: - case IDC_SRMM_ITALICS: - case IDC_SRMM_UNDERLINE: - case IDC_FONTSTRIKEOUT: - if (m_SendFormat != 0) { // dont use formatting if disabled - CHARFORMAT2 cf, cfOld; - memset(&cf, 0, sizeof(CHARFORMAT2)); - memset(&cfOld, 0, sizeof(CHARFORMAT2)); - cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2); - cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT; - m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld); - BOOL isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD); - BOOL isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC); - BOOL isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE); - BOOL isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT); - - int ctrlId = LOWORD(wParam); - if (ctrlId == IDC_SRMM_BOLD && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_BOLD))) - break; - if (ctrlId == IDC_SRMM_ITALICS && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_ITALICS))) - break; - if (ctrlId == IDC_SRMM_UNDERLINE && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_UNDERLINE))) - break; - if (ctrlId == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_FONTSTRIKEOUT))) - break; - if (ctrlId == IDC_SRMM_BOLD) { - cf.dwEffects = isBold ? 0 : CFE_BOLD; - cf.dwMask = CFM_BOLD; - CheckDlgButton(m_hwnd, IDC_SRMM_BOLD, !isBold ? BST_CHECKED : BST_UNCHECKED); - } - else if (ctrlId == IDC_SRMM_ITALICS) { - cf.dwEffects = isItalic ? 0 : CFE_ITALIC; - cf.dwMask = CFM_ITALIC; - CheckDlgButton(m_hwnd, IDC_SRMM_ITALICS, !isItalic ? BST_CHECKED : BST_UNCHECKED); - } - else if (ctrlId == IDC_SRMM_UNDERLINE) { - cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE; - cf.dwMask = CFM_UNDERLINE; - CheckDlgButton(m_hwnd, IDC_SRMM_UNDERLINE, !isUnderline ? BST_CHECKED : BST_UNCHECKED); - } - else if (ctrlId == IDC_FONTSTRIKEOUT) { - cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT; - cf.dwMask = CFM_STRIKEOUT; - CheckDlgButton(m_hwnd, IDC_FONTSTRIKEOUT, !isStrikeout ? BST_CHECKED : BST_UNCHECKED); - } - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); - } - break; - - case IDCANCEL: - ShowWindow(m_pContainer->m_hwnd, SW_MINIMIZE); - return FALSE; - - case IDC_CLOSE: - PostMessage(m_hwnd, WM_CLOSE, 1, 0); - break; - - case IDC_NAME: - if (GetKeyState(VK_SHIFT) & 0x8000) // copy UIN - Utils_ClipboardCopy(m_cache->getUIN()); - else - CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(m_cache->getActiveContact()), 0); - break; - - case IDC_TIME: - submenu = GetSubMenu(PluginConfig.g_hMenuContext, 2); - MsgWindowUpdateMenu(submenu, MENU_LOGMENU); - - GetWindowRect(GetDlgItem(m_hwnd, IDC_TIME), &rc); - - iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr); - return MsgWindowMenuHandler(iSelection, MENU_LOGMENU); - - case IDC_PROTOMENU: - submenu = GetSubMenu(PluginConfig.g_hMenuContext, 4); - { - bool iOldGlobalSendFormat = g_plugin.bSendFormat; - int iLocalFormat = M.GetDword(m_hContact, "sendformat", 0); - int iNewLocalFormat = iLocalFormat; - - GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc); - - CheckMenuItem(submenu, ID_MODE_GLOBAL, !m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_MODE_PRIVATE, m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED); - - // formatting menu.. - CheckMenuItem(submenu, ID_GLOBAL_BBCODE, (g_plugin.bSendFormat) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_GLOBAL_OFF, (g_plugin.bSendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED); - - CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, (iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, (iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_THISCONTACT_OFF, (iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED); - - iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr); - switch (iSelection) { - case ID_MODE_GLOBAL: - m_bSplitterOverride = false; - db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 0); - LoadSplitter(); - AdjustBottomAvatarDisplay(); - DM_RecalcPictureSize(); - Resize(); - break; - - case ID_MODE_PRIVATE: - m_bSplitterOverride = true; - db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 1); - LoadSplitter(); - AdjustBottomAvatarDisplay(); - DM_RecalcPictureSize(); - Resize(); - break; - - case ID_GLOBAL_BBCODE: - g_plugin.bSendFormat = SENDFORMAT_BBCODE; - break; - - case ID_GLOBAL_OFF: - g_plugin.bSendFormat = SENDFORMAT_NONE; - break; - - case ID_THISCONTACT_GLOBALSETTING: - iNewLocalFormat = 0; - break; - - case ID_THISCONTACT_BBCODE: - iNewLocalFormat = SENDFORMAT_BBCODE; - break; - - case ID_THISCONTACT_OFF: - iNewLocalFormat = -1; - break; - } - - if (iNewLocalFormat == 0) - db_unset(m_hContact, SRMSGMOD_T, "sendformat"); - else if (iNewLocalFormat != iLocalFormat) - db_set_dw(m_hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat); - - if (iNewLocalFormat != iLocalFormat || g_plugin.bSendFormat != iOldGlobalSendFormat) { - m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat); - if (m_SendFormat == -1) // per contact override to disable it.. - m_SendFormat = 0; - Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1); - } - } - break; - - case IDC_TOGGLETOOLBAR: - if (lParam == 1) - m_pContainer->cfg.flags.m_bNoMenuBar = !m_pContainer->cfg.flags.m_bNoMenuBar; - else - m_pContainer->cfg.flags.m_bHideToolbar = !m_pContainer->cfg.flags.m_bHideToolbar; - m_pContainer->ApplySetting(true); - break; - - case IDC_SENDMENU: - submenu = GetSubMenu(PluginConfig.g_hMenuContext, 3); - - GetWindowRect(GetDlgItem(m_hwnd, IDOK), &rc); - CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, (m_sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, m_sendMode == 0 ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, (m_sendMode & SMODE_CONTAINER) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, (m_sendMode & SMODE_SENDLATER) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, (m_sendMode & SMODE_NOACK) ? MF_CHECKED : MF_UNCHECKED); - - if (lParam) - iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr); - else - iSelection = HIWORD(wParam); - - switch (iSelection) { - case ID_SENDMENU_SENDTOMULTIPLEUSERS: - m_sendMode ^= SMODE_MULTIPLE; - if (m_sendMode & SMODE_MULTIPLE) - DM_CreateClist(); - else if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST))) - DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST)); - break; - case ID_SENDMENU_SENDDEFAULT: - m_sendMode = 0; - break; - case ID_SENDMENU_SENDTOCONTAINER: - m_sendMode ^= SMODE_CONTAINER; - RedrawWindow(m_hwnd, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW); - break; - case ID_SENDMENU_SENDLATER: - if (SendLater::Avail) - m_sendMode ^= SMODE_SENDLATER; - else - CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION); - break; - case ID_SENDMENU_SENDWITHOUTTIMEOUTS: - m_sendMode ^= SMODE_NOACK; - if (m_sendMode & SMODE_NOACK) - db_set_b(m_hContact, SRMSGMOD_T, "no_ack", 1); - else - db_unset(m_hContact, SRMSGMOD_T, "no_ack"); - break; - } - db_set_b(m_hContact, SRMSGMOD_T, "no_ack", (uint8_t)(m_sendMode & SMODE_NOACK ? 1 : 0)); - SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE); - if (m_sendMode & SMODE_MULTIPLE || m_sendMode & SMODE_CONTAINER) { - SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER | - SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS); - RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN); - } - else { - if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST))) - DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST)); - SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER | - SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS); - RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN); - } - m_pContainer->QueryClientArea(rc); - Resize(); - DM_ScrollToBottom(1, 1); - Utils::showDlgControl(m_hwnd, IDC_MULTISPLITTER, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); - Utils::showDlgControl(m_hwnd, IDC_CLIST, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); - break; - - case IDC_TOGGLESIDEBAR: - SendMessage(m_pContainer->m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0); - break; - - case IDC_PIC: - GetClientRect(m_hwnd, &rc); - - m_bEditNotesActive = !m_bEditNotesActive; - if (m_bEditNotesActive) { - int iLen = GetWindowTextLength(m_message.GetHwnd()); - if (iLen != 0) { - ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("You cannot edit user notes when there are unsent messages")); - m_bEditNotesActive = false; - break; - } - - if (!m_bIsAutosizingInput) { - m_iSplitterSaved = m_iSplitterY; - m_iSplitterY = rc.bottom / 2; - SendMessage(m_hwnd, WM_SIZE, 1, 1); - } - - ptrW wszText(db_get_wsa(m_hContact, "UserInfo", "MyNotes")); - if (wszText != nullptr) - m_message.SetText(wszText); - } - else { - ptrW buf(m_message.GetText()); - db_set_ws(m_hContact, "UserInfo", "MyNotes", buf); - m_message.SetText(L""); - - if (!m_bIsAutosizingInput) { - m_iSplitterY = m_iSplitterSaved; - Resize(); - DM_ScrollToBottom(0, 1); - } - } - SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER | - SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS); - RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); - - if (m_bEditNotesActive) - CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK | MB_ICONINFORMATION); - break; - - case IDM_CLEAR: - tabClearLog(); - break; - - case IDC_PROTOCOL: - submenu = Menu_BuildContactMenu(m_hContact); - if (lParam == 0) - GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc); - else - GetWindowRect((HWND)lParam, &rc); - - iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr); - if (iSelection) - Clist_MenuProcessCommand(LOWORD(iSelection), MPCF_CONTACTMENU, m_hContact); - - DestroyMenu(submenu); - break; - - // error control - case IDC_CANCELSEND: - DM_ErrorDetected(MSGERROR_CANCEL, 0); - break; - - case IDC_RETRY: - DM_ErrorDetected(MSGERROR_RETRY, 0); - break; - - case IDC_MSGSENDLATER: - DM_ErrorDetected(MSGERROR_SENDLATER, 0); - break; - - case IDC_SELFTYPING: - if (AllowTyping()) { - int iCurrentTypingMode = g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew); - if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) { - DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF); - m_nTypeMode = PROTOTYPE_SELFTYPING_OFF; - } - g_plugin.setByte(m_hContact, SRMSGSET_TYPING, (uint8_t)!iCurrentTypingMode); - } - break; - - default: - return 0; - } - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// initialize rich edit control (log and edit control) for both MUC and -// standard IM session windows. - -void CMsgDialog::DM_InitRichEdit() -{ - char *szStreamOut = nullptr; - if (!isChat() && GetWindowTextLength(m_message.GetHwnd()) > 0) - szStreamOut = m_message.GetRichTextRtf(); - SetWindowText(m_message.GetHwnd(), L""); - - m_pLog->UpdateOptions(); - - m_message.SendMsg(EM_SETBKGNDCOLOR, 0, m_pContainer->m_theme.inputbg); - - CHARFORMAT2 cf2 = {}; - cf2.cbSize = sizeof(cf2); - - if (isChat()) { - LOGFONTW lf; - COLORREF inputcharcolor; - LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor); - - cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR; - cf2.crTextColor = inputcharcolor; - cf2.bCharSet = lf.lfCharSet; - cf2.crBackColor = m_pContainer->m_theme.inputbg; - wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE); - cf2.dwEffects = 0; - cf2.wWeight = (uint16_t)lf.lfWeight; - cf2.bPitchAndFamily = lf.lfPitchAndFamily; - cf2.yHeight = abs(lf.lfHeight) * 15; - } - else { - LOGFONTW lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA]; - COLORREF inputcharcolor = m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA]; - - for (auto &it : Utils::rtf_clrs) - if (it->clr == inputcharcolor) - inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1); - - cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC; - cf2.crTextColor = inputcharcolor; - cf2.bCharSet = lf.lfCharSet; - wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE); - cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0); - cf2.wWeight = (uint16_t)lf.lfWeight; - cf2.bPitchAndFamily = lf.lfPitchAndFamily; - cf2.yHeight = abs(lf.lfHeight) * 15; - } - m_message.SendMsg(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2); - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); /* WINE: fix send colour text. */ - m_message.SendMsg(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2); /* WINE: fix send colour text. */ - - // setup the rich edit control(s) - // LOG is always set to RTL, because this is needed for proper bidirectional operation later. - // The real text direction is then enforced by the streaming code which adds appropiate paragraph - // and textflow formatting commands to the - PARAFORMAT2 pf2; - memset(&pf2, 0, sizeof(PARAFORMAT2)); - pf2.cbSize = sizeof(pf2); - pf2.wEffects = PFE_RTLPARA; - pf2.dwMask = PFM_RTLPARA; - if (FindRTLLocale()) - m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2); - if (!(m_dwFlags & MWF_LOG_RTL)) { - pf2.wEffects = 0; - m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2); - } - m_message.SendMsg(EM_SETLANGOPTIONS, 0, (LPARAM)m_message.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD); - - if (m_dwFlags & MWF_LOG_RTL) - SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR); - else - SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR)); - - if (szStreamOut != nullptr) { - SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 }; - m_message.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut); - mir_free(szStreamOut); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// set the states of defined database action buttons(only if button is a toggle) - -void CMsgDialog::DM_SetDBButtonStates() -{ - ButtonItem *buttonItem = m_pContainer->m_buttonItems; - MCONTACT hFinalContact = 0; - HWND hwndContainer = m_pContainer->m_hwnd; - - while (buttonItem) { - HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId); - - if (buttonItem->pfnCallback) - buttonItem->pfnCallback(buttonItem, m_hwnd, this, hWnd); - - if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) { - buttonItem = buttonItem->nextItem; - continue; - } - - BOOL result = FALSE; - char *szModule = buttonItem->szModule; - char *szSetting = buttonItem->szSetting; - if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) { - if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) - szModule = Proto_GetBaseAccountName(m_hContact); - hFinalContact = m_hContact; - } - else hFinalContact = 0; - - switch (buttonItem->type) { - case DBVT_BYTE: - result = (db_get_b(hFinalContact, szModule, szSetting, 0) == buttonItem->bValuePush[0]); - break; - case DBVT_WORD: - result = (db_get_w(hFinalContact, szModule, szSetting, 0) == *((uint16_t *)&buttonItem->bValuePush)); - break; - case DBVT_DWORD: - result = (db_get_dw(hFinalContact, szModule, szSetting, 0) == *((uint32_t *)&buttonItem->bValuePush)); - break; - case DBVT_ASCIIZ: - ptrA szValue(db_get_sa(hFinalContact, szModule, szSetting)); - if (szValue) - result = !mir_strcmp((char*)buttonItem->bValuePush, szValue); - break; - } - SendMessage(hWnd, BM_SETCHECK, result, 0); - buttonItem = buttonItem->nextItem; - } -} - -void CMsgDialog::DM_ScrollToBottom(WPARAM wParam, LPARAM lParam) -{ - if (m_bScrollingDisabled) - return; - - if (IsIconic(m_pContainer->m_hwnd)) - m_bDeferredScroll = true; - - if (m_iLogMode == WANT_BUILTIN_LOG) - ((CLogWindow *)m_pLog)->ScrollToBottom(wParam != 0, lParam != 0); - else - m_pLog->ScrollToBottom(); -} - -void CMsgDialog::DM_RecalcPictureSize() -{ - HBITMAP hbm = ((m_pPanel.isActive()) && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown); - if (hbm) { - BITMAP bminfo; - GetObject(hbm, sizeof(bminfo), &bminfo); - CalcDynamicAvatarSize(&bminfo); - Resize(); - } - else m_pic.cy = m_pic.cx = 60; -} - -void CMsgDialog::DM_UpdateLastMessage() const -{ - if (m_pContainer->m_hwndStatus == nullptr || m_pContainer->m_hwndActive != m_hwnd) - return; - - wchar_t szBuf[100]; - if (m_bShowTyping) { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]); - mir_snwprintf(szBuf, TranslateT("%s is typing a message..."), m_cache->getNick()); - } - else if (m_bStatusSet) { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon); - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str()); - return; - } - else { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0); - - if (m_pContainer->cfg.flags.m_bUinStatusBar) - mir_snwprintf(szBuf, L"UID: %s", m_cache->getUIN()); - else if (m_lastMessage) { - wchar_t date[64], time[64]; - TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"d", date, _countof(date), 0); - if (m_pContainer->cfg.flags.m_bUinStatusBar && mir_wstrlen(date) > 6) - date[mir_wstrlen(date) - 5] = 0; - TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"t", time, _countof(time), 0); - mir_snwprintf(szBuf, TranslateT("Last received: %s at %s"), date, time); - } - else szBuf[0] = 0; - } - - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szBuf); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// create embedded contact list control - -HWND CMsgDialog::DM_CreateClist() -{ - if (!SendLater::Avail) { - CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION); - m_sendMode &= ~SMODE_MULTIPLE; - return nullptr; - } - - HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248, 184, 0, 30, 30, m_hwnd, (HMENU)IDC_CLIST, g_plugin.getInst(), nullptr); - SendMessage(hwndClist, WM_TIMER, 14, 0); - HANDLE hItem = (HANDLE)SendMessage(hwndClist, CLM_FINDCONTACT, m_hContact, 0); - - SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT); - SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL)); - - if (!g_plugin.bAllowOfflineMultisend) - SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE); - - if (hItem) - SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM)hItem, 1); - - SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, Clist::HideEmptyGroups, 0); - SendMessage(hwndClist, CLM_SETUSEGROUPS, Clist::UseGroups, 0); - SendMessage(hwndClist, CLM_FIRST + 106, 0, 1); - SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0); - if (hwndClist) - RedrawWindow(hwndClist, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW); - return hwndClist; -} - -LRESULT CMsgDialog::DM_MouseWheelHandler(WPARAM wParam, LPARAM lParam) -{ - POINT pt; - GetCursorPos(&pt); - - RECT rc; - GetWindowRect(m_message.GetHwnd(), &rc); - if (PtInRect(&rc, pt)) - return 1; - - if (isChat()) { // scroll nick list by just hovering it - RECT rcNicklist; - GetWindowRect(m_nickList.GetHwnd(), &rcNicklist); - if (PtInRect(&rcNicklist, pt)) { - m_nickList.SendMsg(WM_MOUSEWHEEL, wParam, lParam); - return 0; - } - } - - GetWindowRect(m_pLog->GetHwnd(), &rc); - if (PtInRect(&rc, pt)) { - short wDirection = (short)HIWORD(wParam); - - if (LOWORD(wParam) & MK_SHIFT || M.GetByte("fastscroll", 0)) { - if (wDirection < 0) - SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0); - else if (wDirection > 0) - SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0); - } - else SendMessage(m_pLog->GetHwnd(), WM_MOUSEWHEEL, wParam, lParam); - return 0; - } - - if (GetTabItemFromMouse(m_pContainer->m_hwndTabs, &pt) != -1) { - SendMessage(m_pContainer->m_hwndTabs, WM_MOUSEWHEEL, wParam, -1); - return 0; - } - return 1; -} - -void CMsgDialog::DM_FreeTheme() -{ - if (m_hTheme) { - CloseThemeData(m_hTheme); - m_hTheme = nullptr; - } - if (m_hThemeIP) { - CloseThemeData(m_hThemeIP); - m_hThemeIP = nullptr; - } - if (m_hThemeToolbar) { - CloseThemeData(m_hThemeToolbar); - m_hThemeToolbar = nullptr; - } -} - -void CMsgDialog::DM_ThemeChanged() -{ - CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY]; - CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA]; - - m_hTheme = OpenThemeData(m_hwnd, L"EDIT"); - - if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_log->IGNORED)) { - if (m_iLogMode == WANT_BUILTIN_LOG) - LOG()->DisableStaticEdge(); - - if (isChat()) - SetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); - } - - if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_msg->IGNORED)) - SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE); - - m_hThemeIP = M.isAero() ? OpenThemeData(m_hwnd, L"ButtonStyle") : nullptr; - m_hThemeToolbar = (M.isAero() || (!CSkin::m_skinEnabled && M.isVSThemed())) ? OpenThemeData(m_hwnd, L"REBAR") : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// send out message typing notifications (MTN) when the -// user is typing/editing text in the message input area. - -void CMsgDialog::DM_NotifyTyping(int mode) -{ - const char *szProto = m_cache->getActiveProto(); - MCONTACT hContact = m_cache->getActiveContact(); - - // editing user notes or preparing a message for queued delivery -> don't send MTN - if (m_bEditNotesActive || (m_sendMode & SMODE_SENDLATER)) - return; - - // allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle) - if (!g_plugin.getByte(hContact, SRMSGSET_TYPING, g_plugin.bTypingNew)) - return; - - if (szProto == nullptr) // should not, but who knows... - return; - - // check status and capabilities of the protocol - uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0); - if (!(typeCaps & PF4_SUPPORTTYPING)) - return; - - if (isChat()) { - m_nTypeMode = mode; - Chat_DoEventHook(m_si, GC_USER_TYPNOTIFY, 0, 0, m_nTypeMode); - } - else { - uint32_t protoStatus = Proto_GetStatus(szProto); - if (protoStatus < ID_STATUS_ONLINE) - return; - - // check visibility/invisibility lists to not "accidentially" send MTN to contacts who - // should not see them (privacy issue) - uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_VISLIST && db_get_w(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE) - return; - - if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && db_get_w(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE) - return; - - // don't send to contacts which are not permanently added to the contact list, - // unless the option to ignore added status is set. - if (!Contact::OnList(m_hContact) && !g_plugin.bTypingUnknown) - return; - - // End user check - m_nTypeMode = mode; - CallService(MS_PROTO_SELFISTYPING, hContact, m_nTypeMode); - } -} - -void CMsgDialog::DM_OptionsApplied(bool bRemakeLog) -{ - m_szMicroLf[0] = 0; - if (!m_pContainer->m_theme.isPrivate) { - m_pContainer->LoadThemeDefaults(); - m_dwFlags = m_pContainer->m_theme.dwFlags; - } - - LoadLocalFlags(); - m_hTimeZone = TimeZone_CreateByContact(m_hContact, nullptr, TZF_KNOWNONLY); - - m_bShowUIElements = (m_pContainer->cfg.flags.m_bHideToolbar) == 0; - m_bSplitterOverride = M.GetByte(m_hContact, "splitoverride", 0) != 0; - m_pPanel.getVisibility(); - - // small inner margins (padding) for the text areas - m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3)); - - GetSendFormat(); - SetDialogToType(); - SendMessage(m_hwnd, DM_CONFIGURETOOLBAR, 0, 0); - - DM_InitRichEdit(); - if (m_hwnd == m_pContainer->m_hwndActive) - SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0); - InvalidateRect(m_message.GetHwnd(), nullptr, FALSE); - if (bRemakeLog) { - if (IsIconic(m_pContainer->m_hwnd)) - m_bDeferredRemakeLog = true; - else if (isChat()) - RedrawLog(); - else - RemakeLog(); - } - - ShowWindow(m_hwndPanelPicParent, SW_SHOW); - EnableWindow(m_hwndPanelPicParent, TRUE); - - UpdateWindowIcon(); -} - -void CMsgDialog::DM_Typing(bool fForceOff) -{ - HWND hwndContainer = m_pContainer->m_hwnd; - HWND hwndStatus = m_pContainer->m_hwndStatus; - - if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - m_nLastTyping > TIMEOUT_TYPEOFF) - DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF); - - if (m_bShowTyping == 1) { - if (m_nTypeSecs > 0) { - m_nTypeSecs--; - if (GetForegroundWindow() == hwndContainer) - UpdateWindowIcon(); - } - else { - if (!fForceOff) { - m_bShowTyping = 2; - m_nTypeSecs = 86400; - - if (!isChat()) - mir_snwprintf(m_wszStatusBar, TranslateT("%s has entered text."), m_cache->getNick()); - - if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) - SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar); - } - UpdateWindowIcon(); - HandleIconFeedback(this, (HICON)-1); - CMsgDialog *dat_active = (CMsgDialog*)GetWindowLongPtr(m_pContainer->m_hwndActive, GWLP_USERDATA); - if (dat_active && !dat_active->isChat()) - m_pContainer->UpdateTitle(0); - else - m_pContainer->UpdateTitle(0, dat_active); - if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN) - m_pContainer->ReflashContainer(); - } - } - else if (m_bShowTyping == 2) { - if (m_nTypeSecs > 0) - m_nTypeSecs--; - else { - m_wszStatusBar[0] = 0; - m_bShowTyping = 0; - } - tabUpdateStatusBar(); - } - else if (m_nTypeSecs > 0) { - mir_snwprintf(m_wszStatusBar, TranslateT("%s is typing a message"), - (m_pUserTyping) ? m_pUserTyping->pszNick : m_cache->getNick()); - - m_nTypeSecs--; - if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) { - SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar); - SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]); - } - if (IsIconic(hwndContainer) || !IsActive()) { - SetWindowText(hwndContainer, m_wszStatusBar); - m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true; - if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN) - m_pContainer->ReflashContainer(); - } - - if (m_pContainer->m_hwndActive != m_hwnd) { - if (m_bCanFlashTab) - m_iFlashIcon = PluginConfig.g_IconTypingEvent; - HandleIconFeedback(this, PluginConfig.g_IconTypingEvent); - } - else { // active tab may show icon if status bar is disabled - if (!hwndStatus) { - if (TabCtrl_GetItemCount(m_hwndParent) > 1 || !m_pContainer->cfg.flags.m_bHideTabs) - HandleIconFeedback(this, PluginConfig.g_IconTypingEvent); - } - } - if ((GetForegroundWindow() != hwndContainer) || (m_pContainer->m_hwndStatus == nullptr) || (m_pContainer->m_hwndActive != m_hwnd)) - m_pContainer->SetIcon(this, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]); - - m_bShowTyping = 1; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// sync splitter position for all open sessions. -// This cares about private / per container / MUC <> IM splitter syncing and everything. -// called from IM and MUC windows via DM_SPLITTERGLOBALEVENT - -int CMsgDialog::DM_SplitterGlobalEvent(WPARAM wParam, LPARAM lParam) -{ - CMsgDialog *srcDat = PluginConfig.lastSPlitterPos.pSrcDat; - TContainerData *srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer; - bool fCntGlobal = (!m_pContainer->cfg.fPrivate ? true : false); - - if (m_bIsAutosizingInput) - return 0; - - RECT rcWin; - GetWindowRect(m_hwnd, &rcWin); - - LONG newPos; - if (wParam == 0 && lParam == 0) { - if (m_bSplitterOverride && this != srcDat) - return 0; - - if (srcDat->isChat() == isChat()) - newPos = PluginConfig.lastSPlitterPos.pos; - else if (!srcDat->isChat() && isChat()) - newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im; - else if (srcDat->isChat() && !isChat()) - newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im; - else - newPos = 0; - - if (this == srcDat) { - m_pContainer->cfg.iSplitterY = m_iSplitterY; - if (fCntGlobal) - SaveSplitter(); - return 0; - } - - if (!fCntGlobal && m_pContainer != srcCnt) - return 0; - if (srcCnt->cfg.fPrivate && m_pContainer != srcCnt) - return 0; - - // for inactive sessions, delay the splitter repositioning until they become - // active (faster, avoid redraw/resize problems for minimized windows) - if (IsIconic(m_pContainer->m_hwnd) || m_pContainer->m_hwndActive != m_hwnd) { - m_bDelayedSplitter = true; - m_wParam = newPos; - m_lParam = PluginConfig.lastSPlitterPos.lParam; - return 0; - } - } - else newPos = wParam; - - LoadSplitter(); - AdjustBottomAvatarDisplay(); - DM_RecalcPictureSize(); - Resize(); - DM_ScrollToBottom(1, 1); - if (this != srcDat) - UpdateToolbarBG(); - return 0; -} - -void CMsgDialog::DM_AddDivider() -{ - if (!m_bDividerSet && g_plugin.bUseDividers) - if (GetWindowTextLength(m_pLog->GetHwnd()) > 0) - m_bDividerSet = m_bDividerWanted = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// incoming event handler - -void CMsgDialog::DM_EventAdded(WPARAM, LPARAM lParam) -{ - MEVENT hDbEvent = (MEVENT)lParam; - - DBEVENTINFO dbei = {}; - db_event_get(hDbEvent, &dbei); - if (m_hDbEventFirst == 0) - m_hDbEventFirst = hDbEvent; - - bool bIsStatusChangeEvent = IsStatusEvent(dbei.eventType); - bool bDisableNotify = (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ)); - - if (!DbEventIsShown(&dbei)) - return; - - if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) { - m_lastMessage = dbei.timestamp; - m_wszStatusBar[0] = 0; - if (m_bShowTyping) { - m_nTypeSecs = 0; - DM_Typing(true); - m_bShowTyping = 0; - } - HandleIconFeedback(this, (HICON)-1); - if (m_pContainer->m_hwndStatus) - PostMessage(m_hwnd, DM_UPDATELASTMESSAGE, 0, 0); - } - - // set the message log divider to mark new (maybe unseen) messages, if the container has - // been minimized or in the background. - if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) { - if (g_plugin.bDividersUsePopupConfig && g_plugin.bUseDividers) { - if (!MessageWindowOpened(m_hContact, nullptr)) - DM_AddDivider(); - } - else if (g_plugin.bUseDividers) { - if (!m_pContainer->IsActive()) - DM_AddDivider(); - else if (m_pContainer->m_hwndActive != m_hwnd) - DM_AddDivider(); - } - - if (IsWindowVisible(m_pContainer->m_hwnd)) - m_pContainer->m_bHidden = false; - } - m_cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0); - - if (hDbEvent != m_hDbEventFirst || isChat()) - StreamEvents(hDbEvent, 1, 1); - else - RemakeLog(); - - // handle tab flashing - if (!bDisableNotify && !bIsStatusChangeEvent) - if ((TabCtrl_GetCurSel(m_hwndParent) != m_iTabID) && !(dbei.flags & DBEF_SENT)) { - switch (dbei.eventType) { - case EVENTTYPE_MESSAGE: - m_iFlashIcon = PluginConfig.g_IconMsgEvent; - break; - case EVENTTYPE_FILE: - m_iFlashIcon = PluginConfig.g_IconFileEvent; - break; - default: - m_iFlashIcon = PluginConfig.g_IconMsgEvent; - break; - } - timerFlash.Start(TIMEOUT_FLASHWND); - m_bCanFlashTab = true; - } - - // try to flash the contact list... - if (!bDisableNotify) - FlashOnClist(hDbEvent, &dbei); - - // autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch) - // never switch for status changes... - if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) { - if (g_plugin.bAutoSwitchTabs && m_pContainer->m_hwndActive != m_hwnd) { - if ((IsIconic(m_pContainer->m_hwnd) && !IsZoomed(m_pContainer->m_hwnd)) || (g_plugin.bHideOnClose && !IsWindowVisible(m_pContainer->m_hwnd))) { - int iItem = GetTabIndexFromHWND(GetParent(m_hwnd), m_hwnd); - if (iItem >= 0) { - TabCtrl_SetCurSel(m_hwndParent, iItem); - ShowWindow(m_pContainer->m_hwndActive, SW_HIDE); - m_pContainer->m_hwndActive = m_hwnd; - m_pContainer->UpdateTitle(m_hContact); - m_pContainer->cfg.flags.m_bDeferredTabSelect = true; - } - } - } - } - - // flash window if it is not focused - if (!bDisableNotify && !bIsStatusChangeEvent) - if (!IsActive() && !(dbei.flags & DBEF_SENT)) { - if (!m_pContainer->cfg.flags.m_bNoFlash && !m_pContainer->IsActive()) - m_pContainer->FlashContainer(1, 0); - m_pContainer->SetIcon(this, Skin_LoadIcon(SKINICON_EVENT_MESSAGE)); - m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true; - } - - // play a sound - if (!bDisableNotify && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) - PlayIncomingSound(); - - if (m_pWnd) - m_pWnd->Invalidate(); -} - -void CMsgDialog::DM_HandleAutoSizeRequest(REQRESIZE* rr) -{ - if (rr == nullptr || GetForegroundWindow() != m_pContainer->m_hwnd) - return; - - if (!m_bIsAutosizingInput || m_iInputAreaHeight == -1) - return; - - LONG heightLimit = M.GetDword("autoSplitMinLimit", 0); - LONG iNewHeight = rr->rc.bottom - rr->rc.top; - - if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED) - iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2); - - if (heightLimit && iNewHeight < heightLimit) - iNewHeight = heightLimit; - - if (iNewHeight == m_iInputAreaHeight) - return; - - RECT rc; - GetClientRect(m_hwnd, &rc); - LONG cy = rc.bottom - rc.top; - LONG panelHeight = (m_pPanel.isActive() ? m_pPanel.getHeight() : 0); - - if (iNewHeight > (cy - panelHeight) / 2) - iNewHeight = (cy - panelHeight) / 2; - - m_dynaSplitter = iNewHeight - DPISCALEY_S(2); - if (m_pContainer->cfg.flags.m_bBottomToolbar) - m_dynaSplitter += DPISCALEY_S(22); - m_iSplitterY = m_dynaSplitter + DPISCALEY_S(34); - DM_RecalcPictureSize(); - - m_iInputAreaHeight = iNewHeight; - UpdateToolbarBG(); - DM_ScrollToBottom(1, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// status icon stuff (by sje, used for indicating encryption status in the status bar -// this is now part of the message window api - - -static int OnSrmmIconChanged(WPARAM hContact, LPARAM) -{ - if (hContact == 0) - Srmm_Broadcast(DM_STATUSICONCHANGE, 0, 0); - else { - HWND hwnd = Srmm_FindWindow(hContact); - if (hwnd) - PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0); - } - return 0; -} - -void CMsgDialog::DrawStatusIcons(HDC hDC, const RECT &rc, int gap) -{ - int x = rc.left; - int y = (rc.top + rc.bottom - PluginConfig.m_smcxicon) >> 1; - - SetBkMode(hDC, TRANSPARENT); - - int nIcon = 0; - while (StatusIconData *sid = Srmm_GetNthIcon(m_hContact, nIcon++)) { - if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) { - if (sid->dwId == MSG_ICON_SOUND) { - DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS], - PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL); - - DrawIconEx(hDC, x, y, m_pContainer->cfg.flags.m_bNoSound ? - PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled, - PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL); - } - else if (sid->dwId == MSG_ICON_UTN) { - if (AllowTyping()) { - DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL); - - DrawIconEx(hDC, x, y, g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew) ? - PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL); - } - else CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50); - } - } - else { - HICON hIcon; - if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled) - hIcon = sid->hIconDisabled; - else - hIcon = sid->hIcon; - - if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled == nullptr) - CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIcon, 50); - else - DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, nullptr, DI_NORMAL); - } - - x += PluginConfig.m_smcxicon + gap; - } -} - -void CMsgDialog::CheckStatusIconClick(POINT pt, const RECT &rc, int gap, int code) -{ - if (code == NM_CLICK || code == NM_RCLICK) { - POINT ptScreen; - GetCursorPos(&ptScreen); - if (!PtInRect(&rcLastStatusBarClick, ptScreen)) - return; - } - - UINT iconNum = (pt.x - (rc.left + 0)) / (PluginConfig.m_smcxicon + gap); - - StatusIconData *sid = Srmm_GetNthIcon(m_hContact, iconNum); - if (sid == nullptr) - return; - - if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) { - if (sid->dwId == MSG_ICON_SOUND && code != NM_RCLICK) { - if (GetKeyState(VK_SHIFT) & 0x8000) { - for (TContainerData *p = pFirstContainer; p; p = p->pNext) { - p->cfg.flags.m_bNoSound = m_pContainer->cfg.flags.m_bNoSound; - InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE); - } - } - else { - m_pContainer->cfg.flags.m_bNoSound = !m_pContainer->cfg.flags.m_bNoSound; - InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE); - } - } - else if (sid->dwId == MSG_ICON_UTN && code != NM_RCLICK && AllowTyping()) { - SendMessage(m_pContainer->m_hwndActive, WM_COMMAND, IDC_SELFTYPING, 0); - InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE); - } - } - else { - StatusIconClickData sicd = { sizeof(sicd) }; - GetCursorPos(&sicd.clickLocation); - sicd.dwId = sid->dwId; - sicd.szModule = sid->szModule; - sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0); - Srmm_ClickStatusIcon(m_hContact, &sicd); - InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE); - } -} - -void CMsgDialog::DM_ErrorDetected(int type, int flag) -{ - switch (type) { - case MSGERROR_CANCEL: - case MSGERROR_SENDLATER: - if (m_bErrorState) { - m_cache->saveHistory(); - if (type == MSGERROR_SENDLATER) - sendQueue->doSendLater(m_iCurrentQueueError, this); // to be implemented at a later time - m_iOpenJobs--; - sendQueue->dec(); - if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) - sendQueue->clearJob(m_iCurrentQueueError); - m_iCurrentQueueError = -1; - sendQueue->showErrorControls(this, FALSE); - if (type != MSGERROR_CANCEL || flag == 0) - m_message.SetText(L""); - sendQueue->checkQueue(this); - int iNextFailed = sendQueue->findNextFailed(this); - if (iNextFailed >= 0) - sendQueue->handleError(this, iNextFailed); - } - break; - - case MSGERROR_RETRY: - if (m_bErrorState) { - int resent = 0; - - m_cache->saveHistory(); - if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) { - SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError); - if (job->iSendId == 0 && job->hContact == 0) - break; - - job->iSendId = ProtoChainSend(job->hContact, PSS_MESSAGE, job->dwFlags, (LPARAM)job->szSendBuffer); - resent++; - } - - if (resent) { - SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError); - - SetTimer(m_hwnd, TIMERID_MSGSEND + m_iCurrentQueueError, PluginConfig.m_MsgTimeout, nullptr); - job->iStatus = SendQueue::SQ_INPROGRESS; - m_iCurrentQueueError = -1; - sendQueue->showErrorControls(this, FALSE); - m_message.SetText(L""); - sendQueue->checkQueue(this); - - int iNextFailed = sendQueue->findNextFailed(this); - if (iNextFailed >= 0) - sendQueue->handleError(this, iNextFailed); - } - } - } -} - -int SI_InitStatusIcons() -{ - StatusIconData sid = {}; - sid.szModule = MSG_ICON_MODULE; - sid.dwId = MSG_ICON_SOUND; // Sounds - Srmm_AddIcon(&sid, &g_plugin); - - sid.dwId = MSG_ICON_UTN; - Srmm_AddIcon(&sid, &g_plugin); - - HookEvent(ME_MSG_ICONSCHANGED, OnSrmmIconChanged); - return 0; -} +/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// these are generic message handlers which are used by the message dialog window procedure.
+// calling them directly instead of using SendMessage() is faster.
+// also contains various callback functions for custom buttons
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Save message log for given session as RTF document
+
+/*
+void CMsgDialog::DM_SaveLogAsRTF() const
+{
+ if (m_hwndIEView != nullptr) {
+ IEVIEWEVENT event = { sizeof(event) };
+ event.hwnd = m_hwndIEView;
+ event.hContact = m_hContact;
+ event.iType = IEE_SAVE_DOCUMENT;
+ CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+ }
+ else {
+ wchar_t szFilter[MAX_PATH], szFilename[MAX_PATH];
+ mir_snwprintf(szFilter, L"%s%c*.rtf%c%c", TranslateT("Rich Edit file"), 0, 0, 0);
+ mir_snwprintf(szFilename, L"%s.rtf", m_cache->getNick());
+
+ Utils::sanitizeFilename(szFilename);
+
+ wchar_t szInitialDir[MAX_PATH + 2];
+ mir_snwprintf(szInitialDir, L"%s%s\\", M.getDataPath(), L"\\Saved message logs");
+ CreateDirectoryTreeW(szInitialDir);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = m_hwnd;
+ ofn.lpstrFile = szFilename;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrInitialDir = szInitialDir;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.lpstrDefExt = L"rtf";
+ if (GetSaveFileName(&ofn)) {
+ EDITSTREAM stream = { 0 };
+ stream.dwCookie = (DWORD_PTR)szFilename;
+ stream.dwError = 0;
+ stream.pfnCallback = Utils::StreamOut;
+ m_rtf.SendMsg(EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM)&stream);
+ }
+ }
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// checks if the balloon tooltip can be dismissed (usually called by WM_MOUSEMOVE events)
+
+void CMsgDialog::DM_DismissTip(const POINT& pt)
+{
+ if (!IsWindowVisible(m_hwndTip))
+ return;
+
+ RECT rc;
+ GetWindowRect(m_hwndTip, &rc);
+ if (PtInRect(&rc, pt))
+ return;
+
+ if (abs(pt.x - m_ptTipActivation.x) > 5 || abs(pt.y - m_ptTipActivation.y) > 5) {
+ SendMessage(m_hwndTip, TTM_TRACKACTIVATE, FALSE, 0);
+ m_ptTipActivation.x = m_ptTipActivation.y = 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// initialize the balloon tooltip for message window notifications
+
+void CMsgDialog::DM_InitTip()
+{
+ m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, nullptr, g_plugin.getInst(), (LPVOID)nullptr);
+
+ memset(&ti, 0, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.lpszText = TranslateT("No status message");
+ ti.hinst = g_plugin.getInst();
+ ti.hwnd = m_hwnd;
+ ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT;
+ ti.uId = (UINT_PTR)m_hwnd;
+ SendMessage(m_hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ SetWindowPos(m_hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// checks generic hotkeys valid for both IM and MUC sessions
+//
+// returns 1 for handled hotkeys, 0 otherwise.
+
+bool CMsgDialog::DM_GenericHotkeysCheck(MSG *message)
+{
+ LRESULT mim_hotkey_check = Hotkey_Check(message, TABSRMM_HK_SECTION_GENERIC);
+
+ switch (mim_hotkey_check) {
+ case TABSRMM_HK_PASTEANDSEND:
+ HandlePasteAndSend();
+ return true;
+
+ case TABSRMM_HK_HISTORY:
+ m_btnHistory.Click();
+ return true;
+
+ case TABSRMM_HK_CONTAINEROPTIONS:
+ m_pContainer->OptionsDialog();
+ return true;
+
+ case TABSRMM_HK_TOGGLEINFOPANEL:
+ m_pPanel.setActive(!m_pPanel.isActive());
+ m_pPanel.showHide();
+ return true;
+
+ case TABSRMM_HK_TOGGLETOOLBAR:
+ SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLETOOLBAR, 0);
+ return true;
+
+ case TABSRMM_HK_CLEARLOG:
+ tabClearLog();
+ return true;
+
+ case TABSRMM_HK_TOGGLESIDEBAR:
+ if (m_pContainer->m_pSideBar->isActive())
+ SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ return true;
+
+ case TABSRMM_HK_CLOSE_OTHER:
+ CloseOtherTabs(m_pContainer->m_hwndTabs, *this);
+ return true;
+ }
+ return false;
+}
+
+LRESULT CMsgDialog::DM_MsgWindowCmdHandler(UINT cmd, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+ int iSelection;
+ HMENU submenu;
+
+ switch (cmd) {
+ case IDC_SRMM_BOLD:
+ case IDC_SRMM_ITALICS:
+ case IDC_SRMM_UNDERLINE:
+ case IDC_FONTSTRIKEOUT:
+ if (m_SendFormat != 0) { // dont use formatting if disabled
+ CHARFORMAT2 cf, cfOld;
+ memset(&cf, 0, sizeof(CHARFORMAT2));
+ memset(&cfOld, 0, sizeof(CHARFORMAT2));
+ cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2);
+ cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
+ m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld);
+ BOOL isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD);
+ BOOL isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC);
+ BOOL isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE);
+ BOOL isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT);
+
+ int ctrlId = LOWORD(wParam);
+ if (ctrlId == IDC_SRMM_BOLD && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_BOLD)))
+ break;
+ if (ctrlId == IDC_SRMM_ITALICS && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_ITALICS)))
+ break;
+ if (ctrlId == IDC_SRMM_UNDERLINE && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_UNDERLINE)))
+ break;
+ if (ctrlId == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_FONTSTRIKEOUT)))
+ break;
+ if (ctrlId == IDC_SRMM_BOLD) {
+ cf.dwEffects = isBold ? 0 : CFE_BOLD;
+ cf.dwMask = CFM_BOLD;
+ CheckDlgButton(m_hwnd, IDC_SRMM_BOLD, !isBold ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else if (ctrlId == IDC_SRMM_ITALICS) {
+ cf.dwEffects = isItalic ? 0 : CFE_ITALIC;
+ cf.dwMask = CFM_ITALIC;
+ CheckDlgButton(m_hwnd, IDC_SRMM_ITALICS, !isItalic ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else if (ctrlId == IDC_SRMM_UNDERLINE) {
+ cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE;
+ cf.dwMask = CFM_UNDERLINE;
+ CheckDlgButton(m_hwnd, IDC_SRMM_UNDERLINE, !isUnderline ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else if (ctrlId == IDC_FONTSTRIKEOUT) {
+ cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT;
+ cf.dwMask = CFM_STRIKEOUT;
+ CheckDlgButton(m_hwnd, IDC_FONTSTRIKEOUT, !isStrikeout ? BST_CHECKED : BST_UNCHECKED);
+ }
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ }
+ break;
+
+ case IDCANCEL:
+ ShowWindow(m_pContainer->m_hwnd, SW_MINIMIZE);
+ return FALSE;
+
+ case IDC_CLOSE:
+ PostMessage(m_hwnd, WM_CLOSE, 1, 0);
+ break;
+
+ case IDC_NAME:
+ if (GetKeyState(VK_SHIFT) & 0x8000) // copy UIN
+ Utils_ClipboardCopy(m_cache->getUIN());
+ else
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(m_cache->getActiveContact()), 0);
+ break;
+
+ case IDC_TIME:
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 2);
+ MsgWindowUpdateMenu(submenu, MENU_LOGMENU);
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_TIME), &rc);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ return MsgWindowMenuHandler(iSelection, MENU_LOGMENU);
+
+ case IDC_PROTOMENU:
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 4);
+ {
+ bool iOldGlobalSendFormat = g_plugin.bSendFormat;
+ int iLocalFormat = M.GetDword(m_hContact, "sendformat", 0);
+ int iNewLocalFormat = iLocalFormat;
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
+
+ CheckMenuItem(submenu, ID_MODE_GLOBAL, !m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_MODE_PRIVATE, m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
+
+ // formatting menu..
+ CheckMenuItem(submenu, ID_GLOBAL_BBCODE, (g_plugin.bSendFormat) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_GLOBAL_OFF, (g_plugin.bSendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
+
+ CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, (iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, (iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_THISCONTACT_OFF, (iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ switch (iSelection) {
+ case ID_MODE_GLOBAL:
+ m_bSplitterOverride = false;
+ db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 0);
+ LoadSplitter();
+ AdjustBottomAvatarDisplay();
+ DM_RecalcPictureSize();
+ Resize();
+ break;
+
+ case ID_MODE_PRIVATE:
+ m_bSplitterOverride = true;
+ db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 1);
+ LoadSplitter();
+ AdjustBottomAvatarDisplay();
+ DM_RecalcPictureSize();
+ Resize();
+ break;
+
+ case ID_GLOBAL_BBCODE:
+ g_plugin.bSendFormat = SENDFORMAT_BBCODE;
+ break;
+
+ case ID_GLOBAL_OFF:
+ g_plugin.bSendFormat = SENDFORMAT_NONE;
+ break;
+
+ case ID_THISCONTACT_GLOBALSETTING:
+ iNewLocalFormat = 0;
+ break;
+
+ case ID_THISCONTACT_BBCODE:
+ iNewLocalFormat = SENDFORMAT_BBCODE;
+ break;
+
+ case ID_THISCONTACT_OFF:
+ iNewLocalFormat = -1;
+ break;
+ }
+
+ if (iNewLocalFormat == 0)
+ db_unset(m_hContact, SRMSGMOD_T, "sendformat");
+ else if (iNewLocalFormat != iLocalFormat)
+ db_set_dw(m_hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat);
+
+ if (iNewLocalFormat != iLocalFormat || g_plugin.bSendFormat != iOldGlobalSendFormat) {
+ m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
+ if (m_SendFormat == -1) // per contact override to disable it..
+ m_SendFormat = 0;
+ Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
+ }
+ }
+ break;
+
+ case IDC_TOGGLETOOLBAR:
+ if (lParam == 1)
+ m_pContainer->cfg.flags.m_bNoMenuBar = !m_pContainer->cfg.flags.m_bNoMenuBar;
+ else
+ m_pContainer->cfg.flags.m_bHideToolbar = !m_pContainer->cfg.flags.m_bHideToolbar;
+ m_pContainer->ApplySetting(true);
+ break;
+
+ case IDC_SENDMENU:
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 3);
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDOK), &rc);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, (m_sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, m_sendMode == 0 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, (m_sendMode & SMODE_CONTAINER) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, (m_sendMode & SMODE_SENDLATER) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, (m_sendMode & SMODE_NOACK) ? MF_CHECKED : MF_UNCHECKED);
+
+ if (lParam)
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ else
+ iSelection = HIWORD(wParam);
+
+ switch (iSelection) {
+ case ID_SENDMENU_SENDTOMULTIPLEUSERS:
+ m_sendMode ^= SMODE_MULTIPLE;
+ if (m_sendMode & SMODE_MULTIPLE)
+ DM_CreateClist();
+ else if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
+ break;
+ case ID_SENDMENU_SENDDEFAULT:
+ m_sendMode = 0;
+ break;
+ case ID_SENDMENU_SENDTOCONTAINER:
+ m_sendMode ^= SMODE_CONTAINER;
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW);
+ break;
+ case ID_SENDMENU_SENDLATER:
+ if (SendLater::Avail)
+ m_sendMode ^= SMODE_SENDLATER;
+ else
+ CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
+ break;
+ case ID_SENDMENU_SENDWITHOUTTIMEOUTS:
+ m_sendMode ^= SMODE_NOACK;
+ if (m_sendMode & SMODE_NOACK)
+ db_set_b(m_hContact, SRMSGMOD_T, "no_ack", 1);
+ else
+ db_unset(m_hContact, SRMSGMOD_T, "no_ack");
+ break;
+ }
+ db_set_b(m_hContact, SRMSGMOD_T, "no_ack", (uint8_t)(m_sendMode & SMODE_NOACK ? 1 : 0));
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
+ if (m_sendMode & SMODE_MULTIPLE || m_sendMode & SMODE_CONTAINER) {
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ else {
+ if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ m_pContainer->QueryClientArea(rc);
+ Resize();
+ DM_ScrollToBottom(1, 1);
+ Utils::showDlgControl(m_hwnd, IDC_MULTISPLITTER, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
+ Utils::showDlgControl(m_hwnd, IDC_CLIST, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
+ break;
+
+ case IDC_TOGGLESIDEBAR:
+ SendMessage(m_pContainer->m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ break;
+
+ case IDC_PIC:
+ GetClientRect(m_hwnd, &rc);
+
+ m_bEditNotesActive = !m_bEditNotesActive;
+ if (m_bEditNotesActive) {
+ int iLen = GetWindowTextLength(m_message.GetHwnd());
+ if (iLen != 0) {
+ ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("You cannot edit user notes when there are unsent messages"));
+ m_bEditNotesActive = false;
+ break;
+ }
+
+ if (!m_bIsAutosizingInput) {
+ m_iSplitterSaved = m_iSplitterY;
+ m_iSplitterY = rc.bottom / 2;
+ SendMessage(m_hwnd, WM_SIZE, 1, 1);
+ }
+
+ ptrW wszText(db_get_wsa(m_hContact, "UserInfo", "MyNotes"));
+ if (wszText != nullptr)
+ m_message.SetText(wszText);
+ }
+ else {
+ ptrW buf(m_message.GetText());
+ db_set_ws(m_hContact, "UserInfo", "MyNotes", buf);
+ m_message.SetText(L"");
+
+ if (!m_bIsAutosizingInput) {
+ m_iSplitterY = m_iSplitterSaved;
+ Resize();
+ DM_ScrollToBottom(0, 1);
+ }
+ }
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+ if (m_bEditNotesActive)
+ CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK | MB_ICONINFORMATION);
+ break;
+
+ case IDM_CLEAR:
+ tabClearLog();
+ break;
+
+ case IDC_PROTOCOL:
+ submenu = Menu_BuildContactMenu(m_hContact);
+ if (lParam == 0)
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
+ else
+ GetWindowRect((HWND)lParam, &rc);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ if (iSelection)
+ Clist_MenuProcessCommand(LOWORD(iSelection), MPCF_CONTACTMENU, m_hContact);
+
+ DestroyMenu(submenu);
+ break;
+
+ // error control
+ case IDC_CANCELSEND:
+ DM_ErrorDetected(MSGERROR_CANCEL, 0);
+ break;
+
+ case IDC_RETRY:
+ DM_ErrorDetected(MSGERROR_RETRY, 0);
+ break;
+
+ case IDC_MSGSENDLATER:
+ DM_ErrorDetected(MSGERROR_SENDLATER, 0);
+ break;
+
+ case IDC_SELFTYPING:
+ if (AllowTyping()) {
+ int iCurrentTypingMode = g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew);
+ if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) {
+ DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
+ m_nTypeMode = PROTOTYPE_SELFTYPING_OFF;
+ }
+ g_plugin.setByte(m_hContact, SRMSGSET_TYPING, (uint8_t)!iCurrentTypingMode);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// initialize rich edit control (log and edit control) for both MUC and
+// standard IM session windows.
+
+void CMsgDialog::DM_InitRichEdit()
+{
+ char *szStreamOut = nullptr;
+ if (!isChat() && GetWindowTextLength(m_message.GetHwnd()) > 0)
+ szStreamOut = m_message.GetRichTextRtf();
+ SetWindowText(m_message.GetHwnd(), L"");
+
+ m_pLog->UpdateOptions();
+
+ m_message.SendMsg(EM_SETBKGNDCOLOR, 0, m_pContainer->m_theme.inputbg);
+
+ CHARFORMAT2 cf2 = {};
+ cf2.cbSize = sizeof(cf2);
+
+ if (isChat()) {
+ LOGFONTW lf;
+ COLORREF inputcharcolor;
+ LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor);
+
+ cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR;
+ cf2.crTextColor = inputcharcolor;
+ cf2.bCharSet = lf.lfCharSet;
+ cf2.crBackColor = m_pContainer->m_theme.inputbg;
+ wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
+ cf2.dwEffects = 0;
+ cf2.wWeight = (uint16_t)lf.lfWeight;
+ cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+ cf2.yHeight = abs(lf.lfHeight) * 15;
+ }
+ else {
+ LOGFONTW lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
+ COLORREF inputcharcolor = m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA];
+
+ for (auto &it : Utils::rtf_clrs)
+ if (it->clr == inputcharcolor)
+ inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1);
+
+ cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC;
+ cf2.crTextColor = inputcharcolor;
+ cf2.bCharSet = lf.lfCharSet;
+ wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
+ cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0);
+ cf2.wWeight = (uint16_t)lf.lfWeight;
+ cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+ cf2.yHeight = abs(lf.lfHeight) * 15;
+ }
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); /* WINE: fix send colour text. */
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2); /* WINE: fix send colour text. */
+
+ // setup the rich edit control(s)
+ // LOG is always set to RTL, because this is needed for proper bidirectional operation later.
+ // The real text direction is then enforced by the streaming code which adds appropiate paragraph
+ // and textflow formatting commands to the
+ PARAFORMAT2 pf2;
+ memset(&pf2, 0, sizeof(PARAFORMAT2));
+ pf2.cbSize = sizeof(pf2);
+ pf2.wEffects = PFE_RTLPARA;
+ pf2.dwMask = PFM_RTLPARA;
+ if (FindRTLLocale())
+ m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ if (!(m_dwFlags & MWF_LOG_RTL)) {
+ pf2.wEffects = 0;
+ m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ }
+ m_message.SendMsg(EM_SETLANGOPTIONS, 0, (LPARAM)m_message.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
+
+ if (m_dwFlags & MWF_LOG_RTL)
+ SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
+ else
+ SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
+
+ if (szStreamOut != nullptr) {
+ SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
+ m_message.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut);
+ mir_free(szStreamOut);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// set the states of defined database action buttons(only if button is a toggle)
+
+void CMsgDialog::DM_SetDBButtonStates()
+{
+ ButtonItem *buttonItem = m_pContainer->m_buttonItems;
+ MCONTACT hFinalContact = 0;
+ HWND hwndContainer = m_pContainer->m_hwnd;
+
+ while (buttonItem) {
+ HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId);
+
+ if (buttonItem->pfnCallback)
+ buttonItem->pfnCallback(buttonItem, m_hwnd, this, hWnd);
+
+ if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+
+ BOOL result = FALSE;
+ char *szModule = buttonItem->szModule;
+ char *szSetting = buttonItem->szSetting;
+ if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = Proto_GetBaseAccountName(m_hContact);
+ hFinalContact = m_hContact;
+ }
+ else hFinalContact = 0;
+
+ switch (buttonItem->type) {
+ case DBVT_BYTE:
+ result = (db_get_b(hFinalContact, szModule, szSetting, 0) == buttonItem->bValuePush[0]);
+ break;
+ case DBVT_WORD:
+ result = (db_get_w(hFinalContact, szModule, szSetting, 0) == *((uint16_t *)&buttonItem->bValuePush));
+ break;
+ case DBVT_DWORD:
+ result = (db_get_dw(hFinalContact, szModule, szSetting, 0) == *((uint32_t *)&buttonItem->bValuePush));
+ break;
+ case DBVT_ASCIIZ:
+ ptrA szValue(db_get_sa(hFinalContact, szModule, szSetting));
+ if (szValue)
+ result = !mir_strcmp((char*)buttonItem->bValuePush, szValue);
+ break;
+ }
+ SendMessage(hWnd, BM_SETCHECK, result, 0);
+ buttonItem = buttonItem->nextItem;
+ }
+}
+
+void CMsgDialog::DM_ScrollToBottom(WPARAM wParam, LPARAM lParam)
+{
+ if (m_bScrollingDisabled)
+ return;
+
+ if (IsIconic(m_pContainer->m_hwnd))
+ m_bDeferredScroll = true;
+
+ if (m_iLogMode == WANT_BUILTIN_LOG)
+ ((CLogWindow *)m_pLog)->ScrollToBottom(wParam != 0, lParam != 0);
+ else
+ m_pLog->ScrollToBottom();
+}
+
+void CMsgDialog::DM_RecalcPictureSize()
+{
+ HBITMAP hbm = ((m_pPanel.isActive()) && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
+ if (hbm) {
+ BITMAP bminfo;
+ GetObject(hbm, sizeof(bminfo), &bminfo);
+ CalcDynamicAvatarSize(&bminfo);
+ Resize();
+ }
+ else m_pic.cy = m_pic.cx = 60;
+}
+
+void CMsgDialog::DM_UpdateLastMessage() const
+{
+ if (m_pContainer->m_hwndStatus == nullptr || m_pContainer->m_hwndActive != m_hwnd)
+ return;
+
+ wchar_t szBuf[100];
+ if (m_bShowTyping) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ mir_snwprintf(szBuf, TranslateT("%s is typing a message..."), m_cache->getNick());
+ }
+ else if (m_bStatusSet) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+ return;
+ }
+ else {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+
+ if (m_pContainer->cfg.flags.m_bUinStatusBar)
+ mir_snwprintf(szBuf, L"UID: %s", m_cache->getUIN());
+ else if (m_lastMessage) {
+ wchar_t date[64], time[64];
+ TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"d", date, _countof(date), 0);
+ if (m_pContainer->cfg.flags.m_bUinStatusBar && mir_wstrlen(date) > 6)
+ date[mir_wstrlen(date) - 5] = 0;
+ TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"t", time, _countof(time), 0);
+ mir_snwprintf(szBuf, TranslateT("Last received: %s at %s"), date, time);
+ }
+ else szBuf[0] = 0;
+ }
+
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szBuf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// create embedded contact list control
+
+HWND CMsgDialog::DM_CreateClist()
+{
+ if (!SendLater::Avail) {
+ CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
+ m_sendMode &= ~SMODE_MULTIPLE;
+ return nullptr;
+ }
+
+ HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248, 184, 0, 30, 30, m_hwnd, (HMENU)IDC_CLIST, g_plugin.getInst(), nullptr);
+ SendMessage(hwndClist, WM_TIMER, 14, 0);
+ HANDLE hItem = (HANDLE)SendMessage(hwndClist, CLM_FINDCONTACT, m_hContact, 0);
+
+ SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT);
+ SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL));
+
+ if (!g_plugin.bAllowOfflineMultisend)
+ SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE);
+
+ if (hItem)
+ SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM)hItem, 1);
+
+ SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, Clist::HideEmptyGroups, 0);
+ SendMessage(hwndClist, CLM_SETUSEGROUPS, Clist::UseGroups, 0);
+ SendMessage(hwndClist, CLM_FIRST + 106, 0, 1);
+ SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0);
+ if (hwndClist)
+ RedrawWindow(hwndClist, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
+ return hwndClist;
+}
+
+LRESULT CMsgDialog::DM_MouseWheelHandler(WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rc;
+ GetWindowRect(m_message.GetHwnd(), &rc);
+ if (PtInRect(&rc, pt))
+ return 1;
+
+ if (isChat()) { // scroll nick list by just hovering it
+ RECT rcNicklist;
+ GetWindowRect(m_nickList.GetHwnd(), &rcNicklist);
+ if (PtInRect(&rcNicklist, pt)) {
+ m_nickList.SendMsg(WM_MOUSEWHEEL, wParam, lParam);
+ return 0;
+ }
+ }
+
+ GetWindowRect(m_pLog->GetHwnd(), &rc);
+ if (PtInRect(&rc, pt)) {
+ short wDirection = (short)HIWORD(wParam);
+
+ if (LOWORD(wParam) & MK_SHIFT || M.GetByte("fastscroll", 0)) {
+ if (wDirection < 0)
+ SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
+ else if (wDirection > 0)
+ SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
+ }
+ else SendMessage(m_pLog->GetHwnd(), WM_MOUSEWHEEL, wParam, lParam);
+ return 0;
+ }
+
+ if (GetTabItemFromMouse(m_pContainer->m_hwndTabs, &pt) != -1) {
+ SendMessage(m_pContainer->m_hwndTabs, WM_MOUSEWHEEL, wParam, -1);
+ return 0;
+ }
+ return 1;
+}
+
+void CMsgDialog::DM_FreeTheme()
+{
+ if (m_hTheme) {
+ CloseThemeData(m_hTheme);
+ m_hTheme = nullptr;
+ }
+ if (m_hThemeIP) {
+ CloseThemeData(m_hThemeIP);
+ m_hThemeIP = nullptr;
+ }
+ if (m_hThemeToolbar) {
+ CloseThemeData(m_hThemeToolbar);
+ m_hThemeToolbar = nullptr;
+ }
+}
+
+void CMsgDialog::DM_ThemeChanged()
+{
+ CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY];
+ CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA];
+
+ m_hTheme = OpenThemeData(m_hwnd, L"EDIT");
+
+ if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_log->IGNORED)) {
+ if (m_iLogMode == WANT_BUILTIN_LOG)
+ LOG()->DisableStaticEdge();
+
+ if (isChat())
+ SetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+ }
+
+ if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_msg->IGNORED))
+ SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+
+ m_hThemeIP = M.isAero() ? OpenThemeData(m_hwnd, L"ButtonStyle") : nullptr;
+ m_hThemeToolbar = (M.isAero() || (!CSkin::m_skinEnabled && M.isVSThemed())) ? OpenThemeData(m_hwnd, L"REBAR") : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send out message typing notifications (MTN) when the
+// user is typing/editing text in the message input area.
+
+void CMsgDialog::DM_NotifyTyping(int mode)
+{
+ const char *szProto = m_cache->getActiveProto();
+ MCONTACT hContact = m_cache->getActiveContact();
+
+ // editing user notes or preparing a message for queued delivery -> don't send MTN
+ if (m_bEditNotesActive || (m_sendMode & SMODE_SENDLATER))
+ return;
+
+ // allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle)
+ if (!g_plugin.getByte(hContact, SRMSGSET_TYPING, g_plugin.bTypingNew))
+ return;
+
+ if (szProto == nullptr) // should not, but who knows...
+ return;
+
+ // check status and capabilities of the protocol
+ uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+ if (!(typeCaps & PF4_SUPPORTTYPING))
+ return;
+
+ if (isChat()) {
+ m_nTypeMode = mode;
+ Chat_DoEventHook(m_si, GC_USER_TYPNOTIFY, 0, 0, m_nTypeMode);
+ }
+ else {
+ uint32_t protoStatus = Proto_GetStatus(szProto);
+ if (protoStatus < ID_STATUS_ONLINE)
+ return;
+
+ // check visibility/invisibility lists to not "accidentially" send MTN to contacts who
+ // should not see them (privacy issue)
+ uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_VISLIST && db_get_w(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+ return;
+
+ if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && db_get_w(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+ return;
+
+ // don't send to contacts which are not permanently added to the contact list,
+ // unless the option to ignore added status is set.
+ if (!Contact::OnList(m_hContact) && !g_plugin.bTypingUnknown)
+ return;
+
+ // End user check
+ m_nTypeMode = mode;
+ CallService(MS_PROTO_SELFISTYPING, hContact, m_nTypeMode);
+ }
+}
+
+void CMsgDialog::DM_OptionsApplied(bool bRemakeLog)
+{
+ m_szMicroLf[0] = 0;
+ if (!m_pContainer->m_theme.isPrivate) {
+ m_pContainer->LoadThemeDefaults();
+ m_dwFlags = m_pContainer->m_theme.dwFlags;
+ }
+
+ LoadLocalFlags();
+ m_hTimeZone = TimeZone_CreateByContact(m_hContact, nullptr, TZF_KNOWNONLY);
+
+ m_bShowUIElements = (m_pContainer->cfg.flags.m_bHideToolbar) == 0;
+ m_bSplitterOverride = M.GetByte(m_hContact, "splitoverride", 0) != 0;
+ m_pPanel.getVisibility();
+
+ // small inner margins (padding) for the text areas
+ m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
+
+ GetSendFormat();
+ SetDialogToType();
+ SendMessage(m_hwnd, DM_CONFIGURETOOLBAR, 0, 0);
+
+ DM_InitRichEdit();
+ if (m_hwnd == m_pContainer->m_hwndActive)
+ SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
+ InvalidateRect(m_message.GetHwnd(), nullptr, FALSE);
+ if (bRemakeLog) {
+ if (IsIconic(m_pContainer->m_hwnd))
+ m_bDeferredRemakeLog = true;
+ else if (isChat())
+ RedrawLog();
+ else
+ RemakeLog();
+ }
+
+ ShowWindow(m_hwndPanelPicParent, SW_SHOW);
+ EnableWindow(m_hwndPanelPicParent, TRUE);
+
+ UpdateWindowIcon();
+}
+
+void CMsgDialog::DM_Typing(bool fForceOff)
+{
+ HWND hwndContainer = m_pContainer->m_hwnd;
+ HWND hwndStatus = m_pContainer->m_hwndStatus;
+
+ if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - m_nLastTyping > TIMEOUT_TYPEOFF)
+ DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
+
+ if (m_bShowTyping == 1) {
+ if (m_nTypeSecs > 0) {
+ m_nTypeSecs--;
+ if (GetForegroundWindow() == hwndContainer)
+ UpdateWindowIcon();
+ }
+ else {
+ if (!fForceOff) {
+ m_bShowTyping = 2;
+ m_nTypeSecs = 86400;
+
+ if (!isChat())
+ mir_snwprintf(m_wszStatusBar, TranslateT("%s has entered text."), m_cache->getNick());
+
+ if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd)
+ SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+ }
+ UpdateWindowIcon();
+ HandleIconFeedback(this, (HICON)-1);
+ CMsgDialog *dat_active = (CMsgDialog*)GetWindowLongPtr(m_pContainer->m_hwndActive, GWLP_USERDATA);
+ if (dat_active && !dat_active->isChat())
+ m_pContainer->UpdateTitle(0);
+ else
+ m_pContainer->UpdateTitle(0, dat_active);
+ if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
+ m_pContainer->ReflashContainer();
+ }
+ }
+ else if (m_bShowTyping == 2) {
+ if (m_nTypeSecs > 0)
+ m_nTypeSecs--;
+ else {
+ m_wszStatusBar[0] = 0;
+ m_bShowTyping = 0;
+ }
+ tabUpdateStatusBar();
+ }
+ else if (m_nTypeSecs > 0) {
+ mir_snwprintf(m_wszStatusBar, TranslateT("%s is typing a message"),
+ (m_pUserTyping) ? m_pUserTyping->pszNick : m_cache->getNick());
+
+ m_nTypeSecs--;
+ if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
+ SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+ SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ }
+ if (IsIconic(hwndContainer) || !IsActive()) {
+ SetWindowText(hwndContainer, m_wszStatusBar);
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
+ if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
+ m_pContainer->ReflashContainer();
+ }
+
+ if (m_pContainer->m_hwndActive != m_hwnd) {
+ if (m_bCanFlashTab)
+ m_iFlashIcon = PluginConfig.g_IconTypingEvent;
+ HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
+ }
+ else { // active tab may show icon if status bar is disabled
+ if (!hwndStatus) {
+ if (TabCtrl_GetItemCount(m_hwndParent) > 1 || !m_pContainer->cfg.flags.m_bHideTabs)
+ HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
+ }
+ }
+ if ((GetForegroundWindow() != hwndContainer) || (m_pContainer->m_hwndStatus == nullptr) || (m_pContainer->m_hwndActive != m_hwnd))
+ m_pContainer->SetIcon(this, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+
+ m_bShowTyping = 1;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sync splitter position for all open sessions.
+// This cares about private / per container / MUC <> IM splitter syncing and everything.
+// called from IM and MUC windows via DM_SPLITTERGLOBALEVENT
+
+int CMsgDialog::DM_SplitterGlobalEvent(WPARAM wParam, LPARAM lParam)
+{
+ CMsgDialog *srcDat = PluginConfig.lastSPlitterPos.pSrcDat;
+ TContainerData *srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer;
+ bool fCntGlobal = (!m_pContainer->cfg.fPrivate ? true : false);
+
+ if (m_bIsAutosizingInput)
+ return 0;
+
+ RECT rcWin;
+ GetWindowRect(m_hwnd, &rcWin);
+
+ LONG newPos;
+ if (wParam == 0 && lParam == 0) {
+ if (m_bSplitterOverride && this != srcDat)
+ return 0;
+
+ if (srcDat->isChat() == isChat())
+ newPos = PluginConfig.lastSPlitterPos.pos;
+ else if (!srcDat->isChat() && isChat())
+ newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+ else if (srcDat->isChat() && !isChat())
+ newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+ else
+ newPos = 0;
+
+ if (this == srcDat) {
+ m_pContainer->cfg.iSplitterY = m_iSplitterY;
+ if (fCntGlobal)
+ SaveSplitter();
+ return 0;
+ }
+
+ if (!fCntGlobal && m_pContainer != srcCnt)
+ return 0;
+ if (srcCnt->cfg.fPrivate && m_pContainer != srcCnt)
+ return 0;
+
+ // for inactive sessions, delay the splitter repositioning until they become
+ // active (faster, avoid redraw/resize problems for minimized windows)
+ if (IsIconic(m_pContainer->m_hwnd) || m_pContainer->m_hwndActive != m_hwnd) {
+ m_bDelayedSplitter = true;
+ m_wParam = newPos;
+ m_lParam = PluginConfig.lastSPlitterPos.lParam;
+ return 0;
+ }
+ }
+ else newPos = wParam;
+
+ LoadSplitter();
+ AdjustBottomAvatarDisplay();
+ DM_RecalcPictureSize();
+ Resize();
+ DM_ScrollToBottom(1, 1);
+ if (this != srcDat)
+ UpdateToolbarBG();
+ return 0;
+}
+
+void CMsgDialog::DM_AddDivider()
+{
+ if (!m_bDividerSet && g_plugin.bUseDividers)
+ if (GetWindowTextLength(m_pLog->GetHwnd()) > 0)
+ m_bDividerSet = m_bDividerWanted = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// incoming event handler
+
+void CMsgDialog::DM_EventAdded(WPARAM, LPARAM lParam)
+{
+ MEVENT hDbEvent = (MEVENT)lParam;
+
+ DBEVENTINFO dbei = {};
+ db_event_get(hDbEvent, &dbei);
+ if (m_hDbEventFirst == 0)
+ m_hDbEventFirst = hDbEvent;
+
+ bool bIsStatusChangeEvent = IsStatusEvent(dbei.eventType);
+ bool bDisableNotify = (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ));
+
+ if (!DbEventIsShown(&dbei))
+ return;
+
+ if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
+ m_lastMessage = dbei.timestamp;
+ m_wszStatusBar[0] = 0;
+ if (m_bShowTyping) {
+ m_nTypeSecs = 0;
+ DM_Typing(true);
+ m_bShowTyping = 0;
+ }
+ HandleIconFeedback(this, (HICON)-1);
+ if (m_pContainer->m_hwndStatus)
+ PostMessage(m_hwnd, DM_UPDATELASTMESSAGE, 0, 0);
+ }
+
+ // set the message log divider to mark new (maybe unseen) messages, if the container has
+ // been minimized or in the background.
+ if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
+ if (g_plugin.bDividersUsePopupConfig && g_plugin.bUseDividers) {
+ if (!MessageWindowOpened(m_hContact, nullptr))
+ DM_AddDivider();
+ }
+ else if (g_plugin.bUseDividers) {
+ if (!m_pContainer->IsActive())
+ DM_AddDivider();
+ else if (m_pContainer->m_hwndActive != m_hwnd)
+ DM_AddDivider();
+ }
+
+ if (IsWindowVisible(m_pContainer->m_hwnd))
+ m_pContainer->m_bHidden = false;
+ }
+ m_cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0);
+
+ if (hDbEvent != m_hDbEventFirst || isChat())
+ StreamEvents(hDbEvent, 1, 1);
+ else
+ RemakeLog();
+
+ // handle tab flashing
+ if (!bDisableNotify && !bIsStatusChangeEvent)
+ if ((TabCtrl_GetCurSel(m_hwndParent) != m_iTabID) && !(dbei.flags & DBEF_SENT)) {
+ switch (dbei.eventType) {
+ case EVENTTYPE_MESSAGE:
+ m_iFlashIcon = PluginConfig.g_IconMsgEvent;
+ break;
+ case EVENTTYPE_FILE:
+ m_iFlashIcon = PluginConfig.g_IconFileEvent;
+ break;
+ default:
+ m_iFlashIcon = PluginConfig.g_IconMsgEvent;
+ break;
+ }
+ timerFlash.Start(TIMEOUT_FLASHWND);
+ m_bCanFlashTab = true;
+ }
+
+ // try to flash the contact list...
+ if (!bDisableNotify)
+ FlashOnClist(hDbEvent, &dbei);
+
+ // autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch)
+ // never switch for status changes...
+ if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
+ if (g_plugin.bAutoSwitchTabs && m_pContainer->m_hwndActive != m_hwnd) {
+ if ((IsIconic(m_pContainer->m_hwnd) && !IsZoomed(m_pContainer->m_hwnd)) || (g_plugin.bHideOnClose && !IsWindowVisible(m_pContainer->m_hwnd))) {
+ int iItem = GetTabIndexFromHWND(GetParent(m_hwnd), m_hwnd);
+ if (iItem >= 0) {
+ TabCtrl_SetCurSel(m_hwndParent, iItem);
+ ShowWindow(m_pContainer->m_hwndActive, SW_HIDE);
+ m_pContainer->m_hwndActive = m_hwnd;
+ m_pContainer->UpdateTitle(m_hContact);
+ m_pContainer->cfg.flags.m_bDeferredTabSelect = true;
+ }
+ }
+ }
+ }
+
+ // flash window if it is not focused
+ if (!bDisableNotify && !bIsStatusChangeEvent)
+ if (!IsActive() && !(dbei.flags & DBEF_SENT)) {
+ if (!m_pContainer->cfg.flags.m_bNoFlash && !m_pContainer->IsActive())
+ m_pContainer->FlashContainer(1, 0);
+ m_pContainer->SetIcon(this, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
+ }
+
+ // play a sound
+ if (!bDisableNotify && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT)))
+ PlayIncomingSound();
+
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+}
+
+void CMsgDialog::DM_HandleAutoSizeRequest(REQRESIZE* rr)
+{
+ if (rr == nullptr || GetForegroundWindow() != m_pContainer->m_hwnd)
+ return;
+
+ if (!m_bIsAutosizingInput || m_iInputAreaHeight == -1)
+ return;
+
+ LONG heightLimit = M.GetDword("autoSplitMinLimit", 0);
+ LONG iNewHeight = rr->rc.bottom - rr->rc.top;
+
+ if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+ iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2);
+
+ if (heightLimit && iNewHeight < heightLimit)
+ iNewHeight = heightLimit;
+
+ if (iNewHeight == m_iInputAreaHeight)
+ return;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ LONG cy = rc.bottom - rc.top;
+ LONG panelHeight = (m_pPanel.isActive() ? m_pPanel.getHeight() : 0);
+
+ if (iNewHeight > (cy - panelHeight) / 2)
+ iNewHeight = (cy - panelHeight) / 2;
+
+ m_dynaSplitter = iNewHeight - DPISCALEY_S(2);
+ if (m_pContainer->cfg.flags.m_bBottomToolbar)
+ m_dynaSplitter += DPISCALEY_S(22);
+ m_iSplitterY = m_dynaSplitter + DPISCALEY_S(34);
+ DM_RecalcPictureSize();
+
+ m_iInputAreaHeight = iNewHeight;
+ UpdateToolbarBG();
+ DM_ScrollToBottom(1, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// status icon stuff (by sje, used for indicating encryption status in the status bar
+// this is now part of the message window api
+
+
+static int OnSrmmIconChanged(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0)
+ Srmm_Broadcast(DM_STATUSICONCHANGE, 0, 0);
+ else {
+ HWND hwnd = Srmm_FindWindow(hContact);
+ if (hwnd)
+ PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
+ }
+ return 0;
+}
+
+void CMsgDialog::DrawStatusIcons(HDC hDC, const RECT &rc, int gap)
+{
+ int x = rc.left;
+ int y = (rc.top + rc.bottom - PluginConfig.m_smcxicon) >> 1;
+
+ SetBkMode(hDC, TRANSPARENT);
+
+ int nIcon = 0;
+ while (StatusIconData *sid = Srmm_GetNthIcon(m_hContact, nIcon++)) {
+ if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
+ if (sid->dwId == MSG_ICON_SOUND) {
+ DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS],
+ PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+
+ DrawIconEx(hDC, x, y, m_pContainer->cfg.flags.m_bNoSound ?
+ PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled,
+ PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+ }
+ else if (sid->dwId == MSG_ICON_UTN) {
+ if (AllowTyping()) {
+ DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+
+ DrawIconEx(hDC, x, y, g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew) ?
+ PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+ }
+ else CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50);
+ }
+ }
+ else {
+ HICON hIcon;
+ if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled)
+ hIcon = sid->hIconDisabled;
+ else
+ hIcon = sid->hIcon;
+
+ if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled == nullptr)
+ CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIcon, 50);
+ else
+ DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+ }
+
+ x += PluginConfig.m_smcxicon + gap;
+ }
+}
+
+void CMsgDialog::CheckStatusIconClick(POINT pt, const RECT &rc, int gap, int code)
+{
+ if (code == NM_CLICK || code == NM_RCLICK) {
+ POINT ptScreen;
+ GetCursorPos(&ptScreen);
+ if (!PtInRect(&rcLastStatusBarClick, ptScreen))
+ return;
+ }
+
+ UINT iconNum = (pt.x - (rc.left + 0)) / (PluginConfig.m_smcxicon + gap);
+
+ StatusIconData *sid = Srmm_GetNthIcon(m_hContact, iconNum);
+ if (sid == nullptr)
+ return;
+
+ if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
+ if (sid->dwId == MSG_ICON_SOUND && code != NM_RCLICK) {
+ if (GetKeyState(VK_SHIFT) & 0x8000) {
+ for (TContainerData *p = pFirstContainer; p; p = p->pNext) {
+ p->cfg.flags.m_bNoSound = m_pContainer->cfg.flags.m_bNoSound;
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+ }
+ else {
+ m_pContainer->cfg.flags.m_bNoSound = !m_pContainer->cfg.flags.m_bNoSound;
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+ }
+ else if (sid->dwId == MSG_ICON_UTN && code != NM_RCLICK && AllowTyping()) {
+ SendMessage(m_pContainer->m_hwndActive, WM_COMMAND, IDC_SELFTYPING, 0);
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+ }
+ else {
+ StatusIconClickData sicd = { sizeof(sicd) };
+ GetCursorPos(&sicd.clickLocation);
+ sicd.dwId = sid->dwId;
+ sicd.szModule = sid->szModule;
+ sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
+ Srmm_ClickStatusIcon(m_hContact, &sicd);
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+}
+
+void CMsgDialog::DM_ErrorDetected(int type, int flag)
+{
+ switch (type) {
+ case MSGERROR_CANCEL:
+ case MSGERROR_SENDLATER:
+ if (m_bErrorState) {
+ m_cache->saveHistory();
+ if (type == MSGERROR_SENDLATER)
+ sendQueue->doSendLater(m_iCurrentQueueError, this); // to be implemented at a later time
+ m_iOpenJobs--;
+ sendQueue->dec();
+ if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS)
+ sendQueue->clearJob(m_iCurrentQueueError);
+ m_iCurrentQueueError = -1;
+ sendQueue->showErrorControls(this, FALSE);
+ if (type != MSGERROR_CANCEL || flag == 0)
+ m_message.SetText(L"");
+ sendQueue->checkQueue(this);
+ int iNextFailed = sendQueue->findNextFailed(this);
+ if (iNextFailed >= 0)
+ sendQueue->handleError(this, iNextFailed);
+ }
+ break;
+
+ case MSGERROR_RETRY:
+ if (m_bErrorState) {
+ int resent = 0;
+
+ m_cache->saveHistory();
+ if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) {
+ SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
+ if (job->iSendId == 0 && job->hContact == 0)
+ break;
+
+ job->iSendId = ProtoChainSend(job->hContact, PSS_MESSAGE, job->dwFlags, (LPARAM)job->szSendBuffer);
+ resent++;
+ }
+
+ if (resent) {
+ SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
+
+ SetTimer(m_hwnd, TIMERID_MSGSEND + m_iCurrentQueueError, PluginConfig.m_MsgTimeout, nullptr);
+ job->iStatus = SendQueue::SQ_INPROGRESS;
+ m_iCurrentQueueError = -1;
+ sendQueue->showErrorControls(this, FALSE);
+ m_message.SetText(L"");
+ sendQueue->checkQueue(this);
+
+ int iNextFailed = sendQueue->findNextFailed(this);
+ if (iNextFailed >= 0)
+ sendQueue->handleError(this, iNextFailed);
+ }
+ }
+ }
+}
+
+int SI_InitStatusIcons()
+{
+ StatusIconData sid = {};
+ sid.szModule = MSG_ICON_MODULE;
+ sid.dwId = MSG_ICON_SOUND; // Sounds
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ sid.dwId = MSG_ICON_UTN;
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ HookEvent(ME_MSG_ICONSCHANGED, OnSrmmIconChanged);
+ return 0;
+}
diff --git a/plugins/TabSRMM/src/globals.cpp b/plugins/TabSRMM/src/globals.cpp index 953996a5cd..a80ff3f2b2 100644 --- a/plugins/TabSRMM/src/globals.cpp +++ b/plugins/TabSRMM/src/globals.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/globals.h b/plugins/TabSRMM/src/globals.h index fb52d01df2..db6229428e 100644 --- a/plugins/TabSRMM/src/globals.h +++ b/plugins/TabSRMM/src/globals.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/hotkeyhandler.cpp b/plugins/TabSRMM/src/hotkeyhandler.cpp index ebbd290ec5..5deb191552 100644 --- a/plugins/TabSRMM/src/hotkeyhandler.cpp +++ b/plugins/TabSRMM/src/hotkeyhandler.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/infopanel.cpp b/plugins/TabSRMM/src/infopanel.cpp index 69b1400fcb..37d433cd7f 100644 --- a/plugins/TabSRMM/src/infopanel.cpp +++ b/plugins/TabSRMM/src/infopanel.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/infopanel.h b/plugins/TabSRMM/src/infopanel.h index ead415ea29..ffa8c884a1 100644 --- a/plugins/TabSRMM/src/infopanel.h +++ b/plugins/TabSRMM/src/infopanel.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/mim.cpp b/plugins/TabSRMM/src/mim.cpp index 736c9c5e86..ca1e9c3272 100644 --- a/plugins/TabSRMM/src/mim.cpp +++ b/plugins/TabSRMM/src/mim.cpp @@ -1,488 +1,488 @@ -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda NG: the free IM client for Microsoft* Windows* -// -// Copyright (C) 2012-22 Miranda NG team, -// Copyright (c) 2000-09 Miranda ICQ/IM project, -// all portions of this codebase are copyrighted to the people -// listed in contributors.txt. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// you should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// -// part of tabSRMM messaging plugin for Miranda. -// -// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors -// -// wraps some parts of Miranda API -// Also, OS dependent stuff (visual styles api etc.) - -#include "stdafx.h" - -PDTTE CMimAPI::m_pfnDrawThemeTextEx = nullptr; -DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = nullptr; -DICE CMimAPI::m_pfnDwmIsCompositionEnabled = nullptr; -DRT CMimAPI::m_pfnDwmRegisterThumbnail = nullptr; -BPI CMimAPI::m_pfnBufferedPaintInit = nullptr; -BPU CMimAPI::m_pfnBufferedPaintUninit = nullptr; -BBP CMimAPI::m_pfnBeginBufferedPaint = nullptr; -EBP CMimAPI::m_pfnEndBufferedPaint = nullptr; -BBW CMimAPI::m_pfnDwmBlurBehindWindow = nullptr; -DGC CMimAPI::m_pfnDwmGetColorizationColor = nullptr; -BPSA CMimAPI::m_pfnBufferedPaintSetAlpha = nullptr; -DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = nullptr; -DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = nullptr; -DWMUT CMimAPI::m_pfnDwmUpdateThumbnailProperties = nullptr; -DURT CMimAPI::m_pfnDwmUnregisterThumbnail = nullptr; -DSIT CMimAPI::m_pfnDwmSetIconicThumbnail = nullptr; -DSILP CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = nullptr; -bool CMimAPI::m_shutDown = 0; -wchar_t CMimAPI::m_userDir[] = L"\0"; - -bool CMimAPI::m_haveBufferedPaint = false; - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMimAPI::FoldersPathChanged(WPARAM, LPARAM) -{ - return M.foldersPathChanged(); -} - -void CMimAPI::configureCustomFolders() -{ - m_hDataPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Data path"), const_cast<wchar_t *>(getDataPath())); - m_hSkinsPath = FoldersRegisterCustomPathW(LPGEN("Skins"), LPGEN("TabSRMM"), const_cast<wchar_t *>(getSkinPath())); - m_hAvatarsPath = FoldersRegisterCustomPathW(LPGEN("Avatars"), LPGEN("Saved TabSRMM avatars"), const_cast<wchar_t *>(getSavedAvatarPath())); - m_hChatLogsPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Group chat logs root"), const_cast<wchar_t *>(getChatLogPath())); - - if (m_hDataPath) - HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged); - - foldersPathChanged(); -} - -INT_PTR CMimAPI::foldersPathChanged() -{ - wchar_t szTemp[MAX_PATH + 2]; - - if (m_hDataPath) { - szTemp[0] = 0; - FoldersGetCustomPathW(m_hDataPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getDataPath())); - wcsncpy_s(m_szProfilePath, szTemp, _TRUNCATE); - - szTemp[0] = 0; - FoldersGetCustomPathW(m_hSkinsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSkinPath())); - wcsncpy_s(m_szSkinsPath, (MAX_PATH - 1), szTemp, _TRUNCATE); - Utils::ensureTralingBackslash(m_szSkinsPath); - - szTemp[0] = 0; - FoldersGetCustomPathW(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSavedAvatarPath())); - wcsncpy_s(m_szSavedAvatarsPath, szTemp, _TRUNCATE); - - szTemp[0] = 0; - FoldersGetCustomPathW(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getChatLogPath())); - wcsncpy_s(m_szChatLogsPath, (MAX_PATH - 1), szTemp, _TRUNCATE); - Utils::ensureTralingBackslash(m_szChatLogsPath); - } - - CreateDirectoryTreeW(m_szProfilePath); - CreateDirectoryTreeW(m_szSkinsPath); - CreateDirectoryTreeW(m_szSavedAvatarsPath); - - Skin->extractSkinsAndLogo(true); - Skin->setupAeroSkins(); - return 0; -} - -const wchar_t* CMimAPI::getUserDir() -{ - if (m_userDir[0] == 0) { - if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) - wcsncpy_s(m_userDir, L"%miranda_userdata%", _TRUNCATE); - else - wcsncpy_s(m_userDir, VARSW(L"%miranda_userdata%"), _TRUNCATE); - - Utils::ensureTralingBackslash(m_userDir); - } - return m_userDir; -} - -void CMimAPI::InitPaths() -{ - const wchar_t *szUserdataDir = getUserDir(); - - mir_snwprintf(m_szProfilePath, L"%stabSRMM", szUserdataDir); - if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) { - wcsncpy_s(m_szChatLogsPath, L"%miranda_logpath%", _TRUNCATE); - wcsncpy_s(m_szSkinsPath, L"%miranda_path%\\Skins\\TabSRMM", _TRUNCATE); - } - else { - wcsncpy_s(m_szChatLogsPath, VARSW(L"%miranda_logpath%"), _TRUNCATE); - wcsncpy_s(m_szSkinsPath, VARSW(L"%miranda_path%\\Skins\\TabSRMM"), _TRUNCATE); - } - - Utils::ensureTralingBackslash(m_szChatLogsPath); - replaceStrW(g_Settings.pszLogDir, m_szChatLogsPath); - - Utils::ensureTralingBackslash(m_szSkinsPath); - - mir_snwprintf(m_szSavedAvatarsPath, L"%s\\Saved Contact Pictures", m_szProfilePath); -} - -bool CMimAPI::getAeroState() -{ - m_isAero = m_DwmActive = false; - if (IsWinVerVistaPlus()) { - BOOL result = FALSE; - m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result); - m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive; - - } - m_isVsThemed = IsThemeActive() != 0; - return m_isAero; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Initialize various Win32 API functions which are not common to all versions of Windows. -// We have to work with functions pointers here. - -void CMimAPI::InitAPI() -{ - m_hUxTheme = nullptr; - m_hDwmApi = nullptr; - - // vista+ DWM API - if (IsWinVerVistaPlus()) { - m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll"); - if (m_hDwmApi) { - m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi, "DwmExtendFrameIntoClientArea"); - m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi, "DwmIsCompositionEnabled"); - m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail"); - m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow"); - m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor"); - m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps"); - m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute"); - m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties"); - m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail"); - m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail"); - m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap"); - } - - // additional uxtheme APIs (Vista+) - m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll"); - if (m_hUxTheme) { - m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx"); - m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint"); - m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint"); - m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit"); - m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit"); - m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha"); - m_haveBufferedPaint = (m_pfnBeginBufferedPaint != nullptr && m_pfnEndBufferedPaint != nullptr) ? true : false; - if (m_haveBufferedPaint) - m_pfnBufferedPaintInit(); - } - } - else m_haveBufferedPaint = false; - - switch (GetByte("default_ieview", -1)) { - case 1: - db_set_s(0, "SRMM", "Logger", "ieview"); - __fallthrough; - - case 0: - db_unset(0, SRMSGMOD_T, "default_ieview"); - } - - switch (GetByte("default_hpp", -1)) { - case 1: - db_set_s(0, "SRMM", "Logger", "hpp"); - __fallthrough; - - case 0: - db_unset(0, SRMSGMOD_T, "default_hpp"); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// hook subscriber function for incoming message typing events - -int CMimAPI::TypingMessage(WPARAM hContact, LPARAM nSecs) -{ - int foundWin = 0, preTyping = 0; - BOOL fShowOnClist = TRUE; - - auto *pDlg = Srmm_FindDialog(hContact); - MCONTACT hMeta = db_mc_getMeta(hContact); - if (hMeta) { - if (!pDlg) - pDlg = Srmm_FindDialog(hMeta); - hContact = hMeta; - } - - if (pDlg && g_plugin.getByte(SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING)) - preTyping = pDlg->Typing(nSecs); - - if (pDlg && IsWindowVisible(pDlg->GetHwnd())) - foundWin = MessageWindowOpened(0, pDlg); - else - foundWin = 0; - - TContainerData *pContainer = nullptr; - if (pDlg) { - pContainer = pDlg->m_pContainer; - if (pContainer == nullptr) // should never happen - return 0; - } - - if (g_plugin.getByte(SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) { - if (!pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGNOWINOPEN, 1)) - fShowOnClist = false; - if (pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGWINOPEN, 1)) - fShowOnClist = false; - } - else fShowOnClist = false; - - if ((!foundWin || !pContainer->cfg.flags.m_bNoSound) && preTyping != (nSecs != 0)) - Skin_PlaySound(nSecs ? "TNStart" : "TNStop"); - - if (g_plugin.bPopups) { - BOOL fShow = false; - int iMode = M.GetByte("MTN_PopupMode", 0); - - switch (iMode) { - case 0: - fShow = true; - break; - case 1: - if (!foundWin || !(pContainer && pContainer->m_hwndActive == pDlg->GetHwnd() && GetForegroundWindow() == pContainer->m_hwnd)) - fShow = true; - break; - case 2: - if (pDlg == nullptr) - fShow = true; - else { - if (g_plugin.bHideOnClose) { - TContainerData *pCont = pDlg->m_pContainer; - if (pCont && pCont->m_bHidden) - fShow = true; - } - } - break; - } - if (fShow) - TN_TypingMessage(hContact, nSecs); - } - - if (nSecs) { - wchar_t szTip[256]; - mir_snwprintf(szTip, TranslateT("%s is typing a message"), Clist_GetContactDisplayName(hContact)); - if (fShowOnClist && g_plugin.getByte("ShowTypingBalloon", 0)) - Clist_TrayNotifyW(nullptr, TranslateT("Typing notification"), szTip, NIIF_INFO, 1000 * 4); - - if (fShowOnClist) { - g_clistApi.pfnRemoveEvent(hContact, 1); - - CLISTEVENT cle = {}; - cle.hContact = hContact; - cle.hDbEvent = 1; - cle.flags = CLEF_ONLYAFEW | CLEF_UNICODE; - cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]; - cle.pszService = MS_MSG_TYPINGMESSAGE; - cle.szTooltip.w = szTip; - g_clistApi.pfnAddEvent(&cle); - } - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// this is the global ack dispatcher.It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events -// for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches -// it to the owners window -// -// ACKTYPE_AVATAR no longer handled here, because we have avs services now. - -int CMimAPI::ProtoAck(WPARAM, LPARAM lParam) -{ - ACKDATA *pAck = (ACKDATA*)lParam; - - if ((pAck != nullptr) && (pAck->type == ACKTYPE_MESSAGE)) { - int i = 0, iFound = SendQueue::NR_SENDJOBS; - SendJob *jobs = sendQueue->getJobByIndex(0); - MCONTACT hMeta = db_mc_getMeta(pAck->hContact); - for (int j = 0; j < SendQueue::NR_SENDJOBS; j++) { - SendJob &p = jobs[j]; - if (pAck->hProcess == (HANDLE)p.iSendId && pAck->hContact == p.hContact) { - CMsgDialog *dat = p.hOwnerWnd ? (CMsgDialog*)GetWindowLongPtr(p.hOwnerWnd, GWLP_USERDATA) : nullptr; - if (dat == nullptr) { - sendQueue->ackMessage(nullptr, (WPARAM)MAKELONG(j, i), lParam); - return 0; - } - if (dat->m_hContact == p.hContact || dat->m_hContact == hMeta) { - iFound = j; - break; - } - } - } - if (iFound == SendQueue::NR_SENDJOBS) // no matching send info found in the queue - SendLater::processAck(pAck); - else // try to find the process handle in the list of open send later jobs - SendMessage(jobs[iFound].hOwnerWnd, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam); - } - return 0; -} - -int CMimAPI::PrebuildContactMenu(WPARAM hContact, LPARAM) -{ - if (hContact == 0) - return 0; - - bool bEnabled = false; - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) { - // leave this menu item hidden for chats - if (!Contact::IsGroupChat(hContact, szProto)) - if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND) - bEnabled = true; - } - - Menu_ShowItem(PluginConfig.m_hMenuItem, bEnabled); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// this handler is called first in the message window chain - it will handle events for which a message window -// is already open. if not, it will do nothing and the 2nd handler(MessageEventAdded) will perform all -// the needed actions. -// -// this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will -// improve the overall responsiveness when receiving messages. - -int CMimAPI::DispatchNewEvent(WPARAM hContact, LPARAM hDbEvent) -{ - if (hContact) { - Utils::sendContactMessage(hContact, HM_DBEVENTADDED, hContact, hDbEvent); - - // we're in meta and an event belongs to a sub - MCONTACT hReal = db_event_getContact(hDbEvent); - if (hReal != hContact) - Utils::sendContactMessage(hReal, HM_DBEVENTADDED, hContact, hDbEvent); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Message event added is called when a new message is added to the database -// if no session is open for the contact, this function will determine if and how a new message -// session(tab) must be created. -// -// if a session is already created, it just does nothing and DispatchNewEvent() will take care. - -int CMimAPI::MessageEventAdded(WPARAM hContact, LPARAM hDbEvent) -{ - if (hContact == 0) - return 0; - - DBEVENTINFO dbei = {}; - db_event_get(hDbEvent, &dbei); - - auto *pDlg = Srmm_FindDialog(hContact); - if (pDlg == nullptr) - pDlg = Srmm_FindDialog(db_event_getContact(hDbEvent)); - - BOOL isCustomEvent = IsCustomEvent(dbei.eventType); - BOOL isShownCustomEvent = DbEventIsForMsgWindow(&dbei); - if (dbei.markedRead() || (isCustomEvent && !isShownCustomEvent)) - return 0; - - g_clistApi.pfnRemoveEvent(hContact, 1); - - bool bAutoPopup = g_plugin.bAutoPopup; - bool bAutoCreate = g_plugin.bAutoTabs; - bool bAutoContainer = g_plugin.bAutoContainer; - - if (pDlg) { - TContainerData *pTargetContainer = pDlg->m_pContainer; - if (pTargetContainer == nullptr || !g_plugin.bHideOnClose || IsWindowVisible(pTargetContainer->m_hwnd)) - return 0; - - WINDOWPLACEMENT wp = { 0 }; - wp.length = sizeof(wp); - GetWindowPlacement(pTargetContainer->m_hwnd, &wp); - - wchar_t szName[CONTAINER_NAMELEN + 1]; - GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN); - - if (bAutoPopup || bAutoCreate) { - if (bAutoPopup) { - if (wp.showCmd == SW_SHOWMAXIMIZED) - ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMAXIMIZED); - else - ShowWindow(pTargetContainer->m_hwnd, SW_SHOWNOACTIVATE); - return 0; - } - - TContainerData *pContainer = FindContainerByName(szName); - if (pContainer != nullptr) { - if (bAutoContainer) { - ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE); - return 0; - } - goto nowindowcreate; - } - else if (bAutoContainer) { - ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE); - return 0; - } - } - } - else { - switch (dbei.eventType) { - case EVENTTYPE_AUTHREQUEST: - case EVENTTYPE_ADDED: - case EVENTTYPE_FILE: - return 0; - } - } - - if (!NEN::bNoSounds) - Skin_PlaySound("AlertMsg"); - - if (NEN::bNoAutoPopup) - goto nowindowcreate; - - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_CREATECONTAINER, hContact, hDbEvent); - return 0; - -nowindowcreate: - // for tray support, we add the event to the tray menu. otherwise we send it back to - // the contact list for flashing - if (!(dbei.flags & DBEF_READ)) { - AddUnreadContact(hContact); - - wchar_t toolTip[256]; - mir_snwprintf(toolTip, TranslateT("Message from %s"), Clist_GetContactDisplayName(hContact)); - - CLISTEVENT cle = {}; - cle.hContact = hContact; - cle.hDbEvent = hDbEvent; - cle.flags = CLEF_UNICODE; - cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE); - cle.pszService = MS_MSG_READMESSAGE; - cle.szTooltip.w = toolTip; - g_clistApi.pfnAddEvent(&cle); - } - return 0; -} - -CMimAPI M; +/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// wraps some parts of Miranda API
+// Also, OS dependent stuff (visual styles api etc.)
+
+#include "stdafx.h"
+
+PDTTE CMimAPI::m_pfnDrawThemeTextEx = nullptr;
+DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = nullptr;
+DICE CMimAPI::m_pfnDwmIsCompositionEnabled = nullptr;
+DRT CMimAPI::m_pfnDwmRegisterThumbnail = nullptr;
+BPI CMimAPI::m_pfnBufferedPaintInit = nullptr;
+BPU CMimAPI::m_pfnBufferedPaintUninit = nullptr;
+BBP CMimAPI::m_pfnBeginBufferedPaint = nullptr;
+EBP CMimAPI::m_pfnEndBufferedPaint = nullptr;
+BBW CMimAPI::m_pfnDwmBlurBehindWindow = nullptr;
+DGC CMimAPI::m_pfnDwmGetColorizationColor = nullptr;
+BPSA CMimAPI::m_pfnBufferedPaintSetAlpha = nullptr;
+DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = nullptr;
+DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = nullptr;
+DWMUT CMimAPI::m_pfnDwmUpdateThumbnailProperties = nullptr;
+DURT CMimAPI::m_pfnDwmUnregisterThumbnail = nullptr;
+DSIT CMimAPI::m_pfnDwmSetIconicThumbnail = nullptr;
+DSILP CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = nullptr;
+bool CMimAPI::m_shutDown = 0;
+wchar_t CMimAPI::m_userDir[] = L"\0";
+
+bool CMimAPI::m_haveBufferedPaint = false;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMimAPI::FoldersPathChanged(WPARAM, LPARAM)
+{
+ return M.foldersPathChanged();
+}
+
+void CMimAPI::configureCustomFolders()
+{
+ m_hDataPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Data path"), const_cast<wchar_t *>(getDataPath()));
+ m_hSkinsPath = FoldersRegisterCustomPathW(LPGEN("Skins"), LPGEN("TabSRMM"), const_cast<wchar_t *>(getSkinPath()));
+ m_hAvatarsPath = FoldersRegisterCustomPathW(LPGEN("Avatars"), LPGEN("Saved TabSRMM avatars"), const_cast<wchar_t *>(getSavedAvatarPath()));
+ m_hChatLogsPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Group chat logs root"), const_cast<wchar_t *>(getChatLogPath()));
+
+ if (m_hDataPath)
+ HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged);
+
+ foldersPathChanged();
+}
+
+INT_PTR CMimAPI::foldersPathChanged()
+{
+ wchar_t szTemp[MAX_PATH + 2];
+
+ if (m_hDataPath) {
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hDataPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getDataPath()));
+ wcsncpy_s(m_szProfilePath, szTemp, _TRUNCATE);
+
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hSkinsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSkinPath()));
+ wcsncpy_s(m_szSkinsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
+ Utils::ensureTralingBackslash(m_szSkinsPath);
+
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSavedAvatarPath()));
+ wcsncpy_s(m_szSavedAvatarsPath, szTemp, _TRUNCATE);
+
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getChatLogPath()));
+ wcsncpy_s(m_szChatLogsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
+ Utils::ensureTralingBackslash(m_szChatLogsPath);
+ }
+
+ CreateDirectoryTreeW(m_szProfilePath);
+ CreateDirectoryTreeW(m_szSkinsPath);
+ CreateDirectoryTreeW(m_szSavedAvatarsPath);
+
+ Skin->extractSkinsAndLogo(true);
+ Skin->setupAeroSkins();
+ return 0;
+}
+
+const wchar_t* CMimAPI::getUserDir()
+{
+ if (m_userDir[0] == 0) {
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
+ wcsncpy_s(m_userDir, L"%miranda_userdata%", _TRUNCATE);
+ else
+ wcsncpy_s(m_userDir, VARSW(L"%miranda_userdata%"), _TRUNCATE);
+
+ Utils::ensureTralingBackslash(m_userDir);
+ }
+ return m_userDir;
+}
+
+void CMimAPI::InitPaths()
+{
+ const wchar_t *szUserdataDir = getUserDir();
+
+ mir_snwprintf(m_szProfilePath, L"%stabSRMM", szUserdataDir);
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) {
+ wcsncpy_s(m_szChatLogsPath, L"%miranda_logpath%", _TRUNCATE);
+ wcsncpy_s(m_szSkinsPath, L"%miranda_path%\\Skins\\TabSRMM", _TRUNCATE);
+ }
+ else {
+ wcsncpy_s(m_szChatLogsPath, VARSW(L"%miranda_logpath%"), _TRUNCATE);
+ wcsncpy_s(m_szSkinsPath, VARSW(L"%miranda_path%\\Skins\\TabSRMM"), _TRUNCATE);
+ }
+
+ Utils::ensureTralingBackslash(m_szChatLogsPath);
+ replaceStrW(g_Settings.pszLogDir, m_szChatLogsPath);
+
+ Utils::ensureTralingBackslash(m_szSkinsPath);
+
+ mir_snwprintf(m_szSavedAvatarsPath, L"%s\\Saved Contact Pictures", m_szProfilePath);
+}
+
+bool CMimAPI::getAeroState()
+{
+ m_isAero = m_DwmActive = false;
+ if (IsWinVerVistaPlus()) {
+ BOOL result = FALSE;
+ m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result);
+ m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive;
+
+ }
+ m_isVsThemed = IsThemeActive() != 0;
+ return m_isAero;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Initialize various Win32 API functions which are not common to all versions of Windows.
+// We have to work with functions pointers here.
+
+void CMimAPI::InitAPI()
+{
+ m_hUxTheme = nullptr;
+ m_hDwmApi = nullptr;
+
+ // vista+ DWM API
+ if (IsWinVerVistaPlus()) {
+ m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll");
+ if (m_hDwmApi) {
+ m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi, "DwmExtendFrameIntoClientArea");
+ m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi, "DwmIsCompositionEnabled");
+ m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail");
+ m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow");
+ m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor");
+ m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps");
+ m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute");
+ m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties");
+ m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail");
+ m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail");
+ m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap");
+ }
+
+ // additional uxtheme APIs (Vista+)
+ m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll");
+ if (m_hUxTheme) {
+ m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx");
+ m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint");
+ m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint");
+ m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit");
+ m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit");
+ m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha");
+ m_haveBufferedPaint = (m_pfnBeginBufferedPaint != nullptr && m_pfnEndBufferedPaint != nullptr) ? true : false;
+ if (m_haveBufferedPaint)
+ m_pfnBufferedPaintInit();
+ }
+ }
+ else m_haveBufferedPaint = false;
+
+ switch (GetByte("default_ieview", -1)) {
+ case 1:
+ db_set_s(0, "SRMM", "Logger", "ieview");
+ __fallthrough;
+
+ case 0:
+ db_unset(0, SRMSGMOD_T, "default_ieview");
+ }
+
+ switch (GetByte("default_hpp", -1)) {
+ case 1:
+ db_set_s(0, "SRMM", "Logger", "hpp");
+ __fallthrough;
+
+ case 0:
+ db_unset(0, SRMSGMOD_T, "default_hpp");
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// hook subscriber function for incoming message typing events
+
+int CMimAPI::TypingMessage(WPARAM hContact, LPARAM nSecs)
+{
+ int foundWin = 0, preTyping = 0;
+ BOOL fShowOnClist = TRUE;
+
+ auto *pDlg = Srmm_FindDialog(hContact);
+ MCONTACT hMeta = db_mc_getMeta(hContact);
+ if (hMeta) {
+ if (!pDlg)
+ pDlg = Srmm_FindDialog(hMeta);
+ hContact = hMeta;
+ }
+
+ if (pDlg && g_plugin.getByte(SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING))
+ preTyping = pDlg->Typing(nSecs);
+
+ if (pDlg && IsWindowVisible(pDlg->GetHwnd()))
+ foundWin = MessageWindowOpened(0, pDlg);
+ else
+ foundWin = 0;
+
+ TContainerData *pContainer = nullptr;
+ if (pDlg) {
+ pContainer = pDlg->m_pContainer;
+ if (pContainer == nullptr) // should never happen
+ return 0;
+ }
+
+ if (g_plugin.getByte(SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) {
+ if (!pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGNOWINOPEN, 1))
+ fShowOnClist = false;
+ if (pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGWINOPEN, 1))
+ fShowOnClist = false;
+ }
+ else fShowOnClist = false;
+
+ if ((!foundWin || !pContainer->cfg.flags.m_bNoSound) && preTyping != (nSecs != 0))
+ Skin_PlaySound(nSecs ? "TNStart" : "TNStop");
+
+ if (g_plugin.bPopups) {
+ BOOL fShow = false;
+ int iMode = M.GetByte("MTN_PopupMode", 0);
+
+ switch (iMode) {
+ case 0:
+ fShow = true;
+ break;
+ case 1:
+ if (!foundWin || !(pContainer && pContainer->m_hwndActive == pDlg->GetHwnd() && GetForegroundWindow() == pContainer->m_hwnd))
+ fShow = true;
+ break;
+ case 2:
+ if (pDlg == nullptr)
+ fShow = true;
+ else {
+ if (g_plugin.bHideOnClose) {
+ TContainerData *pCont = pDlg->m_pContainer;
+ if (pCont && pCont->m_bHidden)
+ fShow = true;
+ }
+ }
+ break;
+ }
+ if (fShow)
+ TN_TypingMessage(hContact, nSecs);
+ }
+
+ if (nSecs) {
+ wchar_t szTip[256];
+ mir_snwprintf(szTip, TranslateT("%s is typing a message"), Clist_GetContactDisplayName(hContact));
+ if (fShowOnClist && g_plugin.getByte("ShowTypingBalloon", 0))
+ Clist_TrayNotifyW(nullptr, TranslateT("Typing notification"), szTip, NIIF_INFO, 1000 * 4);
+
+ if (fShowOnClist) {
+ g_clistApi.pfnRemoveEvent(hContact, 1);
+
+ CLISTEVENT cle = {};
+ cle.hContact = hContact;
+ cle.hDbEvent = 1;
+ cle.flags = CLEF_ONLYAFEW | CLEF_UNICODE;
+ cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
+ cle.pszService = MS_MSG_TYPINGMESSAGE;
+ cle.szTooltip.w = szTip;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this is the global ack dispatcher.It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events
+// for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches
+// it to the owners window
+//
+// ACKTYPE_AVATAR no longer handled here, because we have avs services now.
+
+int CMimAPI::ProtoAck(WPARAM, LPARAM lParam)
+{
+ ACKDATA *pAck = (ACKDATA*)lParam;
+
+ if ((pAck != nullptr) && (pAck->type == ACKTYPE_MESSAGE)) {
+ int i = 0, iFound = SendQueue::NR_SENDJOBS;
+ SendJob *jobs = sendQueue->getJobByIndex(0);
+ MCONTACT hMeta = db_mc_getMeta(pAck->hContact);
+ for (int j = 0; j < SendQueue::NR_SENDJOBS; j++) {
+ SendJob &p = jobs[j];
+ if (pAck->hProcess == (HANDLE)p.iSendId && pAck->hContact == p.hContact) {
+ CMsgDialog *dat = p.hOwnerWnd ? (CMsgDialog*)GetWindowLongPtr(p.hOwnerWnd, GWLP_USERDATA) : nullptr;
+ if (dat == nullptr) {
+ sendQueue->ackMessage(nullptr, (WPARAM)MAKELONG(j, i), lParam);
+ return 0;
+ }
+ if (dat->m_hContact == p.hContact || dat->m_hContact == hMeta) {
+ iFound = j;
+ break;
+ }
+ }
+ }
+ if (iFound == SendQueue::NR_SENDJOBS) // no matching send info found in the queue
+ SendLater::processAck(pAck);
+ else // try to find the process handle in the list of open send later jobs
+ SendMessage(jobs[iFound].hOwnerWnd, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam);
+ }
+ return 0;
+}
+
+int CMimAPI::PrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0)
+ return 0;
+
+ bool bEnabled = false;
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto) {
+ // leave this menu item hidden for chats
+ if (!Contact::IsGroupChat(hContact, szProto))
+ if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
+ bEnabled = true;
+ }
+
+ Menu_ShowItem(PluginConfig.m_hMenuItem, bEnabled);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this handler is called first in the message window chain - it will handle events for which a message window
+// is already open. if not, it will do nothing and the 2nd handler(MessageEventAdded) will perform all
+// the needed actions.
+//
+// this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will
+// improve the overall responsiveness when receiving messages.
+
+int CMimAPI::DispatchNewEvent(WPARAM hContact, LPARAM hDbEvent)
+{
+ if (hContact) {
+ Utils::sendContactMessage(hContact, HM_DBEVENTADDED, hContact, hDbEvent);
+
+ // we're in meta and an event belongs to a sub
+ MCONTACT hReal = db_event_getContact(hDbEvent);
+ if (hReal != hContact)
+ Utils::sendContactMessage(hReal, HM_DBEVENTADDED, hContact, hDbEvent);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Message event added is called when a new message is added to the database
+// if no session is open for the contact, this function will determine if and how a new message
+// session(tab) must be created.
+//
+// if a session is already created, it just does nothing and DispatchNewEvent() will take care.
+
+int CMimAPI::MessageEventAdded(WPARAM hContact, LPARAM hDbEvent)
+{
+ if (hContact == 0)
+ return 0;
+
+ DBEVENTINFO dbei = {};
+ db_event_get(hDbEvent, &dbei);
+
+ auto *pDlg = Srmm_FindDialog(hContact);
+ if (pDlg == nullptr)
+ pDlg = Srmm_FindDialog(db_event_getContact(hDbEvent));
+
+ BOOL isCustomEvent = IsCustomEvent(dbei.eventType);
+ BOOL isShownCustomEvent = DbEventIsForMsgWindow(&dbei);
+ if (dbei.markedRead() || (isCustomEvent && !isShownCustomEvent))
+ return 0;
+
+ g_clistApi.pfnRemoveEvent(hContact, 1);
+
+ bool bAutoPopup = g_plugin.bAutoPopup;
+ bool bAutoCreate = g_plugin.bAutoTabs;
+ bool bAutoContainer = g_plugin.bAutoContainer;
+
+ if (pDlg) {
+ TContainerData *pTargetContainer = pDlg->m_pContainer;
+ if (pTargetContainer == nullptr || !g_plugin.bHideOnClose || IsWindowVisible(pTargetContainer->m_hwnd))
+ return 0;
+
+ WINDOWPLACEMENT wp = { 0 };
+ wp.length = sizeof(wp);
+ GetWindowPlacement(pTargetContainer->m_hwnd, &wp);
+
+ wchar_t szName[CONTAINER_NAMELEN + 1];
+ GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN);
+
+ if (bAutoPopup || bAutoCreate) {
+ if (bAutoPopup) {
+ if (wp.showCmd == SW_SHOWMAXIMIZED)
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMAXIMIZED);
+ else
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWNOACTIVATE);
+ return 0;
+ }
+
+ TContainerData *pContainer = FindContainerByName(szName);
+ if (pContainer != nullptr) {
+ if (bAutoContainer) {
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
+ return 0;
+ }
+ goto nowindowcreate;
+ }
+ else if (bAutoContainer) {
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
+ return 0;
+ }
+ }
+ }
+ else {
+ switch (dbei.eventType) {
+ case EVENTTYPE_AUTHREQUEST:
+ case EVENTTYPE_ADDED:
+ case EVENTTYPE_FILE:
+ return 0;
+ }
+ }
+
+ if (!NEN::bNoSounds)
+ Skin_PlaySound("AlertMsg");
+
+ if (NEN::bNoAutoPopup)
+ goto nowindowcreate;
+
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_CREATECONTAINER, hContact, hDbEvent);
+ return 0;
+
+nowindowcreate:
+ // for tray support, we add the event to the tray menu. otherwise we send it back to
+ // the contact list for flashing
+ if (!(dbei.flags & DBEF_READ)) {
+ AddUnreadContact(hContact);
+
+ wchar_t toolTip[256];
+ mir_snwprintf(toolTip, TranslateT("Message from %s"), Clist_GetContactDisplayName(hContact));
+
+ CLISTEVENT cle = {};
+ cle.hContact = hContact;
+ cle.hDbEvent = hDbEvent;
+ cle.flags = CLEF_UNICODE;
+ cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = MS_MSG_READMESSAGE;
+ cle.szTooltip.w = toolTip;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ return 0;
+}
+
+CMimAPI M;
diff --git a/plugins/TabSRMM/src/mim.h b/plugins/TabSRMM/src/mim.h index e3081a18d5..b9d094868a 100644 --- a/plugins/TabSRMM/src/mim.h +++ b/plugins/TabSRMM/src/mim.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/modplus.cpp b/plugins/TabSRMM/src/modplus.cpp index 7fbcb934dc..a8daa352ef 100644 --- a/plugins/TabSRMM/src/modplus.cpp +++ b/plugins/TabSRMM/src/modplus.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdialog.cpp b/plugins/TabSRMM/src/msgdialog.cpp index dbf57a6c4e..9c8aa11318 100644 --- a/plugins/TabSRMM/src/msgdialog.cpp +++ b/plugins/TabSRMM/src/msgdialog.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdlgother.cpp b/plugins/TabSRMM/src/msgdlgother.cpp index edaa0bacbc..db968ba08e 100644 --- a/plugins/TabSRMM/src/msgdlgother.cpp +++ b/plugins/TabSRMM/src/msgdlgother.cpp @@ -1,2858 +1,2858 @@ -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda NG: the free IM client for Microsoft* Windows* -// -// Copyright (C) 2012-22 Miranda NG team, -// Copyright (c) 2000-09 Miranda ICQ/IM project, -// all portions of this codebase are copyrighted to the people -// listed in contributors.txt. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// you should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// -// part of tabSRMM messaging plugin for Miranda. -// -// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors -// -// Helper functions for the message dialog. - -#include "stdafx.h" - -UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM, LPARAM lParam); - -///////////////////////////////////////////////////////////////////////////////////////// -// show the balloon tooltip control. - -void CMsgDialog::ActivateTooltip(int iCtrlId, const wchar_t *pwszMessage) -{ - if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd) - m_pPanel.showTip(iCtrlId, pwszMessage); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::AddLog() -{ - if (g_plugin.bUseDividers) { - if (g_plugin.bDividersUsePopupConfig) { - if (!MessageWindowOpened(0, this)) - DM_AddDivider(); - } - else { - if (!IsActive()) - DM_AddDivider(); - else if (m_pContainer->m_hwndActive != m_hwnd) - DM_AddDivider(); - } - } - - CSrmmBaseDialog::AddLog(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::AdjustBottomAvatarDisplay() -{ - GetAvatarVisibility(); - - bool bInfoPanel = m_pPanel.isActive(); - HBITMAP hbm = (bInfoPanel && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown); - if (hbm) { - if (m_dynaSplitter == 0 || m_iSplitterY == 0) - LoadSplitter(); - m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34); - DM_RecalcPictureSize(); - Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE); - InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE); - } - else { - Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE); - m_pic.cy = m_pic.cx = DPISCALEY_S(60); - InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// calculates avatar layouting, based on splitter position to find the optimal size -// for the avatar w/o disturbing the toolbar too much. - -void CMsgDialog::CalcDynamicAvatarSize(BITMAP *bminfo) -{ - if (m_bWasBackgroundCreate || m_pContainer->cfg.flags.m_bDeferredConfigure || m_pContainer->cfg.flags.m_bCreateMinimized || IsIconic(m_pContainer->m_hwnd)) - return; // at this stage, the layout is not yet ready... - - RECT rc; - GetClientRect(m_hwnd, &rc); - - BOOL bBottomToolBar = m_pContainer->cfg.flags.m_bBottomToolbar; - BOOL bToolBar = m_pContainer->cfg.flags.m_bHideToolbar ? 0 : 1; - int iSplitOffset = m_bIsAutosizingInput ? 1 : 0; - - double picAspect = (bminfo->bmWidth == 0 || bminfo->bmHeight == 0) ? 1.0 : (double)(bminfo->bmWidth / (double)bminfo->bmHeight); - double picProjectedWidth = (double)((m_dynaSplitter - ((bBottomToolBar && bToolBar) ? DPISCALEX_S(24) : 0) + ((m_bShowUIElements) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect; - - if ((rc.right - (int)picProjectedWidth) > (m_iButtonBarReallyNeeds) && !PluginConfig.m_bAlwaysFullToolbarWidth && bToolBar) - m_iRealAvatarHeight = m_dynaSplitter + 3 + (m_bShowUIElements ? DPISCALEY_S(28) : DPISCALEY_S(2)); - else - m_iRealAvatarHeight = m_dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset); - - m_iRealAvatarHeight -= ((bBottomToolBar && bToolBar) ? DPISCALEY_S(22) : 0); - - if (PluginConfig.m_LimitStaticAvatarHeight > 0) - m_iRealAvatarHeight = min(m_iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight); - - if (M.GetByte(m_hContact, "dontscaleavatars", M.GetByte("dontscaleavatars", 0))) - m_iRealAvatarHeight = min(bminfo->bmHeight, m_iRealAvatarHeight); - - double aspect = (bminfo->bmHeight != 0) ? (double)m_iRealAvatarHeight / (double)bminfo->bmHeight : 1.0; - double newWidth = (double)bminfo->bmWidth * aspect; - if (newWidth > (double)(rc.right) * 0.8) - newWidth = (double)(rc.right) * 0.8; - m_pic.cy = m_iRealAvatarHeight + 2; - m_pic.cx = (int)newWidth + 2; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::CloseTab() -{ - int iTabs = TabCtrl_GetItemCount(m_hwndParent); - if (iTabs == 1) { - SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 1); - return; - } - - m_pContainer->m_iChilds--; - int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd); - - // after closing a tab, we need to activate the tab to the left side of - // the previously open tab. - // normally, this tab has the same index after the deletion of the formerly active tab - // unless, of course, we closed the last (rightmost) tab. - if (!m_pContainer->m_bDontSmartClose && iTabs > 1) { - if (i == iTabs - 1) - i--; - else - i++; - TabCtrl_SetCurSel(m_hwndParent, i); - - m_pContainer->m_hwndActive = GetTabWindow(m_hwndParent, i); - - RECT rc; - m_pContainer->QueryClientArea(rc); - SetWindowPos(m_pContainer->m_hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW); - ShowWindow(m_pContainer->m_hwndActive, SW_SHOW); - SetForegroundWindow(m_pContainer->m_hwndActive); - SetFocus(m_pContainer->m_hwndActive); - } - - SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0); - DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// calculate the minimum required client height for the given message -// window layout -// -// the container will use this in its WM_GETMINMAXINFO handler to set -// minimum tracking height. - -void CMsgDialog::DetermineMinHeight() -{ - RECT rc; - LONG height = (m_pPanel.isActive() ? m_pPanel.getHeight() + 2 : 0); - if (!m_pContainer->cfg.flags.m_bHideToolbar) - height += DPISCALEY_S(24); // toolbar - GetClientRect(m_message.GetHwnd(), &rc); - height += rc.bottom; // input area - height += 40; // min space for log area and some padding - - m_pContainer->m_uChildMinHeight = height; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting -// tags and return plain text - -static wchar_t tszRtfBreaks[] = L" \\\n\r"; - -static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst) -{ - const wchar_t *pszText = Text; - int iIndex = 1; - - static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];"; - wchar_t szRed[10], szGreen[10], szBlue[10]; - - const wchar_t *p1 = wcsstr(pszText, L"\\colortbl"); - if (!p1) - return; - - const wchar_t *pEnd = wcschr(p1, '}'); - - const wchar_t *p2 = wcsstr(p1, L"\\red"); - - for (int i = 0; i < iCount; i++) - pDst[i] = -1; - - while (p2 && p2 < pEnd) { - if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) { - for (int i = 0; i < iCount; i++) { - if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue))) - pDst[i] = iIndex; - } - } - iIndex++; - p1 = p2; - p1++; - - p2 = wcsstr(p1, L"\\red"); - } -} - -static int RtfColorToIndex(int iNumColors, int *pIndex, int iCol) -{ - for (int i = 0; i < iNumColors; i++) - if (pIndex[i] == iCol) - return i; - - return -1; -} - -BOOL CMsgDialog::DoRtfToTags(CMStringW &pszText) const -{ - if (pszText.IsEmpty()) - return FALSE; - - // used to filter out attributes which are already set for the default message input area font - auto &lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA]; - - // create an index of colors in the module and map them to - // corresponding colors in the RTF color table - int iNumColors = Utils::rtf_clrs.getCount(); - int *pIndex = (int *)_alloca(iNumColors * sizeof(int)); - COLORREF *pColors = (COLORREF *)_alloca(iNumColors * sizeof(COLORREF)); - for (int i = 0; i < iNumColors; i++) - pColors[i] = Utils::rtf_clrs[i].clr; - CreateColorMap(pszText, iNumColors, pColors, pIndex); - - // scan the file for rtf commands and remove or parse them - int idx = pszText.Find(L"\\pard"); - if (idx == -1) { - if ((idx = pszText.Find(L"\\ltrpar")) == -1) - return FALSE; - idx += 7; - } - else idx += 5; - - MODULEINFO *mi = (isChat()) ? m_si->pMI : nullptr; - - bool bInsideColor = false, bInsideUl = false; - CMStringW res; - - // iterate through all characters, if rtf control character found then take action - for (const wchar_t *p = pszText.GetString() + idx; *p;) { - switch (*p) { - case '\\': - if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters - res.AppendChar(p[1]); - p += 2; break; - } - if (p[1] == '~') { // non-breaking space - res.AppendChar(0xA0); - p += 2; break; - } - - if (!wcsncmp(p, L"\\cf", 3)) { // foreground color - int iCol = _wtoi(p + 3); - int iInd = RtfColorToIndex(iNumColors, pIndex, iCol); - - if (iCol > 0) { - if (isChat()) { - if (mi && mi->bColor) { - if (iInd >= 0) { - if (!(res.IsEmpty() && m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA] == pColors[iInd])) - res.AppendFormat(L"%%c%u", iInd); - } - else if (!res.IsEmpty()) - res.Append(L"%%C"); - } - } - else res.AppendFormat((iInd >= 0) ? (bInsideColor ? L"[/color][color=%s]" : L"[color=%s]") : (bInsideColor ? L"[/color]" : L""), Utils::rtf_clrs[iInd].szName); - } - - bInsideColor = iInd >= 0; - } - else if (!wcsncmp(p, L"\\highlight", 10)) { // background color - if (isChat()) { - if (mi && mi->bBkgColor) { - int iInd = RtfColorToIndex(iNumColors, pIndex, _wtoi(p + 10)); - if (iInd >= 0) { - // if the entry field is empty & the color passed is the back color, skip it - if (!(res.IsEmpty() && m_pContainer->m_theme.inputbg == pColors[iInd])) - res.AppendFormat(L"%%f%u", iInd); - } - else if (!res.IsEmpty()) - res.AppendFormat(L"%%F"); - } - } - } - else if (!wcsncmp(p, L"\\line", 5)) { // soft line break; - res.AppendChar('\n'); - } - else if (!wcsncmp(p, L"\\endash", 7)) { - res.AppendChar(0x2013); - } - else if (!wcsncmp(p, L"\\emdash", 7)) { - res.AppendChar(0x2014); - } - else if (!wcsncmp(p, L"\\bullet", 7)) { - res.AppendChar(0x2022); - } - else if (!wcsncmp(p, L"\\ldblquote", 10)) { - res.AppendChar(0x201C); - } - else if (!wcsncmp(p, L"\\rdblquote", 10)) { - res.AppendChar(0x201D); - } - else if (!wcsncmp(p, L"\\lquote", 7)) { - res.AppendChar(0x2018); - } - else if (!wcsncmp(p, L"\\rquote", 7)) { - res.AppendChar(0x2019); - } - else if (!wcsncmp(p, L"\\b", 2)) { //bold - if (isChat()) { - if (mi && mi->bBold) - res.Append((p[2] != '0') ? L"%b" : L"%B"); - } - else { - if (!(lf.lfWeight == FW_BOLD)) // only allow bold if the font itself isn't a bold one, otherwise just strip it.. - if (m_SendFormat) - res.Append((p[2] != '0') ? L"[b]" : L"[/b]"); - } - } - else if (!wcsncmp(p, L"\\i", 2)) { // italics - if (isChat()) { - if (mi && mi->bItalics) - res.Append((p[2] != '0') ? L"%i" : L"%I"); - } - else { - if (!lf.lfItalic && m_SendFormat) - res.Append((p[2] != '0') ? L"[i]" : L"[/i]"); - } - } - else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out - if (!lf.lfStrikeOut && m_SendFormat) - res.Append((p[7] != '0') ? L"[s]" : L"[/s]"); - } - else if (!wcsncmp(p, L"\\ul", 3)) { // underlined - if (isChat()) { - if (mi && mi->bUnderline) - res.Append((p[3] != '0') ? L"%u" : L"%U"); - } - else { - if (!lf.lfUnderline && m_SendFormat) { - if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) { - res.Append(L"[u]"); - bInsideUl = true; - } - else if (!wcsncmp(p + 3, L"none", 4)) { - if (bInsideUl) - res.Append(L"[/u]"); - bInsideUl = false; - } - } - } - } - else if (!wcsncmp(p, L"\\tab", 4)) { // tab - res.AppendChar('\t'); - } - else if (p[1] == '\'') { // special character - if (p[2] != ' ' && p[2] != '\\') { - wchar_t tmp[10]; - - if (p[3] != ' ' && p[3] != '\\') { - wcsncpy(tmp, p + 2, 3); - tmp[3] = 0; - } - else { - wcsncpy(tmp, p + 2, 2); - tmp[2] = 0; - } - - // convert string containing char in hex format to int. - wchar_t *stoppedHere; - res.AppendChar(wcstol(tmp, &stoppedHere, 16)); - } - } - - p++; // skip initial slash - p += wcscspn(p, tszRtfBreaks); - if (*p == ' ') - p++; - break; - - case '{': // other RTF control characters - case '}': - p++; - break; - - case '%': // double % for stupid chat engine - if (isChat()) - res.Append(L"%%"); - else - res.AppendChar(*p); - p++; - break; - - default: // other text that should not be touched - res.AppendChar(*p++); - break; - } - } - - if (bInsideColor && !isChat()) - res.Append(L"[/color]"); - if (bInsideUl) - res.Append(L"[/u]"); - - pszText = res; - return TRUE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::EnableSendButton(bool bMode) const -{ - SendDlgItemMessage(m_hwnd, IDOK, BUTTONSETASNORMAL, bMode, 0); - SendDlgItemMessage(m_hwnd, IDC_PIC, BUTTONSETASNORMAL, m_bEditNotesActive ? TRUE : (!bMode && m_iOpenJobs == 0) ? TRUE : FALSE, 0); - - HWND hwndOK = GetDlgItem(GetParent(GetParent(m_hwnd)), IDOK); - if (IsWindow(hwndOK)) - SendMessage(hwndOK, BUTTONSETASNORMAL, bMode, 0); -} - -void CMsgDialog::EnableSending(bool bMode) const -{ - m_message.SendMsg(EM_SETREADONLY, !bMode, 0); - Utils::enableDlgControl(m_hwnd, IDC_CLIST, bMode); - EnableSendButton(bMode); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::FindFirstEvent() -{ - int historyMode = g_plugin.getByte(m_hContact, SRMSGSET_LOADHISTORY, -1); - if (historyMode == -1) - historyMode = (int)g_plugin.getByte(SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY); - - m_hDbEventFirst = db_event_firstUnread(m_hContact); - - if (m_bActualHistory) - historyMode = LOADHISTORY_COUNT; - - DBEVENTINFO dbei = {}; - DB::ECPTR pCursor(DB::EventsRev(m_hContact, m_hDbEventFirst)); - - switch (historyMode) { - case LOADHISTORY_COUNT: - int i; - - // ability to load only current session's history - if (m_bActualHistory) - i = m_cache->getSessionMsgCount(); - else - i = g_plugin.getWord(SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT); - - for (; i > 0; i--) { - MEVENT hPrevEvent = pCursor.FetchNext(); - if (hPrevEvent == 0) - break; - - dbei.cbBlob = 0; - m_hDbEventFirst = hPrevEvent; - db_event_get(m_hDbEventFirst, &dbei); - if (!DbEventIsShown(&dbei)) - i++; - } - break; - - case LOADHISTORY_TIME: - if (m_hDbEventFirst == 0) - dbei.timestamp = time(0); - else - db_event_get(m_hDbEventFirst, &dbei); - - uint32_t firstTime = dbei.timestamp - 60 * g_plugin.getWord(SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME); - - while (MEVENT hPrevEvent = pCursor.FetchNext()) { - dbei.cbBlob = 0; - db_event_get(hPrevEvent, &dbei); - if (dbei.timestamp < firstTime) - break; - m_hDbEventFirst = hPrevEvent; - } - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// returns != 0 when one of the installed keyboard layouts belongs to an rtl language -// used to find out whether we need to configure the message input box for bidirectional mode - -int CMsgDialog::FindRTLLocale() -{ - int result = 0; - - if (m_iHaveRTLLang == 0) { - HKL layouts[20]; - memset(layouts, 0, sizeof(layouts)); - GetKeyboardLayoutList(20, layouts); - for (int i = 0; i < 20 && layouts[i]; i++) { - uint16_t wCtype2[5]; - LCID lcid = MAKELCID(LOWORD(layouts[i]), 0); - GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2); - if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT) - result = 1; - } - m_iHaveRTLLang = (result ? 1 : -1); - } - else result = m_iHaveRTLLang == 1 ? 1 : 0; - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::FlashOnClist(MEVENT hEvent, DBEVENTINFO *dbei) -{ - m_dwTickLastEvent = GetTickCount(); - - if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) { - m_dwUnread++; - AddUnreadContact(m_hContact); - } - - if (hEvent == 0) - return; - - if (!g_plugin.bFlashOnClist) - return; - - if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !m_bFlashClist) { - CLISTEVENT cle = {}; - cle.hContact = m_hContact; - cle.hDbEvent = hEvent; - cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE); - cle.pszService = MS_MSG_READMESSAGE; - g_clistApi.pfnAddEvent(&cle); - - m_bFlashClist = true; - m_hFlashingEvent = hEvent; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// flash a tab icon if mode = true, otherwise restore default icon -// store flashing state into bState - -void CMsgDialog::FlashTab(bool bInvertMode) -{ - if (bInvertMode) - m_bTabFlash = !m_bTabFlash; - - TCITEM item = {}; - item.mask = TCIF_IMAGE; - TabCtrl_SetItem(m_hwndParent, m_iTabID, &item); - if (m_pContainer->cfg.flags.m_bSideBar) - m_pContainer->m_pSideBar->updateSession(this); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// this translates formatting tags into rtf sequences... -// flags: loword = words only for simple * /_ formatting -// hiword = bbcode support (strip bbcodes if 0) - -static wchar_t *w_bbcodes_begin[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[color=" }; -static wchar_t *w_bbcodes_end[] = { L"[/b]", L"[/i]", L"[/u]", L"[/s]", L"[/color]" }; - -static wchar_t *formatting_strings_begin[] = { L"b1 ", L"i1 ", L"u1 ", L"s1 ", L"c1 " }; -static wchar_t *formatting_strings_end[] = { L"b0 ", L"i0 ", L"u0 ", L"s0 ", L"c0 " }; - -void CMsgDialog::FormatRaw(CMStringW &msg, int flags, bool isSent) -{ - bool clr_was_added = false; - int beginmark = 0, endmark = 0, tempmark = 0; - int i, endindex; - - if (m_dwFlags & MWF_LOG_BBCODE) { - beginmark = 0; - while (true) { - for (i = 0; i < _countof(w_bbcodes_begin); i++) - if ((tempmark = msg.Find(w_bbcodes_begin[i], 0)) != -1) - break; - - if (i >= _countof(w_bbcodes_begin)) - break; - - beginmark = tempmark; - endindex = i; - endmark = msg.Find(w_bbcodes_end[i], beginmark); - if (endindex == 4) { // color - int closing = msg.Find(L"]", beginmark); - bool was_added = false; - - if (closing == -1) { // must be an invalid [color=] tag w/o closing bracket - msg.SetAt(beginmark, ' '); - continue; - } - - CMStringW colorname = msg.Mid(beginmark + 7, closing - beginmark - 7); -search_again: - bool clr_found = false; - for (int ii = 0; ii < Utils::rtf_clrs.getCount(); ii++) { - auto &rtfc = Utils::rtf_clrs[ii]; - if (!wcsicmp(colorname, rtfc.szName)) { - closing = beginmark + 7 + (int)mir_wstrlen(rtfc.szName); - if (endmark != -1) { - msg.Delete(endmark, 8); - msg.Insert(endmark, L"c0 "); - } - msg.Delete(beginmark, closing - beginmark + 1); - - wchar_t szTemp[5]; - msg.Insert(beginmark, L"cxxx "); - mir_snwprintf(szTemp, L"%02d", MSGDLGFONTCOUNT + 13 + ii); - msg.SetAt(beginmark + 3, szTemp[0]); - msg.SetAt(beginmark + 4, szTemp[1]); - clr_found = true; - if (was_added) { - wchar_t wszTemp[100]; - mir_snwprintf(wszTemp, L"##col##%06u:%04u", endmark - closing, ii); - wszTemp[99] = 0; - msg.Insert(beginmark, wszTemp); - } - break; - } - } - if (!clr_found) { - int c_closing = colorname.Find(L"]"); - if (c_closing == -1) - c_closing = colorname.GetLength(); - const wchar_t *wszColname = colorname.c_str(); - if (endmark != -1 && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing - 1])) { - Utils::RTF_ColorAdd(wszColname); - if (!was_added) { - clr_was_added = was_added = true; - goto search_again; - } - else goto invalid_code; - } - else { -invalid_code: - if (endmark != -1) - msg.Delete(endmark, 8); - if (closing != -1 && closing < endmark) - msg.Delete(beginmark, (closing - beginmark) + 1); - else - msg.SetAt(beginmark, ' '); - } - } - continue; - } - - if (endmark != -1) { - msg.Delete(endmark, 4); - msg.Insert(endmark, formatting_strings_end[i]); - } - msg.Delete(beginmark, 3); - msg.Insert(beginmark, L" "); - msg.Insert(beginmark, formatting_strings_begin[i]); - } - } - - if ((m_dwFlags & MWF_LOG_TEXTFORMAT) && msg.Find(L"://") == -1) { - while ((beginmark = msg.Find(L"*/_", beginmark)) != -1) { - wchar_t endmarker = msg[beginmark]; - if (LOWORD(flags)) { - if (beginmark > 0 && !iswspace(msg[beginmark - 1]) && !iswpunct(msg[beginmark - 1])) { - beginmark++; - continue; - } - - // search a corresponding endmarker which fulfills the criteria - INT_PTR mark = beginmark + 1; - while ((endmark = msg.Find(endmarker, mark)) != -1) { - if (iswpunct(msg[endmark + 1]) || iswspace(msg[endmark + 1]) || msg[endmark + 1] == 0 || wcschr(L"*/_", msg[endmark + 1]) != nullptr) - goto ok; - mark = endmark + 1; - } - break; - } - else { - if ((endmark = msg.Find(endmarker, beginmark + 1)) == -1) - break; - } -ok: - if ((endmark - beginmark) < 2) { - beginmark++; - continue; - } - - int index = 0; - switch (endmarker) { - case '*': - index = 0; - break; - case '/': - index = 1; - break; - case '_': - index = 2; - break; - } - - // check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if - // it really is one. - if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) { - CMStringW smcode = msg.Mid(beginmark, (endmark - beginmark) + 1); - - SMADD_BATCHPARSE2 smbp = {}; - smbp.cbSize = sizeof(smbp); - smbp.Protocolname = m_cache->getActiveProto(); - smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0); - smbp.str = (wchar_t*)smcode.c_str(); - smbp.hContact = m_hContact; - - SMADD_BATCHPARSERES *smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&smbp); - if (smbpr) { - CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr); - beginmark = endmark + 1; - continue; - } - } - msg.Delete(endmark, 1); - msg.Insert(endmark, formatting_strings_end[index]); - msg.Delete(beginmark, 1); - msg.Insert(beginmark, formatting_strings_begin[index]); - } - } - - m_bClrAdded = clr_was_added; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// format the title bar string for IM chat sessions using placeholders. -// the caller must mir_free() the returned string - -static wchar_t* Trunc500(wchar_t *str) -{ - if (mir_wstrlen(str) > 500) - str[500] = 0; - return str; -} - -bool CMsgDialog::FormatTitleBar(const wchar_t *szFormat, CMStringW &dest) -{ - for (const wchar_t *src = szFormat; *src; src++) { - if (*src != '%') { - dest.AppendChar(*src); - continue; - } - - switch (*++src) { - case 'n': - dest.Append(m_cache->getNick()); - break; - - case 'p': - case 'a': - dest.Append(m_cache->getRealAccount()); - break; - - case 's': - dest.Append(m_wszStatus); - break; - - case 'u': - dest.Append(m_cache->getUIN()); - break; - - case 'c': - dest.Append(!mir_wstrcmp(m_pContainer->m_wszName, L"default") ? TranslateT("Default container") : m_pContainer->m_wszName); - break; - - case 'o': - { - const char *szProto = m_cache->getActiveProto(); - if (szProto) - dest.Append(_A2T(szProto)); - } - break; - - case 'x': - { - uint8_t xStatus = m_cache->getXStatusId(); - if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) { - ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName")); - dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]); - } - } - break; - - case 'm': - { - uint8_t xStatus = m_cache->getXStatusId(); - if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) { - ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName")); - dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]); - } - else dest.Append(m_wszStatus[0] ? m_wszStatus : L"(undef)"); - } - break; - - // status message (%T will skip the "No status message" for empty messages) - case 't': - case 'T': - { - ptrW tszStatus(m_cache->getNormalizedStatusMsg(m_cache->getStatusMsg(), true)); - if (tszStatus) - dest.Append(tszStatus); - else if (*src == 't') - dest.Append(TranslateT("No status message")); - } - break; - - case 'g': - { - ptrW tszGroup(Clist_GetGroup(m_hContact)); - if (tszGroup != nullptr) - dest.Append(tszGroup); - } - break; - - case 0: // wrongly formed format string - return true; - } - } - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// retrieve the visiblity of the avatar window, depending on the global setting -// and local mode - -bool CMsgDialog::GetAvatarVisibility() -{ - uint8_t bAvatarMode = m_pContainer->cfg.avatarMode; - uint8_t bOwnAvatarMode = m_pContainer->cfg.ownAvatarMode; - char hideOverride = (char)M.GetByte(m_hContact, "hideavatar", -1); - - // infopanel visible, consider own avatar display - m_bShowAvatar = false; - if (m_si) - bAvatarMode = 1; - - if (m_pPanel.isActive() && bAvatarMode != 3) { - if (!bOwnAvatarMode) { - m_bShowAvatar = (m_hOwnPic && m_hOwnPic != PluginConfig.g_hbmUnknown); - if (!m_hwndContactPic) - m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr); - } - - switch (bAvatarMode) { - case 2: - m_bShowInfoAvatar = false; - break; - case 0: - m_bShowInfoAvatar = true; - case 1: - HBITMAP hbm = ((m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr); - if (hbm == nullptr && !bAvatarMode) { - m_bShowInfoAvatar = false; - break; - } - - if (!m_hwndPanelPic) { - m_hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, m_hwndPanelPicParent, (HMENU)7000, nullptr, nullptr); - if (m_hwndPanelPic) - SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE); - } - - if (bAvatarMode != 0) - m_bShowInfoAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown); - break; - } - - if (m_bShowInfoAvatar) - m_bShowInfoAvatar = hideOverride == 0 ? false : m_bShowInfoAvatar; - else - m_bShowInfoAvatar = hideOverride == 1 ? true : m_bShowInfoAvatar; - - Utils::setAvatarContact(m_hwndPanelPic, m_hContact); - SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto()); - } - else { - m_bShowInfoAvatar = false; - - switch (bAvatarMode) { - case 0: // globally on - m_bShowAvatar = true; - LBL_Check: - if (!m_hwndContactPic) - m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr); - break; - case 2: // globally OFF - m_bShowAvatar = false; - break; - case 3: // on, if present - case 1: - HBITMAP hbm = (m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr; - m_bShowAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown); - goto LBL_Check; - } - - if (m_bShowAvatar) - m_bShowAvatar = hideOverride == 0 ? 0 : m_bShowAvatar; - else - m_bShowAvatar = hideOverride == 1 ? 1 : m_bShowAvatar; - - // reloads avatars - if (m_hwndPanelPic) { // shows contact or user picture, depending on panel visibility - SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto()); - Utils::setAvatarContact(m_hwndPanelPic, m_hContact); - } - else Utils::setAvatarContact(m_hwndContactPic, m_hContact); - } - return m_bShowAvatar; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::GetClientIcon() -{ - if (m_hClientIcon) - DestroyIcon(m_hClientIcon); - - m_hClientIcon = nullptr; - if (ServiceExists(MS_FP_GETCLIENTICONT)) { - ptrW tszMirver(db_get_wsa(m_cache->getActiveContact(), m_cache->getActiveProto(), "MirVer")); - if (tszMirver) - m_hClientIcon = Finger_GetClientIcon(tszMirver, 1); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HICON CMsgDialog::GetMyContactIcon(const CMOption<bool> *opt) -{ - int bUseMeta = (opt == nullptr) ? false : M.GetByte(opt->GetDBSettingName(), mir_strcmp(opt->GetDBSettingName(), "MetaiconTab") == 0); - if (bUseMeta) - return Skin_LoadProtoIcon(m_cache->getProto(), m_cache->getStatus()); - return Skin_LoadProtoIcon(m_cache->getActiveProto(), m_cache->getActiveStatus()); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::GetMyNick() -{ - ptrW tszNick(Contact::GetInfo(CNF_CUSTOMNICK, 0, m_cache->getActiveProto())); - if (tszNick == nullptr) - tszNick = Contact::GetInfo(CNF_NICK, 0, m_cache->getActiveProto()); - if (tszNick != nullptr) { - if (mir_wstrlen(tszNick) == 0 || !mir_wstrcmp(tszNick, TranslateT("'(Unknown contact)'"))) - wcsncpy_s(m_wszMyNickname, (m_myUin[0] ? m_myUin : TranslateT("'(Unknown contact)'")), _TRUNCATE); - else - wcsncpy_s(m_wszMyNickname, tszNick, _TRUNCATE); - } - else wcsncpy_s(m_wszMyNickname, L"<undef>", _TRUNCATE); // same here -} - -///////////////////////////////////////////////////////////////////////////////////////// -// retrieve both buddys and my own UIN for a message session and store them in the message -// window *dat respects metacontacts and uses the current protocol if the contact is a MC - -void CMsgDialog::GetMYUIN() -{ - ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, 0, m_cache->getActiveProto())); - if (uid != nullptr) - wcsncpy_s(m_myUin, uid, _TRUNCATE); - else - m_myUin[0] = 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// returns the status of Send button - -LRESULT CMsgDialog::GetSendButtonState() -{ - return m_btnOk.SendMsg(BUTTONGETSTATEID, TRUE, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// reads send format and configures the toolbar buttons -// if mode == 0, int only configures the buttons and does not change send format - -void CMsgDialog::GetSendFormat() -{ - m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat); - if (m_SendFormat == -1) // per contact override to disable it.. - m_SendFormat = 0; - else if (m_SendFormat == 0) - m_SendFormat = g_plugin.bSendFormat; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HICON CMsgDialog::GetXStatusIcon() const -{ - uint8_t xStatus = m_cache->getXStatusId(); - if (xStatus == 0) - return nullptr; - - if (!ProtoServiceExists(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON)) - return nullptr; - - return (HICON)(CallProtoService(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON, xStatus, 0)); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// paste contents of the clipboard into the message input area and send it immediately - -void CMsgDialog::HandlePasteAndSend() -{ - // is feature disabled? - if (!g_plugin.bPasteAndSend) { - ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending messages' section")); - return; - } - - m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0); - if (GetWindowTextLength(m_message.GetHwnd()) > 0) - SendMessage(m_hwnd, WM_COMMAND, IDOK, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// convert the avatar bitmap to icon format so that it can be used on the task bar -// tries to keep correct aspect ratio of the avatar image -// -// @param dat: _MessageWindowData* pointer to the window data -// @return HICON: the icon handle - -HICON CMsgDialog::IconFromAvatar() const -{ - if (!ServiceExists(MS_AV_GETAVATARBITMAP)) - return nullptr; - - AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, m_hContact, 0); - if (ace == nullptr || ace->hbmPic == nullptr) - return nullptr; - - LONG lIconSize = Win7Taskbar->getIconSize(); - double dNewWidth, dNewHeight; - Utils::scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize); - - // resize picture to fit it on the task bar, use an image list for converting it to - // 32bpp icon format. hTaskbarIcon will cache it until avatar is changed - HBITMAP hbmResized = ::Image_Resize(ace->hbmPic, RESIZEBITMAP_STRETCH, dNewWidth, dNewHeight); - HIMAGELIST hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0); - - RECT rc = { 0, 0, lIconSize, lIconSize }; - - HDC hdc = ::GetDC(m_pContainer->m_hwnd); - HDC dc = ::CreateCompatibleDC(hdc); - HDC dcResized = ::CreateCompatibleDC(hdc); - - ReleaseDC(m_pContainer->m_hwnd, hdc); - - HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc); - HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew)); - HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized)); - - LONG ix = (lIconSize - (LONG)dNewWidth) / 2; - LONG iy = (lIconSize - (LONG)dNewHeight) / 2; - CSkin::m_default_bf.SourceConstantAlpha = M.GetByte("taskBarIconAlpha", 255); - GdiAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf); - - CSkin::m_default_bf.SourceConstantAlpha = 255; - ::SelectObject(dc, hbmOld); - ::ImageList_Add(hIml_c, hbmNew, nullptr); - ::DeleteObject(hbmNew); - ::DeleteDC(dc); - - ::SelectObject(dcResized, hbmOldResized); - if (hbmResized != ace->hbmPic) - ::DeleteObject(hbmResized); - ::DeleteDC(dcResized); - HICON hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL); - ::ImageList_RemoveAll(hIml_c); - ::ImageList_Destroy(hIml_c); - return hIcon; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// is window active or not? - -bool CMsgDialog::IsActive() const -{ - return m_pContainer->IsActive() && m_pContainer->m_hwndActive == m_hwnd; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// read keyboard state and return the state of the modifier keys - -void CMsgDialog::KbdState(bool &isShift, bool &isControl, bool &isAlt) -{ - GetKeyboardState(kstate); - isShift = (kstate[VK_SHIFT] & 0x80) != 0; - isControl = (kstate[VK_CONTROL] & 0x80) != 0; - isAlt = (kstate[VK_MENU] & 0x80) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LimitMessageText(int iLen) -{ - if (this != nullptr) - m_message.SendMsg(EM_EXLIMITTEXT, 0, iLen); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadContactAvatar() -{ - m_ace = Utils::loadAvatarFromAVS(m_bIsMeta ? db_mc_getSrmmSub(m_hContact) : m_hContact); - - BITMAP bm; - if (m_ace && m_ace->hbmPic) - GetObject(m_ace->hbmPic, sizeof(bm), &bm); - else if (m_ace == nullptr) - GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm); - else - return; - - AdjustBottomAvatarDisplay(); - CalcDynamicAvatarSize(&bm); - - if (!m_pPanel.isActive() || m_pContainer->cfg.avatarMode == 3) { - m_iRealAvatarHeight = 0; - PostMessage(m_hwnd, WM_SIZE, 0, 0); - } - else if (m_pPanel.isActive()) - GetAvatarVisibility(); - - if (m_pWnd != nullptr) - m_pWnd->verifyDwmState(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadOwnAvatar() -{ - if (ServiceExists(MS_AV_GETMYAVATAR)) - m_ownAce = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(m_cache->getActiveProto())); - else - m_ownAce = nullptr; - - if (m_ownAce) - m_hOwnPic = m_ownAce->hbmPic; - else - m_hOwnPic = PluginConfig.g_hbmUnknown; - - if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) { - BITMAP bm; - - m_iRealAvatarHeight = 0; - AdjustBottomAvatarDisplay(); - GetObject(m_hOwnPic, sizeof(bm), &bm); - CalcDynamicAvatarSize(&bm); - Resize(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadSettings() -{ - m_clrInputBG = m_pContainer->m_theme.inputbg; - LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadSplitter() -{ - if (m_bIsAutosizingInput) { - m_iSplitterY = (m_pContainer->cfg.flags.m_bBottomToolbar) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46); - - if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED) - m_iSplitterY += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2); - return; - } - - if (!m_bSplitterOverride) { - if (!m_pContainer->cfg.fPrivate) - m_iSplitterY = (int)M.GetDword("splitsplity", 60); - else - m_iSplitterY = m_pContainer->cfg.iSplitterY; - } - else m_iSplitterY = (int)M.GetDword(m_hContact, "splitsplity", M.GetDword("splitsplity", 60)); - - if (m_iSplitterY < MINSPLITTERY) - m_iSplitterY = 150; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LogEvent(DBEVENTINFO &dbei) -{ - if (m_iLogMode != WANT_BUILTIN_LOG) { - dbei.flags |= DBEF_TEMPORARY; - - MEVENT hDbEvent = db_event_add(m_hContact, &dbei); - if (hDbEvent) { - m_pLog->LogEvents(hDbEvent, 1, true); - db_event_delete(hDbEvent); - } - } - else LOG()->LogEvents(0, 1, true, &dbei); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// draw various elements of the message window, like avatar(s), info panel fields -// and the color formatting menu - -int CMsgDialog::MsgWindowDrawHandler(DRAWITEMSTRUCT *dis) -{ - if ((dis->hwndItem == GetDlgItem(m_hwnd, IDC_CONTACTPIC) && m_bShowAvatar) || (dis->hwndItem == m_hwnd && m_pPanel.isActive())) { - HBITMAP hbmAvatar = m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown; - if (hbmAvatar == nullptr) - return TRUE; - - int top, cx, cy; - RECT rcClient, rcFrame; - bool bPanelPic = (dis->hwndItem == m_hwnd); - if (bPanelPic && !m_bShowInfoAvatar) - return TRUE; - - RECT rc; - GetClientRect(m_hwnd, &rc); - if (bPanelPic) { - rcClient = dis->rcItem; - cx = (rcClient.right - rcClient.left); - cy = (rcClient.bottom - rcClient.top) + 1; - } - else { - GetClientRect(dis->hwndItem, &rcClient); - cx = rcClient.right; - cy = rcClient.bottom; - } - - if (cx < 5 || cy < 5) - return TRUE; - - HDC hdcDraw = CreateCompatibleDC(dis->hDC); - HBITMAP hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy); - HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw); - - bool bAero = M.isAero(); - - HRGN clipRgn = nullptr; - HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcDraw, bAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE)); - rcFrame = rcClient; - - if (!bPanelPic) { - top = (cy - m_pic.cy) / 2; - RECT rcEdge = { 0, top, m_pic.cx, top + m_pic.cy }; - if (CSkin::m_skinEnabled) - CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, hdcDraw); - else if (PluginConfig.m_fillColor) { - HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor); - FillRect(hdcDraw, &rcFrame, br); - DeleteObject(br); - } - else if (bAero && CSkin::m_pCurrentAeroEffect) { - COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh : - (CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0); - - HBRUSH br = CreateSolidBrush(clr); - FillRect(hdcDraw, &rcFrame, br); - DeleteObject(br); - } - else FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE)); - - HPEN hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr); - HPEN hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder); - - if (CSkin::m_bAvatarBorderType == 1) - Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom); - else if (CSkin::m_bAvatarBorderType == 2) { - clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, 6, 6); - SelectClipRgn(hdcDraw, clipRgn); - - HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr); - FrameRgn(hdcDraw, clipRgn, hbr, 1, 1); - DeleteObject(hbr); - DeleteObject(clipRgn); - } - - SelectObject(hdcDraw, hPenOld); - DeleteObject(hPenBorder); - } - - if (bPanelPic) { - bool bBorder = (CSkin::m_bAvatarBorderType ? true : false); - - int border_off = bBorder ? 1 : 0; - int iMaxHeight = m_iPanelAvatarY - (bBorder ? 2 : 0); - int iMaxWidth = m_iPanelAvatarX - (bBorder ? 2 : 0); - - rcFrame.left = rcFrame.top = 0; - rcFrame.right = (rcClient.right - rcClient.left); - rcFrame.bottom = (rcClient.bottom - rcClient.top); - - rcFrame.left = rcFrame.right - (LONG)m_iPanelAvatarX; - rcFrame.bottom = (LONG)m_iPanelAvatarY; - - int height_off = (cy - iMaxHeight - (bBorder ? 2 : 0)) / 2; - rcFrame.top += height_off; - rcFrame.bottom += height_off; - - SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, bAero ? TRUE : FALSE); - SetWindowPos(m_hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off, - iMaxWidth, iMaxHeight, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING); - } - - SelectObject(hdcDraw, hOldBrush); - if (!bPanelPic) - BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY); - SelectObject(hdcDraw, hbmOld); - DeleteObject(hbmDraw); - DeleteDC(hdcDraw); - return TRUE; - } - - if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(m_hwnd, IDC_LOGFROZENTEXT)) { - wchar_t szWindowText[256]; - if (CSkin::m_skinEnabled) { - SetTextColor(dis->hDC, CSkin::m_DefaultFontColor); - CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC); - } - else { - SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT)); - CSkin::FillBack(dis->hDC, &dis->rcItem); - } - GetWindowText(dis->hwndItem, szWindowText, _countof(szWindowText)); - szWindowText[255] = 0; - SetBkMode(dis->hDC, TRANSPARENT); - DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS); - return TRUE; - } - - if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICERRORICON)) { - if (CSkin::m_skinEnabled) - CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC); - else - CSkin::FillBack(dis->hDC, &dis->rcItem); - DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8, - PluginConfig.g_iconErr, 16, 16, 0, nullptr, DI_NORMAL); - return TRUE; - } - - if (dis->CtlType == ODT_MENU && m_pPanel.isHovered()) { - DrawMenuItem(dis, (HICON)dis->itemData, 0); - return TRUE; - } - - return Menu_DrawItem((LPARAM)dis); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMsgDialog::MsgWindowUpdateMenu(HMENU submenu, int menuID) -{ - bool bInfoPanel = m_pPanel.isActive(); - - if (menuID == MENU_TABCONTEXT) { - EnableMenuItem(submenu, ID_TABMENU_LEAVECHATROOM, (isChat() && ProtoServiceExists(m_szProto, PS_LEAVECHAT)) ? MF_ENABLED : MF_GRAYED); - EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) ? MF_GRAYED : MF_ENABLED); - EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M.GetDword(m_hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED); - } - else if (menuID == MENU_PICMENU) { - wchar_t *szText = nullptr; - char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1); - HMENU visMenu = GetSubMenu(submenu, 0); - BOOL picValid = bInfoPanel ? (m_hOwnPic != nullptr) : (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown); - - MENUITEMINFO mii = { 0 }; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STRING; - - EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, picValid ? MF_ENABLED : MF_GRAYED); - - CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED); - - CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, PluginConfig.m_bAlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED); - if (!bInfoPanel) { - EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED); - szText = TranslateT("Contact picture settings..."); - EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED); - } - else { - EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED); - EnableMenuItem(submenu, ID_PICMENU_SETTINGS, (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED); - szText = TranslateT("Set your avatar..."); - } - mii.dwTypeData = szText; - mii.cch = (int)mir_wstrlen(szText) + 1; - SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii); - } - else if (menuID == MENU_PANELPICMENU) { - HMENU visMenu = GetSubMenu(submenu, 0); - char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1); - - CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED); - - EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED); - EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update state of the container - this is called whenever a tab becomes active, no matter how and -// deals with various things like updating the title bar, removing flashing icons, updating the -// session list, switching the keyboard layout (autolocale active) and the general container status. -// -// it protects itself from being called more than once per session activation and is valid for -// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c) - - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMsgDialog::MsgWindowMenuHandler(int selection, int menuId) -{ - if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) { - switch (selection) { - case ID_TABMENU_ATTACHTOCONTAINER: - SelectContainer(); - return 1; - case ID_TABMENU_CONTAINEROPTIONS: - m_pContainer->OptionsDialog(); - return 1; - case ID_TABMENU_CLOSECONTAINER: - SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 0); - return 1; - case ID_TABMENU_CLOSETAB: - PostMessage(m_hwnd, WM_CLOSE, 1, 0); - return 1; - case ID_TABMENU_SAVETABPOSITION: - db_set_dw(m_hContact, SRMSGMOD_T, "tabindex", m_iTabID * 100); - break; - case ID_TABMENU_CLEARSAVEDTABPOSITION: - db_unset(m_hContact, SRMSGMOD_T, "tabindex"); - break; - case ID_TABMENU_LEAVECHATROOM: - if (isChat()) { - char *szProto = Proto_GetBaseAccountName(m_hContact); - if (szProto) - CallProtoService(szProto, PS_LEAVECHAT, m_hContact, 0); - } - return 1; - - case ID_VISIBILITY_DEFAULT: - case ID_VISIBILITY_HIDDENFORTHISCONTACT: - case ID_VISIBILITY_VISIBLEFORTHISCONTACT: - { - uint8_t avOverrideMode; - if (selection == ID_VISIBILITY_DEFAULT) - avOverrideMode = -1; - else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT) - avOverrideMode = 1; - else - avOverrideMode = 0; - db_set_b(m_hContact, SRMSGMOD_T, "hideavatar", avOverrideMode); - } - - ShowPicture(false); - Resize(); - DM_ScrollToBottom(0, 1); - return 1; - - case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH: - PluginConfig.m_bAlwaysFullToolbarWidth = !PluginConfig.m_bAlwaysFullToolbarWidth; - db_set_b(0, SRMSGMOD_T, "alwaysfulltoolbar", (uint8_t)PluginConfig.m_bAlwaysFullToolbarWidth); - Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1); - break; - - case ID_PICMENU_SAVETHISPICTUREAS: - if (m_pPanel.isActive()) - SaveAvatarToFile(m_hOwnPic, 1); - else if (m_ace) - SaveAvatarToFile(m_ace->hbmPic, 0); - break; - - case ID_PANELPICMENU_SAVETHISPICTUREAS: - if (m_ace) - SaveAvatarToFile(m_ace->hbmPic, 0); - break; - - case ID_PICMENU_SETTINGS: - if (menuId == MENU_PICMENU) { - if (m_pPanel.isActive()) { - if (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) - CallService(MS_AV_SETMYAVATARW, (WPARAM)(m_cache->getActiveProto()), 0); - return TRUE; - } - } - CallService(MS_AV_CONTACTOPTIONS, m_hContact, (LPARAM)m_hwnd); - return 1; - } - } - else if (menuId == MENU_LOGMENU) { - switch (selection) { - case ID_MESSAGELOGSETTINGS_GLOBAL: - g_plugin.openOptions(nullptr, L"Message sessions", L"Message log"); - return 1; - - case ID_MESSAGELOGSETTINGS_FORTHISCONTACT: - CallService(MS_TABMSG_SETUSERPREFS, m_hContact, 0); - return 1; - } - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::NotifyDeliveryFailure() const -{ - if (!g_plugin.bErrorPopup || !Popup_Enabled()) - return; - - POPUPDATAW ppd = {}; - ppd.lchContact = m_hContact; - ppd.PluginWindowProc = Utils::PopupDlgProcError; - ppd.lchIcon = PluginConfig.g_iconErr; - ppd.iSeconds = NEN::iDelayErr; - if (!NEN::bColDefaultErr) { - ppd.colorText = NEN::colTextErr; - ppd.colorBack = NEN::colBackErr; - } - wcsncpy_s(ppd.lpwzContactName, m_cache->getNick(), _TRUNCATE); - wcsncpy_s(ppd.lpwzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE); - PUAddPopupW(&ppd); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::PlayIncomingSound() const -{ - int iPlay = m_pContainer->MustPlaySound(this); - if (iPlay) { - if (GetForegroundWindow() == m_pContainer->m_hwnd && m_pContainer->m_hwndActive == m_hwnd) - Skin_PlaySound("RecvMsgActive"); - else - Skin_PlaySound("RecvMsgInactive"); - } -} - -void CMsgDialog::RemakeLog() -{ - m_szMicroLf[0] = 0; - m_lastEventTime = 0; - m_iLastEventType = -1; - StreamEvents(m_hDbEventFirst, -1, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// saves a contact picture to disk -// takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar) -// requires AVS service (Miranda 0.7+) - -void CMsgDialog::SaveAvatarToFile(HBITMAP hbm, int isOwnPic) -{ - wchar_t szFinalFilename[MAX_PATH]; - time_t t = time(0); - struct tm *lt = localtime(&t); - uint32_t setView = 1; - - wchar_t szTimestamp[100]; - mir_snwprintf(szTimestamp, L"%04u %02u %02u_%02u%02u", lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min); - - wchar_t *szProto = mir_a2u(m_cache->getActiveProto()); - - wchar_t szFinalPath[MAX_PATH]; - mir_snwprintf(szFinalPath, L"%s\\%s", M.getSavedAvatarPath(), szProto); - mir_free(szProto); - - if (CreateDirectory(szFinalPath, nullptr) == 0) { - if (GetLastError() != ERROR_ALREADY_EXISTS) { - MessageBox(nullptr, TranslateT("Error creating destination directory"), - TranslateT("Save contact picture"), MB_OK | MB_ICONSTOP); - return; - } - } - - wchar_t szBaseName[MAX_PATH]; - if (isOwnPic) - mir_snwprintf(szBaseName, L"My Avatar_%s", szTimestamp); - else - mir_snwprintf(szBaseName, L"%s_%s", m_cache->getNick(), szTimestamp); - - mir_snwprintf(szFinalFilename, L"%s.png", szBaseName); - - // do not allow / or \ or % in the filename - Utils::sanitizeFilename(szFinalFilename); - - wchar_t filter[MAX_PATH]; - mir_snwprintf(filter, L"%s%c*.bmp;*.png;*.jpg;*.gif%c%c", TranslateT("Image files"), 0, 0, 0); - - OPENFILENAME ofn = { 0 }; - ofn.lpstrDefExt = L"png"; - ofn.lpstrFilter = filter; - ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK; - ofn.lpfnHook = OpenFileSubclass; - ofn.lStructSize = sizeof(ofn); - ofn.lpstrFile = szFinalFilename; - ofn.lpstrInitialDir = szFinalPath; - ofn.nMaxFile = MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - ofn.lCustData = (LPARAM)& setView; - if (GetSaveFileName(&ofn)) { - if (PathFileExists(szFinalFilename)) - if (MessageBox(nullptr, TranslateT("The file exists. Do you want to overwrite it?"), TranslateT("Save contact picture"), MB_YESNO | MB_ICONQUESTION) == IDNO) - return; - - IMGSRVC_INFO ii; - ii.cbSize = sizeof(ii); - ii.pwszName = szFinalFilename; - ii.hbm = hbm; - ii.dwMask = IMGI_HBITMAP; - ii.fif = FIF_UNKNOWN; // get the format from the filename extension. png is default. - Image_Save(&ii); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::SaveSplitter() -{ - if (m_bIsAutosizingInput) - return; - - if (m_iSplitterY < DPISCALEY_S(MINSPLITTERY) || m_iSplitterY < 0) - m_iSplitterY = DPISCALEY_S(MINSPLITTERY); - - if (m_bSplitterOverride) - db_set_dw(m_hContact, SRMSGMOD_T, "splitsplity", m_iSplitterY); - else { - if (m_pContainer->cfg.fPrivate) - m_pContainer->cfg.iSplitterY = m_iSplitterY; - else - db_set_dw(0, SRMSGMOD_T, "splitsplity", m_iSplitterY); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// send a pasted bitmap by file transfer. - -static LIST<wchar_t> vTempFilenames(5); - -void CMsgDialog::SendHBitmapAsFile(HBITMAP hbmp) const -{ - const wchar_t *mirandatempdir = L"Miranda"; - const wchar_t *filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg"; - wchar_t filename[MAX_PATH]; - size_t tempdirlen = GetTempPath(MAX_PATH, filename); - bool fSend = true; - - const char *szProto = m_cache->getActiveProto(); - int wMyStatus = Proto_GetStatus(szProto); - - uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0); - uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0); - - // check protocol capabilities, status modes and visibility lists (privacy) - // to determine whether the file can be sent. Throw a warning if any of - // these checks fails. - if (!(protoCaps & PF1_FILESEND)) - fSend = false; - - if ((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == m_cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES))) - fSend = false; - - if (protoCaps & PF1_VISLIST && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE) - fSend = false; - - if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE) - fSend = false; - - if (!fSend) { - CWarning::show(CWarning::WARN_SENDFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE); - return; - } - - if (tempdirlen <= 0 || tempdirlen >= MAX_PATH - mir_wstrlen(mirandatempdir) - mir_wstrlen(filenametemplate) - 2) // -2 is because %Y takes 4 symbols - filename[0] = 0; // prompt for a new name - else { - mir_wstrcpy(filename + tempdirlen, mirandatempdir); - if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, nullptr) == 0) - filename[0] = 0; - else { - tempdirlen = mir_wstrlen(filename); - - time_t rawtime; - time(&rawtime); - const tm *timeinfo; - timeinfo = _localtime32((__time32_t *)& rawtime); - wcsftime(filename + tempdirlen, MAX_PATH - tempdirlen, filenametemplate, timeinfo); - size_t firstnumberpos = tempdirlen + 14; - size_t lastnumberpos = tempdirlen + 20; - while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) { // while it exists - for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--) - if (filename[pos]++ != '9') - break; - else - if (pos == firstnumberpos) - filename[0] = 0; // all filenames exist => prompt for a new name - else - filename[pos] = '0'; - } - } - } - - if (filename[0] == 0) { // prompting to save - wchar_t filter[MAX_PATH]; - mir_snwprintf(filter, L"%s%c*.jpg%c%c", TranslateT("JPEG-compressed images"), 0, 0, 0); - - OPENFILENAME dlg; - dlg.lStructSize = sizeof(dlg); - dlg.lpstrFilter = filter; - dlg.nFilterIndex = 1; - dlg.lpstrFile = filename; - dlg.nMaxFile = MAX_PATH; - dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; - dlg.lpstrDefExt = L"jpg"; - if (!GetSaveFileName(&dlg)) - return; - } - - IMGSRVC_INFO ii; - ii.cbSize = sizeof(ii); - ii.hbm = hbmp; - ii.pwszName = filename; - ii.dwMask = IMGI_HBITMAP; - ii.fif = FIF_JPEG; - if (!Image_Save(&ii)) { - CWarning::show(CWarning::WARN_SAVEFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE); - return; - } - - vTempFilenames.insert(mir_wstrdup(filename)); - - wchar_t *ppFiles[2] = { filename, nullptr }; - CallService(MS_FILE_SENDSPECIFICFILEST, m_cache->getActiveContact(), (LPARAM)&ppFiles); -} - -// remove all temporary files created by the "send clipboard as file" feature. -void TSAPI CleanTempFiles() -{ - for (auto &it : vTempFilenames) { - DeleteFileW(it); - mir_free(it); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Sets a status bar text for a contact - -void CMsgDialog::SetStatusText(const wchar_t *wszText, HICON hIcon) -{ - if (wszText != nullptr) { - m_bStatusSet = true; - m_szStatusText = wszText; - m_szStatusIcon = hIcon; - } - else { - m_bStatusSet = false; - m_szStatusText.Empty(); - m_szStatusIcon = nullptr; - } - - tabUpdateStatusBar(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// subclassing for the message filter dialog (set and configure event filters for the -// current session - -static UINT _eventorder[] = -{ - GC_EVENT_ACTION, - GC_EVENT_MESSAGE, - GC_EVENT_NICK, - GC_EVENT_JOIN, - GC_EVENT_PART, - GC_EVENT_TOPIC, - GC_EVENT_ADDSTATUS, - GC_EVENT_INFORMATION, - GC_EVENT_QUIT, - GC_EVENT_KICK, - GC_EVENT_NOTICE -}; - -INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CMsgDialog *pDlg = (CMsgDialog *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - switch (uMsg) { - case WM_INITDIALOG: - pDlg = (CMsgDialog *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); - { - uint32_t dwMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", 0); - uint32_t dwFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", 0); - - uint32_t dwPopupMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", 0); - uint32_t dwPopupFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", 0); - - uint32_t dwTrayMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", 0); - uint32_t dwTrayFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", 0); - - for (int i = 0; i < _countof(_eventorder); i++) { - CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE); - CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE); - CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE); - } - } - return FALSE; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - SetTextColor((HDC)wParam, RGB(60, 60, 150)); - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - - case WM_CLOSE: - if (wParam == 1 && lParam == 1 && pDlg) { - int iFlags = 0; - uint32_t dwMask = 0; - - for (int i = 0; i < _countof(_eventorder); i++) { - int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i); - dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0); - iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0); - } - - if (iFlags & GC_EVENT_ADDSTATUS) - iFlags |= GC_EVENT_REMOVESTATUS; - - if (dwMask == 0) { - db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterFlags"); - db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterMask"); - } - else { - db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", iFlags); - db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", dwMask); - } - - dwMask = iFlags = 0; - - for (int i = 0; i < _countof(_eventorder); i++) { - int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i); - dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0); - iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0); - } - - if (iFlags & GC_EVENT_ADDSTATUS) - iFlags |= GC_EVENT_REMOVESTATUS; - - if (dwMask == 0) { - db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupFlags"); - db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupMask"); - } - else { - db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", iFlags); - db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", dwMask); - } - - dwMask = iFlags = 0; - - for (int i = 0; i < _countof(_eventorder); i++) { - int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i); - dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0); - iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0); - } - if (iFlags & GC_EVENT_ADDSTATUS) - iFlags |= GC_EVENT_REMOVESTATUS; - - if (dwMask == 0) { - db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags"); - db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask"); - } - else { - db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", iFlags); - db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", dwMask); - } - Chat_SetFilters(pDlg->getChat()); - - if (pDlg->m_bFilterEnabled) { - if (pDlg->m_iLogFilterFlags == 0) - pDlg->m_btnFilter.Click(); - pDlg->RedrawLog(); - db_set_b(pDlg->m_hContact, CHAT_MODULE, "FilterEnabled", pDlg->m_bFilterEnabled); - } - } - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} - -void CMsgDialog::ShowFilterMenu() -{ - m_hwndFilter = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_pContainer->m_hwnd, FilterWndProc, (LPARAM)this); - TranslateDialogDefault(m_hwndFilter); - - RECT rcFilter, rcLog; - GetClientRect(m_hwndFilter, &rcFilter); - GetWindowRect(m_pLog->GetHwnd(), &rcLog); - - POINT pt; - pt.x = rcLog.right; pt.y = rcLog.bottom; - ScreenToClient(m_pContainer->m_hwnd, &pt); - - SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::ShowPicture(bool showNewPic) -{ - if (!m_pPanel.isActive()) - m_pic.cy = m_pic.cx = DPISCALEY_S(60); - - if (showNewPic) { - if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) { - if (!m_hwndPanelPic) { - InvalidateRect(m_hwnd, nullptr, TRUE); - UpdateWindow(m_hwnd); - Resize(); - } - return; - } - AdjustBottomAvatarDisplay(); - } - else { - m_bShowAvatar = !m_bShowAvatar; - db_set_b(m_hContact, SRMSGMOD_T, "MOD_ShowPic", m_bShowAvatar); - } - - RECT rc; - GetWindowRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), &rc); - if (m_minEditBoxSize.cy + DPISCALEY_S(3) > m_iSplitterY) - SplitterMoved(rc.bottom - m_minEditBoxSize.cy, GetDlgItem(m_hwnd, IDC_SPLITTERY)); - if (!showNewPic) - SetDialogToType(); - else - Resize(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// show a modified context menu for the richedit control(s) - -void CMsgDialog::ShowPopupMenu(const CCtrlBase &pCtrl, POINT pt) -{ - CHARRANGE sel, all = { 0, -1 }; - - HMENU hSubMenu, hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); - if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) - hSubMenu = GetSubMenu(hMenu, 0); - else { - hSubMenu = GetSubMenu(hMenu, 2); - EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, m_SendFormat != 0 ? MF_ENABLED : MF_GRAYED); - EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, g_plugin.bPasteAndSend ? MF_ENABLED : MF_GRAYED); - CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED); - EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, m_pContainer->m_hwndStatus ? MF_ENABLED : MF_GRAYED); - } - TranslateMenu(hSubMenu); - pCtrl.SendMsg(EM_EXGETSEL, 0, (LPARAM)& sel); - if (sel.cpMin == sel.cpMax) { - EnableMenuItem(hSubMenu, IDM_COPY, MF_GRAYED); - EnableMenuItem(hSubMenu, IDM_QUOTE, MF_GRAYED); - if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE) - EnableMenuItem(hSubMenu, IDM_CUT, MF_GRAYED); - } - - if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) { - InsertMenuA(hSubMenu, 6, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr); - CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, m_bScrollingDisabled ? MF_CHECKED : MF_UNCHECKED); - } - - MessageWindowPopupData mwpd; - // First notification - mwpd.uType = MSG_WINDOWPOPUP_SHOWING; - mwpd.uFlags = (pCtrl.GetCtrlId() == IDC_SRMM_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT); - mwpd.hContact = m_hContact; - mwpd.hwnd = pCtrl.GetHwnd(); - mwpd.hMenu = hSubMenu; - mwpd.selection = 0; - mwpd.pt = pt; - NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd); - - int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr); - - // Second notification - mwpd.selection = iSelection; - mwpd.uType = MSG_WINDOWPOPUP_SELECTED; - NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd); - - switch (iSelection) { - case IDM_COPY: - pCtrl.SendMsg(WM_COPY, 0, 0); - break; - case IDM_CUT: - pCtrl.SendMsg(WM_CUT, 0, 0); - break; - case IDM_PASTE: - case IDM_PASTEFORMATTED: - if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE) - pCtrl.SendMsg(EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0); - break; - case IDM_COPYALL: - pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all); - pCtrl.SendMsg(WM_COPY, 0, 0); - pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& sel); - break; - case IDM_QUOTE: - SendMessage(m_hwnd, WM_COMMAND, IDC_QUOTE, 0); - break; - case IDM_SELECTALL: - pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all); - break; - case IDM_CLEAR: - tabClearLog(); - break; - case ID_LOG_FREEZELOG: - SendDlgItemMessage(m_hwnd, IDC_SRMM_LOG, WM_KEYDOWN, VK_F12, 0); - break; - case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR: - PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator; - db_set_b(0, SRMSGMOD_T, "msgsizebar", (uint8_t)PluginConfig.m_visualMessageSizeIndicator); - Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 0); - Resize(); - if (m_pContainer->m_hwndStatus) - RedrawWindow(m_pContainer->m_hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); - break; - case ID_EDITOR_PASTEANDSENDIMMEDIATELY: - HandlePasteAndSend(); - break; - } - - if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) - RemoveMenu(hSubMenu, 7, MF_BYPOSITION); - DestroyMenu(hMenu); -} - -void CMsgDialog::SplitterMoved(int coord, HWND hwnd) -{ - POINT pt; - RECT rc; - - switch (GetDlgCtrlID(hwnd)) { - case IDC_MULTISPLITTER: - GetClientRect(m_hwnd, &rc); - pt.x = coord; - pt.y = 0; - ScreenToClient(m_hwnd, &pt); - { - int oldSplitterX = m_iMultiSplit; - m_iMultiSplit = rc.right - pt.x; - if (m_iMultiSplit < 25) - m_iMultiSplit = 25; - - if (m_iMultiSplit > ((rc.right - rc.left) - 80)) - m_iMultiSplit = oldSplitterX; - } - Resize(); - break; - - case IDC_SPLITTERX: - GetClientRect(m_hwnd, &rc); - pt.x = coord, pt.y = 0; - ScreenToClient(m_hwnd, &pt); - { - int iSplitterX = rc.right - pt.x + 1; - if (iSplitterX < 35) - iSplitterX = 35; - if (iSplitterX > rc.right - rc.left - 35) - iSplitterX = rc.right - rc.left - 35; - m_pContainer->cfg.iSplitterX = iSplitterX; - } - Resize(); - break; - - case IDC_SPLITTERY: - GetClientRect(m_hwnd, &rc); - rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30); - pt.x = 0; - pt.y = coord; - ScreenToClient(m_hwnd, &pt); - { - int oldSplitterY = m_iSplitterY; - int oldDynaSplitter = m_dynaSplitter; - - m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(23); - - // attempt to fix splitter troubles.. - // hardcoded limits... better solution is possible, but this works for now - int bottomtoolbarH = 0; - if (m_pContainer->cfg.flags.m_bBottomToolbar) - bottomtoolbarH = 22; - - if (m_iSplitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) { // min splitter size - m_iSplitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH); - m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34); - DM_RecalcPictureSize(); - } - else if (m_iSplitterY > (rc.bottom - rc.top)) { - m_iSplitterY = oldSplitterY; - m_dynaSplitter = oldDynaSplitter; - DM_RecalcPictureSize(); - } - else { - m_dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11); - DM_RecalcPictureSize(); - } - } - UpdateToolbarBG(); - Resize(); - break; - - case IDC_PANELSPLITTER: - GetClientRect(m_pLog->GetHwnd(), &rc); - - POINT pnt = { 0, coord }; - ScreenToClient(m_hwnd, &pnt); - if ((pnt.y + 2 >= MIN_PANELHEIGHT + 2) && (pnt.y + 2 < 100) && (pnt.y + 2 < rc.bottom - 30)) - m_pPanel.setHeight(pnt.y + 2, true); - - RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); - if (M.isAero()) - InvalidateRect(GetParent(m_hwnd), nullptr, FALSE); - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::StreamEvents(MEVENT hDbEventFirst, int count, bool bAppend) -{ - m_pLog->LogEvents(hDbEventFirst, count, bAppend); - - DM_ScrollToBottom(0, 0); - if (bAppend && hDbEventFirst) - m_hDbEventLast = hDbEventFirst; - else - m_hDbEventLast = db_event_last(m_hContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// sent by the select container dialog box when a container was selected... - -void CMsgDialog::SwitchToContainer(const wchar_t *szNewName) -{ - if (!mir_wstrcmp(szNewName, TranslateT("Default container"))) - szNewName = CGlobals::m_default_container_name; - - int iOldItems = TabCtrl_GetItemCount(m_hwndParent); - if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN)) - return; - - TContainerData *pNewContainer = FindContainerByName(szNewName); - if (pNewContainer == nullptr) - if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr) - return; - - db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName); - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact); - if (iOldItems > 1) // there were more than 1 tab, container is still valid - SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0); - SetForegroundWindow(pNewContainer->m_hwnd); - SetActiveWindow(pNewContainer->m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CMsgDialog::TabAutoComplete() -{ - LRESULT lResult = m_message.SendMsg(EM_GETSEL, 0, 0); - int start = LOWORD(lResult), end = HIWORD(lResult); - int origStart = start, origEnd = end; - m_message.SendMsg(EM_SETSEL, end, end); - - GETTEXTEX gt = { 0 }; - gt.codepage = 1200; - gt.flags = GTL_DEFAULT | GTL_PRECISE; - int iLen = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)& gt, 0); - if (iLen <= 0) - return false; - - bool isTopic = false, isRoom = false; - wchar_t *pszText = (wchar_t *)mir_calloc((iLen + 10) * sizeof(wchar_t)); - - gt.flags = GT_DEFAULT; - gt.cb = (iLen + 9) * sizeof(wchar_t); - m_message.SendMsg(EM_GETTEXTEX, (WPARAM)& gt, (LPARAM)pszText); - - if (m_wszSearchResult != nullptr) { - int cbResult = (int)mir_wstrlen(m_wszSearchResult); - if (start >= cbResult && !wcsnicmp(m_wszSearchResult, pszText + start - cbResult, cbResult)) { - start -= cbResult; - goto LBL_SkipEnd; - } - } - - while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB) - start--; - -LBL_SkipEnd: - while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB) - end++; - - if (pszText[start] == '#') - isRoom = true; - else { - int topicStart = start; - while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB)) - topicStart--; - if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6]) - isTopic = true; - } - - if (m_wszSearchQuery == nullptr) { - m_wszSearchQuery = mir_wstrndup(pszText + start, end - start); - m_wszSearchResult = mir_wstrdup(m_wszSearchQuery); - m_pLastSession = nullptr; - } - - const wchar_t *pszName = nullptr; - if (isTopic) - pszName = m_si->ptszTopic; - else if (isRoom) { - m_pLastSession = SM_FindSessionAutoComplete(m_si->pszModule, m_si, m_pLastSession, m_wszSearchQuery, m_wszSearchResult); - if (m_pLastSession != nullptr) - pszName = m_pLastSession->ptszName; - } - else pszName = g_chatApi.UM_FindUserAutoComplete(m_si, m_wszSearchQuery, m_wszSearchResult); - - replaceStrW(m_wszSearchResult, nullptr); - - if (pszName != nullptr) { - if (end != start) { - CMStringW szReplace; - if (!isRoom && !isTopic && start == 0) { - szReplace = pszName; - if (mir_wstrlen(g_Settings.pwszAutoText)) - szReplace.Append(g_Settings.pwszAutoText); - szReplace.AppendChar(' '); - m_wszSearchResult = szReplace.Detach(); - pszName = m_wszSearchResult; - } - else m_wszSearchResult = mir_wstrdup(pszName); - - m_message.SendMsg(EM_SETSEL, start, end); - m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pszName); - } - else m_wszSearchResult = mir_wstrdup(pszName); - - return true; - } - - if (end != start) { - m_message.SendMsg(EM_SETSEL, start, end); - m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)m_wszSearchQuery); - } - m_message.SendMsg(EM_SETSEL, origStart, origEnd); - replaceStrW(m_wszSearchQuery, nullptr); - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::tabClearLog() -{ - if (isChat()) { - g_chatApi.LM_RemoveAll(&m_si->pLog, &m_si->pLogEnd); - m_si->iEventCount = 0; - m_si->LastTime = 0; - PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - } - - m_pLog->Clear(); - m_hDbEventFirst = 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CThumbBase *CMsgDialog::tabCreateThumb(CProxyWindow *pProxy) const -{ - if (isChat()) - return new CThumbMUC(pProxy, m_si); - - return new CThumbIM(pProxy); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update all status bar fields and force a redraw of the status bar. - -void CMsgDialog::tabUpdateStatusBar() const -{ - if (m_pContainer->m_hwndStatus && m_pContainer->m_hwndActive == m_hwnd) { - if (!isChat()) { - if (m_wszStatusBar[0]) { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]); - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar); - } - else if (m_bStatusSet) { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon); - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str()); - } - else { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0); - DM_UpdateLastMessage(); - } - } - else { - if (m_bStatusSet) { - SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon); - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str()); - } - else SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0); - } - UpdateReadChars(); - InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE); - SendMessage(m_pContainer->m_hwndStatus, WM_USER + 101, 0, (LPARAM)this); - } -} - -int CMsgDialog::Typing(int secs) -{ - if (!AllowTyping()) - return 0; - - int preTyping = m_nTypeSecs != 0; - - setTyping(m_nTypeSecs = (secs > 0) ? secs : 0); - if (m_nTypeSecs) - m_bShowTyping = 0; - - return preTyping; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::UpdateNickList() -{ - int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0); - m_nickList.SendMsg(LB_SETTOPINDEX, i, 0); - UpdateTitle(); - m_hTabIcon = m_hTabStatusIcon; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::UpdateOptions() -{ - GetSendFormat(); - - DM_InitRichEdit(); - m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0); - - m_nickList.SetItemHeight(0, g_Settings.iNickListFontHeight); - InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE); - - m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(m_bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0); - - CSuper::UpdateOptions(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update the status bar field which displays the number of characters in the input area -// and various indicators (caps lock, num lock, insert mode). - -void CMsgDialog::UpdateReadChars() const -{ - if (!m_pContainer->m_hwndStatus || m_pContainer->m_hwndActive != m_hwnd) - return; - - int len; - if (isChat()) - len = GetWindowTextLength(m_message.GetHwnd()); - else { - // retrieve text length in UTF8 bytes, because this is the relevant length for most protocols - GETTEXTLENGTHEX gtxl = { 0 }; - gtxl.codepage = CP_UTF8; - gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES; - - len = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); - } - - BOOL fCaps = (GetKeyState(VK_CAPITAL) & 1); - BOOL fNum = (GetKeyState(VK_NUMLOCK) & 1); - - wchar_t szBuf[20]; szBuf[0] = 0; - if (m_bInsertMode) - mir_wstrcat(szBuf, L"O"); - if (fCaps) - mir_wstrcat(szBuf, L"C"); - if (fNum) - mir_wstrcat(szBuf, L"N"); - if (m_bInsertMode || fCaps || fNum) - mir_wstrcat(szBuf, L" | "); - - wchar_t buf[128]; - mir_snwprintf(buf, L"%s%s %d/%d", szBuf, m_lcID, m_iOpenJobs, len); - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)buf); - if (PluginConfig.m_visualMessageSizeIndicator) - InvalidateRect(m_pContainer->m_hwndStatus, nullptr, FALSE); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::UpdateSaveAndSendButton() -{ - GETTEXTLENGTHEX gtxl = { 0 }; - gtxl.codepage = CP_UTF8; - gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES; - - int len = SendDlgItemMessage(m_hwnd, IDC_SRMM_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); - if (len && GetSendButtonState() == PBS_DISABLED) - EnableSendButton(true); - else if (len == 0 && GetSendButtonState() != PBS_DISABLED) - EnableSendButton(false); - - if (len) { // looks complex but avoids flickering on the button while typing. - if (!m_bSaveBtn) { - SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]); - SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save and close session"), BATF_UNICODE); - m_bSaveBtn = true; - } - } - else { - SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]); - SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Close session"), BATF_UNICODE); - m_bSaveBtn = false; - } - m_textLen = len; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::UpdateStatusBar() -{ - if (m_pContainer->m_hwndActive != m_hwnd || m_pContainer->m_hwndStatus == nullptr || CMimAPI::m_shutDown || m_wszStatusBar[0]) - return; - - if (m_si->pszModule == nullptr) - return; - - //Mad: strange rare crash here... - MODULEINFO *mi = m_si->pMI; - if (!mi->ptszModDispName) - return; - - int x = 12; - x += Chat_GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->m_hwndStatus, WM_GETFONT, 0, 0), true); - x += GetSystemMetrics(SM_CXSMICON); - - wchar_t szFinalStatusBarText[512]; - if (m_pPanel.isActive()) { - time_t now = time(0); - uint32_t diff = (now - mi->idleTimeStamp) / 60; - if (diff >= 1) { - if (diff > 59) { - uint32_t hours = diff / 60; - uint32_t minutes = diff % 60; - mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"), - hours, hours > 1 ? TranslateT("hours") : TranslateT("hour"), - minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute")); - } - else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute")); - } - else mi->tszIdleMsg[0] = 0; - - mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg); - } - else { - if (m_si->ptszStatusbarText) - mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, m_si->ptszStatusbarText); - else - wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE); - } - SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText); - tabUpdateStatusBar(); - m_pPanel.Invalidate(); - if (m_pWnd) - m_pWnd->Invalidate(); -} - -void CMsgDialog::UpdateTitle() -{ - if (isChat()) { - m_wStatus = m_si->wStatus; - - const wchar_t *szNick = m_cache->getNick(); - if (mir_wstrlen(szNick) > 0) { - if (M.GetByte("cuttitle", 0)) - CutContactName(szNick, m_wszTitle, _countof(m_wszTitle)); - else - wcsncpy_s(m_wszTitle, szNick, _TRUNCATE); - } - - CMStringW wszTitle; - HICON hIcon = nullptr; - int nUsers = m_si->getUserList().getCount(); - - switch (m_si->iType) { - case GCW_CHATROOM: - hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus); - wszTitle.Format((nUsers == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"), - szNick, nUsers, m_bFilterEnabled ? TranslateT(", event filter active") : L""); - break; - - case GCW_PRIVMESS: - hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus); - if (nUsers == 1) - wszTitle.Format(TranslateT("%s: message session"), szNick); - else - wszTitle.Format(TranslateT("%s: message session (%u users)"), szNick, nUsers); - break; - - case GCW_SERVER: - wszTitle.Format(L"%s: Server", szNick); - hIcon = LoadIconEx("window"); - break; - - default: - return; - } - - if (m_pWnd) { - m_pWnd->updateTitle(m_wszTitle); - m_pWnd->updateIcon(hIcon); - } - m_hTabStatusIcon = hIcon; - - if (m_cache->getStatus() != m_cache->getOldStatus()) { - wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_wStatus, 0), _TRUNCATE); - - TCITEM item = {}; - item.mask = TCIF_TEXT; - item.pszText = m_wszTitle; - TabCtrl_SetItem(m_hwndParent, m_iTabID, &item); - } - SetWindowText(m_hwnd, wszTitle); - if (m_pContainer->m_hwndActive == m_hwnd) { - m_pContainer->UpdateTitle(0, this); - UpdateStatusBar(); - } - } - else { - uint32_t dwOldIdle = m_idle; - const char *szActProto = nullptr; - - m_wszStatus[0] = 0; - - if (m_iTabID == -1) - return; - - TCITEM item = {}; - - bool bChanged = false; - wchar_t newtitle[128]; - if (m_szProto) { - szActProto = m_cache->getProto(); - - bool bHasName = (m_cache->getUIN()[0] != 0); - m_idle = m_cache->getIdleTS(); - m_bIsIdle = m_idle != 0; - - m_wStatus = m_cache->getStatus(); - wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_szProto == nullptr ? ID_STATUS_OFFLINE : m_wStatus, 0), _TRUNCATE); - - wchar_t newcontactname[128]; newcontactname[0] = 0; - if (PluginConfig.m_bCutContactNameOnTabs) - CutContactName(m_cache->getNick(), newcontactname, _countof(newcontactname)); - else - wcsncpy_s(newcontactname, m_cache->getNick(), _TRUNCATE); - - Utils::DoubleAmpersands(newcontactname, _countof(newcontactname)); - - if (newcontactname[0] != 0) { - if (g_plugin.bStatusOnTabs) - mir_snwprintf(newtitle, L"%s (%s)", newcontactname, m_wszStatus); - else - wcsncpy_s(newtitle, newcontactname, _TRUNCATE); - } - else wcsncpy_s(newtitle, L"Forward", _TRUNCATE); - - if (mir_wstrcmp(newtitle, m_wszTitle)) - bChanged = true; - else if (m_wStatus != m_wOldStatus) - bChanged = true; - - UpdateWindowIcon(); - - wchar_t fulluin[256]; - if (m_bIsMeta) - mir_snwprintf(fulluin, - TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nRight click for metacontact control\nClick dropdown to add or remove user from your favorites."), - bHasName ? m_cache->getUIN() : TranslateT("No UID")); - else - mir_snwprintf(fulluin, - TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nClick dropdown to change this contact's favorite status."), - bHasName ? m_cache->getUIN() : TranslateT("No UID")); - - SendDlgItemMessage(m_hwnd, IDC_NAME, BUTTONADDTOOLTIP, (WPARAM)fulluin, BATF_UNICODE); - } - else wcsncpy_s(newtitle, L"Message Session", _TRUNCATE); - - m_wOldStatus = m_wStatus; - if (m_idle != dwOldIdle || bChanged) { - if (bChanged) { - item.mask |= TCIF_TEXT; - item.pszText = m_wszTitle; - wcsncpy_s(m_wszTitle, newtitle, _TRUNCATE); - if (m_pWnd) - m_pWnd->updateTitle(m_cache->getNick()); - } - if (m_iTabID >= 0) { - TabCtrl_SetItem(m_hwndParent, m_iTabID, &item); - if (m_pContainer->cfg.flags.m_bSideBar) - m_pContainer->m_pSideBar->updateSession(this); - } - if (m_pContainer->m_hwndActive == m_hwnd && bChanged) - m_pContainer->UpdateTitle(m_hContact); - - m_pPanel.Invalidate(); - if (m_pWnd) - m_pWnd->Invalidate(); - } - - // care about MetaContacts and update the statusbar icon with the currently "most online" contact... - if (m_bIsMeta) { - PostMessage(m_hwnd, DM_OWNNICKCHANGED, 0, 0); - if (m_pContainer->cfg.flags.m_bUinStatusBar) - DM_UpdateLastMessage(); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::UpdateWindowIcon() -{ - if (m_hXStatusIcon) { - DestroyIcon(m_hXStatusIcon); - m_hXStatusIcon = nullptr; - } - - if (LPCSTR szProto = m_cache->getProto()) { - m_hTabIcon = m_hTabStatusIcon = GetMyContactIcon(&g_plugin.bMetaTab); - if (g_plugin.bUseXStatus) - m_hXStatusIcon = GetXStatusIcon(); - - SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BUTTONSETASDIMMED, m_bIsIdle, 0); - SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(m_hXStatusIcon ? m_hXStatusIcon : GetMyContactIcon(&g_plugin.bMetaBar))); - - if (m_pContainer->m_hwndActive == m_hwnd) - m_pContainer->SetIcon(this, m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon); - - if (m_pWnd) - m_pWnd->updateIcon(m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// called whenever a group chat tab becomes active(either by switching tabs or activating a -// container window - -void CMsgDialog::UpdateWindowState(UINT msg) -{ - if (m_iTabID < 0) - return; - - if (msg == WM_ACTIVATE) { - if (m_pContainer->cfg.flags.m_bTransparent) { - uint32_t trans = LOWORD(m_pContainer->cfg.dwTransparency); - SetLayeredWindowAttributes(m_pContainer->m_hwnd, CSkin::m_ContainerColorKey, (uint8_t)trans, (m_pContainer->cfg.flags.m_bTransparent ? LWA_ALPHA : 0)); - } - } - - if (m_hwndFilter) { - POINT pt; - GetCursorPos(&pt); - - RECT rcFilter; - GetWindowRect(m_hwndFilter, &rcFilter); - if (!PtInRect(&rcFilter, pt)) { - SendMessage(m_hwndFilter, WM_CLOSE, 1, 1); - m_hwndFilter = nullptr; - } - } - - if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) { - m_iInputAreaHeight = 0; - m_message.SendMsg(EM_REQUESTRESIZE, 0, 0); - } - - m_pPanel.dismissConfig(); - m_dwUnread = 0; - if (m_pWnd) { - m_pWnd->activateTab(); - m_pWnd->setOverlayIcon(nullptr, true); - } - - if (m_pContainer->m_hwndSaved == m_hwnd) - return; - - m_pContainer->m_hwndSaved = m_hwnd; - m_dwTickLastEvent = 0; - m_bDividerSet = false; - - if (m_pContainer->m_dwFlashingStarted != 0) { - m_pContainer->FlashContainer(0, 0); - m_pContainer->m_dwFlashingStarted = 0; - } - - if (m_si) { - g_chatApi.SetActiveSession(m_si); - m_hTabIcon = m_hTabStatusIcon; - - if (db_get_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0) != 0) - db_set_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0); - if (g_clistApi.pfnGetEvent(m_si->hContact, 0)) - g_clistApi.pfnRemoveEvent(m_si->hContact, GC_FAKE_EVENT); - - UpdateTitle(); - m_hTabIcon = m_hTabStatusIcon; - if (timerFlash.Stop() || m_iFlashIcon) { - FlashTab(false); - m_bCanFlashTab = FALSE; - m_iFlashIcon = nullptr; - } - - m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false; - - if (m_bNeedCheckSize) - PostMessage(m_hwnd, DM_SAVESIZE, 0, 0); - - SetFocus(m_message.GetHwnd()); - m_dwLastActivity = GetTickCount(); - m_pContainer->m_dwLastActivity = m_dwLastActivity; - m_pContainer->m_pMenuBar->configureMenu(); - } - else { - if (timerFlash.Stop()) { - FlashTab(false); - m_bCanFlashTab = false; - } - - if (m_bFlashClist) { - m_bFlashClist = false; - if (m_hFlashingEvent != 0) - g_clistApi.pfnRemoveEvent(m_hContact, m_hFlashingEvent); - m_hFlashingEvent = 0; - } - m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false; - - if (m_bDeferredRemakeLog && !IsIconic(m_pContainer->m_hwnd)) { - RemakeLog(); - m_bDeferredRemakeLog = false; - } - - if (m_bNeedCheckSize) - PostMessage(m_hwnd, DM_SAVESIZE, 0, 0); - - m_pContainer->m_hIconTaskbarOverlay = nullptr; - m_pContainer->UpdateTitle(m_hContact); - - tabUpdateStatusBar(); - m_dwLastActivity = GetTickCount(); - m_pContainer->m_dwLastActivity = m_dwLastActivity; - - m_pContainer->m_pMenuBar->configureMenu(); - g_arUnreadWindows.remove(HANDLE(m_hContact)); - - m_pPanel.Invalidate(); - - if (m_bDeferredScroll) { - m_bDeferredScroll = false; - DM_ScrollToBottom(0, 1); - } - } - - DM_SetDBButtonStates(); - - if (m_bDelayedSplitter) { - m_bDelayedSplitter = false; - ShowWindow(m_pContainer->m_hwnd, SW_RESTORE); - PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam); - PostMessage(m_hwnd, WM_SIZE, 0, 0); - m_wParam = m_lParam = 0; - } - - BB_SetButtonsPos(); - if (M.isAero()) - InvalidateRect(m_hwndParent, nullptr, FALSE); - - if (m_pContainer->cfg.flags.m_bSideBar) - m_pContainer->m_pSideBar->setActiveItem(this, msg == WM_ACTIVATE); - - if (m_pWnd) - m_pWnd->Invalidate(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// generic handler for the WM_COPY message in message log/chat history richedit control(s). -// it filters out the invisible event boundary markers from the text copied to the clipboard. -// WINE Fix: overwrite clippboad data from original control data - -LRESULT CMsgDialog::WMCopyHandler(UINT msg, WPARAM wParam, LPARAM lParam) -{ - LRESULT result = mir_callNextSubclass(m_pLog->GetHwnd(), stubLogProc, msg, wParam, lParam); - - ptrA szFromStream(LOG()->GetRichTextRtf(true, true)); - if (szFromStream != nullptr) { - ptrW converted(mir_utf8decodeW(szFromStream)); - if (converted != nullptr) { - Utils::FilterEventMarkers(converted); - Utils_ClipboardCopy(converted); - } - } - - return result; -} +/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// Helper functions for the message dialog.
+
+#include "stdafx.h"
+
+UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM, LPARAM lParam);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show the balloon tooltip control.
+
+void CMsgDialog::ActivateTooltip(int iCtrlId, const wchar_t *pwszMessage)
+{
+ if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd)
+ m_pPanel.showTip(iCtrlId, pwszMessage);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::AddLog()
+{
+ if (g_plugin.bUseDividers) {
+ if (g_plugin.bDividersUsePopupConfig) {
+ if (!MessageWindowOpened(0, this))
+ DM_AddDivider();
+ }
+ else {
+ if (!IsActive())
+ DM_AddDivider();
+ else if (m_pContainer->m_hwndActive != m_hwnd)
+ DM_AddDivider();
+ }
+ }
+
+ CSrmmBaseDialog::AddLog();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::AdjustBottomAvatarDisplay()
+{
+ GetAvatarVisibility();
+
+ bool bInfoPanel = m_pPanel.isActive();
+ HBITMAP hbm = (bInfoPanel && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
+ if (hbm) {
+ if (m_dynaSplitter == 0 || m_iSplitterY == 0)
+ LoadSplitter();
+ m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
+ DM_RecalcPictureSize();
+ Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
+ }
+ else {
+ Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
+ m_pic.cy = m_pic.cx = DPISCALEY_S(60);
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// calculates avatar layouting, based on splitter position to find the optimal size
+// for the avatar w/o disturbing the toolbar too much.
+
+void CMsgDialog::CalcDynamicAvatarSize(BITMAP *bminfo)
+{
+ if (m_bWasBackgroundCreate || m_pContainer->cfg.flags.m_bDeferredConfigure || m_pContainer->cfg.flags.m_bCreateMinimized || IsIconic(m_pContainer->m_hwnd))
+ return; // at this stage, the layout is not yet ready...
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+
+ BOOL bBottomToolBar = m_pContainer->cfg.flags.m_bBottomToolbar;
+ BOOL bToolBar = m_pContainer->cfg.flags.m_bHideToolbar ? 0 : 1;
+ int iSplitOffset = m_bIsAutosizingInput ? 1 : 0;
+
+ double picAspect = (bminfo->bmWidth == 0 || bminfo->bmHeight == 0) ? 1.0 : (double)(bminfo->bmWidth / (double)bminfo->bmHeight);
+ double picProjectedWidth = (double)((m_dynaSplitter - ((bBottomToolBar && bToolBar) ? DPISCALEX_S(24) : 0) + ((m_bShowUIElements) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect;
+
+ if ((rc.right - (int)picProjectedWidth) > (m_iButtonBarReallyNeeds) && !PluginConfig.m_bAlwaysFullToolbarWidth && bToolBar)
+ m_iRealAvatarHeight = m_dynaSplitter + 3 + (m_bShowUIElements ? DPISCALEY_S(28) : DPISCALEY_S(2));
+ else
+ m_iRealAvatarHeight = m_dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset);
+
+ m_iRealAvatarHeight -= ((bBottomToolBar && bToolBar) ? DPISCALEY_S(22) : 0);
+
+ if (PluginConfig.m_LimitStaticAvatarHeight > 0)
+ m_iRealAvatarHeight = min(m_iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight);
+
+ if (M.GetByte(m_hContact, "dontscaleavatars", M.GetByte("dontscaleavatars", 0)))
+ m_iRealAvatarHeight = min(bminfo->bmHeight, m_iRealAvatarHeight);
+
+ double aspect = (bminfo->bmHeight != 0) ? (double)m_iRealAvatarHeight / (double)bminfo->bmHeight : 1.0;
+ double newWidth = (double)bminfo->bmWidth * aspect;
+ if (newWidth > (double)(rc.right) * 0.8)
+ newWidth = (double)(rc.right) * 0.8;
+ m_pic.cy = m_iRealAvatarHeight + 2;
+ m_pic.cx = (int)newWidth + 2;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::CloseTab()
+{
+ int iTabs = TabCtrl_GetItemCount(m_hwndParent);
+ if (iTabs == 1) {
+ SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 1);
+ return;
+ }
+
+ m_pContainer->m_iChilds--;
+ int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd);
+
+ // after closing a tab, we need to activate the tab to the left side of
+ // the previously open tab.
+ // normally, this tab has the same index after the deletion of the formerly active tab
+ // unless, of course, we closed the last (rightmost) tab.
+ if (!m_pContainer->m_bDontSmartClose && iTabs > 1) {
+ if (i == iTabs - 1)
+ i--;
+ else
+ i++;
+ TabCtrl_SetCurSel(m_hwndParent, i);
+
+ m_pContainer->m_hwndActive = GetTabWindow(m_hwndParent, i);
+
+ RECT rc;
+ m_pContainer->QueryClientArea(rc);
+ SetWindowPos(m_pContainer->m_hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW);
+ ShowWindow(m_pContainer->m_hwndActive, SW_SHOW);
+ SetForegroundWindow(m_pContainer->m_hwndActive);
+ SetFocus(m_pContainer->m_hwndActive);
+ }
+
+ SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
+ DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// calculate the minimum required client height for the given message
+// window layout
+//
+// the container will use this in its WM_GETMINMAXINFO handler to set
+// minimum tracking height.
+
+void CMsgDialog::DetermineMinHeight()
+{
+ RECT rc;
+ LONG height = (m_pPanel.isActive() ? m_pPanel.getHeight() + 2 : 0);
+ if (!m_pContainer->cfg.flags.m_bHideToolbar)
+ height += DPISCALEY_S(24); // toolbar
+ GetClientRect(m_message.GetHwnd(), &rc);
+ height += rc.bottom; // input area
+ height += 40; // min space for log area and some padding
+
+ m_pContainer->m_uChildMinHeight = height;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting
+// tags and return plain text
+
+static wchar_t tszRtfBreaks[] = L" \\\n\r";
+
+static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst)
+{
+ const wchar_t *pszText = Text;
+ int iIndex = 1;
+
+ static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];";
+ wchar_t szRed[10], szGreen[10], szBlue[10];
+
+ const wchar_t *p1 = wcsstr(pszText, L"\\colortbl");
+ if (!p1)
+ return;
+
+ const wchar_t *pEnd = wcschr(p1, '}');
+
+ const wchar_t *p2 = wcsstr(p1, L"\\red");
+
+ for (int i = 0; i < iCount; i++)
+ pDst[i] = -1;
+
+ while (p2 && p2 < pEnd) {
+ if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) {
+ for (int i = 0; i < iCount; i++) {
+ if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue)))
+ pDst[i] = iIndex;
+ }
+ }
+ iIndex++;
+ p1 = p2;
+ p1++;
+
+ p2 = wcsstr(p1, L"\\red");
+ }
+}
+
+static int RtfColorToIndex(int iNumColors, int *pIndex, int iCol)
+{
+ for (int i = 0; i < iNumColors; i++)
+ if (pIndex[i] == iCol)
+ return i;
+
+ return -1;
+}
+
+BOOL CMsgDialog::DoRtfToTags(CMStringW &pszText) const
+{
+ if (pszText.IsEmpty())
+ return FALSE;
+
+ // used to filter out attributes which are already set for the default message input area font
+ auto &lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
+
+ // create an index of colors in the module and map them to
+ // corresponding colors in the RTF color table
+ int iNumColors = Utils::rtf_clrs.getCount();
+ int *pIndex = (int *)_alloca(iNumColors * sizeof(int));
+ COLORREF *pColors = (COLORREF *)_alloca(iNumColors * sizeof(COLORREF));
+ for (int i = 0; i < iNumColors; i++)
+ pColors[i] = Utils::rtf_clrs[i].clr;
+ CreateColorMap(pszText, iNumColors, pColors, pIndex);
+
+ // scan the file for rtf commands and remove or parse them
+ int idx = pszText.Find(L"\\pard");
+ if (idx == -1) {
+ if ((idx = pszText.Find(L"\\ltrpar")) == -1)
+ return FALSE;
+ idx += 7;
+ }
+ else idx += 5;
+
+ MODULEINFO *mi = (isChat()) ? m_si->pMI : nullptr;
+
+ bool bInsideColor = false, bInsideUl = false;
+ CMStringW res;
+
+ // iterate through all characters, if rtf control character found then take action
+ for (const wchar_t *p = pszText.GetString() + idx; *p;) {
+ switch (*p) {
+ case '\\':
+ if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters
+ res.AppendChar(p[1]);
+ p += 2; break;
+ }
+ if (p[1] == '~') { // non-breaking space
+ res.AppendChar(0xA0);
+ p += 2; break;
+ }
+
+ if (!wcsncmp(p, L"\\cf", 3)) { // foreground color
+ int iCol = _wtoi(p + 3);
+ int iInd = RtfColorToIndex(iNumColors, pIndex, iCol);
+
+ if (iCol > 0) {
+ if (isChat()) {
+ if (mi && mi->bColor) {
+ if (iInd >= 0) {
+ if (!(res.IsEmpty() && m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA] == pColors[iInd]))
+ res.AppendFormat(L"%%c%u", iInd);
+ }
+ else if (!res.IsEmpty())
+ res.Append(L"%%C");
+ }
+ }
+ else res.AppendFormat((iInd >= 0) ? (bInsideColor ? L"[/color][color=%s]" : L"[color=%s]") : (bInsideColor ? L"[/color]" : L""), Utils::rtf_clrs[iInd].szName);
+ }
+
+ bInsideColor = iInd >= 0;
+ }
+ else if (!wcsncmp(p, L"\\highlight", 10)) { // background color
+ if (isChat()) {
+ if (mi && mi->bBkgColor) {
+ int iInd = RtfColorToIndex(iNumColors, pIndex, _wtoi(p + 10));
+ if (iInd >= 0) {
+ // if the entry field is empty & the color passed is the back color, skip it
+ if (!(res.IsEmpty() && m_pContainer->m_theme.inputbg == pColors[iInd]))
+ res.AppendFormat(L"%%f%u", iInd);
+ }
+ else if (!res.IsEmpty())
+ res.AppendFormat(L"%%F");
+ }
+ }
+ }
+ else if (!wcsncmp(p, L"\\line", 5)) { // soft line break;
+ res.AppendChar('\n');
+ }
+ else if (!wcsncmp(p, L"\\endash", 7)) {
+ res.AppendChar(0x2013);
+ }
+ else if (!wcsncmp(p, L"\\emdash", 7)) {
+ res.AppendChar(0x2014);
+ }
+ else if (!wcsncmp(p, L"\\bullet", 7)) {
+ res.AppendChar(0x2022);
+ }
+ else if (!wcsncmp(p, L"\\ldblquote", 10)) {
+ res.AppendChar(0x201C);
+ }
+ else if (!wcsncmp(p, L"\\rdblquote", 10)) {
+ res.AppendChar(0x201D);
+ }
+ else if (!wcsncmp(p, L"\\lquote", 7)) {
+ res.AppendChar(0x2018);
+ }
+ else if (!wcsncmp(p, L"\\rquote", 7)) {
+ res.AppendChar(0x2019);
+ }
+ else if (!wcsncmp(p, L"\\b", 2)) { //bold
+ if (isChat()) {
+ if (mi && mi->bBold)
+ res.Append((p[2] != '0') ? L"%b" : L"%B");
+ }
+ else {
+ if (!(lf.lfWeight == FW_BOLD)) // only allow bold if the font itself isn't a bold one, otherwise just strip it..
+ if (m_SendFormat)
+ res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
+ }
+ }
+ else if (!wcsncmp(p, L"\\i", 2)) { // italics
+ if (isChat()) {
+ if (mi && mi->bItalics)
+ res.Append((p[2] != '0') ? L"%i" : L"%I");
+ }
+ else {
+ if (!lf.lfItalic && m_SendFormat)
+ res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
+ }
+ }
+ else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
+ if (!lf.lfStrikeOut && m_SendFormat)
+ res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
+ }
+ else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
+ if (isChat()) {
+ if (mi && mi->bUnderline)
+ res.Append((p[3] != '0') ? L"%u" : L"%U");
+ }
+ else {
+ if (!lf.lfUnderline && m_SendFormat) {
+ if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) {
+ res.Append(L"[u]");
+ bInsideUl = true;
+ }
+ else if (!wcsncmp(p + 3, L"none", 4)) {
+ if (bInsideUl)
+ res.Append(L"[/u]");
+ bInsideUl = false;
+ }
+ }
+ }
+ }
+ else if (!wcsncmp(p, L"\\tab", 4)) { // tab
+ res.AppendChar('\t');
+ }
+ else if (p[1] == '\'') { // special character
+ if (p[2] != ' ' && p[2] != '\\') {
+ wchar_t tmp[10];
+
+ if (p[3] != ' ' && p[3] != '\\') {
+ wcsncpy(tmp, p + 2, 3);
+ tmp[3] = 0;
+ }
+ else {
+ wcsncpy(tmp, p + 2, 2);
+ tmp[2] = 0;
+ }
+
+ // convert string containing char in hex format to int.
+ wchar_t *stoppedHere;
+ res.AppendChar(wcstol(tmp, &stoppedHere, 16));
+ }
+ }
+
+ p++; // skip initial slash
+ p += wcscspn(p, tszRtfBreaks);
+ if (*p == ' ')
+ p++;
+ break;
+
+ case '{': // other RTF control characters
+ case '}':
+ p++;
+ break;
+
+ case '%': // double % for stupid chat engine
+ if (isChat())
+ res.Append(L"%%");
+ else
+ res.AppendChar(*p);
+ p++;
+ break;
+
+ default: // other text that should not be touched
+ res.AppendChar(*p++);
+ break;
+ }
+ }
+
+ if (bInsideColor && !isChat())
+ res.Append(L"[/color]");
+ if (bInsideUl)
+ res.Append(L"[/u]");
+
+ pszText = res;
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::EnableSendButton(bool bMode) const
+{
+ SendDlgItemMessage(m_hwnd, IDOK, BUTTONSETASNORMAL, bMode, 0);
+ SendDlgItemMessage(m_hwnd, IDC_PIC, BUTTONSETASNORMAL, m_bEditNotesActive ? TRUE : (!bMode && m_iOpenJobs == 0) ? TRUE : FALSE, 0);
+
+ HWND hwndOK = GetDlgItem(GetParent(GetParent(m_hwnd)), IDOK);
+ if (IsWindow(hwndOK))
+ SendMessage(hwndOK, BUTTONSETASNORMAL, bMode, 0);
+}
+
+void CMsgDialog::EnableSending(bool bMode) const
+{
+ m_message.SendMsg(EM_SETREADONLY, !bMode, 0);
+ Utils::enableDlgControl(m_hwnd, IDC_CLIST, bMode);
+ EnableSendButton(bMode);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::FindFirstEvent()
+{
+ int historyMode = g_plugin.getByte(m_hContact, SRMSGSET_LOADHISTORY, -1);
+ if (historyMode == -1)
+ historyMode = (int)g_plugin.getByte(SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY);
+
+ m_hDbEventFirst = db_event_firstUnread(m_hContact);
+
+ if (m_bActualHistory)
+ historyMode = LOADHISTORY_COUNT;
+
+ DBEVENTINFO dbei = {};
+ DB::ECPTR pCursor(DB::EventsRev(m_hContact, m_hDbEventFirst));
+
+ switch (historyMode) {
+ case LOADHISTORY_COUNT:
+ int i;
+
+ // ability to load only current session's history
+ if (m_bActualHistory)
+ i = m_cache->getSessionMsgCount();
+ else
+ i = g_plugin.getWord(SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT);
+
+ for (; i > 0; i--) {
+ MEVENT hPrevEvent = pCursor.FetchNext();
+ if (hPrevEvent == 0)
+ break;
+
+ dbei.cbBlob = 0;
+ m_hDbEventFirst = hPrevEvent;
+ db_event_get(m_hDbEventFirst, &dbei);
+ if (!DbEventIsShown(&dbei))
+ i++;
+ }
+ break;
+
+ case LOADHISTORY_TIME:
+ if (m_hDbEventFirst == 0)
+ dbei.timestamp = time(0);
+ else
+ db_event_get(m_hDbEventFirst, &dbei);
+
+ uint32_t firstTime = dbei.timestamp - 60 * g_plugin.getWord(SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME);
+
+ while (MEVENT hPrevEvent = pCursor.FetchNext()) {
+ dbei.cbBlob = 0;
+ db_event_get(hPrevEvent, &dbei);
+ if (dbei.timestamp < firstTime)
+ break;
+ m_hDbEventFirst = hPrevEvent;
+ }
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns != 0 when one of the installed keyboard layouts belongs to an rtl language
+// used to find out whether we need to configure the message input box for bidirectional mode
+
+int CMsgDialog::FindRTLLocale()
+{
+ int result = 0;
+
+ if (m_iHaveRTLLang == 0) {
+ HKL layouts[20];
+ memset(layouts, 0, sizeof(layouts));
+ GetKeyboardLayoutList(20, layouts);
+ for (int i = 0; i < 20 && layouts[i]; i++) {
+ uint16_t wCtype2[5];
+ LCID lcid = MAKELCID(LOWORD(layouts[i]), 0);
+ GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2);
+ if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT)
+ result = 1;
+ }
+ m_iHaveRTLLang = (result ? 1 : -1);
+ }
+ else result = m_iHaveRTLLang == 1 ? 1 : 0;
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::FlashOnClist(MEVENT hEvent, DBEVENTINFO *dbei)
+{
+ m_dwTickLastEvent = GetTickCount();
+
+ if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) {
+ m_dwUnread++;
+ AddUnreadContact(m_hContact);
+ }
+
+ if (hEvent == 0)
+ return;
+
+ if (!g_plugin.bFlashOnClist)
+ return;
+
+ if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !m_bFlashClist) {
+ CLISTEVENT cle = {};
+ cle.hContact = m_hContact;
+ cle.hDbEvent = hEvent;
+ cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = MS_MSG_READMESSAGE;
+ g_clistApi.pfnAddEvent(&cle);
+
+ m_bFlashClist = true;
+ m_hFlashingEvent = hEvent;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// flash a tab icon if mode = true, otherwise restore default icon
+// store flashing state into bState
+
+void CMsgDialog::FlashTab(bool bInvertMode)
+{
+ if (bInvertMode)
+ m_bTabFlash = !m_bTabFlash;
+
+ TCITEM item = {};
+ item.mask = TCIF_IMAGE;
+ TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+ if (m_pContainer->cfg.flags.m_bSideBar)
+ m_pContainer->m_pSideBar->updateSession(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this translates formatting tags into rtf sequences...
+// flags: loword = words only for simple * /_ formatting
+// hiword = bbcode support (strip bbcodes if 0)
+
+static wchar_t *w_bbcodes_begin[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[color=" };
+static wchar_t *w_bbcodes_end[] = { L"[/b]", L"[/i]", L"[/u]", L"[/s]", L"[/color]" };
+
+static wchar_t *formatting_strings_begin[] = { L"b1 ", L"i1 ", L"u1 ", L"s1 ", L"c1 " };
+static wchar_t *formatting_strings_end[] = { L"b0 ", L"i0 ", L"u0 ", L"s0 ", L"c0 " };
+
+void CMsgDialog::FormatRaw(CMStringW &msg, int flags, bool isSent)
+{
+ bool clr_was_added = false;
+ int beginmark = 0, endmark = 0, tempmark = 0;
+ int i, endindex;
+
+ if (m_dwFlags & MWF_LOG_BBCODE) {
+ beginmark = 0;
+ while (true) {
+ for (i = 0; i < _countof(w_bbcodes_begin); i++)
+ if ((tempmark = msg.Find(w_bbcodes_begin[i], 0)) != -1)
+ break;
+
+ if (i >= _countof(w_bbcodes_begin))
+ break;
+
+ beginmark = tempmark;
+ endindex = i;
+ endmark = msg.Find(w_bbcodes_end[i], beginmark);
+ if (endindex == 4) { // color
+ int closing = msg.Find(L"]", beginmark);
+ bool was_added = false;
+
+ if (closing == -1) { // must be an invalid [color=] tag w/o closing bracket
+ msg.SetAt(beginmark, ' ');
+ continue;
+ }
+
+ CMStringW colorname = msg.Mid(beginmark + 7, closing - beginmark - 7);
+search_again:
+ bool clr_found = false;
+ for (int ii = 0; ii < Utils::rtf_clrs.getCount(); ii++) {
+ auto &rtfc = Utils::rtf_clrs[ii];
+ if (!wcsicmp(colorname, rtfc.szName)) {
+ closing = beginmark + 7 + (int)mir_wstrlen(rtfc.szName);
+ if (endmark != -1) {
+ msg.Delete(endmark, 8);
+ msg.Insert(endmark, L"c0 ");
+ }
+ msg.Delete(beginmark, closing - beginmark + 1);
+
+ wchar_t szTemp[5];
+ msg.Insert(beginmark, L"cxxx ");
+ mir_snwprintf(szTemp, L"%02d", MSGDLGFONTCOUNT + 13 + ii);
+ msg.SetAt(beginmark + 3, szTemp[0]);
+ msg.SetAt(beginmark + 4, szTemp[1]);
+ clr_found = true;
+ if (was_added) {
+ wchar_t wszTemp[100];
+ mir_snwprintf(wszTemp, L"##col##%06u:%04u", endmark - closing, ii);
+ wszTemp[99] = 0;
+ msg.Insert(beginmark, wszTemp);
+ }
+ break;
+ }
+ }
+ if (!clr_found) {
+ int c_closing = colorname.Find(L"]");
+ if (c_closing == -1)
+ c_closing = colorname.GetLength();
+ const wchar_t *wszColname = colorname.c_str();
+ if (endmark != -1 && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing - 1])) {
+ Utils::RTF_ColorAdd(wszColname);
+ if (!was_added) {
+ clr_was_added = was_added = true;
+ goto search_again;
+ }
+ else goto invalid_code;
+ }
+ else {
+invalid_code:
+ if (endmark != -1)
+ msg.Delete(endmark, 8);
+ if (closing != -1 && closing < endmark)
+ msg.Delete(beginmark, (closing - beginmark) + 1);
+ else
+ msg.SetAt(beginmark, ' ');
+ }
+ }
+ continue;
+ }
+
+ if (endmark != -1) {
+ msg.Delete(endmark, 4);
+ msg.Insert(endmark, formatting_strings_end[i]);
+ }
+ msg.Delete(beginmark, 3);
+ msg.Insert(beginmark, L" ");
+ msg.Insert(beginmark, formatting_strings_begin[i]);
+ }
+ }
+
+ if ((m_dwFlags & MWF_LOG_TEXTFORMAT) && msg.Find(L"://") == -1) {
+ while ((beginmark = msg.Find(L"*/_", beginmark)) != -1) {
+ wchar_t endmarker = msg[beginmark];
+ if (LOWORD(flags)) {
+ if (beginmark > 0 && !iswspace(msg[beginmark - 1]) && !iswpunct(msg[beginmark - 1])) {
+ beginmark++;
+ continue;
+ }
+
+ // search a corresponding endmarker which fulfills the criteria
+ INT_PTR mark = beginmark + 1;
+ while ((endmark = msg.Find(endmarker, mark)) != -1) {
+ if (iswpunct(msg[endmark + 1]) || iswspace(msg[endmark + 1]) || msg[endmark + 1] == 0 || wcschr(L"*/_", msg[endmark + 1]) != nullptr)
+ goto ok;
+ mark = endmark + 1;
+ }
+ break;
+ }
+ else {
+ if ((endmark = msg.Find(endmarker, beginmark + 1)) == -1)
+ break;
+ }
+ok:
+ if ((endmark - beginmark) < 2) {
+ beginmark++;
+ continue;
+ }
+
+ int index = 0;
+ switch (endmarker) {
+ case '*':
+ index = 0;
+ break;
+ case '/':
+ index = 1;
+ break;
+ case '_':
+ index = 2;
+ break;
+ }
+
+ // check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if
+ // it really is one.
+ if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) {
+ CMStringW smcode = msg.Mid(beginmark, (endmark - beginmark) + 1);
+
+ SMADD_BATCHPARSE2 smbp = {};
+ smbp.cbSize = sizeof(smbp);
+ smbp.Protocolname = m_cache->getActiveProto();
+ smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0);
+ smbp.str = (wchar_t*)smcode.c_str();
+ smbp.hContact = m_hContact;
+
+ SMADD_BATCHPARSERES *smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&smbp);
+ if (smbpr) {
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr);
+ beginmark = endmark + 1;
+ continue;
+ }
+ }
+ msg.Delete(endmark, 1);
+ msg.Insert(endmark, formatting_strings_end[index]);
+ msg.Delete(beginmark, 1);
+ msg.Insert(beginmark, formatting_strings_begin[index]);
+ }
+ }
+
+ m_bClrAdded = clr_was_added;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// format the title bar string for IM chat sessions using placeholders.
+// the caller must mir_free() the returned string
+
+static wchar_t* Trunc500(wchar_t *str)
+{
+ if (mir_wstrlen(str) > 500)
+ str[500] = 0;
+ return str;
+}
+
+bool CMsgDialog::FormatTitleBar(const wchar_t *szFormat, CMStringW &dest)
+{
+ for (const wchar_t *src = szFormat; *src; src++) {
+ if (*src != '%') {
+ dest.AppendChar(*src);
+ continue;
+ }
+
+ switch (*++src) {
+ case 'n':
+ dest.Append(m_cache->getNick());
+ break;
+
+ case 'p':
+ case 'a':
+ dest.Append(m_cache->getRealAccount());
+ break;
+
+ case 's':
+ dest.Append(m_wszStatus);
+ break;
+
+ case 'u':
+ dest.Append(m_cache->getUIN());
+ break;
+
+ case 'c':
+ dest.Append(!mir_wstrcmp(m_pContainer->m_wszName, L"default") ? TranslateT("Default container") : m_pContainer->m_wszName);
+ break;
+
+ case 'o':
+ {
+ const char *szProto = m_cache->getActiveProto();
+ if (szProto)
+ dest.Append(_A2T(szProto));
+ }
+ break;
+
+ case 'x':
+ {
+ uint8_t xStatus = m_cache->getXStatusId();
+ if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+ ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
+ dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
+ }
+ }
+ break;
+
+ case 'm':
+ {
+ uint8_t xStatus = m_cache->getXStatusId();
+ if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+ ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
+ dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
+ }
+ else dest.Append(m_wszStatus[0] ? m_wszStatus : L"(undef)");
+ }
+ break;
+
+ // status message (%T will skip the "No status message" for empty messages)
+ case 't':
+ case 'T':
+ {
+ ptrW tszStatus(m_cache->getNormalizedStatusMsg(m_cache->getStatusMsg(), true));
+ if (tszStatus)
+ dest.Append(tszStatus);
+ else if (*src == 't')
+ dest.Append(TranslateT("No status message"));
+ }
+ break;
+
+ case 'g':
+ {
+ ptrW tszGroup(Clist_GetGroup(m_hContact));
+ if (tszGroup != nullptr)
+ dest.Append(tszGroup);
+ }
+ break;
+
+ case 0: // wrongly formed format string
+ return true;
+ }
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve the visiblity of the avatar window, depending on the global setting
+// and local mode
+
+bool CMsgDialog::GetAvatarVisibility()
+{
+ uint8_t bAvatarMode = m_pContainer->cfg.avatarMode;
+ uint8_t bOwnAvatarMode = m_pContainer->cfg.ownAvatarMode;
+ char hideOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+
+ // infopanel visible, consider own avatar display
+ m_bShowAvatar = false;
+ if (m_si)
+ bAvatarMode = 1;
+
+ if (m_pPanel.isActive() && bAvatarMode != 3) {
+ if (!bOwnAvatarMode) {
+ m_bShowAvatar = (m_hOwnPic && m_hOwnPic != PluginConfig.g_hbmUnknown);
+ if (!m_hwndContactPic)
+ m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
+ }
+
+ switch (bAvatarMode) {
+ case 2:
+ m_bShowInfoAvatar = false;
+ break;
+ case 0:
+ m_bShowInfoAvatar = true;
+ case 1:
+ HBITMAP hbm = ((m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr);
+ if (hbm == nullptr && !bAvatarMode) {
+ m_bShowInfoAvatar = false;
+ break;
+ }
+
+ if (!m_hwndPanelPic) {
+ m_hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, m_hwndPanelPicParent, (HMENU)7000, nullptr, nullptr);
+ if (m_hwndPanelPic)
+ SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE);
+ }
+
+ if (bAvatarMode != 0)
+ m_bShowInfoAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
+ break;
+ }
+
+ if (m_bShowInfoAvatar)
+ m_bShowInfoAvatar = hideOverride == 0 ? false : m_bShowInfoAvatar;
+ else
+ m_bShowInfoAvatar = hideOverride == 1 ? true : m_bShowInfoAvatar;
+
+ Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
+ SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
+ }
+ else {
+ m_bShowInfoAvatar = false;
+
+ switch (bAvatarMode) {
+ case 0: // globally on
+ m_bShowAvatar = true;
+ LBL_Check:
+ if (!m_hwndContactPic)
+ m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
+ break;
+ case 2: // globally OFF
+ m_bShowAvatar = false;
+ break;
+ case 3: // on, if present
+ case 1:
+ HBITMAP hbm = (m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr;
+ m_bShowAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
+ goto LBL_Check;
+ }
+
+ if (m_bShowAvatar)
+ m_bShowAvatar = hideOverride == 0 ? 0 : m_bShowAvatar;
+ else
+ m_bShowAvatar = hideOverride == 1 ? 1 : m_bShowAvatar;
+
+ // reloads avatars
+ if (m_hwndPanelPic) { // shows contact or user picture, depending on panel visibility
+ SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
+ Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
+ }
+ else Utils::setAvatarContact(m_hwndContactPic, m_hContact);
+ }
+ return m_bShowAvatar;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::GetClientIcon()
+{
+ if (m_hClientIcon)
+ DestroyIcon(m_hClientIcon);
+
+ m_hClientIcon = nullptr;
+ if (ServiceExists(MS_FP_GETCLIENTICONT)) {
+ ptrW tszMirver(db_get_wsa(m_cache->getActiveContact(), m_cache->getActiveProto(), "MirVer"));
+ if (tszMirver)
+ m_hClientIcon = Finger_GetClientIcon(tszMirver, 1);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON CMsgDialog::GetMyContactIcon(const CMOption<bool> *opt)
+{
+ int bUseMeta = (opt == nullptr) ? false : M.GetByte(opt->GetDBSettingName(), mir_strcmp(opt->GetDBSettingName(), "MetaiconTab") == 0);
+ if (bUseMeta)
+ return Skin_LoadProtoIcon(m_cache->getProto(), m_cache->getStatus());
+ return Skin_LoadProtoIcon(m_cache->getActiveProto(), m_cache->getActiveStatus());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::GetMyNick()
+{
+ ptrW tszNick(Contact::GetInfo(CNF_CUSTOMNICK, 0, m_cache->getActiveProto()));
+ if (tszNick == nullptr)
+ tszNick = Contact::GetInfo(CNF_NICK, 0, m_cache->getActiveProto());
+ if (tszNick != nullptr) {
+ if (mir_wstrlen(tszNick) == 0 || !mir_wstrcmp(tszNick, TranslateT("'(Unknown contact)'")))
+ wcsncpy_s(m_wszMyNickname, (m_myUin[0] ? m_myUin : TranslateT("'(Unknown contact)'")), _TRUNCATE);
+ else
+ wcsncpy_s(m_wszMyNickname, tszNick, _TRUNCATE);
+ }
+ else wcsncpy_s(m_wszMyNickname, L"<undef>", _TRUNCATE); // same here
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve both buddys and my own UIN for a message session and store them in the message
+// window *dat respects metacontacts and uses the current protocol if the contact is a MC
+
+void CMsgDialog::GetMYUIN()
+{
+ ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, 0, m_cache->getActiveProto()));
+ if (uid != nullptr)
+ wcsncpy_s(m_myUin, uid, _TRUNCATE);
+ else
+ m_myUin[0] = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns the status of Send button
+
+LRESULT CMsgDialog::GetSendButtonState()
+{
+ return m_btnOk.SendMsg(BUTTONGETSTATEID, TRUE, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reads send format and configures the toolbar buttons
+// if mode == 0, int only configures the buttons and does not change send format
+
+void CMsgDialog::GetSendFormat()
+{
+ m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
+ if (m_SendFormat == -1) // per contact override to disable it..
+ m_SendFormat = 0;
+ else if (m_SendFormat == 0)
+ m_SendFormat = g_plugin.bSendFormat;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON CMsgDialog::GetXStatusIcon() const
+{
+ uint8_t xStatus = m_cache->getXStatusId();
+ if (xStatus == 0)
+ return nullptr;
+
+ if (!ProtoServiceExists(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON))
+ return nullptr;
+
+ return (HICON)(CallProtoService(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON, xStatus, 0));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// paste contents of the clipboard into the message input area and send it immediately
+
+void CMsgDialog::HandlePasteAndSend()
+{
+ // is feature disabled?
+ if (!g_plugin.bPasteAndSend) {
+ ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending messages' section"));
+ return;
+ }
+
+ m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0);
+ if (GetWindowTextLength(m_message.GetHwnd()) > 0)
+ SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert the avatar bitmap to icon format so that it can be used on the task bar
+// tries to keep correct aspect ratio of the avatar image
+//
+// @param dat: _MessageWindowData* pointer to the window data
+// @return HICON: the icon handle
+
+HICON CMsgDialog::IconFromAvatar() const
+{
+ if (!ServiceExists(MS_AV_GETAVATARBITMAP))
+ return nullptr;
+
+ AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, m_hContact, 0);
+ if (ace == nullptr || ace->hbmPic == nullptr)
+ return nullptr;
+
+ LONG lIconSize = Win7Taskbar->getIconSize();
+ double dNewWidth, dNewHeight;
+ Utils::scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize);
+
+ // resize picture to fit it on the task bar, use an image list for converting it to
+ // 32bpp icon format. hTaskbarIcon will cache it until avatar is changed
+ HBITMAP hbmResized = ::Image_Resize(ace->hbmPic, RESIZEBITMAP_STRETCH, dNewWidth, dNewHeight);
+ HIMAGELIST hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0);
+
+ RECT rc = { 0, 0, lIconSize, lIconSize };
+
+ HDC hdc = ::GetDC(m_pContainer->m_hwnd);
+ HDC dc = ::CreateCompatibleDC(hdc);
+ HDC dcResized = ::CreateCompatibleDC(hdc);
+
+ ReleaseDC(m_pContainer->m_hwnd, hdc);
+
+ HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew));
+ HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized));
+
+ LONG ix = (lIconSize - (LONG)dNewWidth) / 2;
+ LONG iy = (lIconSize - (LONG)dNewHeight) / 2;
+ CSkin::m_default_bf.SourceConstantAlpha = M.GetByte("taskBarIconAlpha", 255);
+ GdiAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
+
+ CSkin::m_default_bf.SourceConstantAlpha = 255;
+ ::SelectObject(dc, hbmOld);
+ ::ImageList_Add(hIml_c, hbmNew, nullptr);
+ ::DeleteObject(hbmNew);
+ ::DeleteDC(dc);
+
+ ::SelectObject(dcResized, hbmOldResized);
+ if (hbmResized != ace->hbmPic)
+ ::DeleteObject(hbmResized);
+ ::DeleteDC(dcResized);
+ HICON hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL);
+ ::ImageList_RemoveAll(hIml_c);
+ ::ImageList_Destroy(hIml_c);
+ return hIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// is window active or not?
+
+bool CMsgDialog::IsActive() const
+{
+ return m_pContainer->IsActive() && m_pContainer->m_hwndActive == m_hwnd;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// read keyboard state and return the state of the modifier keys
+
+void CMsgDialog::KbdState(bool &isShift, bool &isControl, bool &isAlt)
+{
+ GetKeyboardState(kstate);
+ isShift = (kstate[VK_SHIFT] & 0x80) != 0;
+ isControl = (kstate[VK_CONTROL] & 0x80) != 0;
+ isAlt = (kstate[VK_MENU] & 0x80) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LimitMessageText(int iLen)
+{
+ if (this != nullptr)
+ m_message.SendMsg(EM_EXLIMITTEXT, 0, iLen);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadContactAvatar()
+{
+ m_ace = Utils::loadAvatarFromAVS(m_bIsMeta ? db_mc_getSrmmSub(m_hContact) : m_hContact);
+
+ BITMAP bm;
+ if (m_ace && m_ace->hbmPic)
+ GetObject(m_ace->hbmPic, sizeof(bm), &bm);
+ else if (m_ace == nullptr)
+ GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm);
+ else
+ return;
+
+ AdjustBottomAvatarDisplay();
+ CalcDynamicAvatarSize(&bm);
+
+ if (!m_pPanel.isActive() || m_pContainer->cfg.avatarMode == 3) {
+ m_iRealAvatarHeight = 0;
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ }
+ else if (m_pPanel.isActive())
+ GetAvatarVisibility();
+
+ if (m_pWnd != nullptr)
+ m_pWnd->verifyDwmState();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadOwnAvatar()
+{
+ if (ServiceExists(MS_AV_GETMYAVATAR))
+ m_ownAce = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(m_cache->getActiveProto()));
+ else
+ m_ownAce = nullptr;
+
+ if (m_ownAce)
+ m_hOwnPic = m_ownAce->hbmPic;
+ else
+ m_hOwnPic = PluginConfig.g_hbmUnknown;
+
+ if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
+ BITMAP bm;
+
+ m_iRealAvatarHeight = 0;
+ AdjustBottomAvatarDisplay();
+ GetObject(m_hOwnPic, sizeof(bm), &bm);
+ CalcDynamicAvatarSize(&bm);
+ Resize();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSettings()
+{
+ m_clrInputBG = m_pContainer->m_theme.inputbg;
+ LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSplitter()
+{
+ if (m_bIsAutosizingInput) {
+ m_iSplitterY = (m_pContainer->cfg.flags.m_bBottomToolbar) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46);
+
+ if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+ m_iSplitterY += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2);
+ return;
+ }
+
+ if (!m_bSplitterOverride) {
+ if (!m_pContainer->cfg.fPrivate)
+ m_iSplitterY = (int)M.GetDword("splitsplity", 60);
+ else
+ m_iSplitterY = m_pContainer->cfg.iSplitterY;
+ }
+ else m_iSplitterY = (int)M.GetDword(m_hContact, "splitsplity", M.GetDword("splitsplity", 60));
+
+ if (m_iSplitterY < MINSPLITTERY)
+ m_iSplitterY = 150;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LogEvent(DBEVENTINFO &dbei)
+{
+ if (m_iLogMode != WANT_BUILTIN_LOG) {
+ dbei.flags |= DBEF_TEMPORARY;
+
+ MEVENT hDbEvent = db_event_add(m_hContact, &dbei);
+ if (hDbEvent) {
+ m_pLog->LogEvents(hDbEvent, 1, true);
+ db_event_delete(hDbEvent);
+ }
+ }
+ else LOG()->LogEvents(0, 1, true, &dbei);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// draw various elements of the message window, like avatar(s), info panel fields
+// and the color formatting menu
+
+int CMsgDialog::MsgWindowDrawHandler(DRAWITEMSTRUCT *dis)
+{
+ if ((dis->hwndItem == GetDlgItem(m_hwnd, IDC_CONTACTPIC) && m_bShowAvatar) || (dis->hwndItem == m_hwnd && m_pPanel.isActive())) {
+ HBITMAP hbmAvatar = m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown;
+ if (hbmAvatar == nullptr)
+ return TRUE;
+
+ int top, cx, cy;
+ RECT rcClient, rcFrame;
+ bool bPanelPic = (dis->hwndItem == m_hwnd);
+ if (bPanelPic && !m_bShowInfoAvatar)
+ return TRUE;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ if (bPanelPic) {
+ rcClient = dis->rcItem;
+ cx = (rcClient.right - rcClient.left);
+ cy = (rcClient.bottom - rcClient.top) + 1;
+ }
+ else {
+ GetClientRect(dis->hwndItem, &rcClient);
+ cx = rcClient.right;
+ cy = rcClient.bottom;
+ }
+
+ if (cx < 5 || cy < 5)
+ return TRUE;
+
+ HDC hdcDraw = CreateCompatibleDC(dis->hDC);
+ HBITMAP hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy);
+ HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw);
+
+ bool bAero = M.isAero();
+
+ HRGN clipRgn = nullptr;
+ HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcDraw, bAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE));
+ rcFrame = rcClient;
+
+ if (!bPanelPic) {
+ top = (cy - m_pic.cy) / 2;
+ RECT rcEdge = { 0, top, m_pic.cx, top + m_pic.cy };
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, hdcDraw);
+ else if (PluginConfig.m_fillColor) {
+ HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor);
+ FillRect(hdcDraw, &rcFrame, br);
+ DeleteObject(br);
+ }
+ else if (bAero && CSkin::m_pCurrentAeroEffect) {
+ COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh :
+ (CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0);
+
+ HBRUSH br = CreateSolidBrush(clr);
+ FillRect(hdcDraw, &rcFrame, br);
+ DeleteObject(br);
+ }
+ else FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE));
+
+ HPEN hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr);
+ HPEN hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder);
+
+ if (CSkin::m_bAvatarBorderType == 1)
+ Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom);
+ else if (CSkin::m_bAvatarBorderType == 2) {
+ clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, 6, 6);
+ SelectClipRgn(hdcDraw, clipRgn);
+
+ HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
+ FrameRgn(hdcDraw, clipRgn, hbr, 1, 1);
+ DeleteObject(hbr);
+ DeleteObject(clipRgn);
+ }
+
+ SelectObject(hdcDraw, hPenOld);
+ DeleteObject(hPenBorder);
+ }
+
+ if (bPanelPic) {
+ bool bBorder = (CSkin::m_bAvatarBorderType ? true : false);
+
+ int border_off = bBorder ? 1 : 0;
+ int iMaxHeight = m_iPanelAvatarY - (bBorder ? 2 : 0);
+ int iMaxWidth = m_iPanelAvatarX - (bBorder ? 2 : 0);
+
+ rcFrame.left = rcFrame.top = 0;
+ rcFrame.right = (rcClient.right - rcClient.left);
+ rcFrame.bottom = (rcClient.bottom - rcClient.top);
+
+ rcFrame.left = rcFrame.right - (LONG)m_iPanelAvatarX;
+ rcFrame.bottom = (LONG)m_iPanelAvatarY;
+
+ int height_off = (cy - iMaxHeight - (bBorder ? 2 : 0)) / 2;
+ rcFrame.top += height_off;
+ rcFrame.bottom += height_off;
+
+ SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, bAero ? TRUE : FALSE);
+ SetWindowPos(m_hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off,
+ iMaxWidth, iMaxHeight, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING);
+ }
+
+ SelectObject(hdcDraw, hOldBrush);
+ if (!bPanelPic)
+ BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY);
+ SelectObject(hdcDraw, hbmOld);
+ DeleteObject(hbmDraw);
+ DeleteDC(hdcDraw);
+ return TRUE;
+ }
+
+ if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(m_hwnd, IDC_LOGFROZENTEXT)) {
+ wchar_t szWindowText[256];
+ if (CSkin::m_skinEnabled) {
+ SetTextColor(dis->hDC, CSkin::m_DefaultFontColor);
+ CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
+ }
+ else {
+ SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
+ CSkin::FillBack(dis->hDC, &dis->rcItem);
+ }
+ GetWindowText(dis->hwndItem, szWindowText, _countof(szWindowText));
+ szWindowText[255] = 0;
+ SetBkMode(dis->hDC, TRANSPARENT);
+ DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS);
+ return TRUE;
+ }
+
+ if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICERRORICON)) {
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
+ else
+ CSkin::FillBack(dis->hDC, &dis->rcItem);
+ DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8,
+ PluginConfig.g_iconErr, 16, 16, 0, nullptr, DI_NORMAL);
+ return TRUE;
+ }
+
+ if (dis->CtlType == ODT_MENU && m_pPanel.isHovered()) {
+ DrawMenuItem(dis, (HICON)dis->itemData, 0);
+ return TRUE;
+ }
+
+ return Menu_DrawItem((LPARAM)dis);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMsgDialog::MsgWindowUpdateMenu(HMENU submenu, int menuID)
+{
+ bool bInfoPanel = m_pPanel.isActive();
+
+ if (menuID == MENU_TABCONTEXT) {
+ EnableMenuItem(submenu, ID_TABMENU_LEAVECHATROOM, (isChat() && ProtoServiceExists(m_szProto, PS_LEAVECHAT)) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) ? MF_GRAYED : MF_ENABLED);
+ EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M.GetDword(m_hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED);
+ }
+ else if (menuID == MENU_PICMENU) {
+ wchar_t *szText = nullptr;
+ char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+ HMENU visMenu = GetSubMenu(submenu, 0);
+ BOOL picValid = bInfoPanel ? (m_hOwnPic != nullptr) : (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown);
+
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STRING;
+
+ EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, picValid ? MF_ENABLED : MF_GRAYED);
+
+ CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
+
+ CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, PluginConfig.m_bAlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED);
+ if (!bInfoPanel) {
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
+ szText = TranslateT("Contact picture settings...");
+ EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED);
+ }
+ else {
+ EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED);
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED);
+ szText = TranslateT("Set your avatar...");
+ }
+ mii.dwTypeData = szText;
+ mii.cch = (int)mir_wstrlen(szText) + 1;
+ SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii);
+ }
+ else if (menuID == MENU_PANELPICMENU) {
+ HMENU visMenu = GetSubMenu(submenu, 0);
+ char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+
+ CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
+
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update state of the container - this is called whenever a tab becomes active, no matter how and
+// deals with various things like updating the title bar, removing flashing icons, updating the
+// session list, switching the keyboard layout (autolocale active) and the general container status.
+//
+// it protects itself from being called more than once per session activation and is valid for
+// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c)
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMsgDialog::MsgWindowMenuHandler(int selection, int menuId)
+{
+ if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) {
+ switch (selection) {
+ case ID_TABMENU_ATTACHTOCONTAINER:
+ SelectContainer();
+ return 1;
+ case ID_TABMENU_CONTAINEROPTIONS:
+ m_pContainer->OptionsDialog();
+ return 1;
+ case ID_TABMENU_CLOSECONTAINER:
+ SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 0);
+ return 1;
+ case ID_TABMENU_CLOSETAB:
+ PostMessage(m_hwnd, WM_CLOSE, 1, 0);
+ return 1;
+ case ID_TABMENU_SAVETABPOSITION:
+ db_set_dw(m_hContact, SRMSGMOD_T, "tabindex", m_iTabID * 100);
+ break;
+ case ID_TABMENU_CLEARSAVEDTABPOSITION:
+ db_unset(m_hContact, SRMSGMOD_T, "tabindex");
+ break;
+ case ID_TABMENU_LEAVECHATROOM:
+ if (isChat()) {
+ char *szProto = Proto_GetBaseAccountName(m_hContact);
+ if (szProto)
+ CallProtoService(szProto, PS_LEAVECHAT, m_hContact, 0);
+ }
+ return 1;
+
+ case ID_VISIBILITY_DEFAULT:
+ case ID_VISIBILITY_HIDDENFORTHISCONTACT:
+ case ID_VISIBILITY_VISIBLEFORTHISCONTACT:
+ {
+ uint8_t avOverrideMode;
+ if (selection == ID_VISIBILITY_DEFAULT)
+ avOverrideMode = -1;
+ else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT)
+ avOverrideMode = 1;
+ else
+ avOverrideMode = 0;
+ db_set_b(m_hContact, SRMSGMOD_T, "hideavatar", avOverrideMode);
+ }
+
+ ShowPicture(false);
+ Resize();
+ DM_ScrollToBottom(0, 1);
+ return 1;
+
+ case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH:
+ PluginConfig.m_bAlwaysFullToolbarWidth = !PluginConfig.m_bAlwaysFullToolbarWidth;
+ db_set_b(0, SRMSGMOD_T, "alwaysfulltoolbar", (uint8_t)PluginConfig.m_bAlwaysFullToolbarWidth);
+ Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
+ break;
+
+ case ID_PICMENU_SAVETHISPICTUREAS:
+ if (m_pPanel.isActive())
+ SaveAvatarToFile(m_hOwnPic, 1);
+ else if (m_ace)
+ SaveAvatarToFile(m_ace->hbmPic, 0);
+ break;
+
+ case ID_PANELPICMENU_SAVETHISPICTUREAS:
+ if (m_ace)
+ SaveAvatarToFile(m_ace->hbmPic, 0);
+ break;
+
+ case ID_PICMENU_SETTINGS:
+ if (menuId == MENU_PICMENU) {
+ if (m_pPanel.isActive()) {
+ if (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0))
+ CallService(MS_AV_SETMYAVATARW, (WPARAM)(m_cache->getActiveProto()), 0);
+ return TRUE;
+ }
+ }
+ CallService(MS_AV_CONTACTOPTIONS, m_hContact, (LPARAM)m_hwnd);
+ return 1;
+ }
+ }
+ else if (menuId == MENU_LOGMENU) {
+ switch (selection) {
+ case ID_MESSAGELOGSETTINGS_GLOBAL:
+ g_plugin.openOptions(nullptr, L"Message sessions", L"Message log");
+ return 1;
+
+ case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
+ CallService(MS_TABMSG_SETUSERPREFS, m_hContact, 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::NotifyDeliveryFailure() const
+{
+ if (!g_plugin.bErrorPopup || !Popup_Enabled())
+ return;
+
+ POPUPDATAW ppd = {};
+ ppd.lchContact = m_hContact;
+ ppd.PluginWindowProc = Utils::PopupDlgProcError;
+ ppd.lchIcon = PluginConfig.g_iconErr;
+ ppd.iSeconds = NEN::iDelayErr;
+ if (!NEN::bColDefaultErr) {
+ ppd.colorText = NEN::colTextErr;
+ ppd.colorBack = NEN::colBackErr;
+ }
+ wcsncpy_s(ppd.lpwzContactName, m_cache->getNick(), _TRUNCATE);
+ wcsncpy_s(ppd.lpwzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE);
+ PUAddPopupW(&ppd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::PlayIncomingSound() const
+{
+ int iPlay = m_pContainer->MustPlaySound(this);
+ if (iPlay) {
+ if (GetForegroundWindow() == m_pContainer->m_hwnd && m_pContainer->m_hwndActive == m_hwnd)
+ Skin_PlaySound("RecvMsgActive");
+ else
+ Skin_PlaySound("RecvMsgInactive");
+ }
+}
+
+void CMsgDialog::RemakeLog()
+{
+ m_szMicroLf[0] = 0;
+ m_lastEventTime = 0;
+ m_iLastEventType = -1;
+ StreamEvents(m_hDbEventFirst, -1, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// saves a contact picture to disk
+// takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar)
+// requires AVS service (Miranda 0.7+)
+
+void CMsgDialog::SaveAvatarToFile(HBITMAP hbm, int isOwnPic)
+{
+ wchar_t szFinalFilename[MAX_PATH];
+ time_t t = time(0);
+ struct tm *lt = localtime(&t);
+ uint32_t setView = 1;
+
+ wchar_t szTimestamp[100];
+ mir_snwprintf(szTimestamp, L"%04u %02u %02u_%02u%02u", lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min);
+
+ wchar_t *szProto = mir_a2u(m_cache->getActiveProto());
+
+ wchar_t szFinalPath[MAX_PATH];
+ mir_snwprintf(szFinalPath, L"%s\\%s", M.getSavedAvatarPath(), szProto);
+ mir_free(szProto);
+
+ if (CreateDirectory(szFinalPath, nullptr) == 0) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS) {
+ MessageBox(nullptr, TranslateT("Error creating destination directory"),
+ TranslateT("Save contact picture"), MB_OK | MB_ICONSTOP);
+ return;
+ }
+ }
+
+ wchar_t szBaseName[MAX_PATH];
+ if (isOwnPic)
+ mir_snwprintf(szBaseName, L"My Avatar_%s", szTimestamp);
+ else
+ mir_snwprintf(szBaseName, L"%s_%s", m_cache->getNick(), szTimestamp);
+
+ mir_snwprintf(szFinalFilename, L"%s.png", szBaseName);
+
+ // do not allow / or \ or % in the filename
+ Utils::sanitizeFilename(szFinalFilename);
+
+ wchar_t filter[MAX_PATH];
+ mir_snwprintf(filter, L"%s%c*.bmp;*.png;*.jpg;*.gif%c%c", TranslateT("Image files"), 0, 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lpstrDefExt = L"png";
+ ofn.lpstrFilter = filter;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
+ ofn.lpfnHook = OpenFileSubclass;
+ ofn.lStructSize = sizeof(ofn);
+ ofn.lpstrFile = szFinalFilename;
+ ofn.lpstrInitialDir = szFinalPath;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lCustData = (LPARAM)& setView;
+ if (GetSaveFileName(&ofn)) {
+ if (PathFileExists(szFinalFilename))
+ if (MessageBox(nullptr, TranslateT("The file exists. Do you want to overwrite it?"), TranslateT("Save contact picture"), MB_YESNO | MB_ICONQUESTION) == IDNO)
+ return;
+
+ IMGSRVC_INFO ii;
+ ii.cbSize = sizeof(ii);
+ ii.pwszName = szFinalFilename;
+ ii.hbm = hbm;
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_UNKNOWN; // get the format from the filename extension. png is default.
+ Image_Save(&ii);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::SaveSplitter()
+{
+ if (m_bIsAutosizingInput)
+ return;
+
+ if (m_iSplitterY < DPISCALEY_S(MINSPLITTERY) || m_iSplitterY < 0)
+ m_iSplitterY = DPISCALEY_S(MINSPLITTERY);
+
+ if (m_bSplitterOverride)
+ db_set_dw(m_hContact, SRMSGMOD_T, "splitsplity", m_iSplitterY);
+ else {
+ if (m_pContainer->cfg.fPrivate)
+ m_pContainer->cfg.iSplitterY = m_iSplitterY;
+ else
+ db_set_dw(0, SRMSGMOD_T, "splitsplity", m_iSplitterY);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send a pasted bitmap by file transfer.
+
+static LIST<wchar_t> vTempFilenames(5);
+
+void CMsgDialog::SendHBitmapAsFile(HBITMAP hbmp) const
+{
+ const wchar_t *mirandatempdir = L"Miranda";
+ const wchar_t *filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg";
+ wchar_t filename[MAX_PATH];
+ size_t tempdirlen = GetTempPath(MAX_PATH, filename);
+ bool fSend = true;
+
+ const char *szProto = m_cache->getActiveProto();
+ int wMyStatus = Proto_GetStatus(szProto);
+
+ uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+
+ // check protocol capabilities, status modes and visibility lists (privacy)
+ // to determine whether the file can be sent. Throw a warning if any of
+ // these checks fails.
+ if (!(protoCaps & PF1_FILESEND))
+ fSend = false;
+
+ if ((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == m_cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES)))
+ fSend = false;
+
+ if (protoCaps & PF1_VISLIST && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+ fSend = false;
+
+ if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+ fSend = false;
+
+ if (!fSend) {
+ CWarning::show(CWarning::WARN_SENDFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
+ return;
+ }
+
+ if (tempdirlen <= 0 || tempdirlen >= MAX_PATH - mir_wstrlen(mirandatempdir) - mir_wstrlen(filenametemplate) - 2) // -2 is because %Y takes 4 symbols
+ filename[0] = 0; // prompt for a new name
+ else {
+ mir_wstrcpy(filename + tempdirlen, mirandatempdir);
+ if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, nullptr) == 0)
+ filename[0] = 0;
+ else {
+ tempdirlen = mir_wstrlen(filename);
+
+ time_t rawtime;
+ time(&rawtime);
+ const tm *timeinfo;
+ timeinfo = _localtime32((__time32_t *)& rawtime);
+ wcsftime(filename + tempdirlen, MAX_PATH - tempdirlen, filenametemplate, timeinfo);
+ size_t firstnumberpos = tempdirlen + 14;
+ size_t lastnumberpos = tempdirlen + 20;
+ while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) { // while it exists
+ for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--)
+ if (filename[pos]++ != '9')
+ break;
+ else
+ if (pos == firstnumberpos)
+ filename[0] = 0; // all filenames exist => prompt for a new name
+ else
+ filename[pos] = '0';
+ }
+ }
+ }
+
+ if (filename[0] == 0) { // prompting to save
+ wchar_t filter[MAX_PATH];
+ mir_snwprintf(filter, L"%s%c*.jpg%c%c", TranslateT("JPEG-compressed images"), 0, 0, 0);
+
+ OPENFILENAME dlg;
+ dlg.lStructSize = sizeof(dlg);
+ dlg.lpstrFilter = filter;
+ dlg.nFilterIndex = 1;
+ dlg.lpstrFile = filename;
+ dlg.nMaxFile = MAX_PATH;
+ dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+ dlg.lpstrDefExt = L"jpg";
+ if (!GetSaveFileName(&dlg))
+ return;
+ }
+
+ IMGSRVC_INFO ii;
+ ii.cbSize = sizeof(ii);
+ ii.hbm = hbmp;
+ ii.pwszName = filename;
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_JPEG;
+ if (!Image_Save(&ii)) {
+ CWarning::show(CWarning::WARN_SAVEFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
+ return;
+ }
+
+ vTempFilenames.insert(mir_wstrdup(filename));
+
+ wchar_t *ppFiles[2] = { filename, nullptr };
+ CallService(MS_FILE_SENDSPECIFICFILEST, m_cache->getActiveContact(), (LPARAM)&ppFiles);
+}
+
+// remove all temporary files created by the "send clipboard as file" feature.
+void TSAPI CleanTempFiles()
+{
+ for (auto &it : vTempFilenames) {
+ DeleteFileW(it);
+ mir_free(it);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Sets a status bar text for a contact
+
+void CMsgDialog::SetStatusText(const wchar_t *wszText, HICON hIcon)
+{
+ if (wszText != nullptr) {
+ m_bStatusSet = true;
+ m_szStatusText = wszText;
+ m_szStatusIcon = hIcon;
+ }
+ else {
+ m_bStatusSet = false;
+ m_szStatusText.Empty();
+ m_szStatusIcon = nullptr;
+ }
+
+ tabUpdateStatusBar();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// subclassing for the message filter dialog (set and configure event filters for the
+// current session
+
+static UINT _eventorder[] =
+{
+ GC_EVENT_ACTION,
+ GC_EVENT_MESSAGE,
+ GC_EVENT_NICK,
+ GC_EVENT_JOIN,
+ GC_EVENT_PART,
+ GC_EVENT_TOPIC,
+ GC_EVENT_ADDSTATUS,
+ GC_EVENT_INFORMATION,
+ GC_EVENT_QUIT,
+ GC_EVENT_KICK,
+ GC_EVENT_NOTICE
+};
+
+INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CMsgDialog *pDlg = (CMsgDialog *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ pDlg = (CMsgDialog *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ {
+ uint32_t dwMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", 0);
+ uint32_t dwFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", 0);
+
+ uint32_t dwPopupMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", 0);
+ uint32_t dwPopupFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", 0);
+
+ uint32_t dwTrayMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", 0);
+ uint32_t dwTrayFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", 0);
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+ CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+ CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+ }
+ }
+ return FALSE;
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, RGB(60, 60, 150));
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+
+ case WM_CLOSE:
+ if (wParam == 1 && lParam == 1 && pDlg) {
+ int iFlags = 0;
+ uint32_t dwMask = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i);
+ dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+ iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+ }
+
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ if (dwMask == 0) {
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterFlags");
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterMask");
+ }
+ else {
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", iFlags);
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", dwMask);
+ }
+
+ dwMask = iFlags = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i);
+ dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+ iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+ }
+
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ if (dwMask == 0) {
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupFlags");
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupMask");
+ }
+ else {
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", iFlags);
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", dwMask);
+ }
+
+ dwMask = iFlags = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i);
+ dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+ iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+ }
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ if (dwMask == 0) {
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags");
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask");
+ }
+ else {
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", iFlags);
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", dwMask);
+ }
+ Chat_SetFilters(pDlg->getChat());
+
+ if (pDlg->m_bFilterEnabled) {
+ if (pDlg->m_iLogFilterFlags == 0)
+ pDlg->m_btnFilter.Click();
+ pDlg->RedrawLog();
+ db_set_b(pDlg->m_hContact, CHAT_MODULE, "FilterEnabled", pDlg->m_bFilterEnabled);
+ }
+ }
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+void CMsgDialog::ShowFilterMenu()
+{
+ m_hwndFilter = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_pContainer->m_hwnd, FilterWndProc, (LPARAM)this);
+ TranslateDialogDefault(m_hwndFilter);
+
+ RECT rcFilter, rcLog;
+ GetClientRect(m_hwndFilter, &rcFilter);
+ GetWindowRect(m_pLog->GetHwnd(), &rcLog);
+
+ POINT pt;
+ pt.x = rcLog.right; pt.y = rcLog.bottom;
+ ScreenToClient(m_pContainer->m_hwnd, &pt);
+
+ SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::ShowPicture(bool showNewPic)
+{
+ if (!m_pPanel.isActive())
+ m_pic.cy = m_pic.cx = DPISCALEY_S(60);
+
+ if (showNewPic) {
+ if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
+ if (!m_hwndPanelPic) {
+ InvalidateRect(m_hwnd, nullptr, TRUE);
+ UpdateWindow(m_hwnd);
+ Resize();
+ }
+ return;
+ }
+ AdjustBottomAvatarDisplay();
+ }
+ else {
+ m_bShowAvatar = !m_bShowAvatar;
+ db_set_b(m_hContact, SRMSGMOD_T, "MOD_ShowPic", m_bShowAvatar);
+ }
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), &rc);
+ if (m_minEditBoxSize.cy + DPISCALEY_S(3) > m_iSplitterY)
+ SplitterMoved(rc.bottom - m_minEditBoxSize.cy, GetDlgItem(m_hwnd, IDC_SPLITTERY));
+ if (!showNewPic)
+ SetDialogToType();
+ else
+ Resize();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show a modified context menu for the richedit control(s)
+
+void CMsgDialog::ShowPopupMenu(const CCtrlBase &pCtrl, POINT pt)
+{
+ CHARRANGE sel, all = { 0, -1 };
+
+ HMENU hSubMenu, hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
+ if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
+ hSubMenu = GetSubMenu(hMenu, 0);
+ else {
+ hSubMenu = GetSubMenu(hMenu, 2);
+ EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, m_SendFormat != 0 ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, g_plugin.bPasteAndSend ? MF_ENABLED : MF_GRAYED);
+ CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED);
+ EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, m_pContainer->m_hwndStatus ? MF_ENABLED : MF_GRAYED);
+ }
+ TranslateMenu(hSubMenu);
+ pCtrl.SendMsg(EM_EXGETSEL, 0, (LPARAM)& sel);
+ if (sel.cpMin == sel.cpMax) {
+ EnableMenuItem(hSubMenu, IDM_COPY, MF_GRAYED);
+ EnableMenuItem(hSubMenu, IDM_QUOTE, MF_GRAYED);
+ if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
+ EnableMenuItem(hSubMenu, IDM_CUT, MF_GRAYED);
+ }
+
+ if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) {
+ InsertMenuA(hSubMenu, 6, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+ CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, m_bScrollingDisabled ? MF_CHECKED : MF_UNCHECKED);
+ }
+
+ MessageWindowPopupData mwpd;
+ // First notification
+ mwpd.uType = MSG_WINDOWPOPUP_SHOWING;
+ mwpd.uFlags = (pCtrl.GetCtrlId() == IDC_SRMM_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT);
+ mwpd.hContact = m_hContact;
+ mwpd.hwnd = pCtrl.GetHwnd();
+ mwpd.hMenu = hSubMenu;
+ mwpd.selection = 0;
+ mwpd.pt = pt;
+ NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
+
+ int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
+
+ // Second notification
+ mwpd.selection = iSelection;
+ mwpd.uType = MSG_WINDOWPOPUP_SELECTED;
+ NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
+
+ switch (iSelection) {
+ case IDM_COPY:
+ pCtrl.SendMsg(WM_COPY, 0, 0);
+ break;
+ case IDM_CUT:
+ pCtrl.SendMsg(WM_CUT, 0, 0);
+ break;
+ case IDM_PASTE:
+ case IDM_PASTEFORMATTED:
+ if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
+ pCtrl.SendMsg(EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0);
+ break;
+ case IDM_COPYALL:
+ pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
+ pCtrl.SendMsg(WM_COPY, 0, 0);
+ pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& sel);
+ break;
+ case IDM_QUOTE:
+ SendMessage(m_hwnd, WM_COMMAND, IDC_QUOTE, 0);
+ break;
+ case IDM_SELECTALL:
+ pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
+ break;
+ case IDM_CLEAR:
+ tabClearLog();
+ break;
+ case ID_LOG_FREEZELOG:
+ SendDlgItemMessage(m_hwnd, IDC_SRMM_LOG, WM_KEYDOWN, VK_F12, 0);
+ break;
+ case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR:
+ PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator;
+ db_set_b(0, SRMSGMOD_T, "msgsizebar", (uint8_t)PluginConfig.m_visualMessageSizeIndicator);
+ Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 0);
+ Resize();
+ if (m_pContainer->m_hwndStatus)
+ RedrawWindow(m_pContainer->m_hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ break;
+ case ID_EDITOR_PASTEANDSENDIMMEDIATELY:
+ HandlePasteAndSend();
+ break;
+ }
+
+ if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
+ RemoveMenu(hSubMenu, 7, MF_BYPOSITION);
+ DestroyMenu(hMenu);
+}
+
+void CMsgDialog::SplitterMoved(int coord, HWND hwnd)
+{
+ POINT pt;
+ RECT rc;
+
+ switch (GetDlgCtrlID(hwnd)) {
+ case IDC_MULTISPLITTER:
+ GetClientRect(m_hwnd, &rc);
+ pt.x = coord;
+ pt.y = 0;
+ ScreenToClient(m_hwnd, &pt);
+ {
+ int oldSplitterX = m_iMultiSplit;
+ m_iMultiSplit = rc.right - pt.x;
+ if (m_iMultiSplit < 25)
+ m_iMultiSplit = 25;
+
+ if (m_iMultiSplit > ((rc.right - rc.left) - 80))
+ m_iMultiSplit = oldSplitterX;
+ }
+ Resize();
+ break;
+
+ case IDC_SPLITTERX:
+ GetClientRect(m_hwnd, &rc);
+ pt.x = coord, pt.y = 0;
+ ScreenToClient(m_hwnd, &pt);
+ {
+ int iSplitterX = rc.right - pt.x + 1;
+ if (iSplitterX < 35)
+ iSplitterX = 35;
+ if (iSplitterX > rc.right - rc.left - 35)
+ iSplitterX = rc.right - rc.left - 35;
+ m_pContainer->cfg.iSplitterX = iSplitterX;
+ }
+ Resize();
+ break;
+
+ case IDC_SPLITTERY:
+ GetClientRect(m_hwnd, &rc);
+ rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30);
+ pt.x = 0;
+ pt.y = coord;
+ ScreenToClient(m_hwnd, &pt);
+ {
+ int oldSplitterY = m_iSplitterY;
+ int oldDynaSplitter = m_dynaSplitter;
+
+ m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(23);
+
+ // attempt to fix splitter troubles..
+ // hardcoded limits... better solution is possible, but this works for now
+ int bottomtoolbarH = 0;
+ if (m_pContainer->cfg.flags.m_bBottomToolbar)
+ bottomtoolbarH = 22;
+
+ if (m_iSplitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) { // min splitter size
+ m_iSplitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH);
+ m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
+ DM_RecalcPictureSize();
+ }
+ else if (m_iSplitterY > (rc.bottom - rc.top)) {
+ m_iSplitterY = oldSplitterY;
+ m_dynaSplitter = oldDynaSplitter;
+ DM_RecalcPictureSize();
+ }
+ else {
+ m_dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11);
+ DM_RecalcPictureSize();
+ }
+ }
+ UpdateToolbarBG();
+ Resize();
+ break;
+
+ case IDC_PANELSPLITTER:
+ GetClientRect(m_pLog->GetHwnd(), &rc);
+
+ POINT pnt = { 0, coord };
+ ScreenToClient(m_hwnd, &pnt);
+ if ((pnt.y + 2 >= MIN_PANELHEIGHT + 2) && (pnt.y + 2 < 100) && (pnt.y + 2 < rc.bottom - 30))
+ m_pPanel.setHeight(pnt.y + 2, true);
+
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
+ if (M.isAero())
+ InvalidateRect(GetParent(m_hwnd), nullptr, FALSE);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::StreamEvents(MEVENT hDbEventFirst, int count, bool bAppend)
+{
+ m_pLog->LogEvents(hDbEventFirst, count, bAppend);
+
+ DM_ScrollToBottom(0, 0);
+ if (bAppend && hDbEventFirst)
+ m_hDbEventLast = hDbEventFirst;
+ else
+ m_hDbEventLast = db_event_last(m_hContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sent by the select container dialog box when a container was selected...
+
+void CMsgDialog::SwitchToContainer(const wchar_t *szNewName)
+{
+ if (!mir_wstrcmp(szNewName, TranslateT("Default container")))
+ szNewName = CGlobals::m_default_container_name;
+
+ int iOldItems = TabCtrl_GetItemCount(m_hwndParent);
+ if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN))
+ return;
+
+ TContainerData *pNewContainer = FindContainerByName(szNewName);
+ if (pNewContainer == nullptr)
+ if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr)
+ return;
+
+ db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName);
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact);
+ if (iOldItems > 1) // there were more than 1 tab, container is still valid
+ SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0);
+ SetForegroundWindow(pNewContainer->m_hwnd);
+ SetActiveWindow(pNewContainer->m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CMsgDialog::TabAutoComplete()
+{
+ LRESULT lResult = m_message.SendMsg(EM_GETSEL, 0, 0);
+ int start = LOWORD(lResult), end = HIWORD(lResult);
+ int origStart = start, origEnd = end;
+ m_message.SendMsg(EM_SETSEL, end, end);
+
+ GETTEXTEX gt = { 0 };
+ gt.codepage = 1200;
+ gt.flags = GTL_DEFAULT | GTL_PRECISE;
+ int iLen = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)& gt, 0);
+ if (iLen <= 0)
+ return false;
+
+ bool isTopic = false, isRoom = false;
+ wchar_t *pszText = (wchar_t *)mir_calloc((iLen + 10) * sizeof(wchar_t));
+
+ gt.flags = GT_DEFAULT;
+ gt.cb = (iLen + 9) * sizeof(wchar_t);
+ m_message.SendMsg(EM_GETTEXTEX, (WPARAM)& gt, (LPARAM)pszText);
+
+ if (m_wszSearchResult != nullptr) {
+ int cbResult = (int)mir_wstrlen(m_wszSearchResult);
+ if (start >= cbResult && !wcsnicmp(m_wszSearchResult, pszText + start - cbResult, cbResult)) {
+ start -= cbResult;
+ goto LBL_SkipEnd;
+ }
+ }
+
+ while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB)
+ start--;
+
+LBL_SkipEnd:
+ while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB)
+ end++;
+
+ if (pszText[start] == '#')
+ isRoom = true;
+ else {
+ int topicStart = start;
+ while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB))
+ topicStart--;
+ if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6])
+ isTopic = true;
+ }
+
+ if (m_wszSearchQuery == nullptr) {
+ m_wszSearchQuery = mir_wstrndup(pszText + start, end - start);
+ m_wszSearchResult = mir_wstrdup(m_wszSearchQuery);
+ m_pLastSession = nullptr;
+ }
+
+ const wchar_t *pszName = nullptr;
+ if (isTopic)
+ pszName = m_si->ptszTopic;
+ else if (isRoom) {
+ m_pLastSession = SM_FindSessionAutoComplete(m_si->pszModule, m_si, m_pLastSession, m_wszSearchQuery, m_wszSearchResult);
+ if (m_pLastSession != nullptr)
+ pszName = m_pLastSession->ptszName;
+ }
+ else pszName = g_chatApi.UM_FindUserAutoComplete(m_si, m_wszSearchQuery, m_wszSearchResult);
+
+ replaceStrW(m_wszSearchResult, nullptr);
+
+ if (pszName != nullptr) {
+ if (end != start) {
+ CMStringW szReplace;
+ if (!isRoom && !isTopic && start == 0) {
+ szReplace = pszName;
+ if (mir_wstrlen(g_Settings.pwszAutoText))
+ szReplace.Append(g_Settings.pwszAutoText);
+ szReplace.AppendChar(' ');
+ m_wszSearchResult = szReplace.Detach();
+ pszName = m_wszSearchResult;
+ }
+ else m_wszSearchResult = mir_wstrdup(pszName);
+
+ m_message.SendMsg(EM_SETSEL, start, end);
+ m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pszName);
+ }
+ else m_wszSearchResult = mir_wstrdup(pszName);
+
+ return true;
+ }
+
+ if (end != start) {
+ m_message.SendMsg(EM_SETSEL, start, end);
+ m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)m_wszSearchQuery);
+ }
+ m_message.SendMsg(EM_SETSEL, origStart, origEnd);
+ replaceStrW(m_wszSearchQuery, nullptr);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::tabClearLog()
+{
+ if (isChat()) {
+ g_chatApi.LM_RemoveAll(&m_si->pLog, &m_si->pLogEnd);
+ m_si->iEventCount = 0;
+ m_si->LastTime = 0;
+ PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ }
+
+ m_pLog->Clear();
+ m_hDbEventFirst = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CThumbBase *CMsgDialog::tabCreateThumb(CProxyWindow *pProxy) const
+{
+ if (isChat())
+ return new CThumbMUC(pProxy, m_si);
+
+ return new CThumbIM(pProxy);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update all status bar fields and force a redraw of the status bar.
+
+void CMsgDialog::tabUpdateStatusBar() const
+{
+ if (m_pContainer->m_hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
+ if (!isChat()) {
+ if (m_wszStatusBar[0]) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+ }
+ else if (m_bStatusSet) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+ }
+ else {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+ DM_UpdateLastMessage();
+ }
+ }
+ else {
+ if (m_bStatusSet) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+ }
+ else SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+ }
+ UpdateReadChars();
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ SendMessage(m_pContainer->m_hwndStatus, WM_USER + 101, 0, (LPARAM)this);
+ }
+}
+
+int CMsgDialog::Typing(int secs)
+{
+ if (!AllowTyping())
+ return 0;
+
+ int preTyping = m_nTypeSecs != 0;
+
+ setTyping(m_nTypeSecs = (secs > 0) ? secs : 0);
+ if (m_nTypeSecs)
+ m_bShowTyping = 0;
+
+ return preTyping;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateNickList()
+{
+ int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0);
+ m_nickList.SendMsg(LB_SETTOPINDEX, i, 0);
+ UpdateTitle();
+ m_hTabIcon = m_hTabStatusIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateOptions()
+{
+ GetSendFormat();
+
+ DM_InitRichEdit();
+ m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0);
+
+ m_nickList.SetItemHeight(0, g_Settings.iNickListFontHeight);
+ InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
+
+ m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(m_bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0);
+
+ CSuper::UpdateOptions();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update the status bar field which displays the number of characters in the input area
+// and various indicators (caps lock, num lock, insert mode).
+
+void CMsgDialog::UpdateReadChars() const
+{
+ if (!m_pContainer->m_hwndStatus || m_pContainer->m_hwndActive != m_hwnd)
+ return;
+
+ int len;
+ if (isChat())
+ len = GetWindowTextLength(m_message.GetHwnd());
+ else {
+ // retrieve text length in UTF8 bytes, because this is the relevant length for most protocols
+ GETTEXTLENGTHEX gtxl = { 0 };
+ gtxl.codepage = CP_UTF8;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+ len = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0);
+ }
+
+ BOOL fCaps = (GetKeyState(VK_CAPITAL) & 1);
+ BOOL fNum = (GetKeyState(VK_NUMLOCK) & 1);
+
+ wchar_t szBuf[20]; szBuf[0] = 0;
+ if (m_bInsertMode)
+ mir_wstrcat(szBuf, L"O");
+ if (fCaps)
+ mir_wstrcat(szBuf, L"C");
+ if (fNum)
+ mir_wstrcat(szBuf, L"N");
+ if (m_bInsertMode || fCaps || fNum)
+ mir_wstrcat(szBuf, L" | ");
+
+ wchar_t buf[128];
+ mir_snwprintf(buf, L"%s%s %d/%d", szBuf, m_lcID, m_iOpenJobs, len);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)buf);
+ if (PluginConfig.m_visualMessageSizeIndicator)
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, FALSE);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateSaveAndSendButton()
+{
+ GETTEXTLENGTHEX gtxl = { 0 };
+ gtxl.codepage = CP_UTF8;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+ int len = SendDlgItemMessage(m_hwnd, IDC_SRMM_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0);
+ if (len && GetSendButtonState() == PBS_DISABLED)
+ EnableSendButton(true);
+ else if (len == 0 && GetSendButtonState() != PBS_DISABLED)
+ EnableSendButton(false);
+
+ if (len) { // looks complex but avoids flickering on the button while typing.
+ if (!m_bSaveBtn) {
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]);
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save and close session"), BATF_UNICODE);
+ m_bSaveBtn = true;
+ }
+ }
+ else {
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]);
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Close session"), BATF_UNICODE);
+ m_bSaveBtn = false;
+ }
+ m_textLen = len;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateStatusBar()
+{
+ if (m_pContainer->m_hwndActive != m_hwnd || m_pContainer->m_hwndStatus == nullptr || CMimAPI::m_shutDown || m_wszStatusBar[0])
+ return;
+
+ if (m_si->pszModule == nullptr)
+ return;
+
+ //Mad: strange rare crash here...
+ MODULEINFO *mi = m_si->pMI;
+ if (!mi->ptszModDispName)
+ return;
+
+ int x = 12;
+ x += Chat_GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->m_hwndStatus, WM_GETFONT, 0, 0), true);
+ x += GetSystemMetrics(SM_CXSMICON);
+
+ wchar_t szFinalStatusBarText[512];
+ if (m_pPanel.isActive()) {
+ time_t now = time(0);
+ uint32_t diff = (now - mi->idleTimeStamp) / 60;
+ if (diff >= 1) {
+ if (diff > 59) {
+ uint32_t hours = diff / 60;
+ uint32_t minutes = diff % 60;
+ mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"),
+ hours, hours > 1 ? TranslateT("hours") : TranslateT("hour"),
+ minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute"));
+ }
+ else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute"));
+ }
+ else mi->tszIdleMsg[0] = 0;
+
+ mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg);
+ }
+ else {
+ if (m_si->ptszStatusbarText)
+ mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, m_si->ptszStatusbarText);
+ else
+ wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE);
+ }
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText);
+ tabUpdateStatusBar();
+ m_pPanel.Invalidate();
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+}
+
+void CMsgDialog::UpdateTitle()
+{
+ if (isChat()) {
+ m_wStatus = m_si->wStatus;
+
+ const wchar_t *szNick = m_cache->getNick();
+ if (mir_wstrlen(szNick) > 0) {
+ if (M.GetByte("cuttitle", 0))
+ CutContactName(szNick, m_wszTitle, _countof(m_wszTitle));
+ else
+ wcsncpy_s(m_wszTitle, szNick, _TRUNCATE);
+ }
+
+ CMStringW wszTitle;
+ HICON hIcon = nullptr;
+ int nUsers = m_si->getUserList().getCount();
+
+ switch (m_si->iType) {
+ case GCW_CHATROOM:
+ hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
+ wszTitle.Format((nUsers == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"),
+ szNick, nUsers, m_bFilterEnabled ? TranslateT(", event filter active") : L"");
+ break;
+
+ case GCW_PRIVMESS:
+ hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
+ if (nUsers == 1)
+ wszTitle.Format(TranslateT("%s: message session"), szNick);
+ else
+ wszTitle.Format(TranslateT("%s: message session (%u users)"), szNick, nUsers);
+ break;
+
+ case GCW_SERVER:
+ wszTitle.Format(L"%s: Server", szNick);
+ hIcon = LoadIconEx("window");
+ break;
+
+ default:
+ return;
+ }
+
+ if (m_pWnd) {
+ m_pWnd->updateTitle(m_wszTitle);
+ m_pWnd->updateIcon(hIcon);
+ }
+ m_hTabStatusIcon = hIcon;
+
+ if (m_cache->getStatus() != m_cache->getOldStatus()) {
+ wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_wStatus, 0), _TRUNCATE);
+
+ TCITEM item = {};
+ item.mask = TCIF_TEXT;
+ item.pszText = m_wszTitle;
+ TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+ }
+ SetWindowText(m_hwnd, wszTitle);
+ if (m_pContainer->m_hwndActive == m_hwnd) {
+ m_pContainer->UpdateTitle(0, this);
+ UpdateStatusBar();
+ }
+ }
+ else {
+ uint32_t dwOldIdle = m_idle;
+ const char *szActProto = nullptr;
+
+ m_wszStatus[0] = 0;
+
+ if (m_iTabID == -1)
+ return;
+
+ TCITEM item = {};
+
+ bool bChanged = false;
+ wchar_t newtitle[128];
+ if (m_szProto) {
+ szActProto = m_cache->getProto();
+
+ bool bHasName = (m_cache->getUIN()[0] != 0);
+ m_idle = m_cache->getIdleTS();
+ m_bIsIdle = m_idle != 0;
+
+ m_wStatus = m_cache->getStatus();
+ wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_szProto == nullptr ? ID_STATUS_OFFLINE : m_wStatus, 0), _TRUNCATE);
+
+ wchar_t newcontactname[128]; newcontactname[0] = 0;
+ if (PluginConfig.m_bCutContactNameOnTabs)
+ CutContactName(m_cache->getNick(), newcontactname, _countof(newcontactname));
+ else
+ wcsncpy_s(newcontactname, m_cache->getNick(), _TRUNCATE);
+
+ Utils::DoubleAmpersands(newcontactname, _countof(newcontactname));
+
+ if (newcontactname[0] != 0) {
+ if (g_plugin.bStatusOnTabs)
+ mir_snwprintf(newtitle, L"%s (%s)", newcontactname, m_wszStatus);
+ else
+ wcsncpy_s(newtitle, newcontactname, _TRUNCATE);
+ }
+ else wcsncpy_s(newtitle, L"Forward", _TRUNCATE);
+
+ if (mir_wstrcmp(newtitle, m_wszTitle))
+ bChanged = true;
+ else if (m_wStatus != m_wOldStatus)
+ bChanged = true;
+
+ UpdateWindowIcon();
+
+ wchar_t fulluin[256];
+ if (m_bIsMeta)
+ mir_snwprintf(fulluin,
+ TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nRight click for metacontact control\nClick dropdown to add or remove user from your favorites."),
+ bHasName ? m_cache->getUIN() : TranslateT("No UID"));
+ else
+ mir_snwprintf(fulluin,
+ TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nClick dropdown to change this contact's favorite status."),
+ bHasName ? m_cache->getUIN() : TranslateT("No UID"));
+
+ SendDlgItemMessage(m_hwnd, IDC_NAME, BUTTONADDTOOLTIP, (WPARAM)fulluin, BATF_UNICODE);
+ }
+ else wcsncpy_s(newtitle, L"Message Session", _TRUNCATE);
+
+ m_wOldStatus = m_wStatus;
+ if (m_idle != dwOldIdle || bChanged) {
+ if (bChanged) {
+ item.mask |= TCIF_TEXT;
+ item.pszText = m_wszTitle;
+ wcsncpy_s(m_wszTitle, newtitle, _TRUNCATE);
+ if (m_pWnd)
+ m_pWnd->updateTitle(m_cache->getNick());
+ }
+ if (m_iTabID >= 0) {
+ TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+ if (m_pContainer->cfg.flags.m_bSideBar)
+ m_pContainer->m_pSideBar->updateSession(this);
+ }
+ if (m_pContainer->m_hwndActive == m_hwnd && bChanged)
+ m_pContainer->UpdateTitle(m_hContact);
+
+ m_pPanel.Invalidate();
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+ }
+
+ // care about MetaContacts and update the statusbar icon with the currently "most online" contact...
+ if (m_bIsMeta) {
+ PostMessage(m_hwnd, DM_OWNNICKCHANGED, 0, 0);
+ if (m_pContainer->cfg.flags.m_bUinStatusBar)
+ DM_UpdateLastMessage();
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateWindowIcon()
+{
+ if (m_hXStatusIcon) {
+ DestroyIcon(m_hXStatusIcon);
+ m_hXStatusIcon = nullptr;
+ }
+
+ if (LPCSTR szProto = m_cache->getProto()) {
+ m_hTabIcon = m_hTabStatusIcon = GetMyContactIcon(&g_plugin.bMetaTab);
+ if (g_plugin.bUseXStatus)
+ m_hXStatusIcon = GetXStatusIcon();
+
+ SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BUTTONSETASDIMMED, m_bIsIdle, 0);
+ SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(m_hXStatusIcon ? m_hXStatusIcon : GetMyContactIcon(&g_plugin.bMetaBar)));
+
+ if (m_pContainer->m_hwndActive == m_hwnd)
+ m_pContainer->SetIcon(this, m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
+
+ if (m_pWnd)
+ m_pWnd->updateIcon(m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// called whenever a group chat tab becomes active(either by switching tabs or activating a
+// container window
+
+void CMsgDialog::UpdateWindowState(UINT msg)
+{
+ if (m_iTabID < 0)
+ return;
+
+ if (msg == WM_ACTIVATE) {
+ if (m_pContainer->cfg.flags.m_bTransparent) {
+ uint32_t trans = LOWORD(m_pContainer->cfg.dwTransparency);
+ SetLayeredWindowAttributes(m_pContainer->m_hwnd, CSkin::m_ContainerColorKey, (uint8_t)trans, (m_pContainer->cfg.flags.m_bTransparent ? LWA_ALPHA : 0));
+ }
+ }
+
+ if (m_hwndFilter) {
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rcFilter;
+ GetWindowRect(m_hwndFilter, &rcFilter);
+ if (!PtInRect(&rcFilter, pt)) {
+ SendMessage(m_hwndFilter, WM_CLOSE, 1, 1);
+ m_hwndFilter = nullptr;
+ }
+ }
+
+ if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) {
+ m_iInputAreaHeight = 0;
+ m_message.SendMsg(EM_REQUESTRESIZE, 0, 0);
+ }
+
+ m_pPanel.dismissConfig();
+ m_dwUnread = 0;
+ if (m_pWnd) {
+ m_pWnd->activateTab();
+ m_pWnd->setOverlayIcon(nullptr, true);
+ }
+
+ if (m_pContainer->m_hwndSaved == m_hwnd)
+ return;
+
+ m_pContainer->m_hwndSaved = m_hwnd;
+ m_dwTickLastEvent = 0;
+ m_bDividerSet = false;
+
+ if (m_pContainer->m_dwFlashingStarted != 0) {
+ m_pContainer->FlashContainer(0, 0);
+ m_pContainer->m_dwFlashingStarted = 0;
+ }
+
+ if (m_si) {
+ g_chatApi.SetActiveSession(m_si);
+ m_hTabIcon = m_hTabStatusIcon;
+
+ if (db_get_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0) != 0)
+ db_set_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0);
+ if (g_clistApi.pfnGetEvent(m_si->hContact, 0))
+ g_clistApi.pfnRemoveEvent(m_si->hContact, GC_FAKE_EVENT);
+
+ UpdateTitle();
+ m_hTabIcon = m_hTabStatusIcon;
+ if (timerFlash.Stop() || m_iFlashIcon) {
+ FlashTab(false);
+ m_bCanFlashTab = FALSE;
+ m_iFlashIcon = nullptr;
+ }
+
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
+
+ if (m_bNeedCheckSize)
+ PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
+
+ SetFocus(m_message.GetHwnd());
+ m_dwLastActivity = GetTickCount();
+ m_pContainer->m_dwLastActivity = m_dwLastActivity;
+ m_pContainer->m_pMenuBar->configureMenu();
+ }
+ else {
+ if (timerFlash.Stop()) {
+ FlashTab(false);
+ m_bCanFlashTab = false;
+ }
+
+ if (m_bFlashClist) {
+ m_bFlashClist = false;
+ if (m_hFlashingEvent != 0)
+ g_clistApi.pfnRemoveEvent(m_hContact, m_hFlashingEvent);
+ m_hFlashingEvent = 0;
+ }
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
+
+ if (m_bDeferredRemakeLog && !IsIconic(m_pContainer->m_hwnd)) {
+ RemakeLog();
+ m_bDeferredRemakeLog = false;
+ }
+
+ if (m_bNeedCheckSize)
+ PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
+
+ m_pContainer->m_hIconTaskbarOverlay = nullptr;
+ m_pContainer->UpdateTitle(m_hContact);
+
+ tabUpdateStatusBar();
+ m_dwLastActivity = GetTickCount();
+ m_pContainer->m_dwLastActivity = m_dwLastActivity;
+
+ m_pContainer->m_pMenuBar->configureMenu();
+ g_arUnreadWindows.remove(HANDLE(m_hContact));
+
+ m_pPanel.Invalidate();
+
+ if (m_bDeferredScroll) {
+ m_bDeferredScroll = false;
+ DM_ScrollToBottom(0, 1);
+ }
+ }
+
+ DM_SetDBButtonStates();
+
+ if (m_bDelayedSplitter) {
+ m_bDelayedSplitter = false;
+ ShowWindow(m_pContainer->m_hwnd, SW_RESTORE);
+ PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam);
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ m_wParam = m_lParam = 0;
+ }
+
+ BB_SetButtonsPos();
+ if (M.isAero())
+ InvalidateRect(m_hwndParent, nullptr, FALSE);
+
+ if (m_pContainer->cfg.flags.m_bSideBar)
+ m_pContainer->m_pSideBar->setActiveItem(this, msg == WM_ACTIVATE);
+
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// generic handler for the WM_COPY message in message log/chat history richedit control(s).
+// it filters out the invisible event boundary markers from the text copied to the clipboard.
+// WINE Fix: overwrite clippboad data from original control data
+
+LRESULT CMsgDialog::WMCopyHandler(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT result = mir_callNextSubclass(m_pLog->GetHwnd(), stubLogProc, msg, wParam, lParam);
+
+ ptrA szFromStream(LOG()->GetRichTextRtf(true, true));
+ if (szFromStream != nullptr) {
+ ptrW converted(mir_utf8decodeW(szFromStream));
+ if (converted != nullptr) {
+ Utils::FilterEventMarkers(converted);
+ Utils_ClipboardCopy(converted);
+ }
+ }
+
+ return result;
+}
diff --git a/plugins/TabSRMM/src/msgdlgutils.cpp b/plugins/TabSRMM/src/msgdlgutils.cpp index daec4e8349..eff4705074 100644 --- a/plugins/TabSRMM/src/msgdlgutils.cpp +++ b/plugins/TabSRMM/src/msgdlgutils.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdlgutils.h b/plugins/TabSRMM/src/msgdlgutils.h index b6666fd6ce..b9cd0fd792 100644 --- a/plugins/TabSRMM/src/msgdlgutils.h +++ b/plugins/TabSRMM/src/msgdlgutils.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msglog.cpp b/plugins/TabSRMM/src/msglog.cpp index b4143a69a6..e12507d8e0 100644 --- a/plugins/TabSRMM/src/msglog.cpp +++ b/plugins/TabSRMM/src/msglog.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgoptions.cpp b/plugins/TabSRMM/src/msgoptions.cpp index b87a801874..b71a71dbc7 100644 --- a/plugins/TabSRMM/src/msgoptions.cpp +++ b/plugins/TabSRMM/src/msgoptions.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgs.cpp b/plugins/TabSRMM/src/msgs.cpp index 7753b418d0..1c4448bff0 100644 --- a/plugins/TabSRMM/src/msgs.cpp +++ b/plugins/TabSRMM/src/msgs.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgs.h b/plugins/TabSRMM/src/msgs.h index 693f5aa08c..382ad1f54d 100644 --- a/plugins/TabSRMM/src/msgs.h +++ b/plugins/TabSRMM/src/msgs.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/muchighlight.cpp b/plugins/TabSRMM/src/muchighlight.cpp index ffa1759fa9..9eb5cd96f5 100644 --- a/plugins/TabSRMM/src/muchighlight.cpp +++ b/plugins/TabSRMM/src/muchighlight.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/muchighlight.h b/plugins/TabSRMM/src/muchighlight.h index 197e130859..7338765c40 100644 --- a/plugins/TabSRMM/src/muchighlight.h +++ b/plugins/TabSRMM/src/muchighlight.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/nen.h b/plugins/TabSRMM/src/nen.h index bca0b37885..d40d7367b6 100644 --- a/plugins/TabSRMM/src/nen.h +++ b/plugins/TabSRMM/src/nen.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/selectcontainer.cpp b/plugins/TabSRMM/src/selectcontainer.cpp index c8189f9622..dc835292ca 100644 --- a/plugins/TabSRMM/src/selectcontainer.cpp +++ b/plugins/TabSRMM/src/selectcontainer.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendlater.cpp b/plugins/TabSRMM/src/sendlater.cpp index 1ad1690076..1e8614d1fd 100644 --- a/plugins/TabSRMM/src/sendlater.cpp +++ b/plugins/TabSRMM/src/sendlater.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendlater.h b/plugins/TabSRMM/src/sendlater.h index bfe97b5fe6..5dc2a58df3 100644 --- a/plugins/TabSRMM/src/sendlater.h +++ b/plugins/TabSRMM/src/sendlater.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendqueue.cpp b/plugins/TabSRMM/src/sendqueue.cpp index 4e8372b7e4..be8566f188 100644 --- a/plugins/TabSRMM/src/sendqueue.cpp +++ b/plugins/TabSRMM/src/sendqueue.cpp @@ -1,7 +1,7 @@ ///////////////////////////////////////////////////////////////////////////////////////// // Miranda NG: the free IM client for Microsoft* Windows* // -// Copyright (C) 2012-22 Miranda NG team, +// Copyright (C) 2012-23 Miranda NG team, // Copyright (c) 2000-09 Miranda ICQ/IM project, // all portions of this codebase are copyrighted to the people // listed in contributors.txt. diff --git a/plugins/TabSRMM/src/sendqueue.h b/plugins/TabSRMM/src/sendqueue.h index 89f32b17f8..7dfb4ff9b1 100644 --- a/plugins/TabSRMM/src/sendqueue.h +++ b/plugins/TabSRMM/src/sendqueue.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sidebar.cpp b/plugins/TabSRMM/src/sidebar.cpp index c3014aa210..8f6de0a0f3 100644 --- a/plugins/TabSRMM/src/sidebar.cpp +++ b/plugins/TabSRMM/src/sidebar.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sidebar.h b/plugins/TabSRMM/src/sidebar.h index b02e742d37..2e67eae807 100644 --- a/plugins/TabSRMM/src/sidebar.h +++ b/plugins/TabSRMM/src/sidebar.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/srmm.cpp b/plugins/TabSRMM/src/srmm.cpp index 9c9c3c6d2f..388565d140 100644 --- a/plugins/TabSRMM/src/srmm.cpp +++ b/plugins/TabSRMM/src/srmm.cpp @@ -1,144 +1,144 @@ -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda NG: the free IM client for Microsoft* Windows* -// -// Copyright (C) 2012-22 Miranda NG team, -// Copyright (c) 2000-09 Miranda ICQ/IM project, -// all portions of this codebase are copyrighted to the people -// listed in contributors.txt. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// you should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// -// part of tabSRMM messaging plugin for Miranda. -// -// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors -// -// plugin loading functions and global exports. - -#include "stdafx.h" - -LOGFONT lfDefault = { 0 }; - -/* - * miranda interfaces - */ - -CMPlugin g_plugin; - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {6CA5F042-7A7F-47CC-A715-FC8C46FBF434} - { 0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 } } -}; - -CMPlugin::CMPlugin() : - PLUGIN<CMPlugin>("SRMsg", pluginInfoEx), - - // main settings - bAutoMin(SRMSGMOD_T, "AutoMin", false), - bAutoCopy(SRMSGMOD_T, "autocopy", true), - bAutoTabs(SRMSGMOD_T, "autotabs", true), - bAllowTab(SRMSGMOD_T, "tabmode", false), - bAutoClose(SRMSGMOD_T, "AutoClose", false), - bAutoPopup(SRMSGMOD_T, "AutoPopup", false), - bAutoSplit(SRMSGMOD_T, "autosplit", false), - bDeleteTemp(SRMSGMOD_T, "deletetemp", false), - bUseXStatus(SRMSGMOD_T, "use_xicons", true), - bSendFormat(SRMSGMOD_T, "sendformat", false), - bHideOnClose(SRMSGMOD_T, "hideonclose", false), - bStatusOnTabs(SRMSGMOD_T, "tabstatus", true), - bFlashOnClist(SRMSGMOD_T, "flashcl", false), - bPasteAndSend(SRMSGMOD_T, "pasteandsend", true), - bAutoContainer(SRMSGMOD_T, "autocontainer", true), - bAutoSwitchTabs(SRMSGMOD_T, "autoswitchtabs", true), - bPopupContainer(SRMSGMOD_T, "cpopup", true), - bDetailedTooltips(SRMSGMOD_T, "d_tooltips", false), - bUseSameSplitSize(SRMSGMOD_T, "usesamesplitsize", true), - bAllowOfflineMultisend(SRMSGMOD_T, "AllowOfflineMultisend", true), - - // advanced options - bMetaBar(SRMSGMOD_T, "MetaiconBar", true), - bMetaTab(SRMSGMOD_T, "MetaiconTab", true), - bShowDesc(SRMSGMOD_T, "ShowClientDescription", false), - bCloseSend(SRMSGMOD_T, "adv_AutoClose_2", false), - bErrorPopup(SRMSGMOD_T, "adv_ErrorPopups", true), - - // chat settings - bOpenInDefault(CHAT_MODULE, "DefaultContainer", true), - bCreateWindowOnHighlight(CHAT_MODULE, "CreateWindowOnHighlight", false), - bBBCodeInPopups(CHAT_MODULE, "BBCodeInPopups", false), - bClassicIndicators(CHAT_MODULE, "ClassicIndicators", false), - bLogClassicIndicators(CHAT_MODULE, "LogClassicIndicators", false), - bAlternativeSorting(CHAT_MODULE, "AlternativeSorting", true), - bAnnoyingHighlight(CHAT_MODULE, "AnnoyingHighlight", false), - bLogSymbols(CHAT_MODULE, "LogSymbols", true), - bClickableNicks(CHAT_MODULE, "ClickableNicks", true), - bColorizeNicks(CHAT_MODULE, "ColorizeNicks", true), - bColorizeNicksInLog(CHAT_MODULE, "ColorizeNicksInLog", true), - bScaleIcons(CHAT_MODULE, "ScaleIcons", true), - bNewLineAfterNames(CHAT_MODULE, "NewlineAfterNames", false), - - // typing settings - bPopups(TypingModule, "TypingPopup", true), - bTypingNew(TypingModule, "DefaultTyping", true), - bTypingUnknown(TypingModule, "UnknownTyping", false), - - // log options - bUseDividers(SRMSGMOD_T, "usedividers", false), - bLogStatusChanges(SRMSGMOD_T, "logstatuschanges", false), - bDividersUsePopupConfig(SRMSGMOD_T, "div_popupconfig", false) -{} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_SRMM, MIID_LAST }; - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPlugin::Load() -{ - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE); - - hLogger = RegisterSrmmLog(this, "built-in", LPGENW("tabSRMM internal log"), &logBuilder); - - Chat_Load(); - - return LoadSendRecvMessageModule(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPlugin::Unload() -{ - UnregisterSrmmLog(hLogger); - FreeLogFonts(); - Chat_Unload(); - int iRet = SplitmsgShutdown(); - Skin->setupTabCloseBitmap(true); - Skin->UnloadAeroTabs(); - CleanTempFiles(); - SendLater::shutDown(); - delete Skin; - delete sendQueue; - return iRet; -} +/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// plugin loading functions and global exports.
+
+#include "stdafx.h"
+
+LOGFONT lfDefault = { 0 };
+
+/*
+ * miranda interfaces
+ */
+
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {6CA5F042-7A7F-47CC-A715-FC8C46FBF434}
+ { 0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>("SRMsg", pluginInfoEx),
+
+ // main settings
+ bAutoMin(SRMSGMOD_T, "AutoMin", false),
+ bAutoCopy(SRMSGMOD_T, "autocopy", true),
+ bAutoTabs(SRMSGMOD_T, "autotabs", true),
+ bAllowTab(SRMSGMOD_T, "tabmode", false),
+ bAutoClose(SRMSGMOD_T, "AutoClose", false),
+ bAutoPopup(SRMSGMOD_T, "AutoPopup", false),
+ bAutoSplit(SRMSGMOD_T, "autosplit", false),
+ bDeleteTemp(SRMSGMOD_T, "deletetemp", false),
+ bUseXStatus(SRMSGMOD_T, "use_xicons", true),
+ bSendFormat(SRMSGMOD_T, "sendformat", false),
+ bHideOnClose(SRMSGMOD_T, "hideonclose", false),
+ bStatusOnTabs(SRMSGMOD_T, "tabstatus", true),
+ bFlashOnClist(SRMSGMOD_T, "flashcl", false),
+ bPasteAndSend(SRMSGMOD_T, "pasteandsend", true),
+ bAutoContainer(SRMSGMOD_T, "autocontainer", true),
+ bAutoSwitchTabs(SRMSGMOD_T, "autoswitchtabs", true),
+ bPopupContainer(SRMSGMOD_T, "cpopup", true),
+ bDetailedTooltips(SRMSGMOD_T, "d_tooltips", false),
+ bUseSameSplitSize(SRMSGMOD_T, "usesamesplitsize", true),
+ bAllowOfflineMultisend(SRMSGMOD_T, "AllowOfflineMultisend", true),
+
+ // advanced options
+ bMetaBar(SRMSGMOD_T, "MetaiconBar", true),
+ bMetaTab(SRMSGMOD_T, "MetaiconTab", true),
+ bShowDesc(SRMSGMOD_T, "ShowClientDescription", false),
+ bCloseSend(SRMSGMOD_T, "adv_AutoClose_2", false),
+ bErrorPopup(SRMSGMOD_T, "adv_ErrorPopups", true),
+
+ // chat settings
+ bOpenInDefault(CHAT_MODULE, "DefaultContainer", true),
+ bCreateWindowOnHighlight(CHAT_MODULE, "CreateWindowOnHighlight", false),
+ bBBCodeInPopups(CHAT_MODULE, "BBCodeInPopups", false),
+ bClassicIndicators(CHAT_MODULE, "ClassicIndicators", false),
+ bLogClassicIndicators(CHAT_MODULE, "LogClassicIndicators", false),
+ bAlternativeSorting(CHAT_MODULE, "AlternativeSorting", true),
+ bAnnoyingHighlight(CHAT_MODULE, "AnnoyingHighlight", false),
+ bLogSymbols(CHAT_MODULE, "LogSymbols", true),
+ bClickableNicks(CHAT_MODULE, "ClickableNicks", true),
+ bColorizeNicks(CHAT_MODULE, "ColorizeNicks", true),
+ bColorizeNicksInLog(CHAT_MODULE, "ColorizeNicksInLog", true),
+ bScaleIcons(CHAT_MODULE, "ScaleIcons", true),
+ bNewLineAfterNames(CHAT_MODULE, "NewlineAfterNames", false),
+
+ // typing settings
+ bPopups(TypingModule, "TypingPopup", true),
+ bTypingNew(TypingModule, "DefaultTyping", true),
+ bTypingUnknown(TypingModule, "UnknownTyping", false),
+
+ // log options
+ bUseDividers(SRMSGMOD_T, "usedividers", false),
+ bLogStatusChanges(SRMSGMOD_T, "logstatuschanges", false),
+ bDividersUsePopupConfig(SRMSGMOD_T, "div_popupconfig", false)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_SRMM, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Load()
+{
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
+
+ hLogger = RegisterSrmmLog(this, "built-in", LPGENW("tabSRMM internal log"), &logBuilder);
+
+ Chat_Load();
+
+ return LoadSendRecvMessageModule();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+ UnregisterSrmmLog(hLogger);
+ FreeLogFonts();
+ Chat_Unload();
+ int iRet = SplitmsgShutdown();
+ Skin->setupTabCloseBitmap(true);
+ Skin->UnloadAeroTabs();
+ CleanTempFiles();
+ SendLater::shutDown();
+ delete Skin;
+ delete sendQueue;
+ return iRet;
+}
diff --git a/plugins/TabSRMM/src/stdafx.cxx b/plugins/TabSRMM/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/TabSRMM/src/stdafx.cxx +++ b/plugins/TabSRMM/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/TabSRMM/src/stdafx.h b/plugins/TabSRMM/src/stdafx.h index e09f24597b..4c87b4c428 100644 --- a/plugins/TabSRMM/src/stdafx.h +++ b/plugins/TabSRMM/src/stdafx.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/tabctrl.cpp b/plugins/TabSRMM/src/tabctrl.cpp index 29d2b0511b..35523e14f7 100644 --- a/plugins/TabSRMM/src/tabctrl.cpp +++ b/plugins/TabSRMM/src/tabctrl.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/taskbar.cpp b/plugins/TabSRMM/src/taskbar.cpp index a6eb6b8a2d..d49d937af5 100644 --- a/plugins/TabSRMM/src/taskbar.cpp +++ b/plugins/TabSRMM/src/taskbar.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/taskbar.h b/plugins/TabSRMM/src/taskbar.h index 92f8dcf85d..ed407dadbd 100644 --- a/plugins/TabSRMM/src/taskbar.h +++ b/plugins/TabSRMM/src/taskbar.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/templates.cpp b/plugins/TabSRMM/src/templates.cpp index 369c5ee4fe..a27a3df00f 100644 --- a/plugins/TabSRMM/src/templates.cpp +++ b/plugins/TabSRMM/src/templates.cpp @@ -1,124 +1,124 @@ -///////////////////////////////////////////////////////////////////////////////////////// -// Miranda NG: the free IM client for Microsoft* Windows* -// -// Copyright (C) 2012-22 Miranda NG team, -// Copyright (c) 2000-09 Miranda ICQ/IM project, -// all portions of this codebase are copyrighted to the people -// listed in contributors.txt. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// you should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// -// part of tabSRMM messaging plugin for Miranda. -// -// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors -// -// Simple editor for the message log templates - -#include "stdafx.h" - -/* -* hardcoded default set of templates for both LTR and RTL. -* cannot be changed and may be used at any time to "revert" to a working layout -*/ - -char* TemplateNames[TMPL_MAX] = -{ - LPGEN("Message In"), - LPGEN("Message Out"), - LPGEN("Group In (Start)"), - LPGEN("Group Out (Start)"), - LPGEN("Group In (Inner)"), - LPGEN("Group Out (Inner)"), - LPGEN("Status change"), - LPGEN("Error message") -}; - -wchar_t* TemplateNamesW[TMPL_MAX] = -{ - LPGENW("Message In"), - LPGENW("Message Out"), - LPGENW("Group In (Start)"), - LPGENW("Group Out (Start)"), - LPGENW("Group In (Inner)"), - LPGENW("Group Out (Inner)"), - LPGENW("Status change"), - LPGENW("Error message") -}; - -TTemplateSet LTR_Default = -{ - TRUE, - L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M", - L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M", - L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M", - L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M", - L"%S %T%|%M", - L"%S %T%|%M", - L"%I %S %&r, %&T, %N %M%! ", - L"%I%S %r, %T, %e%l%M", - "Default LTR" -}; - -TTemplateSet RTL_Default = -{ - TRUE, - L"%I %N %r%n%S %T%|%M", - L"%I %N %r%n%S %T%|%M", - L"%I %N %r%n%S %T%|%M", - L"%I %N %r%n%S %T%|%M", - L"%S %T%|%M", - L"%S %T%|%M", - L"%I%S %r, %T, %N %M%! ", - L"%I%S %r, %T, %e%l%M", - "Default RTL" -}; - -TTemplateSet LTR_Active, RTL_Active; - -/* -* loads template set overrides from hContact into the given set of already existing -* templates -*/ - -static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl) -{ - for (int i = 0; i < TMPL_MAX; i++) { - DBVARIANT dbv = { 0 }; - if (db_get_ws(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv)) - continue; - if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) - wcsncpy_s(tSet->szTemplates[i], dbv.pwszVal, _TRUNCATE); - db_free(&dbv); - } -} - -void LoadDefaultTemplates() -{ - LTR_Active = LTR_Default; - RTL_Active = RTL_Default; - - if (db_get_b(0, RTLTEMPLATES_MODULE, "setup", 0) < 2) { - for (int i = 0; i < TMPL_MAX; i++) - db_set_ws(0, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]); - db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2); - } - if (db_get_b(0, TEMPLATES_MODULE, "setup", 0) < 2) { - for (int i = 0; i < TMPL_MAX; i++) - db_set_ws(0, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]); - db_set_b(0, TEMPLATES_MODULE, "setup", 2); - } - LoadTemplatesFrom(<R_Active, 0, 0); - LoadTemplatesFrom(&RTL_Active, 0, 1); -} +/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// Simple editor for the message log templates
+
+#include "stdafx.h"
+
+/*
+* hardcoded default set of templates for both LTR and RTL.
+* cannot be changed and may be used at any time to "revert" to a working layout
+*/
+
+char* TemplateNames[TMPL_MAX] =
+{
+ LPGEN("Message In"),
+ LPGEN("Message Out"),
+ LPGEN("Group In (Start)"),
+ LPGEN("Group Out (Start)"),
+ LPGEN("Group In (Inner)"),
+ LPGEN("Group Out (Inner)"),
+ LPGEN("Status change"),
+ LPGEN("Error message")
+};
+
+wchar_t* TemplateNamesW[TMPL_MAX] =
+{
+ LPGENW("Message In"),
+ LPGENW("Message Out"),
+ LPGENW("Group In (Start)"),
+ LPGENW("Group Out (Start)"),
+ LPGENW("Group In (Inner)"),
+ LPGENW("Group Out (Inner)"),
+ LPGENW("Status change"),
+ LPGENW("Error message")
+};
+
+TTemplateSet LTR_Default =
+{
+ TRUE,
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%S %T%|%M",
+ L"%S %T%|%M",
+ L"%I %S %&r, %&T, %N %M%! ",
+ L"%I%S %r, %T, %e%l%M",
+ "Default LTR"
+};
+
+TTemplateSet RTL_Default =
+{
+ TRUE,
+ L"%I %N %r%n%S %T%|%M",
+ L"%I %N %r%n%S %T%|%M",
+ L"%I %N %r%n%S %T%|%M",
+ L"%I %N %r%n%S %T%|%M",
+ L"%S %T%|%M",
+ L"%S %T%|%M",
+ L"%I%S %r, %T, %N %M%! ",
+ L"%I%S %r, %T, %e%l%M",
+ "Default RTL"
+};
+
+TTemplateSet LTR_Active, RTL_Active;
+
+/*
+* loads template set overrides from hContact into the given set of already existing
+* templates
+*/
+
+static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl)
+{
+ for (int i = 0; i < TMPL_MAX; i++) {
+ DBVARIANT dbv = { 0 };
+ if (db_get_ws(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv))
+ continue;
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR)
+ wcsncpy_s(tSet->szTemplates[i], dbv.pwszVal, _TRUNCATE);
+ db_free(&dbv);
+ }
+}
+
+void LoadDefaultTemplates()
+{
+ LTR_Active = LTR_Default;
+ RTL_Active = RTL_Default;
+
+ if (db_get_b(0, RTLTEMPLATES_MODULE, "setup", 0) < 2) {
+ for (int i = 0; i < TMPL_MAX; i++)
+ db_set_ws(0, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]);
+ db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2);
+ }
+ if (db_get_b(0, TEMPLATES_MODULE, "setup", 0) < 2) {
+ for (int i = 0; i < TMPL_MAX; i++)
+ db_set_ws(0, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]);
+ db_set_b(0, TEMPLATES_MODULE, "setup", 2);
+ }
+ LoadTemplatesFrom(<R_Active, 0, 0);
+ LoadTemplatesFrom(&RTL_Active, 0, 1);
+}
diff --git a/plugins/TabSRMM/src/themeio.cpp b/plugins/TabSRMM/src/themeio.cpp index 7648b8f90b..deaecbfcc6 100644 --- a/plugins/TabSRMM/src/themeio.cpp +++ b/plugins/TabSRMM/src/themeio.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/themes.cpp b/plugins/TabSRMM/src/themes.cpp index 14a762cc0a..7c48a65d5e 100644 --- a/plugins/TabSRMM/src/themes.cpp +++ b/plugins/TabSRMM/src/themes.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/themes.h b/plugins/TabSRMM/src/themes.h index 471c1a73f5..2e7bd1bf1c 100644 --- a/plugins/TabSRMM/src/themes.h +++ b/plugins/TabSRMM/src/themes.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/userprefs.cpp b/plugins/TabSRMM/src/userprefs.cpp index 573f2b883e..880112e041 100644 --- a/plugins/TabSRMM/src/userprefs.cpp +++ b/plugins/TabSRMM/src/userprefs.cpp @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/utils.cpp b/plugins/TabSRMM/src/utils.cpp index 74fab10f8a..484b5dca1d 100644 --- a/plugins/TabSRMM/src/utils.cpp +++ b/plugins/TabSRMM/src/utils.cpp @@ -1,7 +1,7 @@ ////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/utils.h b/plugins/TabSRMM/src/utils.h index f60cebe2e3..66716896ba 100644 --- a/plugins/TabSRMM/src/utils.h +++ b/plugins/TabSRMM/src/utils.h @@ -1,7 +1,7 @@ /////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/version.h b/plugins/TabSRMM/src/version.h index 37a59b1f3a..376079948b 100644 --- a/plugins/TabSRMM/src/version.h +++ b/plugins/TabSRMM/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "IM and group chat module for Miranda NG."
#define __AUTHOR "The Miranda developers team and contributors"
#define __AUTHORWEB "https://miranda-ng.org/p/TabSRMM"
-#define __COPYRIGHT "© 2012-22 Miranda NG team, 2000-2010 Miranda Project and contributors."
+#define __COPYRIGHT "© 2012-23 Miranda NG team, 2000-2010 Miranda Project and contributors."
diff --git a/plugins/TabSRMM/src/warning.cpp b/plugins/TabSRMM/src/warning.cpp index c5bf30736f..fe93ea374c 100644 --- a/plugins/TabSRMM/src/warning.cpp +++ b/plugins/TabSRMM/src/warning.cpp @@ -1,328 +1,328 @@ -/* -Copyright (C) 2012-22 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" - -using namespace CWarning; - -static MWindowList hWindowList; - -class CWarningImpl -{ - ptrW m_szTitle, m_szText; - UINT m_uId; - HFONT m_hFontCaption = nullptr; - uint32_t m_dwFlags; - HWND m_hwnd = nullptr; - bool m_fIsModal; - -public: - CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) : - m_szTitle(mir_wstrdup(tszTitle)), - m_szText(mir_wstrdup(tszText)) - { - m_uId = uId; - m_dwFlags = dwFlags; - m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false); - } - - ~CWarningImpl() - { - if (m_hFontCaption) - ::DeleteObject(m_hFontCaption); - } - - // static function to construct and show the dialog, returns the user's choice - LRESULT ShowDialog() const - { - if (!m_fIsModal) { - ::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this)); - return 0; - } - - return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this)); - } - - ///////////////////////////////////////////////////////////////////////////////////////// - // stub dlg procedure.Just register the object pointer in WM_INITDIALOG - - static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - { - CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); - if (w) - return(w->dlgProc(hwnd, msg, wParam, lParam)); - - switch (msg) { - case WM_INITDIALOG: - w = reinterpret_cast<CWarningImpl *>(lParam); - if (w) { - ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); - return(w->dlgProc(hwnd, msg, wParam, lParam)); - } - break; - } - return FALSE; - } - - ///////////////////////////////////////////////////////////////////////////////////////// - // dialog procedure for the warning dialog box - - INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - { - switch (msg) { - case WM_INITDIALOG: - m_hwnd = hwnd; - - ::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message")); - ::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA); - ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0); - ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK); - - TranslateDialogDefault(hwnd); - { - CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15); - str.Append(m_szText); - str.Append(L"}"); - str.Replace(L"\n", L"\\line "); - SETTEXTEX stx = {ST_SELECTION, CP_UTF8}; - ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str)); - - ::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle); - - if (m_dwFlags & CWF_NOALLOWHIDE) - Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE); - if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) { - Utils::showDlgControl(hwnd, IDOK, SW_HIDE); - ::SetFocus(::GetDlgItem(hwnd, IDCANCEL)); - } - else { - Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE); - Utils::showDlgControl(hwnd, IDYES, SW_HIDE); - Utils::showDlgControl(hwnd, IDNO, SW_HIDE); - ::SetFocus(::GetDlgItem(hwnd, IDOK)); - } - - UINT uResId = 0; - if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND)) - uResId = 32513; - else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING)) - uResId = 32515; - else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION)) - uResId = 32516; - else if (m_dwFlags & MB_ICONQUESTION) - uResId = 32514; - - HICON hIcon; - if (uResId) - hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); - else - hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true); - ::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0); - - if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL)) - ::ShowWindow(hwnd, SW_SHOWNORMAL); - - WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd); - } - return TRUE; - - case WM_CTLCOLORSTATIC: - { - HWND hwndChild = reinterpret_cast<HWND>(lParam); - UINT id = ::GetDlgCtrlID(hwndChild); - if (nullptr == m_hFontCaption) { - HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0)); - LOGFONT lf = {0}; - - ::GetObject(hFont, sizeof(lf), &lf); - lf.lfHeight = (int)((double)lf.lfHeight * 1.7f); - m_hFontCaption = ::CreateFontIndirect(&lf); - ::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE); - } - - if (IDC_CAPTION == id) { - ::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT)); - ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE); - } - - if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) { - ::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW)); - return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW)); - } - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDYES: - case IDNO: - if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) { - uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId); - db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal); - - if (LOWORD(wParam) != IDNO) { - newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId); - db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal); - } - } - __fallthrough; - - case IDCANCEL: - if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll() - ::DestroyWindow(hwnd); - else - ::EndDialog(hwnd, LOWORD(wParam)); - break; - } - break; - - case WM_NOTIFY: - switch (((NMHDR *)lParam)->code) { - case EN_LINK: - switch (((ENLINK *)lParam)->msg) { - case WM_LBUTTONUP: - ENLINK *e = reinterpret_cast<ENLINK *>(lParam); - - const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT)); - if (wszUrl) { - Utils_OpenUrlW(wszUrl); - mir_free(const_cast<wchar_t *>(wszUrl)); - } - } - } - break; - - case WM_DESTROY: - ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - delete this; - - WindowList_Remove(hWindowList, hwnd); - Window_FreeIcon_IcoLib(hwnd); - break; - } - - return FALSE; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// implementation of the CWarningImpl class -// -// IMPORTANT note to translators for translation of the warning dialogs: -// -// Make sure to NOT remove the pipe character ( | ) from the strings. This separates the -// warning title from the actual warning text. -// -// Also, do NOT insert multiple | characters in the translated string. Not well-formatted -// warnings cannot be translated and the plugin will show the untranslated versions. -// -// strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This -// will be used for important and critical error messages only. -// -// some strings are empty, this is intentional and used for error messages that share -// the message with other possible error notifications (popups, tool tips etc.) -// -// Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them. - -static wchar_t *Warnings[] = { - nullptr, - LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE - LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"), /* WARN_EDITUSERNOTES */ - LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"), /* WARN_ICONPACKMISSING */ - LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."), /* WARN_AEROPEEKSKIN */ - LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */ - LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0 is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */ - LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0 and \\b1 send to multiple contacts\\b0 features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */ - LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"), /* WARN_CLOSEWINDOW */ - LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */ - LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */ -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// send cancel message to all open warning dialogs so they are destroyed -// before TabSRMM is unloaded. -// -// called by the OkToExit handler in globals.cpp - -void CWarning::destroyAll() -{ - if (hWindowList) - WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// show a warning dialog using the id value. Check whether the user has chosen to -// not show this message again. This has room for 64 different warning dialogs, which -// should be enough in the first place. Extending it should not be too hard though. - -LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt) -{ - if (hWindowList == nullptr) - hWindowList = WindowList_Create(); - - // don't open new warnings when shutdown was initiated (modal ones will otherwise - // block the shutdown) - if (CMimAPI::m_shutDown) - return -1; - - wchar_t *_s = nullptr; - if (tszTxt) - _s = const_cast<wchar_t *>(tszTxt); - else { - if (uId == -1) - return -1; - - if (dwFlags & CWF_UNTRANSLATED) - _s = TranslateW(Warnings[uId]); - else { - // revert to untranslated warning when the translated message - // is not well-formatted. - _s = TranslateW(Warnings[uId]); - - if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|')) - _s = TranslateW(Warnings[uId]); - } - } - - if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) { - if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) { - uint32_t val = M.GetDword("cWarningsL", 0); - uint32_t mask = ((__int64)1L) << uId; - if (mask & val) { - bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0; - if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL) - return (bResult) ? IDYES : IDNO; - return IDOK; - } - } - - ptrW s(mir_wstrdup(_s)); - wchar_t *separator_pos = wcschr(s, '|'); - - if (separator_pos) { - *separator_pos = 0; - - CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags); - if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL) - return w->ShowDialog(); - - w->ShowDialog(); - } - } - return -1; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+using namespace CWarning;
+
+static MWindowList hWindowList;
+
+class CWarningImpl
+{
+ ptrW m_szTitle, m_szText;
+ UINT m_uId;
+ HFONT m_hFontCaption = nullptr;
+ uint32_t m_dwFlags;
+ HWND m_hwnd = nullptr;
+ bool m_fIsModal;
+
+public:
+ CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) :
+ m_szTitle(mir_wstrdup(tszTitle)),
+ m_szText(mir_wstrdup(tszText))
+ {
+ m_uId = uId;
+ m_dwFlags = dwFlags;
+ m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
+ }
+
+ ~CWarningImpl()
+ {
+ if (m_hFontCaption)
+ ::DeleteObject(m_hFontCaption);
+ }
+
+ // static function to construct and show the dialog, returns the user's choice
+ LRESULT ShowDialog() const
+ {
+ if (!m_fIsModal) {
+ ::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
+ return 0;
+ }
+
+ return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // stub dlg procedure.Just register the object pointer in WM_INITDIALOG
+
+ static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+ if (w)
+ return(w->dlgProc(hwnd, msg, wParam, lParam));
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ w = reinterpret_cast<CWarningImpl *>(lParam);
+ if (w) {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ return(w->dlgProc(hwnd, msg, wParam, lParam));
+ }
+ break;
+ }
+ return FALSE;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // dialog procedure for the warning dialog box
+
+ INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (msg) {
+ case WM_INITDIALOG:
+ m_hwnd = hwnd;
+
+ ::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message"));
+ ::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA);
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0);
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
+
+ TranslateDialogDefault(hwnd);
+ {
+ CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15);
+ str.Append(m_szText);
+ str.Append(L"}");
+ str.Replace(L"\n", L"\\line ");
+ SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str));
+
+ ::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle);
+
+ if (m_dwFlags & CWF_NOALLOWHIDE)
+ Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
+ if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
+ Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
+ ::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
+ }
+ else {
+ Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
+ Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
+ Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
+ ::SetFocus(::GetDlgItem(hwnd, IDOK));
+ }
+
+ UINT uResId = 0;
+ if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND))
+ uResId = 32513;
+ else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING))
+ uResId = 32515;
+ else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION))
+ uResId = 32516;
+ else if (m_dwFlags & MB_ICONQUESTION)
+ uResId = 32514;
+
+ HICON hIcon;
+ if (uResId)
+ hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
+ else
+ hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true);
+ ::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);
+
+ if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
+ ::ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd);
+ }
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ HWND hwndChild = reinterpret_cast<HWND>(lParam);
+ UINT id = ::GetDlgCtrlID(hwndChild);
+ if (nullptr == m_hFontCaption) {
+ HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
+ LOGFONT lf = {0};
+
+ ::GetObject(hFont, sizeof(lf), &lf);
+ lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
+ m_hFontCaption = ::CreateFontIndirect(&lf);
+ ::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+ }
+
+ if (IDC_CAPTION == id) {
+ ::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
+ ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+ }
+
+ if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
+ ::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
+ return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDYES:
+ case IDNO:
+ if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
+ uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId);
+ db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal);
+
+ if (LOWORD(wParam) != IDNO) {
+ newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId);
+ db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal);
+ }
+ }
+ __fallthrough;
+
+ case IDCANCEL:
+ if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll()
+ ::DestroyWindow(hwnd);
+ else
+ ::EndDialog(hwnd, LOWORD(wParam));
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR *)lParam)->code) {
+ case EN_LINK:
+ switch (((ENLINK *)lParam)->msg) {
+ case WM_LBUTTONUP:
+ ENLINK *e = reinterpret_cast<ENLINK *>(lParam);
+
+ const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
+ if (wszUrl) {
+ Utils_OpenUrlW(wszUrl);
+ mir_free(const_cast<wchar_t *>(wszUrl));
+ }
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ delete this;
+
+ WindowList_Remove(hWindowList, hwnd);
+ Window_FreeIcon_IcoLib(hwnd);
+ break;
+ }
+
+ return FALSE;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// implementation of the CWarningImpl class
+//
+// IMPORTANT note to translators for translation of the warning dialogs:
+//
+// Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
+// warning title from the actual warning text.
+//
+// Also, do NOT insert multiple | characters in the translated string. Not well-formatted
+// warnings cannot be translated and the plugin will show the untranslated versions.
+//
+// strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
+// will be used for important and critical error messages only.
+//
+// some strings are empty, this is intentional and used for error messages that share
+// the message with other possible error notifications (popups, tool tips etc.)
+//
+// Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them.
+
+static wchar_t *Warnings[] = {
+ nullptr,
+ LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE
+ LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"), /* WARN_EDITUSERNOTES */
+ LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"), /* WARN_ICONPACKMISSING */
+ LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."), /* WARN_AEROPEEKSKIN */
+ LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */
+ LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0 is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
+ LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0 and \\b1 send to multiple contacts\\b0 features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */
+ LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"), /* WARN_CLOSEWINDOW */
+ LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
+ LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send cancel message to all open warning dialogs so they are destroyed
+// before TabSRMM is unloaded.
+//
+// called by the OkToExit handler in globals.cpp
+
+void CWarning::destroyAll()
+{
+ if (hWindowList)
+ WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show a warning dialog using the id value. Check whether the user has chosen to
+// not show this message again. This has room for 64 different warning dialogs, which
+// should be enough in the first place. Extending it should not be too hard though.
+
+LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt)
+{
+ if (hWindowList == nullptr)
+ hWindowList = WindowList_Create();
+
+ // don't open new warnings when shutdown was initiated (modal ones will otherwise
+ // block the shutdown)
+ if (CMimAPI::m_shutDown)
+ return -1;
+
+ wchar_t *_s = nullptr;
+ if (tszTxt)
+ _s = const_cast<wchar_t *>(tszTxt);
+ else {
+ if (uId == -1)
+ return -1;
+
+ if (dwFlags & CWF_UNTRANSLATED)
+ _s = TranslateW(Warnings[uId]);
+ else {
+ // revert to untranslated warning when the translated message
+ // is not well-formatted.
+ _s = TranslateW(Warnings[uId]);
+
+ if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|'))
+ _s = TranslateW(Warnings[uId]);
+ }
+ }
+
+ if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) {
+ if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) {
+ uint32_t val = M.GetDword("cWarningsL", 0);
+ uint32_t mask = ((__int64)1L) << uId;
+ if (mask & val) {
+ bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0;
+ if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
+ return (bResult) ? IDYES : IDNO;
+ return IDOK;
+ }
+ }
+
+ ptrW s(mir_wstrdup(_s));
+ wchar_t *separator_pos = wcschr(s, '|');
+
+ if (separator_pos) {
+ *separator_pos = 0;
+
+ CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags);
+ if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
+ return w->ShowDialog();
+
+ w->ShowDialog();
+ }
+ }
+ return -1;
+}
diff --git a/plugins/TipperYM/src/stdafx.cxx b/plugins/TipperYM/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/TipperYM/src/stdafx.cxx +++ b/plugins/TipperYM/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Toaster/src/version.h b/plugins/Toaster/src/version.h index 7a78c7aa15..a34fa9e38e 100644 --- a/plugins/Toaster/src/version.h +++ b/plugins/Toaster/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Provides popup services based on Windows toast notification for different plugins."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Toaster"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/TooltipNotify/src/stdafx.cxx b/plugins/TooltipNotify/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/TooltipNotify/src/stdafx.cxx +++ b/plugins/TooltipNotify/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/TopToolBar/src/stdafx.cxx b/plugins/TopToolBar/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/TopToolBar/src/stdafx.cxx +++ b/plugins/TopToolBar/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/TrafficCounter/src/stdafx.cxx b/plugins/TrafficCounter/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/TrafficCounter/src/stdafx.cxx +++ b/plugins/TrafficCounter/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/TranslitSwitcher/src/stdafx.cxx b/plugins/TranslitSwitcher/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/TranslitSwitcher/src/stdafx.cxx +++ b/plugins/TranslitSwitcher/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/TranslitSwitcher/src/version.h b/plugins/TranslitSwitcher/src/version.h index edbc8f70f6..4174dbf099 100644 --- a/plugins/TranslitSwitcher/src/version.h +++ b/plugins/TranslitSwitcher/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Allows you to switch a layout or transliterate or invert case of the entered text in the message window with SmileyAdd support."
#define __AUTHOR "Mataes, tico-tico, Tim"
#define __AUTHORWEB "https://miranda-ng.org/p/TranslitSwitcher"
-#define __COPYRIGHT "© 2007 Dmitry Titkov, 2011-22 Mataes, tico-tico"
+#define __COPYRIGHT "© 2007 Dmitry Titkov, 2011-23 Mataes, tico-tico"
diff --git a/plugins/UserGuide/src/stdafx.cxx b/plugins/UserGuide/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/UserGuide/src/stdafx.cxx +++ b/plugins/UserGuide/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.cpp b/plugins/UserInfoEx/src/dlg_msgbox.cpp index 37b54314a2..c97ac57579 100644 --- a/plugins/UserInfoEx/src/dlg_msgbox.cpp +++ b/plugins/UserInfoEx/src/dlg_msgbox.cpp @@ -2,7 +2,7 @@ UserinfoEx plugin for Miranda NG
Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
+© 2012-23 Miranda NG team (https://miranda-ng.org)
© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
This program is free software; you can redistribute it and/or
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.h b/plugins/UserInfoEx/src/dlg_msgbox.h index d1133cd29a..4f21859077 100644 --- a/plugins/UserInfoEx/src/dlg_msgbox.h +++ b/plugins/UserInfoEx/src/dlg_msgbox.h @@ -2,7 +2,7 @@ UserinfoEx plugin for Miranda NG
Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
+© 2012-23 Miranda NG team (https://miranda-ng.org)
© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
This program is free software; you can redistribute it and/or
diff --git a/plugins/UserInfoEx/src/stdafx.cxx b/plugins/UserInfoEx/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/UserInfoEx/src/stdafx.cxx +++ b/plugins/UserInfoEx/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/Variables/src/stdafx.cxx b/plugins/Variables/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Variables/src/stdafx.cxx +++ b/plugins/Variables/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/VoiceService/src/stdafx.cxx b/plugins/VoiceService/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/plugins/VoiceService/src/stdafx.cxx +++ b/plugins/VoiceService/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/plugins/VoiceService/src/version.h b/plugins/VoiceService/src/version.h index 1c7453b966..b80a24ec4a 100644 --- a/plugins/VoiceService/src/version.h +++ b/plugins/VoiceService/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Provide services for protocols that support voice calls."
#define __AUTHOR "Ricardo Pescuma Domenecci"
#define __AUTHORWEB "https://miranda-ng.org/p/VoiceService"
-#define __COPYRIGHT "© 2007-2009 Ricardo Pescuma Domenecci, 2020-22 Miranda NG team"
+#define __COPYRIGHT "© 2007-2009 Ricardo Pescuma Domenecci, 2020-23 Miranda NG team"
diff --git a/plugins/Watrack_MPD/src/stdafx.cxx b/plugins/Watrack_MPD/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/Watrack_MPD/src/stdafx.cxx +++ b/plugins/Watrack_MPD/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/WhenWasIt/src/dlg_handlers.cpp b/plugins/WhenWasIt/src/dlg_handlers.cpp index a9ffcc8fed..ce856c2f75 100644 --- a/plugins/WhenWasIt/src/dlg_handlers.cpp +++ b/plugins/WhenWasIt/src/dlg_handlers.cpp @@ -2,7 +2,7 @@ WhenWasIt (birthday reminder) plugin for Miranda IM
Copyright © 2006 Cristian Libotean
-Copyright (C) 2014-22 Rozhuk Ivan
+Copyright (C) 2014-23 Rozhuk Ivan
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/plugins/WhenWasIt/src/stdafx.cxx b/plugins/WhenWasIt/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/WhenWasIt/src/stdafx.cxx +++ b/plugins/WhenWasIt/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/WhoUsesMyFiles/src/options.cpp b/plugins/WhoUsesMyFiles/src/options.cpp index 57d3c9570f..750bbcab13 100644 --- a/plugins/WhoUsesMyFiles/src/options.cpp +++ b/plugins/WhoUsesMyFiles/src/options.cpp @@ -1,258 +1,258 @@ -/* -Copyright (C) 2012-22 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 ShowThePreview() -{ - if (WumfOptions.AlertFolders) { - ShowThePopup(nullptr, L"Guest", L"C:\\My Share"); - Sleep(300); - ShowThePopup(nullptr, L"Guest", L"C:\\My Share\\Photos"); - Sleep(300); - } - ShowThePopup(nullptr, L"Guest", L"C:\\Share\\My Photos\\photo.jpg"); - Sleep(300); - if (WumfOptions.AlertFolders) { - ShowThePopup(nullptr, L"User", L"C:\\My Share"); - Sleep(300); - ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies"); - Sleep(300); - } - ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies\\The Two Towers.avi"); - Sleep(300); - if (WumfOptions.AlertFolders) { - ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives"); - Sleep(300); - ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k"); - Sleep(300); - } - ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k\\setup.exe"); -} - -void DisableDelayOptions(HWND hwndDlg) -{ - CheckDlgButton(hwndDlg, IDC_DELAY_INF,BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DELAY_SET,BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DELAY_DEF,BST_CHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_INF), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SET), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_DEF), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_TX_DELAY_SEC), FALSE); -} - -void ChooseFile(HWND hwndDlg) -{ - wchar_t szFile[MAX_PATH]; szFile[0]=0; - - // Initialize OPENFILENAME - OPENFILENAME ofn = {0}; // common dialog box structure - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = hwndDlg; - ofn.lpstrFile = szFile; - ofn.nMaxFile = _countof(szFile); - ofn.lpstrFilter = L"All files (*.*)\0*.*\0Text files (*.txt)\0*.txt\0Log files (*.log)\0*.log\0\0"; - ofn.nFilterIndex = 2; - ofn.Flags = OFN_CREATEPROMPT; - // Display the Open dialog box. - if (GetSaveFileName(&ofn)) { - HANDLE hf = CreateFile(szFile,GENERIC_WRITE,0,nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, nullptr); - if (hf != INVALID_HANDLE_VALUE) { - SetDlgItemText(hwndDlg,IDC_FILE,szFile); - mir_wstrncpy(WumfOptions.LogFile, szFile, MAX_PATH); - CloseHandle(hf); - } - } - else if (CommDlgExtendedError() != 0) { - wchar_t str[256]; - mir_snwprintf(str, TranslateT("Common Dialog Error 0x%lx"), CommDlgExtendedError()); - MessageBox(hwndDlg, str, TranslateT("Error"), MB_OK | MB_ICONSTOP); - } -} - -INT_PTR CALLBACK OptionsDlgProc(HWND hwndDlg,UINT msg,WPARAM wparam,LPARAM lparam) -{ - uint16_t wControlId = LOWORD(wparam); - uint16_t wNotifyCode = HIWORD(wparam); - int seconds; - - switch(msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - CheckDlgButton(hwndDlg, IDC_COLOR_WIN, WumfOptions.UseWinColor ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_COLOR_DEF, WumfOptions.UseDefColor ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_COLOR_SET, WumfOptions.SelectColor ? BST_CHECKED : BST_UNCHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK), WumfOptions.SelectColor); - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor); - if (WumfOptions.SelectColor) { - SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack); - SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText); - } - - CheckDlgButton(hwndDlg, IDC_DELAY_INF, WumfOptions.DelayInf ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DELAY_DEF, WumfOptions.DelayDef ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_DELAY_SET, WumfOptions.DelaySet ? BST_CHECKED : BST_UNCHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet); - SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE); - //Logging & alerts - CheckDlgButton(hwndDlg, IDC_LOG_FOLDER, WumfOptions.LogFolders ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_ALERT_FOLDER, WumfOptions.AlertFolders ? BST_CHECKED : BST_UNCHECKED); - - if (WumfOptions.LogToFile) { - CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_CHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), TRUE); - EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), TRUE); - SetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile); - } - else { - CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_UNCHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), FALSE); - SetDlgItemText(hwndDlg, IDC_FILE, L""); - } - break; - - case WM_COMMAND: - switch(wNotifyCode) { - case BN_CLICKED : - switch(wControlId) { - case IDC_DELAY_SET: - case IDC_DELAY_DEF: - case IDC_DELAY_INF: - WumfOptions.DelaySet = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_SET) == BST_CHECKED); - WumfOptions.DelayDef = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_DEF) == BST_CHECKED); - WumfOptions.DelayInf = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_INF) == BST_CHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet); - SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, TRUE); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case IDC_COLOR_SET: - case IDC_COLOR_DEF: - case IDC_COLOR_WIN: - WumfOptions.SelectColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_SET) == BST_CHECKED); - WumfOptions.UseDefColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_DEF) == BST_CHECKED); - WumfOptions.UseWinColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_WIN) == BST_CHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK),WumfOptions.SelectColor); - EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor); - SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack); - SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - /* end */ - case IDC_LOG_INTO_FILE: - WumfOptions.LogToFile = (IsDlgButtonChecked(hwndDlg, IDC_LOG_INTO_FILE) == BST_CHECKED); - EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), WumfOptions.LogToFile); - EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), WumfOptions.LogToFile); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case IDC_SEL_FILE: - ChooseFile(hwndDlg); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case IDC_LOG_FOLDER: - WumfOptions.LogFolders = (IsDlgButtonChecked(hwndDlg, IDC_LOG_FOLDER) == BST_CHECKED); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case IDC_ALERT_FOLDER: - WumfOptions.AlertFolders = (IsDlgButtonChecked(hwndDlg, IDC_ALERT_FOLDER) == BST_CHECKED); - break; - case IDC_PREVIEW: - ShowThePreview(); - break; - case IDC_CONN: - CallService(MS_WUMF_CONNECTIONSSHOW, 0, 0); - break; - } - break; - - case CPN_COLOURCHANGED: - WumfOptions.ColorText = SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_GETCOLOUR,0,0); - WumfOptions.ColorBack = SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_GETCOLOUR,0,0); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case EN_CHANGE: - switch(wControlId) { - case IDC_DELAY_SEC: - seconds = GetDlgItemInt(hwndDlg, IDC_DELAY_SEC, nullptr, FALSE); - if (seconds > LIFETIME_MAX) - WumfOptions.DelaySec = LIFETIME_MAX; - else if (seconds < LIFETIME_MIN) - WumfOptions.DelaySec = LIFETIME_MIN; - else if (seconds <= LIFETIME_MAX || seconds >= LIFETIME_MIN) - WumfOptions.DelaySec = seconds; - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case IDC_FILE: - GetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile, _countof(WumfOptions.LogFile)); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - } - break; - case EN_KILLFOCUS: - switch(wControlId) { - case IDC_DELAY_SEC: - SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE); - break; - } - break; - } - break; - - case WM_NOTIFY: - switch(((LPNMHDR)lparam)->idFrom) { - case 0: - switch (((LPNMHDR)lparam)->code) { - case PSN_RESET: - LoadOptions(); - return TRUE; - - case PSN_APPLY: - g_plugin.setDword(COLOR_TEXT, (uint32_t)WumfOptions.ColorText); - g_plugin.setDword(COLOR_BACK, (uint32_t)WumfOptions.ColorBack); - g_plugin.setByte(COLOR_DEF, (uint8_t)WumfOptions.UseDefColor); - g_plugin.setByte(COLOR_WIN, (uint8_t)WumfOptions.UseWinColor); - g_plugin.setByte(COLOR_SET, (uint8_t)WumfOptions.SelectColor ); - g_plugin.setByte(DELAY_DEF, (uint8_t)WumfOptions.DelayDef); - g_plugin.setByte(DELAY_INF, (uint8_t)WumfOptions.DelayInf); - g_plugin.setByte(DELAY_SET, (uint8_t)WumfOptions.DelaySet); - g_plugin.setByte(DELAY_SEC, (uint8_t)WumfOptions.DelaySec); - g_plugin.setByte(LOG_INTO_FILE, (uint8_t)WumfOptions.LogToFile); - g_plugin.setByte(LOG_FOLDER, (uint8_t)WumfOptions.LogFolders); - g_plugin.setByte(ALERT_FOLDER, (uint8_t)WumfOptions.AlertFolders); - GetDlgItemText(hwndDlg, IDC_FILE, WumfOptions.LogFile, _countof(WumfOptions.LogFile)); - g_plugin.setWString(OPT_FILE, WumfOptions.LogFile); - } - } - break; - } - return 0; -} - -int OptionsInit(WPARAM wparam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 945000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS); - odp.szTitle.a = LPGEN("Who uses my files"); - odp.pfnDlgProc = OptionsDlgProc; - odp.szGroup.a = LPGEN("Services"); - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wparam, &odp); - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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 ShowThePreview()
+{
+ if (WumfOptions.AlertFolders) {
+ ShowThePopup(nullptr, L"Guest", L"C:\\My Share");
+ Sleep(300);
+ ShowThePopup(nullptr, L"Guest", L"C:\\My Share\\Photos");
+ Sleep(300);
+ }
+ ShowThePopup(nullptr, L"Guest", L"C:\\Share\\My Photos\\photo.jpg");
+ Sleep(300);
+ if (WumfOptions.AlertFolders) {
+ ShowThePopup(nullptr, L"User", L"C:\\My Share");
+ Sleep(300);
+ ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies");
+ Sleep(300);
+ }
+ ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies\\The Two Towers.avi");
+ Sleep(300);
+ if (WumfOptions.AlertFolders) {
+ ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives");
+ Sleep(300);
+ ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k");
+ Sleep(300);
+ }
+ ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k\\setup.exe");
+}
+
+void DisableDelayOptions(HWND hwndDlg)
+{
+ CheckDlgButton(hwndDlg, IDC_DELAY_INF,BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_SET,BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_DEF,BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_INF), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SET), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_DEF), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TX_DELAY_SEC), FALSE);
+}
+
+void ChooseFile(HWND hwndDlg)
+{
+ wchar_t szFile[MAX_PATH]; szFile[0]=0;
+
+ // Initialize OPENFILENAME
+ OPENFILENAME ofn = {0}; // common dialog box structure
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = _countof(szFile);
+ ofn.lpstrFilter = L"All files (*.*)\0*.*\0Text files (*.txt)\0*.txt\0Log files (*.log)\0*.log\0\0";
+ ofn.nFilterIndex = 2;
+ ofn.Flags = OFN_CREATEPROMPT;
+ // Display the Open dialog box.
+ if (GetSaveFileName(&ofn)) {
+ HANDLE hf = CreateFile(szFile,GENERIC_WRITE,0,nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hf != INVALID_HANDLE_VALUE) {
+ SetDlgItemText(hwndDlg,IDC_FILE,szFile);
+ mir_wstrncpy(WumfOptions.LogFile, szFile, MAX_PATH);
+ CloseHandle(hf);
+ }
+ }
+ else if (CommDlgExtendedError() != 0) {
+ wchar_t str[256];
+ mir_snwprintf(str, TranslateT("Common Dialog Error 0x%lx"), CommDlgExtendedError());
+ MessageBox(hwndDlg, str, TranslateT("Error"), MB_OK | MB_ICONSTOP);
+ }
+}
+
+INT_PTR CALLBACK OptionsDlgProc(HWND hwndDlg,UINT msg,WPARAM wparam,LPARAM lparam)
+{
+ uint16_t wControlId = LOWORD(wparam);
+ uint16_t wNotifyCode = HIWORD(wparam);
+ int seconds;
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_COLOR_WIN, WumfOptions.UseWinColor ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_COLOR_DEF, WumfOptions.UseDefColor ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_COLOR_SET, WumfOptions.SelectColor ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK), WumfOptions.SelectColor);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor);
+ if (WumfOptions.SelectColor) {
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack);
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText);
+ }
+
+ CheckDlgButton(hwndDlg, IDC_DELAY_INF, WumfOptions.DelayInf ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_DEF, WumfOptions.DelayDef ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_SET, WumfOptions.DelaySet ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet);
+ SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE);
+ //Logging & alerts
+ CheckDlgButton(hwndDlg, IDC_LOG_FOLDER, WumfOptions.LogFolders ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ALERT_FOLDER, WumfOptions.AlertFolders ? BST_CHECKED : BST_UNCHECKED);
+
+ if (WumfOptions.LogToFile) {
+ CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), TRUE);
+ SetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile);
+ }
+ else {
+ CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), FALSE);
+ SetDlgItemText(hwndDlg, IDC_FILE, L"");
+ }
+ break;
+
+ case WM_COMMAND:
+ switch(wNotifyCode) {
+ case BN_CLICKED :
+ switch(wControlId) {
+ case IDC_DELAY_SET:
+ case IDC_DELAY_DEF:
+ case IDC_DELAY_INF:
+ WumfOptions.DelaySet = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_SET) == BST_CHECKED);
+ WumfOptions.DelayDef = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_DEF) == BST_CHECKED);
+ WumfOptions.DelayInf = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_INF) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet);
+ SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, TRUE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_COLOR_SET:
+ case IDC_COLOR_DEF:
+ case IDC_COLOR_WIN:
+ WumfOptions.SelectColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_SET) == BST_CHECKED);
+ WumfOptions.UseDefColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_DEF) == BST_CHECKED);
+ WumfOptions.UseWinColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_WIN) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK),WumfOptions.SelectColor);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor);
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack);
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ /* end */
+ case IDC_LOG_INTO_FILE:
+ WumfOptions.LogToFile = (IsDlgButtonChecked(hwndDlg, IDC_LOG_INTO_FILE) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), WumfOptions.LogToFile);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), WumfOptions.LogToFile);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_SEL_FILE:
+ ChooseFile(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_LOG_FOLDER:
+ WumfOptions.LogFolders = (IsDlgButtonChecked(hwndDlg, IDC_LOG_FOLDER) == BST_CHECKED);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_ALERT_FOLDER:
+ WumfOptions.AlertFolders = (IsDlgButtonChecked(hwndDlg, IDC_ALERT_FOLDER) == BST_CHECKED);
+ break;
+ case IDC_PREVIEW:
+ ShowThePreview();
+ break;
+ case IDC_CONN:
+ CallService(MS_WUMF_CONNECTIONSSHOW, 0, 0);
+ break;
+ }
+ break;
+
+ case CPN_COLOURCHANGED:
+ WumfOptions.ColorText = SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_GETCOLOUR,0,0);
+ WumfOptions.ColorBack = SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_GETCOLOUR,0,0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case EN_CHANGE:
+ switch(wControlId) {
+ case IDC_DELAY_SEC:
+ seconds = GetDlgItemInt(hwndDlg, IDC_DELAY_SEC, nullptr, FALSE);
+ if (seconds > LIFETIME_MAX)
+ WumfOptions.DelaySec = LIFETIME_MAX;
+ else if (seconds < LIFETIME_MIN)
+ WumfOptions.DelaySec = LIFETIME_MIN;
+ else if (seconds <= LIFETIME_MAX || seconds >= LIFETIME_MIN)
+ WumfOptions.DelaySec = seconds;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_FILE:
+ GetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile, _countof(WumfOptions.LogFile));
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+ case EN_KILLFOCUS:
+ switch(wControlId) {
+ case IDC_DELAY_SEC:
+ SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lparam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lparam)->code) {
+ case PSN_RESET:
+ LoadOptions();
+ return TRUE;
+
+ case PSN_APPLY:
+ g_plugin.setDword(COLOR_TEXT, (uint32_t)WumfOptions.ColorText);
+ g_plugin.setDword(COLOR_BACK, (uint32_t)WumfOptions.ColorBack);
+ g_plugin.setByte(COLOR_DEF, (uint8_t)WumfOptions.UseDefColor);
+ g_plugin.setByte(COLOR_WIN, (uint8_t)WumfOptions.UseWinColor);
+ g_plugin.setByte(COLOR_SET, (uint8_t)WumfOptions.SelectColor );
+ g_plugin.setByte(DELAY_DEF, (uint8_t)WumfOptions.DelayDef);
+ g_plugin.setByte(DELAY_INF, (uint8_t)WumfOptions.DelayInf);
+ g_plugin.setByte(DELAY_SET, (uint8_t)WumfOptions.DelaySet);
+ g_plugin.setByte(DELAY_SEC, (uint8_t)WumfOptions.DelaySec);
+ g_plugin.setByte(LOG_INTO_FILE, (uint8_t)WumfOptions.LogToFile);
+ g_plugin.setByte(LOG_FOLDER, (uint8_t)WumfOptions.LogFolders);
+ g_plugin.setByte(ALERT_FOLDER, (uint8_t)WumfOptions.AlertFolders);
+ GetDlgItemText(hwndDlg, IDC_FILE, WumfOptions.LogFile, _countof(WumfOptions.LogFile));
+ g_plugin.setWString(OPT_FILE, WumfOptions.LogFile);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+int OptionsInit(WPARAM wparam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 945000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.szTitle.a = LPGEN("Who uses my files");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.szGroup.a = LPGEN("Services");
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wparam, &odp);
+ return 0;
+}
diff --git a/plugins/WhoUsesMyFiles/src/stdafx.cxx b/plugins/WhoUsesMyFiles/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/WhoUsesMyFiles/src/stdafx.cxx +++ b/plugins/WhoUsesMyFiles/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/WinterSpeak/src/stdafx.cxx b/plugins/WinterSpeak/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/WinterSpeak/src/stdafx.cxx +++ b/plugins/WinterSpeak/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/XSoundNotify/src/stdafx.cxx b/plugins/XSoundNotify/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/XSoundNotify/src/stdafx.cxx +++ b/plugins/XSoundNotify/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/YARelay/src/stdafx.cxx b/plugins/YARelay/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/YARelay/src/stdafx.cxx +++ b/plugins/YARelay/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ZeroNotification/src/options.cpp b/plugins/ZeroNotification/src/options.cpp index 2643116729..2e3ce755e5 100644 --- a/plugins/ZeroNotification/src/options.cpp +++ b/plugins/ZeroNotification/src/options.cpp @@ -1,137 +1,137 @@ -/* -Copyright (C) 2012-22 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" - -struct CheckBoxValues_t -{ - uint32_t style; - wchar_t *szDescr; -} -static const statusValues[] = -{ - { PF2_ONLINE, TEXT("Online") }, - { PF2_SHORTAWAY, TEXT("Away") }, - { PF2_LONGAWAY, TEXT("Not available") }, - { PF2_LIGHTDND, TEXT("Occupied") }, - { PF2_HEAVYDND, TEXT("Do not disturb") }, - { PF2_FREECHAT, TEXT("Free for chat") }, - { PF2_INVISIBLE, TEXT("Invisible") } -}; - -static void FillCheckBoxTree(HWND hwndTree, const struct CheckBoxValues_t *values, int nValues, uint32_t style) -{ - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - for (int i = 0; i < nValues; i++) { - tvis.item.lParam = values[i].style; - tvis.item.pszText = TranslateW(values[i].szDescr); - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.state = INDEXTOSTATEIMAGEMASK((style & tvis.item.lParam) != 0 ? 2 : 1); - TreeView_InsertItem(hwndTree, &tvis); - } -} - -static uint32_t MakeCheckBoxTreeFlags(HWND hwndTree) -{ - uint32_t flags = 0; - - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.hItem = TreeView_GetRoot(hwndTree); - while (tvi.hItem) { - TreeView_GetItem(hwndTree, &tvi); - if (((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2)) - flags |= tvi.lParam; - tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem); - } - return flags; -} - -static INT_PTR CALLBACK DlgProcNoSoundOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam) -{ - uint32_t test; - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); - - FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOSOUND), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoSound", DEFAULT_NOSOUND)); - FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoBlink", DEFAULT_NOBLINK)); - FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoCLCBlink", DEFAULT_NOCLCBLINK)); - return TRUE; - - case WM_COMMAND: - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case IDC_NOSOUND: - case IDC_NOBLINK: - case IDC_NOCLCBLINK: - if (((LPNMHDR)lParam)->code == NM_CLICK) { - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt); - if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti)) { - if (hti.flags & TVHT_ONITEMSTATEICON) { - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvi.hItem = hti.hItem; - TreeView_GetItem(((LPNMHDR)lParam)->hwndFrom, &tvi); - tvi.iImage = tvi.iSelectedImage = tvi.iImage == 1 ? 2 : 1; - TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom, &tvi); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - } - break; - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - g_plugin.setDword("NoSound", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOSOUND))); - g_plugin.setDword("NoBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOBLINK))); - g_plugin.setDword("NoCLCBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOCLCBLINK))); - - test = db_get_w(0, "CList", "Status", 0); - SetNotify(Proto_Status2Flag(db_get_w(0, "CList", "Status", 0))); - return TRUE; - } - break; - } - } - return FALSE; -} - -int OptionsInitialize(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 100000000; - odp.flags = ODPF_UNICODE; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NOSOUND); - odp.szTitle.w = LPGENW("Zero Notifications"); - odp.szGroup.w = LPGENW("Plugins"); - odp.pfnDlgProc = DlgProcNoSoundOpts; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+struct CheckBoxValues_t
+{
+ uint32_t style;
+ wchar_t *szDescr;
+}
+static const statusValues[] =
+{
+ { PF2_ONLINE, TEXT("Online") },
+ { PF2_SHORTAWAY, TEXT("Away") },
+ { PF2_LONGAWAY, TEXT("Not available") },
+ { PF2_LIGHTDND, TEXT("Occupied") },
+ { PF2_HEAVYDND, TEXT("Do not disturb") },
+ { PF2_FREECHAT, TEXT("Free for chat") },
+ { PF2_INVISIBLE, TEXT("Invisible") }
+};
+
+static void FillCheckBoxTree(HWND hwndTree, const struct CheckBoxValues_t *values, int nValues, uint32_t style)
+{
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ for (int i = 0; i < nValues; i++) {
+ tvis.item.lParam = values[i].style;
+ tvis.item.pszText = TranslateW(values[i].szDescr);
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((style & tvis.item.lParam) != 0 ? 2 : 1);
+ TreeView_InsertItem(hwndTree, &tvis);
+ }
+}
+
+static uint32_t MakeCheckBoxTreeFlags(HWND hwndTree)
+{
+ uint32_t flags = 0;
+
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while (tvi.hItem) {
+ TreeView_GetItem(hwndTree, &tvi);
+ if (((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2))
+ flags |= tvi.lParam;
+ tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
+ }
+ return flags;
+}
+
+static INT_PTR CALLBACK DlgProcNoSoundOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ uint32_t test;
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOSOUND), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoSound", DEFAULT_NOSOUND));
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoBlink", DEFAULT_NOBLINK));
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoCLCBlink", DEFAULT_NOCLCBLINK));
+ return TRUE;
+
+ case WM_COMMAND:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_NOSOUND:
+ case IDC_NOBLINK:
+ case IDC_NOCLCBLINK:
+ if (((LPNMHDR)lParam)->code == NM_CLICK) {
+ TVHITTESTINFO hti;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti)) {
+ if (hti.flags & TVHT_ONITEMSTATEICON) {
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = hti.hItem;
+ TreeView_GetItem(((LPNMHDR)lParam)->hwndFrom, &tvi);
+ tvi.iImage = tvi.iSelectedImage = tvi.iImage == 1 ? 2 : 1;
+ TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom, &tvi);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ g_plugin.setDword("NoSound", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOSOUND)));
+ g_plugin.setDword("NoBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOBLINK)));
+ g_plugin.setDword("NoCLCBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOCLCBLINK)));
+
+ test = db_get_w(0, "CList", "Status", 0);
+ SetNotify(Proto_Status2Flag(db_get_w(0, "CList", "Status", 0)));
+ return TRUE;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+int OptionsInitialize(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 100000000;
+ odp.flags = ODPF_UNICODE;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NOSOUND);
+ odp.szTitle.w = LPGENW("Zero Notifications");
+ odp.szGroup.w = LPGENW("Plugins");
+ odp.pfnDlgProc = DlgProcNoSoundOpts;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/ZeroNotification/src/stdafx.cxx b/plugins/ZeroNotification/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/plugins/ZeroNotification/src/stdafx.cxx +++ b/plugins/ZeroNotification/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/ZeroSwitch/src/stdafx.cxx b/plugins/ZeroSwitch/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/ZeroSwitch/src/stdafx.cxx +++ b/plugins/ZeroSwitch/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/plugins/helpers/commonheaders.h b/plugins/helpers/commonheaders.h index e8075d563a..025b0da3d9 100644 --- a/plugins/helpers/commonheaders.h +++ b/plugins/helpers/commonheaders.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/wbOSD/src/stdafx.cxx b/plugins/wbOSD/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/plugins/wbOSD/src/stdafx.cxx +++ b/plugins/wbOSD/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/CloudFile/src/stdafx.cxx b/protocols/CloudFile/src/stdafx.cxx index 090527701a..3f8c5724b4 100644 --- a/protocols/CloudFile/src/stdafx.cxx +++ b/protocols/CloudFile/src/stdafx.cxx @@ -1,20 +1,20 @@ -/* -Copyright (C) 2012-22 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" - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
ULONG FileTransferParam::hFileProcess = 1;
\ No newline at end of file diff --git a/protocols/CloudFile/src/version.h b/protocols/CloudFile/src/version.h index a06ae42f39..f1891030a5 100644 --- a/protocols/CloudFile/src/version.h +++ b/protocols/CloudFile/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Allows you to transfer files via cloud services."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/CloudFile"
-#define __COPYRIGHT "© 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2017-23 Miranda NG team"
diff --git a/protocols/ConnectionNotify/src/stdafx.cxx b/protocols/ConnectionNotify/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/ConnectionNotify/src/stdafx.cxx +++ b/protocols/ConnectionNotify/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/CurrencyRates/src/stdafx.cxx b/protocols/CurrencyRates/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/protocols/CurrencyRates/src/stdafx.cxx +++ b/protocols/CurrencyRates/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/protocols/Dummy/src/dummy.h b/protocols/Dummy/src/dummy.h index 14b4e4ea44..12071af068 100644 --- a/protocols/Dummy/src/dummy.h +++ b/protocols/Dummy/src/dummy.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Dummy/src/dummy_options.cpp b/protocols/Dummy/src/dummy_options.cpp index 4369bfe9df..3ceecc0225 100644 --- a/protocols/Dummy/src/dummy_options.cpp +++ b/protocols/Dummy/src/dummy_options.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Dummy/src/dummy_proto.cpp b/protocols/Dummy/src/dummy_proto.cpp index fe075aab59..7ec308eb3e 100644 --- a/protocols/Dummy/src/dummy_proto.cpp +++ b/protocols/Dummy/src/dummy_proto.cpp @@ -1,188 +1,188 @@ -/* -Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation 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" - -const ttemplate templates[DUMMY_PROTO_COUNT] = -{ - { LPGEN("Custom"), "", "" }, - { "AIM", "SN", LPGEN("Screen name") }, - { "Discord", "id", LPGEN("Discord ID") }, - { "EmLAN", "Nick", LPGEN("User name") }, - { "Facebook", "ID", LPGEN("Facebook ID") }, - { "GG", "UIN", LPGEN("Gadu-Gadu number") }, - { "ICQ", "UIN", LPGEN("User ID") }, - { "ICQCorp", "UIN", LPGEN("ICQ number") }, - { "IRC", "Nick", LPGEN("Nickname") }, - { "Jabber", "jid", LPGEN("JID") }, - { "MinecraftDynmap", "Nick", LPGEN("Visible name") }, - { "MRA", "e-mail", LPGEN("E-mail address") }, - { "MSN", "wlid", LPGEN("Live ID") }, - { "Omegle", "nick", LPGEN("Visible name") }, - { "Sametime", "stid", LPGEN("ID") }, - { "Skype (SkypeKit)", "sid", LPGEN("Skype name") }, - { "Skype (Classic)", "Username", LPGEN("Skype name") }, - { "Skype (Web)", "Username", LPGEN("Skype name") }, - { "Steam", "SteamID", LPGEN("Steam ID") }, - { "Tlen", "jid", LPGEN("Tlen login") }, - { "Tox", "ToxID", LPGEN("Tox ID") }, - { "Twitter", "Username", LPGEN("Username") }, - { "VK", "ID", LPGEN("VKontakte ID") }, - { "WhatsApp", "ID", LPGEN("WhatsApp ID") }, - { "XFire", "Username", LPGEN("Username") }, - { "Yahoo", "yahoo_id", LPGEN("ID") }, -}; - -void CDummyProto::SearchIdAckThread(void *targ) -{ - PROTOSEARCHRESULT psr = { 0 }; - psr.cbSize = sizeof(psr); - psr.flags = PSR_UNICODE; - psr.id.w = (wchar_t*)targ; - ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&psr); - - ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0); - - mir_free(targ); -} - -static int sttCompareProtocols(const CDummyProto *p1, const CDummyProto *p2) -{ - return mir_wstrcmp(p1->m_tszUserName, p2->m_tszUserName); -} - -CDummyProto::CDummyProto(const char *szModuleName, const wchar_t *ptszUserName) : - PROTO<CDummyProto>(szModuleName, ptszUserName) -{ - CreateProtoService(PS_CREATEACCMGRUI, &CDummyProto::SvcCreateAccMgrUI); - - msgid = 0; - - int id = getTemplateId(); - ptrA setting(id > 0 ? mir_strdup(templates[id].setting) : getStringA(DUMMY_ID_SETTING)); - if (setting != NULL) { - strncpy_s(uniqueIdText, setting, _TRUNCATE); - Proto_SetUniqueId(m_szModuleName, uniqueIdText); - } - else uniqueIdText[0] = '\0'; - - uniqueIdSetting[0] = '\0'; -} - -CDummyProto::~CDummyProto() -{ -} - -////////////////////////////////////////////////////////////////////////////// - -int CDummyProto::getTemplateId() -{ - int id = this->getByte(DUMMY_ID_TEMPLATE, -1); - if (id >= 0 && id < _countof(templates)) - return id; - - CMStringA szProto(getMStringA("AM_BaseProto")); - for (auto &it : templates) - if (!stricmp(it.name, szProto)) - return int(&it - templates); - - return 0; -} - -INT_PTR CDummyProto::GetCaps(int type, MCONTACT) -{ - switch(type) { - case PFLAGNUM_1: - return PF1_IM | PF1_BASICSEARCH | PF1_ADDSEARCHRES; - - case PFLAGNUM_2: - return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_FREECHAT; - - case PFLAGNUM_3: - return 0; - - case PFLAGNUM_4: - return PF4_AVATARS | PF4_NOAUTHDENYREASON | PF4_NOCUSTOMAUTH; - - case PFLAGNUM_5: - return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_FREECHAT; - - case PFLAG_MAXLENOFMESSAGE: - return 0; - - case PFLAG_UNIQUEIDTEXT: - if (uniqueIdSetting[0] == '\0') { - int id = getTemplateId(); - ptrW setting(id > 0 ? mir_a2u(Translate(templates[id].text)) : getWStringA(DUMMY_ID_TEXT)); - if (setting != NULL) - wcsncpy_s(uniqueIdSetting, setting, _TRUNCATE); - } - return (INT_PTR)uniqueIdSetting; - } - return 0; -} - -////////////////////////////////////////////////////////////////////////////// - -int CDummyProto::SendMsg(MCONTACT hContact, int, const char *msg) -{ - std::string message = msg; - unsigned int id = InterlockedIncrement(&this->msgid); - - if (getByte(DUMMY_KEY_ALLOW_SENDING, 0)) - ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)id); - else - ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)id, (LPARAM)TranslateT("This Dummy account has disabled sending messages. Enable it in account options.")); - return id; -} - -int CDummyProto::SetStatus(int) -{ - return 0; -} - -HANDLE CDummyProto::SearchBasic(const wchar_t* id) -{ - if (uniqueIdSetting[0] == '\0') - return nullptr; - - wchar_t *tid = mir_wstrdup(id); - ForkThread(&CDummyProto::SearchIdAckThread, tid); - return tid; -} - -MCONTACT CDummyProto::AddToList(int flags, PROTOSEARCHRESULT* psr) -{ - if (psr->id.w == nullptr) - return NULL; - - MCONTACT hContact = db_add_contact(); - Proto_AddToContact(hContact, m_szModuleName); - - if (flags & PALF_TEMPORARY) { - Contact::Hide(hContact); - Contact::RemoveFromList(hContact); - } - else if (!Contact::OnList(hContact)) { - Contact::Hide(hContact, false); - Contact::PutOnList(hContact); - } - setWString(hContact, _T2A(uniqueIdSetting), psr->id.w); - setWString(hContact, "Nick", psr->id.w); - - return hContact; -} +/*
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+const ttemplate templates[DUMMY_PROTO_COUNT] =
+{
+ { LPGEN("Custom"), "", "" },
+ { "AIM", "SN", LPGEN("Screen name") },
+ { "Discord", "id", LPGEN("Discord ID") },
+ { "EmLAN", "Nick", LPGEN("User name") },
+ { "Facebook", "ID", LPGEN("Facebook ID") },
+ { "GG", "UIN", LPGEN("Gadu-Gadu number") },
+ { "ICQ", "UIN", LPGEN("User ID") },
+ { "ICQCorp", "UIN", LPGEN("ICQ number") },
+ { "IRC", "Nick", LPGEN("Nickname") },
+ { "Jabber", "jid", LPGEN("JID") },
+ { "MinecraftDynmap", "Nick", LPGEN("Visible name") },
+ { "MRA", "e-mail", LPGEN("E-mail address") },
+ { "MSN", "wlid", LPGEN("Live ID") },
+ { "Omegle", "nick", LPGEN("Visible name") },
+ { "Sametime", "stid", LPGEN("ID") },
+ { "Skype (SkypeKit)", "sid", LPGEN("Skype name") },
+ { "Skype (Classic)", "Username", LPGEN("Skype name") },
+ { "Skype (Web)", "Username", LPGEN("Skype name") },
+ { "Steam", "SteamID", LPGEN("Steam ID") },
+ { "Tlen", "jid", LPGEN("Tlen login") },
+ { "Tox", "ToxID", LPGEN("Tox ID") },
+ { "Twitter", "Username", LPGEN("Username") },
+ { "VK", "ID", LPGEN("VKontakte ID") },
+ { "WhatsApp", "ID", LPGEN("WhatsApp ID") },
+ { "XFire", "Username", LPGEN("Username") },
+ { "Yahoo", "yahoo_id", LPGEN("ID") },
+};
+
+void CDummyProto::SearchIdAckThread(void *targ)
+{
+ PROTOSEARCHRESULT psr = { 0 };
+ psr.cbSize = sizeof(psr);
+ psr.flags = PSR_UNICODE;
+ psr.id.w = (wchar_t*)targ;
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&psr);
+
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0);
+
+ mir_free(targ);
+}
+
+static int sttCompareProtocols(const CDummyProto *p1, const CDummyProto *p2)
+{
+ return mir_wstrcmp(p1->m_tszUserName, p2->m_tszUserName);
+}
+
+CDummyProto::CDummyProto(const char *szModuleName, const wchar_t *ptszUserName) :
+ PROTO<CDummyProto>(szModuleName, ptszUserName)
+{
+ CreateProtoService(PS_CREATEACCMGRUI, &CDummyProto::SvcCreateAccMgrUI);
+
+ msgid = 0;
+
+ int id = getTemplateId();
+ ptrA setting(id > 0 ? mir_strdup(templates[id].setting) : getStringA(DUMMY_ID_SETTING));
+ if (setting != NULL) {
+ strncpy_s(uniqueIdText, setting, _TRUNCATE);
+ Proto_SetUniqueId(m_szModuleName, uniqueIdText);
+ }
+ else uniqueIdText[0] = '\0';
+
+ uniqueIdSetting[0] = '\0';
+}
+
+CDummyProto::~CDummyProto()
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int CDummyProto::getTemplateId()
+{
+ int id = this->getByte(DUMMY_ID_TEMPLATE, -1);
+ if (id >= 0 && id < _countof(templates))
+ return id;
+
+ CMStringA szProto(getMStringA("AM_BaseProto"));
+ for (auto &it : templates)
+ if (!stricmp(it.name, szProto))
+ return int(&it - templates);
+
+ return 0;
+}
+
+INT_PTR CDummyProto::GetCaps(int type, MCONTACT)
+{
+ switch(type) {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_BASICSEARCH | PF1_ADDSEARCHRES;
+
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_FREECHAT;
+
+ case PFLAGNUM_3:
+ return 0;
+
+ case PFLAGNUM_4:
+ return PF4_AVATARS | PF4_NOAUTHDENYREASON | PF4_NOCUSTOMAUTH;
+
+ case PFLAGNUM_5:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_FREECHAT;
+
+ case PFLAG_MAXLENOFMESSAGE:
+ return 0;
+
+ case PFLAG_UNIQUEIDTEXT:
+ if (uniqueIdSetting[0] == '\0') {
+ int id = getTemplateId();
+ ptrW setting(id > 0 ? mir_a2u(Translate(templates[id].text)) : getWStringA(DUMMY_ID_TEXT));
+ if (setting != NULL)
+ wcsncpy_s(uniqueIdSetting, setting, _TRUNCATE);
+ }
+ return (INT_PTR)uniqueIdSetting;
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int CDummyProto::SendMsg(MCONTACT hContact, int, const char *msg)
+{
+ std::string message = msg;
+ unsigned int id = InterlockedIncrement(&this->msgid);
+
+ if (getByte(DUMMY_KEY_ALLOW_SENDING, 0))
+ ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)id);
+ else
+ ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)id, (LPARAM)TranslateT("This Dummy account has disabled sending messages. Enable it in account options."));
+ return id;
+}
+
+int CDummyProto::SetStatus(int)
+{
+ return 0;
+}
+
+HANDLE CDummyProto::SearchBasic(const wchar_t* id)
+{
+ if (uniqueIdSetting[0] == '\0')
+ return nullptr;
+
+ wchar_t *tid = mir_wstrdup(id);
+ ForkThread(&CDummyProto::SearchIdAckThread, tid);
+ return tid;
+}
+
+MCONTACT CDummyProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
+{
+ if (psr->id.w == nullptr)
+ return NULL;
+
+ MCONTACT hContact = db_add_contact();
+ Proto_AddToContact(hContact, m_szModuleName);
+
+ if (flags & PALF_TEMPORARY) {
+ Contact::Hide(hContact);
+ Contact::RemoveFromList(hContact);
+ }
+ else if (!Contact::OnList(hContact)) {
+ Contact::Hide(hContact, false);
+ Contact::PutOnList(hContact);
+ }
+ setWString(hContact, _T2A(uniqueIdSetting), psr->id.w);
+ setWString(hContact, "Nick", psr->id.w);
+
+ return hContact;
+}
diff --git a/protocols/Dummy/src/dummy_proto.h b/protocols/Dummy/src/dummy_proto.h index b5a902cbc6..ffe2d4b9fc 100644 --- a/protocols/Dummy/src/dummy_proto.h +++ b/protocols/Dummy/src/dummy_proto.h @@ -1,53 +1,53 @@ -/* -Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation 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/>. -*/ - -#pragma once - -struct CDummyProto; - -struct CDummyProto : public PROTO<CDummyProto> -{ - CDummyProto(const char*, const wchar_t*); - ~CDummyProto(); - - //==================================================================================== - // PROTO_INTERFACE - //==================================================================================== - - INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override; - - int SendMsg(MCONTACT hContact, int flags, const char* msg) override; - - int SetStatus(int iNewStatus) override; - - HANDLE SearchBasic(const wchar_t* id) override; - - MCONTACT AddToList(int flags, PROTOSEARCHRESULT* psr) override; - - //==== Services ====================================================================== - - INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); - - void __cdecl SearchIdAckThread(void*); - - char uniqueIdText[100]; - wchar_t uniqueIdSetting[100]; - - int getTemplateId(); - - volatile unsigned int msgid; -}; +/*
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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/>.
+*/
+
+#pragma once
+
+struct CDummyProto;
+
+struct CDummyProto : public PROTO<CDummyProto>
+{
+ CDummyProto(const char*, const wchar_t*);
+ ~CDummyProto();
+
+ //====================================================================================
+ // PROTO_INTERFACE
+ //====================================================================================
+
+ INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override;
+
+ int SendMsg(MCONTACT hContact, int flags, const char* msg) override;
+
+ int SetStatus(int iNewStatus) override;
+
+ HANDLE SearchBasic(const wchar_t* id) override;
+
+ MCONTACT AddToList(int flags, PROTOSEARCHRESULT* psr) override;
+
+ //==== Services ======================================================================
+
+ INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM);
+
+ void __cdecl SearchIdAckThread(void*);
+
+ char uniqueIdText[100];
+ wchar_t uniqueIdSetting[100];
+
+ int getTemplateId();
+
+ volatile unsigned int msgid;
+};
diff --git a/protocols/Dummy/src/main.cpp b/protocols/Dummy/src/main.cpp index acb41872b4..950b4d953c 100644 --- a/protocols/Dummy/src/main.cpp +++ b/protocols/Dummy/src/main.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Dummy/src/stdafx.cxx b/protocols/Dummy/src/stdafx.cxx index 4f38935a64..23ad62d26a 100644 --- a/protocols/Dummy/src/stdafx.cxx +++ b/protocols/Dummy/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation 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" +/*
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/protocols/Dummy/src/stdafx.h b/protocols/Dummy/src/stdafx.h index ca2b5e2f57..881c816a4f 100644 --- a/protocols/Dummy/src/stdafx.h +++ b/protocols/Dummy/src/stdafx.h @@ -1,60 +1,60 @@ -/* -Copyright (c) 2014-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation 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/>. -*/ - -#pragma once - -#include <Windows.h> -#include <Shlwapi.h> -#include <Wincrypt.h> - -#include <stdio.h> -#include <malloc.h> -#include <time.h> - -#include <newpluginapi.h> -#include <m_system.h> - -#include <m_avatars.h> -#include <m_clistint.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_extraicons.h> -#include <m_file.h> -#include <m_fontservice.h> -#include <m_genmenu.h> -#include <m_hotkeys.h> -#include <m_icolib.h> -#include <m_idle.h> -#include <m_imgsrvc.h> -#include <m_json.h> -#include <m_langpack.h> -#include <m_message.h> -#include <m_netlib.h> -#include <m_options.h> -#include <m_protosvc.h> -#include <m_protoint.h> -#include <m_skin.h> -#include <m_timezones.h> -#include <m_toptoolbar.h> -#include <m_userinfo.h> -#include <m_utils.h> -#include <m_proto_listeningto.h> -#include <m_folders.h> - -#include "resource.h" -#include "dummy.h" -#include "dummy_proto.h" +/*
+Copyright (c) 2014-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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/>.
+*/
+
+#pragma once
+
+#include <Windows.h>
+#include <Shlwapi.h>
+#include <Wincrypt.h>
+
+#include <stdio.h>
+#include <malloc.h>
+#include <time.h>
+
+#include <newpluginapi.h>
+#include <m_system.h>
+
+#include <m_avatars.h>
+#include <m_clistint.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_extraicons.h>
+#include <m_file.h>
+#include <m_fontservice.h>
+#include <m_genmenu.h>
+#include <m_hotkeys.h>
+#include <m_icolib.h>
+#include <m_idle.h>
+#include <m_imgsrvc.h>
+#include <m_json.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_skin.h>
+#include <m_timezones.h>
+#include <m_toptoolbar.h>
+#include <m_userinfo.h>
+#include <m_utils.h>
+#include <m_proto_listeningto.h>
+#include <m_folders.h>
+
+#include "resource.h"
+#include "dummy.h"
+#include "dummy_proto.h"
diff --git a/protocols/Dummy/src/version.h b/protocols/Dummy/src/version.h index 83a6d2faa9..89cf1ae1c4 100644 --- a/protocols/Dummy/src/version.h +++ b/protocols/Dummy/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Dummy protocol for Miranda NG. Could be used for holding contacts and history from deprecated protocols or for creating virtual contacts."
#define __AUTHOR "Robert Pösel"
#define __AUTHORWEB "https://miranda-ng.org/p/Dummy"
-#define __COPYRIGHT "© 2014-17 Robert Pösel, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2014-17 Robert Pösel, 2017-23 Miranda NG team"
diff --git a/protocols/Facebook/src/avatars.cpp b/protocols/Facebook/src/avatars.cpp index 0ced4b1b25..0e73a9241b 100644 --- a/protocols/Facebook/src/avatars.cpp +++ b/protocols/Facebook/src/avatars.cpp @@ -1,131 +1,131 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -void FacebookProto::GetAvatarFilename(MCONTACT hContact, wchar_t *pwszFileName) -{ - wchar_t wszPath[MAX_PATH]; - mir_snwprintf(wszPath, MAX_PATH, L"%s\\%S", VARSW(L"%miranda_avatarcache%"), m_szModuleName); - CreateDirectoryTreeW(wszPath); - - CMStringW id(getMStringW(hContact, DBKEY_ID)); - mir_snwprintf(pwszFileName, MAX_PATH, L"%s\\%s.jpg", wszPath, id.c_str()); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void __cdecl FacebookProto::AvatarsUpdate(void *) -{ - NETLIBHTTPREQUEST req = {}; - req.cbSize = sizeof(req); - req.flags = NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; - req.requestType = REQUEST_GET; - - CMStringA szParams((m_bUseBigAvatars) ? "type=large" : "type=normal"); - szParams.AppendFormat("&access_token=%s", m_szAuthToken.c_str()); - - for (auto &cc : AccContacts()) { - if (Miranda_IsTerminated()) - break; - - if (!getByte(cc, "UpdateNeeded")) - continue; - - delSetting(cc, "UpdateNeeded"); - - CMStringA szUrl(FORMAT, "https://graph.facebook.com/%s/picture?%s", getMStringA(cc, DBKEY_ID).c_str(), szParams.c_str()); - req.szUrl = szUrl.GetBuffer(); - - NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(m_hNetlibUser, &req); - if (pReply == nullptr) { - debugLogA("Failed to retrieve avatar from url: %s", szUrl.c_str()); - continue; - } - - PROTO_AVATAR_INFORMATION ai; - ai.hContact = cc; - ai.format = PA_FORMAT_UNKNOWN; - GetAvatarFilename(cc, ai.filename); - - bool bSuccess = false; - if (pReply->resultCode == 200 && pReply->pData && pReply->dataLength) { - if (auto *pszHdr = Netlib_GetHeader(pReply, "Content-Type")) - ai.format = ProtoGetAvatarFormatByMimeType(pszHdr); - - if (ai.format != PA_FORMAT_UNKNOWN) { - FILE *fout = _wfopen(ai.filename, L"wb"); - if (fout) { - fwrite(pReply->pData, 1, pReply->dataLength, fout); - fclose(fout); - bSuccess = true; - } - else debugLogA("Error saving avatar to file %S", ai.filename); - } - else debugLogA("unknown avatar mime type"); - } - else debugLogA("Error %d reading avatar from url: %s", pReply->resultCode, szUrl.c_str()); - - ProtoBroadcastAck(cc, ACKTYPE_AVATAR, bSuccess ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&ai); - - Netlib_FreeHttpRequest(pReply); - } -} - -INT_PTR FacebookProto::GetAvatarInfo(WPARAM flags, LPARAM lParam) -{ - PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam; - GetAvatarFilename(pai->hContact, pai->filename); - - bool bFileExist = _waccess(pai->filename, 0) == 0; - - // if we still need to load an avatar - if ((flags & GAIF_FORCE) || !bFileExist) { - setByte(pai->hContact, "UpdateNeeded", 1); - ForkThread(&FacebookProto::AvatarsUpdate); - return GAIR_WAITFOR; - } - - return (bFileExist) ? GAIR_SUCCESS : GAIR_NOAVATAR; -} - -INT_PTR FacebookProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam) -{ - int res = 0; - - switch (wParam) { - case AF_MAXSIZE: - ((POINT *)lParam)->x = ((POINT *)lParam)->y = 128; - break; - - case AF_FORMATSUPPORTED: - res = lParam == PA_FORMAT_PNG || lParam == PA_FORMAT_GIF || lParam == PA_FORMAT_JPEG; - break; - - case AF_ENABLED: - case AF_FETCHIFPROTONOTVISIBLE: - case AF_FETCHIFCONTACTOFFLINE: - return 1; - } - - return res; -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void FacebookProto::GetAvatarFilename(MCONTACT hContact, wchar_t *pwszFileName)
+{
+ wchar_t wszPath[MAX_PATH];
+ mir_snwprintf(wszPath, MAX_PATH, L"%s\\%S", VARSW(L"%miranda_avatarcache%"), m_szModuleName);
+ CreateDirectoryTreeW(wszPath);
+
+ CMStringW id(getMStringW(hContact, DBKEY_ID));
+ mir_snwprintf(pwszFileName, MAX_PATH, L"%s\\%s.jpg", wszPath, id.c_str());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void __cdecl FacebookProto::AvatarsUpdate(void *)
+{
+ NETLIBHTTPREQUEST req = {};
+ req.cbSize = sizeof(req);
+ req.flags = NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ req.requestType = REQUEST_GET;
+
+ CMStringA szParams((m_bUseBigAvatars) ? "type=large" : "type=normal");
+ szParams.AppendFormat("&access_token=%s", m_szAuthToken.c_str());
+
+ for (auto &cc : AccContacts()) {
+ if (Miranda_IsTerminated())
+ break;
+
+ if (!getByte(cc, "UpdateNeeded"))
+ continue;
+
+ delSetting(cc, "UpdateNeeded");
+
+ CMStringA szUrl(FORMAT, "https://graph.facebook.com/%s/picture?%s", getMStringA(cc, DBKEY_ID).c_str(), szParams.c_str());
+ req.szUrl = szUrl.GetBuffer();
+
+ NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(m_hNetlibUser, &req);
+ if (pReply == nullptr) {
+ debugLogA("Failed to retrieve avatar from url: %s", szUrl.c_str());
+ continue;
+ }
+
+ PROTO_AVATAR_INFORMATION ai;
+ ai.hContact = cc;
+ ai.format = PA_FORMAT_UNKNOWN;
+ GetAvatarFilename(cc, ai.filename);
+
+ bool bSuccess = false;
+ if (pReply->resultCode == 200 && pReply->pData && pReply->dataLength) {
+ if (auto *pszHdr = Netlib_GetHeader(pReply, "Content-Type"))
+ ai.format = ProtoGetAvatarFormatByMimeType(pszHdr);
+
+ if (ai.format != PA_FORMAT_UNKNOWN) {
+ FILE *fout = _wfopen(ai.filename, L"wb");
+ if (fout) {
+ fwrite(pReply->pData, 1, pReply->dataLength, fout);
+ fclose(fout);
+ bSuccess = true;
+ }
+ else debugLogA("Error saving avatar to file %S", ai.filename);
+ }
+ else debugLogA("unknown avatar mime type");
+ }
+ else debugLogA("Error %d reading avatar from url: %s", pReply->resultCode, szUrl.c_str());
+
+ ProtoBroadcastAck(cc, ACKTYPE_AVATAR, bSuccess ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&ai);
+
+ Netlib_FreeHttpRequest(pReply);
+ }
+}
+
+INT_PTR FacebookProto::GetAvatarInfo(WPARAM flags, LPARAM lParam)
+{
+ PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam;
+ GetAvatarFilename(pai->hContact, pai->filename);
+
+ bool bFileExist = _waccess(pai->filename, 0) == 0;
+
+ // if we still need to load an avatar
+ if ((flags & GAIF_FORCE) || !bFileExist) {
+ setByte(pai->hContact, "UpdateNeeded", 1);
+ ForkThread(&FacebookProto::AvatarsUpdate);
+ return GAIR_WAITFOR;
+ }
+
+ return (bFileExist) ? GAIR_SUCCESS : GAIR_NOAVATAR;
+}
+
+INT_PTR FacebookProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam)
+{
+ int res = 0;
+
+ switch (wParam) {
+ case AF_MAXSIZE:
+ ((POINT *)lParam)->x = ((POINT *)lParam)->y = 128;
+ break;
+
+ case AF_FORMATSUPPORTED:
+ res = lParam == PA_FORMAT_PNG || lParam == PA_FORMAT_GIF || lParam == PA_FORMAT_JPEG;
+ break;
+
+ case AF_ENABLED:
+ case AF_FETCHIFPROTONOTVISIBLE:
+ case AF_FETCHIFCONTACTOFFLINE:
+ return 1;
+ }
+
+ return res;
+}
diff --git a/protocols/Facebook/src/db.h b/protocols/Facebook/src/db.h index eb31b19ef0..8095cfad65 100644 --- a/protocols/Facebook/src/db.h +++ b/protocols/Facebook/src/db.h @@ -1,44 +1,44 @@ -/* - -Facebook plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -#define MODULENAME "Facebook" - -// Contact DB keys -#define DBKEY_LOGIN "Email" -#define DBKEY_ID "ID" -#define DBKEY_SID "SID" -#define DBKEY_NICK "Nick" -#define DBKEY_PASS "Password" -#define DBKEY_CLIENT_ID "ClientID" -#define DBKEY_DEVICE_ID "DeviceID" -#define DBKEY_AVATAR "Avatar" -#define DBKEY_CONTACT_TYPE "ContactType" -#define DBKEY_TOKEN "Token" -#define DBKEY_SYNC_TOKEN "SyncToken" - -// Account DB keys -#define DBKEY_SET_MIRANDA_STATUS "SetMirandaStatus" - -// Hidden account DB keys (can't be changed through GUI) -#define DBKEY_LOCALE "Locale" // [HIDDEN] - (string) en_US, cs_CZ, etc. (requires restart to apply) +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#define MODULENAME "Facebook"
+
+// Contact DB keys
+#define DBKEY_LOGIN "Email"
+#define DBKEY_ID "ID"
+#define DBKEY_SID "SID"
+#define DBKEY_NICK "Nick"
+#define DBKEY_PASS "Password"
+#define DBKEY_CLIENT_ID "ClientID"
+#define DBKEY_DEVICE_ID "DeviceID"
+#define DBKEY_AVATAR "Avatar"
+#define DBKEY_CONTACT_TYPE "ContactType"
+#define DBKEY_TOKEN "Token"
+#define DBKEY_SYNC_TOKEN "SyncToken"
+
+// Account DB keys
+#define DBKEY_SET_MIRANDA_STATUS "SetMirandaStatus"
+
+// Hidden account DB keys (can't be changed through GUI)
+#define DBKEY_LOCALE "Locale" // [HIDDEN] - (string) en_US, cs_CZ, etc. (requires restart to apply)
diff --git a/protocols/Facebook/src/dialogs.cpp b/protocols/Facebook/src/dialogs.cpp index 4e9e215b86..1938c94448 100644 --- a/protocols/Facebook/src/dialogs.cpp +++ b/protocols/Facebook/src/dialogs.cpp @@ -1,78 +1,78 @@ -/* - -Facebook plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -INT_PTR CALLBACK FBAccountProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) -{ - FacebookProto *proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); - - switch (message) { - - case WM_INITDIALOG: - { - TranslateDialogDefault(hwnd); - - proto = reinterpret_cast<FacebookProto*>(lparam); - SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam); - - ptrA login(db_get_sa(0, proto->ModuleName(), DBKEY_LOGIN)); - if (login != nullptr) - SetDlgItemTextA(hwnd, IDC_UN, login); - - ptrA password(db_get_sa(0, proto->ModuleName(), DBKEY_PASS)); - if (password != nullptr) - SetDlgItemTextA(hwnd, IDC_PW, password); - - //if (!proto->isOffline()) { - // SendDlgItemMessage(hwnd, IDC_UN, EM_SETREADONLY, 1, 0); - // SendDlgItemMessage(hwnd, IDC_PW, EM_SETREADONLY, 1, 0); - //} - return TRUE; - } - case WM_COMMAND: - if (HIWORD(wparam) == EN_CHANGE && reinterpret_cast<HWND>(lparam) == GetFocus()) { - switch (LOWORD(wparam)) { - case IDC_UN: - case IDC_PW: - SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0); - } - } - break; - - case WM_NOTIFY: - if (reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY) { - char str[128]; - - GetDlgItemTextA(hwnd, IDC_UN, str, _countof(str)); - db_set_s(0, proto->ModuleName(), DBKEY_LOGIN, str); - - GetDlgItemTextA(hwnd, IDC_PW, str, _countof(str)); - db_set_s(0, proto->ModuleName(), DBKEY_PASS, str); - return TRUE; - } - break; - - } - - return FALSE; -} +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+INT_PTR CALLBACK FBAccountProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ FacebookProto *proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+
+ switch (message) {
+
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<FacebookProto*>(lparam);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
+
+ ptrA login(db_get_sa(0, proto->ModuleName(), DBKEY_LOGIN));
+ if (login != nullptr)
+ SetDlgItemTextA(hwnd, IDC_UN, login);
+
+ ptrA password(db_get_sa(0, proto->ModuleName(), DBKEY_PASS));
+ if (password != nullptr)
+ SetDlgItemTextA(hwnd, IDC_PW, password);
+
+ //if (!proto->isOffline()) {
+ // SendDlgItemMessage(hwnd, IDC_UN, EM_SETREADONLY, 1, 0);
+ // SendDlgItemMessage(hwnd, IDC_PW, EM_SETREADONLY, 1, 0);
+ //}
+ return TRUE;
+ }
+ case WM_COMMAND:
+ if (HIWORD(wparam) == EN_CHANGE && reinterpret_cast<HWND>(lparam) == GetFocus()) {
+ switch (LOWORD(wparam)) {
+ case IDC_UN:
+ case IDC_PW:
+ SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY) {
+ char str[128];
+
+ GetDlgItemTextA(hwnd, IDC_UN, str, _countof(str));
+ db_set_s(0, proto->ModuleName(), DBKEY_LOGIN, str);
+
+ GetDlgItemTextA(hwnd, IDC_PW, str, _countof(str));
+ db_set_s(0, proto->ModuleName(), DBKEY_PASS, str);
+ return TRUE;
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
diff --git a/protocols/Facebook/src/dialogs.h b/protocols/Facebook/src/dialogs.h index 23d821c697..5dcaeb667b 100644 --- a/protocols/Facebook/src/dialogs.h +++ b/protocols/Facebook/src/dialogs.h @@ -3,7 +3,7 @@ Facebook plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Facebook/src/groupchats.cpp b/protocols/Facebook/src/groupchats.cpp index 92266e3e2d..b43e05696a 100644 --- a/protocols/Facebook/src/groupchats.cpp +++ b/protocols/Facebook/src/groupchats.cpp @@ -1,255 +1,255 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Invitation dialog - -class CGroupchatInviteDlg : public CFBDlgBase -{ - CCtrlClc m_clc; - SESSION_INFO *m_si; - - void FilterList(CCtrlClc *) - { - for (auto &hContact : Contacts()) { - char *proto = Proto_GetBaseAccountName(hContact); - if (mir_strcmp(proto, m_proto->m_szModuleName) || m_proto->isChatRoom(hContact)) - if (HANDLE hItem = m_clc.FindContact(hContact)) - m_clc.DeleteItem(hItem); - } - } - - void ResetListOptions(CCtrlClc *) - { - m_clc.SetHideEmptyGroups(1); - m_clc.SetHideOfflineRoot(1); - m_clc.SetOfflineModes(PF2_NONE); - } - -public: - CGroupchatInviteDlg(FacebookProto *ppro, SESSION_INFO *si) : - CFBDlgBase(ppro, IDD_GROUPCHAT_INVITE), - m_si(si), - m_clc(this, IDC_CLIST) - { - m_clc.OnNewContact = - m_clc.OnListRebuilt = Callback(this, &CGroupchatInviteDlg::FilterList); - m_clc.OnOptionsChanged = Callback(this, &CGroupchatInviteDlg::ResetListOptions); - } - - bool OnInitDialog() override - { - SetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE, - GetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES); - m_clc.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0); - - ResetListOptions(&m_clc); - FilterList(&m_clc); - return true; - } - - bool OnApply() override - { - JSONNode list(JSON_ARRAY); - - for (auto &hContact : m_proto->AccContacts()) { - if (m_proto->isChatRoom(hContact)) - continue; - - if (HANDLE hItem = m_clc.FindContact(hContact)) { - if (m_clc.GetCheck(hItem)) { - JSONNode user; user << CHAR_PARAM("type", "id") << CHAR_PARAM("id", m_proto->getMStringA(hContact, DBKEY_ID)); - list << user; - } - } - } - - auto *pReq = m_proto->CreateRequest(FB_API_URL_PARTS, "addMembers", "POST"); - pReq << CHAR_PARAM("to", list.write().c_str()) << WCHAR_PARAM("id", CMStringW(FORMAT, L"t_%s", m_si->ptszID)); - pReq->CalcSig(); - - JsonReply reply(m_proto->ExecuteRequest(pReq)); - return true; - } -}; - -void FacebookProto::Chat_InviteUser(SESSION_INFO *si) -{ - CGroupchatInviteDlg dlg(this, si); - if (si->pDlg) - dlg.SetParent(((CDlgBase *)si->pDlg)->GetHwnd()); - dlg.DoModal(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Group chats - -enum ChatMenuItems -{ - IDM_INVITE = 10, IDM_LEAVE, - - IDM_KICK = 20 -}; - -static gc_item sttLogListItems[] = -{ - { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM }, - { nullptr, 0, MENU_SEPARATOR }, - { LPGENW("&Leave/destroy chat"), IDM_LEAVE, MENU_ITEM }, -}; - -static gc_item sttNickListItems[] = -{ - { LPGENW("&Kick user"), IDM_KICK, MENU_ITEM }, -}; - -int FacebookProto::GroupchatMenuHook(WPARAM, LPARAM lParam) -{ - GCMENUITEMS *gcmi = (GCMENUITEMS *)lParam; - if (gcmi == nullptr) - return 0; - - if (mir_strcmpi(gcmi->pszModule, m_szModuleName)) - return 0; - - if (SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule)) { - if (gcmi->Type == MENU_ON_LOG) - Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin); - if (gcmi->Type == MENU_ON_NICKLIST) - Chat_AddMenuItems(gcmi->hMenu, _countof(sttNickListItems), sttNickListItems, &g_plugin); - } - - return 0; -} - -int FacebookProto::GroupchatEventHook(WPARAM, LPARAM lParam) -{ - GCHOOK *gch = (GCHOOK *)lParam; - if (gch == nullptr) - return 0; - - if (mir_strcmpi(gch->si->pszModule, m_szModuleName)) - return 0; - - SESSION_INFO *si = g_chatApi.SM_FindSession(gch->si->ptszID, gch->si->pszModule); - if (si == nullptr) - return 1; - - switch (gch->iType) { - case GC_USER_MESSAGE: - rtrimw(gch->ptszText); - if (!mir_wstrlen(gch->ptszText)) - break; - - if (m_bOnline) { - wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText); - Chat_UnescapeTags(wszText); - - int mid = SendMsg(si->hContact, 0, T2Utf(wszText)); - - mir_cslock lck(m_csOwnMessages); - for (auto &msg : arOwnMessages) - if (msg->reqId == mid) - msg->wszText = wszText; - } - break; - - case GC_USER_PRIVMESS: - Chat_SendPrivateMessage(gch); - break; - - case GC_USER_LOGMENU: - Chat_ProcessLogMenu(si, gch); - break; - - case GC_USER_NICKLISTMENU: - Chat_ProcessNickMenu(si, gch); - break; - } - - return 1; -} - -void FacebookProto::Chat_ProcessLogMenu(SESSION_INFO *si, GCHOOK *gch) -{ - switch (gch->dwData) { - case IDM_INVITE: - Chat_InviteUser(si); - break; - - case IDM_LEAVE: - Chat_Leave(si); - break; - } -} - -void FacebookProto::Chat_ProcessNickMenu(SESSION_INFO *si, GCHOOK *gch) -{ - switch (gch->dwData) { - case IDM_KICK: - Chat_KickUser(si, gch->ptszUID); - break; - } -} - -void FacebookProto::Chat_SendPrivateMessage(GCHOOK *gch) -{ - auto *pUser = FindUser(_wtoi64(gch->ptszUID)); - if (pUser == nullptr) { - pUser = AddContact(gch->ptszUID, true); - setWString(pUser->hContact, "Nick", gch->ptszNick); - db_set_b(pUser->hContact, "CList", "Hidden", 1); - db_set_dw(pUser->hContact, "Ignore", "Mask1", 0); - } - - CallService(MS_MSG_SENDMESSAGE, pUser->hContact, 0); -} - -int FacebookProto::Chat_KickUser(SESSION_INFO *si, const wchar_t *pwszUid) -{ - auto *pReq = CreateRequest(FB_API_URL_PARTS, "removeMembers", "DELETE"); - pReq << WCHAR_PARAM("id", CMStringW(FORMAT, L"t_%s", si->ptszID)); - if (pwszUid != nullptr) { - JSONNode list(JSON_ARRAY); - JSONNode user; user << CHAR_PARAM("type", "id") << WCHAR_PARAM("id", pwszUid); - list << user; - pReq << CHAR_PARAM("to", list.write().c_str()); - } - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - return reply.error(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void __cdecl DestroyRoomThread(SESSION_INFO *si) -{ - ::Sleep(100); - Chat_Terminate(si->pszModule, si->ptszID, true); -} - -void FacebookProto::Chat_Leave(SESSION_INFO *si) -{ - if (Chat_KickUser(si, nullptr) == 0) - mir_forkThread<SESSION_INFO>(DestroyRoomThread, si); -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Invitation dialog
+
+class CGroupchatInviteDlg : public CFBDlgBase
+{
+ CCtrlClc m_clc;
+ SESSION_INFO *m_si;
+
+ void FilterList(CCtrlClc *)
+ {
+ for (auto &hContact : Contacts()) {
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (mir_strcmp(proto, m_proto->m_szModuleName) || m_proto->isChatRoom(hContact))
+ if (HANDLE hItem = m_clc.FindContact(hContact))
+ m_clc.DeleteItem(hItem);
+ }
+ }
+
+ void ResetListOptions(CCtrlClc *)
+ {
+ m_clc.SetHideEmptyGroups(1);
+ m_clc.SetHideOfflineRoot(1);
+ m_clc.SetOfflineModes(PF2_NONE);
+ }
+
+public:
+ CGroupchatInviteDlg(FacebookProto *ppro, SESSION_INFO *si) :
+ CFBDlgBase(ppro, IDD_GROUPCHAT_INVITE),
+ m_si(si),
+ m_clc(this, IDC_CLIST)
+ {
+ m_clc.OnNewContact =
+ m_clc.OnListRebuilt = Callback(this, &CGroupchatInviteDlg::FilterList);
+ m_clc.OnOptionsChanged = Callback(this, &CGroupchatInviteDlg::ResetListOptions);
+ }
+
+ bool OnInitDialog() override
+ {
+ SetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
+ m_clc.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0);
+
+ ResetListOptions(&m_clc);
+ FilterList(&m_clc);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ JSONNode list(JSON_ARRAY);
+
+ for (auto &hContact : m_proto->AccContacts()) {
+ if (m_proto->isChatRoom(hContact))
+ continue;
+
+ if (HANDLE hItem = m_clc.FindContact(hContact)) {
+ if (m_clc.GetCheck(hItem)) {
+ JSONNode user; user << CHAR_PARAM("type", "id") << CHAR_PARAM("id", m_proto->getMStringA(hContact, DBKEY_ID));
+ list << user;
+ }
+ }
+ }
+
+ auto *pReq = m_proto->CreateRequest(FB_API_URL_PARTS, "addMembers", "POST");
+ pReq << CHAR_PARAM("to", list.write().c_str()) << WCHAR_PARAM("id", CMStringW(FORMAT, L"t_%s", m_si->ptszID));
+ pReq->CalcSig();
+
+ JsonReply reply(m_proto->ExecuteRequest(pReq));
+ return true;
+ }
+};
+
+void FacebookProto::Chat_InviteUser(SESSION_INFO *si)
+{
+ CGroupchatInviteDlg dlg(this, si);
+ if (si->pDlg)
+ dlg.SetParent(((CDlgBase *)si->pDlg)->GetHwnd());
+ dlg.DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Group chats
+
+enum ChatMenuItems
+{
+ IDM_INVITE = 10, IDM_LEAVE,
+
+ IDM_KICK = 20
+};
+
+static gc_item sttLogListItems[] =
+{
+ { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM },
+ { nullptr, 0, MENU_SEPARATOR },
+ { LPGENW("&Leave/destroy chat"), IDM_LEAVE, MENU_ITEM },
+};
+
+static gc_item sttNickListItems[] =
+{
+ { LPGENW("&Kick user"), IDM_KICK, MENU_ITEM },
+};
+
+int FacebookProto::GroupchatMenuHook(WPARAM, LPARAM lParam)
+{
+ GCMENUITEMS *gcmi = (GCMENUITEMS *)lParam;
+ if (gcmi == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
+ return 0;
+
+ if (SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule)) {
+ if (gcmi->Type == MENU_ON_LOG)
+ Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin);
+ if (gcmi->Type == MENU_ON_NICKLIST)
+ Chat_AddMenuItems(gcmi->hMenu, _countof(sttNickListItems), sttNickListItems, &g_plugin);
+ }
+
+ return 0;
+}
+
+int FacebookProto::GroupchatEventHook(WPARAM, LPARAM lParam)
+{
+ GCHOOK *gch = (GCHOOK *)lParam;
+ if (gch == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gch->si->pszModule, m_szModuleName))
+ return 0;
+
+ SESSION_INFO *si = g_chatApi.SM_FindSession(gch->si->ptszID, gch->si->pszModule);
+ if (si == nullptr)
+ return 1;
+
+ switch (gch->iType) {
+ case GC_USER_MESSAGE:
+ rtrimw(gch->ptszText);
+ if (!mir_wstrlen(gch->ptszText))
+ break;
+
+ if (m_bOnline) {
+ wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText);
+ Chat_UnescapeTags(wszText);
+
+ int mid = SendMsg(si->hContact, 0, T2Utf(wszText));
+
+ mir_cslock lck(m_csOwnMessages);
+ for (auto &msg : arOwnMessages)
+ if (msg->reqId == mid)
+ msg->wszText = wszText;
+ }
+ break;
+
+ case GC_USER_PRIVMESS:
+ Chat_SendPrivateMessage(gch);
+ break;
+
+ case GC_USER_LOGMENU:
+ Chat_ProcessLogMenu(si, gch);
+ break;
+
+ case GC_USER_NICKLISTMENU:
+ Chat_ProcessNickMenu(si, gch);
+ break;
+ }
+
+ return 1;
+}
+
+void FacebookProto::Chat_ProcessLogMenu(SESSION_INFO *si, GCHOOK *gch)
+{
+ switch (gch->dwData) {
+ case IDM_INVITE:
+ Chat_InviteUser(si);
+ break;
+
+ case IDM_LEAVE:
+ Chat_Leave(si);
+ break;
+ }
+}
+
+void FacebookProto::Chat_ProcessNickMenu(SESSION_INFO *si, GCHOOK *gch)
+{
+ switch (gch->dwData) {
+ case IDM_KICK:
+ Chat_KickUser(si, gch->ptszUID);
+ break;
+ }
+}
+
+void FacebookProto::Chat_SendPrivateMessage(GCHOOK *gch)
+{
+ auto *pUser = FindUser(_wtoi64(gch->ptszUID));
+ if (pUser == nullptr) {
+ pUser = AddContact(gch->ptszUID, true);
+ setWString(pUser->hContact, "Nick", gch->ptszNick);
+ db_set_b(pUser->hContact, "CList", "Hidden", 1);
+ db_set_dw(pUser->hContact, "Ignore", "Mask1", 0);
+ }
+
+ CallService(MS_MSG_SENDMESSAGE, pUser->hContact, 0);
+}
+
+int FacebookProto::Chat_KickUser(SESSION_INFO *si, const wchar_t *pwszUid)
+{
+ auto *pReq = CreateRequest(FB_API_URL_PARTS, "removeMembers", "DELETE");
+ pReq << WCHAR_PARAM("id", CMStringW(FORMAT, L"t_%s", si->ptszID));
+ if (pwszUid != nullptr) {
+ JSONNode list(JSON_ARRAY);
+ JSONNode user; user << CHAR_PARAM("type", "id") << WCHAR_PARAM("id", pwszUid);
+ list << user;
+ pReq << CHAR_PARAM("to", list.write().c_str());
+ }
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ return reply.error();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void __cdecl DestroyRoomThread(SESSION_INFO *si)
+{
+ ::Sleep(100);
+ Chat_Terminate(si->pszModule, si->ptszID, true);
+}
+
+void FacebookProto::Chat_Leave(SESSION_INFO *si)
+{
+ if (Chat_KickUser(si, nullptr) == 0)
+ mir_forkThread<SESSION_INFO>(DestroyRoomThread, si);
+}
diff --git a/protocols/Facebook/src/http.cpp b/protocols/Facebook/src/http.cpp index 7bc6e5082c..6f520c0486 100644 --- a/protocols/Facebook/src/http.cpp +++ b/protocols/Facebook/src/http.cpp @@ -1,185 +1,185 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CompareParams(const AsyncHttpRequest::Param *p1, const AsyncHttpRequest::Param *p2) -{ - return strcmp(p1->key, p2->key); -} - -AsyncHttpRequest::AsyncHttpRequest() : - params(5, CompareParams) -{ -} - -void AsyncHttpRequest::CalcSig() -{ - CMStringA buf; - for (auto &it : params) - buf.AppendFormat("%s=%s", it->key.c_str(), it->val.c_str()); - - buf.Append(FB_API_SECRET); - - char szHash[33]; - uint8_t digest[16]; - mir_md5_hash((uint8_t*)buf.c_str(), buf.GetLength(), digest); - bin2hex(digest, sizeof(digest), szHash); - this << CHAR_PARAM("sig", szHash); - - for (auto &it : params) { - if (!m_szParam.IsEmpty()) - m_szParam.AppendChar('&'); - m_szParam.AppendFormat("%s=%s", it->key.c_str(), mir_urlEncode(it->val.c_str()).c_str()); - } -} - -AsyncHttpRequest* operator<<(AsyncHttpRequest *pReq, const CHAR_PARAM ¶m) -{ - pReq->params.insert(new AsyncHttpRequest::Param(param.szName, param.szValue)); - return pReq; -} - -AsyncHttpRequest* operator<<(AsyncHttpRequest *pReq, const INT_PARAM ¶m) -{ - char value[40]; - itoa(param.iValue, value, 10); - pReq->params.insert(new AsyncHttpRequest::Param(param.szName, value)); - return pReq; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply) -{ - if (pReply == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = pReply->resultCode; - if (m_errorCode != 200) - return; - - m_root = json_parse(pReply->pData); - if (m_root == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = (*m_root)["error_code"].as_int(); -} - -JsonReply::~JsonReply() -{ - json_delete(m_root); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -AsyncHttpRequest* FacebookProto::CreateRequest(const char *szUrl, const char *szName, const char *szMethod) -{ - AsyncHttpRequest *pReq = new AsyncHttpRequest(); - pReq->m_szUrl = szUrl; - pReq->requestType = REQUEST_POST; - pReq << CHAR_PARAM("api_key", FB_API_KEY) - << CHAR_PARAM("device_id", m_szDeviceID) - << CHAR_PARAM("fb_api_req_friendly_name", szName) - << CHAR_PARAM("format", "json") - << CHAR_PARAM("method", szMethod); - - CMStringA szLocale = getMStringA(DBKEY_LOCALE); - if (szLocale.IsEmpty()) - szLocale = "en"; - pReq << CHAR_PARAM("locale", szLocale); - - if (!m_szAuthToken.IsEmpty()) { - pReq->flags |= NLHRF_NODUMPHEADERS; - pReq->AddHeader("Authorization", "OAuth " + m_szAuthToken); - } - - pReq->AddHeader("User-Agent", FB_API_AGENT); - pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); - return pReq; -} - -AsyncHttpRequest* FacebookProto::CreateRequestGQL(int64_t query_id) { - const char* szName; - - switch (query_id) { - case FB_API_QUERY_CONTACT: - szName = "UsersQuery"; - break; - case FB_API_QUERY_CONTACTS: - szName = "FetchContactsFullQuery"; - break; - case FB_API_QUERY_CONTACTS_AFTER: - szName = "FetchContactsFullWithAfterQuery"; - break; - case FB_API_QUERY_CONTACTS_DELTA: - szName = "FetchContactsDeltaQuery"; - break; - case FB_API_QUERY_STICKER: - szName = "FetchStickersWithPreviewsQuery"; - break; - case FB_API_QUERY_THREAD: - szName = "ThreadQuery"; - break; - case FB_API_QUERY_SEQ_ID: - case FB_API_QUERY_THREADS: - szName = "ThreadListQuery"; - break; - case FB_API_QUERY_XMA: - szName = "XMAQuery"; - break; - default: - return nullptr; - } - - AsyncHttpRequest* pReq = CreateRequest(FB_API_URL_GQL, szName, "get"); - pReq << INT64_PARAM("query_id", query_id); - return pReq; -} - -NETLIBHTTPREQUEST* FacebookProto::ExecuteRequest(AsyncHttpRequest *pReq) -{ - CMStringA str; - - pReq->flags |= NLHRF_HTTP11; - pReq->szUrl = pReq->m_szUrl.GetBuffer(); - if (!pReq->m_szParam.IsEmpty()) { - if (pReq->requestType == REQUEST_GET) { - str.Format("%s?%s", pReq->m_szUrl.c_str(), pReq->m_szParam.c_str()); - pReq->szUrl = str.GetBuffer(); - } - else { - pReq->dataLength = pReq->m_szParam.GetLength(); - pReq->pData = mir_strdup(pReq->m_szParam); - } - } - - debugLogA("Executing request:\n%s", pReq->szUrl); - - NETLIBHTTPREQUEST *reply = Netlib_HttpTransaction(m_hNetlibUser, pReq); - delete pReq; - return reply; -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CompareParams(const AsyncHttpRequest::Param *p1, const AsyncHttpRequest::Param *p2)
+{
+ return strcmp(p1->key, p2->key);
+}
+
+AsyncHttpRequest::AsyncHttpRequest() :
+ params(5, CompareParams)
+{
+}
+
+void AsyncHttpRequest::CalcSig()
+{
+ CMStringA buf;
+ for (auto &it : params)
+ buf.AppendFormat("%s=%s", it->key.c_str(), it->val.c_str());
+
+ buf.Append(FB_API_SECRET);
+
+ char szHash[33];
+ uint8_t digest[16];
+ mir_md5_hash((uint8_t*)buf.c_str(), buf.GetLength(), digest);
+ bin2hex(digest, sizeof(digest), szHash);
+ this << CHAR_PARAM("sig", szHash);
+
+ for (auto &it : params) {
+ if (!m_szParam.IsEmpty())
+ m_szParam.AppendChar('&');
+ m_szParam.AppendFormat("%s=%s", it->key.c_str(), mir_urlEncode(it->val.c_str()).c_str());
+ }
+}
+
+AsyncHttpRequest* operator<<(AsyncHttpRequest *pReq, const CHAR_PARAM ¶m)
+{
+ pReq->params.insert(new AsyncHttpRequest::Param(param.szName, param.szValue));
+ return pReq;
+}
+
+AsyncHttpRequest* operator<<(AsyncHttpRequest *pReq, const INT_PARAM ¶m)
+{
+ char value[40];
+ itoa(param.iValue, value, 10);
+ pReq->params.insert(new AsyncHttpRequest::Param(param.szName, value));
+ return pReq;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = pReply->resultCode;
+ if (m_errorCode != 200)
+ return;
+
+ m_root = json_parse(pReply->pData);
+ if (m_root == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = (*m_root)["error_code"].as_int();
+}
+
+JsonReply::~JsonReply()
+{
+ json_delete(m_root);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpRequest* FacebookProto::CreateRequest(const char *szUrl, const char *szName, const char *szMethod)
+{
+ AsyncHttpRequest *pReq = new AsyncHttpRequest();
+ pReq->m_szUrl = szUrl;
+ pReq->requestType = REQUEST_POST;
+ pReq << CHAR_PARAM("api_key", FB_API_KEY)
+ << CHAR_PARAM("device_id", m_szDeviceID)
+ << CHAR_PARAM("fb_api_req_friendly_name", szName)
+ << CHAR_PARAM("format", "json")
+ << CHAR_PARAM("method", szMethod);
+
+ CMStringA szLocale = getMStringA(DBKEY_LOCALE);
+ if (szLocale.IsEmpty())
+ szLocale = "en";
+ pReq << CHAR_PARAM("locale", szLocale);
+
+ if (!m_szAuthToken.IsEmpty()) {
+ pReq->flags |= NLHRF_NODUMPHEADERS;
+ pReq->AddHeader("Authorization", "OAuth " + m_szAuthToken);
+ }
+
+ pReq->AddHeader("User-Agent", FB_API_AGENT);
+ pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ return pReq;
+}
+
+AsyncHttpRequest* FacebookProto::CreateRequestGQL(int64_t query_id) {
+ const char* szName;
+
+ switch (query_id) {
+ case FB_API_QUERY_CONTACT:
+ szName = "UsersQuery";
+ break;
+ case FB_API_QUERY_CONTACTS:
+ szName = "FetchContactsFullQuery";
+ break;
+ case FB_API_QUERY_CONTACTS_AFTER:
+ szName = "FetchContactsFullWithAfterQuery";
+ break;
+ case FB_API_QUERY_CONTACTS_DELTA:
+ szName = "FetchContactsDeltaQuery";
+ break;
+ case FB_API_QUERY_STICKER:
+ szName = "FetchStickersWithPreviewsQuery";
+ break;
+ case FB_API_QUERY_THREAD:
+ szName = "ThreadQuery";
+ break;
+ case FB_API_QUERY_SEQ_ID:
+ case FB_API_QUERY_THREADS:
+ szName = "ThreadListQuery";
+ break;
+ case FB_API_QUERY_XMA:
+ szName = "XMAQuery";
+ break;
+ default:
+ return nullptr;
+ }
+
+ AsyncHttpRequest* pReq = CreateRequest(FB_API_URL_GQL, szName, "get");
+ pReq << INT64_PARAM("query_id", query_id);
+ return pReq;
+}
+
+NETLIBHTTPREQUEST* FacebookProto::ExecuteRequest(AsyncHttpRequest *pReq)
+{
+ CMStringA str;
+
+ pReq->flags |= NLHRF_HTTP11;
+ pReq->szUrl = pReq->m_szUrl.GetBuffer();
+ if (!pReq->m_szParam.IsEmpty()) {
+ if (pReq->requestType == REQUEST_GET) {
+ str.Format("%s?%s", pReq->m_szUrl.c_str(), pReq->m_szParam.c_str());
+ pReq->szUrl = str.GetBuffer();
+ }
+ else {
+ pReq->dataLength = pReq->m_szParam.GetLength();
+ pReq->pData = mir_strdup(pReq->m_szParam);
+ }
+ }
+
+ debugLogA("Executing request:\n%s", pReq->szUrl);
+
+ NETLIBHTTPREQUEST *reply = Netlib_HttpTransaction(m_hNetlibUser, pReq);
+ delete pReq;
+ return reply;
+}
diff --git a/protocols/Facebook/src/main.cpp b/protocols/Facebook/src/main.cpp index 9e308016fc..2b9215e2ed 100644 --- a/protocols/Facebook/src/main.cpp +++ b/protocols/Facebook/src/main.cpp @@ -1,72 +1,72 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -#pragma comment(lib, "Rpcrt4.lib") - -CMPlugin g_plugin; - -bool g_bMessageState; - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {86033E58-A1E3-43AD-AE8E-305E15E72A91} - { 0xee0543fb, 0x711d, 0x4ac8, { 0xb6, 0xc0, 0x1d, 0xda, 0x48, 0x38, 0x10, 0x7e }} -}; - -CMPlugin::CMPlugin() : - ACCPROTOPLUGIN<FacebookProto>(MODULENAME, pluginInfoEx) -{ - SetUniqueId(DBKEY_ID); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Interface information - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; - -///////////////////////////////////////////////////////////////////////////////////////// -// Load - -static int OnModuleLoaded(WPARAM, LPARAM) -{ - g_bMessageState = ServiceExists(MS_MESSAGESTATE_UPDATE) != 0; - return 0; -} - -int CMPlugin::Load() -{ - HookEvent(ME_SYSTEM_MODULELOAD, OnModuleLoaded); - HookEvent(ME_SYSTEM_MODULEUNLOAD, OnModuleLoaded); - HookEvent(ME_SYSTEM_MODULESLOADED, OnModuleLoaded); - - // Initialize random generator (used only as fallback in utils) - return 0; -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+#pragma comment(lib, "Rpcrt4.lib")
+
+CMPlugin g_plugin;
+
+bool g_bMessageState;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {86033E58-A1E3-43AD-AE8E-305E15E72A91}
+ { 0xee0543fb, 0x711d, 0x4ac8, { 0xb6, 0xc0, 0x1d, 0xda, 0x48, 0x38, 0x10, 0x7e }}
+};
+
+CMPlugin::CMPlugin() :
+ ACCPROTOPLUGIN<FacebookProto>(MODULENAME, pluginInfoEx)
+{
+ SetUniqueId(DBKEY_ID);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Interface information
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load
+
+static int OnModuleLoaded(WPARAM, LPARAM)
+{
+ g_bMessageState = ServiceExists(MS_MESSAGESTATE_UPDATE) != 0;
+ return 0;
+}
+
+int CMPlugin::Load()
+{
+ HookEvent(ME_SYSTEM_MODULELOAD, OnModuleLoaded);
+ HookEvent(ME_SYSTEM_MODULEUNLOAD, OnModuleLoaded);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModuleLoaded);
+
+ // Initialize random generator (used only as fallback in utils)
+ return 0;
+}
diff --git a/protocols/Facebook/src/mqtt.cpp b/protocols/Facebook/src/mqtt.cpp index 7c2eb5ef5f..b7fbe0a3bd 100644 --- a/protocols/Facebook/src/mqtt.cpp +++ b/protocols/Facebook/src/mqtt.cpp @@ -1,344 +1,344 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -uint8_t *FacebookProto::doZip(size_t cbData, const void *pData, size_t &cbRes) -{ - size_t dataSize = cbData + 100; - uint8_t *pRes = (uint8_t *)mir_alloc(dataSize); - - z_stream zStreamOut = {}; - deflateInit(&zStreamOut, Z_BEST_COMPRESSION); - zStreamOut.avail_in = (unsigned)cbData; - zStreamOut.next_in = (uint8_t *)pData; - zStreamOut.avail_out = (unsigned)dataSize; - zStreamOut.next_out = (uint8_t *)pRes; - deflate(&zStreamOut, Z_FINISH); - deflateEnd(&zStreamOut); - - cbRes = dataSize - zStreamOut.avail_out; - return pRes; -} - -uint8_t *FacebookProto::doUnzip(size_t cbData, const void *pData, size_t &cbRes) -{ - size_t dataSize = cbData * 10; - uint8_t *pRes = (uint8_t *)mir_alloc(dataSize); - - z_stream zStreamOut = {}; - inflateInit(&zStreamOut); - zStreamOut.avail_in = (unsigned)cbData; - zStreamOut.next_in = (uint8_t *)pData; - zStreamOut.avail_out = (unsigned)dataSize; - zStreamOut.next_out = (uint8_t *)pRes; - int rc = inflate(&zStreamOut, Z_FINISH); - inflateEnd(&zStreamOut); - - switch (rc) { - case Z_OK: - case Z_STREAM_END: - cbRes = dataSize - zStreamOut.avail_out; - return pRes; - } - - mir_free(pRes); - cbRes = 0; - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// MqttMessage class members - -MqttMessage::MqttMessage() : - m_leadingByte(0) -{ -} - -MqttMessage::MqttMessage(FbMqttMessageType type, uint8_t flags) -{ - m_leadingByte = ((type & 0x0F) << 4) | (flags & 0x0F); -} - -char* MqttMessage::readStr(const uint8_t *&pData) const -{ - u_short len = ntohs(*(u_short *)pData); pData += sizeof(u_short); - if (len == 0) - return nullptr; - - char *res = (char*)mir_alloc(len + 1); - memcpy(res, pData, len); - res[len] = 0; - pData += len; - return res; -} - -void MqttMessage::writeStr(const char *str) -{ - size_t len = mir_strlen(str); - writeInt16((uint16_t)len); - writeBuf(str, len); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// MQTT functions - -bool FacebookProto::MqttParse(const MqttMessage &payload) -{ - auto *pData = (const uint8_t *)payload.data(), *pBeg = pData; - int flags = payload.getFlags(); - uint16_t mid; - - switch (payload.getType()) { - case FB_MQTT_MESSAGE_TYPE_CONNACK: - if (pData[1] != 0) { // connection failed; - int iErrorCode = ntohs(*(u_short *)pData); - debugLogA("Login failed with error %d", iErrorCode); - - if (iErrorCode == 4) { // invalid login/password - delSetting(DBKEY_TOKEN); - m_szAuthToken.Empty(); - ProtoBroadcastAck(0, ACKTYPE_LOGIN, ACKRESULT_FAILED, 0, LOGINERR_WRONGPASSWORD); - } - else ProtoBroadcastAck(0, ACKTYPE_LOGIN, ACKRESULT_FAILED, 0, LOGINERR_WRONGPROTOCOL); - return false; - } - - OnLoggedIn(); - break; - - case FB_MQTT_MESSAGE_TYPE_PUBREL: - mid = ntohs(*(u_short *)pData); - pData += 2; - { - MqttMessage reply(FB_MQTT_MESSAGE_TYPE_PUBCOMP); - reply.writeInt16(mid); - MqttSend(reply); - } - break; - - case FB_MQTT_MESSAGE_TYPE_PUBLISH: - char *str = payload.readStr(pData); - - if ((flags & FB_MQTT_MESSAGE_FLAG_QOS1) || (flags & FB_MQTT_MESSAGE_FLAG_QOS2)) { - mid = ntohs(*(u_short *)pData); - pData += 2; - - MqttMessage reply((flags & FB_MQTT_MESSAGE_FLAG_QOS1) ? FB_MQTT_MESSAGE_TYPE_PUBACK : FB_MQTT_MESSAGE_TYPE_PUBREC); - reply.writeInt16(mid); - MqttSend(reply); - } - - OnPublish(str, pData, payload.size() - (pData - pBeg)); - mir_free(str); - break; - } - - return true; -} - -bool FacebookProto::MqttRead(MqttMessage &payload) -{ - uint8_t b; - int res = Netlib_Recv(m_mqttConn, (char *)&b, sizeof(b), MSG_NODUMP); - if (res != 1) - return false; - - payload.m_leadingByte = b; - - uint32_t m = 1, remainingBytes = 0; - do { - if ((res = Netlib_Recv(m_mqttConn, (char *)&b, sizeof(b), MSG_NODUMP)) != 1) - return false; - - remainingBytes += (b & 0x7F) * m; - m *= 128; - } while ((b & 0x80) != 0); - - debugLogA("Received message of type=%d, flags=%x, body length=%d", payload.getType(), payload.getFlags(), remainingBytes); - - if (remainingBytes != 0) { - while (remainingBytes > 0) { - uint8_t buf[1024]; - int size = min(remainingBytes, sizeof(buf)); - if ((res = Netlib_Recv(m_mqttConn, (char *)buf, size)) <= 0) - return false; - - payload.writeBuf(buf, res); - remainingBytes -= res; - } - } - - return true; -} - -void FacebookProto::MqttSend(const MqttMessage &payload) -{ - FbThrift msg; - msg << payload.m_leadingByte; - msg.writeIntV(payload.size()); - msg.writeBuf(payload.data(), payload.size()); - Netlib_Send(m_mqttConn, (char*)msg.data(), (unsigned)msg.size()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// creates initial MQTT will and sends initialization packet - -void FacebookProto::MqttLogin() -{ - uint8_t zeroByte = 0; - Utils_GetRandom(&m_iMqttId, sizeof(m_iMqttId) / 2); - - FbThrift thrift; - thrift.writeField(FB_THRIFT_TYPE_STRING); // Client identifier - thrift << m_szClientID; - - thrift.writeField(FB_THRIFT_TYPE_STRUCT, 4, 1); - - thrift.writeField(FB_THRIFT_TYPE_I64); // User identifier - thrift.writeInt64(m_uid); - - thrift.writeField(FB_THRIFT_TYPE_STRING); // User agent - thrift << FB_API_MQTT_AGENT; - - thrift.writeField(FB_THRIFT_TYPE_I64); - thrift.writeInt64(23); - thrift.writeField(FB_THRIFT_TYPE_I64); - thrift.writeInt64(26); - thrift.writeField(FB_THRIFT_TYPE_I32); - thrift.writeInt32(1); - - thrift.writeBool(true); - thrift.writeBool(!m_bLoginInvisible); // visibility - - thrift.writeField(FB_THRIFT_TYPE_STRING); // device id - thrift << m_szDeviceID; - - thrift.writeBool(true); - thrift.writeField(FB_THRIFT_TYPE_I32); - thrift.writeInt32(1); - thrift.writeField(FB_THRIFT_TYPE_I32); - thrift.writeInt32(0); - thrift.writeField(FB_THRIFT_TYPE_I64); - thrift.writeInt64(m_iMqttId); - - thrift.writeField(FB_THRIFT_TYPE_LIST, 14, 12); - thrift.writeList(FB_THRIFT_TYPE_I32, 0); - thrift << zeroByte; - - thrift.writeField(FB_THRIFT_TYPE_STRING); - thrift << m_szAuthToken << zeroByte; - - size_t dataSize; - mir_ptr<uint8_t> pData(doZip(thrift.size(), thrift.data(), dataSize)); - - uint8_t protocolVersion = 3; - uint8_t flags = FB_MQTT_CONNECT_FLAG_USER | FB_MQTT_CONNECT_FLAG_PASS | FB_MQTT_CONNECT_FLAG_CLR | FB_MQTT_CONNECT_FLAG_QOS1; - MqttMessage payload(FB_MQTT_MESSAGE_TYPE_CONNECT); - payload.writeStr("MQTToT"); - payload << protocolVersion << flags; - payload.writeInt16(60); // timeout - payload.writeBuf(pData, dataSize); - MqttSend(payload); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// various MQTT send commands - -void FacebookProto::MqttPing() -{ - MqttMessage payload(FB_MQTT_MESSAGE_TYPE_PINGREQ, FB_MQTT_MESSAGE_FLAG_QOS1); - MqttSend(payload); -} - -void FacebookProto::MqttPublish(const char *topic, const JSONNode &value) -{ - auto str = value.write(); - debugLogA("Publish: <%s> -> <%s>", topic, str.c_str()); - - size_t dataSize; - mir_ptr<uint8_t> pData(doZip(str.length(), str.c_str(), dataSize)); - - MqttMessage payload(FB_MQTT_MESSAGE_TYPE_PUBLISH, FB_MQTT_MESSAGE_FLAG_QOS1); - payload.writeStr(topic); - payload.writeInt16(++m_mid); - payload.writeBuf(pData, dataSize); - MqttSend(payload); -} - -void FacebookProto::MqttSubscribe(const char *topic, ...) -{ - uint8_t zeroByte = 0; - - MqttMessage payload(FB_MQTT_MESSAGE_TYPE_SUBSCRIBE, FB_MQTT_MESSAGE_FLAG_QOS1); - payload.writeInt16(++m_mid); - payload.writeStr(topic); - payload << zeroByte; - - va_list ap; - va_start(ap, topic); - while ((topic = va_arg(ap, const char *)) != nullptr) { - payload.writeStr(topic); - payload << zeroByte; - } - va_end(ap); - - MqttSend(payload); -} - -void FacebookProto::MqttUnsubscribe(const char *topic, ...) -{ - MqttMessage payload(FB_MQTT_MESSAGE_TYPE_UNSUBSCRIBE, FB_MQTT_MESSAGE_FLAG_QOS1); - payload.writeInt16(++m_mid); - payload.writeStr(topic); - - va_list ap; - va_start(ap, topic); - while ((topic = va_arg(ap, const char *)) != nullptr) - payload.writeStr(topic); - va_end(ap); - - MqttSend(payload); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// MQTT queue - -void FacebookProto::MqttQueueConnect() -{ - JSONNode query; - query << INT_PARAM("delta_batch_size", 125) << INT_PARAM("max_deltas_able_to_process", 1000) << INT_PARAM("sync_api_version", 3) << CHAR_PARAM("encoding", "JSON"); - if (m_szSyncToken.IsEmpty()) { - JSONNode hashes; hashes.set_name("graphql_query_hashes"); hashes << CHAR_PARAM("xma_query_id", __STRINGIFY(FB_API_QUERY_XMA)); - - JSONNode xma; xma.set_name(__STRINGIFY(FB_API_QUERY_XMA)); xma << CHAR_PARAM("xma_id", "<ID>"); - JSONNode hql; hql.set_name("graphql_query_params"); hql << xma; - - JSONNode params; params.set_name("queue_params"); - params << CHAR_PARAM("buzz_on_deltas_enabled", "false") << hashes << hql; - - query << INT64_PARAM("initial_titan_sequence_id", m_sid) << CHAR_PARAM("device_id", m_szDeviceID) << INT64_PARAM("entity_fbid", m_uid) << params; - MqttPublish("/messenger_sync_create_queue", query); - } - else { - query << INT64_PARAM("last_seq_id", m_sid) << CHAR_PARAM("sync_token", m_szSyncToken); - MqttPublish("/messenger_sync_get_diffs", query); - } -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+uint8_t *FacebookProto::doZip(size_t cbData, const void *pData, size_t &cbRes)
+{
+ size_t dataSize = cbData + 100;
+ uint8_t *pRes = (uint8_t *)mir_alloc(dataSize);
+
+ z_stream zStreamOut = {};
+ deflateInit(&zStreamOut, Z_BEST_COMPRESSION);
+ zStreamOut.avail_in = (unsigned)cbData;
+ zStreamOut.next_in = (uint8_t *)pData;
+ zStreamOut.avail_out = (unsigned)dataSize;
+ zStreamOut.next_out = (uint8_t *)pRes;
+ deflate(&zStreamOut, Z_FINISH);
+ deflateEnd(&zStreamOut);
+
+ cbRes = dataSize - zStreamOut.avail_out;
+ return pRes;
+}
+
+uint8_t *FacebookProto::doUnzip(size_t cbData, const void *pData, size_t &cbRes)
+{
+ size_t dataSize = cbData * 10;
+ uint8_t *pRes = (uint8_t *)mir_alloc(dataSize);
+
+ z_stream zStreamOut = {};
+ inflateInit(&zStreamOut);
+ zStreamOut.avail_in = (unsigned)cbData;
+ zStreamOut.next_in = (uint8_t *)pData;
+ zStreamOut.avail_out = (unsigned)dataSize;
+ zStreamOut.next_out = (uint8_t *)pRes;
+ int rc = inflate(&zStreamOut, Z_FINISH);
+ inflateEnd(&zStreamOut);
+
+ switch (rc) {
+ case Z_OK:
+ case Z_STREAM_END:
+ cbRes = dataSize - zStreamOut.avail_out;
+ return pRes;
+ }
+
+ mir_free(pRes);
+ cbRes = 0;
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MqttMessage class members
+
+MqttMessage::MqttMessage() :
+ m_leadingByte(0)
+{
+}
+
+MqttMessage::MqttMessage(FbMqttMessageType type, uint8_t flags)
+{
+ m_leadingByte = ((type & 0x0F) << 4) | (flags & 0x0F);
+}
+
+char* MqttMessage::readStr(const uint8_t *&pData) const
+{
+ u_short len = ntohs(*(u_short *)pData); pData += sizeof(u_short);
+ if (len == 0)
+ return nullptr;
+
+ char *res = (char*)mir_alloc(len + 1);
+ memcpy(res, pData, len);
+ res[len] = 0;
+ pData += len;
+ return res;
+}
+
+void MqttMessage::writeStr(const char *str)
+{
+ size_t len = mir_strlen(str);
+ writeInt16((uint16_t)len);
+ writeBuf(str, len);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MQTT functions
+
+bool FacebookProto::MqttParse(const MqttMessage &payload)
+{
+ auto *pData = (const uint8_t *)payload.data(), *pBeg = pData;
+ int flags = payload.getFlags();
+ uint16_t mid;
+
+ switch (payload.getType()) {
+ case FB_MQTT_MESSAGE_TYPE_CONNACK:
+ if (pData[1] != 0) { // connection failed;
+ int iErrorCode = ntohs(*(u_short *)pData);
+ debugLogA("Login failed with error %d", iErrorCode);
+
+ if (iErrorCode == 4) { // invalid login/password
+ delSetting(DBKEY_TOKEN);
+ m_szAuthToken.Empty();
+ ProtoBroadcastAck(0, ACKTYPE_LOGIN, ACKRESULT_FAILED, 0, LOGINERR_WRONGPASSWORD);
+ }
+ else ProtoBroadcastAck(0, ACKTYPE_LOGIN, ACKRESULT_FAILED, 0, LOGINERR_WRONGPROTOCOL);
+ return false;
+ }
+
+ OnLoggedIn();
+ break;
+
+ case FB_MQTT_MESSAGE_TYPE_PUBREL:
+ mid = ntohs(*(u_short *)pData);
+ pData += 2;
+ {
+ MqttMessage reply(FB_MQTT_MESSAGE_TYPE_PUBCOMP);
+ reply.writeInt16(mid);
+ MqttSend(reply);
+ }
+ break;
+
+ case FB_MQTT_MESSAGE_TYPE_PUBLISH:
+ char *str = payload.readStr(pData);
+
+ if ((flags & FB_MQTT_MESSAGE_FLAG_QOS1) || (flags & FB_MQTT_MESSAGE_FLAG_QOS2)) {
+ mid = ntohs(*(u_short *)pData);
+ pData += 2;
+
+ MqttMessage reply((flags & FB_MQTT_MESSAGE_FLAG_QOS1) ? FB_MQTT_MESSAGE_TYPE_PUBACK : FB_MQTT_MESSAGE_TYPE_PUBREC);
+ reply.writeInt16(mid);
+ MqttSend(reply);
+ }
+
+ OnPublish(str, pData, payload.size() - (pData - pBeg));
+ mir_free(str);
+ break;
+ }
+
+ return true;
+}
+
+bool FacebookProto::MqttRead(MqttMessage &payload)
+{
+ uint8_t b;
+ int res = Netlib_Recv(m_mqttConn, (char *)&b, sizeof(b), MSG_NODUMP);
+ if (res != 1)
+ return false;
+
+ payload.m_leadingByte = b;
+
+ uint32_t m = 1, remainingBytes = 0;
+ do {
+ if ((res = Netlib_Recv(m_mqttConn, (char *)&b, sizeof(b), MSG_NODUMP)) != 1)
+ return false;
+
+ remainingBytes += (b & 0x7F) * m;
+ m *= 128;
+ } while ((b & 0x80) != 0);
+
+ debugLogA("Received message of type=%d, flags=%x, body length=%d", payload.getType(), payload.getFlags(), remainingBytes);
+
+ if (remainingBytes != 0) {
+ while (remainingBytes > 0) {
+ uint8_t buf[1024];
+ int size = min(remainingBytes, sizeof(buf));
+ if ((res = Netlib_Recv(m_mqttConn, (char *)buf, size)) <= 0)
+ return false;
+
+ payload.writeBuf(buf, res);
+ remainingBytes -= res;
+ }
+ }
+
+ return true;
+}
+
+void FacebookProto::MqttSend(const MqttMessage &payload)
+{
+ FbThrift msg;
+ msg << payload.m_leadingByte;
+ msg.writeIntV(payload.size());
+ msg.writeBuf(payload.data(), payload.size());
+ Netlib_Send(m_mqttConn, (char*)msg.data(), (unsigned)msg.size());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// creates initial MQTT will and sends initialization packet
+
+void FacebookProto::MqttLogin()
+{
+ uint8_t zeroByte = 0;
+ Utils_GetRandom(&m_iMqttId, sizeof(m_iMqttId) / 2);
+
+ FbThrift thrift;
+ thrift.writeField(FB_THRIFT_TYPE_STRING); // Client identifier
+ thrift << m_szClientID;
+
+ thrift.writeField(FB_THRIFT_TYPE_STRUCT, 4, 1);
+
+ thrift.writeField(FB_THRIFT_TYPE_I64); // User identifier
+ thrift.writeInt64(m_uid);
+
+ thrift.writeField(FB_THRIFT_TYPE_STRING); // User agent
+ thrift << FB_API_MQTT_AGENT;
+
+ thrift.writeField(FB_THRIFT_TYPE_I64);
+ thrift.writeInt64(23);
+ thrift.writeField(FB_THRIFT_TYPE_I64);
+ thrift.writeInt64(26);
+ thrift.writeField(FB_THRIFT_TYPE_I32);
+ thrift.writeInt32(1);
+
+ thrift.writeBool(true);
+ thrift.writeBool(!m_bLoginInvisible); // visibility
+
+ thrift.writeField(FB_THRIFT_TYPE_STRING); // device id
+ thrift << m_szDeviceID;
+
+ thrift.writeBool(true);
+ thrift.writeField(FB_THRIFT_TYPE_I32);
+ thrift.writeInt32(1);
+ thrift.writeField(FB_THRIFT_TYPE_I32);
+ thrift.writeInt32(0);
+ thrift.writeField(FB_THRIFT_TYPE_I64);
+ thrift.writeInt64(m_iMqttId);
+
+ thrift.writeField(FB_THRIFT_TYPE_LIST, 14, 12);
+ thrift.writeList(FB_THRIFT_TYPE_I32, 0);
+ thrift << zeroByte;
+
+ thrift.writeField(FB_THRIFT_TYPE_STRING);
+ thrift << m_szAuthToken << zeroByte;
+
+ size_t dataSize;
+ mir_ptr<uint8_t> pData(doZip(thrift.size(), thrift.data(), dataSize));
+
+ uint8_t protocolVersion = 3;
+ uint8_t flags = FB_MQTT_CONNECT_FLAG_USER | FB_MQTT_CONNECT_FLAG_PASS | FB_MQTT_CONNECT_FLAG_CLR | FB_MQTT_CONNECT_FLAG_QOS1;
+ MqttMessage payload(FB_MQTT_MESSAGE_TYPE_CONNECT);
+ payload.writeStr("MQTToT");
+ payload << protocolVersion << flags;
+ payload.writeInt16(60); // timeout
+ payload.writeBuf(pData, dataSize);
+ MqttSend(payload);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// various MQTT send commands
+
+void FacebookProto::MqttPing()
+{
+ MqttMessage payload(FB_MQTT_MESSAGE_TYPE_PINGREQ, FB_MQTT_MESSAGE_FLAG_QOS1);
+ MqttSend(payload);
+}
+
+void FacebookProto::MqttPublish(const char *topic, const JSONNode &value)
+{
+ auto str = value.write();
+ debugLogA("Publish: <%s> -> <%s>", topic, str.c_str());
+
+ size_t dataSize;
+ mir_ptr<uint8_t> pData(doZip(str.length(), str.c_str(), dataSize));
+
+ MqttMessage payload(FB_MQTT_MESSAGE_TYPE_PUBLISH, FB_MQTT_MESSAGE_FLAG_QOS1);
+ payload.writeStr(topic);
+ payload.writeInt16(++m_mid);
+ payload.writeBuf(pData, dataSize);
+ MqttSend(payload);
+}
+
+void FacebookProto::MqttSubscribe(const char *topic, ...)
+{
+ uint8_t zeroByte = 0;
+
+ MqttMessage payload(FB_MQTT_MESSAGE_TYPE_SUBSCRIBE, FB_MQTT_MESSAGE_FLAG_QOS1);
+ payload.writeInt16(++m_mid);
+ payload.writeStr(topic);
+ payload << zeroByte;
+
+ va_list ap;
+ va_start(ap, topic);
+ while ((topic = va_arg(ap, const char *)) != nullptr) {
+ payload.writeStr(topic);
+ payload << zeroByte;
+ }
+ va_end(ap);
+
+ MqttSend(payload);
+}
+
+void FacebookProto::MqttUnsubscribe(const char *topic, ...)
+{
+ MqttMessage payload(FB_MQTT_MESSAGE_TYPE_UNSUBSCRIBE, FB_MQTT_MESSAGE_FLAG_QOS1);
+ payload.writeInt16(++m_mid);
+ payload.writeStr(topic);
+
+ va_list ap;
+ va_start(ap, topic);
+ while ((topic = va_arg(ap, const char *)) != nullptr)
+ payload.writeStr(topic);
+ va_end(ap);
+
+ MqttSend(payload);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MQTT queue
+
+void FacebookProto::MqttQueueConnect()
+{
+ JSONNode query;
+ query << INT_PARAM("delta_batch_size", 125) << INT_PARAM("max_deltas_able_to_process", 1000) << INT_PARAM("sync_api_version", 3) << CHAR_PARAM("encoding", "JSON");
+ if (m_szSyncToken.IsEmpty()) {
+ JSONNode hashes; hashes.set_name("graphql_query_hashes"); hashes << CHAR_PARAM("xma_query_id", __STRINGIFY(FB_API_QUERY_XMA));
+
+ JSONNode xma; xma.set_name(__STRINGIFY(FB_API_QUERY_XMA)); xma << CHAR_PARAM("xma_id", "<ID>");
+ JSONNode hql; hql.set_name("graphql_query_params"); hql << xma;
+
+ JSONNode params; params.set_name("queue_params");
+ params << CHAR_PARAM("buzz_on_deltas_enabled", "false") << hashes << hql;
+
+ query << INT64_PARAM("initial_titan_sequence_id", m_sid) << CHAR_PARAM("device_id", m_szDeviceID) << INT64_PARAM("entity_fbid", m_uid) << params;
+ MqttPublish("/messenger_sync_create_queue", query);
+ }
+ else {
+ query << INT64_PARAM("last_seq_id", m_sid) << CHAR_PARAM("sync_token", m_szSyncToken);
+ MqttPublish("/messenger_sync_get_diffs", query);
+ }
+}
diff --git a/protocols/Facebook/src/mqtt.h b/protocols/Facebook/src/mqtt.h index 08849fbc4c..8e1b4964c7 100644 --- a/protocols/Facebook/src/mqtt.h +++ b/protocols/Facebook/src/mqtt.h @@ -1,139 +1,139 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -#define FACEBOOK_ORCA_AGENT FB_API_MQTT_AGENT - -#define FB_THRIFT_TYPE_STOP 0 -#define FB_THRIFT_TYPE_VOID 1 -#define FB_THRIFT_TYPE_BOOL 2 -#define FB_THRIFT_TYPE_BYTE 3 -#define FB_THRIFT_TYPE_DOUBLE 4 -#define FB_THRIFT_TYPE_I16 6 -#define FB_THRIFT_TYPE_I32 8 -#define FB_THRIFT_TYPE_I64 10 -#define FB_THRIFT_TYPE_STRING 11 -#define FB_THRIFT_TYPE_STRUCT 12 -#define FB_THRIFT_TYPE_MAP 13 -#define FB_THRIFT_TYPE_SET 14 -#define FB_THRIFT_TYPE_LIST 15 - -enum FbMqttMessageType -{ - FB_MQTT_MESSAGE_TYPE_CONNECT = 1, - FB_MQTT_MESSAGE_TYPE_CONNACK = 2, - FB_MQTT_MESSAGE_TYPE_PUBLISH = 3, - FB_MQTT_MESSAGE_TYPE_PUBACK = 4, - FB_MQTT_MESSAGE_TYPE_PUBREC = 5, - FB_MQTT_MESSAGE_TYPE_PUBREL = 6, - FB_MQTT_MESSAGE_TYPE_PUBCOMP = 7, - FB_MQTT_MESSAGE_TYPE_SUBSCRIBE = 8, - FB_MQTT_MESSAGE_TYPE_SUBACK = 9, - FB_MQTT_MESSAGE_TYPE_UNSUBSCRIBE = 10, - FB_MQTT_MESSAGE_TYPE_UNSUBACK = 11, - FB_MQTT_MESSAGE_TYPE_PINGREQ = 12, - FB_MQTT_MESSAGE_TYPE_PINGRESP = 13, - FB_MQTT_MESSAGE_TYPE_DISCONNECT = 14 -}; - -class FbThrift -{ - MBinBuffer m_buf; - -public: - __forceinline void* data() const { return m_buf.data(); } - __forceinline size_t size() const { return m_buf.length(); } - - __forceinline void reset(size_t cbLen, void *pData) - { m_buf.assign(pData, cbLen); - } - - FbThrift& operator<<(uint8_t); - FbThrift& operator<<(const char *); - - void writeBool(bool value); - void writeBuf(const void *pData, size_t cbLen); - void writeInt16(uint16_t value); - void writeInt32(int32_t value); - void writeInt64(int64_t value); - void writeIntV(uint64_t value); - void writeField(int type); - void writeField(int type, int id, int lastid); - void writeList(int iType, int size); -}; - -class FbThriftReader : public FbThrift -{ - bool m_lastBool = false, m_lastBval = false; - size_t offset = 0; - - uint8_t decodeType(int type); - -public: - __forceinline CMStringA rest() const { return CMStringA((char*)data() + offset, int(size() - offset)); } - - bool isStop(); - bool readBool(bool &bVal); - bool readByte(uint8_t &val); - bool readField(uint8_t &type, uint16_t &id); - bool readInt16(uint16_t &val); - bool readInt32(uint32_t &val); - bool readInt64(uint64_t &val); - bool readIntV(uint64_t &val); - bool readList(uint8_t &type, uint32_t &size); - bool readStr(char *&val); // val must be freed via mir_free() -}; - -class MqttMessage : public FbThrift -{ - friend class FacebookProto; - - uint8_t m_leadingByte; - -public: - MqttMessage(); - MqttMessage(FbMqttMessageType type, uint8_t flags = 0); - - __forceinline int getType() const - { - return m_leadingByte >> 4; - } - - __forceinline int getFlags() const - { return m_leadingByte & 0x0F; - } - - char* readStr(const uint8_t *&pData) const; - void writeStr(const char *str); -}; - -#define FB_MQTT_CONNECT_FLAG_CLR 0x0002 -#define FB_MQTT_CONNECT_FLAG_WILL 0x0004 -#define FB_MQTT_CONNECT_FLAG_QOS0 0x0000 -#define FB_MQTT_CONNECT_FLAG_QOS1 0x0008 -#define FB_MQTT_CONNECT_FLAG_QOS2 0x0010 -#define FB_MQTT_CONNECT_FLAG_RET 0x0020 -#define FB_MQTT_CONNECT_FLAG_PASS 0x0040 -#define FB_MQTT_CONNECT_FLAG_USER 0x0080 - -#define FB_MQTT_MESSAGE_FLAG_QOS0 0x0000 -#define FB_MQTT_MESSAGE_FLAG_QOS1 0x0002 -#define FB_MQTT_MESSAGE_FLAG_QOS2 0x0004 +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#define FACEBOOK_ORCA_AGENT FB_API_MQTT_AGENT
+
+#define FB_THRIFT_TYPE_STOP 0
+#define FB_THRIFT_TYPE_VOID 1
+#define FB_THRIFT_TYPE_BOOL 2
+#define FB_THRIFT_TYPE_BYTE 3
+#define FB_THRIFT_TYPE_DOUBLE 4
+#define FB_THRIFT_TYPE_I16 6
+#define FB_THRIFT_TYPE_I32 8
+#define FB_THRIFT_TYPE_I64 10
+#define FB_THRIFT_TYPE_STRING 11
+#define FB_THRIFT_TYPE_STRUCT 12
+#define FB_THRIFT_TYPE_MAP 13
+#define FB_THRIFT_TYPE_SET 14
+#define FB_THRIFT_TYPE_LIST 15
+
+enum FbMqttMessageType
+{
+ FB_MQTT_MESSAGE_TYPE_CONNECT = 1,
+ FB_MQTT_MESSAGE_TYPE_CONNACK = 2,
+ FB_MQTT_MESSAGE_TYPE_PUBLISH = 3,
+ FB_MQTT_MESSAGE_TYPE_PUBACK = 4,
+ FB_MQTT_MESSAGE_TYPE_PUBREC = 5,
+ FB_MQTT_MESSAGE_TYPE_PUBREL = 6,
+ FB_MQTT_MESSAGE_TYPE_PUBCOMP = 7,
+ FB_MQTT_MESSAGE_TYPE_SUBSCRIBE = 8,
+ FB_MQTT_MESSAGE_TYPE_SUBACK = 9,
+ FB_MQTT_MESSAGE_TYPE_UNSUBSCRIBE = 10,
+ FB_MQTT_MESSAGE_TYPE_UNSUBACK = 11,
+ FB_MQTT_MESSAGE_TYPE_PINGREQ = 12,
+ FB_MQTT_MESSAGE_TYPE_PINGRESP = 13,
+ FB_MQTT_MESSAGE_TYPE_DISCONNECT = 14
+};
+
+class FbThrift
+{
+ MBinBuffer m_buf;
+
+public:
+ __forceinline void* data() const { return m_buf.data(); }
+ __forceinline size_t size() const { return m_buf.length(); }
+
+ __forceinline void reset(size_t cbLen, void *pData)
+ { m_buf.assign(pData, cbLen);
+ }
+
+ FbThrift& operator<<(uint8_t);
+ FbThrift& operator<<(const char *);
+
+ void writeBool(bool value);
+ void writeBuf(const void *pData, size_t cbLen);
+ void writeInt16(uint16_t value);
+ void writeInt32(int32_t value);
+ void writeInt64(int64_t value);
+ void writeIntV(uint64_t value);
+ void writeField(int type);
+ void writeField(int type, int id, int lastid);
+ void writeList(int iType, int size);
+};
+
+class FbThriftReader : public FbThrift
+{
+ bool m_lastBool = false, m_lastBval = false;
+ size_t offset = 0;
+
+ uint8_t decodeType(int type);
+
+public:
+ __forceinline CMStringA rest() const { return CMStringA((char*)data() + offset, int(size() - offset)); }
+
+ bool isStop();
+ bool readBool(bool &bVal);
+ bool readByte(uint8_t &val);
+ bool readField(uint8_t &type, uint16_t &id);
+ bool readInt16(uint16_t &val);
+ bool readInt32(uint32_t &val);
+ bool readInt64(uint64_t &val);
+ bool readIntV(uint64_t &val);
+ bool readList(uint8_t &type, uint32_t &size);
+ bool readStr(char *&val); // val must be freed via mir_free()
+};
+
+class MqttMessage : public FbThrift
+{
+ friend class FacebookProto;
+
+ uint8_t m_leadingByte;
+
+public:
+ MqttMessage();
+ MqttMessage(FbMqttMessageType type, uint8_t flags = 0);
+
+ __forceinline int getType() const
+ {
+ return m_leadingByte >> 4;
+ }
+
+ __forceinline int getFlags() const
+ { return m_leadingByte & 0x0F;
+ }
+
+ char* readStr(const uint8_t *&pData) const;
+ void writeStr(const char *str);
+};
+
+#define FB_MQTT_CONNECT_FLAG_CLR 0x0002
+#define FB_MQTT_CONNECT_FLAG_WILL 0x0004
+#define FB_MQTT_CONNECT_FLAG_QOS0 0x0000
+#define FB_MQTT_CONNECT_FLAG_QOS1 0x0008
+#define FB_MQTT_CONNECT_FLAG_QOS2 0x0010
+#define FB_MQTT_CONNECT_FLAG_RET 0x0020
+#define FB_MQTT_CONNECT_FLAG_PASS 0x0040
+#define FB_MQTT_CONNECT_FLAG_USER 0x0080
+
+#define FB_MQTT_MESSAGE_FLAG_QOS0 0x0000
+#define FB_MQTT_MESSAGE_FLAG_QOS1 0x0002
+#define FB_MQTT_MESSAGE_FLAG_QOS2 0x0004
diff --git a/protocols/Facebook/src/options.cpp b/protocols/Facebook/src/options.cpp index 0d8c1eb38a..2ed98b1dfc 100644 --- a/protocols/Facebook/src/options.cpp +++ b/protocols/Facebook/src/options.cpp @@ -1,85 +1,85 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -class CFacebookAccOptsDlg : public CFBDlgBase -{ - CCtrlEdit edtGroup; - CCtrlCheck chkEnableChats, chkHideChats, chkKeepUnread, chkLoginInvis, chkLoadAll; - CCtrlHyperlink linkMainPage; - -public: - CFacebookAccOptsDlg(FacebookProto *pThis) : - CFBDlgBase(pThis, IDD_OPTIONS), - edtGroup(this, IDC_GROUP), - chkLoadAll(this, IDC_LOADALL), - chkHideChats(this, IDC_HIDECHATS), - chkKeepUnread(this, IDC_KEEP_UNREAD), - chkLoginInvis(this, IDC_INVIS_LOGIN), - chkEnableChats(this, IDC_ENABLECHATS), - linkMainPage(this, IDC_NEWACCOUNTLINK, "https://www.facebook.com") - { - CreateLink(edtGroup, pThis->m_wszDefaultGroup); - CreateLink(chkLoadAll, pThis->m_bLoadAll); - CreateLink(chkHideChats, pThis->m_bHideGroupchats); - CreateLink(chkKeepUnread, pThis->m_bKeepUnread); - CreateLink(chkLoginInvis, pThis->m_bLoginInvisible); - CreateLink(chkEnableChats, pThis->m_bUseGroupchats); - } - - bool OnInitDialog() override - { - ptrA login(m_proto->getStringA(DBKEY_LOGIN)); - if (login != nullptr) - SetDlgItemTextA(m_hwnd, IDC_UN, login); - - ptrA password(m_proto->getStringA(DBKEY_PASS)); - if (password != nullptr) - SetDlgItemTextA(m_hwnd, IDC_PW, password); - return true; - } - - bool OnApply() override - { - char str[128]; - - GetDlgItemTextA(m_hwnd, IDC_UN, str, _countof(str)); - m_proto->setString(DBKEY_LOGIN, str); - - GetDlgItemTextA(m_hwnd, IDC_PW, str, _countof(str)); - m_proto->setString(DBKEY_PASS, str); - return true; - } -}; - -int FacebookProto::OnOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -790000000; - odp.szTitle.w = m_tszUserName; - odp.szGroup.w = LPGENW("Network"); - odp.flags = ODPF_UNICODE; - - odp.szTab.w = LPGENW("Account"); - odp.pDialog = new CFacebookAccOptsDlg(this); - g_plugin.addOptions(wParam, &odp); - return 0; +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+class CFacebookAccOptsDlg : public CFBDlgBase
+{
+ CCtrlEdit edtGroup;
+ CCtrlCheck chkEnableChats, chkHideChats, chkKeepUnread, chkLoginInvis, chkLoadAll;
+ CCtrlHyperlink linkMainPage;
+
+public:
+ CFacebookAccOptsDlg(FacebookProto *pThis) :
+ CFBDlgBase(pThis, IDD_OPTIONS),
+ edtGroup(this, IDC_GROUP),
+ chkLoadAll(this, IDC_LOADALL),
+ chkHideChats(this, IDC_HIDECHATS),
+ chkKeepUnread(this, IDC_KEEP_UNREAD),
+ chkLoginInvis(this, IDC_INVIS_LOGIN),
+ chkEnableChats(this, IDC_ENABLECHATS),
+ linkMainPage(this, IDC_NEWACCOUNTLINK, "https://www.facebook.com")
+ {
+ CreateLink(edtGroup, pThis->m_wszDefaultGroup);
+ CreateLink(chkLoadAll, pThis->m_bLoadAll);
+ CreateLink(chkHideChats, pThis->m_bHideGroupchats);
+ CreateLink(chkKeepUnread, pThis->m_bKeepUnread);
+ CreateLink(chkLoginInvis, pThis->m_bLoginInvisible);
+ CreateLink(chkEnableChats, pThis->m_bUseGroupchats);
+ }
+
+ bool OnInitDialog() override
+ {
+ ptrA login(m_proto->getStringA(DBKEY_LOGIN));
+ if (login != nullptr)
+ SetDlgItemTextA(m_hwnd, IDC_UN, login);
+
+ ptrA password(m_proto->getStringA(DBKEY_PASS));
+ if (password != nullptr)
+ SetDlgItemTextA(m_hwnd, IDC_PW, password);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ char str[128];
+
+ GetDlgItemTextA(m_hwnd, IDC_UN, str, _countof(str));
+ m_proto->setString(DBKEY_LOGIN, str);
+
+ GetDlgItemTextA(m_hwnd, IDC_PW, str, _countof(str));
+ m_proto->setString(DBKEY_PASS, str);
+ return true;
+ }
+};
+
+int FacebookProto::OnOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -790000000;
+ odp.szTitle.w = m_tszUserName;
+ odp.szGroup.w = LPGENW("Network");
+ odp.flags = ODPF_UNICODE;
+
+ odp.szTab.w = LPGENW("Account");
+ odp.pDialog = new CFacebookAccOptsDlg(this);
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
}
\ No newline at end of file diff --git a/protocols/Facebook/src/proto.cpp b/protocols/Facebook/src/proto.cpp index 7a1d8b1c93..db2d401e21 100644 --- a/protocols/Facebook/src/proto.cpp +++ b/protocols/Facebook/src/proto.cpp @@ -1,302 +1,302 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -static int CompareUsers(const FacebookUser *p1, const FacebookUser *p2) -{ - if (p1->id == p2->id) - return 0; - - return (p1->id < p2->id) ? -1 : 1; -} - -static int CompareMessages(const COwnMessage *p1, const COwnMessage *p2) -{ - if (p1->msgId == p2->msgId) - return 0; - - return (p1->msgId < p2->msgId) ? -1 : 1; -} - -FacebookProto::FacebookProto(const char *proto_name, const wchar_t *username) : - PROTO<FacebookProto>(proto_name, username), - m_impl(*this), - m_users(50, CompareUsers), - arOwnMessages(1, CompareMessages), - m_bLoadAll(this, "LoadAllContacts", false), - m_bKeepUnread(this, "KeepUnread", false), - m_bUseBigAvatars(this, "UseBigAvatars", true), - m_bUseGroupchats(this, "UseGroupChats", true), - m_bHideGroupchats(this, "HideGroupChats", true), - m_bLoginInvisible(this, "LoginInvisible", false), - m_wszDefaultGroup(this, "DefaultGroup", L"Facebook") -{ - // to upgrade previous settings - if (getByte("Compatibility") < 1) { - setByte("Compatibility", 1); - delSetting(DBKEY_DEVICE_ID); - } - - m_szDeviceID = getMStringA(DBKEY_DEVICE_ID); - if (m_szDeviceID.IsEmpty()) { - UUID deviceId; - UuidCreate(&deviceId); - RPC_CSTR szId; - UuidToStringA(&deviceId, &szId); - m_szDeviceID = szId; - setString(DBKEY_DEVICE_ID, m_szDeviceID); - RpcStringFreeA(&szId); - } - - m_szClientID = getMStringA(DBKEY_CLIENT_ID); - if (m_szClientID.IsEmpty()) { - for (int i = 0; i < 20; i++) { - uint32_t dwRandon; - Utils_GetRandom(&dwRandon, sizeof(dwRandon)); - int c = dwRandon % 62; - if (c >= 0 && c < 26) - c += 'a'; - else if (c >= 26 && c < 52) - c += 'A' - 26; - else if (c >= 52 && c < 62) - c += '0' - 52; - m_szClientID.AppendChar(c); - } - setString(DBKEY_CLIENT_ID, m_szClientID); - } - - m_uid = _atoi64(getMStringA(DBKEY_ID)); - m_sid = _atoi64(getMStringA(DBKEY_SID)); - m_szSyncToken = getMStringA(DBKEY_SYNC_TOKEN); - - // Create standard network connection - NETLIBUSER nlu = {}; - nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; - nlu.szSettingsModule = m_szModuleName; - nlu.szDescriptiveName.w = m_tszUserName; - m_hNetlibUser = Netlib_RegisterUser(&nlu); - - db_set_resident(m_szModuleName, "UpdateNeeded"); - - // Services - CreateProtoService(PS_CREATEACCMGRUI, &FacebookProto::SvcCreateAccMgrUI); - CreateProtoService(PS_GETAVATARINFO, &FacebookProto::GetAvatarInfo); - CreateProtoService(PS_GETAVATARCAPS, &FacebookProto::GetAvatarCaps); - - // Events - HookProtoEvent(ME_GC_EVENT, &FacebookProto::GroupchatEventHook); - HookProtoEvent(ME_GC_BUILDMENU, &FacebookProto::GroupchatMenuHook); - HookProtoEvent(ME_OPT_INITIALISE, &FacebookProto::OnOptionsInit); - HookProtoEvent(ME_DB_EVENT_MARKED_READ, &FacebookProto::OnMarkedRead); - - // Group chats - GCREGISTER gcr = {}; - gcr.dwFlags = GC_TYPNOTIF; - gcr.ptszDispName = m_tszUserName; - gcr.pszModule = m_szModuleName; - Chat_Register(&gcr); -} - -FacebookProto::~FacebookProto() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol events - -void FacebookProto::OnContactAdded(MCONTACT hContact) -{ - __int64 userId = _atoi64(getMStringA(hContact, DBKEY_ID)); - if (userId && !FindUser(userId)) { - mir_cslock lck(m_csUsers); - m_users.insert(new FacebookUser(userId, hContact)); - } -} - -void FacebookProto::OnModulesLoaded() -{ - VARSW wszCache(L"%miranda_avatarcache%"); - - CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers\\*.png", wszCache.get(), m_szModuleName); - SMADD_CONT cont = { 2, m_szModuleName, wszPath }; - CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont)); - - wszPath.Format(L"%s\\%S\\Stickers\\*.webp", wszCache.get(), m_szModuleName); - cont.path = wszPath; - CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont)); - - // contacts cache - for (auto &cc : AccContacts()) { - CMStringA szId(getMStringA(cc, DBKEY_ID)); - if (!szId.IsEmpty()) - m_users.insert(new FacebookUser(_atoi64(szId), cc, isChatRoom(cc))); - } - - // Default group - Clist_GroupCreate(0, m_wszDefaultGroup); -} - -void FacebookProto::OnShutdown() -{ - if (m_mqttConn != nullptr) - Netlib_Shutdown(m_mqttConn); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MCONTACT FacebookProto::AddToList(int, PROTOSEARCHRESULT *psr) -{ - if (!mir_wstrlen(psr->id.w)) - return 0; - - if (auto *pUser = FindUser(_wtoi64(psr->id.w))) - return pUser->hContact; - - MCONTACT hContact = db_add_contact(); - setWString(hContact, DBKEY_ID, psr->id.w); - Proto_AddToContact(hContact, m_szModuleName); - return hContact; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR FacebookProto::GetCaps(int type, MCONTACT) -{ - switch (type) { - case PFLAGNUM_1: - { - DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ; - - if (getByte(DBKEY_SET_MIRANDA_STATUS)) - return flags |= PF1_MODEMSG; - else - return flags |= PF1_MODEMSGRECV; - } - - case PFLAGNUM_2: - return PF2_ONLINE | PF2_SHORTAWAY | PF2_INVISIBLE | PF2_IDLE; - - case PFLAGNUM_3: - if (getByte(DBKEY_SET_MIRANDA_STATUS)) - return PF2_ONLINE; // | PF2_SHORTAWAY; - else - return 0; - - case PFLAGNUM_4: - return PF4_NOCUSTOMAUTH | PF4_AVATARS | PF4_SUPPORTTYPING | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_READNOTIFY; - - case PFLAG_MAXLENOFMESSAGE: - return FACEBOOK_MESSAGE_LIMIT; - - case PFLAG_UNIQUEIDTEXT: - return (INT_PTR) L"Facebook ID"; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int FacebookProto::SendMsg(MCONTACT hContact, int, const char *pszSrc) -{ - if (!m_bOnline) { - ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)1, (LPARAM)TranslateT("Protocol is offline or user isn't authorized yet")); - return 1; - } - - CMStringA userId(getMStringA(hContact, DBKEY_ID)); - - __int64 msgId; - Utils_GetRandom(&msgId, sizeof(msgId)); - msgId = abs(msgId); - - JSONNode root; root << CHAR_PARAM("body", pszSrc) << INT64_PARAM("msgid", msgId) << INT64_PARAM("sender_fbid", m_uid) << CHAR_PARAM("to", userId); - MqttPublish("/send_message2", root); - - mir_cslock lck(m_csOwnMessages); - arOwnMessages.insert(new COwnMessage(msgId, m_mid, hContact)); - return m_mid; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int FacebookProto::SetStatus(int iNewStatus) -{ - if (iNewStatus != ID_STATUS_OFFLINE && IsStatusConnecting(m_iStatus)) { - debugLogA("=== Status is already connecting, no change"); - return 0; - } - - // Routing statuses not supported by Facebook - switch (iNewStatus) { - case ID_STATUS_ONLINE: - case ID_STATUS_OFFLINE: - break; - - default: - iNewStatus = ID_STATUS_AWAY; - break; - } - - if (m_iStatus == iNewStatus) { - debugLogA("=== Statuses are same, no change"); - return 0; - } - - m_iDesiredStatus = iNewStatus; - - int iOldStatus = m_iStatus; - - // log off & free all resources - if (iNewStatus == ID_STATUS_OFFLINE) { - OnShutdown(); - - m_iStatus = ID_STATUS_OFFLINE; - } - else if (m_iStatus == ID_STATUS_OFFLINE) { // we gonna connect - debugLogA("*** Beginning SignOn process"); - - m_iStatus = ID_STATUS_CONNECTING; - - ForkThread(&FacebookProto::ServerThread); - } - else m_iStatus = iNewStatus; - - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus); - return 0; -} - -////////////////////////////////////////////////////////////////////////////// - -int FacebookProto::UserIsTyping(MCONTACT hContact, int type) -{ - JSONNode root; root << INT_PARAM("state", type == PROTOTYPE_SELFTYPING_ON) << CHAR_PARAM("to", getMStringA(hContact, DBKEY_ID)); - MqttPublish("/typing", root); - return 0; -} - -////////////////////////////////////////////////////////////////////////////// -// Services - -INT_PTR FacebookProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam) -{ - return (INT_PTR) CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FACEBOOKACCOUNT), - (HWND) lParam, FBAccountProc, (LPARAM) this); -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+static int CompareUsers(const FacebookUser *p1, const FacebookUser *p2)
+{
+ if (p1->id == p2->id)
+ return 0;
+
+ return (p1->id < p2->id) ? -1 : 1;
+}
+
+static int CompareMessages(const COwnMessage *p1, const COwnMessage *p2)
+{
+ if (p1->msgId == p2->msgId)
+ return 0;
+
+ return (p1->msgId < p2->msgId) ? -1 : 1;
+}
+
+FacebookProto::FacebookProto(const char *proto_name, const wchar_t *username) :
+ PROTO<FacebookProto>(proto_name, username),
+ m_impl(*this),
+ m_users(50, CompareUsers),
+ arOwnMessages(1, CompareMessages),
+ m_bLoadAll(this, "LoadAllContacts", false),
+ m_bKeepUnread(this, "KeepUnread", false),
+ m_bUseBigAvatars(this, "UseBigAvatars", true),
+ m_bUseGroupchats(this, "UseGroupChats", true),
+ m_bHideGroupchats(this, "HideGroupChats", true),
+ m_bLoginInvisible(this, "LoginInvisible", false),
+ m_wszDefaultGroup(this, "DefaultGroup", L"Facebook")
+{
+ // to upgrade previous settings
+ if (getByte("Compatibility") < 1) {
+ setByte("Compatibility", 1);
+ delSetting(DBKEY_DEVICE_ID);
+ }
+
+ m_szDeviceID = getMStringA(DBKEY_DEVICE_ID);
+ if (m_szDeviceID.IsEmpty()) {
+ UUID deviceId;
+ UuidCreate(&deviceId);
+ RPC_CSTR szId;
+ UuidToStringA(&deviceId, &szId);
+ m_szDeviceID = szId;
+ setString(DBKEY_DEVICE_ID, m_szDeviceID);
+ RpcStringFreeA(&szId);
+ }
+
+ m_szClientID = getMStringA(DBKEY_CLIENT_ID);
+ if (m_szClientID.IsEmpty()) {
+ for (int i = 0; i < 20; i++) {
+ uint32_t dwRandon;
+ Utils_GetRandom(&dwRandon, sizeof(dwRandon));
+ int c = dwRandon % 62;
+ if (c >= 0 && c < 26)
+ c += 'a';
+ else if (c >= 26 && c < 52)
+ c += 'A' - 26;
+ else if (c >= 52 && c < 62)
+ c += '0' - 52;
+ m_szClientID.AppendChar(c);
+ }
+ setString(DBKEY_CLIENT_ID, m_szClientID);
+ }
+
+ m_uid = _atoi64(getMStringA(DBKEY_ID));
+ m_sid = _atoi64(getMStringA(DBKEY_SID));
+ m_szSyncToken = getMStringA(DBKEY_SYNC_TOKEN);
+
+ // Create standard network connection
+ NETLIBUSER nlu = {};
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE;
+ nlu.szSettingsModule = m_szModuleName;
+ nlu.szDescriptiveName.w = m_tszUserName;
+ m_hNetlibUser = Netlib_RegisterUser(&nlu);
+
+ db_set_resident(m_szModuleName, "UpdateNeeded");
+
+ // Services
+ CreateProtoService(PS_CREATEACCMGRUI, &FacebookProto::SvcCreateAccMgrUI);
+ CreateProtoService(PS_GETAVATARINFO, &FacebookProto::GetAvatarInfo);
+ CreateProtoService(PS_GETAVATARCAPS, &FacebookProto::GetAvatarCaps);
+
+ // Events
+ HookProtoEvent(ME_GC_EVENT, &FacebookProto::GroupchatEventHook);
+ HookProtoEvent(ME_GC_BUILDMENU, &FacebookProto::GroupchatMenuHook);
+ HookProtoEvent(ME_OPT_INITIALISE, &FacebookProto::OnOptionsInit);
+ HookProtoEvent(ME_DB_EVENT_MARKED_READ, &FacebookProto::OnMarkedRead);
+
+ // Group chats
+ GCREGISTER gcr = {};
+ gcr.dwFlags = GC_TYPNOTIF;
+ gcr.ptszDispName = m_tszUserName;
+ gcr.pszModule = m_szModuleName;
+ Chat_Register(&gcr);
+}
+
+FacebookProto::~FacebookProto()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// protocol events
+
+void FacebookProto::OnContactAdded(MCONTACT hContact)
+{
+ __int64 userId = _atoi64(getMStringA(hContact, DBKEY_ID));
+ if (userId && !FindUser(userId)) {
+ mir_cslock lck(m_csUsers);
+ m_users.insert(new FacebookUser(userId, hContact));
+ }
+}
+
+void FacebookProto::OnModulesLoaded()
+{
+ VARSW wszCache(L"%miranda_avatarcache%");
+
+ CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers\\*.png", wszCache.get(), m_szModuleName);
+ SMADD_CONT cont = { 2, m_szModuleName, wszPath };
+ CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont));
+
+ wszPath.Format(L"%s\\%S\\Stickers\\*.webp", wszCache.get(), m_szModuleName);
+ cont.path = wszPath;
+ CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont));
+
+ // contacts cache
+ for (auto &cc : AccContacts()) {
+ CMStringA szId(getMStringA(cc, DBKEY_ID));
+ if (!szId.IsEmpty())
+ m_users.insert(new FacebookUser(_atoi64(szId), cc, isChatRoom(cc)));
+ }
+
+ // Default group
+ Clist_GroupCreate(0, m_wszDefaultGroup);
+}
+
+void FacebookProto::OnShutdown()
+{
+ if (m_mqttConn != nullptr)
+ Netlib_Shutdown(m_mqttConn);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MCONTACT FacebookProto::AddToList(int, PROTOSEARCHRESULT *psr)
+{
+ if (!mir_wstrlen(psr->id.w))
+ return 0;
+
+ if (auto *pUser = FindUser(_wtoi64(psr->id.w)))
+ return pUser->hContact;
+
+ MCONTACT hContact = db_add_contact();
+ setWString(hContact, DBKEY_ID, psr->id.w);
+ Proto_AddToContact(hContact, m_szModuleName);
+ return hContact;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR FacebookProto::GetCaps(int type, MCONTACT)
+{
+ switch (type) {
+ case PFLAGNUM_1:
+ {
+ DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ;
+
+ if (getByte(DBKEY_SET_MIRANDA_STATUS))
+ return flags |= PF1_MODEMSG;
+ else
+ return flags |= PF1_MODEMSGRECV;
+ }
+
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_INVISIBLE | PF2_IDLE;
+
+ case PFLAGNUM_3:
+ if (getByte(DBKEY_SET_MIRANDA_STATUS))
+ return PF2_ONLINE; // | PF2_SHORTAWAY;
+ else
+ return 0;
+
+ case PFLAGNUM_4:
+ return PF4_NOCUSTOMAUTH | PF4_AVATARS | PF4_SUPPORTTYPING | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_READNOTIFY;
+
+ case PFLAG_MAXLENOFMESSAGE:
+ return FACEBOOK_MESSAGE_LIMIT;
+
+ case PFLAG_UNIQUEIDTEXT:
+ return (INT_PTR) L"Facebook ID";
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int FacebookProto::SendMsg(MCONTACT hContact, int, const char *pszSrc)
+{
+ if (!m_bOnline) {
+ ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)1, (LPARAM)TranslateT("Protocol is offline or user isn't authorized yet"));
+ return 1;
+ }
+
+ CMStringA userId(getMStringA(hContact, DBKEY_ID));
+
+ __int64 msgId;
+ Utils_GetRandom(&msgId, sizeof(msgId));
+ msgId = abs(msgId);
+
+ JSONNode root; root << CHAR_PARAM("body", pszSrc) << INT64_PARAM("msgid", msgId) << INT64_PARAM("sender_fbid", m_uid) << CHAR_PARAM("to", userId);
+ MqttPublish("/send_message2", root);
+
+ mir_cslock lck(m_csOwnMessages);
+ arOwnMessages.insert(new COwnMessage(msgId, m_mid, hContact));
+ return m_mid;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int FacebookProto::SetStatus(int iNewStatus)
+{
+ if (iNewStatus != ID_STATUS_OFFLINE && IsStatusConnecting(m_iStatus)) {
+ debugLogA("=== Status is already connecting, no change");
+ return 0;
+ }
+
+ // Routing statuses not supported by Facebook
+ switch (iNewStatus) {
+ case ID_STATUS_ONLINE:
+ case ID_STATUS_OFFLINE:
+ break;
+
+ default:
+ iNewStatus = ID_STATUS_AWAY;
+ break;
+ }
+
+ if (m_iStatus == iNewStatus) {
+ debugLogA("=== Statuses are same, no change");
+ return 0;
+ }
+
+ m_iDesiredStatus = iNewStatus;
+
+ int iOldStatus = m_iStatus;
+
+ // log off & free all resources
+ if (iNewStatus == ID_STATUS_OFFLINE) {
+ OnShutdown();
+
+ m_iStatus = ID_STATUS_OFFLINE;
+ }
+ else if (m_iStatus == ID_STATUS_OFFLINE) { // we gonna connect
+ debugLogA("*** Beginning SignOn process");
+
+ m_iStatus = ID_STATUS_CONNECTING;
+
+ ForkThread(&FacebookProto::ServerThread);
+ }
+ else m_iStatus = iNewStatus;
+
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int FacebookProto::UserIsTyping(MCONTACT hContact, int type)
+{
+ JSONNode root; root << INT_PARAM("state", type == PROTOTYPE_SELFTYPING_ON) << CHAR_PARAM("to", getMStringA(hContact, DBKEY_ID));
+ MqttPublish("/typing", root);
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Services
+
+INT_PTR FacebookProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam)
+{
+ return (INT_PTR) CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FACEBOOKACCOUNT),
+ (HWND) lParam, FBAccountProc, (LPARAM) this);
+}
diff --git a/protocols/Facebook/src/proto.h b/protocols/Facebook/src/proto.h index 14dc343d18..7fbeebd2cb 100644 --- a/protocols/Facebook/src/proto.h +++ b/protocols/Facebook/src/proto.h @@ -1,557 +1,557 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - - -/** - * FB_API_AHOST: - * - * The HTTP host for the Facebook API. - */ -#define FB_API_AHOST "https://api.facebook.com" - -/** - * FB_API_BHOST: - * - * The HTTP host for the Facebook BAPI. - */ -#define FB_API_BHOST "https://b-api.facebook.com" - -/** - * FB_API_GHOST: - * - * The HTTP host for the Facebook Graph API. - */ -#define FB_API_GHOST "https://graph.facebook.com" - -/** - * FB_API_WHOST: - * - * The HTTP host for the Facebook website. - */ -#define FB_API_WHOST "https://www.facebook.com" - -/** - * FB_API_FBRPC_PREFIX - * - * The fbrpc URL prefix used in links shared from the mobile app. - */ -#define FB_API_FBRPC_PREFIX "fbrpc://facebook/nativethirdparty" - -/** - * FB_API_KEY: - * - * The Facebook API key. - */ -#define FB_API_KEY "256002347743983" - -/** - * FB_API_SECRET: - * - * The Facebook API secret. - */ -#define FB_API_SECRET "374e60f8b9bb6b8cbb30f78030438895" - -/** - * FB_ORCA_AGENT - * - * The part of the user agent that looks like the official client, since the - * server started checking this. - */ - -#define FB_ORCA_AGENT "[FBAN/Orca-Android;FBAV/537.0.0.31.101;FBPN/com.facebook.orca;FBLC/en_US;FBBV/52182662]" - -/** - * FB_API_AGENT: - * - * The HTTP User-Agent header. - */ -#define FB_API_AGENT "Facebook plugin / Purple / 0.9.6 " FB_ORCA_AGENT - -/** - * FB_API_MQTT_AGENT - * - * The client information string sent in the MQTT CONNECT message - */ - -#define FB_API_MQTT_AGENT FB_API_AGENT - -/** - * FB_API_URL_ATTACH: - * - * The URL for attachment URL requests. - */ -#define FB_API_URL_ATTACH FB_API_AHOST "/method/messaging.getAttachment" -//#define FB_API_URL_ATTACH FB_API_AHOST "/method/messaging.attachmentRedirect" - -/** - * FB_API_URL_AUTH: - * - * The URL for authentication requests. - */ -#define FB_API_URL_AUTH FB_API_BHOST "/method/auth.login" - -/** - * FB_API_URL_GQL: - * - * The URL for GraphQL requests. - */ -#define FB_API_URL_GQL FB_API_GHOST "/graphql" - -/** - * FB_API_URL_MESSAGES: - * - * The URL for linking message threads. - */ -#define FB_API_URL_MESSAGES FB_API_WHOST "/messages" - -/** - * FB_API_URL_PARTS: - * - * The URL for participant management requests. - */ -#define FB_API_URL_PARTS FB_API_GHOST "/participants" - -/** - * FB_API_URL_THREADS: - * - * The URL for thread management requests. - */ -#define FB_API_URL_THREADS FB_API_GHOST "/me/group_threads" - -/** - * FB_API_URL_TOPIC: - * - * The URL for thread topic requests. - */ -#define FB_API_URL_TOPIC FB_API_AHOST "/method/messaging.setthreadname" - -/** - * FB_API_QUERY_CONTACT: - * - * The query hash for the `UsersQuery`. - * - * Key mapping: - * 0: user_fbids - * 1: include_full_user_info - * 2: profile_pic_large_size - * 3: profile_pic_medium_size - * 4: profile_pic_small_size - */ -#define FB_API_QUERY_CONTACT 10153915107411729 - -/** - * FB_API_QUERY_CONTACTS: - * - * The query hash for the `FetchContactsFullQuery`. - * - * Key mapping: - * 0: profile_types - * 1: limit - * 2: big_img_size - * 3: huge_img_size - * 4: small_img_size - */ -#define FB_API_QUERY_CONTACTS 10154444360806729 - -/** - * FB_API_QUERY_CONTACTS_AFTER: - * - * The query hash for the `FetchContactsFullWithAfterQuery`. - * - * Key mapping: - * 0: profile_types - * 1: after - * 2: limit - * 3: big_img_size - * 4: huge_img_size - * 5: small_img_size - */ -#define FB_API_QUERY_CONTACTS_AFTER 10154444360816729 - - -/** - * FB_API_QUERY_CONTACTS_DELTA: - * - * The query hash for the `FetchContactsDeltaQuery`. - * - * Key mapping: - * 0: after - * 1: profile_types - * 2: limit - * 3: big_img_size - * 4: huge_img_size - * 5: small_img_size - */ -#define FB_API_QUERY_CONTACTS_DELTA 10154444360801729 - -/** - * FB_API_QUERY_STICKER: - * - * The query hash for the `FetchStickersWithPreviewsQuery`. - * - * Key mapping: - * 0: sticker_ids - * 1: media_type - * 2: preview_size - * 3: scaling_factor - * 4: animated_media_type - */ -#define FB_API_QUERY_STICKER 10152877994321729 - -/** - * FB_API_QUERY_THREAD: - * - * The query hash for the `ThreadQuery`. - * - * Key mapping: - * 0: thread_ids - * 1: verification_type - * 2: hash_key - * 3: small_preview_size - * 4: large_preview_size - * 5: item_count - * 6: event_count - * 7: full_screen_height - * 8: full_screen_width - * 9: medium_preview_size - * 10: fetch_users_separately - * 11: include_message_info - * 12: msg_count - * 13: include_full_user_info - * 14: profile_pic_large_size - * 15: profile_pic_medium_size - * 16: profile_pic_small_size - */ -#define FB_API_QUERY_THREAD 10153919752036729 - -/** - * FB_API_QUERY_THREADS: - * - * The query hash for the `ThreadListQuery`. - * - * Key mapping: - * 0: folder_tag (INBOX, PENDING, ARCHIVED, OTHER, UNREAD) - * 1: thread_count (result is sorted from newest to oldest when parameter is sent) - * 2: include_thread_info - * 3: verification_type - * 4: hash_key - * 5: small_preview_size - * 6: large_preview_size - * 7: item_count - * 8: event_count - * 9: full_screen_height - * 10: full_screen_width - * 11: medium_preview_size - * 12: fetch_users_separately - * 13: include_message_info - * 14: msg_count - * 15: UNKNOWN - * 16: profile_pic_large_size - * 17: profile_pic_medium_size - * 18: profile_pic_small_size - */ -#define FB_API_QUERY_THREADS 10153919752026729 - -/** - * FB_API_QUERY_SEQ_ID: - * - * A variant of ThreadListQuery with sequence ID - * - * TODO: parameters. - */ - -#define FB_API_QUERY_SEQ_ID 10155268192741729 - -/** - * FB_API_QUERY_XMA: - * - * The query hash for the `XMAQuery`. - * - * Key mapping: - * 0: xma_id - */ -#define FB_API_QUERY_XMA 10153919431161729 - -/** - * FB_API_CONTACTS_COUNT: - * - * The maximum amount of contacts to fetch in a single request. If this - * value is set too high, HTTP request will fail. This is due to the - * request data being too large. - */ -#define FB_API_CONTACTS_COUNT 500 - -#define FACEBOOK_MESSAGE_LIMIT 100000 - -class FacebookProto; - -///////////////////////////////////////////////////////////////////////////////////////// - -struct AsyncHttpRequest : public MTHttpRequest<FacebookProto> -{ - struct Param - { - Param(const char *p1, const char *p2) : - key(p1), val(p2) - {} - - CMStringA key, val; - }; - OBJLIST<Param> params; - - AsyncHttpRequest(); - - void CalcSig(); -}; - -AsyncHttpRequest *operator<<(AsyncHttpRequest *, const CHAR_PARAM &); -AsyncHttpRequest *operator<<(AsyncHttpRequest *, const INT_PARAM &); - -class JsonReply -{ - JSONNode *m_root = nullptr; - int m_errorCode = 0; - -public: - JsonReply(NETLIBHTTPREQUEST *); - ~JsonReply(); - - __forceinline JSONNode &data() const { return *m_root; } - __forceinline int error() const { return m_errorCode; } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -struct FacebookUser -{ - FacebookUser(__int64 _p1, MCONTACT _p2, bool _p3 = false, bool _p4 = false) : - id(_p1), - hContact(_p2), - bIsChat(_p3), - bIsChatInitialized(_p4) - {} - - __int64 id; - MCONTACT hContact; - bool bIsChat; - bool bIsChatInitialized; -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -struct COwnMessage -{ - __int64 msgId; - int reqId; - MCONTACT hContact; - CMStringW wszText; - - COwnMessage() : - msgId(0), - reqId(0), - hContact(0) - { - } - - COwnMessage(__int64 _id, int _reqId, MCONTACT _hContact) : - msgId(_id), - reqId(_reqId), - hContact(_hContact) - { - } -}; - -class FacebookProto : public PROTO<FacebookProto> -{ - friend class CGroupchatInviteDlg; - - class FacebookImpl - { - friend class FacebookProto; - - FacebookProto &m_proto; - CTimer m_heartBeat; - - void OnHeartBeat(CTimer *) - { - m_proto.MqttPing(); - } - - FacebookImpl(FacebookProto &pro) : - m_proto(pro), - m_heartBeat(Miranda_GetSystemWindow(), (UINT_PTR)this) - { - m_heartBeat.OnEvent = Callback(this, &FacebookImpl::OnHeartBeat); - } - } m_impl; - - uint8_t *doZip(size_t cbData, const void *pData, size_t &cbRes); - uint8_t *doUnzip(size_t cbData, const void *pData, size_t &cbRes); - - void ConnectionFailed(int iErrorCode = 0); - - AsyncHttpRequest *CreateRequest(const char *szUrl, const char *szName, const char *szMethod); - AsyncHttpRequest *CreateRequestGQL(int64_t id); - NETLIBHTTPREQUEST *ExecuteRequest(AsyncHttpRequest *pReq); - - // Avatars - void __cdecl AvatarsUpdate(void *); - void GetAvatarFilename(MCONTACT hContact, wchar_t *pwszFileName); - - // Group chats - void Chat_InviteUser(SESSION_INFO *si); - int Chat_KickUser(SESSION_INFO *si, const wchar_t *pwszUid); - void Chat_Leave(SESSION_INFO *si); - void Chat_SendPrivateMessage(GCHOOK *gch); - void Chat_ProcessLogMenu(SESSION_INFO *si, GCHOOK *gch); - void Chat_ProcessNickMenu(SESSION_INFO *si, GCHOOK *gch); - - // MQTT - void MqttLogin(); - - void MqttPing(); - void MqttPublish(const char *topic, const JSONNode &value); - void MqttSubscribe(const char *topic, ...); - void MqttUnsubscribe(const char *topic, ...); - - bool MqttRead(MqttMessage &payload); - bool MqttParse(const MqttMessage &payload); - void MqttSend(const MqttMessage &payload); - - void MqttQueueConnect(); - - void OnPublish(const char *str, const uint8_t *payLoad, size_t cbLen); - void OnPublishMessage(FbThriftReader &rdr); - void OnPublishPresence(FbThriftReader &rdr); - void OnPublishUtn(FbThriftReader &rdr); - - HNETLIBCONN m_mqttConn; - __int64 m_iMqttId; - int16_t m_mid; // MQTT message id - - // internal data - CMStringA m_szDeviceID; // stored, GUID that identifies this miranda's account - CMStringA m_szClientID; // stored, random alphanumeric string of 20 chars - __int64 m_uid; // stored, Facebook user id - - CMStringA m_szSyncToken; // stored, sequence query token - __int64 m_sid; // stored, Facebook sequence id - - int m_iUnread; - bool m_bOnline; - bool m_QueueCreated; - - CMStringA m_szAuthToken; // calculated - - mir_cs m_csOwnMessages; - OBJLIST<COwnMessage> arOwnMessages; - bool ExtractOwnMessage(__int64 msgId, COwnMessage &res); - - mir_cs m_csUsers; - OBJLIST<FacebookUser> m_users; - - FacebookUser* FindUser(__int64 id); - - FacebookUser *UserFromJson(const JSONNode &root, CMStringW &wszId, bool &bIsChat); - - bool CheckOwnMessage(FacebookUser *pUser, __int64 offlineId, const char *pszMsgId); - void FetchAttach(const CMStringA &mid, __int64 fbid, CMStringA &szBody); - - void OnLoggedIn(); - void OnLoggedOut(); - - FacebookUser* RefreshThread(JSONNode &n); - FacebookUser* RefreshThread(CMStringW &wszId); - bool RefreshSid(); - int RefreshToken(); - void RefreshThreads(); - int RefreshContacts(); - - FacebookUser *AddContact(const CMStringW &wszId, bool bTemp = true); - - void __cdecl ServerThread(void *); - -public: - FacebookProto(const char *proto_name, const wchar_t *username); - ~FacebookProto(); - - inline const char *ModuleName() const - { - return m_szModuleName; - } - - void OnPublishPrivateMessage(const JSONNode &json); - void OnPublishReadReceipt(const JSONNode &json); - void OnPublishSentMessage(const JSONNode &json); - void OnPublishThreadName(const JSONNode &json); - void OnPublishChatJoin(const JSONNode &json); - void OnPublishChatLeave(const JSONNode &json); - - ////////////////////////////////////////////////////////////////////////////////////// - // options - - CMOption<wchar_t *> m_wszDefaultGroup; // clist group to store contacts - CMOption<bool> m_bUseBigAvatars; // use big or small avatars by default - CMOption<bool> m_bUseGroupchats; // do we need group chats at all? - CMOption<bool> m_bHideGroupchats; // do not open chat windows on creation - CMOption<bool> m_bLoginInvisible; // login in the invisible mode - CMOption<bool> m_bKeepUnread; // do not mark incoming messages as read - CMOption<bool> m_bLoadAll; // load all contacts, not only those who have ARE_FRIENDS status - - //////////////////////////////////////////////////////////////////////////////////////// - // PROTO_INTERFACE - - void OnContactAdded(MCONTACT) override; - void OnModulesLoaded() override; - void OnShutdown() override; - - MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr) override; - INT_PTR GetCaps(int type, MCONTACT hContact) override; - int SendMsg(MCONTACT hContact, int flags, const char *pszSrc) override; - int SetStatus(int iNewStatus) override; - int UserIsTyping(MCONTACT hContact, int type) override; - - //////////////////////////////////////////////////////////////////////////////////////// - // Events - - int __cdecl OnMarkedRead(WPARAM, LPARAM); - int __cdecl OnOptionsInit(WPARAM, LPARAM); - - int __cdecl GroupchatMenuHook(WPARAM, LPARAM); - int __cdecl GroupchatEventHook(WPARAM, LPARAM); - - //////////////////////////////////////////////////////////////////////////////////////// - // Services - - INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM); - INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM); - INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); -}; - -typedef CProtoDlgBase<FacebookProto> CFBDlgBase; - -struct CMPlugin : public ACCPROTOPLUGIN<FacebookProto> -{ - CMPlugin(); - - int Load() override; -}; +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+
+/**
+ * FB_API_AHOST:
+ *
+ * The HTTP host for the Facebook API.
+ */
+#define FB_API_AHOST "https://api.facebook.com"
+
+/**
+ * FB_API_BHOST:
+ *
+ * The HTTP host for the Facebook BAPI.
+ */
+#define FB_API_BHOST "https://b-api.facebook.com"
+
+/**
+ * FB_API_GHOST:
+ *
+ * The HTTP host for the Facebook Graph API.
+ */
+#define FB_API_GHOST "https://graph.facebook.com"
+
+/**
+ * FB_API_WHOST:
+ *
+ * The HTTP host for the Facebook website.
+ */
+#define FB_API_WHOST "https://www.facebook.com"
+
+/**
+ * FB_API_FBRPC_PREFIX
+ *
+ * The fbrpc URL prefix used in links shared from the mobile app.
+ */
+#define FB_API_FBRPC_PREFIX "fbrpc://facebook/nativethirdparty"
+
+/**
+ * FB_API_KEY:
+ *
+ * The Facebook API key.
+ */
+#define FB_API_KEY "256002347743983"
+
+/**
+ * FB_API_SECRET:
+ *
+ * The Facebook API secret.
+ */
+#define FB_API_SECRET "374e60f8b9bb6b8cbb30f78030438895"
+
+/**
+ * FB_ORCA_AGENT
+ *
+ * The part of the user agent that looks like the official client, since the
+ * server started checking this.
+ */
+
+#define FB_ORCA_AGENT "[FBAN/Orca-Android;FBAV/537.0.0.31.101;FBPN/com.facebook.orca;FBLC/en_US;FBBV/52182662]"
+
+/**
+ * FB_API_AGENT:
+ *
+ * The HTTP User-Agent header.
+ */
+#define FB_API_AGENT "Facebook plugin / Purple / 0.9.6 " FB_ORCA_AGENT
+
+/**
+ * FB_API_MQTT_AGENT
+ *
+ * The client information string sent in the MQTT CONNECT message
+ */
+
+#define FB_API_MQTT_AGENT FB_API_AGENT
+
+/**
+ * FB_API_URL_ATTACH:
+ *
+ * The URL for attachment URL requests.
+ */
+#define FB_API_URL_ATTACH FB_API_AHOST "/method/messaging.getAttachment"
+//#define FB_API_URL_ATTACH FB_API_AHOST "/method/messaging.attachmentRedirect"
+
+/**
+ * FB_API_URL_AUTH:
+ *
+ * The URL for authentication requests.
+ */
+#define FB_API_URL_AUTH FB_API_BHOST "/method/auth.login"
+
+/**
+ * FB_API_URL_GQL:
+ *
+ * The URL for GraphQL requests.
+ */
+#define FB_API_URL_GQL FB_API_GHOST "/graphql"
+
+/**
+ * FB_API_URL_MESSAGES:
+ *
+ * The URL for linking message threads.
+ */
+#define FB_API_URL_MESSAGES FB_API_WHOST "/messages"
+
+/**
+ * FB_API_URL_PARTS:
+ *
+ * The URL for participant management requests.
+ */
+#define FB_API_URL_PARTS FB_API_GHOST "/participants"
+
+/**
+ * FB_API_URL_THREADS:
+ *
+ * The URL for thread management requests.
+ */
+#define FB_API_URL_THREADS FB_API_GHOST "/me/group_threads"
+
+/**
+ * FB_API_URL_TOPIC:
+ *
+ * The URL for thread topic requests.
+ */
+#define FB_API_URL_TOPIC FB_API_AHOST "/method/messaging.setthreadname"
+
+/**
+ * FB_API_QUERY_CONTACT:
+ *
+ * The query hash for the `UsersQuery`.
+ *
+ * Key mapping:
+ * 0: user_fbids
+ * 1: include_full_user_info
+ * 2: profile_pic_large_size
+ * 3: profile_pic_medium_size
+ * 4: profile_pic_small_size
+ */
+#define FB_API_QUERY_CONTACT 10153915107411729
+
+/**
+ * FB_API_QUERY_CONTACTS:
+ *
+ * The query hash for the `FetchContactsFullQuery`.
+ *
+ * Key mapping:
+ * 0: profile_types
+ * 1: limit
+ * 2: big_img_size
+ * 3: huge_img_size
+ * 4: small_img_size
+ */
+#define FB_API_QUERY_CONTACTS 10154444360806729
+
+/**
+ * FB_API_QUERY_CONTACTS_AFTER:
+ *
+ * The query hash for the `FetchContactsFullWithAfterQuery`.
+ *
+ * Key mapping:
+ * 0: profile_types
+ * 1: after
+ * 2: limit
+ * 3: big_img_size
+ * 4: huge_img_size
+ * 5: small_img_size
+ */
+#define FB_API_QUERY_CONTACTS_AFTER 10154444360816729
+
+
+/**
+ * FB_API_QUERY_CONTACTS_DELTA:
+ *
+ * The query hash for the `FetchContactsDeltaQuery`.
+ *
+ * Key mapping:
+ * 0: after
+ * 1: profile_types
+ * 2: limit
+ * 3: big_img_size
+ * 4: huge_img_size
+ * 5: small_img_size
+ */
+#define FB_API_QUERY_CONTACTS_DELTA 10154444360801729
+
+/**
+ * FB_API_QUERY_STICKER:
+ *
+ * The query hash for the `FetchStickersWithPreviewsQuery`.
+ *
+ * Key mapping:
+ * 0: sticker_ids
+ * 1: media_type
+ * 2: preview_size
+ * 3: scaling_factor
+ * 4: animated_media_type
+ */
+#define FB_API_QUERY_STICKER 10152877994321729
+
+/**
+ * FB_API_QUERY_THREAD:
+ *
+ * The query hash for the `ThreadQuery`.
+ *
+ * Key mapping:
+ * 0: thread_ids
+ * 1: verification_type
+ * 2: hash_key
+ * 3: small_preview_size
+ * 4: large_preview_size
+ * 5: item_count
+ * 6: event_count
+ * 7: full_screen_height
+ * 8: full_screen_width
+ * 9: medium_preview_size
+ * 10: fetch_users_separately
+ * 11: include_message_info
+ * 12: msg_count
+ * 13: include_full_user_info
+ * 14: profile_pic_large_size
+ * 15: profile_pic_medium_size
+ * 16: profile_pic_small_size
+ */
+#define FB_API_QUERY_THREAD 10153919752036729
+
+/**
+ * FB_API_QUERY_THREADS:
+ *
+ * The query hash for the `ThreadListQuery`.
+ *
+ * Key mapping:
+ * 0: folder_tag (INBOX, PENDING, ARCHIVED, OTHER, UNREAD)
+ * 1: thread_count (result is sorted from newest to oldest when parameter is sent)
+ * 2: include_thread_info
+ * 3: verification_type
+ * 4: hash_key
+ * 5: small_preview_size
+ * 6: large_preview_size
+ * 7: item_count
+ * 8: event_count
+ * 9: full_screen_height
+ * 10: full_screen_width
+ * 11: medium_preview_size
+ * 12: fetch_users_separately
+ * 13: include_message_info
+ * 14: msg_count
+ * 15: UNKNOWN
+ * 16: profile_pic_large_size
+ * 17: profile_pic_medium_size
+ * 18: profile_pic_small_size
+ */
+#define FB_API_QUERY_THREADS 10153919752026729
+
+/**
+ * FB_API_QUERY_SEQ_ID:
+ *
+ * A variant of ThreadListQuery with sequence ID
+ *
+ * TODO: parameters.
+ */
+
+#define FB_API_QUERY_SEQ_ID 10155268192741729
+
+/**
+ * FB_API_QUERY_XMA:
+ *
+ * The query hash for the `XMAQuery`.
+ *
+ * Key mapping:
+ * 0: xma_id
+ */
+#define FB_API_QUERY_XMA 10153919431161729
+
+/**
+ * FB_API_CONTACTS_COUNT:
+ *
+ * The maximum amount of contacts to fetch in a single request. If this
+ * value is set too high, HTTP request will fail. This is due to the
+ * request data being too large.
+ */
+#define FB_API_CONTACTS_COUNT 500
+
+#define FACEBOOK_MESSAGE_LIMIT 100000
+
+class FacebookProto;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct AsyncHttpRequest : public MTHttpRequest<FacebookProto>
+{
+ struct Param
+ {
+ Param(const char *p1, const char *p2) :
+ key(p1), val(p2)
+ {}
+
+ CMStringA key, val;
+ };
+ OBJLIST<Param> params;
+
+ AsyncHttpRequest();
+
+ void CalcSig();
+};
+
+AsyncHttpRequest *operator<<(AsyncHttpRequest *, const CHAR_PARAM &);
+AsyncHttpRequest *operator<<(AsyncHttpRequest *, const INT_PARAM &);
+
+class JsonReply
+{
+ JSONNode *m_root = nullptr;
+ int m_errorCode = 0;
+
+public:
+ JsonReply(NETLIBHTTPREQUEST *);
+ ~JsonReply();
+
+ __forceinline JSONNode &data() const { return *m_root; }
+ __forceinline int error() const { return m_errorCode; }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct FacebookUser
+{
+ FacebookUser(__int64 _p1, MCONTACT _p2, bool _p3 = false, bool _p4 = false) :
+ id(_p1),
+ hContact(_p2),
+ bIsChat(_p3),
+ bIsChatInitialized(_p4)
+ {}
+
+ __int64 id;
+ MCONTACT hContact;
+ bool bIsChat;
+ bool bIsChatInitialized;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct COwnMessage
+{
+ __int64 msgId;
+ int reqId;
+ MCONTACT hContact;
+ CMStringW wszText;
+
+ COwnMessage() :
+ msgId(0),
+ reqId(0),
+ hContact(0)
+ {
+ }
+
+ COwnMessage(__int64 _id, int _reqId, MCONTACT _hContact) :
+ msgId(_id),
+ reqId(_reqId),
+ hContact(_hContact)
+ {
+ }
+};
+
+class FacebookProto : public PROTO<FacebookProto>
+{
+ friend class CGroupchatInviteDlg;
+
+ class FacebookImpl
+ {
+ friend class FacebookProto;
+
+ FacebookProto &m_proto;
+ CTimer m_heartBeat;
+
+ void OnHeartBeat(CTimer *)
+ {
+ m_proto.MqttPing();
+ }
+
+ FacebookImpl(FacebookProto &pro) :
+ m_proto(pro),
+ m_heartBeat(Miranda_GetSystemWindow(), (UINT_PTR)this)
+ {
+ m_heartBeat.OnEvent = Callback(this, &FacebookImpl::OnHeartBeat);
+ }
+ } m_impl;
+
+ uint8_t *doZip(size_t cbData, const void *pData, size_t &cbRes);
+ uint8_t *doUnzip(size_t cbData, const void *pData, size_t &cbRes);
+
+ void ConnectionFailed(int iErrorCode = 0);
+
+ AsyncHttpRequest *CreateRequest(const char *szUrl, const char *szName, const char *szMethod);
+ AsyncHttpRequest *CreateRequestGQL(int64_t id);
+ NETLIBHTTPREQUEST *ExecuteRequest(AsyncHttpRequest *pReq);
+
+ // Avatars
+ void __cdecl AvatarsUpdate(void *);
+ void GetAvatarFilename(MCONTACT hContact, wchar_t *pwszFileName);
+
+ // Group chats
+ void Chat_InviteUser(SESSION_INFO *si);
+ int Chat_KickUser(SESSION_INFO *si, const wchar_t *pwszUid);
+ void Chat_Leave(SESSION_INFO *si);
+ void Chat_SendPrivateMessage(GCHOOK *gch);
+ void Chat_ProcessLogMenu(SESSION_INFO *si, GCHOOK *gch);
+ void Chat_ProcessNickMenu(SESSION_INFO *si, GCHOOK *gch);
+
+ // MQTT
+ void MqttLogin();
+
+ void MqttPing();
+ void MqttPublish(const char *topic, const JSONNode &value);
+ void MqttSubscribe(const char *topic, ...);
+ void MqttUnsubscribe(const char *topic, ...);
+
+ bool MqttRead(MqttMessage &payload);
+ bool MqttParse(const MqttMessage &payload);
+ void MqttSend(const MqttMessage &payload);
+
+ void MqttQueueConnect();
+
+ void OnPublish(const char *str, const uint8_t *payLoad, size_t cbLen);
+ void OnPublishMessage(FbThriftReader &rdr);
+ void OnPublishPresence(FbThriftReader &rdr);
+ void OnPublishUtn(FbThriftReader &rdr);
+
+ HNETLIBCONN m_mqttConn;
+ __int64 m_iMqttId;
+ int16_t m_mid; // MQTT message id
+
+ // internal data
+ CMStringA m_szDeviceID; // stored, GUID that identifies this miranda's account
+ CMStringA m_szClientID; // stored, random alphanumeric string of 20 chars
+ __int64 m_uid; // stored, Facebook user id
+
+ CMStringA m_szSyncToken; // stored, sequence query token
+ __int64 m_sid; // stored, Facebook sequence id
+
+ int m_iUnread;
+ bool m_bOnline;
+ bool m_QueueCreated;
+
+ CMStringA m_szAuthToken; // calculated
+
+ mir_cs m_csOwnMessages;
+ OBJLIST<COwnMessage> arOwnMessages;
+ bool ExtractOwnMessage(__int64 msgId, COwnMessage &res);
+
+ mir_cs m_csUsers;
+ OBJLIST<FacebookUser> m_users;
+
+ FacebookUser* FindUser(__int64 id);
+
+ FacebookUser *UserFromJson(const JSONNode &root, CMStringW &wszId, bool &bIsChat);
+
+ bool CheckOwnMessage(FacebookUser *pUser, __int64 offlineId, const char *pszMsgId);
+ void FetchAttach(const CMStringA &mid, __int64 fbid, CMStringA &szBody);
+
+ void OnLoggedIn();
+ void OnLoggedOut();
+
+ FacebookUser* RefreshThread(JSONNode &n);
+ FacebookUser* RefreshThread(CMStringW &wszId);
+ bool RefreshSid();
+ int RefreshToken();
+ void RefreshThreads();
+ int RefreshContacts();
+
+ FacebookUser *AddContact(const CMStringW &wszId, bool bTemp = true);
+
+ void __cdecl ServerThread(void *);
+
+public:
+ FacebookProto(const char *proto_name, const wchar_t *username);
+ ~FacebookProto();
+
+ inline const char *ModuleName() const
+ {
+ return m_szModuleName;
+ }
+
+ void OnPublishPrivateMessage(const JSONNode &json);
+ void OnPublishReadReceipt(const JSONNode &json);
+ void OnPublishSentMessage(const JSONNode &json);
+ void OnPublishThreadName(const JSONNode &json);
+ void OnPublishChatJoin(const JSONNode &json);
+ void OnPublishChatLeave(const JSONNode &json);
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // options
+
+ CMOption<wchar_t *> m_wszDefaultGroup; // clist group to store contacts
+ CMOption<bool> m_bUseBigAvatars; // use big or small avatars by default
+ CMOption<bool> m_bUseGroupchats; // do we need group chats at all?
+ CMOption<bool> m_bHideGroupchats; // do not open chat windows on creation
+ CMOption<bool> m_bLoginInvisible; // login in the invisible mode
+ CMOption<bool> m_bKeepUnread; // do not mark incoming messages as read
+ CMOption<bool> m_bLoadAll; // load all contacts, not only those who have ARE_FRIENDS status
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // PROTO_INTERFACE
+
+ void OnContactAdded(MCONTACT) override;
+ void OnModulesLoaded() override;
+ void OnShutdown() override;
+
+ MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr) override;
+ INT_PTR GetCaps(int type, MCONTACT hContact) override;
+ int SendMsg(MCONTACT hContact, int flags, const char *pszSrc) override;
+ int SetStatus(int iNewStatus) override;
+ int UserIsTyping(MCONTACT hContact, int type) override;
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Events
+
+ int __cdecl OnMarkedRead(WPARAM, LPARAM);
+ int __cdecl OnOptionsInit(WPARAM, LPARAM);
+
+ int __cdecl GroupchatMenuHook(WPARAM, LPARAM);
+ int __cdecl GroupchatEventHook(WPARAM, LPARAM);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Services
+
+ INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM);
+ INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM);
+ INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM);
+};
+
+typedef CProtoDlgBase<FacebookProto> CFBDlgBase;
+
+struct CMPlugin : public ACCPROTOPLUGIN<FacebookProto>
+{
+ CMPlugin();
+
+ int Load() override;
+};
diff --git a/protocols/Facebook/src/server.cpp b/protocols/Facebook/src/server.cpp index b35cc5461a..39ba67f83d 100644 --- a/protocols/Facebook/src/server.cpp +++ b/protocols/Facebook/src/server.cpp @@ -1,1051 +1,1051 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -void FacebookProto::ConnectionFailed(int iErrorCode) -{ - if (iErrorCode) { - POPUPDATAW popup; - popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR), true); - wcscpy_s(popup.lpwzContactName, m_tszUserName); - mir_snwprintf(popup.lpwzText, TranslateT("Connection failed with error code %d"), iErrorCode); - PUAddPopupW(&popup); - } - - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)m_iStatus, m_iDesiredStatus); - - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus); - - OnShutdown(); -} - -bool FacebookProto::ExtractOwnMessage(__int64 msgId, COwnMessage &res) -{ - mir_cslock lck(m_csOwnMessages); - for (auto &it : arOwnMessages) - if (it->msgId == msgId) { - res = *it; - arOwnMessages.removeItem(&it); - return true; - } - - return false; -} - -void FacebookProto::OnLoggedIn() -{ - m_mid = 0; - - JSONNode root; root << BOOL_PARAM("foreground", true) << INT_PARAM("keepalive_timeout", 60); - MqttPublish("/foreground_state", root); - - MqttSubscribe("/inbox", "/mercury", "/messaging_events", "/orca_presence", "/orca_typing_notifications", "/pp", "/t_ms", "/t_p", "/t_rtc", "/webrtc", "/webrtc_response", 0); - MqttUnsubscribe("/orca_message_notifications", 0); - - // if sequence is not initialized, request SID from the server - if (m_sid == 0) { - if (!RefreshSid()) { - ConnectionFailed(); - return; - } - } - - // point of no return; - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus); - m_iStatus = m_iDesiredStatus; - m_bOnline = true; - m_impl.m_heartBeat.Start(60000); - - // connect message queue - MqttQueueConnect(); - - // request message threads if needed - if (m_bUseGroupchats) - RefreshThreads(); -} - -void FacebookProto::OnLoggedOut() -{ - m_impl.m_heartBeat.Stop(); - m_bOnline = false; - - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE); - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - - setAllContactStatuses(ID_STATUS_OFFLINE, false); -} - -FacebookUser* FacebookProto::AddContact(const CMStringW &wszId, bool bTemp) -{ - MCONTACT hContact = db_add_contact(); - setWString(hContact, DBKEY_ID, wszId); - Proto_AddToContact(hContact, m_szModuleName); - Clist_SetGroup(hContact, m_wszDefaultGroup); - if (bTemp) - Contact::RemoveFromList(hContact); - - return FindUser(_wtoi64(wszId)); -} - -FacebookUser* FacebookProto::FindUser(__int64 id) -{ - mir_cslock lck(m_csUsers); - return m_users.find((FacebookUser *)&id); -} - -FacebookUser* FacebookProto::UserFromJson(const JSONNode &root, CMStringW &wszUserId, bool &bIsChat) -{ - bIsChat = false; - wszUserId = root["threadKey"]["otherUserFbId"].as_mstring(); - if (wszUserId.IsEmpty()) { - // if only thread id is present, it must be a group chat - wszUserId = root["threadKey"]["threadFbId"].as_mstring(); - bIsChat = true; - } - - auto *pUser = FindUser(_wtoi64(wszUserId)); - if (pUser == nullptr) { - debugLogA("Message from unknown contact %s, ignored", wszUserId.c_str()); - return nullptr; - } - - if (pUser->bIsChat != bIsChat) { - debugLogA("Wrong chat user: %d vs %d for user %lld, ignored", pUser->bIsChat, bIsChat, pUser->id); - return nullptr; - } - - return pUser; -} - -int FacebookProto::RefreshContacts() -{ - CMStringA szCursor; - bool bNeedUpdate = false; - - while (true) { - JSONNode root; root << CHAR_PARAM("0", "user"); - - AsyncHttpRequest *pReq; - if (szCursor.IsEmpty()) { - pReq = CreateRequestGQL(FB_API_QUERY_CONTACTS); - root << INT_PARAM("1", FB_API_CONTACTS_COUNT); - } - else { - pReq = CreateRequestGQL(FB_API_QUERY_CONTACTS_AFTER); - root << CHAR_PARAM("1", szCursor) << INT_PARAM("2", FB_API_CONTACTS_COUNT); - } - pReq << CHAR_PARAM("query_params", root.write().c_str()); - pReq->flags |= NLHRF_NODUMPSEND; - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - if (int iErrorCode = reply.error()) - return iErrorCode; // unknown error - - bool bLoadAll = m_bLoadAll; - auto &data = reply.data()["viewer"]["messenger_contacts"]; - - for (auto &it : data["nodes"]) { - auto &n = it["represented_profile"]; - CMStringW wszId(n["id"].as_mstring()); - __int64 id = _wtoi64(wszId); - - MCONTACT hContact; - if (id != m_uid) { - bool bIsFriend = bLoadAll || n["friendship_status"].as_mstring() == L"ARE_FRIENDS"; - - auto *pUser = FindUser(id); - if (pUser == nullptr) { - if (!bIsFriend) - continue; - pUser = AddContact(wszId, false); - } - else if (!bIsFriend) - Contact::RemoveFromList(pUser->hContact); // adios! - - hContact = pUser->hContact; - } - else hContact = 0; - - if (auto &nName = it["structured_name"]) { - CMStringW wszName(nName["text"].as_mstring()); - setWString(hContact, DBKEY_NICK, wszName); - for (auto &nn : nName["parts"]) { - CMStringW wszPart(nn["part"].as_mstring()); - int offset = nn["offset"].as_int(), length = nn["length"].as_int(); - if (wszPart == L"first") - setWString(hContact, "FirstName", wszName.Mid(offset, length)); - else if (wszPart == L"last") - setWString(hContact, "LastName", wszName.Mid(offset, length)); - } - } - - if (auto &nBirth = n["birthdate"]) { - setDword(hContact, "BirthDay", nBirth["day"].as_int()); - setDword(hContact, "BirthMonth", nBirth["month"].as_int()); - } - - if (auto &nCity = n["current_city"]) - setWString(hContact, "City", nCity["name"].as_mstring()); - - if (auto &nAva = it[(m_bUseBigAvatars) ? "hugePictureUrl" : "bigPictureUrl"]) { - CMStringW wszOldUrl(getMStringW(hContact, DBKEY_AVATAR)), wszNewUrl(nAva["uri"].as_mstring()); - if (wszOldUrl != wszNewUrl) { - bNeedUpdate = true; - setByte(hContact, "UpdateNeeded", 1); - setWString(hContact, DBKEY_AVATAR, wszNewUrl); - } - } - } - - if (!data["page_info"]["has_next_page"].as_bool()) { - debugLogA("Got no next page, exiting", szCursor.c_str()); - break; - } - - szCursor = data["page_info"]["end_cursor"].as_mstring(); - debugLogA("Got cursor: %s", szCursor.c_str()); - } - - if (bNeedUpdate) - ForkThread(&FacebookProto::AvatarsUpdate); - return 0; -} - -bool FacebookProto::RefreshSid() -{ - auto *pReq = CreateRequestGQL(FB_API_QUERY_SEQ_ID); - JSONNode root; root << CHAR_PARAM("1", "0"); - pReq << CHAR_PARAM("query_params", root.write().c_str()); - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - if (reply.error()) - return false; - - auto &n = reply.data()["viewer"]["message_threads"]; - CMStringW wszSid(n["sync_sequence_id"].as_mstring()); - setWString(DBKEY_SID, wszSid); - m_sid = _wtoi64(wszSid); - m_iUnread = n["unread_count"].as_int(); - return true; -} - -FacebookUser* FacebookProto::RefreshThread(JSONNode &n) -{ - if (!n["is_group_thread"].as_bool()) - return nullptr; - - CMStringW chatId(n["thread_key"]["thread_fbid"].as_mstring()); - CMStringW name(n["name"].as_mstring()); - if (name.IsEmpty()) { - for (auto &u : n["all_participants"]["nodes"]) { - auto &ur = u["messaging_actor"]; - CMStringW userId(ur["id"].as_mstring()); - if (_wtoi64(userId) == m_uid) - continue; - - if (!name.IsEmpty()) - name.Append(L", "); - name += ur["name"].as_mstring(); - } - - if (name.GetLength() > 128) { - name.Truncate(125); - name.Append(L"..."); - } - } - - auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, chatId, name); - if (si == nullptr) - return nullptr; - - setWString(si->hContact, DBKEY_ID, chatId); - Chat_AddGroup(si, TranslateT("Participant")); - - for (auto &u : n["all_participants"]["nodes"]) { - auto &ur = u["messaging_actor"]; - CMStringW userId(ur["id"].as_mstring()); - CMStringW userName(ur["name"].as_mstring()); - - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_JOIN }; - gce.pszID.w = chatId; - gce.pszUID.w = userId; - gce.pszNick.w = userName; - gce.bIsMe = _wtoi64(userId) == m_uid; - gce.time = time(0); - Chat_Event(&gce); - } - - Chat_Control(m_szModuleName, chatId, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE); - Chat_Control(m_szModuleName, chatId, SESSION_ONLINE); - - __int64 userId = _wtoi64(chatId); - auto *pUser = FindUser(userId); - - if (pUser == nullptr) { - mir_cslock lck(m_csUsers); - pUser = new FacebookUser(userId, si->hContact, true, true); - m_users.insert(pUser); - } - else { - pUser->hContact = si->hContact; - pUser->bIsChatInitialized = true; - } - - return pUser; -} - -FacebookUser* FacebookProto::RefreshThread(CMStringW &wszId) -{ - auto *pReq = CreateRequestGQL(FB_API_QUERY_THREAD); - pReq << WCHAR_PARAM("query_params", CMStringW(FORMAT, L"{\"0\":[\"%s\"], \"12\":0, \"13\":\"false\"}", wszId.c_str())); - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - if (!reply.error()) { - auto &root = reply.data(); - for (auto &n : root) - return RefreshThread(n); - } - - return nullptr; -} - -void FacebookProto::RefreshThreads() -{ - int threadsLimit = 40; - - auto * pReq = CreateRequestGQL(FB_API_QUERY_THREADS); - JSONNode json; json << INT_PARAM("1", threadsLimit) << CHAR_PARAM("2", "true") << CHAR_PARAM("12", "false") << CHAR_PARAM("13", "false"); - pReq << CHAR_PARAM("query_params", json.write().c_str()); - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - if (!reply.error()) { - auto &root = reply.data()["viewer"]["message_threads"]; - - for (auto &n : root["nodes"]) { - if (n["is_group_thread"].as_bool() && n["is_viewer_subscribed"].as_bool() && !n["has_viewer_archived"].as_bool()) - RefreshThread(n); - } - - // TODO: save timestamp of last message/action/... into DB - // TODO: lower threadsLimit to 10, load next pages if timestamp of last message is higher than timestamp in DB - } -} - -int FacebookProto::RefreshToken() -{ - auto *pReq = CreateRequest(FB_API_URL_AUTH, "authenticate", "auth.login"); - pReq->flags |= NLHRF_NODUMP; - pReq << CHAR_PARAM("email", getMStringA(DBKEY_LOGIN)); - pReq << CHAR_PARAM("password", getMStringA(DBKEY_PASS)); - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - if (reply.error()) - return reply.error(); - - m_szAuthToken = reply.data()["access_token"].as_mstring(); - setString(DBKEY_TOKEN, m_szAuthToken); - - CMStringA m_szUid = reply.data()["uid"].as_mstring(); - setString(DBKEY_ID, m_szUid); - m_uid = _atoi64(m_szUid); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void FacebookProto::ServerThread(void *) -{ - m_QueueCreated = false; - -LBL_Begin: - m_szAuthToken = getMStringA(DBKEY_TOKEN); - if (m_szAuthToken.IsEmpty()) { - if (int iErrorCode = RefreshToken()) { - ConnectionFailed(iErrorCode); - return; - } - } - - int iErrorCode = RefreshContacts(); - if (iErrorCode != 0) { - if (iErrorCode == 401) { - delSetting(DBKEY_TOKEN); - goto LBL_Begin; - } - - ConnectionFailed(iErrorCode); - return; - } - - // connect to MQTT server - m_mqttConn = Netlib_OpenConnection(m_hNetlibUser, "mqtt.facebook.com", 443, 0, NLOCF_SSL); - if (m_mqttConn == nullptr) { - debugLogA("connection failed, exiting"); - ConnectionFailed(); - return; - } - - // send initial packet - MqttLogin(); - - while (!Miranda_IsTerminated()) { - NETLIBSELECT nls = {}; - nls.hReadConns[0] = m_mqttConn; - nls.dwTimeout = 1000; - int ret = Netlib_Select(&nls); - if (ret == SOCKET_ERROR) { - debugLogA("Netlib_Recv() failed, error=%d", WSAGetLastError()); - break; - } - - // no data, continue waiting - if (ret == 0) - continue; - - MqttMessage msg; - if (!MqttRead(msg)) { - debugLogA("MqttRead() failed"); - break; - } - - if (!MqttParse(msg)) { - debugLogA("MqttParse() failed"); - break; - } - } - - debugLogA("exiting ServerThread"); - - Netlib_CloseHandle(m_mqttConn); - m_mqttConn = nullptr; - - OnLoggedOut(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int FacebookProto::OnMarkedRead(WPARAM, LPARAM hDbEvent) -{ - MCONTACT hContact = db_event_getContact(hDbEvent); - if (!hContact) - return 0; - - // filter out only events of my protocol - const char *szProto = Proto_GetBaseAccountName(hContact); - if (mir_strcmp(szProto, m_szModuleName)) - return 0; - - if (m_bKeepUnread) - return 0; - - JSONNode root; root << BOOL_PARAM("state", true) << INT_PARAM("syncSeqId", m_sid) << CHAR_PARAM("mark", "read"); - if (isChatRoom(hContact)) - root << CHAR_PARAM("threadFbId", getMStringA(hContact, DBKEY_ID)); - else - root << CHAR_PARAM("otherUserFbId", getMStringA(hContact, DBKEY_ID)); - MqttPublish("/mark_thread", root); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void FacebookProto::OnPublish(const char *topic, const uint8_t *p, size_t cbLen) -{ - FbThriftReader rdr; - - // that might be a zipped buffer - if (cbLen >= 2) { - size_t dataSize; - void *pData = doUnzip(cbLen, p, dataSize); - if (pData != nullptr) { - debugLogA("UNZIP: %d bytes unzipped ok", dataSize); - Netlib_Dump(m_mqttConn, pData, dataSize, false, 0); - rdr.reset(dataSize, pData); - mir_free(pData); - } - } - - if (rdr.size() == 0) - rdr.reset(cbLen, (void*)p); - - if (!strcmp(topic, "/t_p")) - OnPublishPresence(rdr); - else if (!strcmp(topic, "/t_ms")) - OnPublishMessage(rdr); - else if (!strcmp(topic, "/orca_typing_notifications")) - OnPublishUtn(rdr); -} - -void FacebookProto::OnPublishPresence(FbThriftReader &rdr) -{ - char *str = nullptr; - rdr.readStr(str); - mir_free(str); - - bool bVal; - uint8_t fieldType; - uint16_t fieldId; - rdr.readField(fieldType, fieldId); - assert(fieldType == FB_THRIFT_TYPE_BOOL); - assert(fieldId == 1); - rdr.readBool(bVal); - - rdr.readField(fieldType, fieldId); - assert(fieldType == FB_THRIFT_TYPE_LIST); - assert(fieldId == 1); - - uint32_t size; - rdr.readList(fieldType, size); - assert(fieldType == FB_THRIFT_TYPE_STRUCT); - - debugLogA("Received list of presences: %d records", size); - for (uint32_t i = 0; i < size; i++) { - uint64_t userId, timestamp, voipBits; - rdr.readField(fieldType, fieldId); - assert(fieldType == FB_THRIFT_TYPE_I64); - assert(fieldId == 1); - rdr.readInt64(userId); - - uint32_t u32; - rdr.readField(fieldType, fieldId); - assert(fieldType == FB_THRIFT_TYPE_I32); - assert(fieldId == 1); - rdr.readInt32(u32); - - auto *pUser = FindUser(userId); - if (pUser == nullptr) - debugLogA("Skipping presence from unknown user %lld", userId); - else { - debugLogA("Presence from user %lld => %d", userId, u32); - setWord(pUser->hContact, "Status", (u32 != 0) ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE); - } - - rdr.readField(fieldType, fieldId); - assert(fieldType == FB_THRIFT_TYPE_I64); - assert(fieldId == 1 || fieldId == 3 || fieldId == 4); - rdr.readInt64(timestamp); - - while (!rdr.isStop()) { - rdr.readField(fieldType, fieldId); - assert(fieldType == FB_THRIFT_TYPE_I64 || fieldType == FB_THRIFT_TYPE_I16 || fieldType == FB_THRIFT_TYPE_I32); - rdr.readIntV(voipBits); - } - - rdr.readByte(fieldType); - assert(fieldType == FB_THRIFT_TYPE_STOP); - } - - rdr.readByte(fieldType); - assert(fieldType == FB_THRIFT_TYPE_STOP); -} - -void FacebookProto::OnPublishUtn(FbThriftReader &rdr) -{ - JSONNode root = JSONNode::parse(rdr.rest()); - auto *pUser = FindUser(_wtoi64(root["sender_fbid"].as_mstring())); - if (pUser != nullptr) { - int length = (root["state"].as_int() == 0) ? PROTOTYPE_CONTACTTYPING_OFF : 60; - CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, length); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct -{ - const char *messageType; - void (FacebookProto:: *pFunc)(const JSONNode &); -} -static MsgHandlers[] = -{ - { "deltaNewMessage", &FacebookProto::OnPublishPrivateMessage }, - { "deltaThreadName", &FacebookProto::OnPublishThreadName }, - { "deltaSentMessage", &FacebookProto::OnPublishSentMessage }, - { "deltaReadReceipt", &FacebookProto::OnPublishReadReceipt }, - { "deltaParticipantsAddedToGroupThread", &FacebookProto::OnPublishChatJoin }, - { "deltaParticipantLeftGroupThread", &FacebookProto::OnPublishChatLeave }, -}; - -void FacebookProto::OnPublishMessage(FbThriftReader &rdr) -{ - uint8_t stop; - if (rdr.isStop()) - rdr.readByte(stop); - else { - uint8_t type; - uint16_t id; - rdr.readField(type, id); - _ASSERT(type == FB_THRIFT_TYPE_STRING); - _ASSERT(id == 1 || id == 2); - - char *szShit = nullptr; - rdr.readStr(szShit); - mir_free(szShit); - - rdr.readByte(stop); - } - - CMStringA szJson(rdr.rest()); - debugLogA("MS: <%s>", szJson.c_str()); - JSONNode root = JSONNode::parse(szJson); - - CMStringA errorCode = root["errorCode"].as_mstring(); - if (!errorCode.IsEmpty()) { - if (!m_QueueCreated && (errorCode == "ERROR_QUEUE_OVERFLOW" || errorCode == "ERROR_QUEUE_NOT_FOUND" || errorCode == "ERROR_QUEUE_LOST" || errorCode == "ERROR_QUEUE_EXCEEDS_MAX_DELTAS")) { - m_QueueCreated = true; // prevent queue creation request from being sent twice - delSetting(DBKEY_SYNC_TOKEN); m_szSyncToken.Empty(); - delSetting(DBKEY_SID); m_sid = 0; - if (!RefreshSid()) { - ConnectionFailed(); - return; - } - - MqttQueueConnect(); - } - } - - CMStringW str = root["lastIssuedSeqId"].as_mstring(); - if (!str.IsEmpty()) { - setWString(DBKEY_SID, str); - m_sid = _wtoi64(str); - } - - str = root["syncToken"].as_mstring(); - if (!str.IsEmpty()) { - m_szSyncToken = str; - setString(DBKEY_SYNC_TOKEN, m_szSyncToken); - return; - } - - for (auto &it : root["deltas"]) { - for (auto &handler : MsgHandlers) { - auto &json = it[handler.messageType]; - if (json) { - (this->*(handler.pFunc))(json); - break; - } - } - } -} - -// new message arrived -struct -{ - const char *szTag, *szClientVersion; -} -static facebookClients[] = -{ - { "source:titan:web", "Facebook (website)" }, - { "app_id:256002347743983", "Facebook (Facebook Messenger)" } -}; - -void FacebookProto::FetchAttach(const CMStringA &mid, __int64 fbid, CMStringA &szBody) -{ - for (int iAttempt = 0; iAttempt < 5; iAttempt++) { - auto *pReq = CreateRequest(FB_API_URL_ATTACH, "getAttachment", "messaging.getAttachment"); - pReq << CHAR_PARAM("mid", mid) << INT64_PARAM("aid", fbid); - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - switch (reply.error()) { - case 0: - { - std::string uri = reply.data()["redirect_uri"].as_string(); - std::string type = reply.data()["content_type"].as_string(); - if (!uri.empty()) - szBody.AppendFormat("\r\n%s: %s", TranslateU(type.find("image/") != -1 ? "Picture attachment" : "File attachment"), uri.c_str()); - } - return; - - case 509: // attachment isn't ready, wait a bit and retry - ::Sleep(100); - continue; - - default: // shit happened, exiting - return; - } - } -} - -void FacebookProto::OnPublishPrivateMessage(const JSONNode &root) -{ - auto &metadata = root["messageMetadata"]; - __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring()); - if (!offlineId) { - debugLogA("We care about messages only, event skipped"); - return; - } - - bool bIsChat; - CMStringW wszUserId; - auto *pUser = UserFromJson(metadata, wszUserId, bIsChat); - - if (!bIsChat && pUser == nullptr) - pUser = AddContact(wszUserId, true); - else if (bIsChat && (pUser == nullptr || !pUser->bIsChatInitialized)) // chat room does not exists or is not initialized - pUser = RefreshThread(wszUserId); - - if (pUser == nullptr) { - debugLogA("User not found and adding failed, event skipped"); - return; - } - - for (auto &it : metadata["tags"]) { - auto *szTagName = it.name(); - for (auto &cli : facebookClients) { - if (!mir_strcmp(szTagName, cli.szTag)) { - setString(pUser->hContact, "MirVer", cli.szClientVersion); - break; - } - } - } - - CMStringA szId(metadata["messageId"].as_mstring()); - if (CheckOwnMessage(pUser, offlineId, szId)) { - debugLogA("own message <%s> skipped", szId.c_str()); - return; - } - - if (db_event_getById(m_szModuleName, szId)) { - debugLogA("this message <%s> was already stored, exiting", szId.c_str()); - return; - } - - // parse message body - CMStringA szBody(root["body"].as_string().c_str()); - if (szBody.IsEmpty()) - szBody = metadata["snippet"].as_string().c_str(); - - // parse stickers - CMStringA stickerId = root["stickerId"].as_mstring(); - if (!stickerId.IsEmpty()) { - if (ServiceExists(MS_SMILEYADD_LOADCONTACTSMILEYS)) { - CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName); - CreateDirectoryTreeW(wszPath); - - bool bSuccess = false; - CMStringW wszFileName(FORMAT, L"%s\\STK{%S}.png", wszPath.c_str(), stickerId.c_str()); - uint32_t dwAttrib = GetFileAttributesW(wszFileName); - if (dwAttrib == INVALID_FILE_ATTRIBUTES) { - wszFileName.Format(L"%s\\STK{%S}.webp", wszPath.c_str(), stickerId.c_str()); - dwAttrib = GetFileAttributesW(wszFileName); - } - - // new sticker - if (dwAttrib == INVALID_FILE_ATTRIBUTES) { - auto *pReq = CreateRequestGQL(FB_API_QUERY_STICKER); - pReq << CHAR_PARAM("query_params", CMStringA(FORMAT, "{\"0\":[\"%s\"]}", stickerId.c_str())); - pReq->CalcSig(); - - JsonReply reply(ExecuteRequest(pReq)); - if (!reply.error()) { - for (auto &sticker : reply.data()) { - // std::string szUrl = sticker["animated_image"]["uri"].as_string(); - // if (szUrl.empty()) - // szUrl = sticker["thread_image"]["uri"].as_string(); - // else - // wszFileName.Format(L"%s\\STK{%S}.webp", wszPath.c_str(), stickerId.c_str()); - std::string szUrl = sticker["thread_image"]["uri"].as_string(); - - NETLIBHTTPREQUEST req = {}; - req.cbSize = sizeof(req); - req.flags = NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; - req.requestType = REQUEST_GET; - req.szUrl = (char*)szUrl.c_str(); - - NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(m_hNetlibUser, &req); - if (pReply != nullptr && pReply->resultCode == 200 && pReply->pData && pReply->dataLength) { - bSuccess = true; - FILE *out = _wfopen(wszFileName, L"wb"); - fwrite(pReply->pData, 1, pReply->dataLength, out); - fclose(out); - } - } - } - } - else bSuccess = true; - - if (bSuccess) { - if (!szBody.IsEmpty()) - szBody += "\r\n"; - szBody += "STK{" + stickerId + "}"; - - SMADD_CONT cont = { 1, m_szModuleName, wszFileName }; - CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont)); - } - else szBody += TranslateU("Sticker received"); - } - else szBody += TranslateU("SmileyAdd plugin required to support stickers"); - } - - // parse attachments (links, files, ...) - for (auto &it : root["attachments"]) { - // madness... json inside json - CMStringA szJson(it["xmaGraphQL"].as_mstring()); - if (szJson.IsEmpty()) { - __int64 fbid = _wtoi64(it["fbid"].as_mstring()); - if (fbid == 0) { - debugLogA("Neither a GQL nor an inline attachment, nothing to do"); - continue; - } - - // inline attachment, request its description - FetchAttach(szId, fbid, szBody); - continue; - } - - JSONROOT nBody(szJson); - if (!nBody) - continue; - - const JSONNode &attach = (*nBody).at((json_index_t)0)["story_attachment"]; - szBody += "\r\n-----------------------------------"; - - CMStringA str = attach["url"].as_mstring(); - if (!str.IsEmpty()) { - if (str.Left(8) == "fbrpc://") { - int iStart = str.Find("target_url="); - if (iStart != 0) { - CMStringA tmp; - - iStart += 11; - int iEnd = str.Find("&", iStart); - if (iEnd != -1) - tmp = str.Mid(iStart, iEnd - iStart); - else - tmp = str.Right(iStart); - - mir_urlDecode(tmp.GetBuffer()); - szBody.AppendFormat("\r\n\t%s: %s", TranslateU("URL"), tmp.c_str()); - } - } - else szBody.AppendFormat("\r\n\t%s: %s", TranslateU("URL"), str.c_str()); - } - - str = attach["title"].as_string().c_str(); - if (!str.IsEmpty()) - szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Title"), str.c_str()); - - str = attach["source"]["text"].as_string().c_str(); - if (!str.IsEmpty()) - szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Source"), str.c_str()); - - str = attach["description"]["text"].as_string().c_str(); - if (!str.IsEmpty()) - szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Description"), str.c_str()); - - str = attach["media"]["playable_url"].as_string().c_str(); - if (!str.IsEmpty()) - szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Playable media"), str.c_str()); - } - - // if that's a group chat, send it to the room - CMStringW wszActorFbId(metadata["actorFbId"].as_mstring()); - __int64 actorFbId = _wtoi64(wszActorFbId); - - if (pUser->bIsChat) { - szBody.Replace("%", "%%"); - ptrW wszText(mir_utf8decodeW(szBody)); - - // TODO: GC_EVENT_JOIN for chat participants which are missing (for example added later during group chat) - - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE }; - gce.pszID.w = wszUserId; - gce.dwFlags = GCEF_ADDTOLOG; - gce.pszUID.w = wszActorFbId; - gce.pszText.w = wszText; - gce.time = time(0); - gce.bIsMe = actorFbId == m_uid; - Chat_Event(&gce); - - debugLogA("New channel %lld message from %S: %s", pUser->id, gce.pszUID.w, gce.pszText.w); - } - else { // otherwise store a private message - PROTORECVEVENT pre = {}; - pre.timestamp = uint32_t(_wtoi64(metadata["timestamp"].as_mstring()) / 1000); - pre.szMessage = (char *)szBody.c_str(); - pre.szMsgId = (char *)szId.c_str(); - - if (m_uid == actorFbId) - pre.flags |= PREF_SENT; - - ProtoChainRecvMsg(pUser->hContact, &pre); - } -} - -// changing thread name -void FacebookProto::OnPublishThreadName(const JSONNode &root) -{ - auto &metadata = root["messageMetadata"]; - __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring()); - if (!offlineId) { - debugLogA("We care about messages only, event skipped"); - return; - } - - bool bIsChat; - CMStringW wszUserId; - auto *pUser = UserFromJson(metadata, wszUserId, bIsChat); - if (!bIsChat || pUser == nullptr) - return; - - CMStringW wszTitle = root["name"].as_mstring(); - if (!wszTitle.IsEmpty()) - setWString(pUser->hContact, DBKEY_NICK, wszTitle); - else - delSetting(pUser->hContact, DBKEY_NICK); -} - -// user joined chat -void FacebookProto::OnPublishChatJoin(const JSONNode &root) -{ - auto &metadata = root["messageMetadata"]; - __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring()); - if (!offlineId) { - debugLogA("We care about messages only, event skipped"); - return; - } - - bool bIsChat; - CMStringW wszUserId; - auto *pUser = UserFromJson(metadata, wszUserId, bIsChat); - if (!bIsChat || pUser == nullptr) - return; - - CMStringW wszText(metadata["adminText"].as_mstring()); - for (auto &it : root["addedParticipants"]) { - CMStringW wszNick(it["fullName"].as_mstring()), wszId(it["userFbId"].as_mstring()); - - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_JOIN }; - gce.pszID.w = wszUserId; - gce.dwFlags = GCEF_ADDTOLOG; - gce.pszNick.w = wszNick; - gce.pszUID.w = wszId; - gce.pszText.w = wszText; - gce.time = time(0); - gce.bIsMe = _wtoi64(wszId) == m_uid; - Chat_Event(&gce); - } -} - -// user left chat -void FacebookProto::OnPublishChatLeave(const JSONNode &root) -{ - auto &metadata = root["messageMetadata"]; - __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring()); - if (!offlineId) { - debugLogA("We care about messages only, event skipped"); - return; - } - - bool bIsChat; - CMStringW wszUserId; - auto *pUser = UserFromJson(metadata, wszUserId, bIsChat); - if (!bIsChat || pUser == nullptr) - return; - - CMStringW wszText(metadata["adminText"].as_mstring()), wszId(root["leftParticipantFbId"].as_mstring()); - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_PART }; - gce.pszID.w = wszUserId; - gce.dwFlags = GCEF_ADDTOLOG; - gce.pszUID.w = wszId; - gce.pszText.w = wszText; - gce.time = time(0); - gce.bIsMe = _wtoi64(wszId) == m_uid; - Chat_Event(&gce); -} - -// read notification -void FacebookProto::OnPublishReadReceipt(const JSONNode &root) -{ - CMStringW wszUserId; - bool bIsChat; - auto *pUser = UserFromJson(root, wszUserId, bIsChat); - if (pUser == nullptr) { - debugLogA("Message from unknown contact %S, ignored", wszUserId.c_str()); - return; - } - - uint32_t timestamp = _wtoi64(root["watermarkTimestampMs"].as_mstring()); - for (MEVENT ev = db_event_firstUnread(pUser->hContact); ev != 0; ev = db_event_next(pUser->hContact, ev)) { - DBEVENTINFO dbei = {}; - if (db_event_get(ev, &dbei)) - continue; - - if (dbei.timestamp > timestamp) - break; - - if (!dbei.markedRead()) - db_event_markRead(pUser->hContact, ev); - } -} - -// my own message was sent -bool FacebookProto::CheckOwnMessage(FacebookUser *pUser, __int64 offlineId, const char *pszMsgId) -{ - COwnMessage tmp; - if (!ExtractOwnMessage(offlineId, tmp)) - return false; - - if (pUser->bIsChat) { - CMStringW wszId(FORMAT, L"%lld", m_uid); - tmp.wszText.Replace(L"%", L"%%"); - - wchar_t userId[100]; - _i64tow_s(pUser->id, userId, _countof(userId), 10); - - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE }; - gce.pszID.w = userId; - gce.dwFlags = GCEF_ADDTOLOG; - gce.pszUID.w = wszId; - gce.pszText.w = tmp.wszText; - gce.time = time(0); - gce.bIsMe = true; - Chat_Event(&gce); - } - else ProtoBroadcastAck(pUser->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)tmp.reqId, (LPARAM)pszMsgId); - - return true; -} - -void FacebookProto::OnPublishSentMessage(const JSONNode &root) -{ - auto &metadata = root["messageMetadata"]; - - __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring()); - - CMStringW wszUserId; - bool bIsChat; - auto *pUser = UserFromJson(metadata, wszUserId, bIsChat); - if (pUser == nullptr) { - debugLogA("Message from unknown contact %s, ignored", wszUserId.c_str()); - return; - } - - std::string szMsgId(metadata["messageId"].as_string()); - CheckOwnMessage(pUser, offlineId, szMsgId.c_str()); -} +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+void FacebookProto::ConnectionFailed(int iErrorCode)
+{
+ if (iErrorCode) {
+ POPUPDATAW popup;
+ popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR), true);
+ wcscpy_s(popup.lpwzContactName, m_tszUserName);
+ mir_snwprintf(popup.lpwzText, TranslateT("Connection failed with error code %d"), iErrorCode);
+ PUAddPopupW(&popup);
+ }
+
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)m_iStatus, m_iDesiredStatus);
+
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus);
+
+ OnShutdown();
+}
+
+bool FacebookProto::ExtractOwnMessage(__int64 msgId, COwnMessage &res)
+{
+ mir_cslock lck(m_csOwnMessages);
+ for (auto &it : arOwnMessages)
+ if (it->msgId == msgId) {
+ res = *it;
+ arOwnMessages.removeItem(&it);
+ return true;
+ }
+
+ return false;
+}
+
+void FacebookProto::OnLoggedIn()
+{
+ m_mid = 0;
+
+ JSONNode root; root << BOOL_PARAM("foreground", true) << INT_PARAM("keepalive_timeout", 60);
+ MqttPublish("/foreground_state", root);
+
+ MqttSubscribe("/inbox", "/mercury", "/messaging_events", "/orca_presence", "/orca_typing_notifications", "/pp", "/t_ms", "/t_p", "/t_rtc", "/webrtc", "/webrtc_response", 0);
+ MqttUnsubscribe("/orca_message_notifications", 0);
+
+ // if sequence is not initialized, request SID from the server
+ if (m_sid == 0) {
+ if (!RefreshSid()) {
+ ConnectionFailed();
+ return;
+ }
+ }
+
+ // point of no return;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus);
+ m_iStatus = m_iDesiredStatus;
+ m_bOnline = true;
+ m_impl.m_heartBeat.Start(60000);
+
+ // connect message queue
+ MqttQueueConnect();
+
+ // request message threads if needed
+ if (m_bUseGroupchats)
+ RefreshThreads();
+}
+
+void FacebookProto::OnLoggedOut()
+{
+ m_impl.m_heartBeat.Stop();
+ m_bOnline = false;
+
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE);
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
+}
+
+FacebookUser* FacebookProto::AddContact(const CMStringW &wszId, bool bTemp)
+{
+ MCONTACT hContact = db_add_contact();
+ setWString(hContact, DBKEY_ID, wszId);
+ Proto_AddToContact(hContact, m_szModuleName);
+ Clist_SetGroup(hContact, m_wszDefaultGroup);
+ if (bTemp)
+ Contact::RemoveFromList(hContact);
+
+ return FindUser(_wtoi64(wszId));
+}
+
+FacebookUser* FacebookProto::FindUser(__int64 id)
+{
+ mir_cslock lck(m_csUsers);
+ return m_users.find((FacebookUser *)&id);
+}
+
+FacebookUser* FacebookProto::UserFromJson(const JSONNode &root, CMStringW &wszUserId, bool &bIsChat)
+{
+ bIsChat = false;
+ wszUserId = root["threadKey"]["otherUserFbId"].as_mstring();
+ if (wszUserId.IsEmpty()) {
+ // if only thread id is present, it must be a group chat
+ wszUserId = root["threadKey"]["threadFbId"].as_mstring();
+ bIsChat = true;
+ }
+
+ auto *pUser = FindUser(_wtoi64(wszUserId));
+ if (pUser == nullptr) {
+ debugLogA("Message from unknown contact %s, ignored", wszUserId.c_str());
+ return nullptr;
+ }
+
+ if (pUser->bIsChat != bIsChat) {
+ debugLogA("Wrong chat user: %d vs %d for user %lld, ignored", pUser->bIsChat, bIsChat, pUser->id);
+ return nullptr;
+ }
+
+ return pUser;
+}
+
+int FacebookProto::RefreshContacts()
+{
+ CMStringA szCursor;
+ bool bNeedUpdate = false;
+
+ while (true) {
+ JSONNode root; root << CHAR_PARAM("0", "user");
+
+ AsyncHttpRequest *pReq;
+ if (szCursor.IsEmpty()) {
+ pReq = CreateRequestGQL(FB_API_QUERY_CONTACTS);
+ root << INT_PARAM("1", FB_API_CONTACTS_COUNT);
+ }
+ else {
+ pReq = CreateRequestGQL(FB_API_QUERY_CONTACTS_AFTER);
+ root << CHAR_PARAM("1", szCursor) << INT_PARAM("2", FB_API_CONTACTS_COUNT);
+ }
+ pReq << CHAR_PARAM("query_params", root.write().c_str());
+ pReq->flags |= NLHRF_NODUMPSEND;
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ if (int iErrorCode = reply.error())
+ return iErrorCode; // unknown error
+
+ bool bLoadAll = m_bLoadAll;
+ auto &data = reply.data()["viewer"]["messenger_contacts"];
+
+ for (auto &it : data["nodes"]) {
+ auto &n = it["represented_profile"];
+ CMStringW wszId(n["id"].as_mstring());
+ __int64 id = _wtoi64(wszId);
+
+ MCONTACT hContact;
+ if (id != m_uid) {
+ bool bIsFriend = bLoadAll || n["friendship_status"].as_mstring() == L"ARE_FRIENDS";
+
+ auto *pUser = FindUser(id);
+ if (pUser == nullptr) {
+ if (!bIsFriend)
+ continue;
+ pUser = AddContact(wszId, false);
+ }
+ else if (!bIsFriend)
+ Contact::RemoveFromList(pUser->hContact); // adios!
+
+ hContact = pUser->hContact;
+ }
+ else hContact = 0;
+
+ if (auto &nName = it["structured_name"]) {
+ CMStringW wszName(nName["text"].as_mstring());
+ setWString(hContact, DBKEY_NICK, wszName);
+ for (auto &nn : nName["parts"]) {
+ CMStringW wszPart(nn["part"].as_mstring());
+ int offset = nn["offset"].as_int(), length = nn["length"].as_int();
+ if (wszPart == L"first")
+ setWString(hContact, "FirstName", wszName.Mid(offset, length));
+ else if (wszPart == L"last")
+ setWString(hContact, "LastName", wszName.Mid(offset, length));
+ }
+ }
+
+ if (auto &nBirth = n["birthdate"]) {
+ setDword(hContact, "BirthDay", nBirth["day"].as_int());
+ setDword(hContact, "BirthMonth", nBirth["month"].as_int());
+ }
+
+ if (auto &nCity = n["current_city"])
+ setWString(hContact, "City", nCity["name"].as_mstring());
+
+ if (auto &nAva = it[(m_bUseBigAvatars) ? "hugePictureUrl" : "bigPictureUrl"]) {
+ CMStringW wszOldUrl(getMStringW(hContact, DBKEY_AVATAR)), wszNewUrl(nAva["uri"].as_mstring());
+ if (wszOldUrl != wszNewUrl) {
+ bNeedUpdate = true;
+ setByte(hContact, "UpdateNeeded", 1);
+ setWString(hContact, DBKEY_AVATAR, wszNewUrl);
+ }
+ }
+ }
+
+ if (!data["page_info"]["has_next_page"].as_bool()) {
+ debugLogA("Got no next page, exiting", szCursor.c_str());
+ break;
+ }
+
+ szCursor = data["page_info"]["end_cursor"].as_mstring();
+ debugLogA("Got cursor: %s", szCursor.c_str());
+ }
+
+ if (bNeedUpdate)
+ ForkThread(&FacebookProto::AvatarsUpdate);
+ return 0;
+}
+
+bool FacebookProto::RefreshSid()
+{
+ auto *pReq = CreateRequestGQL(FB_API_QUERY_SEQ_ID);
+ JSONNode root; root << CHAR_PARAM("1", "0");
+ pReq << CHAR_PARAM("query_params", root.write().c_str());
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ if (reply.error())
+ return false;
+
+ auto &n = reply.data()["viewer"]["message_threads"];
+ CMStringW wszSid(n["sync_sequence_id"].as_mstring());
+ setWString(DBKEY_SID, wszSid);
+ m_sid = _wtoi64(wszSid);
+ m_iUnread = n["unread_count"].as_int();
+ return true;
+}
+
+FacebookUser* FacebookProto::RefreshThread(JSONNode &n)
+{
+ if (!n["is_group_thread"].as_bool())
+ return nullptr;
+
+ CMStringW chatId(n["thread_key"]["thread_fbid"].as_mstring());
+ CMStringW name(n["name"].as_mstring());
+ if (name.IsEmpty()) {
+ for (auto &u : n["all_participants"]["nodes"]) {
+ auto &ur = u["messaging_actor"];
+ CMStringW userId(ur["id"].as_mstring());
+ if (_wtoi64(userId) == m_uid)
+ continue;
+
+ if (!name.IsEmpty())
+ name.Append(L", ");
+ name += ur["name"].as_mstring();
+ }
+
+ if (name.GetLength() > 128) {
+ name.Truncate(125);
+ name.Append(L"...");
+ }
+ }
+
+ auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, chatId, name);
+ if (si == nullptr)
+ return nullptr;
+
+ setWString(si->hContact, DBKEY_ID, chatId);
+ Chat_AddGroup(si, TranslateT("Participant"));
+
+ for (auto &u : n["all_participants"]["nodes"]) {
+ auto &ur = u["messaging_actor"];
+ CMStringW userId(ur["id"].as_mstring());
+ CMStringW userName(ur["name"].as_mstring());
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_JOIN };
+ gce.pszID.w = chatId;
+ gce.pszUID.w = userId;
+ gce.pszNick.w = userName;
+ gce.bIsMe = _wtoi64(userId) == m_uid;
+ gce.time = time(0);
+ Chat_Event(&gce);
+ }
+
+ Chat_Control(m_szModuleName, chatId, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE);
+ Chat_Control(m_szModuleName, chatId, SESSION_ONLINE);
+
+ __int64 userId = _wtoi64(chatId);
+ auto *pUser = FindUser(userId);
+
+ if (pUser == nullptr) {
+ mir_cslock lck(m_csUsers);
+ pUser = new FacebookUser(userId, si->hContact, true, true);
+ m_users.insert(pUser);
+ }
+ else {
+ pUser->hContact = si->hContact;
+ pUser->bIsChatInitialized = true;
+ }
+
+ return pUser;
+}
+
+FacebookUser* FacebookProto::RefreshThread(CMStringW &wszId)
+{
+ auto *pReq = CreateRequestGQL(FB_API_QUERY_THREAD);
+ pReq << WCHAR_PARAM("query_params", CMStringW(FORMAT, L"{\"0\":[\"%s\"], \"12\":0, \"13\":\"false\"}", wszId.c_str()));
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ if (!reply.error()) {
+ auto &root = reply.data();
+ for (auto &n : root)
+ return RefreshThread(n);
+ }
+
+ return nullptr;
+}
+
+void FacebookProto::RefreshThreads()
+{
+ int threadsLimit = 40;
+
+ auto * pReq = CreateRequestGQL(FB_API_QUERY_THREADS);
+ JSONNode json; json << INT_PARAM("1", threadsLimit) << CHAR_PARAM("2", "true") << CHAR_PARAM("12", "false") << CHAR_PARAM("13", "false");
+ pReq << CHAR_PARAM("query_params", json.write().c_str());
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ if (!reply.error()) {
+ auto &root = reply.data()["viewer"]["message_threads"];
+
+ for (auto &n : root["nodes"]) {
+ if (n["is_group_thread"].as_bool() && n["is_viewer_subscribed"].as_bool() && !n["has_viewer_archived"].as_bool())
+ RefreshThread(n);
+ }
+
+ // TODO: save timestamp of last message/action/... into DB
+ // TODO: lower threadsLimit to 10, load next pages if timestamp of last message is higher than timestamp in DB
+ }
+}
+
+int FacebookProto::RefreshToken()
+{
+ auto *pReq = CreateRequest(FB_API_URL_AUTH, "authenticate", "auth.login");
+ pReq->flags |= NLHRF_NODUMP;
+ pReq << CHAR_PARAM("email", getMStringA(DBKEY_LOGIN));
+ pReq << CHAR_PARAM("password", getMStringA(DBKEY_PASS));
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ if (reply.error())
+ return reply.error();
+
+ m_szAuthToken = reply.data()["access_token"].as_mstring();
+ setString(DBKEY_TOKEN, m_szAuthToken);
+
+ CMStringA m_szUid = reply.data()["uid"].as_mstring();
+ setString(DBKEY_ID, m_szUid);
+ m_uid = _atoi64(m_szUid);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void FacebookProto::ServerThread(void *)
+{
+ m_QueueCreated = false;
+
+LBL_Begin:
+ m_szAuthToken = getMStringA(DBKEY_TOKEN);
+ if (m_szAuthToken.IsEmpty()) {
+ if (int iErrorCode = RefreshToken()) {
+ ConnectionFailed(iErrorCode);
+ return;
+ }
+ }
+
+ int iErrorCode = RefreshContacts();
+ if (iErrorCode != 0) {
+ if (iErrorCode == 401) {
+ delSetting(DBKEY_TOKEN);
+ goto LBL_Begin;
+ }
+
+ ConnectionFailed(iErrorCode);
+ return;
+ }
+
+ // connect to MQTT server
+ m_mqttConn = Netlib_OpenConnection(m_hNetlibUser, "mqtt.facebook.com", 443, 0, NLOCF_SSL);
+ if (m_mqttConn == nullptr) {
+ debugLogA("connection failed, exiting");
+ ConnectionFailed();
+ return;
+ }
+
+ // send initial packet
+ MqttLogin();
+
+ while (!Miranda_IsTerminated()) {
+ NETLIBSELECT nls = {};
+ nls.hReadConns[0] = m_mqttConn;
+ nls.dwTimeout = 1000;
+ int ret = Netlib_Select(&nls);
+ if (ret == SOCKET_ERROR) {
+ debugLogA("Netlib_Recv() failed, error=%d", WSAGetLastError());
+ break;
+ }
+
+ // no data, continue waiting
+ if (ret == 0)
+ continue;
+
+ MqttMessage msg;
+ if (!MqttRead(msg)) {
+ debugLogA("MqttRead() failed");
+ break;
+ }
+
+ if (!MqttParse(msg)) {
+ debugLogA("MqttParse() failed");
+ break;
+ }
+ }
+
+ debugLogA("exiting ServerThread");
+
+ Netlib_CloseHandle(m_mqttConn);
+ m_mqttConn = nullptr;
+
+ OnLoggedOut();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int FacebookProto::OnMarkedRead(WPARAM, LPARAM hDbEvent)
+{
+ MCONTACT hContact = db_event_getContact(hDbEvent);
+ if (!hContact)
+ return 0;
+
+ // filter out only events of my protocol
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (mir_strcmp(szProto, m_szModuleName))
+ return 0;
+
+ if (m_bKeepUnread)
+ return 0;
+
+ JSONNode root; root << BOOL_PARAM("state", true) << INT_PARAM("syncSeqId", m_sid) << CHAR_PARAM("mark", "read");
+ if (isChatRoom(hContact))
+ root << CHAR_PARAM("threadFbId", getMStringA(hContact, DBKEY_ID));
+ else
+ root << CHAR_PARAM("otherUserFbId", getMStringA(hContact, DBKEY_ID));
+ MqttPublish("/mark_thread", root);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void FacebookProto::OnPublish(const char *topic, const uint8_t *p, size_t cbLen)
+{
+ FbThriftReader rdr;
+
+ // that might be a zipped buffer
+ if (cbLen >= 2) {
+ size_t dataSize;
+ void *pData = doUnzip(cbLen, p, dataSize);
+ if (pData != nullptr) {
+ debugLogA("UNZIP: %d bytes unzipped ok", dataSize);
+ Netlib_Dump(m_mqttConn, pData, dataSize, false, 0);
+ rdr.reset(dataSize, pData);
+ mir_free(pData);
+ }
+ }
+
+ if (rdr.size() == 0)
+ rdr.reset(cbLen, (void*)p);
+
+ if (!strcmp(topic, "/t_p"))
+ OnPublishPresence(rdr);
+ else if (!strcmp(topic, "/t_ms"))
+ OnPublishMessage(rdr);
+ else if (!strcmp(topic, "/orca_typing_notifications"))
+ OnPublishUtn(rdr);
+}
+
+void FacebookProto::OnPublishPresence(FbThriftReader &rdr)
+{
+ char *str = nullptr;
+ rdr.readStr(str);
+ mir_free(str);
+
+ bool bVal;
+ uint8_t fieldType;
+ uint16_t fieldId;
+ rdr.readField(fieldType, fieldId);
+ assert(fieldType == FB_THRIFT_TYPE_BOOL);
+ assert(fieldId == 1);
+ rdr.readBool(bVal);
+
+ rdr.readField(fieldType, fieldId);
+ assert(fieldType == FB_THRIFT_TYPE_LIST);
+ assert(fieldId == 1);
+
+ uint32_t size;
+ rdr.readList(fieldType, size);
+ assert(fieldType == FB_THRIFT_TYPE_STRUCT);
+
+ debugLogA("Received list of presences: %d records", size);
+ for (uint32_t i = 0; i < size; i++) {
+ uint64_t userId, timestamp, voipBits;
+ rdr.readField(fieldType, fieldId);
+ assert(fieldType == FB_THRIFT_TYPE_I64);
+ assert(fieldId == 1);
+ rdr.readInt64(userId);
+
+ uint32_t u32;
+ rdr.readField(fieldType, fieldId);
+ assert(fieldType == FB_THRIFT_TYPE_I32);
+ assert(fieldId == 1);
+ rdr.readInt32(u32);
+
+ auto *pUser = FindUser(userId);
+ if (pUser == nullptr)
+ debugLogA("Skipping presence from unknown user %lld", userId);
+ else {
+ debugLogA("Presence from user %lld => %d", userId, u32);
+ setWord(pUser->hContact, "Status", (u32 != 0) ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE);
+ }
+
+ rdr.readField(fieldType, fieldId);
+ assert(fieldType == FB_THRIFT_TYPE_I64);
+ assert(fieldId == 1 || fieldId == 3 || fieldId == 4);
+ rdr.readInt64(timestamp);
+
+ while (!rdr.isStop()) {
+ rdr.readField(fieldType, fieldId);
+ assert(fieldType == FB_THRIFT_TYPE_I64 || fieldType == FB_THRIFT_TYPE_I16 || fieldType == FB_THRIFT_TYPE_I32);
+ rdr.readIntV(voipBits);
+ }
+
+ rdr.readByte(fieldType);
+ assert(fieldType == FB_THRIFT_TYPE_STOP);
+ }
+
+ rdr.readByte(fieldType);
+ assert(fieldType == FB_THRIFT_TYPE_STOP);
+}
+
+void FacebookProto::OnPublishUtn(FbThriftReader &rdr)
+{
+ JSONNode root = JSONNode::parse(rdr.rest());
+ auto *pUser = FindUser(_wtoi64(root["sender_fbid"].as_mstring()));
+ if (pUser != nullptr) {
+ int length = (root["state"].as_int() == 0) ? PROTOTYPE_CONTACTTYPING_OFF : 60;
+ CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, length);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct
+{
+ const char *messageType;
+ void (FacebookProto:: *pFunc)(const JSONNode &);
+}
+static MsgHandlers[] =
+{
+ { "deltaNewMessage", &FacebookProto::OnPublishPrivateMessage },
+ { "deltaThreadName", &FacebookProto::OnPublishThreadName },
+ { "deltaSentMessage", &FacebookProto::OnPublishSentMessage },
+ { "deltaReadReceipt", &FacebookProto::OnPublishReadReceipt },
+ { "deltaParticipantsAddedToGroupThread", &FacebookProto::OnPublishChatJoin },
+ { "deltaParticipantLeftGroupThread", &FacebookProto::OnPublishChatLeave },
+};
+
+void FacebookProto::OnPublishMessage(FbThriftReader &rdr)
+{
+ uint8_t stop;
+ if (rdr.isStop())
+ rdr.readByte(stop);
+ else {
+ uint8_t type;
+ uint16_t id;
+ rdr.readField(type, id);
+ _ASSERT(type == FB_THRIFT_TYPE_STRING);
+ _ASSERT(id == 1 || id == 2);
+
+ char *szShit = nullptr;
+ rdr.readStr(szShit);
+ mir_free(szShit);
+
+ rdr.readByte(stop);
+ }
+
+ CMStringA szJson(rdr.rest());
+ debugLogA("MS: <%s>", szJson.c_str());
+ JSONNode root = JSONNode::parse(szJson);
+
+ CMStringA errorCode = root["errorCode"].as_mstring();
+ if (!errorCode.IsEmpty()) {
+ if (!m_QueueCreated && (errorCode == "ERROR_QUEUE_OVERFLOW" || errorCode == "ERROR_QUEUE_NOT_FOUND" || errorCode == "ERROR_QUEUE_LOST" || errorCode == "ERROR_QUEUE_EXCEEDS_MAX_DELTAS")) {
+ m_QueueCreated = true; // prevent queue creation request from being sent twice
+ delSetting(DBKEY_SYNC_TOKEN); m_szSyncToken.Empty();
+ delSetting(DBKEY_SID); m_sid = 0;
+ if (!RefreshSid()) {
+ ConnectionFailed();
+ return;
+ }
+
+ MqttQueueConnect();
+ }
+ }
+
+ CMStringW str = root["lastIssuedSeqId"].as_mstring();
+ if (!str.IsEmpty()) {
+ setWString(DBKEY_SID, str);
+ m_sid = _wtoi64(str);
+ }
+
+ str = root["syncToken"].as_mstring();
+ if (!str.IsEmpty()) {
+ m_szSyncToken = str;
+ setString(DBKEY_SYNC_TOKEN, m_szSyncToken);
+ return;
+ }
+
+ for (auto &it : root["deltas"]) {
+ for (auto &handler : MsgHandlers) {
+ auto &json = it[handler.messageType];
+ if (json) {
+ (this->*(handler.pFunc))(json);
+ break;
+ }
+ }
+ }
+}
+
+// new message arrived
+struct
+{
+ const char *szTag, *szClientVersion;
+}
+static facebookClients[] =
+{
+ { "source:titan:web", "Facebook (website)" },
+ { "app_id:256002347743983", "Facebook (Facebook Messenger)" }
+};
+
+void FacebookProto::FetchAttach(const CMStringA &mid, __int64 fbid, CMStringA &szBody)
+{
+ for (int iAttempt = 0; iAttempt < 5; iAttempt++) {
+ auto *pReq = CreateRequest(FB_API_URL_ATTACH, "getAttachment", "messaging.getAttachment");
+ pReq << CHAR_PARAM("mid", mid) << INT64_PARAM("aid", fbid);
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ switch (reply.error()) {
+ case 0:
+ {
+ std::string uri = reply.data()["redirect_uri"].as_string();
+ std::string type = reply.data()["content_type"].as_string();
+ if (!uri.empty())
+ szBody.AppendFormat("\r\n%s: %s", TranslateU(type.find("image/") != -1 ? "Picture attachment" : "File attachment"), uri.c_str());
+ }
+ return;
+
+ case 509: // attachment isn't ready, wait a bit and retry
+ ::Sleep(100);
+ continue;
+
+ default: // shit happened, exiting
+ return;
+ }
+ }
+}
+
+void FacebookProto::OnPublishPrivateMessage(const JSONNode &root)
+{
+ auto &metadata = root["messageMetadata"];
+ __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring());
+ if (!offlineId) {
+ debugLogA("We care about messages only, event skipped");
+ return;
+ }
+
+ bool bIsChat;
+ CMStringW wszUserId;
+ auto *pUser = UserFromJson(metadata, wszUserId, bIsChat);
+
+ if (!bIsChat && pUser == nullptr)
+ pUser = AddContact(wszUserId, true);
+ else if (bIsChat && (pUser == nullptr || !pUser->bIsChatInitialized)) // chat room does not exists or is not initialized
+ pUser = RefreshThread(wszUserId);
+
+ if (pUser == nullptr) {
+ debugLogA("User not found and adding failed, event skipped");
+ return;
+ }
+
+ for (auto &it : metadata["tags"]) {
+ auto *szTagName = it.name();
+ for (auto &cli : facebookClients) {
+ if (!mir_strcmp(szTagName, cli.szTag)) {
+ setString(pUser->hContact, "MirVer", cli.szClientVersion);
+ break;
+ }
+ }
+ }
+
+ CMStringA szId(metadata["messageId"].as_mstring());
+ if (CheckOwnMessage(pUser, offlineId, szId)) {
+ debugLogA("own message <%s> skipped", szId.c_str());
+ return;
+ }
+
+ if (db_event_getById(m_szModuleName, szId)) {
+ debugLogA("this message <%s> was already stored, exiting", szId.c_str());
+ return;
+ }
+
+ // parse message body
+ CMStringA szBody(root["body"].as_string().c_str());
+ if (szBody.IsEmpty())
+ szBody = metadata["snippet"].as_string().c_str();
+
+ // parse stickers
+ CMStringA stickerId = root["stickerId"].as_mstring();
+ if (!stickerId.IsEmpty()) {
+ if (ServiceExists(MS_SMILEYADD_LOADCONTACTSMILEYS)) {
+ CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName);
+ CreateDirectoryTreeW(wszPath);
+
+ bool bSuccess = false;
+ CMStringW wszFileName(FORMAT, L"%s\\STK{%S}.png", wszPath.c_str(), stickerId.c_str());
+ uint32_t dwAttrib = GetFileAttributesW(wszFileName);
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
+ wszFileName.Format(L"%s\\STK{%S}.webp", wszPath.c_str(), stickerId.c_str());
+ dwAttrib = GetFileAttributesW(wszFileName);
+ }
+
+ // new sticker
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
+ auto *pReq = CreateRequestGQL(FB_API_QUERY_STICKER);
+ pReq << CHAR_PARAM("query_params", CMStringA(FORMAT, "{\"0\":[\"%s\"]}", stickerId.c_str()));
+ pReq->CalcSig();
+
+ JsonReply reply(ExecuteRequest(pReq));
+ if (!reply.error()) {
+ for (auto &sticker : reply.data()) {
+ // std::string szUrl = sticker["animated_image"]["uri"].as_string();
+ // if (szUrl.empty())
+ // szUrl = sticker["thread_image"]["uri"].as_string();
+ // else
+ // wszFileName.Format(L"%s\\STK{%S}.webp", wszPath.c_str(), stickerId.c_str());
+ std::string szUrl = sticker["thread_image"]["uri"].as_string();
+
+ NETLIBHTTPREQUEST req = {};
+ req.cbSize = sizeof(req);
+ req.flags = NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ req.requestType = REQUEST_GET;
+ req.szUrl = (char*)szUrl.c_str();
+
+ NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(m_hNetlibUser, &req);
+ if (pReply != nullptr && pReply->resultCode == 200 && pReply->pData && pReply->dataLength) {
+ bSuccess = true;
+ FILE *out = _wfopen(wszFileName, L"wb");
+ fwrite(pReply->pData, 1, pReply->dataLength, out);
+ fclose(out);
+ }
+ }
+ }
+ }
+ else bSuccess = true;
+
+ if (bSuccess) {
+ if (!szBody.IsEmpty())
+ szBody += "\r\n";
+ szBody += "STK{" + stickerId + "}";
+
+ SMADD_CONT cont = { 1, m_szModuleName, wszFileName };
+ CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont));
+ }
+ else szBody += TranslateU("Sticker received");
+ }
+ else szBody += TranslateU("SmileyAdd plugin required to support stickers");
+ }
+
+ // parse attachments (links, files, ...)
+ for (auto &it : root["attachments"]) {
+ // madness... json inside json
+ CMStringA szJson(it["xmaGraphQL"].as_mstring());
+ if (szJson.IsEmpty()) {
+ __int64 fbid = _wtoi64(it["fbid"].as_mstring());
+ if (fbid == 0) {
+ debugLogA("Neither a GQL nor an inline attachment, nothing to do");
+ continue;
+ }
+
+ // inline attachment, request its description
+ FetchAttach(szId, fbid, szBody);
+ continue;
+ }
+
+ JSONROOT nBody(szJson);
+ if (!nBody)
+ continue;
+
+ const JSONNode &attach = (*nBody).at((json_index_t)0)["story_attachment"];
+ szBody += "\r\n-----------------------------------";
+
+ CMStringA str = attach["url"].as_mstring();
+ if (!str.IsEmpty()) {
+ if (str.Left(8) == "fbrpc://") {
+ int iStart = str.Find("target_url=");
+ if (iStart != 0) {
+ CMStringA tmp;
+
+ iStart += 11;
+ int iEnd = str.Find("&", iStart);
+ if (iEnd != -1)
+ tmp = str.Mid(iStart, iEnd - iStart);
+ else
+ tmp = str.Right(iStart);
+
+ mir_urlDecode(tmp.GetBuffer());
+ szBody.AppendFormat("\r\n\t%s: %s", TranslateU("URL"), tmp.c_str());
+ }
+ }
+ else szBody.AppendFormat("\r\n\t%s: %s", TranslateU("URL"), str.c_str());
+ }
+
+ str = attach["title"].as_string().c_str();
+ if (!str.IsEmpty())
+ szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Title"), str.c_str());
+
+ str = attach["source"]["text"].as_string().c_str();
+ if (!str.IsEmpty())
+ szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Source"), str.c_str());
+
+ str = attach["description"]["text"].as_string().c_str();
+ if (!str.IsEmpty())
+ szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Description"), str.c_str());
+
+ str = attach["media"]["playable_url"].as_string().c_str();
+ if (!str.IsEmpty())
+ szBody.AppendFormat("\r\n\t%s: %s", TranslateU("Playable media"), str.c_str());
+ }
+
+ // if that's a group chat, send it to the room
+ CMStringW wszActorFbId(metadata["actorFbId"].as_mstring());
+ __int64 actorFbId = _wtoi64(wszActorFbId);
+
+ if (pUser->bIsChat) {
+ szBody.Replace("%", "%%");
+ ptrW wszText(mir_utf8decodeW(szBody));
+
+ // TODO: GC_EVENT_JOIN for chat participants which are missing (for example added later during group chat)
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE };
+ gce.pszID.w = wszUserId;
+ gce.dwFlags = GCEF_ADDTOLOG;
+ gce.pszUID.w = wszActorFbId;
+ gce.pszText.w = wszText;
+ gce.time = time(0);
+ gce.bIsMe = actorFbId == m_uid;
+ Chat_Event(&gce);
+
+ debugLogA("New channel %lld message from %S: %s", pUser->id, gce.pszUID.w, gce.pszText.w);
+ }
+ else { // otherwise store a private message
+ PROTORECVEVENT pre = {};
+ pre.timestamp = uint32_t(_wtoi64(metadata["timestamp"].as_mstring()) / 1000);
+ pre.szMessage = (char *)szBody.c_str();
+ pre.szMsgId = (char *)szId.c_str();
+
+ if (m_uid == actorFbId)
+ pre.flags |= PREF_SENT;
+
+ ProtoChainRecvMsg(pUser->hContact, &pre);
+ }
+}
+
+// changing thread name
+void FacebookProto::OnPublishThreadName(const JSONNode &root)
+{
+ auto &metadata = root["messageMetadata"];
+ __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring());
+ if (!offlineId) {
+ debugLogA("We care about messages only, event skipped");
+ return;
+ }
+
+ bool bIsChat;
+ CMStringW wszUserId;
+ auto *pUser = UserFromJson(metadata, wszUserId, bIsChat);
+ if (!bIsChat || pUser == nullptr)
+ return;
+
+ CMStringW wszTitle = root["name"].as_mstring();
+ if (!wszTitle.IsEmpty())
+ setWString(pUser->hContact, DBKEY_NICK, wszTitle);
+ else
+ delSetting(pUser->hContact, DBKEY_NICK);
+}
+
+// user joined chat
+void FacebookProto::OnPublishChatJoin(const JSONNode &root)
+{
+ auto &metadata = root["messageMetadata"];
+ __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring());
+ if (!offlineId) {
+ debugLogA("We care about messages only, event skipped");
+ return;
+ }
+
+ bool bIsChat;
+ CMStringW wszUserId;
+ auto *pUser = UserFromJson(metadata, wszUserId, bIsChat);
+ if (!bIsChat || pUser == nullptr)
+ return;
+
+ CMStringW wszText(metadata["adminText"].as_mstring());
+ for (auto &it : root["addedParticipants"]) {
+ CMStringW wszNick(it["fullName"].as_mstring()), wszId(it["userFbId"].as_mstring());
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_JOIN };
+ gce.pszID.w = wszUserId;
+ gce.dwFlags = GCEF_ADDTOLOG;
+ gce.pszNick.w = wszNick;
+ gce.pszUID.w = wszId;
+ gce.pszText.w = wszText;
+ gce.time = time(0);
+ gce.bIsMe = _wtoi64(wszId) == m_uid;
+ Chat_Event(&gce);
+ }
+}
+
+// user left chat
+void FacebookProto::OnPublishChatLeave(const JSONNode &root)
+{
+ auto &metadata = root["messageMetadata"];
+ __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring());
+ if (!offlineId) {
+ debugLogA("We care about messages only, event skipped");
+ return;
+ }
+
+ bool bIsChat;
+ CMStringW wszUserId;
+ auto *pUser = UserFromJson(metadata, wszUserId, bIsChat);
+ if (!bIsChat || pUser == nullptr)
+ return;
+
+ CMStringW wszText(metadata["adminText"].as_mstring()), wszId(root["leftParticipantFbId"].as_mstring());
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_PART };
+ gce.pszID.w = wszUserId;
+ gce.dwFlags = GCEF_ADDTOLOG;
+ gce.pszUID.w = wszId;
+ gce.pszText.w = wszText;
+ gce.time = time(0);
+ gce.bIsMe = _wtoi64(wszId) == m_uid;
+ Chat_Event(&gce);
+}
+
+// read notification
+void FacebookProto::OnPublishReadReceipt(const JSONNode &root)
+{
+ CMStringW wszUserId;
+ bool bIsChat;
+ auto *pUser = UserFromJson(root, wszUserId, bIsChat);
+ if (pUser == nullptr) {
+ debugLogA("Message from unknown contact %S, ignored", wszUserId.c_str());
+ return;
+ }
+
+ uint32_t timestamp = _wtoi64(root["watermarkTimestampMs"].as_mstring());
+ for (MEVENT ev = db_event_firstUnread(pUser->hContact); ev != 0; ev = db_event_next(pUser->hContact, ev)) {
+ DBEVENTINFO dbei = {};
+ if (db_event_get(ev, &dbei))
+ continue;
+
+ if (dbei.timestamp > timestamp)
+ break;
+
+ if (!dbei.markedRead())
+ db_event_markRead(pUser->hContact, ev);
+ }
+}
+
+// my own message was sent
+bool FacebookProto::CheckOwnMessage(FacebookUser *pUser, __int64 offlineId, const char *pszMsgId)
+{
+ COwnMessage tmp;
+ if (!ExtractOwnMessage(offlineId, tmp))
+ return false;
+
+ if (pUser->bIsChat) {
+ CMStringW wszId(FORMAT, L"%lld", m_uid);
+ tmp.wszText.Replace(L"%", L"%%");
+
+ wchar_t userId[100];
+ _i64tow_s(pUser->id, userId, _countof(userId), 10);
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE };
+ gce.pszID.w = userId;
+ gce.dwFlags = GCEF_ADDTOLOG;
+ gce.pszUID.w = wszId;
+ gce.pszText.w = tmp.wszText;
+ gce.time = time(0);
+ gce.bIsMe = true;
+ Chat_Event(&gce);
+ }
+ else ProtoBroadcastAck(pUser->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)tmp.reqId, (LPARAM)pszMsgId);
+
+ return true;
+}
+
+void FacebookProto::OnPublishSentMessage(const JSONNode &root)
+{
+ auto &metadata = root["messageMetadata"];
+
+ __int64 offlineId = _wtoi64(metadata["offlineThreadingId"].as_mstring());
+
+ CMStringW wszUserId;
+ bool bIsChat;
+ auto *pUser = UserFromJson(metadata, wszUserId, bIsChat);
+ if (pUser == nullptr) {
+ debugLogA("Message from unknown contact %s, ignored", wszUserId.c_str());
+ return;
+ }
+
+ std::string szMsgId(metadata["messageId"].as_string());
+ CheckOwnMessage(pUser, offlineId, szMsgId.c_str());
+}
diff --git a/protocols/Facebook/src/stdafx.cxx b/protocols/Facebook/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/protocols/Facebook/src/stdafx.cxx +++ b/protocols/Facebook/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/protocols/Facebook/src/stdafx.h b/protocols/Facebook/src/stdafx.h index 3d5f28057c..aadac15f0e 100644 --- a/protocols/Facebook/src/stdafx.h +++ b/protocols/Facebook/src/stdafx.h @@ -1,67 +1,67 @@ -/* - -Facebook plugin for Miranda NG -Copyright © 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -#include <windows.h> -#include <malloc.h> -#include <time.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <assert.h> - -#include <newpluginapi.h> -#include <m_avatars.h> -#include <m_chat_int.h> -#include <m_clistint.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_idle.h> -#include <m_ignore.h> -#include <m_langpack.h> -#include <m_message.h> -#include <m_netlib.h> -#include <m_options.h> -#include <m_popup.h> -#include <m_protosvc.h> -#include <m_protoint.h> -#include <m_skin.h> -#include <m_icolib.h> -#include <m_hotkeys.h> -#include <m_folders.h> -#include <m_smileyadd.h> -#include <m_toptoolbar.h> -#include <m_json.h> -#include <m_imgsrvc.h> -#include <m_http.h> -#include <m_messagestate.h> -#include <m_gui.h> - -#include "../../libs/zlib/src/zlib.h" - -#include "db.h" -#include "dialogs.h" -#include "mqtt.h" -#include "proto.h" -#include "resource.h" -#include "version.h" - -extern bool g_bMessageState; +/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#include <windows.h>
+#include <malloc.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include <newpluginapi.h>
+#include <m_avatars.h>
+#include <m_chat_int.h>
+#include <m_clistint.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_idle.h>
+#include <m_ignore.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_hotkeys.h>
+#include <m_folders.h>
+#include <m_smileyadd.h>
+#include <m_toptoolbar.h>
+#include <m_json.h>
+#include <m_imgsrvc.h>
+#include <m_http.h>
+#include <m_messagestate.h>
+#include <m_gui.h>
+
+#include "../../libs/zlib/src/zlib.h"
+
+#include "db.h"
+#include "dialogs.h"
+#include "mqtt.h"
+#include "proto.h"
+#include "resource.h"
+#include "version.h"
+
+extern bool g_bMessageState;
diff --git a/protocols/Facebook/src/thrift.cpp b/protocols/Facebook/src/thrift.cpp index af0ceb4dd7..c7718c1a60 100644 --- a/protocols/Facebook/src/thrift.cpp +++ b/protocols/Facebook/src/thrift.cpp @@ -1,308 +1,308 @@ -/* - -Facebook plugin for Miranda NG -Copyright 2019-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// FbThrift class members - -#include "stdafx.h" - -static uint8_t encodeType(int type) -{ - switch (type) { - case FB_THRIFT_TYPE_BOOL: - return 2; - case FB_THRIFT_TYPE_BYTE: - return 3; - case FB_THRIFT_TYPE_I16: - return 4; - case FB_THRIFT_TYPE_I32: - return 5; - case FB_THRIFT_TYPE_I64: - return 6; - case FB_THRIFT_TYPE_DOUBLE: - return 7; - case FB_THRIFT_TYPE_STRING: - return 8; - case FB_THRIFT_TYPE_LIST: - return 9; - case FB_THRIFT_TYPE_SET: - return 10; - case FB_THRIFT_TYPE_MAP: - return 11; - case FB_THRIFT_TYPE_STRUCT: - return 12; - default: - return 0; - } -} - -FbThrift& FbThrift::operator<<(uint8_t value) -{ - m_buf.append(&value, 1); - return *this; -} - -FbThrift& FbThrift::operator<<(const char *str) -{ - size_t len = mir_strlen(str); - writeIntV(len); - m_buf.append(str, len); - return *this; -} - -void FbThrift::writeBool(bool bValue) -{ - uint8_t b = (bValue) ? 0x11 : 0x12; - m_buf.append(&b, 1); -} - -void FbThrift::writeBuf(const void *pData, size_t cbLen) -{ - m_buf.append(pData, cbLen); -} - -void FbThrift::writeField(int iType) -{ - uint8_t type = encodeType(iType) + 0x10; - m_buf.append(&type, 1); -} - -void FbThrift::writeField(int iType, int id, int lastid) -{ - uint8_t type = encodeType(iType); - uint8_t diff = uint8_t(id - lastid); - if (diff > 0x0F) { - m_buf.append(&type, 1); - writeInt64(id); - } - else { - type += (diff << 4); - m_buf.append(&type, 1); - } -} - -void FbThrift::writeList(int iType, int size) -{ - uint8_t type = encodeType(iType); - if (size > 14) { - writeIntV(size); - *this << (type | 0xF0); - } - else *this << (type | (size << 4)); -} - -void FbThrift::writeInt16(uint16_t value) -{ - value = htons(value); - m_buf.append(&value, sizeof(value)); -} - -void FbThrift::writeInt32(int32_t value) -{ - writeIntV((value << 1) ^ (value >> 31)); -} - -void FbThrift::writeInt64(int64_t value) -{ - writeIntV((value << 1) ^ (value >> 63)); -} - -void FbThrift::writeIntV(uint64_t value) -{ - bool bLast; - do { - bLast = (value & ~0x7F) == 0; - uint8_t b = value & 0x7F; - if (!bLast) { - b |= 0x80; - value >>= 7; - } - m_buf.append(&b, 1); - } while (!bLast); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// FbThriftReader class members - -uint8_t FbThriftReader::decodeType(int type) -{ - switch (type) { - case 0: - return FB_THRIFT_TYPE_STOP; - case 1: - m_lastBval = m_lastBool = true; - return FB_THRIFT_TYPE_BOOL; - case 2: - m_lastBool = true; - m_lastBval = false; - return FB_THRIFT_TYPE_BOOL; - case 3: - return FB_THRIFT_TYPE_BYTE; - case 4: - return FB_THRIFT_TYPE_I16; - case 5: - return FB_THRIFT_TYPE_I32; - case 6: - return FB_THRIFT_TYPE_I64; - case 7: - return FB_THRIFT_TYPE_DOUBLE; - case 8: - return FB_THRIFT_TYPE_STRING; - case 9: - return FB_THRIFT_TYPE_LIST; - case 10: - return FB_THRIFT_TYPE_SET; - case 11: - return FB_THRIFT_TYPE_MAP; - case 12: - return FB_THRIFT_TYPE_STRUCT; - default: - return 0; - } -} - -bool FbThriftReader::isStop() -{ - byte b; - if (!readByte(b)) - return true; - - offset--; - return b == FB_THRIFT_TYPE_STOP; -} - -bool FbThriftReader::readBool(bool &bVal) -{ - if (m_lastBool) { - bVal = m_lastBval; - m_lastBool = false; - return true; - } - - byte b; - if (!readByte(b)) - return false; - - bVal = b == 0x11; - return true; -} - -bool FbThriftReader::readByte(uint8_t &b) -{ - if (offset >= size()) - return false; - - b = *((uint8_t *)data() + offset); - offset++; - return true; -} - -bool FbThriftReader::readField(uint8_t &type, uint16_t &id) -{ - byte b; - if (!readByte(b)) - return false; - - type = decodeType(b & 0x0F); - id = (b >> 4); - return (id == 0) ? readInt16(id) : true; -} - -bool FbThriftReader::readIntV(uint64_t &val) -{ - uint8_t b; - unsigned i = 0; - val = 0; - - do { - if (!readByte(b)) - return false; - - val |= (uint64_t(b & 0x7F) << i); - i += 7; - } while ((b & 0x80) != 0); - - return true; -} - -bool FbThriftReader::readList(uint8_t &type, uint32_t &size) -{ - byte b; - if (!readByte(b)) - return false; - - type = decodeType(b & 0x0F); - size = b >> 4; - if (size == 0x0F) { - uint64_t tmp; - if (!readIntV(tmp)) - return false; - size = (uint32_t)tmp; - } - return true; -} - -bool FbThriftReader::readStr(char *&val) -{ - uint64_t tmp; - if (!readIntV(tmp)) - return false; - - uint32_t cbLen = (uint32_t)tmp; - if (offset + cbLen >= size()) - return false; - - if (cbLen > 0) { - val = mir_strndup((char *)data() + offset, cbLen); - offset += cbLen; - } - else val = nullptr; - return true; -} - -bool FbThriftReader::readInt16(uint16_t &val) -{ - if (offset + 2 >= size()) - return false; - - val = ntohs(*(u_short *)((char *)data() + offset)); - offset += 2; - return true; -} - -bool FbThriftReader::readInt32(uint32_t &val) -{ - uint64_t tmp; - if (!readIntV(tmp)) - return false; - - val = (uint32_t )tmp; - return true; -} - -bool FbThriftReader::readInt64(uint64_t &val) -{ - uint64_t tmp; - if (!readIntV(tmp)) - return false; - - val = (tmp >> 0x01) ^ -(tmp & 0x01); - return true; -} +/*
+
+Facebook plugin for Miranda NG
+Copyright 2019-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// FbThrift class members
+
+#include "stdafx.h"
+
+static uint8_t encodeType(int type)
+{
+ switch (type) {
+ case FB_THRIFT_TYPE_BOOL:
+ return 2;
+ case FB_THRIFT_TYPE_BYTE:
+ return 3;
+ case FB_THRIFT_TYPE_I16:
+ return 4;
+ case FB_THRIFT_TYPE_I32:
+ return 5;
+ case FB_THRIFT_TYPE_I64:
+ return 6;
+ case FB_THRIFT_TYPE_DOUBLE:
+ return 7;
+ case FB_THRIFT_TYPE_STRING:
+ return 8;
+ case FB_THRIFT_TYPE_LIST:
+ return 9;
+ case FB_THRIFT_TYPE_SET:
+ return 10;
+ case FB_THRIFT_TYPE_MAP:
+ return 11;
+ case FB_THRIFT_TYPE_STRUCT:
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+FbThrift& FbThrift::operator<<(uint8_t value)
+{
+ m_buf.append(&value, 1);
+ return *this;
+}
+
+FbThrift& FbThrift::operator<<(const char *str)
+{
+ size_t len = mir_strlen(str);
+ writeIntV(len);
+ m_buf.append(str, len);
+ return *this;
+}
+
+void FbThrift::writeBool(bool bValue)
+{
+ uint8_t b = (bValue) ? 0x11 : 0x12;
+ m_buf.append(&b, 1);
+}
+
+void FbThrift::writeBuf(const void *pData, size_t cbLen)
+{
+ m_buf.append(pData, cbLen);
+}
+
+void FbThrift::writeField(int iType)
+{
+ uint8_t type = encodeType(iType) + 0x10;
+ m_buf.append(&type, 1);
+}
+
+void FbThrift::writeField(int iType, int id, int lastid)
+{
+ uint8_t type = encodeType(iType);
+ uint8_t diff = uint8_t(id - lastid);
+ if (diff > 0x0F) {
+ m_buf.append(&type, 1);
+ writeInt64(id);
+ }
+ else {
+ type += (diff << 4);
+ m_buf.append(&type, 1);
+ }
+}
+
+void FbThrift::writeList(int iType, int size)
+{
+ uint8_t type = encodeType(iType);
+ if (size > 14) {
+ writeIntV(size);
+ *this << (type | 0xF0);
+ }
+ else *this << (type | (size << 4));
+}
+
+void FbThrift::writeInt16(uint16_t value)
+{
+ value = htons(value);
+ m_buf.append(&value, sizeof(value));
+}
+
+void FbThrift::writeInt32(int32_t value)
+{
+ writeIntV((value << 1) ^ (value >> 31));
+}
+
+void FbThrift::writeInt64(int64_t value)
+{
+ writeIntV((value << 1) ^ (value >> 63));
+}
+
+void FbThrift::writeIntV(uint64_t value)
+{
+ bool bLast;
+ do {
+ bLast = (value & ~0x7F) == 0;
+ uint8_t b = value & 0x7F;
+ if (!bLast) {
+ b |= 0x80;
+ value >>= 7;
+ }
+ m_buf.append(&b, 1);
+ } while (!bLast);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// FbThriftReader class members
+
+uint8_t FbThriftReader::decodeType(int type)
+{
+ switch (type) {
+ case 0:
+ return FB_THRIFT_TYPE_STOP;
+ case 1:
+ m_lastBval = m_lastBool = true;
+ return FB_THRIFT_TYPE_BOOL;
+ case 2:
+ m_lastBool = true;
+ m_lastBval = false;
+ return FB_THRIFT_TYPE_BOOL;
+ case 3:
+ return FB_THRIFT_TYPE_BYTE;
+ case 4:
+ return FB_THRIFT_TYPE_I16;
+ case 5:
+ return FB_THRIFT_TYPE_I32;
+ case 6:
+ return FB_THRIFT_TYPE_I64;
+ case 7:
+ return FB_THRIFT_TYPE_DOUBLE;
+ case 8:
+ return FB_THRIFT_TYPE_STRING;
+ case 9:
+ return FB_THRIFT_TYPE_LIST;
+ case 10:
+ return FB_THRIFT_TYPE_SET;
+ case 11:
+ return FB_THRIFT_TYPE_MAP;
+ case 12:
+ return FB_THRIFT_TYPE_STRUCT;
+ default:
+ return 0;
+ }
+}
+
+bool FbThriftReader::isStop()
+{
+ byte b;
+ if (!readByte(b))
+ return true;
+
+ offset--;
+ return b == FB_THRIFT_TYPE_STOP;
+}
+
+bool FbThriftReader::readBool(bool &bVal)
+{
+ if (m_lastBool) {
+ bVal = m_lastBval;
+ m_lastBool = false;
+ return true;
+ }
+
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ bVal = b == 0x11;
+ return true;
+}
+
+bool FbThriftReader::readByte(uint8_t &b)
+{
+ if (offset >= size())
+ return false;
+
+ b = *((uint8_t *)data() + offset);
+ offset++;
+ return true;
+}
+
+bool FbThriftReader::readField(uint8_t &type, uint16_t &id)
+{
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ type = decodeType(b & 0x0F);
+ id = (b >> 4);
+ return (id == 0) ? readInt16(id) : true;
+}
+
+bool FbThriftReader::readIntV(uint64_t &val)
+{
+ uint8_t b;
+ unsigned i = 0;
+ val = 0;
+
+ do {
+ if (!readByte(b))
+ return false;
+
+ val |= (uint64_t(b & 0x7F) << i);
+ i += 7;
+ } while ((b & 0x80) != 0);
+
+ return true;
+}
+
+bool FbThriftReader::readList(uint8_t &type, uint32_t &size)
+{
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ type = decodeType(b & 0x0F);
+ size = b >> 4;
+ if (size == 0x0F) {
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+ size = (uint32_t)tmp;
+ }
+ return true;
+}
+
+bool FbThriftReader::readStr(char *&val)
+{
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+
+ uint32_t cbLen = (uint32_t)tmp;
+ if (offset + cbLen >= size())
+ return false;
+
+ if (cbLen > 0) {
+ val = mir_strndup((char *)data() + offset, cbLen);
+ offset += cbLen;
+ }
+ else val = nullptr;
+ return true;
+}
+
+bool FbThriftReader::readInt16(uint16_t &val)
+{
+ if (offset + 2 >= size())
+ return false;
+
+ val = ntohs(*(u_short *)((char *)data() + offset));
+ offset += 2;
+ return true;
+}
+
+bool FbThriftReader::readInt32(uint32_t &val)
+{
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+
+ val = (uint32_t )tmp;
+ return true;
+}
+
+bool FbThriftReader::readInt64(uint64_t &val)
+{
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+
+ val = (tmp >> 0x01) ^ -(tmp & 0x01);
+ return true;
+}
diff --git a/protocols/Facebook/src/version.h b/protocols/Facebook/src/version.h index 4a483c02f1..660e8cfe94 100644 --- a/protocols/Facebook/src/version.h +++ b/protocols/Facebook/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Facebook protocol support for Miranda NG."
#define __AUTHOR "Miranda NG Team"
#define __AUTHORWEB "https://miranda-ng.org/p/Facebook"
-#define __COPYRIGHT "© 2019-22 Miranda NG team"
+#define __COPYRIGHT "© 2019-23 Miranda NG team"
diff --git a/protocols/Gadu-Gadu/src/stdafx.cxx b/protocols/Gadu-Gadu/src/stdafx.cxx index 2e0bdaf2a1..e3dcdf89b5 100644 --- a/protocols/Gadu-Gadu/src/stdafx.cxx +++ b/protocols/Gadu-Gadu/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Gadu-Gadu/src/userinfo.cpp b/protocols/Gadu-Gadu/src/userinfo.cpp index 75caf4f09f..66c63d13d9 100644 --- a/protocols/Gadu-Gadu/src/userinfo.cpp +++ b/protocols/Gadu-Gadu/src/userinfo.cpp @@ -1,328 +1,328 @@ -/* -Copyright (C) 2012-22 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 "gg.h" - -//////////////////////////////////////////////////////////////////////////////// -// Info Page UI dialog - -class GaduUserInfoDlg : public CUserInfoPageDlg -{ - GaduProto *gg; - bool updating = false; - - CCtrlCombo cmbGender; - CCtrlButton btnSave; - - void SetValue(int idCtrl, char *szModule, char *szSetting, int special) - { - DBVARIANT dbv = { 0 }; - wchar_t str[256]; - wchar_t *ptstr = nullptr; - wchar_t* valT = nullptr; - bool unspecified; - - dbv.type = DBVT_DELETED; - if (szModule == nullptr) - unspecified = true; - else - unspecified = db_get(m_hContact, szModule, szSetting, &dbv) != 0; - - if (!unspecified) { - switch (dbv.type) { - case DBVT_BYTE: - if (special == SVS_GENDER) { - if (dbv.cVal == 'M') - ptstr = TranslateT("Male"); - else if (dbv.cVal == 'F') - ptstr = TranslateT("Female"); - else - unspecified = 1; - } - else if (special == SVS_MONTH) { - if (dbv.bVal > 0 && dbv.bVal <= 12) { - ptstr = str; - GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, str, _countof(str)); - } - else - unspecified = 1; - } - else if (special == SVS_TIMEZONE) { - if (dbv.cVal == -100) - unspecified = 1; - else { - ptstr = str; - mir_snwprintf(str, dbv.cVal ? L"GMT%+d:%02d" : L"GMT", -dbv.cVal / 2, (dbv.cVal & 1) * 30); - } - } - else { - unspecified = (special == SVS_ZEROISUNSPEC && dbv.bVal == 0); - ptstr = _itow(special == SVS_SIGNED ? dbv.cVal : dbv.bVal, str, 10); - } - break; - case DBVT_WORD: - if (special == SVS_COUNTRY) { - char* pstr = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, dbv.wVal, 0); - if (pstr == nullptr) { - unspecified = 1; - } - else { - ptstr = str; - mir_snwprintf(str, L"%S", pstr); - } - } - else { - unspecified = (special == SVS_ZEROISUNSPEC && dbv.wVal == 0); - ptstr = _itow(special == SVS_SIGNED ? dbv.sVal : dbv.wVal, str, 10); - } - break; - case DBVT_DWORD: - unspecified = (special == SVS_ZEROISUNSPEC && dbv.dVal == 0); - if (special == SVS_IP) { - struct in_addr ia; - ia.S_un.S_addr = htonl(dbv.dVal); - char* pstr = inet_ntoa(ia); - if (pstr == nullptr) { - unspecified = 1; - } - else { - ptstr = str; - mir_snwprintf(str, L"%S", pstr); - } - if (dbv.dVal == 0) - unspecified = 1; - } - else if (special == SVS_GGVERSION) { - ptstr = str; - mir_snwprintf(str, L"%S", (char *)gg_version2string(dbv.dVal)); - } - else { - ptstr = _itow(special == SVS_SIGNED ? dbv.lVal : dbv.dVal, str, 10); - } - break; - case DBVT_ASCIIZ: - unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - ptstr = str; - mir_snwprintf(str, L"%S", dbv.pszVal); - break; - case DBVT_WCHAR: - unspecified = (special == SVS_ZEROISUNSPEC && dbv.pwszVal[0] == '\0'); - ptstr = dbv.pwszVal; - break; - case DBVT_UTF8: - unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - valT = mir_utf8decodeW(dbv.pszVal); - ptstr = str; - wcscpy_s(str, _countof(str), valT); - mir_free(valT); - break; - default: - ptstr = str; - mir_wstrcpy(str, L"???"); - break; - } - } - - if (m_hContact != 0) { - EnableWindow(GetDlgItem(m_hwnd, idCtrl), !unspecified); - if (unspecified) - SetDlgItemText(m_hwnd, idCtrl, TranslateT("<not specified>")); - else - SetDlgItemText(m_hwnd, idCtrl, ptstr); - } - else { - EnableWindow(GetDlgItem(m_hwnd, idCtrl), TRUE); - if (!unspecified) - SetDlgItemText(m_hwnd, idCtrl, ptstr); - } - db_free(&dbv); - } - -public: - GaduUserInfoDlg(GaduProto *_gg, int idDialog) : - CUserInfoPageDlg(g_plugin, idDialog), - gg(_gg), - btnSave(this, IDC_SAVE), - cmbGender(this, IDC_GENDER) - { - btnSave.OnClick = Callback(this, &GaduUserInfoDlg::onClick_Save); - } - - bool OnInitDialog() override - { - // Add genders - cmbGender.AddString(L"", 0); - cmbGender.AddString(TranslateT("Female"), 1); - cmbGender.AddString(TranslateT("Male"), 2); - return true; - } - - bool OnRefresh() override - { - // Show updated message - if (updating) { - MessageBox(nullptr, TranslateT("Your details has been uploaded to the public directory."), - gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); - updating = false; - return false; - } - - char *szProto = (m_hContact == NULL) ? gg->m_szModuleName : Proto_GetBaseAccountName(m_hContact); - if (szProto == nullptr) - return false; - - // Disable when updating - m_bInitialized = false; - - SetValue(IDC_UIN, szProto, GG_KEY_UIN, 0); - SetValue(IDC_REALIP, szProto, GG_KEY_CLIENTIP, SVS_IP); - SetValue(IDC_PORT, szProto, GG_KEY_CLIENTPORT, SVS_ZEROISUNSPEC); - SetValue(IDC_VERSION, szProto, GG_KEY_CLIENTVERSION, SVS_GGVERSION); - - SetValue(IDC_FIRSTNAME, szProto, GG_KEY_PD_FIRSTNAME, SVS_NORMAL); - SetValue(IDC_LASTNAME, szProto, GG_KEY_PD_LASTNAME, SVS_NORMAL); - SetValue(IDC_NICKNAME, szProto, GG_KEY_PD_NICKNAME, SVS_NORMAL); - SetValue(IDC_BIRTHYEAR, szProto, GG_KEY_PD_BIRTHYEAR, SVS_ZEROISUNSPEC); - SetValue(IDC_CITY, szProto, GG_KEY_PD_CITY, SVS_NORMAL); - SetValue(IDC_FAMILYNAME, szProto, GG_KEY_PD_FAMILYNAME, SVS_NORMAL); - SetValue(IDC_CITYORIGIN, szProto, GG_KEY_PD_FAMILYCITY, SVS_NORMAL); - - if (m_hContact) { - SetValue(IDC_GENDER, szProto, GG_KEY_PD_GANDER, SVS_GENDER); - SetValue(IDC_STATUSDESCR, "CList", GG_KEY_STATUSDESCR, SVS_NORMAL); - } - else switch ((char)db_get_b(m_hContact, gg->m_szModuleName, GG_KEY_PD_GANDER, (uint8_t)'?')) { - case 'F': - SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_SETCURSEL, 1, 0); - break; - case 'M': - SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_SETCURSEL, 2, 0); - break; - default: - SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_SETCURSEL, 0, 0); - } - - // Disable when updating - m_bInitialized = true; - return false; - } - - void OnChange() override - { - EnableWindow(GetDlgItem(m_hwnd, IDC_SAVE), TRUE); - } - - void onClick_Save(CCtrlButton*) - { - wchar_t text[256]; - - if (!gg->isonline()) { - MessageBox(nullptr, - TranslateT("You have to be logged in before you can change your details."), - gg->m_tszUserName, MB_OK | MB_ICONSTOP); - return; - } - - EnableWindow(GetDlgItem(m_hwnd, IDC_SAVE), FALSE); - - gg_pubdir50_t req = gg_pubdir50_new(GG_PUBDIR50_WRITE); - if (req == nullptr) - return; - - GetDlgItemText(m_hwnd, IDC_FIRSTNAME, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, T2Utf(text)); - - GetDlgItemText(m_hwnd, IDC_LASTNAME, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, T2Utf(text)); - - GetDlgItemText(m_hwnd, IDC_NICKNAME, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, T2Utf(text)); - - GetDlgItemText(m_hwnd, IDC_CITY, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_CITY, T2Utf(text)); - - // Gadu-Gadu Female <-> Male - switch (SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_GETCURSEL, 0, 0)) { - case 1: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE); - break; - case 2: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE); - break; - default: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, ""); - } - - GetDlgItemText(m_hwnd, IDC_BIRTHYEAR, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, T2Utf(text)); - - GetDlgItemText(m_hwnd, IDC_FAMILYNAME, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, T2Utf(text)); - - GetDlgItemText(m_hwnd, IDC_CITYORIGIN, text, _countof(text)); - if (mir_wstrlen(text)) - gg_pubdir50_add(req, GG_PUBDIR50_FAMILYCITY, T2Utf(text)); - - // Run update - gg_pubdir50_seq_set(req, GG_SEQ_CHINFO); - gg->gg_EnterCriticalSection(&gg->sess_mutex, "gg_detailsdlgproc", 35, "sess_mutex", 1); - gg_pubdir50(gg->m_sess, req); - gg->gg_LeaveCriticalSection(&gg->sess_mutex, "gg_genoptsdlgproc", 35, 1, "sess_mutex", 1); - updating = true; - - gg_pubdir50_free(req); - } -}; - -int GaduProto::details_init(WPARAM wParam, LPARAM hContact) -{ - int idDialog; - - // View/Change My Details - if (hContact == NULL) { - idDialog = IDD_CHINFO_GG; - } - // Other user details - else { - char* szProto = Proto_GetBaseAccountName(hContact); - if (szProto == nullptr) - return 0; - if (mir_strcmp(szProto, m_szModuleName) || isChatRoom(hContact)) - return 0; - idDialog = IDD_INFO_GG; - } - - USERINFOPAGE uip = {}; - uip.flags = ODPF_DONTTRANSLATE | ODPF_UNICODE | ODPF_ICON; - uip.position = -1900000000; - uip.pDialog = new GaduUserInfoDlg(this, idDialog); - uip.szTitle.w = m_tszUserName; - uip.dwInitParam = LPARAM(g_plugin.getIconHandle(IDI_GG)); - g_plugin.addUserInfo(wParam, &uip); - - // Start search for user data - if (hContact == NULL) - GetInfo(NULL, 0); - - return 0; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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 "gg.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Info Page UI dialog
+
+class GaduUserInfoDlg : public CUserInfoPageDlg
+{
+ GaduProto *gg;
+ bool updating = false;
+
+ CCtrlCombo cmbGender;
+ CCtrlButton btnSave;
+
+ void SetValue(int idCtrl, char *szModule, char *szSetting, int special)
+ {
+ DBVARIANT dbv = { 0 };
+ wchar_t str[256];
+ wchar_t *ptstr = nullptr;
+ wchar_t* valT = nullptr;
+ bool unspecified;
+
+ dbv.type = DBVT_DELETED;
+ if (szModule == nullptr)
+ unspecified = true;
+ else
+ unspecified = db_get(m_hContact, szModule, szSetting, &dbv) != 0;
+
+ if (!unspecified) {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ if (special == SVS_GENDER) {
+ if (dbv.cVal == 'M')
+ ptstr = TranslateT("Male");
+ else if (dbv.cVal == 'F')
+ ptstr = TranslateT("Female");
+ else
+ unspecified = 1;
+ }
+ else if (special == SVS_MONTH) {
+ if (dbv.bVal > 0 && dbv.bVal <= 12) {
+ ptstr = str;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, str, _countof(str));
+ }
+ else
+ unspecified = 1;
+ }
+ else if (special == SVS_TIMEZONE) {
+ if (dbv.cVal == -100)
+ unspecified = 1;
+ else {
+ ptstr = str;
+ mir_snwprintf(str, dbv.cVal ? L"GMT%+d:%02d" : L"GMT", -dbv.cVal / 2, (dbv.cVal & 1) * 30);
+ }
+ }
+ else {
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.bVal == 0);
+ ptstr = _itow(special == SVS_SIGNED ? dbv.cVal : dbv.bVal, str, 10);
+ }
+ break;
+ case DBVT_WORD:
+ if (special == SVS_COUNTRY) {
+ char* pstr = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, dbv.wVal, 0);
+ if (pstr == nullptr) {
+ unspecified = 1;
+ }
+ else {
+ ptstr = str;
+ mir_snwprintf(str, L"%S", pstr);
+ }
+ }
+ else {
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.wVal == 0);
+ ptstr = _itow(special == SVS_SIGNED ? dbv.sVal : dbv.wVal, str, 10);
+ }
+ break;
+ case DBVT_DWORD:
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.dVal == 0);
+ if (special == SVS_IP) {
+ struct in_addr ia;
+ ia.S_un.S_addr = htonl(dbv.dVal);
+ char* pstr = inet_ntoa(ia);
+ if (pstr == nullptr) {
+ unspecified = 1;
+ }
+ else {
+ ptstr = str;
+ mir_snwprintf(str, L"%S", pstr);
+ }
+ if (dbv.dVal == 0)
+ unspecified = 1;
+ }
+ else if (special == SVS_GGVERSION) {
+ ptstr = str;
+ mir_snwprintf(str, L"%S", (char *)gg_version2string(dbv.dVal));
+ }
+ else {
+ ptstr = _itow(special == SVS_SIGNED ? dbv.lVal : dbv.dVal, str, 10);
+ }
+ break;
+ case DBVT_ASCIIZ:
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ ptstr = str;
+ mir_snwprintf(str, L"%S", dbv.pszVal);
+ break;
+ case DBVT_WCHAR:
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.pwszVal[0] == '\0');
+ ptstr = dbv.pwszVal;
+ break;
+ case DBVT_UTF8:
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ valT = mir_utf8decodeW(dbv.pszVal);
+ ptstr = str;
+ wcscpy_s(str, _countof(str), valT);
+ mir_free(valT);
+ break;
+ default:
+ ptstr = str;
+ mir_wstrcpy(str, L"???");
+ break;
+ }
+ }
+
+ if (m_hContact != 0) {
+ EnableWindow(GetDlgItem(m_hwnd, idCtrl), !unspecified);
+ if (unspecified)
+ SetDlgItemText(m_hwnd, idCtrl, TranslateT("<not specified>"));
+ else
+ SetDlgItemText(m_hwnd, idCtrl, ptstr);
+ }
+ else {
+ EnableWindow(GetDlgItem(m_hwnd, idCtrl), TRUE);
+ if (!unspecified)
+ SetDlgItemText(m_hwnd, idCtrl, ptstr);
+ }
+ db_free(&dbv);
+ }
+
+public:
+ GaduUserInfoDlg(GaduProto *_gg, int idDialog) :
+ CUserInfoPageDlg(g_plugin, idDialog),
+ gg(_gg),
+ btnSave(this, IDC_SAVE),
+ cmbGender(this, IDC_GENDER)
+ {
+ btnSave.OnClick = Callback(this, &GaduUserInfoDlg::onClick_Save);
+ }
+
+ bool OnInitDialog() override
+ {
+ // Add genders
+ cmbGender.AddString(L"", 0);
+ cmbGender.AddString(TranslateT("Female"), 1);
+ cmbGender.AddString(TranslateT("Male"), 2);
+ return true;
+ }
+
+ bool OnRefresh() override
+ {
+ // Show updated message
+ if (updating) {
+ MessageBox(nullptr, TranslateT("Your details has been uploaded to the public directory."),
+ gg->m_tszUserName, MB_OK | MB_ICONINFORMATION);
+ updating = false;
+ return false;
+ }
+
+ char *szProto = (m_hContact == NULL) ? gg->m_szModuleName : Proto_GetBaseAccountName(m_hContact);
+ if (szProto == nullptr)
+ return false;
+
+ // Disable when updating
+ m_bInitialized = false;
+
+ SetValue(IDC_UIN, szProto, GG_KEY_UIN, 0);
+ SetValue(IDC_REALIP, szProto, GG_KEY_CLIENTIP, SVS_IP);
+ SetValue(IDC_PORT, szProto, GG_KEY_CLIENTPORT, SVS_ZEROISUNSPEC);
+ SetValue(IDC_VERSION, szProto, GG_KEY_CLIENTVERSION, SVS_GGVERSION);
+
+ SetValue(IDC_FIRSTNAME, szProto, GG_KEY_PD_FIRSTNAME, SVS_NORMAL);
+ SetValue(IDC_LASTNAME, szProto, GG_KEY_PD_LASTNAME, SVS_NORMAL);
+ SetValue(IDC_NICKNAME, szProto, GG_KEY_PD_NICKNAME, SVS_NORMAL);
+ SetValue(IDC_BIRTHYEAR, szProto, GG_KEY_PD_BIRTHYEAR, SVS_ZEROISUNSPEC);
+ SetValue(IDC_CITY, szProto, GG_KEY_PD_CITY, SVS_NORMAL);
+ SetValue(IDC_FAMILYNAME, szProto, GG_KEY_PD_FAMILYNAME, SVS_NORMAL);
+ SetValue(IDC_CITYORIGIN, szProto, GG_KEY_PD_FAMILYCITY, SVS_NORMAL);
+
+ if (m_hContact) {
+ SetValue(IDC_GENDER, szProto, GG_KEY_PD_GANDER, SVS_GENDER);
+ SetValue(IDC_STATUSDESCR, "CList", GG_KEY_STATUSDESCR, SVS_NORMAL);
+ }
+ else switch ((char)db_get_b(m_hContact, gg->m_szModuleName, GG_KEY_PD_GANDER, (uint8_t)'?')) {
+ case 'F':
+ SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_SETCURSEL, 1, 0);
+ break;
+ case 'M':
+ SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_SETCURSEL, 2, 0);
+ break;
+ default:
+ SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_SETCURSEL, 0, 0);
+ }
+
+ // Disable when updating
+ m_bInitialized = true;
+ return false;
+ }
+
+ void OnChange() override
+ {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_SAVE), TRUE);
+ }
+
+ void onClick_Save(CCtrlButton*)
+ {
+ wchar_t text[256];
+
+ if (!gg->isonline()) {
+ MessageBox(nullptr,
+ TranslateT("You have to be logged in before you can change your details."),
+ gg->m_tszUserName, MB_OK | MB_ICONSTOP);
+ return;
+ }
+
+ EnableWindow(GetDlgItem(m_hwnd, IDC_SAVE), FALSE);
+
+ gg_pubdir50_t req = gg_pubdir50_new(GG_PUBDIR50_WRITE);
+ if (req == nullptr)
+ return;
+
+ GetDlgItemText(m_hwnd, IDC_FIRSTNAME, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, T2Utf(text));
+
+ GetDlgItemText(m_hwnd, IDC_LASTNAME, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, T2Utf(text));
+
+ GetDlgItemText(m_hwnd, IDC_NICKNAME, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, T2Utf(text));
+
+ GetDlgItemText(m_hwnd, IDC_CITY, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_CITY, T2Utf(text));
+
+ // Gadu-Gadu Female <-> Male
+ switch (SendDlgItemMessage(m_hwnd, IDC_GENDER, CB_GETCURSEL, 0, 0)) {
+ case 1:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE);
+ break;
+ case 2:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE);
+ break;
+ default:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, "");
+ }
+
+ GetDlgItemText(m_hwnd, IDC_BIRTHYEAR, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, T2Utf(text));
+
+ GetDlgItemText(m_hwnd, IDC_FAMILYNAME, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, T2Utf(text));
+
+ GetDlgItemText(m_hwnd, IDC_CITYORIGIN, text, _countof(text));
+ if (mir_wstrlen(text))
+ gg_pubdir50_add(req, GG_PUBDIR50_FAMILYCITY, T2Utf(text));
+
+ // Run update
+ gg_pubdir50_seq_set(req, GG_SEQ_CHINFO);
+ gg->gg_EnterCriticalSection(&gg->sess_mutex, "gg_detailsdlgproc", 35, "sess_mutex", 1);
+ gg_pubdir50(gg->m_sess, req);
+ gg->gg_LeaveCriticalSection(&gg->sess_mutex, "gg_genoptsdlgproc", 35, 1, "sess_mutex", 1);
+ updating = true;
+
+ gg_pubdir50_free(req);
+ }
+};
+
+int GaduProto::details_init(WPARAM wParam, LPARAM hContact)
+{
+ int idDialog;
+
+ // View/Change My Details
+ if (hContact == NULL) {
+ idDialog = IDD_CHINFO_GG;
+ }
+ // Other user details
+ else {
+ char* szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr)
+ return 0;
+ if (mir_strcmp(szProto, m_szModuleName) || isChatRoom(hContact))
+ return 0;
+ idDialog = IDD_INFO_GG;
+ }
+
+ USERINFOPAGE uip = {};
+ uip.flags = ODPF_DONTTRANSLATE | ODPF_UNICODE | ODPF_ICON;
+ uip.position = -1900000000;
+ uip.pDialog = new GaduUserInfoDlg(this, idDialog);
+ uip.szTitle.w = m_tszUserName;
+ uip.dwInitParam = LPARAM(g_plugin.getIconHandle(IDI_GG));
+ g_plugin.addUserInfo(wParam, &uip);
+
+ // Start search for user data
+ if (hContact == NULL)
+ GetInfo(NULL, 0);
+
+ return 0;
+}
diff --git a/protocols/GmailNotifier/src/stdafx.cxx b/protocols/GmailNotifier/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/GmailNotifier/src/stdafx.cxx +++ b/protocols/GmailNotifier/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/ICQ-WIM/src/groupchats.cpp b/protocols/ICQ-WIM/src/groupchats.cpp index e92811baa1..807bec394a 100644 --- a/protocols/ICQ-WIM/src/groupchats.cpp +++ b/protocols/ICQ-WIM/src/groupchats.cpp @@ -1,292 +1,292 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -void CIcqProto::LoadChatInfo(SESSION_INFO *si) -{ - int memberCount = getDword(si->hContact, "MemberCount"); - for (int i = 0; i < memberCount; i++) { - char buf[100]; - mir_snprintf(buf, "m%d", i); - ptrW szSetting(getWStringA(si->hContact, buf)); - JSONNode *node = json_parse(T2Utf(szSetting)); - if (node == nullptr) - continue; - - CMStringW nick((*node)["nick"].as_mstring()); - CMStringW role((*node)["role"].as_mstring()); - CMStringW sn((*node)["sn"].as_mstring()); - - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_JOIN }; - gce.dwFlags = GCEF_SILENT; - gce.pszID.w = si->ptszID; - gce.pszNick.w = nick; - gce.pszUID.w = sn; - gce.time = ::time(0); - gce.bIsMe = sn == m_szOwnId; - gce.pszStatus.w = TranslateW(role); - Chat_Event(&gce); - - json_delete(node); - } -} - -void CIcqProto::OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - SESSION_INFO *si = (SESSION_INFO*)pReq->pUserInfo; - - RobustReply root(pReply); - if (root.error() != 20000) - return; - - int n = 0; - char buf[100]; - const JSONNode &results = root.results(); - for (auto &it : results["members"]) { - mir_snprintf(buf, "m%d", n++); - - CMStringW friendly = it["friendly"].as_mstring(); - CMStringW role = it["role"].as_mstring(); - CMStringW sn = it["sn"].as_mstring(); - - JSONNode member; - member << WCHAR_PARAM("nick", friendly) << WCHAR_PARAM("role", role) << WCHAR_PARAM("sn", sn); - ptrW text(json_write(&member)); - setWString(si->hContact, buf, text); - } - - setDword(si->hContact, "MemberCount", n); - setId(si->hContact, "InfoVersion", _wtoi64(results["infoVersion"].as_mstring())); - setId(si->hContact, "MembersVersion", _wtoi64(results["membersVersion"].as_mstring())); - - LoadChatInfo(si); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Invitation dialog - -class CGroupchatInviteDlg : public CIcqDlgBase -{ - CCtrlClc m_clc; - SESSION_INFO *m_si; - - void FilterList(CCtrlClc*) - { - for (auto &hContact : Contacts()) { - char *proto = Proto_GetBaseAccountName(hContact); - if (mir_strcmp(proto, m_proto->m_szModuleName) || m_proto->isChatRoom(hContact)) - if (HANDLE hItem = m_clc.FindContact(hContact)) - m_clc.DeleteItem(hItem); - } - } - - void ResetListOptions(CCtrlClc*) - { - m_clc.SetHideEmptyGroups(1); - m_clc.SetHideOfflineRoot(1); - } - -public: - CGroupchatInviteDlg(CIcqProto *ppro, SESSION_INFO *si) : - CIcqDlgBase(ppro, IDD_GROUPCHAT_INVITE), - m_si(si), - m_clc(this, IDC_CLIST) - { - m_clc.OnNewContact = - m_clc.OnListRebuilt = Callback(this, &CGroupchatInviteDlg::FilterList); - m_clc.OnOptionsChanged = Callback(this, &CGroupchatInviteDlg::ResetListOptions); - } - - bool OnInitDialog() override - { - SetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE, - GetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES); - m_clc.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0); - - ResetListOptions(&m_clc); - FilterList(&m_clc); - return true; - } - - bool OnApply() override - { - CMStringW szMembers; - for (auto &hContact : m_proto->AccContacts()) { - if (m_proto->isChatRoom(hContact)) - continue; - - if (HANDLE hItem = m_clc.FindContact(hContact)) { - if (m_clc.GetCheck(hItem)) { - if (!szMembers.IsEmpty()) - szMembers.AppendChar(','); - szMembers.Append(m_proto->GetUserId(hContact)); - } - } - } - - m_proto->Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/mchat/AddChat") - << AIMSID(m_proto) << WCHAR_PARAM("chat_id", m_si->ptszID) << WCHAR_PARAM("members", szMembers)); - return true; - } -}; - -void CIcqProto::InviteUserToChat(SESSION_INFO *si) -{ - CGroupchatInviteDlg dlg(this, si); - if (si->pDlg) - dlg.SetParent(((CDlgBase*)si->pDlg)->GetHwnd()); - dlg.DoModal(); -} - -void CIcqProto::LeaveDestroyChat(SESSION_INFO *si) -{ - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/hideChat") - << AIMSID(this) << WCHAR_PARAM("buddy", si->ptszID) << INT64_PARAM("lastMsgId", getId(si->hContact, DB_KEY_LASTMSGID))); - - Chat_Terminate(si->pszModule, si->ptszID, true); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Group chats - -static gc_item sttLogListItems[] = -{ - { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM }, - { nullptr, 0, MENU_SEPARATOR }, - { LPGENW("&Leave/destroy chat"), IDM_LEAVE, MENU_ITEM } -}; - -int CIcqProto::GroupchatMenuHook(WPARAM, LPARAM lParam) -{ - GCMENUITEMS *gcmi = (GCMENUITEMS*)lParam; - if (gcmi == nullptr) - return 0; - - if (mir_strcmpi(gcmi->pszModule, m_szModuleName)) - return 0; - - SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule); - if (si == nullptr) - return 0; - - if (gcmi->Type == MENU_ON_LOG) - Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin); - - return 0; -} - -int CIcqProto::GroupchatEventHook(WPARAM, LPARAM lParam) -{ - GCHOOK *gch = (GCHOOK*)lParam; - if (gch == nullptr) - return 0; - - if (mir_strcmpi(gch->si->pszModule, m_szModuleName)) - return 0; - - SESSION_INFO *si = g_chatApi.SM_FindSession(gch->si->ptszID, gch->si->pszModule); - if (si == nullptr) - return 1; - - switch (gch->iType) { - case GC_USER_MESSAGE: - rtrimw(gch->ptszText); - if (!mir_wstrlen(gch->ptszText)) - break; - - if (m_bOnline) { - wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText); - Chat_UnescapeTags(wszText); - SendMsg(si->hContact, 0, T2Utf(wszText)); - } - break; - - case GC_USER_PRIVMESS: - Chat_SendPrivateMessage(gch); - break; - - case GC_USER_LOGMENU: - Chat_ProcessLogMenu(si, gch->dwData); - break; - } - - return 1; -} - -void CIcqProto::Chat_ProcessLogMenu(SESSION_INFO *si, int iChoice) -{ - switch (iChoice) { - case IDM_INVITE: - InviteUserToChat(si); - break; - - case IDM_LEAVE: - LeaveDestroyChat(si); - break; - } -} - -void CIcqProto::Chat_SendPrivateMessage(GCHOOK *gch) -{ - MCONTACT hContact; - auto *pCache = FindContactByUIN(gch->ptszUID); - if (pCache == nullptr) { - hContact = CreateContact(gch->ptszUID, true); - setWString(hContact, "Nick", gch->ptszNick); - Contact::Hide(hContact); - db_set_dw(hContact, "Ignore", "Mask1", 0); - } - else hContact = pCache->m_hContact; - - CallService(MS_MSG_SENDMESSAGE, hContact, 0); -} - -void CIcqProto::ProcessGroupChat(const JSONNode &ev) -{ - for (auto &it : ev["mchats"]) { - CMStringW wszId(it["sender"].as_mstring()); - SESSION_INFO *si = g_chatApi.SM_FindSession(wszId, m_szModuleName); - if (si == nullptr) - continue; - - CMStringW method(it["method"].as_mstring()); - GCEVENT gce = { m_szModuleName, 0, (method == "add_members") ? GC_EVENT_JOIN : GC_EVENT_PART }; - gce.pszID.w = si->ptszID; - - int iStart = 0; - CMStringW members(it["members"].as_mstring()); - while (true) { - CMStringW member = members.Tokenize(L",", iStart); - if (member.IsEmpty()) - break; - - auto *pCache = FindContactByUIN(member); - if (pCache == nullptr) - continue; - - gce.pszNick.w = Clist_GetContactDisplayName(pCache->m_hContact); - gce.pszUID.w = member; - gce.time = ::time(0); - gce.bIsMe = member == m_szOwnId; - Chat_Event(&gce); - } - } -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+void CIcqProto::LoadChatInfo(SESSION_INFO *si)
+{
+ int memberCount = getDword(si->hContact, "MemberCount");
+ for (int i = 0; i < memberCount; i++) {
+ char buf[100];
+ mir_snprintf(buf, "m%d", i);
+ ptrW szSetting(getWStringA(si->hContact, buf));
+ JSONNode *node = json_parse(T2Utf(szSetting));
+ if (node == nullptr)
+ continue;
+
+ CMStringW nick((*node)["nick"].as_mstring());
+ CMStringW role((*node)["role"].as_mstring());
+ CMStringW sn((*node)["sn"].as_mstring());
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_JOIN };
+ gce.dwFlags = GCEF_SILENT;
+ gce.pszID.w = si->ptszID;
+ gce.pszNick.w = nick;
+ gce.pszUID.w = sn;
+ gce.time = ::time(0);
+ gce.bIsMe = sn == m_szOwnId;
+ gce.pszStatus.w = TranslateW(role);
+ Chat_Event(&gce);
+
+ json_delete(node);
+ }
+}
+
+void CIcqProto::OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ SESSION_INFO *si = (SESSION_INFO*)pReq->pUserInfo;
+
+ RobustReply root(pReply);
+ if (root.error() != 20000)
+ return;
+
+ int n = 0;
+ char buf[100];
+ const JSONNode &results = root.results();
+ for (auto &it : results["members"]) {
+ mir_snprintf(buf, "m%d", n++);
+
+ CMStringW friendly = it["friendly"].as_mstring();
+ CMStringW role = it["role"].as_mstring();
+ CMStringW sn = it["sn"].as_mstring();
+
+ JSONNode member;
+ member << WCHAR_PARAM("nick", friendly) << WCHAR_PARAM("role", role) << WCHAR_PARAM("sn", sn);
+ ptrW text(json_write(&member));
+ setWString(si->hContact, buf, text);
+ }
+
+ setDword(si->hContact, "MemberCount", n);
+ setId(si->hContact, "InfoVersion", _wtoi64(results["infoVersion"].as_mstring()));
+ setId(si->hContact, "MembersVersion", _wtoi64(results["membersVersion"].as_mstring()));
+
+ LoadChatInfo(si);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Invitation dialog
+
+class CGroupchatInviteDlg : public CIcqDlgBase
+{
+ CCtrlClc m_clc;
+ SESSION_INFO *m_si;
+
+ void FilterList(CCtrlClc*)
+ {
+ for (auto &hContact : Contacts()) {
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (mir_strcmp(proto, m_proto->m_szModuleName) || m_proto->isChatRoom(hContact))
+ if (HANDLE hItem = m_clc.FindContact(hContact))
+ m_clc.DeleteItem(hItem);
+ }
+ }
+
+ void ResetListOptions(CCtrlClc*)
+ {
+ m_clc.SetHideEmptyGroups(1);
+ m_clc.SetHideOfflineRoot(1);
+ }
+
+public:
+ CGroupchatInviteDlg(CIcqProto *ppro, SESSION_INFO *si) :
+ CIcqDlgBase(ppro, IDD_GROUPCHAT_INVITE),
+ m_si(si),
+ m_clc(this, IDC_CLIST)
+ {
+ m_clc.OnNewContact =
+ m_clc.OnListRebuilt = Callback(this, &CGroupchatInviteDlg::FilterList);
+ m_clc.OnOptionsChanged = Callback(this, &CGroupchatInviteDlg::ResetListOptions);
+ }
+
+ bool OnInitDialog() override
+ {
+ SetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
+ m_clc.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0);
+
+ ResetListOptions(&m_clc);
+ FilterList(&m_clc);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ CMStringW szMembers;
+ for (auto &hContact : m_proto->AccContacts()) {
+ if (m_proto->isChatRoom(hContact))
+ continue;
+
+ if (HANDLE hItem = m_clc.FindContact(hContact)) {
+ if (m_clc.GetCheck(hItem)) {
+ if (!szMembers.IsEmpty())
+ szMembers.AppendChar(',');
+ szMembers.Append(m_proto->GetUserId(hContact));
+ }
+ }
+ }
+
+ m_proto->Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/mchat/AddChat")
+ << AIMSID(m_proto) << WCHAR_PARAM("chat_id", m_si->ptszID) << WCHAR_PARAM("members", szMembers));
+ return true;
+ }
+};
+
+void CIcqProto::InviteUserToChat(SESSION_INFO *si)
+{
+ CGroupchatInviteDlg dlg(this, si);
+ if (si->pDlg)
+ dlg.SetParent(((CDlgBase*)si->pDlg)->GetHwnd());
+ dlg.DoModal();
+}
+
+void CIcqProto::LeaveDestroyChat(SESSION_INFO *si)
+{
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/hideChat")
+ << AIMSID(this) << WCHAR_PARAM("buddy", si->ptszID) << INT64_PARAM("lastMsgId", getId(si->hContact, DB_KEY_LASTMSGID)));
+
+ Chat_Terminate(si->pszModule, si->ptszID, true);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Group chats
+
+static gc_item sttLogListItems[] =
+{
+ { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM },
+ { nullptr, 0, MENU_SEPARATOR },
+ { LPGENW("&Leave/destroy chat"), IDM_LEAVE, MENU_ITEM }
+};
+
+int CIcqProto::GroupchatMenuHook(WPARAM, LPARAM lParam)
+{
+ GCMENUITEMS *gcmi = (GCMENUITEMS*)lParam;
+ if (gcmi == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
+ return 0;
+
+ SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule);
+ if (si == nullptr)
+ return 0;
+
+ if (gcmi->Type == MENU_ON_LOG)
+ Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin);
+
+ return 0;
+}
+
+int CIcqProto::GroupchatEventHook(WPARAM, LPARAM lParam)
+{
+ GCHOOK *gch = (GCHOOK*)lParam;
+ if (gch == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gch->si->pszModule, m_szModuleName))
+ return 0;
+
+ SESSION_INFO *si = g_chatApi.SM_FindSession(gch->si->ptszID, gch->si->pszModule);
+ if (si == nullptr)
+ return 1;
+
+ switch (gch->iType) {
+ case GC_USER_MESSAGE:
+ rtrimw(gch->ptszText);
+ if (!mir_wstrlen(gch->ptszText))
+ break;
+
+ if (m_bOnline) {
+ wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText);
+ Chat_UnescapeTags(wszText);
+ SendMsg(si->hContact, 0, T2Utf(wszText));
+ }
+ break;
+
+ case GC_USER_PRIVMESS:
+ Chat_SendPrivateMessage(gch);
+ break;
+
+ case GC_USER_LOGMENU:
+ Chat_ProcessLogMenu(si, gch->dwData);
+ break;
+ }
+
+ return 1;
+}
+
+void CIcqProto::Chat_ProcessLogMenu(SESSION_INFO *si, int iChoice)
+{
+ switch (iChoice) {
+ case IDM_INVITE:
+ InviteUserToChat(si);
+ break;
+
+ case IDM_LEAVE:
+ LeaveDestroyChat(si);
+ break;
+ }
+}
+
+void CIcqProto::Chat_SendPrivateMessage(GCHOOK *gch)
+{
+ MCONTACT hContact;
+ auto *pCache = FindContactByUIN(gch->ptszUID);
+ if (pCache == nullptr) {
+ hContact = CreateContact(gch->ptszUID, true);
+ setWString(hContact, "Nick", gch->ptszNick);
+ Contact::Hide(hContact);
+ db_set_dw(hContact, "Ignore", "Mask1", 0);
+ }
+ else hContact = pCache->m_hContact;
+
+ CallService(MS_MSG_SENDMESSAGE, hContact, 0);
+}
+
+void CIcqProto::ProcessGroupChat(const JSONNode &ev)
+{
+ for (auto &it : ev["mchats"]) {
+ CMStringW wszId(it["sender"].as_mstring());
+ SESSION_INFO *si = g_chatApi.SM_FindSession(wszId, m_szModuleName);
+ if (si == nullptr)
+ continue;
+
+ CMStringW method(it["method"].as_mstring());
+ GCEVENT gce = { m_szModuleName, 0, (method == "add_members") ? GC_EVENT_JOIN : GC_EVENT_PART };
+ gce.pszID.w = si->ptszID;
+
+ int iStart = 0;
+ CMStringW members(it["members"].as_mstring());
+ while (true) {
+ CMStringW member = members.Tokenize(L",", iStart);
+ if (member.IsEmpty())
+ break;
+
+ auto *pCache = FindContactByUIN(member);
+ if (pCache == nullptr)
+ continue;
+
+ gce.pszNick.w = Clist_GetContactDisplayName(pCache->m_hContact);
+ gce.pszUID.w = member;
+ gce.time = ::time(0);
+ gce.bIsMe = member == m_szOwnId;
+ Chat_Event(&gce);
+ }
+ }
+}
diff --git a/protocols/ICQ-WIM/src/http.cpp b/protocols/ICQ-WIM/src/http.cpp index 0d15a71b9f..79910dad11 100644 --- a/protocols/ICQ-WIM/src/http.cpp +++ b/protocols/ICQ-WIM/src/http.cpp @@ -1,385 +1,385 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -#pragma comment(lib, "Rpcrt4.lib") - -void CIcqProto::DropQueue() -{ - mir_cslock lck(m_csHttpQueue); - - while (m_arHttpQueue.getCount()) { - auto *pReq = m_arHttpQueue[0]; - m_arHttpQueue.remove(0); - delete pReq; - } -} - -bool CIcqProto::IsQueueEmpty() -{ - mir_cslock lck(m_csHttpQueue); - return m_arHttpQueue.getCount() == 0; -} - -void __cdecl CIcqProto::ServerThread(void*) -{ - memset(&m_ConnPool, 0, sizeof(m_ConnPool)); - m_bTerminated = false; - - debugLogA("CIcqProto::WorkerThread: %s", "entering"); - - while (true) { - WaitForSingleObject(m_evRequestsQueue, 1000); - if (m_bTerminated) - break; - - while (true) { - bool bNeedSleep = false; - AsyncHttpRequest *pReq; - { - mir_cslock lck(m_csHttpQueue); - if (m_arHttpQueue.getCount() == 0) - break; - - pReq = m_arHttpQueue[0]; - m_arHttpQueue.remove(0); - bNeedSleep = (m_arHttpQueue.getCount() > 1); - } - if (m_bTerminated) - break; - - ExecuteRequest(pReq); - if (bNeedSleep) - Sleep(200); - } - - int ts = time(0); - for (auto &it : m_ConnPool) { - int idx = int(&it - m_ConnPool); - if (idx == CONN_FETCH) - continue; - - if (it.s && it.lastTs + it.timeout < ts) { - debugLogA("Socket #1 (%p) expired", idx, it.s); - Netlib_CloseHandle(it.s); - it.s = nullptr; - it.lastTs = 0; - } - } - } - - m_hWorkerThread = nullptr; - for (auto &it : m_ConnPool) { - if (it.s) - Netlib_CloseHandle(it.s); - it.s = nullptr; - it.lastTs = it.timeout = 0; - } - - debugLogA("CIcqProto::WorkerThread: %s", "leaving"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -AsyncRapiRequest::AsyncRapiRequest(CIcqProto *ppro, const char *pszMethod, MTHttpRequestHandler pFunc) : - AsyncHttpRequest(CONN_RAPI, REQUEST_POST, ICQ_ROBUST_SERVER, pFunc) -{ - params.set_name("params"); - - if (ppro->getByte(DB_KEY_PHONEREG)) { - m_szUrl.AppendChar('/'); - m_szUrl.Append(pszMethod); - - AddHeader("Content-Type", "application/json"); - request << CHAR_PARAM("aimsid", ppro->m_aimsid); - } - else request << CHAR_PARAM("method", pszMethod); -} - -void AsyncRapiRequest::OnPush() -{ - request << CHAR_PARAM("reqId", m_reqId) << params; - - m_szParam = ptrW(json_write(&request)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -AsyncHttpRequest::AsyncHttpRequest(IcqConnection conn, int iType, const char *szUrl, MTHttpRequestHandler pFunc) : - m_conn(conn) -{ - flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_DUMPASTEXT; - requestType = iType; - m_szUrl = szUrl; - m_pFunc = pFunc; - timeout = 10000; - - GUID packetId; - UuidCreate(&packetId); - - RPC_CSTR szId; - UuidToStringA(&packetId, &szId); - strncpy_s(m_reqId, (char*)szId, _TRUNCATE); - RpcStringFreeA(&szId); - - if (iType == REQUEST_POST) { - AddHeader("Content-Type", "application/x-www-form-urlencoded"); - - dataLength = m_szParam.GetLength(); - pData = m_szParam.Detach(); - } -} - -void AsyncHttpRequest::ReplaceJsonParam(const JSONNode &n) -{ - auto *szNodeName = n.name(); - - JSONNode root = JSONNode::parse(m_szParam); - JSONNode& old = root.at(szNodeName); - if (old) - old = n; - else - root.push_back(n); - m_szParam = root.write().c_str(); - - replaceStr(pData, nullptr); - dataLength = 0; -} - -bool CIcqProto::ExecuteRequest(AsyncHttpRequest *pReq) -{ - CMStringA str; - - pReq->szUrl = pReq->m_szUrl.GetBuffer(); - if (!pReq->m_szParam.IsEmpty()) { - if (pReq->requestType == REQUEST_GET) { - str.Format("%s?%s", pReq->m_szUrl.c_str(), pReq->m_szParam.c_str()); - pReq->szUrl = str.GetBuffer(); - } - else { - pReq->dataLength = pReq->m_szParam.GetLength(); - pReq->pData = mir_strdup(pReq->m_szParam); - } - } - - // replace credentials inside JSON body for pure RAPI requests - if (pReq->m_conn == CONN_RAPI && !mir_strcmp(pReq->szUrl, ICQ_ROBUST_SERVER) && !getByte(DB_KEY_PHONEREG)) { - CMStringA szAgent(FORMAT, "%S Mail.ru Windows ICQ (version 10.0.1999)", (wchar_t*)m_szOwnId); - pReq->AddHeader("User-Agent", szAgent); - pReq->AddHeader("Content-Type", "application/json"); - - if (m_szRToken.IsEmpty()) { - if (!RefreshRobustToken(pReq)) { - delete pReq; - return false; - } - } - - if (m_iRClientId) - pReq->ReplaceJsonParam(JSONNode("clientId", m_iRClientId)); - pReq->ReplaceJsonParam(JSONNode("authToken", m_szRToken)); - pReq->dataLength = pReq->m_szParam.GetLength(); - pReq->pData = mir_strdup(pReq->m_szParam); - } - - debugLogA("Executing request %s:\n%s", pReq->m_reqId, pReq->szUrl); - - if (pReq->m_conn != CONN_NONE) { - pReq->flags |= NLHRF_PERSISTENT; - pReq->nlc = m_ConnPool[pReq->m_conn].s; - m_ConnPool[pReq->m_conn].lastTs = time(0); - } - - bool bRet; - NLHR_PTR reply(Netlib_HttpTransaction(m_hNetlibUser, pReq)); - if (reply != nullptr) { - if (pReq->m_conn != CONN_NONE) { - auto &conn = m_ConnPool[pReq->m_conn]; - conn.s = reply->nlc; - conn.timeout = 0; - if (auto *pszHdr = Netlib_GetHeader(reply, "Keep-Alive")) { - int timeout; - if (1 == sscanf(pszHdr, "timeout=%d", &timeout)) - conn.timeout = timeout; - } - } - - if (pReq->m_conn == CONN_RAPI && reply->pData && strstr(reply->pData, "\"code\": 40201")) { - RobustReply r(reply); - if (r.error() == 40201) { // robust token expired - m_szRToken.Empty(); - - // if token refresh succeeded, replace it in the query and push request back - if (!RefreshRobustToken(pReq)) { - delete pReq; - return false; - } - - Push(pReq); - return true; - } - } - - if (pReq->m_pFunc != nullptr) - (this->*(pReq->m_pFunc))(reply, pReq); - - bRet = true; - } - else { - debugLogA("Request %s failed", pReq->m_reqId); - - if (IsStatusConnecting(m_iStatus)) - ConnectionFailed(LOGINERR_NONETWORK); - - if (pReq->m_conn != CONN_NONE) - m_ConnPool[pReq->m_conn].s = nullptr; - - bRet = false; - } - - delete pReq; - return bRet; -} - -void CIcqProto::Push(MHttpRequest *p) -{ - AsyncHttpRequest *pReq = (AsyncHttpRequest*)p; - - pReq->OnPush(); - { - mir_cslock lck(m_csHttpQueue); - m_arHttpQueue.insert(pReq); - } - - SetEvent(m_evRequestsQueue); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -AsyncHttpRequest* operator<<(AsyncHttpRequest *pReq, const AIMSID ¶m) -{ - pReq << CHAR_PARAM("f", "json") << CHAR_PARAM("aimsid", param.m_ppro->m_aimsid) << CHAR_PARAM("r", pReq->m_reqId); - #ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; - #endif - return pReq; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MHttpRequest* operator<<(MHttpRequest *pReq, const GROUP_PARAM ¶m) -{ - if (param.wszValue) { - CMStringW tmp(param.wszValue); - tmp.Replace(L"\\", L">"); - tmp.Replace(L"/", L">"); - pReq << WCHAR_PARAM(param.szName, tmp); - } - return pReq; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply) -{ - if (pReply == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = pReply->resultCode; - if (m_errorCode != 200) - return; - - m_root = json_parse(pReply->pData); - if (m_root == nullptr) { - m_errorCode = 500; - return; - } - - JSONNode &response = (*m_root)["response"]; - m_errorCode = response["statusCode"].as_int(); - m_requestId = response["requestId"].as_mstring(); - m_detailCode = response["statusDetailCode"].as_int(); - m_data = &response["data"]; -} - -JsonReply::~JsonReply() -{ - json_delete(m_root); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -FileReply::FileReply(NETLIBHTTPREQUEST *pReply) -{ - if (pReply == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = pReply->resultCode; - if (m_errorCode != 200) - return; - - m_root = json_parse(pReply->pData); - if (m_root == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = (*m_root)["status"].as_int(); - m_data = &(*m_root)["data"]; -} - -FileReply::~FileReply() -{ - json_delete(m_root); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -RobustReply::RobustReply(NETLIBHTTPREQUEST *pReply) -{ - if (pReply == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = pReply->resultCode; - if (m_errorCode != 200) - return; - - m_root = json_parse(pReply->pData); - if (m_root == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = (*m_root)["status"]["code"].as_int(); - m_result = &(*m_root)["result"]; - m_results = &(*m_root)["results"]; -} - -RobustReply::~RobustReply() -{ - json_delete(m_root); -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#pragma comment(lib, "Rpcrt4.lib")
+
+void CIcqProto::DropQueue()
+{
+ mir_cslock lck(m_csHttpQueue);
+
+ while (m_arHttpQueue.getCount()) {
+ auto *pReq = m_arHttpQueue[0];
+ m_arHttpQueue.remove(0);
+ delete pReq;
+ }
+}
+
+bool CIcqProto::IsQueueEmpty()
+{
+ mir_cslock lck(m_csHttpQueue);
+ return m_arHttpQueue.getCount() == 0;
+}
+
+void __cdecl CIcqProto::ServerThread(void*)
+{
+ memset(&m_ConnPool, 0, sizeof(m_ConnPool));
+ m_bTerminated = false;
+
+ debugLogA("CIcqProto::WorkerThread: %s", "entering");
+
+ while (true) {
+ WaitForSingleObject(m_evRequestsQueue, 1000);
+ if (m_bTerminated)
+ break;
+
+ while (true) {
+ bool bNeedSleep = false;
+ AsyncHttpRequest *pReq;
+ {
+ mir_cslock lck(m_csHttpQueue);
+ if (m_arHttpQueue.getCount() == 0)
+ break;
+
+ pReq = m_arHttpQueue[0];
+ m_arHttpQueue.remove(0);
+ bNeedSleep = (m_arHttpQueue.getCount() > 1);
+ }
+ if (m_bTerminated)
+ break;
+
+ ExecuteRequest(pReq);
+ if (bNeedSleep)
+ Sleep(200);
+ }
+
+ int ts = time(0);
+ for (auto &it : m_ConnPool) {
+ int idx = int(&it - m_ConnPool);
+ if (idx == CONN_FETCH)
+ continue;
+
+ if (it.s && it.lastTs + it.timeout < ts) {
+ debugLogA("Socket #1 (%p) expired", idx, it.s);
+ Netlib_CloseHandle(it.s);
+ it.s = nullptr;
+ it.lastTs = 0;
+ }
+ }
+ }
+
+ m_hWorkerThread = nullptr;
+ for (auto &it : m_ConnPool) {
+ if (it.s)
+ Netlib_CloseHandle(it.s);
+ it.s = nullptr;
+ it.lastTs = it.timeout = 0;
+ }
+
+ debugLogA("CIcqProto::WorkerThread: %s", "leaving");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+AsyncRapiRequest::AsyncRapiRequest(CIcqProto *ppro, const char *pszMethod, MTHttpRequestHandler pFunc) :
+ AsyncHttpRequest(CONN_RAPI, REQUEST_POST, ICQ_ROBUST_SERVER, pFunc)
+{
+ params.set_name("params");
+
+ if (ppro->getByte(DB_KEY_PHONEREG)) {
+ m_szUrl.AppendChar('/');
+ m_szUrl.Append(pszMethod);
+
+ AddHeader("Content-Type", "application/json");
+ request << CHAR_PARAM("aimsid", ppro->m_aimsid);
+ }
+ else request << CHAR_PARAM("method", pszMethod);
+}
+
+void AsyncRapiRequest::OnPush()
+{
+ request << CHAR_PARAM("reqId", m_reqId) << params;
+
+ m_szParam = ptrW(json_write(&request));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpRequest::AsyncHttpRequest(IcqConnection conn, int iType, const char *szUrl, MTHttpRequestHandler pFunc) :
+ m_conn(conn)
+{
+ flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_DUMPASTEXT;
+ requestType = iType;
+ m_szUrl = szUrl;
+ m_pFunc = pFunc;
+ timeout = 10000;
+
+ GUID packetId;
+ UuidCreate(&packetId);
+
+ RPC_CSTR szId;
+ UuidToStringA(&packetId, &szId);
+ strncpy_s(m_reqId, (char*)szId, _TRUNCATE);
+ RpcStringFreeA(&szId);
+
+ if (iType == REQUEST_POST) {
+ AddHeader("Content-Type", "application/x-www-form-urlencoded");
+
+ dataLength = m_szParam.GetLength();
+ pData = m_szParam.Detach();
+ }
+}
+
+void AsyncHttpRequest::ReplaceJsonParam(const JSONNode &n)
+{
+ auto *szNodeName = n.name();
+
+ JSONNode root = JSONNode::parse(m_szParam);
+ JSONNode& old = root.at(szNodeName);
+ if (old)
+ old = n;
+ else
+ root.push_back(n);
+ m_szParam = root.write().c_str();
+
+ replaceStr(pData, nullptr);
+ dataLength = 0;
+}
+
+bool CIcqProto::ExecuteRequest(AsyncHttpRequest *pReq)
+{
+ CMStringA str;
+
+ pReq->szUrl = pReq->m_szUrl.GetBuffer();
+ if (!pReq->m_szParam.IsEmpty()) {
+ if (pReq->requestType == REQUEST_GET) {
+ str.Format("%s?%s", pReq->m_szUrl.c_str(), pReq->m_szParam.c_str());
+ pReq->szUrl = str.GetBuffer();
+ }
+ else {
+ pReq->dataLength = pReq->m_szParam.GetLength();
+ pReq->pData = mir_strdup(pReq->m_szParam);
+ }
+ }
+
+ // replace credentials inside JSON body for pure RAPI requests
+ if (pReq->m_conn == CONN_RAPI && !mir_strcmp(pReq->szUrl, ICQ_ROBUST_SERVER) && !getByte(DB_KEY_PHONEREG)) {
+ CMStringA szAgent(FORMAT, "%S Mail.ru Windows ICQ (version 10.0.1999)", (wchar_t*)m_szOwnId);
+ pReq->AddHeader("User-Agent", szAgent);
+ pReq->AddHeader("Content-Type", "application/json");
+
+ if (m_szRToken.IsEmpty()) {
+ if (!RefreshRobustToken(pReq)) {
+ delete pReq;
+ return false;
+ }
+ }
+
+ if (m_iRClientId)
+ pReq->ReplaceJsonParam(JSONNode("clientId", m_iRClientId));
+ pReq->ReplaceJsonParam(JSONNode("authToken", m_szRToken));
+ pReq->dataLength = pReq->m_szParam.GetLength();
+ pReq->pData = mir_strdup(pReq->m_szParam);
+ }
+
+ debugLogA("Executing request %s:\n%s", pReq->m_reqId, pReq->szUrl);
+
+ if (pReq->m_conn != CONN_NONE) {
+ pReq->flags |= NLHRF_PERSISTENT;
+ pReq->nlc = m_ConnPool[pReq->m_conn].s;
+ m_ConnPool[pReq->m_conn].lastTs = time(0);
+ }
+
+ bool bRet;
+ NLHR_PTR reply(Netlib_HttpTransaction(m_hNetlibUser, pReq));
+ if (reply != nullptr) {
+ if (pReq->m_conn != CONN_NONE) {
+ auto &conn = m_ConnPool[pReq->m_conn];
+ conn.s = reply->nlc;
+ conn.timeout = 0;
+ if (auto *pszHdr = Netlib_GetHeader(reply, "Keep-Alive")) {
+ int timeout;
+ if (1 == sscanf(pszHdr, "timeout=%d", &timeout))
+ conn.timeout = timeout;
+ }
+ }
+
+ if (pReq->m_conn == CONN_RAPI && reply->pData && strstr(reply->pData, "\"code\": 40201")) {
+ RobustReply r(reply);
+ if (r.error() == 40201) { // robust token expired
+ m_szRToken.Empty();
+
+ // if token refresh succeeded, replace it in the query and push request back
+ if (!RefreshRobustToken(pReq)) {
+ delete pReq;
+ return false;
+ }
+
+ Push(pReq);
+ return true;
+ }
+ }
+
+ if (pReq->m_pFunc != nullptr)
+ (this->*(pReq->m_pFunc))(reply, pReq);
+
+ bRet = true;
+ }
+ else {
+ debugLogA("Request %s failed", pReq->m_reqId);
+
+ if (IsStatusConnecting(m_iStatus))
+ ConnectionFailed(LOGINERR_NONETWORK);
+
+ if (pReq->m_conn != CONN_NONE)
+ m_ConnPool[pReq->m_conn].s = nullptr;
+
+ bRet = false;
+ }
+
+ delete pReq;
+ return bRet;
+}
+
+void CIcqProto::Push(MHttpRequest *p)
+{
+ AsyncHttpRequest *pReq = (AsyncHttpRequest*)p;
+
+ pReq->OnPush();
+ {
+ mir_cslock lck(m_csHttpQueue);
+ m_arHttpQueue.insert(pReq);
+ }
+
+ SetEvent(m_evRequestsQueue);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpRequest* operator<<(AsyncHttpRequest *pReq, const AIMSID ¶m)
+{
+ pReq << CHAR_PARAM("f", "json") << CHAR_PARAM("aimsid", param.m_ppro->m_aimsid) << CHAR_PARAM("r", pReq->m_reqId);
+ #ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+ #endif
+ return pReq;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MHttpRequest* operator<<(MHttpRequest *pReq, const GROUP_PARAM ¶m)
+{
+ if (param.wszValue) {
+ CMStringW tmp(param.wszValue);
+ tmp.Replace(L"\\", L">");
+ tmp.Replace(L"/", L">");
+ pReq << WCHAR_PARAM(param.szName, tmp);
+ }
+ return pReq;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = pReply->resultCode;
+ if (m_errorCode != 200)
+ return;
+
+ m_root = json_parse(pReply->pData);
+ if (m_root == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ JSONNode &response = (*m_root)["response"];
+ m_errorCode = response["statusCode"].as_int();
+ m_requestId = response["requestId"].as_mstring();
+ m_detailCode = response["statusDetailCode"].as_int();
+ m_data = &response["data"];
+}
+
+JsonReply::~JsonReply()
+{
+ json_delete(m_root);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+FileReply::FileReply(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = pReply->resultCode;
+ if (m_errorCode != 200)
+ return;
+
+ m_root = json_parse(pReply->pData);
+ if (m_root == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = (*m_root)["status"].as_int();
+ m_data = &(*m_root)["data"];
+}
+
+FileReply::~FileReply()
+{
+ json_delete(m_root);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+RobustReply::RobustReply(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = pReply->resultCode;
+ if (m_errorCode != 200)
+ return;
+
+ m_root = json_parse(pReply->pData);
+ if (m_root == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = (*m_root)["status"]["code"].as_int();
+ m_result = &(*m_root)["result"];
+ m_results = &(*m_root)["results"];
+}
+
+RobustReply::~RobustReply()
+{
+ json_delete(m_root);
+}
diff --git a/protocols/ICQ-WIM/src/ignore.cpp b/protocols/ICQ-WIM/src/ignore.cpp index d2d6dfb36a..a2ce8c098f 100644 --- a/protocols/ICQ-WIM/src/ignore.cpp +++ b/protocols/ICQ-WIM/src/ignore.cpp @@ -1,82 +1,82 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- -// Server permissions - -#include "stdafx.h" - -void CIcqProto::GetPermitDeny() -{ - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/preference/getPermitDeny", &CIcqProto::OnGetPermitDeny) << AIMSID(this)); -} - -void CIcqProto::OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) -{ - JsonReply root(pReply); - if (root.error() == 200) - ProcessPermissions(root.data()); -} - -void CIcqProto::ProcessPermissions(const JSONNode &ev) -{ - { mir_cslock lck(m_csCache); - for (auto &it : m_arCache) - it->m_iApparentMode = 0; - } - - for (auto &it : ev["allows"]) { - auto *p = FindContactByUIN(it.as_mstring()); - if (p) - p->m_iApparentMode = ID_STATUS_ONLINE; - } - - m_bIgnoreListEmpty = true; - for (auto &it : ev["ignores"]) { - CMStringW wszId(it.as_mstring()); - auto *p = FindContactByUIN(wszId); - if (p == nullptr) { - CreateContact(wszId, false); - p = FindContactByUIN(wszId); - } - p->m_iApparentMode = ID_STATUS_OFFLINE; - Contact::Hide(p->m_hContact); - m_bIgnoreListEmpty = false; - } - - { mir_cslock lck(m_csCache); - for (auto &it : m_arCache) { - int oldMode = getDword(it->m_hContact, "ApparentMode"); - if (oldMode != it->m_iApparentMode) { - if (it->m_iApparentMode == 0) - delSetting(it->m_hContact, "ApparentMode"); - else - setDword(it->m_hContact, "ApparentMode", it->m_iApparentMode); - } - } - } -} - -void CIcqProto::SetPermitDeny(const CMStringW &userId, bool bAllow) -{ - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/preference/setPermitDeny") - << AIMSID(this) << WCHAR_PARAM((bAllow) ? "pdIgnoreRemove" : "pdIgnore", userId); - if (!m_bIgnoreListEmpty) - pReq << CHAR_PARAM("pdMode", "denySome"); - Push(pReq); -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+// Server permissions
+
+#include "stdafx.h"
+
+void CIcqProto::GetPermitDeny()
+{
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/preference/getPermitDeny", &CIcqProto::OnGetPermitDeny) << AIMSID(this));
+}
+
+void CIcqProto::OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+{
+ JsonReply root(pReply);
+ if (root.error() == 200)
+ ProcessPermissions(root.data());
+}
+
+void CIcqProto::ProcessPermissions(const JSONNode &ev)
+{
+ { mir_cslock lck(m_csCache);
+ for (auto &it : m_arCache)
+ it->m_iApparentMode = 0;
+ }
+
+ for (auto &it : ev["allows"]) {
+ auto *p = FindContactByUIN(it.as_mstring());
+ if (p)
+ p->m_iApparentMode = ID_STATUS_ONLINE;
+ }
+
+ m_bIgnoreListEmpty = true;
+ for (auto &it : ev["ignores"]) {
+ CMStringW wszId(it.as_mstring());
+ auto *p = FindContactByUIN(wszId);
+ if (p == nullptr) {
+ CreateContact(wszId, false);
+ p = FindContactByUIN(wszId);
+ }
+ p->m_iApparentMode = ID_STATUS_OFFLINE;
+ Contact::Hide(p->m_hContact);
+ m_bIgnoreListEmpty = false;
+ }
+
+ { mir_cslock lck(m_csCache);
+ for (auto &it : m_arCache) {
+ int oldMode = getDword(it->m_hContact, "ApparentMode");
+ if (oldMode != it->m_iApparentMode) {
+ if (it->m_iApparentMode == 0)
+ delSetting(it->m_hContact, "ApparentMode");
+ else
+ setDword(it->m_hContact, "ApparentMode", it->m_iApparentMode);
+ }
+ }
+ }
+}
+
+void CIcqProto::SetPermitDeny(const CMStringW &userId, bool bAllow)
+{
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/preference/setPermitDeny")
+ << AIMSID(this) << WCHAR_PARAM((bAllow) ? "pdIgnoreRemove" : "pdIgnore", userId);
+ if (!m_bIgnoreListEmpty)
+ pReq << CHAR_PARAM("pdMode", "denySome");
+ Push(pReq);
+}
diff --git a/protocols/ICQ-WIM/src/main.cpp b/protocols/ICQ-WIM/src/main.cpp index 68245fff9c..b445bc7b29 100644 --- a/protocols/ICQ-WIM/src/main.cpp +++ b/protocols/ICQ-WIM/src/main.cpp @@ -1,102 +1,102 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -bool g_bSecureIM, g_bMessageState; - -///////////////////////////////////////////////////////////////////////////////////////// - -static PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - { 0xEFB2355B, 0x82B3, 0x4759, { 0xb7, 0xd8, 0x95, 0xf8, 0xe9, 0x50, 0x62, 0x91 } } // {EFB2355B-82B3-4759-B7D8-95F8E9506291} -}; - -CMPlugin g_plugin; - -///////////////////////////////////////////////////////////////////////////////////////// - -struct CMPluginMra : public ACCPROTOPLUGIN<CIcqProto> -{ - CMPluginMra() : ACCPROTOPLUGIN<CIcqProto>("MRA", pluginInfoEx) - { - SetUniqueId(DB_KEY_ID); - } - - void Register() - { - m_hInst = g_plugin.getInst(); - RegisterProtocol(PROTOTYPE_PROTOCOL, g_plugin.fnInit, g_plugin.fnUninit); - } -} -static g_pluginMra; - -///////////////////////////////////////////////////////////////////////////////////////// - -CMPlugin::CMPlugin() : - ACCPROTOPLUGIN<CIcqProto>(MODULENAME, pluginInfoEx) -{ - SetUniqueId(DB_KEY_ID); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; - -///////////////////////////////////////////////////////////////////////////////////////// - -static int ModuleLoad(WPARAM, LPARAM) -{ - g_bSecureIM = ServiceExists("SecureIM/IsContactSecured"); - g_bMessageState = ServiceExists(MS_MESSAGESTATE_UPDATE); - return 0; -} - -static int OnModulesLoaded(WPARAM, LPARAM) -{ - ModuleLoad(0, 0); - return 0; -} - -static IconItem iconList[] = -{ - { LPGEN("E-mail"), "icq_email", IDI_INBOX }, - { LPGEN("E-mail notification"), "icq_email_notif", IDI_MAIL_NOTIFY } -}; - -int CMPlugin::Load() -{ - // register the second instance of this plugin as MRA - g_pluginMra.Register(); - - registerIcon("Protocols/ICQ", iconList, "ICQ"); - - HookEvent(ME_SYSTEM_MODULELOAD, ModuleLoad); - HookEvent(ME_SYSTEM_MODULEUNLOAD, ModuleLoad); - HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); - return 0; -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+bool g_bSecureIM, g_bMessageState;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ { 0xEFB2355B, 0x82B3, 0x4759, { 0xb7, 0xd8, 0x95, 0xf8, 0xe9, 0x50, 0x62, 0x91 } } // {EFB2355B-82B3-4759-B7D8-95F8E9506291}
+};
+
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct CMPluginMra : public ACCPROTOPLUGIN<CIcqProto>
+{
+ CMPluginMra() : ACCPROTOPLUGIN<CIcqProto>("MRA", pluginInfoEx)
+ {
+ SetUniqueId(DB_KEY_ID);
+ }
+
+ void Register()
+ {
+ m_hInst = g_plugin.getInst();
+ RegisterProtocol(PROTOTYPE_PROTOCOL, g_plugin.fnInit, g_plugin.fnUninit);
+ }
+}
+static g_pluginMra;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CMPlugin::CMPlugin() :
+ ACCPROTOPLUGIN<CIcqProto>(MODULENAME, pluginInfoEx)
+{
+ SetUniqueId(DB_KEY_ID);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int ModuleLoad(WPARAM, LPARAM)
+{
+ g_bSecureIM = ServiceExists("SecureIM/IsContactSecured");
+ g_bMessageState = ServiceExists(MS_MESSAGESTATE_UPDATE);
+ return 0;
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ ModuleLoad(0, 0);
+ return 0;
+}
+
+static IconItem iconList[] =
+{
+ { LPGEN("E-mail"), "icq_email", IDI_INBOX },
+ { LPGEN("E-mail notification"), "icq_email_notif", IDI_MAIL_NOTIFY }
+};
+
+int CMPlugin::Load()
+{
+ // register the second instance of this plugin as MRA
+ g_pluginMra.Register();
+
+ registerIcon("Protocols/ICQ", iconList, "ICQ");
+
+ HookEvent(ME_SYSTEM_MODULELOAD, ModuleLoad);
+ HookEvent(ME_SYSTEM_MODULEUNLOAD, ModuleLoad);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ return 0;
+}
diff --git a/protocols/ICQ-WIM/src/mra.cpp b/protocols/ICQ-WIM/src/mra.cpp index f5dcaae595..0c1422ddab 100644 --- a/protocols/ICQ-WIM/src/mra.cpp +++ b/protocols/ICQ-WIM/src/mra.cpp @@ -1,141 +1,141 @@ -/* -Copyright (C) 2012-22 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 CIcqProto::SendMrimLogin(NETLIBHTTPREQUEST *pReply) -{ - if (pReply) { - for (int i=0; i < pReply->headersCount; i++) { - if (!mir_strcmpi(pReply->headers[i].szName, "Set-Cookie")) { - char *p = strchr(pReply->headers[i].szValue, ';'); - if (p) *p = 0; - if (!m_szMraCookie.IsEmpty()) - m_szMraCookie.Append("; "); - - m_szMraCookie.Append(pReply->headers[i].szValue); - } - } - } - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://icqapilogin.mail.ru/auth/mrimLogin", &CIcqProto::OnCheckMrimLogin); - pReq->AddHeader("User-Agent", NETLIB_USER_AGENT); - if (!m_szMraCookie.IsEmpty()) - pReq->AddHeader("Cookie", m_szMraCookie); - pReq << CHAR_PARAM("clientName", "webagent") << INT_PARAM("clientVersion", 711) << CHAR_PARAM("devId", MRA_APP_ID) - << CHAR_PARAM("r", "91640-1626423568") << CHAR_PARAM("f", "json"); -#ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; -#endif - Push(pReq); -} - -void CIcqProto::OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) -{ - JsonReply root(pReply); - switch (root.error()) { - case 200: - case 302: - break; - - case 460: // no cookies at all, obtain them via MRA auth site - { - CMStringW uin(m_szOwnId); - - int iStart = 0; - CMStringW wszLogin = uin.Tokenize(L"@", iStart); - CMStringW wszDomain = uin.Tokenize(L"@", iStart); - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://auth.mail.ru/cgi-bin/auth?from=splash", &CIcqProto::OnCheckMraAuth); - pReq->AddHeader("User-Agent", NETLIB_USER_AGENT); - if (!m_szMraCookie.IsEmpty()) - pReq->AddHeader("Cookie", m_szMraCookie); - pReq << WCHAR_PARAM("Domain", wszDomain) << WCHAR_PARAM("Login", wszLogin) << CHAR_PARAM("Password", m_szPassword) - << INT_PARAM("new_auth_form", 1) << INT_PARAM("saveauth", 1); -#ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; -#endif - Push(pReq); - } - return; - - case 462: // bad cookies, refresh them - if (!m_bError462) { - m_bError462 = true; - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://auth.mail.ru/sdc?JSONP_call=jscb_tmp_c85825&from=https%3A%2F%2Ficqapilogin.mail.ru%2Fauth%2FmrimLogin", &CIcqProto::OnCheckMraAuthFinal); - pReq->flags |= NLHRF_REDIRECT; - pReq->AddHeader("Cookie", m_szMraCookie); - pReq->AddHeader("Referer", "https://webagent.mail.ru/"); - pReq->AddHeader("User-Agent", NETLIB_USER_AGENT); - Push(pReq); - return; - } - - m_bError462 = false; - __fallthrough; - - default: - ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error()); - return; - } - - JSONNode &data = root.data(); - m_szAToken = data["token"]["a"].as_mstring(); - mir_urlDecode(m_szAToken.GetBuffer()); - setString(DB_KEY_ATOKEN, m_szAToken); - - m_szSessionKey = data["sessionKey"].as_mstring(); - - CMStringW szUin = data["loginId"].as_mstring(); - if (szUin) - m_szOwnId = szUin; - - int srvTS = data["hostTime"].as_int(); - m_iTimeShift = (srvTS) ? time(0) - srvTS : 0; - - StartSession(); -} - -void CIcqProto::OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) -{ - JsonReply root(pReply); - switch (root.error()) { - case 200: - case 302: - m_szMraCookie.Empty(); - SendMrimLogin(pReply); - break; - - default: - ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error()); - } -} - -void CIcqProto::OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) -{ - switch (pReply->resultCode) { - case 200: - case 302: - // accumulate sdcs cookie and resend request - SendMrimLogin(pReply); - break; - - default: - ConnectionFailed(LOGINERR_WRONGPROTOCOL, 500); - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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 CIcqProto::SendMrimLogin(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply) {
+ for (int i=0; i < pReply->headersCount; i++) {
+ if (!mir_strcmpi(pReply->headers[i].szName, "Set-Cookie")) {
+ char *p = strchr(pReply->headers[i].szValue, ';');
+ if (p) *p = 0;
+ if (!m_szMraCookie.IsEmpty())
+ m_szMraCookie.Append("; ");
+
+ m_szMraCookie.Append(pReply->headers[i].szValue);
+ }
+ }
+ }
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://icqapilogin.mail.ru/auth/mrimLogin", &CIcqProto::OnCheckMrimLogin);
+ pReq->AddHeader("User-Agent", NETLIB_USER_AGENT);
+ if (!m_szMraCookie.IsEmpty())
+ pReq->AddHeader("Cookie", m_szMraCookie);
+ pReq << CHAR_PARAM("clientName", "webagent") << INT_PARAM("clientVersion", 711) << CHAR_PARAM("devId", MRA_APP_ID)
+ << CHAR_PARAM("r", "91640-1626423568") << CHAR_PARAM("f", "json");
+#ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+#endif
+ Push(pReq);
+}
+
+void CIcqProto::OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ case 302:
+ break;
+
+ case 460: // no cookies at all, obtain them via MRA auth site
+ {
+ CMStringW uin(m_szOwnId);
+
+ int iStart = 0;
+ CMStringW wszLogin = uin.Tokenize(L"@", iStart);
+ CMStringW wszDomain = uin.Tokenize(L"@", iStart);
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://auth.mail.ru/cgi-bin/auth?from=splash", &CIcqProto::OnCheckMraAuth);
+ pReq->AddHeader("User-Agent", NETLIB_USER_AGENT);
+ if (!m_szMraCookie.IsEmpty())
+ pReq->AddHeader("Cookie", m_szMraCookie);
+ pReq << WCHAR_PARAM("Domain", wszDomain) << WCHAR_PARAM("Login", wszLogin) << CHAR_PARAM("Password", m_szPassword)
+ << INT_PARAM("new_auth_form", 1) << INT_PARAM("saveauth", 1);
+#ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+#endif
+ Push(pReq);
+ }
+ return;
+
+ case 462: // bad cookies, refresh them
+ if (!m_bError462) {
+ m_bError462 = true;
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://auth.mail.ru/sdc?JSONP_call=jscb_tmp_c85825&from=https%3A%2F%2Ficqapilogin.mail.ru%2Fauth%2FmrimLogin", &CIcqProto::OnCheckMraAuthFinal);
+ pReq->flags |= NLHRF_REDIRECT;
+ pReq->AddHeader("Cookie", m_szMraCookie);
+ pReq->AddHeader("Referer", "https://webagent.mail.ru/");
+ pReq->AddHeader("User-Agent", NETLIB_USER_AGENT);
+ Push(pReq);
+ return;
+ }
+
+ m_bError462 = false;
+ __fallthrough;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error());
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_szAToken = data["token"]["a"].as_mstring();
+ mir_urlDecode(m_szAToken.GetBuffer());
+ setString(DB_KEY_ATOKEN, m_szAToken);
+
+ m_szSessionKey = data["sessionKey"].as_mstring();
+
+ CMStringW szUin = data["loginId"].as_mstring();
+ if (szUin)
+ m_szOwnId = szUin;
+
+ int srvTS = data["hostTime"].as_int();
+ m_iTimeShift = (srvTS) ? time(0) - srvTS : 0;
+
+ StartSession();
+}
+
+void CIcqProto::OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ case 302:
+ m_szMraCookie.Empty();
+ SendMrimLogin(pReply);
+ break;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error());
+ }
+}
+
+void CIcqProto::OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ switch (pReply->resultCode) {
+ case 200:
+ case 302:
+ // accumulate sdcs cookie and resend request
+ SendMrimLogin(pReply);
+ break;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, 500);
+ }
+}
diff --git a/protocols/ICQ-WIM/src/options.cpp b/protocols/ICQ-WIM/src/options.cpp index 7a53219a3d..2de6641efc 100644 --- a/protocols/ICQ-WIM/src/options.cpp +++ b/protocols/ICQ-WIM/src/options.cpp @@ -1,394 +1,394 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -class CIcqEnterLoginDlg : public CIcqDlgBase -{ - CCtrlEdit edtPass; - CCtrlCheck chkSave; - -public: - CIcqEnterLoginDlg(CIcqProto *ppro) : - CIcqDlgBase(ppro, IDD_LOGINPW), - edtPass(this, IDC_PASSWORD), - chkSave(this, IDC_SAVEPASS) - { - } - - bool OnInitDialog() override - { - m_proto->m_bDlgActive = true; - chkSave.SetState(m_proto->getBool("RememberPass")); - Window_SetIcon_IcoLib(m_hwnd, m_proto->m_hProtoIcon); - return true; - } - - bool OnApply() override - { - m_proto->setByte("RememberPass", m_proto->m_bRememberPwd = chkSave.GetState()); - m_proto->m_szPassword = ptrA(edtPass.GetTextU()); - EndModal(true); - return true; - } - - void OnDestroy() override - { - m_proto->m_bDlgActive = false; - } -}; - -bool CIcqProto::RetrievePassword() -{ - // if we registered via phone (i.e., server holds the password), we don't need to enter it - if (getByte(DB_KEY_PHONEREG)) - return true; - - if (!m_szPassword.IsEmpty() && m_bRememberPwd) - return true; - - m_szPassword = getMStringA("Password"); - if (!m_szPassword.IsEmpty()) { - m_bRememberPwd = true; - return true; - } - - if (m_bDlgActive) - return false; - - return CIcqEnterLoginDlg(this).DoModal(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct CIcqRegistrationDlg : public CIcqDlgBase -{ - CMStringA szTrans, szMsisdn; - int iErrorCode = 0; - - CCtrlEdit edtPhone, edtCode; - CCtrlButton btnSendSms; - - CIcqRegistrationDlg(CIcqProto *ppro) : - CIcqDlgBase(ppro, IDD_REGISTER), - edtPhone(this, IDC_PHONE), - edtCode(this, IDC_CODE), - btnSendSms(this, IDC_SENDSMS) - { - btnSendSms.OnClick = Callback(this, &CIcqRegistrationDlg::onClick_SendSms); - edtPhone.OnChange = Callback(this, &CIcqRegistrationDlg::onChange_Phone); - } - - bool OnApply() override - { - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://u.icq.net/api/v60/smsreg/loginWithPhoneNumber.php", &CIcqProto::OnLoginViaPhone); - pReq << CHAR_PARAM("locale", "en") << CHAR_PARAM("msisdn", szMsisdn) << CHAR_PARAM("trans_id", szTrans) << CHAR_PARAM("k", m_proto->appId()) - << CHAR_PARAM("r", pReq->m_reqId) << CHAR_PARAM("f", "json") << WCHAR_PARAM("sms_code", ptrW(edtCode.GetText())) << INT_PARAM("create_account", 1); - pReq->pUserInfo = this; - - SetCursor(LoadCursor(0, IDC_WAIT)); - m_proto->ExecuteRequest(pReq); - SetCursor(LoadCursor(0, IDC_ARROW)); - - if (iErrorCode != 200) - return false; - - EndDialog(m_hwnd, 1); - return true; - } - - void onChange_Phone(CCtrlEdit*) - { - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://u.icq.net/api/v60/smsapi/fcgi-bin/smsphoneinfo", &CIcqProto::OnCheckPhone); - pReq << CHAR_PARAM("service", "icq_registration") << CHAR_PARAM("info", "typing_check,score,iso_country_code") << CHAR_PARAM("lang", "ru") - << WCHAR_PARAM("phone", ptrW(edtPhone.GetText())) << CHAR_PARAM("id", pReq->m_reqId); - pReq->pUserInfo = this; - m_proto->Push(pReq); - } - - void onClick_SendSms(CCtrlButton*) - { - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://u.icq.net/api/v60/smsreg/requestPhoneValidation.php", &CIcqProto::OnValidateSms); - pReq << CHAR_PARAM("locale", "en") << CHAR_PARAM("msisdn", szMsisdn) << CHAR_PARAM("r", pReq->m_reqId) - << CHAR_PARAM("smsFormatType", "human") << CHAR_PARAM("k", m_proto->appId()); - pReq->pUserInfo = this; - m_proto->Push(pReq); - } -}; - -void CIcqProto::OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - if (pReply == nullptr || pReply->resultCode != 200) - return; - - CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo; - pDlg->btnSendSms.Disable(); - pDlg->edtCode.Disable(); - - JSONROOT root(pReply->pData); - CMStringW wszStatus((*root)["status"].as_mstring()); - if (wszStatus != L"OK") { - pDlg->edtCode.SetText((*root)["printable"].as_mstring()); - return; - } - - CMStringA szPhoneNumber((*root)["typing_check"]["modified_phone_number"].as_mstring()); - CMStringA szPrefix((*root)["modified_prefix"].as_mstring()); - - auto *pNew = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://www.icq.com/smsreg/normalizePhoneNumber.php", &CIcqProto::OnNormalizePhone); - pNew << CHAR_PARAM("countryCode", szPrefix) << CHAR_PARAM("phoneNumber", szPhoneNumber.c_str() + szPrefix.GetLength()) - << CHAR_PARAM("k", appId()) << CHAR_PARAM("r", pReq->m_reqId); - pNew->pUserInfo = pDlg; - Push(pNew); -} - -void CIcqProto::OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo; - - JsonReply root(pReply); - pDlg->iErrorCode = root.error(); - if (root.error() != 200) - return; - - const JSONNode &data = root.data(); - pDlg->szMsisdn = data["msisdn"].as_mstring(); - pDlg->btnSendSms.Enable(); -} - -void CIcqProto::OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - JsonReply root(pReply); - if (root.error() != 200) - return; - - CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo; - const JSONNode &data = root.data(); - pDlg->szTrans = data["trans_id"].as_mstring(); - - pDlg->edtCode.Enable(); - pDlg->edtCode.SetText(L""); -} - -void CIcqProto::OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo; - - JsonReply root(pReply); - pDlg->iErrorCode = root.error(); - if (root.error() != 200) - return; - - const JSONNode &data = root.data(); - m_szAToken = data["token"]["a"].as_mstring(); - mir_urlDecode(m_szAToken.GetBuffer()); - setString(DB_KEY_ATOKEN, m_szAToken); - - int srvTS = data["hostTime"].as_int(); - m_iTimeShift = (srvTS) ? time(0) - srvTS : 0; - - m_szSessionKey = data["sessionKey"].as_mstring(); - setString(DB_KEY_SESSIONKEY, m_szSessionKey); - - m_szOwnId = data["loginId"].as_mstring(); - setByte(DB_KEY_PHONEREG, 1); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CIcqOptionsDlg : public CIcqDlgBase -{ - CCtrlEdit edtUin, edtPassword; - CCtrlCheck chkHideChats, chkTrayIcon, chkLaunchMailbox, chkShowErrorPopups; - CCtrlButton btnCreate; - CMStringW wszOldPass; - -public: - CIcqOptionsDlg(CIcqProto *ppro, int iDlgID, bool bFullDlg) : - CIcqDlgBase(ppro, iDlgID), - edtUin(this, IDC_UIN), - btnCreate(this, IDC_REGISTER), - edtPassword(this, IDC_PASSWORD), - chkTrayIcon(this, IDC_USETRAYICON), - chkHideChats(this, IDC_HIDECHATS), - chkLaunchMailbox(this, IDC_LAUNCH_MAILBOX), - chkShowErrorPopups(this, IDC_SHOWERRORPOPUPS) - { - btnCreate.OnClick = Callback(this, &CIcqOptionsDlg::onClick_Register); - - CreateLink(edtUin, ppro->m_szOwnId); - if (bFullDlg) { - CreateLink(chkHideChats, ppro->m_bHideGroupchats); - CreateLink(chkTrayIcon, ppro->m_bUseTrayIcon); - CreateLink(chkLaunchMailbox, ppro->m_bLaunchMailbox); - CreateLink(chkShowErrorPopups, ppro->m_bErrorPopups); - - chkTrayIcon.OnChange = Callback(this, &CIcqOptionsDlg::onChange_Tray); - } - } - - bool OnInitDialog() override - { - if (m_proto->m_isMra) - btnCreate.Hide(); - else - SetDlgItemText(m_hwnd, IDC_UIN_LABEL, TranslateT("UIN:")); - - wszOldPass = m_proto->getMStringW("Password"); - edtPassword.SetText(wszOldPass); - return true; - } - - bool OnApply() override - { - ptrW wszPass(edtPassword.GetText()); - if (wszPass) - m_proto->setWString("Password", wszPass); - else - m_proto->delSetting("Password"); - - if (wszOldPass != wszPass) { - m_proto->delSetting(DB_KEY_ATOKEN); - m_proto->delSetting(DB_KEY_SESSIONKEY); - m_proto->delSetting(DB_KEY_PHONEREG); - } - - if (mir_wstrlen(wszPass)) { - m_proto->m_szPassword = T2Utf(wszPass).get(); - m_proto->m_bRememberPwd = true; - } - else m_proto->m_bRememberPwd = m_proto->getByte("RememberPass"); - - return true; - } - - void onChange_Tray(CCtrlCheck*) - { - chkLaunchMailbox.Enable(chkTrayIcon.GetState()); - } - - void onClick_Register(CCtrlButton*) - { - CIcqRegistrationDlg dlg(m_proto); - dlg.SetParent(m_hwnd); - if (dlg.DoModal()) // force exit to avoid data corruption - PostMessage(m_hwndParent, WM_COMMAND, MAKELONG(IDCANCEL, BN_CLICKED), 0); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Advanced options - -class CIcqOptionsAdv : public CIcqDlgBase -{ - CCtrlEdit edtDiff1, edtDiff2; - CCtrlSpin spin1, spin2; - CCtrlCombo cmbStatus1, cmbStatus2; - -public: - CIcqOptionsAdv(CIcqProto *ppro) : - CIcqDlgBase(ppro, IDD_OPTIONS_ADV), - spin1(this, IDC_SPIN1, 32000), - spin2(this, IDC_SPIN2, 32000), - edtDiff1(this, IDC_DIFF1), - edtDiff2(this, IDC_DIFF2), - cmbStatus1(this, IDC_STATUS1), - cmbStatus2(this, IDC_STATUS2) - { - edtDiff1.OnChange = Callback(this, &CIcqOptionsAdv::onChange_Timeout1); - edtDiff2.OnChange = Callback(this, &CIcqOptionsAdv::onChange_Timeout2); - - CreateLink(spin1, ppro->m_iTimeDiff1); - CreateLink(spin2, ppro->m_iTimeDiff2); - } - - bool OnInitDialog() override - { - if (cmbStatus1.GetHwnd()) { - for (uint32_t iStatus = ID_STATUS_OFFLINE; iStatus <= ID_STATUS_MAX; iStatus++) { - int idx = cmbStatus1.AddString(Clist_GetStatusModeDescription(iStatus, 0)); - cmbStatus1.SetItemData(idx, iStatus); - if (iStatus == m_proto->m_iStatus1) - cmbStatus1.SetCurSel(idx); - - idx = cmbStatus2.AddString(Clist_GetStatusModeDescription(iStatus, 0)); - cmbStatus2.SetItemData(idx, iStatus); - if (iStatus == m_proto->m_iStatus2) - cmbStatus2.SetCurSel(idx); - } - } - - return true; - } - - bool OnApply() override - { - if (cmbStatus1.GetHwnd()) { - m_proto->m_iStatus1 = cmbStatus1.GetCurData(); - m_proto->m_iStatus2 = cmbStatus2.GetCurData(); - } - - return true; - } - - void onChange_Timeout1(CCtrlEdit*) - { - bool bEnabled = edtDiff1.GetInt() != 0; - spin2.Enable(bEnabled); - edtDiff2.Enable(bEnabled); - cmbStatus1.Enable(bEnabled); - cmbStatus2.Enable(bEnabled && edtDiff2.GetInt() != 0); - } - - void onChange_Timeout2(CCtrlEdit*) - { - bool bEnabled = edtDiff2.GetInt() != 0; - cmbStatus2.Enable(bEnabled); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Services - -INT_PTR CIcqProto::CreateAccMgrUI(WPARAM, LPARAM hwndParent) -{ - CIcqOptionsDlg *pDlg = new CIcqOptionsDlg(this, IDD_OPTIONS_ACCMGR, false); - pDlg->SetParent((HWND)hwndParent); - pDlg->Create(); - return (INT_PTR)pDlg->GetHwnd(); -} - -int CIcqProto::OnOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szTitle.w = m_tszUserName; - odp.flags = ODPF_UNICODE | ODPF_BOLDGROUPS; - odp.szGroup.w = LPGENW("Network"); - odp.position = 1; - - odp.szTab.w = LPGENW("General"); - odp.pDialog = new CIcqOptionsDlg(this, IDD_OPTIONS_FULL, true); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("Advanced"); - odp.pDialog = new CIcqOptionsAdv(this); - g_plugin.addOptions(wParam, &odp); - return 0; -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+class CIcqEnterLoginDlg : public CIcqDlgBase
+{
+ CCtrlEdit edtPass;
+ CCtrlCheck chkSave;
+
+public:
+ CIcqEnterLoginDlg(CIcqProto *ppro) :
+ CIcqDlgBase(ppro, IDD_LOGINPW),
+ edtPass(this, IDC_PASSWORD),
+ chkSave(this, IDC_SAVEPASS)
+ {
+ }
+
+ bool OnInitDialog() override
+ {
+ m_proto->m_bDlgActive = true;
+ chkSave.SetState(m_proto->getBool("RememberPass"));
+ Window_SetIcon_IcoLib(m_hwnd, m_proto->m_hProtoIcon);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ m_proto->setByte("RememberPass", m_proto->m_bRememberPwd = chkSave.GetState());
+ m_proto->m_szPassword = ptrA(edtPass.GetTextU());
+ EndModal(true);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ m_proto->m_bDlgActive = false;
+ }
+};
+
+bool CIcqProto::RetrievePassword()
+{
+ // if we registered via phone (i.e., server holds the password), we don't need to enter it
+ if (getByte(DB_KEY_PHONEREG))
+ return true;
+
+ if (!m_szPassword.IsEmpty() && m_bRememberPwd)
+ return true;
+
+ m_szPassword = getMStringA("Password");
+ if (!m_szPassword.IsEmpty()) {
+ m_bRememberPwd = true;
+ return true;
+ }
+
+ if (m_bDlgActive)
+ return false;
+
+ return CIcqEnterLoginDlg(this).DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct CIcqRegistrationDlg : public CIcqDlgBase
+{
+ CMStringA szTrans, szMsisdn;
+ int iErrorCode = 0;
+
+ CCtrlEdit edtPhone, edtCode;
+ CCtrlButton btnSendSms;
+
+ CIcqRegistrationDlg(CIcqProto *ppro) :
+ CIcqDlgBase(ppro, IDD_REGISTER),
+ edtPhone(this, IDC_PHONE),
+ edtCode(this, IDC_CODE),
+ btnSendSms(this, IDC_SENDSMS)
+ {
+ btnSendSms.OnClick = Callback(this, &CIcqRegistrationDlg::onClick_SendSms);
+ edtPhone.OnChange = Callback(this, &CIcqRegistrationDlg::onChange_Phone);
+ }
+
+ bool OnApply() override
+ {
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://u.icq.net/api/v60/smsreg/loginWithPhoneNumber.php", &CIcqProto::OnLoginViaPhone);
+ pReq << CHAR_PARAM("locale", "en") << CHAR_PARAM("msisdn", szMsisdn) << CHAR_PARAM("trans_id", szTrans) << CHAR_PARAM("k", m_proto->appId())
+ << CHAR_PARAM("r", pReq->m_reqId) << CHAR_PARAM("f", "json") << WCHAR_PARAM("sms_code", ptrW(edtCode.GetText())) << INT_PARAM("create_account", 1);
+ pReq->pUserInfo = this;
+
+ SetCursor(LoadCursor(0, IDC_WAIT));
+ m_proto->ExecuteRequest(pReq);
+ SetCursor(LoadCursor(0, IDC_ARROW));
+
+ if (iErrorCode != 200)
+ return false;
+
+ EndDialog(m_hwnd, 1);
+ return true;
+ }
+
+ void onChange_Phone(CCtrlEdit*)
+ {
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://u.icq.net/api/v60/smsapi/fcgi-bin/smsphoneinfo", &CIcqProto::OnCheckPhone);
+ pReq << CHAR_PARAM("service", "icq_registration") << CHAR_PARAM("info", "typing_check,score,iso_country_code") << CHAR_PARAM("lang", "ru")
+ << WCHAR_PARAM("phone", ptrW(edtPhone.GetText())) << CHAR_PARAM("id", pReq->m_reqId);
+ pReq->pUserInfo = this;
+ m_proto->Push(pReq);
+ }
+
+ void onClick_SendSms(CCtrlButton*)
+ {
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://u.icq.net/api/v60/smsreg/requestPhoneValidation.php", &CIcqProto::OnValidateSms);
+ pReq << CHAR_PARAM("locale", "en") << CHAR_PARAM("msisdn", szMsisdn) << CHAR_PARAM("r", pReq->m_reqId)
+ << CHAR_PARAM("smsFormatType", "human") << CHAR_PARAM("k", m_proto->appId());
+ pReq->pUserInfo = this;
+ m_proto->Push(pReq);
+ }
+};
+
+void CIcqProto::OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ if (pReply == nullptr || pReply->resultCode != 200)
+ return;
+
+ CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo;
+ pDlg->btnSendSms.Disable();
+ pDlg->edtCode.Disable();
+
+ JSONROOT root(pReply->pData);
+ CMStringW wszStatus((*root)["status"].as_mstring());
+ if (wszStatus != L"OK") {
+ pDlg->edtCode.SetText((*root)["printable"].as_mstring());
+ return;
+ }
+
+ CMStringA szPhoneNumber((*root)["typing_check"]["modified_phone_number"].as_mstring());
+ CMStringA szPrefix((*root)["modified_prefix"].as_mstring());
+
+ auto *pNew = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, "https://www.icq.com/smsreg/normalizePhoneNumber.php", &CIcqProto::OnNormalizePhone);
+ pNew << CHAR_PARAM("countryCode", szPrefix) << CHAR_PARAM("phoneNumber", szPhoneNumber.c_str() + szPrefix.GetLength())
+ << CHAR_PARAM("k", appId()) << CHAR_PARAM("r", pReq->m_reqId);
+ pNew->pUserInfo = pDlg;
+ Push(pNew);
+}
+
+void CIcqProto::OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo;
+
+ JsonReply root(pReply);
+ pDlg->iErrorCode = root.error();
+ if (root.error() != 200)
+ return;
+
+ const JSONNode &data = root.data();
+ pDlg->szMsisdn = data["msisdn"].as_mstring();
+ pDlg->btnSendSms.Enable();
+}
+
+void CIcqProto::OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ JsonReply root(pReply);
+ if (root.error() != 200)
+ return;
+
+ CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo;
+ const JSONNode &data = root.data();
+ pDlg->szTrans = data["trans_id"].as_mstring();
+
+ pDlg->edtCode.Enable();
+ pDlg->edtCode.SetText(L"");
+}
+
+void CIcqProto::OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ CIcqRegistrationDlg *pDlg = (CIcqRegistrationDlg*)pReq->pUserInfo;
+
+ JsonReply root(pReply);
+ pDlg->iErrorCode = root.error();
+ if (root.error() != 200)
+ return;
+
+ const JSONNode &data = root.data();
+ m_szAToken = data["token"]["a"].as_mstring();
+ mir_urlDecode(m_szAToken.GetBuffer());
+ setString(DB_KEY_ATOKEN, m_szAToken);
+
+ int srvTS = data["hostTime"].as_int();
+ m_iTimeShift = (srvTS) ? time(0) - srvTS : 0;
+
+ m_szSessionKey = data["sessionKey"].as_mstring();
+ setString(DB_KEY_SESSIONKEY, m_szSessionKey);
+
+ m_szOwnId = data["loginId"].as_mstring();
+ setByte(DB_KEY_PHONEREG, 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CIcqOptionsDlg : public CIcqDlgBase
+{
+ CCtrlEdit edtUin, edtPassword;
+ CCtrlCheck chkHideChats, chkTrayIcon, chkLaunchMailbox, chkShowErrorPopups;
+ CCtrlButton btnCreate;
+ CMStringW wszOldPass;
+
+public:
+ CIcqOptionsDlg(CIcqProto *ppro, int iDlgID, bool bFullDlg) :
+ CIcqDlgBase(ppro, iDlgID),
+ edtUin(this, IDC_UIN),
+ btnCreate(this, IDC_REGISTER),
+ edtPassword(this, IDC_PASSWORD),
+ chkTrayIcon(this, IDC_USETRAYICON),
+ chkHideChats(this, IDC_HIDECHATS),
+ chkLaunchMailbox(this, IDC_LAUNCH_MAILBOX),
+ chkShowErrorPopups(this, IDC_SHOWERRORPOPUPS)
+ {
+ btnCreate.OnClick = Callback(this, &CIcqOptionsDlg::onClick_Register);
+
+ CreateLink(edtUin, ppro->m_szOwnId);
+ if (bFullDlg) {
+ CreateLink(chkHideChats, ppro->m_bHideGroupchats);
+ CreateLink(chkTrayIcon, ppro->m_bUseTrayIcon);
+ CreateLink(chkLaunchMailbox, ppro->m_bLaunchMailbox);
+ CreateLink(chkShowErrorPopups, ppro->m_bErrorPopups);
+
+ chkTrayIcon.OnChange = Callback(this, &CIcqOptionsDlg::onChange_Tray);
+ }
+ }
+
+ bool OnInitDialog() override
+ {
+ if (m_proto->m_isMra)
+ btnCreate.Hide();
+ else
+ SetDlgItemText(m_hwnd, IDC_UIN_LABEL, TranslateT("UIN:"));
+
+ wszOldPass = m_proto->getMStringW("Password");
+ edtPassword.SetText(wszOldPass);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ ptrW wszPass(edtPassword.GetText());
+ if (wszPass)
+ m_proto->setWString("Password", wszPass);
+ else
+ m_proto->delSetting("Password");
+
+ if (wszOldPass != wszPass) {
+ m_proto->delSetting(DB_KEY_ATOKEN);
+ m_proto->delSetting(DB_KEY_SESSIONKEY);
+ m_proto->delSetting(DB_KEY_PHONEREG);
+ }
+
+ if (mir_wstrlen(wszPass)) {
+ m_proto->m_szPassword = T2Utf(wszPass).get();
+ m_proto->m_bRememberPwd = true;
+ }
+ else m_proto->m_bRememberPwd = m_proto->getByte("RememberPass");
+
+ return true;
+ }
+
+ void onChange_Tray(CCtrlCheck*)
+ {
+ chkLaunchMailbox.Enable(chkTrayIcon.GetState());
+ }
+
+ void onClick_Register(CCtrlButton*)
+ {
+ CIcqRegistrationDlg dlg(m_proto);
+ dlg.SetParent(m_hwnd);
+ if (dlg.DoModal()) // force exit to avoid data corruption
+ PostMessage(m_hwndParent, WM_COMMAND, MAKELONG(IDCANCEL, BN_CLICKED), 0);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Advanced options
+
+class CIcqOptionsAdv : public CIcqDlgBase
+{
+ CCtrlEdit edtDiff1, edtDiff2;
+ CCtrlSpin spin1, spin2;
+ CCtrlCombo cmbStatus1, cmbStatus2;
+
+public:
+ CIcqOptionsAdv(CIcqProto *ppro) :
+ CIcqDlgBase(ppro, IDD_OPTIONS_ADV),
+ spin1(this, IDC_SPIN1, 32000),
+ spin2(this, IDC_SPIN2, 32000),
+ edtDiff1(this, IDC_DIFF1),
+ edtDiff2(this, IDC_DIFF2),
+ cmbStatus1(this, IDC_STATUS1),
+ cmbStatus2(this, IDC_STATUS2)
+ {
+ edtDiff1.OnChange = Callback(this, &CIcqOptionsAdv::onChange_Timeout1);
+ edtDiff2.OnChange = Callback(this, &CIcqOptionsAdv::onChange_Timeout2);
+
+ CreateLink(spin1, ppro->m_iTimeDiff1);
+ CreateLink(spin2, ppro->m_iTimeDiff2);
+ }
+
+ bool OnInitDialog() override
+ {
+ if (cmbStatus1.GetHwnd()) {
+ for (uint32_t iStatus = ID_STATUS_OFFLINE; iStatus <= ID_STATUS_MAX; iStatus++) {
+ int idx = cmbStatus1.AddString(Clist_GetStatusModeDescription(iStatus, 0));
+ cmbStatus1.SetItemData(idx, iStatus);
+ if (iStatus == m_proto->m_iStatus1)
+ cmbStatus1.SetCurSel(idx);
+
+ idx = cmbStatus2.AddString(Clist_GetStatusModeDescription(iStatus, 0));
+ cmbStatus2.SetItemData(idx, iStatus);
+ if (iStatus == m_proto->m_iStatus2)
+ cmbStatus2.SetCurSel(idx);
+ }
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (cmbStatus1.GetHwnd()) {
+ m_proto->m_iStatus1 = cmbStatus1.GetCurData();
+ m_proto->m_iStatus2 = cmbStatus2.GetCurData();
+ }
+
+ return true;
+ }
+
+ void onChange_Timeout1(CCtrlEdit*)
+ {
+ bool bEnabled = edtDiff1.GetInt() != 0;
+ spin2.Enable(bEnabled);
+ edtDiff2.Enable(bEnabled);
+ cmbStatus1.Enable(bEnabled);
+ cmbStatus2.Enable(bEnabled && edtDiff2.GetInt() != 0);
+ }
+
+ void onChange_Timeout2(CCtrlEdit*)
+ {
+ bool bEnabled = edtDiff2.GetInt() != 0;
+ cmbStatus2.Enable(bEnabled);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Services
+
+INT_PTR CIcqProto::CreateAccMgrUI(WPARAM, LPARAM hwndParent)
+{
+ CIcqOptionsDlg *pDlg = new CIcqOptionsDlg(this, IDD_OPTIONS_ACCMGR, false);
+ pDlg->SetParent((HWND)hwndParent);
+ pDlg->Create();
+ return (INT_PTR)pDlg->GetHwnd();
+}
+
+int CIcqProto::OnOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.szTitle.w = m_tszUserName;
+ odp.flags = ODPF_UNICODE | ODPF_BOLDGROUPS;
+ odp.szGroup.w = LPGENW("Network");
+ odp.position = 1;
+
+ odp.szTab.w = LPGENW("General");
+ odp.pDialog = new CIcqOptionsDlg(this, IDD_OPTIONS_FULL, true);
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("Advanced");
+ odp.pDialog = new CIcqOptionsAdv(this);
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/protocols/ICQ-WIM/src/poll.cpp b/protocols/ICQ-WIM/src/poll.cpp index d7ca7298ad..6fca78728f 100644 --- a/protocols/ICQ-WIM/src/poll.cpp +++ b/protocols/ICQ-WIM/src/poll.cpp @@ -1,409 +1,409 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- -// Long-poll thread and its item handlers - -#include "stdafx.h" - -void CIcqProto::ProcessBuddyList(const JSONNode &ev) -{ - m_arGroups.destroy(); - - LIST<IcqGroup> tmpGroups(10); - bool bEnableMenu = false; - - for (auto &it : ev["groups"]) { - auto *pGroup = new IcqGroup(it["id"].as_int(), it["name"].as_mstring()); - debugLogA("new group: id=%d, level=%d, name=%S", pGroup->id, pGroup->level, pGroup->wszName.c_str()); - if (pGroup->level != 0) { - for (auto &p : tmpGroups.rev_iter()) { - if (p->level == pGroup->level-1) { - pGroup->wszName = p->wszName + L"\\" + pGroup->wszName; - debugLogA("Group name fixed as %S", pGroup->wszName.c_str()); - break; - } - } - } - tmpGroups.insert(pGroup); - m_arGroups.insert(pGroup); - - bool bCreated = false; - - for (auto &buddy : it["buddies"]) { - MCONTACT hContact = ParseBuddyInfo(buddy); - if (hContact == INVALID_CONTACT_ID) - continue; - - setWString(hContact, "IcqGroup", pGroup->wszName); - - if (!bCreated) { - Clist_GroupCreate(0, pGroup->wszName); - bCreated = true; - } - - ptrW mirGroup(Clist_GetGroup(hContact)); - if (mir_wstrcmp(mirGroup, pGroup->wszName)) - bEnableMenu = true; - - if (!mirGroup) - Clist_SetGroup(hContact, pGroup->wszName); - } - } - - if (bEnableMenu) - Menu_ShowItem(m_hUploadGroups, true); - - for (auto &it : m_arCache) - if (!it->m_bInList && !getBool(it->m_hContact, "IcqDeleted")) - Contact::RemoveFromList(it->m_hContact); -} - -void CIcqProto::ProcessDiff(const JSONNode &ev) -{ - for (auto &block : ev) { - CMStringW szType = block["type"].as_mstring(); - if (szType != "updated" && szType != "created" && szType != "deleted") - continue; - - for (auto &it : block["data"]) { - int grpId = it["id"].as_int(); - CMStringW wszName = it["name"].as_mstring(); - - auto *pGroup = m_arGroups.find((IcqGroup *)&grpId); - if (pGroup == nullptr) { - if (szType != "created") { - debugLogA("Group %d isn't found", grpId); - continue; - } - - pGroup = new IcqGroup(grpId, wszName); - m_arGroups.insert(pGroup); - } - else { - pGroup->wszSrvName = wszName; - pGroup->SetName(wszName); - } - - bool bCreated = false, bDeleted = (szType == "deleted"); - - for (auto &buddy : it["buddies"]) { - if (bDeleted) - continue; - - MCONTACT hContact = ParseBuddyInfo(buddy, true); - if (hContact == INVALID_CONTACT_ID) - continue; - - setWString(hContact, "IcqGroup", pGroup->wszName); - - if (!bCreated) { - Clist_GroupCreate(0, pGroup->wszName); - bCreated = true; - } - - ptrW wszGroup(Clist_GetGroup(hContact)); - if (!wszGroup) - Clist_SetGroup(hContact, pGroup->wszName); - } - - if (bDeleted) - m_arGroups.remove(pGroup); - } - - RefreshGroups(); - } -} - -void CIcqProto::ProcessEvent(const JSONNode &ev) -{ - const JSONNode &pData = ev["eventData"]; - CMStringW szType = ev["type"].as_mstring(); - if (szType == L"buddylist") - ProcessBuddyList(pData); - else if (szType == L"diff") - ProcessDiff(pData); - else if (szType == L"histDlgState") - ProcessHistData(pData); - else if (szType == L"imState") - ProcessImState(pData); - else if (szType == L"mchat") - ProcessGroupChat(pData); - else if (szType == L"myInfo") - ProcessMyInfo(pData); - else if (szType == L"notification") - ProcessNotification(pData); - else if (szType == L"permitDeny") - ProcessPermissions(pData); - else if (szType == L"presence") - ProcessPresence(pData); - else if (szType == L"sessionEnded") - ProcessSessionEnd(pData); - else if (szType == L"typing") - ProcessTyping(pData); -} - -void CIcqProto::ProcessHistData(const JSONNode &ev) -{ - MCONTACT hContact; - bool bVeryBeginning = m_bFirstBos; - - CMStringW wszId(ev["sn"].as_mstring()); - auto *pCache = FindContactByUIN(wszId); // might be NULL for groupchats - - if (IsChat(wszId)) { - SESSION_INFO *si = g_chatApi.SM_FindSession(wszId, m_szModuleName); - if (si == nullptr) - return; - - hContact = si->hContact; - - if (si->arUsers.getCount() == 0) { - __int64 srvInfoVer = _wtoi64(ev["mchatState"]["infoVersion"].as_mstring()); - __int64 srvMembersVer = _wtoi64(ev["mchatState"]["membersVersion"].as_mstring()); - if (srvInfoVer != getId(hContact, "InfoVersion") || srvMembersVer != getId(hContact, "MembersVersion")) { - auto *pReq = new AsyncRapiRequest(this, "getChatInfo", &CIcqProto::OnGetChatInfo); - pReq->params << WCHAR_PARAM("sn", wszId) << INT_PARAM("memberLimit", 100) << CHAR_PARAM("aimSid", m_aimsid); - pReq->pUserInfo = si; - Push(pReq); - } - else LoadChatInfo(si); - } - } - else { - hContact = CreateContact(wszId, true); - - // for temporary contacts that just gonna be created - if (pCache == nullptr) { - bVeryBeginning = true; - pCache = FindContactByUIN(wszId); - } - } - - // restore reading from the previous point, if we just installed Miranda - __int64 lastMsgId = getId(hContact, DB_KEY_LASTMSGID); - if (lastMsgId == 0) { - lastMsgId = _wtoi64(ev["yours"]["lastRead"].as_mstring()); - setId(hContact, DB_KEY_LASTMSGID, lastMsgId); - } - - __int64 patchVersion = _wtoi64(ev["patchVersion"].as_mstring()); - setId(hContact, DB_KEY_PATCHVER, patchVersion); - - __int64 srvLastId = _wtoi64(ev["lastMsgId"].as_mstring()); - - // we load history in the very beginning or if the previous message - if (bVeryBeginning) { - if (pCache) { - debugLogA("Setting cache = %lld for %d", srvLastId, hContact); - pCache->m_iProcessedMsgId = srvLastId; - } - - if (srvLastId > lastMsgId) { - debugLogA("We need to retrieve history for %S: %lld > %lld", wszId.c_str(), srvLastId, lastMsgId); - RetrieveUserHistory(hContact, lastMsgId, false); - } - } - else { - if (!(pCache && pCache->m_iProcessedMsgId >= srvLastId)) { - if (pCache) - debugLogA("Proceeding with cache for %d: %lld < %lld", hContact, pCache->m_iProcessedMsgId, srvLastId); - else - debugLogA("Proceeding with empty cache for %d", hContact); - - for (auto &it : ev["tail"]["messages"]) - ParseMessage(hContact, lastMsgId, it, false, true); - - setId(hContact, DB_KEY_LASTMSGID, lastMsgId); - if (pCache) { - pCache->m_iProcessedMsgId = lastMsgId; - debugLogA("Setting second cache = %lld for %d", srvLastId, hContact); - } - } - } - - // check remote read - if (g_bMessageState) { - __int64 srvRemoteRead = _wtoi64(ev["theirs"]["lastRead"].as_mstring()); - __int64 lastRemoteRead = getId(hContact, DB_KEY_REMOTEREAD); - if (srvRemoteRead > lastRemoteRead) { - setId(hContact, DB_KEY_REMOTEREAD, srvRemoteRead); - - if (g_bMessageState) - CallService(MS_MESSAGESTATE_UPDATE, hContact, MRD_TYPE_READ); - } - } -} - -void CIcqProto::ProcessImState(const JSONNode &ev) -{ - for (auto &it : ev["imStates"]) { - if (it["state"].as_mstring() != L"sent") - continue; - - CMStringA reqId(it["sendReqId"].as_mstring()); - CMStringA msgId(it["histMsgId"].as_mstring()); - MCONTACT hContact = CheckOwnMessage(reqId, msgId, false); - if (hContact) - CheckLastId(hContact, ev); - } -} - -void CIcqProto::ProcessMyInfo(const JSONNode &ev) -{ - if (auto &var = ev["friendly"]) - setWString("Nick", var.as_mstring()); - - if (auto &var = ev["attachedPhoneNumber"]) - setWString(DB_KEY_PHONE, var.as_mstring()); - - CheckAvatarChange(0, ev); -} - -void CIcqProto::ProcessNotification(const JSONNode &ev) -{ - for (auto &fld : ev["fields"]) { - const JSONNode &email = fld["mailbox.newMessage"]; - if (email) { - JSONROOT root(email.as_string().c_str()); - CMStringW wszFrom((*root)["from"].as_mstring()); - CMStringW wszSubj((*root)["subject"].as_mstring()); - m_unreadEmails = (*root)["unreadCount"].as_int(); - debugLogW(L"You received e-mail (%d) from <%s>: <%s>", m_unreadEmails, wszFrom.c_str(), wszSubj.c_str()); - - CMStringW wszMessage(FORMAT, TranslateT("You received e-mail from %s: %s"), wszFrom.c_str(), wszSubj.c_str()); - EmailNotification(wszMessage); - } - - const JSONNode &status = fld["mailbox.status"]; - if (status) { - int iOldCount = m_unreadEmails; - - JSONROOT root(status.as_string().c_str()); - m_szMailBox = (*root)["email"].as_mstring(); - m_unreadEmails = (*root)["unreadCount"].as_int(); - - // we've read/removed some messages from server - if (iOldCount > m_unreadEmails) { - g_clistApi.pfnRemoveEvent(0, ICQ_FAKE_EVENT_ID); - return; - } - - // we notify about initial mail count only during login - if (m_bFirstBos && m_unreadEmails > 0) { - CMStringW wszMessage(FORMAT, TranslateT("You have %d unread emails"), m_unreadEmails); - EmailNotification(wszMessage); - } - } - } -} - -void CIcqProto::ProcessPresence(const JSONNode &ev) -{ - CMStringW aimId = ev["aimId"].as_mstring(); - - IcqCacheItem *pCache = FindContactByUIN(aimId); - if (pCache == nullptr) - return; - - int iNewStatus = StatusFromPresence(ev, pCache->m_hContact); - if (iNewStatus == -1) - iNewStatus = ID_STATUS_OFFLINE; - - // major crutch dedicated to the official client behaviour to go offline - // when its window gets closed. we change the status of a contact to the - // first chosen one from options and initialize a timer - if (iNewStatus == ID_STATUS_OFFLINE) { - if (m_iTimeDiff1) { - iNewStatus = m_iStatus1; - pCache->m_timer1 = time(0); - } - } - // if a client returns back online, we clear timers not to play with statuses anymore - else pCache->m_timer1 = pCache->m_timer2 = 0; - - setWord(pCache->m_hContact, "Status", iNewStatus); - - Json2string(pCache->m_hContact, ev, "friendly", "Nick", true); - CheckAvatarChange(pCache->m_hContact, ev); -} - -void CIcqProto::ProcessSessionEnd(const JSONNode &/*ev*/) -{ - m_szRToken.Empty(); - m_iRClientId = 0; - delSetting(DB_KEY_RCLIENTID); - - ShutdownSession(); -} - -void CIcqProto::ProcessTyping(const JSONNode &ev) -{ - CMStringW aimId = ev["aimId"].as_mstring(); - CMStringW wszStatus = ev["typingStatus"].as_mstring(); - - IcqCacheItem *pCache = FindContactByUIN(aimId); - if (pCache) { - if (wszStatus == "typing") - CallService(MS_PROTO_CONTACTISTYPING, pCache->m_hContact, 60); - else - CallService(MS_PROTO_CONTACTISTYPING, pCache->m_hContact, PROTOTYPE_CONTACTTYPING_OFF); - } -} - -void CIcqProto::OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) -{ - JsonReply root(pReply); - if (root.error() != 200) { - ShutdownSession(); - return; - } - - JSONNode &data = root.data(); - m_fetchBaseURL = data["fetchBaseURL"].as_mstring(); - - for (auto &it : data["events"]) - ProcessEvent(it); -} - -void __cdecl CIcqProto::PollThread(void*) -{ - debugLogA("Polling thread started"); - m_bFirstBos = true; - - while (m_bOnline && !m_fetchBaseURL.IsEmpty()) { - CMStringA szUrl = m_fetchBaseURL; - if (m_bFirstBos) - szUrl.Append("&first=1"); - else - szUrl.Append("&timeout=25000"); - - auto *pReq = new AsyncHttpRequest(CONN_FETCH, REQUEST_GET, szUrl, &CIcqProto::OnFetchEvents); - if (!m_bFirstBos) - pReq->timeout = 62000; - - if (!ExecuteRequest(pReq)) { - ShutdownSession(); - break; - } - - m_bFirstBos = false; - } - - debugLogA("Polling thread ended"); -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+// Long-poll thread and its item handlers
+
+#include "stdafx.h"
+
+void CIcqProto::ProcessBuddyList(const JSONNode &ev)
+{
+ m_arGroups.destroy();
+
+ LIST<IcqGroup> tmpGroups(10);
+ bool bEnableMenu = false;
+
+ for (auto &it : ev["groups"]) {
+ auto *pGroup = new IcqGroup(it["id"].as_int(), it["name"].as_mstring());
+ debugLogA("new group: id=%d, level=%d, name=%S", pGroup->id, pGroup->level, pGroup->wszName.c_str());
+ if (pGroup->level != 0) {
+ for (auto &p : tmpGroups.rev_iter()) {
+ if (p->level == pGroup->level-1) {
+ pGroup->wszName = p->wszName + L"\\" + pGroup->wszName;
+ debugLogA("Group name fixed as %S", pGroup->wszName.c_str());
+ break;
+ }
+ }
+ }
+ tmpGroups.insert(pGroup);
+ m_arGroups.insert(pGroup);
+
+ bool bCreated = false;
+
+ for (auto &buddy : it["buddies"]) {
+ MCONTACT hContact = ParseBuddyInfo(buddy);
+ if (hContact == INVALID_CONTACT_ID)
+ continue;
+
+ setWString(hContact, "IcqGroup", pGroup->wszName);
+
+ if (!bCreated) {
+ Clist_GroupCreate(0, pGroup->wszName);
+ bCreated = true;
+ }
+
+ ptrW mirGroup(Clist_GetGroup(hContact));
+ if (mir_wstrcmp(mirGroup, pGroup->wszName))
+ bEnableMenu = true;
+
+ if (!mirGroup)
+ Clist_SetGroup(hContact, pGroup->wszName);
+ }
+ }
+
+ if (bEnableMenu)
+ Menu_ShowItem(m_hUploadGroups, true);
+
+ for (auto &it : m_arCache)
+ if (!it->m_bInList && !getBool(it->m_hContact, "IcqDeleted"))
+ Contact::RemoveFromList(it->m_hContact);
+}
+
+void CIcqProto::ProcessDiff(const JSONNode &ev)
+{
+ for (auto &block : ev) {
+ CMStringW szType = block["type"].as_mstring();
+ if (szType != "updated" && szType != "created" && szType != "deleted")
+ continue;
+
+ for (auto &it : block["data"]) {
+ int grpId = it["id"].as_int();
+ CMStringW wszName = it["name"].as_mstring();
+
+ auto *pGroup = m_arGroups.find((IcqGroup *)&grpId);
+ if (pGroup == nullptr) {
+ if (szType != "created") {
+ debugLogA("Group %d isn't found", grpId);
+ continue;
+ }
+
+ pGroup = new IcqGroup(grpId, wszName);
+ m_arGroups.insert(pGroup);
+ }
+ else {
+ pGroup->wszSrvName = wszName;
+ pGroup->SetName(wszName);
+ }
+
+ bool bCreated = false, bDeleted = (szType == "deleted");
+
+ for (auto &buddy : it["buddies"]) {
+ if (bDeleted)
+ continue;
+
+ MCONTACT hContact = ParseBuddyInfo(buddy, true);
+ if (hContact == INVALID_CONTACT_ID)
+ continue;
+
+ setWString(hContact, "IcqGroup", pGroup->wszName);
+
+ if (!bCreated) {
+ Clist_GroupCreate(0, pGroup->wszName);
+ bCreated = true;
+ }
+
+ ptrW wszGroup(Clist_GetGroup(hContact));
+ if (!wszGroup)
+ Clist_SetGroup(hContact, pGroup->wszName);
+ }
+
+ if (bDeleted)
+ m_arGroups.remove(pGroup);
+ }
+
+ RefreshGroups();
+ }
+}
+
+void CIcqProto::ProcessEvent(const JSONNode &ev)
+{
+ const JSONNode &pData = ev["eventData"];
+ CMStringW szType = ev["type"].as_mstring();
+ if (szType == L"buddylist")
+ ProcessBuddyList(pData);
+ else if (szType == L"diff")
+ ProcessDiff(pData);
+ else if (szType == L"histDlgState")
+ ProcessHistData(pData);
+ else if (szType == L"imState")
+ ProcessImState(pData);
+ else if (szType == L"mchat")
+ ProcessGroupChat(pData);
+ else if (szType == L"myInfo")
+ ProcessMyInfo(pData);
+ else if (szType == L"notification")
+ ProcessNotification(pData);
+ else if (szType == L"permitDeny")
+ ProcessPermissions(pData);
+ else if (szType == L"presence")
+ ProcessPresence(pData);
+ else if (szType == L"sessionEnded")
+ ProcessSessionEnd(pData);
+ else if (szType == L"typing")
+ ProcessTyping(pData);
+}
+
+void CIcqProto::ProcessHistData(const JSONNode &ev)
+{
+ MCONTACT hContact;
+ bool bVeryBeginning = m_bFirstBos;
+
+ CMStringW wszId(ev["sn"].as_mstring());
+ auto *pCache = FindContactByUIN(wszId); // might be NULL for groupchats
+
+ if (IsChat(wszId)) {
+ SESSION_INFO *si = g_chatApi.SM_FindSession(wszId, m_szModuleName);
+ if (si == nullptr)
+ return;
+
+ hContact = si->hContact;
+
+ if (si->arUsers.getCount() == 0) {
+ __int64 srvInfoVer = _wtoi64(ev["mchatState"]["infoVersion"].as_mstring());
+ __int64 srvMembersVer = _wtoi64(ev["mchatState"]["membersVersion"].as_mstring());
+ if (srvInfoVer != getId(hContact, "InfoVersion") || srvMembersVer != getId(hContact, "MembersVersion")) {
+ auto *pReq = new AsyncRapiRequest(this, "getChatInfo", &CIcqProto::OnGetChatInfo);
+ pReq->params << WCHAR_PARAM("sn", wszId) << INT_PARAM("memberLimit", 100) << CHAR_PARAM("aimSid", m_aimsid);
+ pReq->pUserInfo = si;
+ Push(pReq);
+ }
+ else LoadChatInfo(si);
+ }
+ }
+ else {
+ hContact = CreateContact(wszId, true);
+
+ // for temporary contacts that just gonna be created
+ if (pCache == nullptr) {
+ bVeryBeginning = true;
+ pCache = FindContactByUIN(wszId);
+ }
+ }
+
+ // restore reading from the previous point, if we just installed Miranda
+ __int64 lastMsgId = getId(hContact, DB_KEY_LASTMSGID);
+ if (lastMsgId == 0) {
+ lastMsgId = _wtoi64(ev["yours"]["lastRead"].as_mstring());
+ setId(hContact, DB_KEY_LASTMSGID, lastMsgId);
+ }
+
+ __int64 patchVersion = _wtoi64(ev["patchVersion"].as_mstring());
+ setId(hContact, DB_KEY_PATCHVER, patchVersion);
+
+ __int64 srvLastId = _wtoi64(ev["lastMsgId"].as_mstring());
+
+ // we load history in the very beginning or if the previous message
+ if (bVeryBeginning) {
+ if (pCache) {
+ debugLogA("Setting cache = %lld for %d", srvLastId, hContact);
+ pCache->m_iProcessedMsgId = srvLastId;
+ }
+
+ if (srvLastId > lastMsgId) {
+ debugLogA("We need to retrieve history for %S: %lld > %lld", wszId.c_str(), srvLastId, lastMsgId);
+ RetrieveUserHistory(hContact, lastMsgId, false);
+ }
+ }
+ else {
+ if (!(pCache && pCache->m_iProcessedMsgId >= srvLastId)) {
+ if (pCache)
+ debugLogA("Proceeding with cache for %d: %lld < %lld", hContact, pCache->m_iProcessedMsgId, srvLastId);
+ else
+ debugLogA("Proceeding with empty cache for %d", hContact);
+
+ for (auto &it : ev["tail"]["messages"])
+ ParseMessage(hContact, lastMsgId, it, false, true);
+
+ setId(hContact, DB_KEY_LASTMSGID, lastMsgId);
+ if (pCache) {
+ pCache->m_iProcessedMsgId = lastMsgId;
+ debugLogA("Setting second cache = %lld for %d", srvLastId, hContact);
+ }
+ }
+ }
+
+ // check remote read
+ if (g_bMessageState) {
+ __int64 srvRemoteRead = _wtoi64(ev["theirs"]["lastRead"].as_mstring());
+ __int64 lastRemoteRead = getId(hContact, DB_KEY_REMOTEREAD);
+ if (srvRemoteRead > lastRemoteRead) {
+ setId(hContact, DB_KEY_REMOTEREAD, srvRemoteRead);
+
+ if (g_bMessageState)
+ CallService(MS_MESSAGESTATE_UPDATE, hContact, MRD_TYPE_READ);
+ }
+ }
+}
+
+void CIcqProto::ProcessImState(const JSONNode &ev)
+{
+ for (auto &it : ev["imStates"]) {
+ if (it["state"].as_mstring() != L"sent")
+ continue;
+
+ CMStringA reqId(it["sendReqId"].as_mstring());
+ CMStringA msgId(it["histMsgId"].as_mstring());
+ MCONTACT hContact = CheckOwnMessage(reqId, msgId, false);
+ if (hContact)
+ CheckLastId(hContact, ev);
+ }
+}
+
+void CIcqProto::ProcessMyInfo(const JSONNode &ev)
+{
+ if (auto &var = ev["friendly"])
+ setWString("Nick", var.as_mstring());
+
+ if (auto &var = ev["attachedPhoneNumber"])
+ setWString(DB_KEY_PHONE, var.as_mstring());
+
+ CheckAvatarChange(0, ev);
+}
+
+void CIcqProto::ProcessNotification(const JSONNode &ev)
+{
+ for (auto &fld : ev["fields"]) {
+ const JSONNode &email = fld["mailbox.newMessage"];
+ if (email) {
+ JSONROOT root(email.as_string().c_str());
+ CMStringW wszFrom((*root)["from"].as_mstring());
+ CMStringW wszSubj((*root)["subject"].as_mstring());
+ m_unreadEmails = (*root)["unreadCount"].as_int();
+ debugLogW(L"You received e-mail (%d) from <%s>: <%s>", m_unreadEmails, wszFrom.c_str(), wszSubj.c_str());
+
+ CMStringW wszMessage(FORMAT, TranslateT("You received e-mail from %s: %s"), wszFrom.c_str(), wszSubj.c_str());
+ EmailNotification(wszMessage);
+ }
+
+ const JSONNode &status = fld["mailbox.status"];
+ if (status) {
+ int iOldCount = m_unreadEmails;
+
+ JSONROOT root(status.as_string().c_str());
+ m_szMailBox = (*root)["email"].as_mstring();
+ m_unreadEmails = (*root)["unreadCount"].as_int();
+
+ // we've read/removed some messages from server
+ if (iOldCount > m_unreadEmails) {
+ g_clistApi.pfnRemoveEvent(0, ICQ_FAKE_EVENT_ID);
+ return;
+ }
+
+ // we notify about initial mail count only during login
+ if (m_bFirstBos && m_unreadEmails > 0) {
+ CMStringW wszMessage(FORMAT, TranslateT("You have %d unread emails"), m_unreadEmails);
+ EmailNotification(wszMessage);
+ }
+ }
+ }
+}
+
+void CIcqProto::ProcessPresence(const JSONNode &ev)
+{
+ CMStringW aimId = ev["aimId"].as_mstring();
+
+ IcqCacheItem *pCache = FindContactByUIN(aimId);
+ if (pCache == nullptr)
+ return;
+
+ int iNewStatus = StatusFromPresence(ev, pCache->m_hContact);
+ if (iNewStatus == -1)
+ iNewStatus = ID_STATUS_OFFLINE;
+
+ // major crutch dedicated to the official client behaviour to go offline
+ // when its window gets closed. we change the status of a contact to the
+ // first chosen one from options and initialize a timer
+ if (iNewStatus == ID_STATUS_OFFLINE) {
+ if (m_iTimeDiff1) {
+ iNewStatus = m_iStatus1;
+ pCache->m_timer1 = time(0);
+ }
+ }
+ // if a client returns back online, we clear timers not to play with statuses anymore
+ else pCache->m_timer1 = pCache->m_timer2 = 0;
+
+ setWord(pCache->m_hContact, "Status", iNewStatus);
+
+ Json2string(pCache->m_hContact, ev, "friendly", "Nick", true);
+ CheckAvatarChange(pCache->m_hContact, ev);
+}
+
+void CIcqProto::ProcessSessionEnd(const JSONNode &/*ev*/)
+{
+ m_szRToken.Empty();
+ m_iRClientId = 0;
+ delSetting(DB_KEY_RCLIENTID);
+
+ ShutdownSession();
+}
+
+void CIcqProto::ProcessTyping(const JSONNode &ev)
+{
+ CMStringW aimId = ev["aimId"].as_mstring();
+ CMStringW wszStatus = ev["typingStatus"].as_mstring();
+
+ IcqCacheItem *pCache = FindContactByUIN(aimId);
+ if (pCache) {
+ if (wszStatus == "typing")
+ CallService(MS_PROTO_CONTACTISTYPING, pCache->m_hContact, 60);
+ else
+ CallService(MS_PROTO_CONTACTISTYPING, pCache->m_hContact, PROTOTYPE_CONTACTTYPING_OFF);
+ }
+}
+
+void CIcqProto::OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+{
+ JsonReply root(pReply);
+ if (root.error() != 200) {
+ ShutdownSession();
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_fetchBaseURL = data["fetchBaseURL"].as_mstring();
+
+ for (auto &it : data["events"])
+ ProcessEvent(it);
+}
+
+void __cdecl CIcqProto::PollThread(void*)
+{
+ debugLogA("Polling thread started");
+ m_bFirstBos = true;
+
+ while (m_bOnline && !m_fetchBaseURL.IsEmpty()) {
+ CMStringA szUrl = m_fetchBaseURL;
+ if (m_bFirstBos)
+ szUrl.Append("&first=1");
+ else
+ szUrl.Append("&timeout=25000");
+
+ auto *pReq = new AsyncHttpRequest(CONN_FETCH, REQUEST_GET, szUrl, &CIcqProto::OnFetchEvents);
+ if (!m_bFirstBos)
+ pReq->timeout = 62000;
+
+ if (!ExecuteRequest(pReq)) {
+ ShutdownSession();
+ break;
+ }
+
+ m_bFirstBos = false;
+ }
+
+ debugLogA("Polling thread ended");
+}
diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp index 84839ad923..5a86c896d8 100644 --- a/protocols/ICQ-WIM/src/proto.cpp +++ b/protocols/ICQ-WIM/src/proto.cpp @@ -1,678 +1,678 @@ -// ---------------------------------------------------------------------------80 -// ICQ plugin for Miranda Instant Messenger -// ________________________________________ -// -// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede -// Copyright © 2001-2002 Jon Keating, Richard Hughes -// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater -// Copyright © 2004-2010 Joe Kucera, George Hazan -// Copyright © 2012-2022 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- -// DESCRIPTION: -// -// Protocol Interface Implementation -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -#include "m_icolib.h" - -#pragma warning(disable:4355) - -static int CompareCache(const IcqCacheItem *p1, const IcqCacheItem *p2) -{ - return mir_wstrcmp(p1->m_aimid, p2->m_aimid); -} - -CIcqProto::CIcqProto(const char *aProtoName, const wchar_t *aUserName) : - PROTO<CIcqProto>(aProtoName, aUserName), - m_impl(*this), - m_arHttpQueue(10), - m_arOwnIds(1, PtrKeySortT), - m_arCache(20, &CompareCache), - m_arGroups(10, NumericKeySortT), - m_arMarkReadQueue(10, NumericKeySortT), - m_evRequestsQueue(CreateEvent(nullptr, FALSE, FALSE, nullptr)), - m_szOwnId(this, DB_KEY_ID), - m_iStatus1(this, "Status1", ID_STATUS_AWAY), - m_iStatus2(this, "Status2", ID_STATUS_NA), - m_iTimeDiff1(this, "TimeDiff1", 0), - m_iTimeDiff2(this, "TimeDiff2", 0), - m_bHideGroupchats(this, "HideChats", true), - m_bUseTrayIcon(this, "UseTrayIcon", false), - m_bErrorPopups(this, "ShowErrorPopups", true), - m_bLaunchMailbox(this, "LaunchMailbox", true) -{ - db_set_resident(m_szModuleName, DB_KEY_IDLE); - db_set_resident(m_szModuleName, DB_KEY_ONLINETS); - - m_isMra = !stricmp(Proto_GetAccount(m_szModuleName)->szProtoName, "MRA"); - - // services - CreateProtoService(PS_CREATEACCMGRUI, &CIcqProto::CreateAccMgrUI); - - CreateProtoService(PS_GETAVATARCAPS, &CIcqProto::GetAvatarCaps); - CreateProtoService(PS_GETAVATARINFO, &CIcqProto::GetAvatarInfo); - CreateProtoService(PS_GETMYAVATAR, &CIcqProto::GetAvatar); - CreateProtoService(PS_SETMYAVATAR, &CIcqProto::SetAvatar); - - CreateProtoService(PS_MENU_LOADHISTORY, &CIcqProto::OnMenuLoadHistory); - CreateProtoService(PS_GETUNREADEMAILCOUNT, &CIcqProto::GetEmailCount); - CreateProtoService(PS_GOTO_INBOX, &CIcqProto::GotoInbox); - - // events - HookProtoEvent(ME_CLIST_GROUPCHANGE, &CIcqProto::OnGroupChange); - HookProtoEvent(ME_DB_EVENT_MARKED_READ, &CIcqProto::OnDbEventRead); - HookProtoEvent(ME_GC_EVENT, &CIcqProto::GroupchatEventHook); - HookProtoEvent(ME_GC_BUILDMENU, &CIcqProto::GroupchatMenuHook); - HookProtoEvent(ME_OPT_INITIALISE, &CIcqProto::OnOptionsInit); - - // group chats - GCREGISTER gcr = {}; - gcr.dwFlags = GC_TYPNOTIF | GC_CHANMGR; - gcr.ptszDispName = m_tszUserName; - gcr.pszModule = m_szModuleName; - Chat_Register(&gcr); - - // netlib handle - NETLIBUSER nlu = {}; - nlu.szSettingsModule = m_szModuleName; - nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; - nlu.szDescriptiveName.w = m_tszUserName; - m_hNetlibUser = Netlib_RegisterUser(&nlu); - - // this was previously an old ICQ account - ptrW wszUin(GetUIN(0)); - if (wszUin != nullptr) { - delSetting("UIN"); - - m_szOwnId = wszUin; - - for (auto &it : AccContacts()) - delSetting(it, "e-mail"); - } - // this was previously an old MRA account - else { - CMStringW wszEmail(getMStringW("e-mail")); - if (!wszEmail.IsEmpty()) { - m_szOwnId = wszEmail; - delSetting("e-mail"); - } - } - - m_hWorkerThread = ForkThreadEx(&CIcqProto::ServerThread, nullptr, nullptr); -} - -CIcqProto::~CIcqProto() -{ - ::CloseHandle(m_evRequestsQueue); -} - -//////////////////////////////////////////////////////////////////////////////////////// -// OnModulesLoadedEx - performs hook registration - -void CIcqProto::OnModulesLoaded() -{ - InitContactCache(); - - HookProtoEvent(ME_USERINFO_INITIALISE, &CIcqProto::OnUserInfoInit); - - // load custom smilies - CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers\\*.png", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName); - SMADD_CONT cont = { 2, m_szModuleName, wszPath }; - CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont)); -} - -void CIcqProto::OnShutdown() -{ - m_bTerminated = true; -} - -void CIcqProto::OnContactAdded(MCONTACT hContact) -{ - CMStringW wszId(getMStringW(hContact, DB_KEY_ID)); - if (!wszId.IsEmpty() && !FindContactByUIN(wszId)) { - mir_cslock l(m_csCache); - m_arCache.insert(new IcqCacheItem(wszId, hContact)); - } -} - -void CIcqProto::OnContactDeleted(MCONTACT hContact) -{ - CMStringW szId(GetUserId(hContact)); - if (!isChatRoom(hContact)) - m_arCache.remove(FindContactByUIN(szId)); - - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/removeBuddy") - << AIMSID(this) << WCHAR_PARAM("buddy", szId) << INT_PARAM("allGroups", 1)); -} - -void CIcqProto::OnEventEdited(MCONTACT, MEVENT) -{ - -} - -INT_PTR CIcqProto::OnMenuLoadHistory(WPARAM hContact, LPARAM) -{ - delSetting(hContact, DB_KEY_LASTMSGID); - - RetrieveUserHistory(hContact, 1, true); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::OnBuildProtoMenu() -{ - CMenuItem mi(&g_plugin); - mi.root = Menu_GetProtocolRoot(this); - mi.flags = CMIF_UNMOVABLE; - - // "Bookmarks..." - mi.pszService = "/UploadGroups"; - CreateProtoService(mi.pszService, &CIcqProto::UploadGroups); - mi.name.a = LPGEN("Synchronize server groups"); - mi.position = 200001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_GROUP); - m_hUploadGroups = Menu_AddProtoMenuItem(&mi, m_szModuleName); - - mi.pszService = "/EditGroups"; - CreateProtoService(mi.pszService, &CIcqProto::EditGroups); - mi.name.a = LPGEN("Edit server groups"); - mi.position = 200002; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_GROUP); - Menu_AddProtoMenuItem(&mi, m_szModuleName); - - mi.pszService = "/EditProfile"; - CreateProtoService(mi.pszService, &CIcqProto::EditProfile); - mi.name.a = LPGEN("Edit my web profile"); - mi.position = 210001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB); - Menu_AddProtoMenuItem(&mi, m_szModuleName); - - Menu_ShowItem(m_hUploadGroups, false); -} - -INT_PTR CIcqProto::UploadGroups(WPARAM, LPARAM) -{ - for (auto &it : AccContacts()) { - if (isChatRoom(it)) - continue; - - ptrW wszIcqGroup(getWStringA(it, "IcqGroup")); - if (wszIcqGroup == nullptr) - continue; - - ptrW wszMirGroup(Clist_GetGroup(it)); - if (!wszMirGroup) - wszMirGroup = mir_wstrdup(L"General"); - if (mir_wstrcmp(wszIcqGroup, wszMirGroup)) - MoveContactToGroup(it, wszIcqGroup, wszMirGroup); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CGroupEditDlg : public CIcqDlgBase -{ - CCtrlListView groups; - -public: - - static CGroupEditDlg *pDlg; - - CGroupEditDlg(CIcqProto *ppro) : - CIcqDlgBase(ppro, IDD_EDITGROUPS), - groups(this, IDC_GROUPS) - { - groups.OnBuildMenu = Callback(this, &CGroupEditDlg::onMenu); - } - - void RefreshGroups() - { - for (auto &it : m_proto->m_arGroups.rev_iter()) - groups.AddItem(it->wszName, 0, (LPARAM)it); - } - - bool OnInitDialog() override - { - pDlg = this; - groups.AddColumn(0, TranslateT("Name"), 300); - RefreshGroups(); - return true; - } - - void OnDestroy() override - { - pDlg = nullptr; - } - - void onMenu(void *) - { - int cur = groups.GetSelectionMark(); - if (cur == -1) - return; - - IcqGroup *pGroup = (IcqGroup *)groups.GetItemData(cur); - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Rename")); - AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete")); - - POINT pt; - GetCursorPos(&pt); - int cmd = TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr); - DestroyMenu(hMenu); - - if (cmd == 1) { // rename - ENTER_STRING es = {}; - es.szModuleName = m_proto->m_szModuleName; - es.caption = TranslateT("Enter new group name"); - if (!EnterString(&es)) - return; - - m_proto->Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/renameGroup") - << AIMSID(m_proto) << WCHAR_PARAM("oldGroup", pGroup->wszSrvName) << GROUP_PARAM("newGroup", es.ptszResult)); - - mir_free(es.ptszResult); - } - else if (cmd == 2) { // delete - m_proto->Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/removeGroup") - << AIMSID(m_proto) << WCHAR_PARAM("group", pGroup->wszSrvName)); - } - } -}; - -CGroupEditDlg *CGroupEditDlg::pDlg = nullptr; - -INT_PTR CIcqProto::EditGroups(WPARAM, LPARAM) -{ - (new CGroupEditDlg(this))->Show(); - return 0; -} - -INT_PTR CIcqProto::EditProfile(WPARAM, LPARAM) -{ - if (mir_wstrlen(m_szOwnId)) - Utils_OpenUrlW(CMStringW(FORMAT, L"https://icq.com/people/%s/edit/", (wchar_t*)m_szOwnId)); - return 0; -} - -void RefreshGroups(void) -{ - if (CGroupEditDlg::pDlg != nullptr) - CGroupEditDlg::pDlg->RefreshGroups(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CIcqProto::GetEmailCount(WPARAM, LPARAM) -{ - if (!m_bOnline) - return 0; - return m_unreadEmails; -} - -INT_PTR CIcqProto::GotoInbox(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://e.mail.ru/messages/inbox"); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::SendMarkRead() -{ - mir_cslock lck(m_csMarkReadQueue); - while (m_arMarkReadQueue.getCount()) { - IcqCacheItem *pUser = m_arMarkReadQueue[0]; - - auto *pReq = new AsyncRapiRequest(this, "setDlgStateWim"); - pReq->params << WCHAR_PARAM("sn", GetUserId(pUser->m_hContact)) << INT64_PARAM("lastRead", getId(pUser->m_hContact, DB_KEY_LASTMSGID)); - Push(pReq); - - m_arMarkReadQueue.remove(0); - } -} - -int CIcqProto::OnDbEventRead(WPARAM, LPARAM hDbEvent) -{ - MCONTACT hContact = db_event_getContact(hDbEvent); - if (!hContact) - return 0; - - // filter out only events of my protocol - const char *szProto = Proto_GetBaseAccountName(hContact); - if (mir_strcmp(szProto, m_szModuleName)) - return 0; - - MarkAsRead(hContact); - return 0; -} - -int CIcqProto::OnGroupChange(WPARAM hContact, LPARAM lParam) -{ - if (!m_bOnline) - return 0; - - CLISTGROUPCHANGE *pParam = (CLISTGROUPCHANGE*)lParam; - if (hContact == 0) { // whole group is changed - if (pParam->pszOldName == nullptr) { - for (auto &it : m_arGroups) - if (it->wszName == pParam->pszNewName) - return 0; - - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/addGroup") - << AIMSID(this) << GROUP_PARAM("group", pParam->pszNewName)); - } - else if (pParam->pszNewName == nullptr) { - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/removeGroup") - << AIMSID(this) << GROUP_PARAM("group", pParam->pszOldName)); - } - else { - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/renameGroup") - << AIMSID(this) << GROUP_PARAM("oldGroup", pParam->pszOldName) << GROUP_PARAM("newGroup", pParam->pszNewName)); - } - } - else MoveContactToGroup(hContact, ptrW(getWStringA(hContact, "IcqGroup")), pParam->pszNewName); - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// PS_AddToList - adds a contact to the contact list - -MCONTACT CIcqProto::AddToList(int, PROTOSEARCHRESULT *psr) -{ - if (mir_wstrlen(psr->id.w) == 0) - return 0; - - MCONTACT hContact = CreateContact(psr->id.w, true); - if (psr->nick.w) - setWString(hContact, "Nick", psr->nick.w); - if (psr->firstName.w) - setWString(hContact, "FirstName", psr->firstName.w); - if (psr->lastName.w) - setWString(hContact, "LastName", psr->lastName.w); - - return hContact; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// PSR_AUTH - -int CIcqProto::AuthRecv(MCONTACT, PROTORECVEVENT *pre) -{ - return Proto_AuthRecv(m_szModuleName, pre); -} - -//////////////////////////////////////////////////////////////////////////////////////// -// PSS_AUTHREQUEST - -int CIcqProto::AuthRequest(MCONTACT hContact, const wchar_t* szMessage) -{ - ptrW wszGroup(Clist_GetGroup(hContact)); - if (!wszGroup) - wszGroup = mir_wstrdup(L"General"); - - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/buddylist/addBuddy", &CIcqProto::OnAddBuddy); - pReq << AIMSID(this) << WCHAR_PARAM("authorizationMsg", szMessage) << WCHAR_PARAM("buddy", GetUserId(hContact)) << WCHAR_PARAM("group", wszGroup) << INT_PARAM("preAuthorized", 1); - pReq->hContact = hContact; - Push(pReq); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// File operations - -HANDLE CIcqProto::FileAllow(MCONTACT, HANDLE hTransfer, const wchar_t *pwszSavePath) -{ - if (!m_bOnline) - return nullptr; - - auto *ft = (IcqFileTransfer *)hTransfer; - ft->m_wszFileName.Insert(0, pwszSavePath); - ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, ft->m_szHost, &CIcqProto::OnFileRecv); - pReq->pUserInfo = ft; - pReq->AddHeader("Sec-Fetch-User", "?1"); - pReq->AddHeader("Sec-Fetch-Site", "cross-site"); - pReq->AddHeader("Sec-Fetch-Mode", "navigate"); - Push(pReq); - - return hTransfer; -} - -int CIcqProto::FileCancel(MCONTACT hContact, HANDLE hTransfer) -{ - ProtoBroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FAILED, hTransfer); - - auto *ft = (IcqFileTransfer *)hTransfer; - if (ft->pfts.currentFileTime != 0) - ft->m_bCanceled = true; - else - delete ft; - return 0; -} - -int CIcqProto::FileResume(HANDLE hTransfer, int, const wchar_t *szFilename) -{ - auto *ft = (IcqFileTransfer *)hTransfer; - if (!m_bOnline || ft == nullptr) - return 1; - - if (szFilename != nullptr) { - ft->m_wszFileName = szFilename; - ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); - } - - ::SetEvent(ft->hWaitEvent); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// GetCaps - return protocol capabilities bits - -INT_PTR CIcqProto::GetCaps(int type, MCONTACT) -{ - INT_PTR nReturn = 0; - - switch (type) { - case PFLAGNUM_1: - nReturn = PF1_IM | PF1_AUTHREQ | PF1_BASICSEARCH | PF1_ADDSEARCHRES | /*PF1_SEARCHBYNAME | TODO */ - PF1_VISLIST | PF1_FILE | PF1_CONTACT | PF1_SERVERCLIST; - break; - - case PFLAGNUM_2: - return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_INVISIBLE; - - case PFLAGNUM_3: - return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_INVISIBLE; - - case PFLAGNUM_5: - return PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_INVISIBLE; - - case PFLAGNUM_4: - nReturn = PF4_FORCEAUTH | PF4_SUPPORTIDLE | PF4_OFFLINEFILES | PF4_IMSENDOFFLINE | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_SERVERMSGID | PF4_READNOTIFY; - break; - - case PFLAG_UNIQUEIDTEXT: - return (INT_PTR)TranslateT("UIN/e-mail/phone"); - } - - return nReturn; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// GetInfo - retrieves a contact info - -int CIcqProto::GetInfo(MCONTACT hContact, int) -{ - RetrieveUserInfo(hContact); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// SearchBasic - searches the contact by UID - -HANDLE CIcqProto::SearchBasic(const wchar_t *pszSearch) -{ - if (!m_bOnline) - return nullptr; - - auto *pReq = new AsyncRapiRequest(this, "search", &CIcqProto::OnSearchResults); - pReq->params << WCHAR_PARAM(*pszSearch == '+' ? "phonenum" : "keyword", pszSearch); - Push(pReq); - - return pReq; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// SendFile - sends a file - -HANDLE CIcqProto::SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) -{ - // we can't send more than one file at a time - if (ppszFiles[1] != 0) - return nullptr; - - struct _stat statbuf; - if (_wstat(ppszFiles[0], &statbuf)) { - debugLogW(L"'%s' is an invalid filename", ppszFiles[0]); - return nullptr; - } - - int iFileId = _wopen(ppszFiles[0], _O_RDONLY | _O_BINARY, _S_IREAD); - if (iFileId < 0) - return nullptr; - - auto *pTransfer = new IcqFileTransfer(hContact, ppszFiles[0]); - pTransfer->pfts.totalFiles = 1; - pTransfer->pfts.currentFileSize = pTransfer->pfts.totalBytes = statbuf.st_size; - pTransfer->m_fileId = iFileId; - if (mir_wstrlen(szDescription)) - pTransfer->m_wszDescr = szDescription; - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, "https://files.icq.com/files/init", &CIcqProto::OnFileInit); - pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("client", "icq") << CHAR_PARAM("f", "json") << WCHAR_PARAM("filename", pTransfer->m_wszShortName) - << CHAR_PARAM("k", appId()) << INT_PARAM("size", statbuf.st_size) << INT_PARAM("ts", TS()); - CalcHash(pReq); - pReq->pUserInfo = pTransfer; - Push(pReq); - - return pTransfer; // Failure -} - -//////////////////////////////////////////////////////////////////////////////////////// -// PS_SendMessage - sends a message - -int CIcqProto::SendMsg(MCONTACT hContact, int, const char *pszSrc) -{ - CMStringA szUserid(GetUserId(hContact)); - if (szUserid.IsEmpty()) - return 0; - - int id = InterlockedIncrement(&m_msgId); - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/im/sendIM", &CIcqProto::OnSendMessage); - - auto *pOwn = new IcqOwnMessage(hContact, id, pReq->m_reqId); - pReq->pUserInfo = pOwn; - { - mir_cslock lck(m_csOwnIds); - m_arOwnIds.insert(pOwn); - } - - pReq << AIMSID(this) << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("mentions", "") - << CHAR_PARAM("message", pszSrc) << CHAR_PARAM("offlineIM", "true") << CHAR_PARAM("t", szUserid) << INT_PARAM("ts", TS()); - Push(pReq); - return id; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// PS_SetStatus - sets the protocol status - -int CIcqProto::SetStatus(int iNewStatus) -{ - debugLogA("CIcqProto::SetStatus iNewStatus = %d, m_iStatus = %d, m_iDesiredStatus = %d m_hWorkerThread = %p", iNewStatus, m_iStatus, m_iDesiredStatus, m_hWorkerThread); - - if (iNewStatus == m_iStatus) - return 0; - - m_iDesiredStatus = iNewStatus; - int iOldStatus = m_iStatus; - - // go offline - if (iNewStatus == ID_STATUS_OFFLINE) { - if (m_bOnline) - SetServerStatus(ID_STATUS_OFFLINE); - - m_iStatus = m_iDesiredStatus; - setAllContactStatuses(ID_STATUS_OFFLINE, false); - - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus); - } - // not logged in? come on - else if (!m_bOnline && !IsStatusConnecting(m_iStatus)) { - m_iStatus = ID_STATUS_CONNECTING; - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus); - - if (mir_wstrlen(m_szOwnId) == 0) { - debugLogA("Thread ended, UIN/password are not configured"); - ConnectionFailed(LOGINERR_BADUSERID); - return 0; - } - - if (!RetrievePassword()) { - debugLogA("Thread ended, password is not configured"); - ConnectionFailed(LOGINERR_BADUSERID); - return 0; - } - - CheckPassword(); - } - else if (m_bOnline) { - debugLogA("setting server online status to %d", iNewStatus); - SetServerStatus(iNewStatus); - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// PS_UserIsTyping - sends a UTN notification - -int CIcqProto::UserIsTyping(MCONTACT hContact, int type) -{ - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/im/setTyping") - << AIMSID(this) << WCHAR_PARAM("t", GetUserId(hContact)) << CHAR_PARAM("typingStatus", (type == PROTOTYPE_SELFTYPING_ON) ? "typing" : "typed")); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// PS_SetApparentMode - sets the visibility status - -int CIcqProto::SetApparentMode(MCONTACT hContact, int iMode) -{ - int oldMode = getWord(hContact, "ApparentMode"); - if (oldMode != iMode) { - setWord(hContact, "ApparentMode", iMode); - SetPermitDeny(GetUserId(hContact), iMode != ID_STATUS_OFFLINE); - } - return 0; -} +// ---------------------------------------------------------------------------80
+// ICQ plugin for Miranda Instant Messenger
+// ________________________________________
+//
+// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede
+// Copyright © 2001-2002 Jon Keating, Richard Hughes
+// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater
+// Copyright © 2004-2010 Joe Kucera, George Hazan
+// Copyright © 2012-2023 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+// DESCRIPTION:
+//
+// Protocol Interface Implementation
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "m_icolib.h"
+
+#pragma warning(disable:4355)
+
+static int CompareCache(const IcqCacheItem *p1, const IcqCacheItem *p2)
+{
+ return mir_wstrcmp(p1->m_aimid, p2->m_aimid);
+}
+
+CIcqProto::CIcqProto(const char *aProtoName, const wchar_t *aUserName) :
+ PROTO<CIcqProto>(aProtoName, aUserName),
+ m_impl(*this),
+ m_arHttpQueue(10),
+ m_arOwnIds(1, PtrKeySortT),
+ m_arCache(20, &CompareCache),
+ m_arGroups(10, NumericKeySortT),
+ m_arMarkReadQueue(10, NumericKeySortT),
+ m_evRequestsQueue(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
+ m_szOwnId(this, DB_KEY_ID),
+ m_iStatus1(this, "Status1", ID_STATUS_AWAY),
+ m_iStatus2(this, "Status2", ID_STATUS_NA),
+ m_iTimeDiff1(this, "TimeDiff1", 0),
+ m_iTimeDiff2(this, "TimeDiff2", 0),
+ m_bHideGroupchats(this, "HideChats", true),
+ m_bUseTrayIcon(this, "UseTrayIcon", false),
+ m_bErrorPopups(this, "ShowErrorPopups", true),
+ m_bLaunchMailbox(this, "LaunchMailbox", true)
+{
+ db_set_resident(m_szModuleName, DB_KEY_IDLE);
+ db_set_resident(m_szModuleName, DB_KEY_ONLINETS);
+
+ m_isMra = !stricmp(Proto_GetAccount(m_szModuleName)->szProtoName, "MRA");
+
+ // services
+ CreateProtoService(PS_CREATEACCMGRUI, &CIcqProto::CreateAccMgrUI);
+
+ CreateProtoService(PS_GETAVATARCAPS, &CIcqProto::GetAvatarCaps);
+ CreateProtoService(PS_GETAVATARINFO, &CIcqProto::GetAvatarInfo);
+ CreateProtoService(PS_GETMYAVATAR, &CIcqProto::GetAvatar);
+ CreateProtoService(PS_SETMYAVATAR, &CIcqProto::SetAvatar);
+
+ CreateProtoService(PS_MENU_LOADHISTORY, &CIcqProto::OnMenuLoadHistory);
+ CreateProtoService(PS_GETUNREADEMAILCOUNT, &CIcqProto::GetEmailCount);
+ CreateProtoService(PS_GOTO_INBOX, &CIcqProto::GotoInbox);
+
+ // events
+ HookProtoEvent(ME_CLIST_GROUPCHANGE, &CIcqProto::OnGroupChange);
+ HookProtoEvent(ME_DB_EVENT_MARKED_READ, &CIcqProto::OnDbEventRead);
+ HookProtoEvent(ME_GC_EVENT, &CIcqProto::GroupchatEventHook);
+ HookProtoEvent(ME_GC_BUILDMENU, &CIcqProto::GroupchatMenuHook);
+ HookProtoEvent(ME_OPT_INITIALISE, &CIcqProto::OnOptionsInit);
+
+ // group chats
+ GCREGISTER gcr = {};
+ gcr.dwFlags = GC_TYPNOTIF | GC_CHANMGR;
+ gcr.ptszDispName = m_tszUserName;
+ gcr.pszModule = m_szModuleName;
+ Chat_Register(&gcr);
+
+ // netlib handle
+ NETLIBUSER nlu = {};
+ nlu.szSettingsModule = m_szModuleName;
+ nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE;
+ nlu.szDescriptiveName.w = m_tszUserName;
+ m_hNetlibUser = Netlib_RegisterUser(&nlu);
+
+ // this was previously an old ICQ account
+ ptrW wszUin(GetUIN(0));
+ if (wszUin != nullptr) {
+ delSetting("UIN");
+
+ m_szOwnId = wszUin;
+
+ for (auto &it : AccContacts())
+ delSetting(it, "e-mail");
+ }
+ // this was previously an old MRA account
+ else {
+ CMStringW wszEmail(getMStringW("e-mail"));
+ if (!wszEmail.IsEmpty()) {
+ m_szOwnId = wszEmail;
+ delSetting("e-mail");
+ }
+ }
+
+ m_hWorkerThread = ForkThreadEx(&CIcqProto::ServerThread, nullptr, nullptr);
+}
+
+CIcqProto::~CIcqProto()
+{
+ ::CloseHandle(m_evRequestsQueue);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// OnModulesLoadedEx - performs hook registration
+
+void CIcqProto::OnModulesLoaded()
+{
+ InitContactCache();
+
+ HookProtoEvent(ME_USERINFO_INITIALISE, &CIcqProto::OnUserInfoInit);
+
+ // load custom smilies
+ CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers\\*.png", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName);
+ SMADD_CONT cont = { 2, m_szModuleName, wszPath };
+ CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont));
+}
+
+void CIcqProto::OnShutdown()
+{
+ m_bTerminated = true;
+}
+
+void CIcqProto::OnContactAdded(MCONTACT hContact)
+{
+ CMStringW wszId(getMStringW(hContact, DB_KEY_ID));
+ if (!wszId.IsEmpty() && !FindContactByUIN(wszId)) {
+ mir_cslock l(m_csCache);
+ m_arCache.insert(new IcqCacheItem(wszId, hContact));
+ }
+}
+
+void CIcqProto::OnContactDeleted(MCONTACT hContact)
+{
+ CMStringW szId(GetUserId(hContact));
+ if (!isChatRoom(hContact))
+ m_arCache.remove(FindContactByUIN(szId));
+
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/removeBuddy")
+ << AIMSID(this) << WCHAR_PARAM("buddy", szId) << INT_PARAM("allGroups", 1));
+}
+
+void CIcqProto::OnEventEdited(MCONTACT, MEVENT)
+{
+
+}
+
+INT_PTR CIcqProto::OnMenuLoadHistory(WPARAM hContact, LPARAM)
+{
+ delSetting(hContact, DB_KEY_LASTMSGID);
+
+ RetrieveUserHistory(hContact, 1, true);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OnBuildProtoMenu()
+{
+ CMenuItem mi(&g_plugin);
+ mi.root = Menu_GetProtocolRoot(this);
+ mi.flags = CMIF_UNMOVABLE;
+
+ // "Bookmarks..."
+ mi.pszService = "/UploadGroups";
+ CreateProtoService(mi.pszService, &CIcqProto::UploadGroups);
+ mi.name.a = LPGEN("Synchronize server groups");
+ mi.position = 200001;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_GROUP);
+ m_hUploadGroups = Menu_AddProtoMenuItem(&mi, m_szModuleName);
+
+ mi.pszService = "/EditGroups";
+ CreateProtoService(mi.pszService, &CIcqProto::EditGroups);
+ mi.name.a = LPGEN("Edit server groups");
+ mi.position = 200002;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_GROUP);
+ Menu_AddProtoMenuItem(&mi, m_szModuleName);
+
+ mi.pszService = "/EditProfile";
+ CreateProtoService(mi.pszService, &CIcqProto::EditProfile);
+ mi.name.a = LPGEN("Edit my web profile");
+ mi.position = 210001;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB);
+ Menu_AddProtoMenuItem(&mi, m_szModuleName);
+
+ Menu_ShowItem(m_hUploadGroups, false);
+}
+
+INT_PTR CIcqProto::UploadGroups(WPARAM, LPARAM)
+{
+ for (auto &it : AccContacts()) {
+ if (isChatRoom(it))
+ continue;
+
+ ptrW wszIcqGroup(getWStringA(it, "IcqGroup"));
+ if (wszIcqGroup == nullptr)
+ continue;
+
+ ptrW wszMirGroup(Clist_GetGroup(it));
+ if (!wszMirGroup)
+ wszMirGroup = mir_wstrdup(L"General");
+ if (mir_wstrcmp(wszIcqGroup, wszMirGroup))
+ MoveContactToGroup(it, wszIcqGroup, wszMirGroup);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CGroupEditDlg : public CIcqDlgBase
+{
+ CCtrlListView groups;
+
+public:
+
+ static CGroupEditDlg *pDlg;
+
+ CGroupEditDlg(CIcqProto *ppro) :
+ CIcqDlgBase(ppro, IDD_EDITGROUPS),
+ groups(this, IDC_GROUPS)
+ {
+ groups.OnBuildMenu = Callback(this, &CGroupEditDlg::onMenu);
+ }
+
+ void RefreshGroups()
+ {
+ for (auto &it : m_proto->m_arGroups.rev_iter())
+ groups.AddItem(it->wszName, 0, (LPARAM)it);
+ }
+
+ bool OnInitDialog() override
+ {
+ pDlg = this;
+ groups.AddColumn(0, TranslateT("Name"), 300);
+ RefreshGroups();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pDlg = nullptr;
+ }
+
+ void onMenu(void *)
+ {
+ int cur = groups.GetSelectionMark();
+ if (cur == -1)
+ return;
+
+ IcqGroup *pGroup = (IcqGroup *)groups.GetItemData(cur);
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Rename"));
+ AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete"));
+
+ POINT pt;
+ GetCursorPos(&pt);
+ int cmd = TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
+ DestroyMenu(hMenu);
+
+ if (cmd == 1) { // rename
+ ENTER_STRING es = {};
+ es.szModuleName = m_proto->m_szModuleName;
+ es.caption = TranslateT("Enter new group name");
+ if (!EnterString(&es))
+ return;
+
+ m_proto->Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/renameGroup")
+ << AIMSID(m_proto) << WCHAR_PARAM("oldGroup", pGroup->wszSrvName) << GROUP_PARAM("newGroup", es.ptszResult));
+
+ mir_free(es.ptszResult);
+ }
+ else if (cmd == 2) { // delete
+ m_proto->Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/removeGroup")
+ << AIMSID(m_proto) << WCHAR_PARAM("group", pGroup->wszSrvName));
+ }
+ }
+};
+
+CGroupEditDlg *CGroupEditDlg::pDlg = nullptr;
+
+INT_PTR CIcqProto::EditGroups(WPARAM, LPARAM)
+{
+ (new CGroupEditDlg(this))->Show();
+ return 0;
+}
+
+INT_PTR CIcqProto::EditProfile(WPARAM, LPARAM)
+{
+ if (mir_wstrlen(m_szOwnId))
+ Utils_OpenUrlW(CMStringW(FORMAT, L"https://icq.com/people/%s/edit/", (wchar_t*)m_szOwnId));
+ return 0;
+}
+
+void RefreshGroups(void)
+{
+ if (CGroupEditDlg::pDlg != nullptr)
+ CGroupEditDlg::pDlg->RefreshGroups();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CIcqProto::GetEmailCount(WPARAM, LPARAM)
+{
+ if (!m_bOnline)
+ return 0;
+ return m_unreadEmails;
+}
+
+INT_PTR CIcqProto::GotoInbox(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://e.mail.ru/messages/inbox");
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::SendMarkRead()
+{
+ mir_cslock lck(m_csMarkReadQueue);
+ while (m_arMarkReadQueue.getCount()) {
+ IcqCacheItem *pUser = m_arMarkReadQueue[0];
+
+ auto *pReq = new AsyncRapiRequest(this, "setDlgStateWim");
+ pReq->params << WCHAR_PARAM("sn", GetUserId(pUser->m_hContact)) << INT64_PARAM("lastRead", getId(pUser->m_hContact, DB_KEY_LASTMSGID));
+ Push(pReq);
+
+ m_arMarkReadQueue.remove(0);
+ }
+}
+
+int CIcqProto::OnDbEventRead(WPARAM, LPARAM hDbEvent)
+{
+ MCONTACT hContact = db_event_getContact(hDbEvent);
+ if (!hContact)
+ return 0;
+
+ // filter out only events of my protocol
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (mir_strcmp(szProto, m_szModuleName))
+ return 0;
+
+ MarkAsRead(hContact);
+ return 0;
+}
+
+int CIcqProto::OnGroupChange(WPARAM hContact, LPARAM lParam)
+{
+ if (!m_bOnline)
+ return 0;
+
+ CLISTGROUPCHANGE *pParam = (CLISTGROUPCHANGE*)lParam;
+ if (hContact == 0) { // whole group is changed
+ if (pParam->pszOldName == nullptr) {
+ for (auto &it : m_arGroups)
+ if (it->wszName == pParam->pszNewName)
+ return 0;
+
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/addGroup")
+ << AIMSID(this) << GROUP_PARAM("group", pParam->pszNewName));
+ }
+ else if (pParam->pszNewName == nullptr) {
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/removeGroup")
+ << AIMSID(this) << GROUP_PARAM("group", pParam->pszOldName));
+ }
+ else {
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/renameGroup")
+ << AIMSID(this) << GROUP_PARAM("oldGroup", pParam->pszOldName) << GROUP_PARAM("newGroup", pParam->pszNewName));
+ }
+ }
+ else MoveContactToGroup(hContact, ptrW(getWStringA(hContact, "IcqGroup")), pParam->pszNewName);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PS_AddToList - adds a contact to the contact list
+
+MCONTACT CIcqProto::AddToList(int, PROTOSEARCHRESULT *psr)
+{
+ if (mir_wstrlen(psr->id.w) == 0)
+ return 0;
+
+ MCONTACT hContact = CreateContact(psr->id.w, true);
+ if (psr->nick.w)
+ setWString(hContact, "Nick", psr->nick.w);
+ if (psr->firstName.w)
+ setWString(hContact, "FirstName", psr->firstName.w);
+ if (psr->lastName.w)
+ setWString(hContact, "LastName", psr->lastName.w);
+
+ return hContact;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSR_AUTH
+
+int CIcqProto::AuthRecv(MCONTACT, PROTORECVEVENT *pre)
+{
+ return Proto_AuthRecv(m_szModuleName, pre);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSS_AUTHREQUEST
+
+int CIcqProto::AuthRequest(MCONTACT hContact, const wchar_t* szMessage)
+{
+ ptrW wszGroup(Clist_GetGroup(hContact));
+ if (!wszGroup)
+ wszGroup = mir_wstrdup(L"General");
+
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/buddylist/addBuddy", &CIcqProto::OnAddBuddy);
+ pReq << AIMSID(this) << WCHAR_PARAM("authorizationMsg", szMessage) << WCHAR_PARAM("buddy", GetUserId(hContact)) << WCHAR_PARAM("group", wszGroup) << INT_PARAM("preAuthorized", 1);
+ pReq->hContact = hContact;
+ Push(pReq);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// File operations
+
+HANDLE CIcqProto::FileAllow(MCONTACT, HANDLE hTransfer, const wchar_t *pwszSavePath)
+{
+ if (!m_bOnline)
+ return nullptr;
+
+ auto *ft = (IcqFileTransfer *)hTransfer;
+ ft->m_wszFileName.Insert(0, pwszSavePath);
+ ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer();
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, ft->m_szHost, &CIcqProto::OnFileRecv);
+ pReq->pUserInfo = ft;
+ pReq->AddHeader("Sec-Fetch-User", "?1");
+ pReq->AddHeader("Sec-Fetch-Site", "cross-site");
+ pReq->AddHeader("Sec-Fetch-Mode", "navigate");
+ Push(pReq);
+
+ return hTransfer;
+}
+
+int CIcqProto::FileCancel(MCONTACT hContact, HANDLE hTransfer)
+{
+ ProtoBroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FAILED, hTransfer);
+
+ auto *ft = (IcqFileTransfer *)hTransfer;
+ if (ft->pfts.currentFileTime != 0)
+ ft->m_bCanceled = true;
+ else
+ delete ft;
+ return 0;
+}
+
+int CIcqProto::FileResume(HANDLE hTransfer, int, const wchar_t *szFilename)
+{
+ auto *ft = (IcqFileTransfer *)hTransfer;
+ if (!m_bOnline || ft == nullptr)
+ return 1;
+
+ if (szFilename != nullptr) {
+ ft->m_wszFileName = szFilename;
+ ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer();
+ }
+
+ ::SetEvent(ft->hWaitEvent);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetCaps - return protocol capabilities bits
+
+INT_PTR CIcqProto::GetCaps(int type, MCONTACT)
+{
+ INT_PTR nReturn = 0;
+
+ switch (type) {
+ case PFLAGNUM_1:
+ nReturn = PF1_IM | PF1_AUTHREQ | PF1_BASICSEARCH | PF1_ADDSEARCHRES | /*PF1_SEARCHBYNAME | TODO */
+ PF1_VISLIST | PF1_FILE | PF1_CONTACT | PF1_SERVERCLIST;
+ break;
+
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_INVISIBLE;
+
+ case PFLAGNUM_3:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_INVISIBLE;
+
+ case PFLAGNUM_5:
+ return PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND | PF2_INVISIBLE;
+
+ case PFLAGNUM_4:
+ nReturn = PF4_FORCEAUTH | PF4_SUPPORTIDLE | PF4_OFFLINEFILES | PF4_IMSENDOFFLINE | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_SERVERMSGID | PF4_READNOTIFY;
+ break;
+
+ case PFLAG_UNIQUEIDTEXT:
+ return (INT_PTR)TranslateT("UIN/e-mail/phone");
+ }
+
+ return nReturn;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetInfo - retrieves a contact info
+
+int CIcqProto::GetInfo(MCONTACT hContact, int)
+{
+ RetrieveUserInfo(hContact);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SearchBasic - searches the contact by UID
+
+HANDLE CIcqProto::SearchBasic(const wchar_t *pszSearch)
+{
+ if (!m_bOnline)
+ return nullptr;
+
+ auto *pReq = new AsyncRapiRequest(this, "search", &CIcqProto::OnSearchResults);
+ pReq->params << WCHAR_PARAM(*pszSearch == '+' ? "phonenum" : "keyword", pszSearch);
+ Push(pReq);
+
+ return pReq;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendFile - sends a file
+
+HANDLE CIcqProto::SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles)
+{
+ // we can't send more than one file at a time
+ if (ppszFiles[1] != 0)
+ return nullptr;
+
+ struct _stat statbuf;
+ if (_wstat(ppszFiles[0], &statbuf)) {
+ debugLogW(L"'%s' is an invalid filename", ppszFiles[0]);
+ return nullptr;
+ }
+
+ int iFileId = _wopen(ppszFiles[0], _O_RDONLY | _O_BINARY, _S_IREAD);
+ if (iFileId < 0)
+ return nullptr;
+
+ auto *pTransfer = new IcqFileTransfer(hContact, ppszFiles[0]);
+ pTransfer->pfts.totalFiles = 1;
+ pTransfer->pfts.currentFileSize = pTransfer->pfts.totalBytes = statbuf.st_size;
+ pTransfer->m_fileId = iFileId;
+ if (mir_wstrlen(szDescription))
+ pTransfer->m_wszDescr = szDescription;
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, "https://files.icq.com/files/init", &CIcqProto::OnFileInit);
+ pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("client", "icq") << CHAR_PARAM("f", "json") << WCHAR_PARAM("filename", pTransfer->m_wszShortName)
+ << CHAR_PARAM("k", appId()) << INT_PARAM("size", statbuf.st_size) << INT_PARAM("ts", TS());
+ CalcHash(pReq);
+ pReq->pUserInfo = pTransfer;
+ Push(pReq);
+
+ return pTransfer; // Failure
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PS_SendMessage - sends a message
+
+int CIcqProto::SendMsg(MCONTACT hContact, int, const char *pszSrc)
+{
+ CMStringA szUserid(GetUserId(hContact));
+ if (szUserid.IsEmpty())
+ return 0;
+
+ int id = InterlockedIncrement(&m_msgId);
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/im/sendIM", &CIcqProto::OnSendMessage);
+
+ auto *pOwn = new IcqOwnMessage(hContact, id, pReq->m_reqId);
+ pReq->pUserInfo = pOwn;
+ {
+ mir_cslock lck(m_csOwnIds);
+ m_arOwnIds.insert(pOwn);
+ }
+
+ pReq << AIMSID(this) << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("mentions", "")
+ << CHAR_PARAM("message", pszSrc) << CHAR_PARAM("offlineIM", "true") << CHAR_PARAM("t", szUserid) << INT_PARAM("ts", TS());
+ Push(pReq);
+ return id;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PS_SetStatus - sets the protocol status
+
+int CIcqProto::SetStatus(int iNewStatus)
+{
+ debugLogA("CIcqProto::SetStatus iNewStatus = %d, m_iStatus = %d, m_iDesiredStatus = %d m_hWorkerThread = %p", iNewStatus, m_iStatus, m_iDesiredStatus, m_hWorkerThread);
+
+ if (iNewStatus == m_iStatus)
+ return 0;
+
+ m_iDesiredStatus = iNewStatus;
+ int iOldStatus = m_iStatus;
+
+ // go offline
+ if (iNewStatus == ID_STATUS_OFFLINE) {
+ if (m_bOnline)
+ SetServerStatus(ID_STATUS_OFFLINE);
+
+ m_iStatus = m_iDesiredStatus;
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
+
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
+ }
+ // not logged in? come on
+ else if (!m_bOnline && !IsStatusConnecting(m_iStatus)) {
+ m_iStatus = ID_STATUS_CONNECTING;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
+
+ if (mir_wstrlen(m_szOwnId) == 0) {
+ debugLogA("Thread ended, UIN/password are not configured");
+ ConnectionFailed(LOGINERR_BADUSERID);
+ return 0;
+ }
+
+ if (!RetrievePassword()) {
+ debugLogA("Thread ended, password is not configured");
+ ConnectionFailed(LOGINERR_BADUSERID);
+ return 0;
+ }
+
+ CheckPassword();
+ }
+ else if (m_bOnline) {
+ debugLogA("setting server online status to %d", iNewStatus);
+ SetServerStatus(iNewStatus);
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// PS_UserIsTyping - sends a UTN notification
+
+int CIcqProto::UserIsTyping(MCONTACT hContact, int type)
+{
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/im/setTyping")
+ << AIMSID(this) << WCHAR_PARAM("t", GetUserId(hContact)) << CHAR_PARAM("typingStatus", (type == PROTOTYPE_SELFTYPING_ON) ? "typing" : "typed"));
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PS_SetApparentMode - sets the visibility status
+
+int CIcqProto::SetApparentMode(MCONTACT hContact, int iMode)
+{
+ int oldMode = getWord(hContact, "ApparentMode");
+ if (oldMode != iMode) {
+ setWord(hContact, "ApparentMode", iMode);
+ SetPermitDeny(GetUserId(hContact), iMode != ID_STATUS_OFFLINE);
+ }
+ return 0;
+}
diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h index b909a0d74a..20636cc4c6 100644 --- a/protocols/ICQ-WIM/src/proto.h +++ b/protocols/ICQ-WIM/src/proto.h @@ -1,494 +1,494 @@ -// ---------------------------------------------------------------------------80 -// ICQ plugin for Miranda Instant Messenger -// ________________________________________ -// -// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede -// Copyright © 2001-2002 Jon Keating, Richard Hughes -// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater -// Copyright © 2004-2010 Joe Kucera, George Hazan -// Copyright © 2012-2022 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- -// DESCRIPTION: -// -// Protocol Interface declarations -// ----------------------------------------------------------------------------- - -#ifndef _ICQ_PROTO_H_ -#define _ICQ_PROTO_H_ - -#include "m_system.h" -#include "m_protoint.h" - -#define MRA_APP_ID "ic1pzYNtEU6dDnEQ" -#define ICQ_APP_ID "ic1nmMjqg7Yu-0hL" -#define ICQ_API_SERVER "https://u.icq.net/wim" -#define ICQ_FILE_SERVER "https://u.icq.net/files/api/v1.1" -#define ICQ_FAKE_EVENT_ID 0xBABAEB -#define ICQ_ROBUST_SERVER "https://u.icq.net/rapi" - -#define PS_DUMMY "/DoNothing" -#define PS_GOTO_INBOX "/GotoInbox" - -#define WIM_CAP_VOIP_VOICE "094613504c7f11d18222444553540000" -#define WIM_CAP_VOIP_VIDEO "094613514c7f11d18222444553540000" -#define WIM_CAP_FILETRANSFER "094613434c7f11d18222444553540000" -#define WIM_CAP_UNIQ_REQ_ID "094613534c7f11d18222444553540000" -#define WIM_CAP_EMOJI "094613544c7f11d18222444553540000" -#define WIM_CAP_MENTIONS "0946135b4c7f11d18222444553540000" -#define WIM_CAP_MAIL_NOTIFICATIONS "094613594c7f11d18222444553540000" -#define WIM_CAP_INTRO_DLG_STATE "0946135a4c7f11d18222444553540000" - -#define NG_CAP_SECUREIM "4d69724e47536563757265494d000000" - -typedef CProtoDlgBase<CIcqProto> CIcqDlgBase; - -struct AIMSID -{ - AIMSID(CIcqProto *_ppro) : - m_ppro(_ppro) - {} - - CIcqProto *m_ppro; -}; - -enum ChatMenuItems -{ - IDM_INVITE = 10, IDM_LEAVE -}; - -struct IcqFileInfo -{ - IcqFileInfo(const std::string &pszUrl, const CMStringW &pwszDescr, uint32_t dwSize) : - szUrl(pszUrl.c_str()), - wszDescr(pwszDescr), - dwFileSize(dwSize) - {} - - CMStringA szUrl; - CMStringW wszDescr; - uint32_t dwFileSize; - bool bIsSticker = false; -}; - -struct IcqGroup -{ - IcqGroup(int _p1, const CMStringW &_p2) : - id(_p1), - wszSrvName(_p2) - { - SetName(_p2); - } - - int id; - int level; - CMStringW wszName, wszSrvName; - - void SetName(const CMStringW &str) - { - wszName = str; - level = wszName.SpanIncluding(L">").GetLength(); - if (level != 0) - wszName.Delete(0, level); - wszName.Replace(L">", L"\\"); - } -}; - -struct IcqCacheItem : public MZeroedObject -{ - IcqCacheItem(const CMStringW &wszId, MCONTACT _contact) : - m_aimid(wszId), - m_hContact(_contact) - {} - - CMStringW m_aimid; - MCONTACT m_hContact; - bool m_bInList; - __int64 m_iProcessedMsgId; - int m_iApparentMode; - time_t m_timer1, m_timer2; -}; - -struct IcqOwnMessage -{ - IcqOwnMessage(MCONTACT _hContact, int _msgid, const char *guid) - : m_hContact(_hContact), m_msgid(_msgid) - { - strncpy_s(m_guid, guid, _TRUNCATE); - } - - MCONTACT m_hContact; - int m_msgid; - char m_guid[50]; -}; - -struct IcqConn -{ - HNETLIBCONN s; - int lastTs, timeout; -}; - -struct IcqFileTransfer : public MZeroedObject -{ - // create an object for receiving - IcqFileTransfer(MCONTACT hContact, const char *pszUrl) : - m_szHost(pszUrl) - { - pfts.hContact = hContact; - pfts.totalFiles = 1; - pfts.flags = PFTS_UNICODE | PFTS_RECEIVING; - - ptrW pwszFileName(mir_utf8decodeW(pszUrl)); - if (pwszFileName == nullptr) - pwszFileName = mir_a2u(pszUrl); - - const wchar_t *p = wcsrchr(pwszFileName, '/'); - m_wszFileName = (p == nullptr) ? pwszFileName : p + 1; - m_wszShortName = m_wszFileName; - } - - // create an object for sending - IcqFileTransfer(MCONTACT hContact, const wchar_t *pwszFileName) : - m_wszFileName(pwszFileName) - { - pfts.flags = PFTS_UNICODE | PFTS_SENDING; - pfts.hContact = hContact; - pfts.szCurrentFile.w = m_wszFileName.GetBuffer(); - - const wchar_t *p = wcsrchr(pfts.szCurrentFile.w, '\\'); - if (pwszFileName != nullptr) - p++; - else - p = pfts.szCurrentFile.w; - m_wszShortName = p; - } - - ~IcqFileTransfer() - { - if (m_fileId >= 0) - _close(m_fileId); - } - - bool m_bCanceled = false, m_bStarted = false; - int m_fileId = -1; - CMStringA m_szHost; - CMStringW m_wszFileName, m_wszDescr; - const wchar_t *m_wszShortName; - PROTOFILETRANSFERSTATUS pfts; - HANDLE hWaitEvent; - - void FillHeaders(AsyncHttpRequest *pReq) - { - pReq->AddHeader("Content-Type", "application/octet-stream"); - pReq->AddHeader("Content-Disposition", CMStringA(FORMAT, "attachment; filename=\"%s\"", T2Utf(m_wszShortName).get())); - - uint32_t dwPortion = pfts.currentFileSize - pfts.currentFileProgress; - if (dwPortion > 1000000) - dwPortion = 1000000; - - pReq->AddHeader("Content-Range", CMStringA(FORMAT, "bytes %lld-%lld/%lld", pfts.currentFileProgress, pfts.currentFileProgress + dwPortion - 1, pfts.currentFileSize)); - pReq->AddHeader("Content-Length", CMStringA(FORMAT, "%d", dwPortion)); - - pReq->dataLength = dwPortion; - pReq->pData = (char*)mir_alloc(dwPortion); - _lseek(m_fileId, pfts.currentFileProgress, SEEK_SET); - _read(m_fileId, pReq->pData, dwPortion); - - pfts.currentFileProgress += dwPortion; - pfts.totalProgress += dwPortion; - } -}; - -class CIcqProto : public PROTO<CIcqProto> -{ - friend struct AsyncRapiRequest; - - class CIcqProtoImpl - { - friend class CIcqProto; - - CIcqProto &m_proto; - CTimer m_heartBeat, m_markRead; - - void OnHeartBeat(CTimer *) { - m_proto.CheckStatus(); - } - - void OnMarkRead(CTimer *pTimer) { - m_proto.SendMarkRead(); - pTimer->Stop(); - } - - CIcqProtoImpl(CIcqProto &pro) : - m_proto(pro), - m_markRead(Miranda_GetSystemWindow(), UINT_PTR(this)), - m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this) + 1) - { - m_markRead.OnEvent = Callback(this, &CIcqProtoImpl::OnMarkRead); - m_heartBeat.OnEvent = Callback(this, &CIcqProtoImpl::OnHeartBeat); - } - } m_impl; - - friend struct CIcqRegistrationDlg; - friend class CGroupchatInviteDlg; - friend class CEditIgnoreListDlg; - friend class CIcqEnterLoginDlg; - friend class CIcqOptionsDlg; - friend class CGroupEditDlg; - - friend AsyncHttpRequest* operator <<(AsyncHttpRequest*, const AIMSID&); - - bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462; - int m_iTimeShift; - - MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove); - void CheckPassword(void); - void ConnectionFailed(int iReason, int iErrorCode = 0); - void EmailNotification(const wchar_t *pwszText); - void GetPermitDeny(); - wchar_t* GetUIN(MCONTACT hContact); - void MarkAsRead(MCONTACT hContact); - void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup); - bool RetrievePassword(); - void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead); - void RetrieveUserInfo(MCONTACT hContact); - void SendMrimLogin(NETLIBHTTPREQUEST *pReply); - void SetServerStatus(int iNewStatus); - void ShutdownSession(void); - void StartSession(void); - - void CheckAvatarChange(MCONTACT hContact, const JSONNode&); - IcqFileInfo* CheckFile(MCONTACT hContact, CMStringW &wszFileName, bool &bIsFile); - void CheckLastId(MCONTACT hContact, const JSONNode&); - void Json2int(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); - void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); - MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false); - void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime); - int StatusFromPresence(const JSONNode &presence, MCONTACT hContact); - - void OnLoggedIn(void); - void OnLoggedOut(void); - - mir_cs m_csMarkReadQueue; - LIST<IcqCacheItem> m_arMarkReadQueue; - void SendMarkRead(); - - __int64 getId(MCONTACT hContact, const char *szSetting); - void setId(MCONTACT hContact, const char *szSetting, __int64 iValue); - - void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - - void ProcessBuddyList(const JSONNode &pRoot); - void ProcessDiff(const JSONNode &pRoot); - void ProcessEvent(const JSONNode &pRoot); - void ProcessGroupChat(const JSONNode &pRoot); - void ProcessHistData(const JSONNode &pRoot); - void ProcessImState(const JSONNode &pRoot); - void ProcessMyInfo(const JSONNode &pRoot); - void ProcessNotification(const JSONNode &pRoot); - void ProcessPermissions(const JSONNode &pRoot); - void ProcessPresence(const JSONNode &pRoot); - void ProcessSessionEnd(const JSONNode &pRoot); - void ProcessTyping(const JSONNode &pRoot); - - IcqConn m_ConnPool[CONN_LAST]; - CMStringA m_szPassword; - CMStringA m_szSessionKey; - CMStringA m_szAToken; - CMStringA m_szRToken; - CMStringA m_fetchBaseURL; - CMStringA m_aimsid; - CMStringA m_szMraCookie; - LONG m_msgId = 1; - int m_iRClientId; - HGENMENU m_hUploadGroups; - - mir_cs m_csOwnIds; - OBJLIST<IcqOwnMessage> m_arOwnIds; - - OBJLIST<IcqGroup> m_arGroups; - - int m_unreadEmails = -1; - CMStringA m_szMailBox; - - bool m_bIgnoreListEmpty = true; - bool m_bRememberPwd; // store password in a database - bool m_bDlgActive; - - //////////////////////////////////////////////////////////////////////////////////////// - // group chats - - int __cdecl GroupchatEventHook(WPARAM, LPARAM); - int __cdecl GroupchatMenuHook(WPARAM, LPARAM); - - void Chat_ProcessLogMenu(SESSION_INFO *si, int); - void Chat_SendPrivateMessage(GCHOOK *gch); - - void InviteUserToChat(SESSION_INFO *si); - void LeaveDestroyChat(SESSION_INFO *si); - void LoadChatInfo(SESSION_INFO *si); - - //////////////////////////////////////////////////////////////////////////////////////// - // http queue - - mir_cs m_csHttpQueue; - HANDLE m_evRequestsQueue; - LIST<AsyncHttpRequest> m_arHttpQueue; - - void CalcHash(AsyncHttpRequest*); - void DropQueue(); - bool ExecuteRequest(AsyncHttpRequest*); - bool IsQueueEmpty(); - void Push(MHttpRequest*); - bool RefreshRobustToken(AsyncHttpRequest *pReq); - - //////////////////////////////////////////////////////////////////////////////////////// - // cache - - mir_cs m_csCache; - OBJLIST<IcqCacheItem> m_arCache; - - void InitContactCache(void); - IcqCacheItem* FindContactByUIN(const CMStringW &pwszId); - MCONTACT CreateContact(const CMStringW &pwszId, bool bTemporary); - - void GetAvatarFileName(MCONTACT hContact, wchar_t *pszDest, size_t cbLen); - - //////////////////////////////////////////////////////////////////////////////////////// - // threads - - HANDLE m_hWorkerThread; - void __cdecl ServerThread(void*); - void __cdecl PollThread(void*); - - //////////////////////////////////////////////////////////////////////////////////////// - // services - - INT_PTR __cdecl GetAvatar(WPARAM, LPARAM); - INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM); - INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM); - INT_PTR __cdecl SetAvatar(WPARAM, LPARAM); - - INT_PTR __cdecl CreateAccMgrUI(WPARAM, LPARAM); - INT_PTR __cdecl EditGroups(WPARAM, LPARAM); - INT_PTR __cdecl EditProfile(WPARAM, LPARAM); - INT_PTR __cdecl GetEmailCount(WPARAM, LPARAM); - INT_PTR __cdecl GotoInbox(WPARAM, LPARAM); - INT_PTR __cdecl UploadGroups(WPARAM, LPARAM); - - INT_PTR __cdecl OnMenuLoadHistory(WPARAM, LPARAM); - - //////////////////////////////////////////////////////////////////////////////////////// - // events - - int __cdecl OnGroupChange(WPARAM, LPARAM); - int __cdecl OnDbEventRead(WPARAM, LPARAM); - int __cdecl OnOptionsInit(WPARAM, LPARAM); - int __cdecl OnUserInfoInit(WPARAM, LPARAM); - - //////////////////////////////////////////////////////////////////////////////////////// - // PROTO_INTERFACE - - MCONTACT AddToList( int flags, PROTOSEARCHRESULT *psr) override; - - int AuthRecv(MCONTACT, PROTORECVEVENT *pre) override; - int AuthRequest(MCONTACT hContact, const wchar_t *szMessage) override; - - INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override; - int GetInfo(MCONTACT hContact, int infoType) override; - - HANDLE SearchBasic(const wchar_t *id) override; - - HANDLE FileAllow(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szPath) override; - int FileCancel(MCONTACT hContact, HANDLE hTransfer) override; - int FileResume(HANDLE hTransfer, int action, const wchar_t *szFilename) override; - - HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override; - int SendMsg(MCONTACT hContact, int flags, const char *msg) override; - - int SetApparentMode(MCONTACT hContact, int mode) override; - int SetStatus(int iNewStatus) override; - - int UserIsTyping(MCONTACT hContact, int type) override; - - void OnBuildProtoMenu(void) override; - void OnContactAdded(MCONTACT) override; - void OnContactDeleted(MCONTACT) override; - void OnEventEdited(MCONTACT, MEVENT) override; - void OnModulesLoaded() override; - void OnShutdown() override; - -public: - CIcqProto(const char*, const wchar_t*); - ~CIcqProto(); - - CMOption<wchar_t*> m_szOwnId; // our own aim id - CMOption<uint8_t> m_bHideGroupchats; // don't pop up group chat windows on startup - CMOption<uint8_t> m_bUseTrayIcon; // use tray icon notifications - CMOption<uint8_t> m_bErrorPopups; // display popups with errors - CMOption<uint8_t> m_bLaunchMailbox; // launch browser to view email - CMOption<uint32_t> m_iTimeDiff1; // set this status to m_iStatus1 after this interval of secs - CMOption<uint32_t> m_iStatus1; - CMOption<uint32_t> m_iTimeDiff2; // set this status to m_iStatus2 after this interval of secs - CMOption<uint32_t> m_iStatus2; - - void CheckStatus(void); - CMStringW GetUserId(MCONTACT); - - __forceinline int TS() const - { return time(0) - m_iTimeShift; - } - - __forceinline const char *appId() const - { return (m_isMra) ? MRA_APP_ID : ICQ_APP_ID; - } - - void SetPermitDeny(const CMStringW &userId, bool bAllow); -}; - -struct CMPlugin : public ACCPROTOPLUGIN<CIcqProto> -{ - CMPlugin(); - - int Load() override; -}; - -#endif +// ---------------------------------------------------------------------------80
+// ICQ plugin for Miranda Instant Messenger
+// ________________________________________
+//
+// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede
+// Copyright © 2001-2002 Jon Keating, Richard Hughes
+// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater
+// Copyright © 2004-2010 Joe Kucera, George Hazan
+// Copyright © 2012-2023 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+// DESCRIPTION:
+//
+// Protocol Interface declarations
+// -----------------------------------------------------------------------------
+
+#ifndef _ICQ_PROTO_H_
+#define _ICQ_PROTO_H_
+
+#include "m_system.h"
+#include "m_protoint.h"
+
+#define MRA_APP_ID "ic1pzYNtEU6dDnEQ"
+#define ICQ_APP_ID "ic1nmMjqg7Yu-0hL"
+#define ICQ_API_SERVER "https://u.icq.net/wim"
+#define ICQ_FILE_SERVER "https://u.icq.net/files/api/v1.1"
+#define ICQ_FAKE_EVENT_ID 0xBABAEB
+#define ICQ_ROBUST_SERVER "https://u.icq.net/rapi"
+
+#define PS_DUMMY "/DoNothing"
+#define PS_GOTO_INBOX "/GotoInbox"
+
+#define WIM_CAP_VOIP_VOICE "094613504c7f11d18222444553540000"
+#define WIM_CAP_VOIP_VIDEO "094613514c7f11d18222444553540000"
+#define WIM_CAP_FILETRANSFER "094613434c7f11d18222444553540000"
+#define WIM_CAP_UNIQ_REQ_ID "094613534c7f11d18222444553540000"
+#define WIM_CAP_EMOJI "094613544c7f11d18222444553540000"
+#define WIM_CAP_MENTIONS "0946135b4c7f11d18222444553540000"
+#define WIM_CAP_MAIL_NOTIFICATIONS "094613594c7f11d18222444553540000"
+#define WIM_CAP_INTRO_DLG_STATE "0946135a4c7f11d18222444553540000"
+
+#define NG_CAP_SECUREIM "4d69724e47536563757265494d000000"
+
+typedef CProtoDlgBase<CIcqProto> CIcqDlgBase;
+
+struct AIMSID
+{
+ AIMSID(CIcqProto *_ppro) :
+ m_ppro(_ppro)
+ {}
+
+ CIcqProto *m_ppro;
+};
+
+enum ChatMenuItems
+{
+ IDM_INVITE = 10, IDM_LEAVE
+};
+
+struct IcqFileInfo
+{
+ IcqFileInfo(const std::string &pszUrl, const CMStringW &pwszDescr, uint32_t dwSize) :
+ szUrl(pszUrl.c_str()),
+ wszDescr(pwszDescr),
+ dwFileSize(dwSize)
+ {}
+
+ CMStringA szUrl;
+ CMStringW wszDescr;
+ uint32_t dwFileSize;
+ bool bIsSticker = false;
+};
+
+struct IcqGroup
+{
+ IcqGroup(int _p1, const CMStringW &_p2) :
+ id(_p1),
+ wszSrvName(_p2)
+ {
+ SetName(_p2);
+ }
+
+ int id;
+ int level;
+ CMStringW wszName, wszSrvName;
+
+ void SetName(const CMStringW &str)
+ {
+ wszName = str;
+ level = wszName.SpanIncluding(L">").GetLength();
+ if (level != 0)
+ wszName.Delete(0, level);
+ wszName.Replace(L">", L"\\");
+ }
+};
+
+struct IcqCacheItem : public MZeroedObject
+{
+ IcqCacheItem(const CMStringW &wszId, MCONTACT _contact) :
+ m_aimid(wszId),
+ m_hContact(_contact)
+ {}
+
+ CMStringW m_aimid;
+ MCONTACT m_hContact;
+ bool m_bInList;
+ __int64 m_iProcessedMsgId;
+ int m_iApparentMode;
+ time_t m_timer1, m_timer2;
+};
+
+struct IcqOwnMessage
+{
+ IcqOwnMessage(MCONTACT _hContact, int _msgid, const char *guid)
+ : m_hContact(_hContact), m_msgid(_msgid)
+ {
+ strncpy_s(m_guid, guid, _TRUNCATE);
+ }
+
+ MCONTACT m_hContact;
+ int m_msgid;
+ char m_guid[50];
+};
+
+struct IcqConn
+{
+ HNETLIBCONN s;
+ int lastTs, timeout;
+};
+
+struct IcqFileTransfer : public MZeroedObject
+{
+ // create an object for receiving
+ IcqFileTransfer(MCONTACT hContact, const char *pszUrl) :
+ m_szHost(pszUrl)
+ {
+ pfts.hContact = hContact;
+ pfts.totalFiles = 1;
+ pfts.flags = PFTS_UNICODE | PFTS_RECEIVING;
+
+ ptrW pwszFileName(mir_utf8decodeW(pszUrl));
+ if (pwszFileName == nullptr)
+ pwszFileName = mir_a2u(pszUrl);
+
+ const wchar_t *p = wcsrchr(pwszFileName, '/');
+ m_wszFileName = (p == nullptr) ? pwszFileName : p + 1;
+ m_wszShortName = m_wszFileName;
+ }
+
+ // create an object for sending
+ IcqFileTransfer(MCONTACT hContact, const wchar_t *pwszFileName) :
+ m_wszFileName(pwszFileName)
+ {
+ pfts.flags = PFTS_UNICODE | PFTS_SENDING;
+ pfts.hContact = hContact;
+ pfts.szCurrentFile.w = m_wszFileName.GetBuffer();
+
+ const wchar_t *p = wcsrchr(pfts.szCurrentFile.w, '\\');
+ if (pwszFileName != nullptr)
+ p++;
+ else
+ p = pfts.szCurrentFile.w;
+ m_wszShortName = p;
+ }
+
+ ~IcqFileTransfer()
+ {
+ if (m_fileId >= 0)
+ _close(m_fileId);
+ }
+
+ bool m_bCanceled = false, m_bStarted = false;
+ int m_fileId = -1;
+ CMStringA m_szHost;
+ CMStringW m_wszFileName, m_wszDescr;
+ const wchar_t *m_wszShortName;
+ PROTOFILETRANSFERSTATUS pfts;
+ HANDLE hWaitEvent;
+
+ void FillHeaders(AsyncHttpRequest *pReq)
+ {
+ pReq->AddHeader("Content-Type", "application/octet-stream");
+ pReq->AddHeader("Content-Disposition", CMStringA(FORMAT, "attachment; filename=\"%s\"", T2Utf(m_wszShortName).get()));
+
+ uint32_t dwPortion = pfts.currentFileSize - pfts.currentFileProgress;
+ if (dwPortion > 1000000)
+ dwPortion = 1000000;
+
+ pReq->AddHeader("Content-Range", CMStringA(FORMAT, "bytes %lld-%lld/%lld", pfts.currentFileProgress, pfts.currentFileProgress + dwPortion - 1, pfts.currentFileSize));
+ pReq->AddHeader("Content-Length", CMStringA(FORMAT, "%d", dwPortion));
+
+ pReq->dataLength = dwPortion;
+ pReq->pData = (char*)mir_alloc(dwPortion);
+ _lseek(m_fileId, pfts.currentFileProgress, SEEK_SET);
+ _read(m_fileId, pReq->pData, dwPortion);
+
+ pfts.currentFileProgress += dwPortion;
+ pfts.totalProgress += dwPortion;
+ }
+};
+
+class CIcqProto : public PROTO<CIcqProto>
+{
+ friend struct AsyncRapiRequest;
+
+ class CIcqProtoImpl
+ {
+ friend class CIcqProto;
+
+ CIcqProto &m_proto;
+ CTimer m_heartBeat, m_markRead;
+
+ void OnHeartBeat(CTimer *) {
+ m_proto.CheckStatus();
+ }
+
+ void OnMarkRead(CTimer *pTimer) {
+ m_proto.SendMarkRead();
+ pTimer->Stop();
+ }
+
+ CIcqProtoImpl(CIcqProto &pro) :
+ m_proto(pro),
+ m_markRead(Miranda_GetSystemWindow(), UINT_PTR(this)),
+ m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this) + 1)
+ {
+ m_markRead.OnEvent = Callback(this, &CIcqProtoImpl::OnMarkRead);
+ m_heartBeat.OnEvent = Callback(this, &CIcqProtoImpl::OnHeartBeat);
+ }
+ } m_impl;
+
+ friend struct CIcqRegistrationDlg;
+ friend class CGroupchatInviteDlg;
+ friend class CEditIgnoreListDlg;
+ friend class CIcqEnterLoginDlg;
+ friend class CIcqOptionsDlg;
+ friend class CGroupEditDlg;
+
+ friend AsyncHttpRequest* operator <<(AsyncHttpRequest*, const AIMSID&);
+
+ bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462;
+ int m_iTimeShift;
+
+ MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove);
+ void CheckPassword(void);
+ void ConnectionFailed(int iReason, int iErrorCode = 0);
+ void EmailNotification(const wchar_t *pwszText);
+ void GetPermitDeny();
+ wchar_t* GetUIN(MCONTACT hContact);
+ void MarkAsRead(MCONTACT hContact);
+ void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup);
+ bool RetrievePassword();
+ void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead);
+ void RetrieveUserInfo(MCONTACT hContact);
+ void SendMrimLogin(NETLIBHTTPREQUEST *pReply);
+ void SetServerStatus(int iNewStatus);
+ void ShutdownSession(void);
+ void StartSession(void);
+
+ void CheckAvatarChange(MCONTACT hContact, const JSONNode&);
+ IcqFileInfo* CheckFile(MCONTACT hContact, CMStringW &wszFileName, bool &bIsFile);
+ void CheckLastId(MCONTACT hContact, const JSONNode&);
+ void Json2int(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial);
+ void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial);
+ MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false);
+ void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime);
+ int StatusFromPresence(const JSONNode &presence, MCONTACT hContact);
+
+ void OnLoggedIn(void);
+ void OnLoggedOut(void);
+
+ mir_cs m_csMarkReadQueue;
+ LIST<IcqCacheItem> m_arMarkReadQueue;
+ void SendMarkRead();
+
+ __int64 getId(MCONTACT hContact, const char *szSetting);
+ void setId(MCONTACT hContact, const char *szSetting, __int64 iValue);
+
+ void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+
+ void ProcessBuddyList(const JSONNode &pRoot);
+ void ProcessDiff(const JSONNode &pRoot);
+ void ProcessEvent(const JSONNode &pRoot);
+ void ProcessGroupChat(const JSONNode &pRoot);
+ void ProcessHistData(const JSONNode &pRoot);
+ void ProcessImState(const JSONNode &pRoot);
+ void ProcessMyInfo(const JSONNode &pRoot);
+ void ProcessNotification(const JSONNode &pRoot);
+ void ProcessPermissions(const JSONNode &pRoot);
+ void ProcessPresence(const JSONNode &pRoot);
+ void ProcessSessionEnd(const JSONNode &pRoot);
+ void ProcessTyping(const JSONNode &pRoot);
+
+ IcqConn m_ConnPool[CONN_LAST];
+ CMStringA m_szPassword;
+ CMStringA m_szSessionKey;
+ CMStringA m_szAToken;
+ CMStringA m_szRToken;
+ CMStringA m_fetchBaseURL;
+ CMStringA m_aimsid;
+ CMStringA m_szMraCookie;
+ LONG m_msgId = 1;
+ int m_iRClientId;
+ HGENMENU m_hUploadGroups;
+
+ mir_cs m_csOwnIds;
+ OBJLIST<IcqOwnMessage> m_arOwnIds;
+
+ OBJLIST<IcqGroup> m_arGroups;
+
+ int m_unreadEmails = -1;
+ CMStringA m_szMailBox;
+
+ bool m_bIgnoreListEmpty = true;
+ bool m_bRememberPwd; // store password in a database
+ bool m_bDlgActive;
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // group chats
+
+ int __cdecl GroupchatEventHook(WPARAM, LPARAM);
+ int __cdecl GroupchatMenuHook(WPARAM, LPARAM);
+
+ void Chat_ProcessLogMenu(SESSION_INFO *si, int);
+ void Chat_SendPrivateMessage(GCHOOK *gch);
+
+ void InviteUserToChat(SESSION_INFO *si);
+ void LeaveDestroyChat(SESSION_INFO *si);
+ void LoadChatInfo(SESSION_INFO *si);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // http queue
+
+ mir_cs m_csHttpQueue;
+ HANDLE m_evRequestsQueue;
+ LIST<AsyncHttpRequest> m_arHttpQueue;
+
+ void CalcHash(AsyncHttpRequest*);
+ void DropQueue();
+ bool ExecuteRequest(AsyncHttpRequest*);
+ bool IsQueueEmpty();
+ void Push(MHttpRequest*);
+ bool RefreshRobustToken(AsyncHttpRequest *pReq);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // cache
+
+ mir_cs m_csCache;
+ OBJLIST<IcqCacheItem> m_arCache;
+
+ void InitContactCache(void);
+ IcqCacheItem* FindContactByUIN(const CMStringW &pwszId);
+ MCONTACT CreateContact(const CMStringW &pwszId, bool bTemporary);
+
+ void GetAvatarFileName(MCONTACT hContact, wchar_t *pszDest, size_t cbLen);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // threads
+
+ HANDLE m_hWorkerThread;
+ void __cdecl ServerThread(void*);
+ void __cdecl PollThread(void*);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // services
+
+ INT_PTR __cdecl GetAvatar(WPARAM, LPARAM);
+ INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM);
+ INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM);
+ INT_PTR __cdecl SetAvatar(WPARAM, LPARAM);
+
+ INT_PTR __cdecl CreateAccMgrUI(WPARAM, LPARAM);
+ INT_PTR __cdecl EditGroups(WPARAM, LPARAM);
+ INT_PTR __cdecl EditProfile(WPARAM, LPARAM);
+ INT_PTR __cdecl GetEmailCount(WPARAM, LPARAM);
+ INT_PTR __cdecl GotoInbox(WPARAM, LPARAM);
+ INT_PTR __cdecl UploadGroups(WPARAM, LPARAM);
+
+ INT_PTR __cdecl OnMenuLoadHistory(WPARAM, LPARAM);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // events
+
+ int __cdecl OnGroupChange(WPARAM, LPARAM);
+ int __cdecl OnDbEventRead(WPARAM, LPARAM);
+ int __cdecl OnOptionsInit(WPARAM, LPARAM);
+ int __cdecl OnUserInfoInit(WPARAM, LPARAM);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // PROTO_INTERFACE
+
+ MCONTACT AddToList( int flags, PROTOSEARCHRESULT *psr) override;
+
+ int AuthRecv(MCONTACT, PROTORECVEVENT *pre) override;
+ int AuthRequest(MCONTACT hContact, const wchar_t *szMessage) override;
+
+ INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override;
+ int GetInfo(MCONTACT hContact, int infoType) override;
+
+ HANDLE SearchBasic(const wchar_t *id) override;
+
+ HANDLE FileAllow(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szPath) override;
+ int FileCancel(MCONTACT hContact, HANDLE hTransfer) override;
+ int FileResume(HANDLE hTransfer, int action, const wchar_t *szFilename) override;
+
+ HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override;
+ int SendMsg(MCONTACT hContact, int flags, const char *msg) override;
+
+ int SetApparentMode(MCONTACT hContact, int mode) override;
+ int SetStatus(int iNewStatus) override;
+
+ int UserIsTyping(MCONTACT hContact, int type) override;
+
+ void OnBuildProtoMenu(void) override;
+ void OnContactAdded(MCONTACT) override;
+ void OnContactDeleted(MCONTACT) override;
+ void OnEventEdited(MCONTACT, MEVENT) override;
+ void OnModulesLoaded() override;
+ void OnShutdown() override;
+
+public:
+ CIcqProto(const char*, const wchar_t*);
+ ~CIcqProto();
+
+ CMOption<wchar_t*> m_szOwnId; // our own aim id
+ CMOption<uint8_t> m_bHideGroupchats; // don't pop up group chat windows on startup
+ CMOption<uint8_t> m_bUseTrayIcon; // use tray icon notifications
+ CMOption<uint8_t> m_bErrorPopups; // display popups with errors
+ CMOption<uint8_t> m_bLaunchMailbox; // launch browser to view email
+ CMOption<uint32_t> m_iTimeDiff1; // set this status to m_iStatus1 after this interval of secs
+ CMOption<uint32_t> m_iStatus1;
+ CMOption<uint32_t> m_iTimeDiff2; // set this status to m_iStatus2 after this interval of secs
+ CMOption<uint32_t> m_iStatus2;
+
+ void CheckStatus(void);
+ CMStringW GetUserId(MCONTACT);
+
+ __forceinline int TS() const
+ { return time(0) - m_iTimeShift;
+ }
+
+ __forceinline const char *appId() const
+ { return (m_isMra) ? MRA_APP_ID : ICQ_APP_ID;
+ }
+
+ void SetPermitDeny(const CMStringW &userId, bool bAllow);
+};
+
+struct CMPlugin : public ACCPROTOPLUGIN<CIcqProto>
+{
+ CMPlugin();
+
+ int Load() override;
+};
+
+#endif
diff --git a/protocols/ICQ-WIM/src/server.cpp b/protocols/ICQ-WIM/src/server.cpp index 167aabfee9..114ad5ed99 100644 --- a/protocols/ICQ-WIM/src/server.cpp +++ b/protocols/ICQ-WIM/src/server.cpp @@ -1,1220 +1,1220 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -#pragma comment(lib, "libcrypto.lib") - -void CIcqProto::CheckAvatarChange(MCONTACT hContact, const JSONNode &ev) -{ - CMStringW wszIconId(ev["bigIconId"].as_mstring()); - if (wszIconId.IsEmpty()) - wszIconId = ev["iconId"].as_mstring(); - - if (!wszIconId.IsEmpty()) { - CMStringW oldIconID(getMStringW(hContact, "IconId")); - if (wszIconId == oldIconID) { - wchar_t wszFullName[MAX_PATH]; - GetAvatarFileName(hContact, wszFullName, _countof(wszFullName)); - if (_waccess(wszFullName, 0) == 0) - return; - } - - setWString(hContact, "IconId", wszIconId); - - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/expressions/get", &CIcqProto::OnReceiveAvatar); - pReq << CHAR_PARAM("f", "native") << WCHAR_PARAM("t", GetUserId(hContact)) << CHAR_PARAM("type", "bigBuddyIcon"); - pReq->hContact = hContact; - Push(pReq); - } - else delSetting(hContact, "IconId"); -} - -void CIcqProto::CheckLastId(MCONTACT hContact, const JSONNode &ev) -{ - __int64 msgId = _wtoi64(ev["histMsgId"].as_mstring()); - __int64 lastId = getId(hContact, DB_KEY_LASTMSGID); - if (msgId > lastId) - setId(hContact, DB_KEY_LASTMSGID, msgId); -} - -MCONTACT CIcqProto::CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove) -{ - IcqOwnMessage *pOwn = nullptr; - { - mir_cslock lck(m_csOwnIds); - for (auto &it : m_arOwnIds) { - if (reqId == it->m_guid) { - pOwn = it; - break; - } - } - } - - if (pOwn == nullptr) - return 0; - - ProtoBroadcastAck(pOwn->m_hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)pOwn->m_msgid, (LPARAM)msgId.c_str()); - - MCONTACT ret = pOwn->m_hContact; - if (bRemove) { - mir_cslock lck(m_csOwnIds); - m_arOwnIds.remove(pOwn); - } - return ret; -} - -void CIcqProto::CheckPassword() -{ - char mirVer[100]; - Miranda_GetVersionText(mirVer, _countof(mirVer)); - - m_szAToken = getMStringA(DB_KEY_ATOKEN); - m_iRClientId = getDword(DB_KEY_RCLIENTID); - m_szSessionKey = getMStringA(DB_KEY_SESSIONKEY); - if (!m_szAToken.IsEmpty() && !m_szSessionKey.IsEmpty()) { - StartSession(); - return; - } - - if (m_isMra) { - m_bError462 = false; - SendMrimLogin(nullptr); - } - else { - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "https://api.login.icq.net/auth/clientLogin", &CIcqProto::OnCheckPassword); - pReq << CHAR_PARAM("clientName", "Miranda NG") << CHAR_PARAM("clientVersion", mirVer) << CHAR_PARAM("devId", appId()) - << CHAR_PARAM("f", "json") << CHAR_PARAM("tokenType", "longTerm") << WCHAR_PARAM("s", m_szOwnId) << CHAR_PARAM("pwd", m_szPassword); - #ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; - #endif - Push(pReq); - } -} - -IcqFileInfo* CIcqProto::CheckFile(MCONTACT hContact, CMStringW &wszText, bool &bIsFile) -{ - CMStringW wszUrl(wszText.Mid(26)); - int idx = wszUrl.Find(' '); - if (idx != -1) - wszUrl.Truncate(idx); - - bIsFile = false; - IcqFileInfo *pFileInfo = nullptr; - - // is it already downloaded sticker? - CMStringW wszLoadedPath(FORMAT, L"%s\\%S\\Stickers\\STK{%s}.png", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName, wszUrl.c_str()); - if (!_waccess(wszLoadedPath, 0)) { - pFileInfo = (IcqFileInfo *)this; - wszText.Format(L"STK{%s}", wszUrl.c_str()); - } - else { - // download file info - CMStringA szUrl(FORMAT, ICQ_FILE_SERVER "/info/%S/", wszUrl.c_str()); - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnFileInfo); - pReq->hContact = hContact; - pReq->pUserInfo = &pFileInfo; - pReq << CHAR_PARAM("aimsid", m_aimsid) << CHAR_PARAM("previews", "192,600,xlarge"); - if (!ExecuteRequest(pReq)) - return nullptr; - - // is it a sticker? - if (pFileInfo->bIsSticker) { - if (ServiceExists(MS_SMILEYADD_LOADCONTACTSMILEYS)) { - auto *pNew = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, pFileInfo->szUrl, &CIcqProto::OnGetSticker); - pNew->flags |= NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; - pNew->pUserInfo = wszUrl.GetBuffer(); - pNew->AddHeader("Sec-Fetch-User", "?1"); - pNew->AddHeader("Sec-Fetch-Site", "cross-site"); - pNew->AddHeader("Sec-Fetch-Mode", "navigate"); - if (!ExecuteRequest(pNew)) - return nullptr; - - wszText.Format(L"STK{%s}", wszUrl.c_str()); - delete pFileInfo; - } - else wszText = TranslateT("SmileyAdd plugin required to support stickers"); - } - else bIsFile = true; - } - - return pFileInfo; -} - -void CIcqProto::CheckStatus() -{ - time_t now = time(0); - int diff1 = m_iTimeDiff1, diff2 = m_iTimeDiff2; - - for (auto &it : m_arCache) { - // this contact is really offline and is on the first timer - // if the first timer is expired, we clear it and look for the second status - if (diff1 && it->m_timer1 && now - it->m_timer1 > diff1) { - it->m_timer1 = 0; - - // if the second timer is set up, activate it - if (m_iTimeDiff2) { - setWord(it->m_hContact, "Status", m_iStatus2); - it->m_timer2 = now; - } - // if the second timer is not set, simply mark a contact as offline - else setWord(it->m_hContact, "Status", ID_STATUS_OFFLINE); - continue; - } - - // if the second timer is expired, set status to offline - if (diff2 && it->m_timer2 && now - it->m_timer2 > m_iTimeDiff2) { - it->m_timer2 = 0; - setWord(it->m_hContact, "Status", ID_STATUS_OFFLINE); - } - } -} - -void CIcqProto::ConnectionFailed(int iReason, int iErrorCode) -{ - debugLogA("ConnectionFailed -> reason %d", iReason); - - if (m_bErrorPopups) { - POPUPDATAW Popup = {}; - Popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR), true); - wcscpy_s(Popup.lpwzContactName, m_tszUserName); - switch (iReason) { - case LOGINERR_BADUSERID: - mir_snwprintf(Popup.lpwzText, TranslateT("You have not entered a login or password.\nConfigure this in Options -> Network -> ICQ and try again.")); - break; - case LOGINERR_WRONGPASSWORD: - mir_snwprintf(Popup.lpwzText, TranslateT("Connection failed.\nYour login or password was rejected (%d)."), iErrorCode); - break; - case LOGINERR_NONETWORK: - case LOGINERR_NOSERVER: - mir_snwprintf(Popup.lpwzText, TranslateT("Connection failed.\nThe server is temporarily unavailable (%d)."), iErrorCode); - break; - default: - mir_snwprintf(Popup.lpwzText, TranslateT("Connection failed.\nUnknown error during sign on: %d"), iErrorCode); - break; - } - PUAddPopupW(&Popup); - } - - ProtoBroadcastAck(0, ACKTYPE_LOGIN, ACKRESULT_FAILED, nullptr, iReason); - ShutdownSession(); -} - -void CIcqProto::MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup) -{ - // otherwise we'll get a server error - if (!mir_wstrlen(pwszGroup)) - return; - - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/moveBuddy") << AIMSID(this) << WCHAR_PARAM("buddy", GetUserId(hContact)) - << GROUP_PARAM("group", pwszGroup); - if (mir_wstrlen(pwszNewGroup)) - pReq << GROUP_PARAM("newGroup", pwszNewGroup); - Push(pReq); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::OnLoggedIn() -{ - debugLogA("CIcqProto::OnLoggedIn"); - m_bOnline = true; - m_impl.m_heartBeat.Start(1000); - - for (auto &it : m_arCache) - it->m_timer1 = it->m_timer2 = 0; - - SetServerStatus(m_iDesiredStatus); - RetrieveUserInfo(0); - GetPermitDeny(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::OnLoggedOut() -{ - debugLogA("CIcqProto::OnLoggedOut"); - m_bOnline = false; - m_impl.m_heartBeat.Stop(); - - for (auto &it : m_arCache) - it->m_timer1 = it->m_timer2 = 0; - - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE); - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - - setAllContactStatuses(ID_STATUS_OFFLINE, false); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::MarkAsRead(MCONTACT hContact) -{ - if (!m_bOnline) - return; - - m_impl.m_markRead.Start(200); - - auto *pCache = FindContactByUIN(GetUserId(hContact)); - if (pCache) { - mir_cslock lck(m_csMarkReadQueue); - if (m_arMarkReadQueue.indexOf(pCache) == -1) - m_arMarkReadQueue.insert(pCache); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MCONTACT CIcqProto::ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact, bool bIsPartial) -{ - // user chat? - CMStringW wszId(buddy["aimId"].as_mstring()); - if (IsChat(wszId)) { - CMStringW wszChatId(buddy["aimId"].as_mstring()); - CMStringW wszChatName(buddy["friendly"].as_mstring()); - - auto *pContact = FindContactByUIN(wszId); - if (pContact && pContact->m_iApparentMode == ID_STATUS_OFFLINE) - return INVALID_CONTACT_ID; - - auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszChatId, wszChatName); - if (si == nullptr) - return INVALID_CONTACT_ID; - - Chat_AddGroup(si, TranslateT("admin")); - Chat_AddGroup(si, TranslateT("member")); - Chat_Control(m_szModuleName, wszChatId, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE); - Chat_Control(m_szModuleName, wszChatId, SESSION_ONLINE); - return si->hContact; - } - - bool bIgnored = !IsValidType(buddy); - if (hContact == INVALID_CONTACT_ID) { - if (bIgnored) - return INVALID_CONTACT_ID; - - hContact = CreateContact(wszId, false); - FindContactByUIN(wszId)->m_bInList = true; - } - else if (bIgnored) { - db_delete_contact(hContact); - return INVALID_CONTACT_ID; - } - - CMStringA szVer; - bool bVersionDetected = false, bSecureIM = false; - - for (auto &it : buddy["capabilities"]) { - CMStringW wszCap(it.as_mstring()); - if (wszCap.GetLength() != 32) - continue; - - uint8_t cap[16]; - hex2binW(wszCap, cap, sizeof(cap)); - if (!memcmp(cap, "MiNG", 4)) { // Miranda - int v[4]; - if (4 == swscanf(wszCap.c_str() + 16, L"%04x%04x%04x%04x", &v[0], &v[1], &v[2], &v[3])) { - szVer.Format("Miranda NG %d.%d.%d.%d (ICQ %d.%d.%d.%d)", v[0], v[1], v[2], v[3], cap[4], cap[5], cap[6], cap[7]); - setString(hContact, "MirVer", szVer); - bVersionDetected = true; - } - } - else if (wszCap == _A2W(NG_CAP_SECUREIM)) { - bSecureIM = bVersionDetected = true; - } - else if (!memcmp(cap, "Mod by Mikanoshi", 16)) { - szVer = "R&Q build by Mikanoshi"; - bVersionDetected = true; - } - else if (!memcmp(cap, "Mandarin IM", 11)) { - szVer = "Mandarin IM"; - bVersionDetected = true; - } - } - - if (bVersionDetected) { - if (bSecureIM) - szVer.Append(" + SecureIM"); - setString(hContact, "MirVer", szVer); - } - else delSetting(hContact, "MirVer"); - - const JSONNode &var = buddy["friendly"]; - if (var) - setWString(hContact, "Nick", var.as_mstring()); - - if (buddy["deleted"].as_bool()) { - setByte(hContact, "IcqDeleted", 1); - Contact::PutOnList(hContact); - } - - Json2string(hContact, buddy, "emailId", "Email", bIsPartial); - Json2string(hContact, buddy, "cellNumber", "Cellular", bIsPartial); - Json2string(hContact, buddy, "workNumber", "CompanyPhone", bIsPartial); - - // we shall not remove existing phone number anyhow - Json2string(hContact, buddy, "phoneNumber", DB_KEY_PHONE, true); - - Json2int(hContact, buddy, "official", "Official", bIsPartial); - Json2int(hContact, buddy, "onlineTime", DB_KEY_ONLINETS, bIsPartial); - Json2int(hContact, buddy, "idleTime", "IdleTS", bIsPartial); - Json2int(hContact, buddy, "memberSince", DB_KEY_MEMBERSINCE, bIsPartial); - - int iStatus = StatusFromPresence(buddy, hContact); - if (iStatus > 0) - setWord(hContact, "Status", iStatus); - - const JSONNode &profile = buddy["profile"]; - if (profile) { - Json2string(hContact, profile, "friendlyName", DB_KEY_ICQNICK, bIsPartial); - Json2string(hContact, profile, "firstName", "FirstName", bIsPartial); - Json2string(hContact, profile, "lastName", "LastName", bIsPartial); - Json2string(hContact, profile, "aboutMe", DB_KEY_ABOUT, bIsPartial); - - ptrW wszNick(getWStringA(hContact, "Nick")); - if (!wszNick) { - CMStringW srvNick = profile["friendlyName"].as_mstring(); - if (!srvNick.IsEmpty()) - setWString(hContact, "Nick", srvNick); - } - - time_t birthDate = profile["birthDate"].as_int(); - if (birthDate != 0) { - struct tm *timeinfo = localtime(&birthDate); - if (timeinfo != nullptr) { - setWord(hContact, "BirthDay", timeinfo->tm_mday); - setWord(hContact, "BirthMonth", timeinfo->tm_mon+1); - setWord(hContact, "BirthYear", timeinfo->tm_year+1900); - } - } - - CMStringW str = profile["gender"].as_mstring(); - if (!str.IsEmpty()) { - if (str == "male") - setByte(hContact, "Gender", 'M'); - else if (str == "female") - setByte(hContact, "Gender", 'F'); - } - - for (auto &it : profile["homeAddress"]) { - Json2string(hContact, it, "city", "City", bIsPartial); - Json2string(hContact, it, "state", "State", bIsPartial); - Json2string(hContact, it, "country", "Country", bIsPartial); - } - } - - CMStringW str = buddy["statusMsg"].as_mstring(); - if (str.IsEmpty()) - db_unset(hContact, "CList", "StatusMsg"); - else - db_set_ws(hContact, "CList", "StatusMsg", str); - - CheckAvatarChange(hContact, buddy); - return hContact; -} - -void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &it, bool bCreateRead, bool bLocalTime) -{ - CMStringA szMsgId(it["msgId"].as_mstring()); - __int64 msgId = _atoi64(szMsgId); - if (msgId > lastMsgId) - lastMsgId = msgId; - - CMStringW wszText; - const JSONNode &sticker = it["sticker"]; - if (sticker) { - CMStringW wszUrl, wszSticker(sticker["id"].as_mstring()); - int iCollectionId, iStickerId; - if (2 == swscanf(wszSticker, L"ext:%d:sticker:%d", &iCollectionId, &iStickerId)) - wszUrl.Format(L"https://c.icq.com/store/stickers/%d/%d/medium", iCollectionId, iStickerId); - else - wszUrl = TranslateT("Unknown sticker"); - wszText.Format(L"%s\n%s", TranslateT("User sent a sticker:"), wszUrl.c_str()); - } - else { - wszText = it["text"].as_mstring(); - wszText.TrimRight(); - - // user added you - if (it["class"].as_mstring() == L"event" && it["eventTypeId"].as_mstring() == L"27:33000") { - if (bLocalTime) { - CMStringA id = getMStringA(hContact, DB_KEY_ID); - int pos = id.Find('@'); - CMStringA nick = (pos == -1) ? id : id.Left(pos); - DB::AUTH_BLOB blob(hContact, nick, nullptr, nullptr, id, nullptr); - - PROTORECVEVENT pre = {}; - pre.timestamp = (uint32_t)time(0); - pre.lParam = blob.size(); - pre.szMessage = blob; - ProtoChainRecv(hContact, PSR_AUTH, 0, (LPARAM)&pre); - } - return; - } - } - - int iMsgTime = (bLocalTime) ? time(0) : it["time"].as_int(); - bool bIsOutgoing = it["outgoing"].as_bool(), bIsFileTransfer = false; - IcqFileInfo *pFileInfo = nullptr; - - if (!bCreateRead && !bIsOutgoing && wszText.Left(26) == L"https://files.icq.net/get/") { - pFileInfo = CheckFile(hContact, wszText, bIsFileTransfer); - if (!pFileInfo) - return; - - for (auto &jt : it["parts"]) { - CMStringW wszDescr(jt["captionedContent"]["caption"].as_mstring()); - if (!wszDescr.IsEmpty()) - pFileInfo->wszDescr = wszDescr; - } - } - - if (isChatRoom(hContact)) { - CMStringA reqId(it["reqId"].as_mstring()); - CheckOwnMessage(reqId, szMsgId, true); - - CMStringW wszSender(it["chat"]["sender"].as_mstring()); - CMStringW wszChatId(getMStringW(hContact, "ChatRoomID")); - - if (bIsFileTransfer) { - wszText = pFileInfo->szUrl; - if (!pFileInfo->wszDescr) - wszText.AppendFormat(L"\r\n%s", pFileInfo->wszDescr.c_str()); - delete pFileInfo; - } - - GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE }; - gce.pszID.w = wszChatId; - gce.dwFlags = GCEF_ADDTOLOG; - gce.pszUID.w = wszSender; - gce.pszText.w = wszText; - gce.time = iMsgTime; - gce.bIsMe = wszSender == m_szOwnId; - Chat_Event(&gce); - return; - } - - // skip own messages, just set the server msgid - CMStringA reqId(it["reqId"].as_mstring()); - if (CheckOwnMessage(reqId, szMsgId, true)) { - debugLogA("Skipping our own message %s", szMsgId.c_str()); - return; - } - - // ignore duplicates - MEVENT hDbEvent = db_event_getById(m_szModuleName, szMsgId); - if (hDbEvent != 0) { - debugLogA("Message %s already exists", szMsgId.c_str()); - return; - } - - // convert a file info into Miranda's file transfer - if (bIsFileTransfer) { - auto *ft = new IcqFileTransfer(hContact, pFileInfo->szUrl); - ft->pfts.totalBytes = ft->pfts.currentFileSize = pFileInfo->dwFileSize; - ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); - - PROTORECVFILE pre = {}; - pre.dwFlags = PRFF_UNICODE; - pre.fileCount = 1; - pre.timestamp = iMsgTime; - pre.files.w = &ft->m_wszShortName; - pre.descr.w = pFileInfo->wszDescr; - pre.lParam = (LPARAM)ft; - ProtoChainRecvFile(hContact, &pre); - - delete pFileInfo; - return; - } - - // suppress notifications for already loaded/processed messages - __int64 storedLastId = getId(hContact, DB_KEY_LASTMSGID); - if (msgId <= storedLastId) { - debugLogA("Parsing old/processed message with id %lld < %lld, setting CR to true", msgId, storedLastId); - bCreateRead = true; - } - - debugLogA("Adding message %d:%lld (CR=%d)", hContact, msgId, bCreateRead); - - ptrA szUtf(mir_utf8encodeW(wszText)); - - PROTORECVEVENT pre = {}; - if (bIsOutgoing) pre.flags |= PREF_SENT; - if (bCreateRead) pre.flags |= PREF_CREATEREAD; - pre.szMsgId = szMsgId; - pre.timestamp = iMsgTime; - pre.szMessage = szUtf; - ProtoChainRecvMsg(hContact, &pre); -} - -bool CIcqProto::RefreshRobustToken(AsyncHttpRequest *pOrigReq) -{ - if (!m_szRToken.IsEmpty()) - return true; - - auto *pReq = new AsyncHttpRequest(CONN_RAPI, REQUEST_POST, ICQ_ROBUST_SERVER "/genToken", &CIcqProto::OnGenToken); - #ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; - #endif - - int ts = TS(); - pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("nonce", CMStringA(FORMAT, "%d-%d", ts, rand() % 10)) << INT_PARAM("ts", ts); - CalcHash(pReq); - - CMStringA szAgent(FORMAT, "%S Mail.ru Windows ICQ (version 10.0.1999)", (wchar_t*)m_szOwnId); - pReq->AddHeader("User-Agent", szAgent); - if (!ExecuteRequest(pReq)) { -LBL_Error: - (this->*(pOrigReq->m_pFunc))(nullptr, pOrigReq); - return false; - } - if (m_szRToken.IsEmpty()) - goto LBL_Error; - - // now add this token - bool bRet = false; - pReq = new AsyncHttpRequest(CONN_RAPI, REQUEST_POST, ICQ_ROBUST_SERVER "/addClient", &CIcqProto::OnAddClient); - #ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; - #endif - pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("f", "json") << CHAR_PARAM("k", appId()) << INT_PARAM("ts", ts) - << CHAR_PARAM("client", "icq") << CHAR_PARAM("reqId", pReq->m_reqId) << CHAR_PARAM("authToken", m_szRToken); - pReq->pUserInfo = &bRet; - if (!ExecuteRequest(pReq)) - goto LBL_Error; - - return bRet; -} - -void CIcqProto::RetrieveUserInfo(MCONTACT hContact) -{ - auto *pReq = new AsyncRapiRequest(this, "getUserInfo", &CIcqProto::OnGetUserInfo); - pReq->params << WCHAR_PARAM("sn", GetUserId(hContact)); - pReq->hContact = hContact; - Push(pReq); -} - -void CIcqProto::RetrieveUserHistory(MCONTACT hContact, __int64 startMsgId, bool bCreateRead) -{ - if (startMsgId == 0) - startMsgId = -1; - - __int64 patchVer = getId(hContact, DB_KEY_PATCHVER); - if (patchVer == 0) - patchVer = 1; - - auto *pReq = new AsyncRapiRequest(this, "getHistory", &CIcqProto::OnGetUserHistory); - #ifndef _DEBUG - pReq->flags |= NLHRF_NODUMPSEND; - #endif - pReq->hContact = hContact; - pReq->pUserInfo = (bCreateRead) ? pReq : 0; - pReq->params << WCHAR_PARAM("sn", GetUserId(hContact)) << INT64_PARAM("fromMsgId", startMsgId) << INT_PARAM("count", 1000) - << SINT64_PARAM("patchVersion", patchVer) << CHAR_PARAM("language", "ru-ru"); - Push(pReq); -} - -void CIcqProto::SetServerStatus(int iStatus) -{ - const char *szStatus = "online"; - int invisible = 0; - - switch (iStatus) { - case ID_STATUS_OFFLINE: szStatus = "offline"; break; - case ID_STATUS_NA: szStatus = "occupied"; break; - case ID_STATUS_AWAY: - case ID_STATUS_DND: szStatus = "away"; break; - case ID_STATUS_INVISIBLE: - invisible = 1; - } - - Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/presence/setState") - << AIMSID(this) << CHAR_PARAM("view", szStatus) << INT_PARAM("invisible", invisible)); - - if (iStatus == ID_STATUS_OFFLINE && !getByte(DB_KEY_PHONEREG)) { - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, ICQ_API_SERVER "/aim/endSession", &CIcqProto::OnSessionEnd); - pReq << AIMSID(this) << INT_PARAM("invalidateToken", 1); - ExecuteRequest(pReq); - } - - int iOldStatus = m_iStatus; m_iStatus = iStatus; - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus); -} - -void CIcqProto::ShutdownSession() -{ - if (m_bTerminated) - return; - - debugLogA("CIcqProto::ShutdownSession"); - - // shutdown all resources - DropQueue(); - - if (m_hWorkerThread) - SetEvent(m_evRequestsQueue); - - OnLoggedOut(); - - for (auto &it : m_ConnPool) { - if (it.s) { - Netlib_Shutdown(it.s); - it.s = nullptr; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define EVENTS "myInfo,presence,buddylist,typing,dataIM,userAddedToBuddyList,mchat,hist,hiddenChat,diff,permitDeny,imState,notification,apps" -#define FIELDS "aimId,buddyIcon,bigBuddyIcon,iconId,bigIconId,largeIconId,displayId,friendly,offlineMsg,state,statusMsg,userType,phoneNumber,cellNumber,smsNumber,workNumber,otherNumber,capabilities,ssl,abPhoneNumber,moodIcon,lastName,abPhones,abContactName,lastseen,mute,livechat,official" - -void CIcqProto::StartSession() -{ - ptrA szDeviceId(getStringA("DeviceId")); - if (szDeviceId == nullptr) { - UUID deviceId; - UuidCreate(&deviceId); - RPC_CSTR szId; - UuidToStringA(&deviceId, &szId); - szDeviceId = mir_strdup((char*)szId); - setString("DeviceId", szDeviceId); - RpcStringFreeA(&szId); - } - - int ts = TS(); - CMStringA nonce(FORMAT, "%d-2", ts); - CMStringA caps(WIM_CAP_UNIQ_REQ_ID "," WIM_CAP_EMOJI "," WIM_CAP_MAIL_NOTIFICATIONS "," WIM_CAP_INTRO_DLG_STATE); - if (g_bSecureIM) { - caps.AppendChar(','); - caps.Append(NG_CAP_SECUREIM); - } - - MFileVersion v; - Miranda_GetFileVersion(&v); - caps.AppendFormat(",%02x%02x%02x%02x%02x%02x%02x%02x%04x%04x%04x%04x", 'M', 'i', 'N', 'G', - __MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM, v[0], v[1], v[2], v[3]); - - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/aim/startSession", &CIcqProto::OnStartSession); - pReq << CHAR_PARAM("a", m_szAToken) << INT_PARAM("activeTimeout", 180) << CHAR_PARAM("assertCaps", caps) - << INT_PARAM("buildNumber", __BUILD_NUM) << CHAR_PARAM("deviceId", szDeviceId) << CHAR_PARAM("events", EVENTS) - << CHAR_PARAM("f", "json") << CHAR_PARAM("imf", "plain") << CHAR_PARAM("inactiveView", "offline") - << CHAR_PARAM("includePresenceFields", FIELDS) << CHAR_PARAM("invisible", "false") - << CHAR_PARAM("k", appId()) << INT_PARAM("mobile", 0) << CHAR_PARAM("nonce", nonce) << CHAR_PARAM("r", pReq->m_reqId) - << INT_PARAM("rawMsg", 0) << INT_PARAM("sessionTimeout", 7776000) << INT_PARAM("ts", ts) << CHAR_PARAM("view", "online"); - - CalcHash(pReq); - Push(pReq); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - JsonReply root(pReply); - if (root.error() != 200) - return; - - CMStringW wszId = getMStringW(pReq->hContact, DB_KEY_ID); - for (auto &it : root.data()["results"]) { - if (it["buddy"].as_mstring() != wszId) - continue; - - switch (int iResultCode = it["resultCode"].as_int()) { - case 0: // success - case 3: // already in contact list - break; - - default: - debugLogA("Contact %d failed to add: error %d", pReq->hContact, iResultCode); - - POPUPDATAW Popup = {}; - Popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR)); - wcsncpy_s(Popup.lpwzText, TranslateT("Buddy addition failed"), _TRUNCATE); - wcsncpy_s(Popup.lpwzContactName, Clist_GetContactDisplayName(pReq->hContact), _TRUNCATE); - Popup.iSeconds = 20; - PUAddPopupW(&Popup); - - // Contact::RemoveFromList(pReq->hContact); - } - - RetrieveUserInfo(pReq->hContact); - Contact::PutOnList(pReq->hContact); - } -} - -void CIcqProto::OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - bool *pRet = (bool*)pReq->pUserInfo; - - RobustReply reply(pReply); - if (reply.error() != 20000) { - *pRet = false; - return; - } - - const JSONNode &results = reply.results(); - m_iRClientId = results["clientId"].as_int(); - setDword(DB_KEY_RCLIENTID, m_iRClientId); - *pRet = true; -} - -void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) -{ - JsonReply root(pReply); - switch (root.error()) { - case 200: - break; - - case 330: - case 440: - ConnectionFailed(LOGINERR_WRONGPASSWORD, root.error()); - return; - - default: - ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error()); - return; - } - - JSONNode &data = root.data(); - m_szAToken = data["token"]["a"].as_mstring(); - mir_urlDecode(m_szAToken.GetBuffer()); - setString(DB_KEY_ATOKEN, m_szAToken); - - CMStringA szSessionSecret = data["sessionSecret"].as_mstring(); - CMStringA szPassTemp = m_szPassword; - - unsigned int len; - uint8_t hashOut[MIR_SHA256_HASH_SIZE]; - HMAC(EVP_sha256(), szPassTemp, szPassTemp.GetLength(), (uint8_t*)szSessionSecret.c_str(), szSessionSecret.GetLength(), hashOut, &len); - m_szSessionKey = ptrA(mir_base64_encode(hashOut, sizeof(hashOut))); - setString(DB_KEY_SESSIONKEY, m_szSessionKey); - - CMStringW szUin = data["loginId"].as_mstring(); - if (szUin) - m_szOwnId = szUin; - - int srvTS = data["hostTime"].as_int(); - m_iTimeShift = (srvTS) ? time(0) - srvTS : 0; - - StartSession(); -} - -void CIcqProto::OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pOld) -{ - IcqFileTransfer *pTransfer = (IcqFileTransfer*)pOld->pUserInfo; - if (pTransfer->m_bCanceled) { -LBL_Error: - ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, pTransfer); - delete pTransfer; - return; - } - - switch (pReply->resultCode) { - case 200: // final ok - case 206: // partial ok - break; - - default: - goto LBL_Error; - } - - // file transfer succeeded? - if (pTransfer->pfts.currentFileProgress == pTransfer->pfts.currentFileSize) { - FileReply root(pReply); - if (root.error() != 200) - goto LBL_Error; - - ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, pTransfer); - - const JSONNode &data = root.data(); - CMStringW wszUrl(data["static_url"].as_mstring()); - - JSONNode bundle, contents; contents.set_name("captionedContent"); - contents << WCHAR_PARAM("caption", pTransfer->m_wszDescr) << WCHAR_PARAM("url", wszUrl); - bundle << CHAR_PARAM("mediaType", "text") << CHAR_PARAM("text", "") << contents; - CMStringW wszParts(FORMAT, L"[%s]", ptrW(json_write(&bundle)).get()); - - if (!pTransfer->m_wszDescr.IsEmpty()) - wszUrl += L" " + pTransfer->m_wszDescr; - - int id = InterlockedIncrement(&m_msgId); - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/im/sendIM", &CIcqProto::OnSendMessage); - - auto *pOwn = new IcqOwnMessage(pTransfer->pfts.hContact, id, pReq->m_reqId); - pReq->pUserInfo = pOwn; - { - mir_cslock lck(m_csOwnIds); - m_arOwnIds.insert(pOwn); - } - - pReq << AIMSID(this) << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("mentions", "") << WCHAR_PARAM("message", wszUrl) - << CHAR_PARAM("offlineIM", "true") << WCHAR_PARAM("parts", wszParts) << WCHAR_PARAM("t", GetUserId(pTransfer->pfts.hContact)) << INT_PARAM("ts", TS()); - Push(pReq); - - delete pTransfer; - return; - } - - // else send the next portion - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, pTransfer->m_szHost, &CIcqProto::OnFileContinue); - pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("client", "icq") << CHAR_PARAM("k", appId()) << INT_PARAM("ts", TS()); - CalcHash(pReq); - pReq->m_szUrl.AppendChar('?'); - pReq->m_szUrl += pReq->m_szParam; pReq->m_szParam.Empty(); - pReq->pUserInfo = pTransfer; - pTransfer->FillHeaders(pReq); - Push(pReq); - - pTransfer->pfts.currentFileTime = time(0); - ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, pTransfer, (LPARAM)&pTransfer->pfts); -} - -void CIcqProto::OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pOld) -{ - IcqFileTransfer *pTransfer = (IcqFileTransfer*)pOld->pUserInfo; - if (pTransfer->m_bCanceled) { -LBL_Error: - ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, pTransfer); - delete pTransfer; - return; - } - - FileReply root(pReply); - if (root.error() != 200) - goto LBL_Error; - - ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, pTransfer); - pTransfer->pfts.currentFileTime = time(0); - - const JSONNode &data = root.data(); - CMStringW wszHost(data["host"].as_mstring()); - CMStringW wszUrl(data["url"].as_mstring()); - pTransfer->m_szHost = L"https://" + wszHost + wszUrl; - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, pTransfer->m_szHost, &CIcqProto::OnFileContinue); - pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("client", "icq") << CHAR_PARAM("k", appId()) << INT_PARAM("ts", TS()); - CalcHash(pReq); - pReq->m_szUrl.AppendChar('?'); - pReq->m_szUrl += pReq->m_szParam; pReq->m_szParam.Empty(); - pReq->pUserInfo = pTransfer; - pTransfer->FillHeaders(pReq); - Push(pReq); - - ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, pTransfer, (LPARAM)&pTransfer->pfts); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Support for stickers - -void CIcqProto::OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - if (pReply->resultCode != 200) { - debugLogA("Error getting sticker: %d", pReply->resultCode); - return; - } - - CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName); - CreateDirectoryTreeW(wszPath); - - CMStringW wszFileName(FORMAT, L"%s\\STK{%s}.png", wszPath.c_str(), pReq->pUserInfo); - FILE *out = _wfopen(wszFileName, L"wb"); - fwrite(pReply->pData, 1, pReply->dataLength, out); - fclose(out); - - SMADD_CONT cont = { 1, m_szModuleName, wszFileName }; - CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont)); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// File info request - -void CIcqProto::OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - IcqFileInfo **res = (IcqFileInfo **)pReq->pUserInfo; - *res = nullptr; - - RobustReply root(pReply); - if (root.error() != 200) - return; - - auto &pData = root.result(); - auto &pInfo = pData["info"] ; - std::string szUrl(pInfo["dlink"].as_string()); - if (szUrl.empty()) - return; - - MarkAsRead(pReq->hContact); - - bool bIsSticker; - CMStringW wszDescr(pInfo["file_name"].as_mstring()); - if (!mir_wstrncmp(wszDescr, L"dnld", 4)) { - bIsSticker = true; - - std::string szPreview = pData["previews"]["192"].as_string(); - if (!szPreview.empty()) - szUrl = szPreview; - } - else bIsSticker = false; - - mir_urlDecode(&*szUrl.begin()); - - *res = new IcqFileInfo(szUrl, wszDescr, pInfo["file_size"].as_int()); - res[0]->bIsSticker = bIsSticker; -} - -void CIcqProto::OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - auto *ft = (IcqFileTransfer*)pReq->pUserInfo; - - if (pReply->resultCode != 200) { -LBL_Error: - FileCancel(pReq->hContact, ft); - return; - } - - ft->hWaitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, ft, (LPARAM)&ft->pfts)) - WaitForSingleObject(ft->hWaitEvent, INFINITE); - CloseHandle(ft->hWaitEvent); - - debugLogW(L"Saving to [%s]", ft->pfts.szCurrentFile.w); - int fileId = _wopen(ft->pfts.szCurrentFile.w, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); - if (fileId == -1) { - debugLogW(L"Cannot open [%s] for writing", ft->pfts.szCurrentFile.w); - goto LBL_Error; - } - - int result = _write(fileId, pReply->pData, pReply->dataLength); - _close(fileId); - if (result != pReply->dataLength) { - debugLogW(L"Error writing data into [%s]", ft->pfts.szCurrentFile.w); - goto LBL_Error; - } - - ft->pfts.totalProgress += pReply->dataLength; - ft->pfts.currentFileProgress += pReply->dataLength; - ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->pfts); - - ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft); - delete ft; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) -{ - RobustReply root(pReply); - if (root.error() != 20000) - return; - - auto &results = root.results(); - m_szRToken = results["authToken"].as_mstring(); -} - -void CIcqProto::OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - RobustReply root(pReply); - if (root.error() != 20000) - return; - - __int64 lastMsgId = getId(pReq->hContact, DB_KEY_LASTMSGID); - - int count = 0; - auto &results = root.results(); - for (auto &it : results["messages"]) { - ParseMessage(pReq->hContact, lastMsgId, it, pReq->pUserInfo != nullptr, false); - count++; - } - - setId(pReq->hContact, DB_KEY_LASTMSGID, lastMsgId); - - if (count >= 999) - RetrieveUserHistory(pReq->hContact, lastMsgId, pReq->pUserInfo != nullptr); -} - -void CIcqProto::OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - RobustReply root(pReply); - if (root.error() != 20000) { - ProtoBroadcastAck(pReq->hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, nullptr); - return; - } - - ParseBuddyInfo(root.results(), pReq->hContact, true); - - ProtoBroadcastAck(pReq->hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, nullptr); -} - -void CIcqProto::OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) -{ - JsonReply root(pReply); - switch (root.error()) { - case 200: - break; - - case 451: - // session forcibly closed from site - delSetting(DB_KEY_ATOKEN); - delSetting(DB_KEY_SESSIONKEY); - CheckPassword(); - return; - - case 401: - delSetting(DB_KEY_ATOKEN); - delSetting(DB_KEY_SESSIONKEY); - if (root.detail() == 1002) // session expired - CheckPassword(); - else - ConnectionFailed(LOGINERR_WRONGPASSWORD, root.error()); - return; - - case 400: - if (root.detail() == 1015 && m_iTimeShift == 0) { // wrong timestamp - JSONNode &data = root.data(); - int srvTS = data["ts"].as_int(); - m_iTimeShift = (srvTS) ? time(0) - srvTS : 0; - StartSession(); - return; - } - __fallthrough; - - default: - ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error()); - return; - } - - JSONNode &data = root.data(); - m_fetchBaseURL = data["fetchBaseURL"].as_mstring(); - m_aimsid = data["aimsid"].as_mstring(); - - ProcessMyInfo(data["myInfo"]); - - int srvTS = data["ts"].as_int(); - m_iTimeShift = (srvTS) ? time(0) - srvTS : 0; - - OnLoggedIn(); - - for (auto &it : data["events"]) - ProcessEvent(it); - - ForkThread(&CIcqProto::PollThread); -} - -void CIcqProto::OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - PROTO_AVATAR_INFORMATION ai = {}; - ai.hContact = pReq->hContact; - - if (pReply->resultCode != 200 || pReply->pData == nullptr) { -LBL_Error: - ProtoBroadcastAck(pReq->hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE(&ai), 0); - return; - } - - const char *szContentType = Netlib_GetHeader(pReply, "Content-Type"); - if (szContentType == nullptr) - szContentType = "image/jpeg"; - - ai.format = ProtoGetAvatarFormatByMimeType(szContentType); - setByte(pReq->hContact, "AvatarType", ai.format); - GetAvatarFileName(pReq->hContact, ai.filename, _countof(ai.filename)); - - FILE *out = _wfopen(ai.filename, L"wb"); - if (out == nullptr) - goto LBL_Error; - - fwrite(pReply->pData, pReply->dataLength, 1, out); - fclose(out); - - if (pReq->hContact != 0) { - ProtoBroadcastAck(pReq->hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE(&ai), 0); - debugLogW(L"Broadcast new avatar: %s", ai.filename); - } - else ReportSelfAvatarChanged(); -} - -void CIcqProto::OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - RobustReply root(pReply); - if (root.error() != 20000) { - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)pReq, 0); - return; - } - - const JSONNode &results = root.results(); - - PROTOSEARCHRESULT psr = {}; - psr.cbSize = sizeof(psr); - psr.flags = PSR_UNICODE; - for (auto &it : results["persons"]) { - CMStringW wszId = it["sn"].as_mstring(); - if (wszId == m_szOwnId) - continue; - - CMStringW wszNick = it["friendly"].as_mstring(); - CMStringW wszFirst = it["firstName"].as_mstring(); - CMStringW wszLast = it["lastName"].as_mstring(); - - psr.id.w = wszId.GetBuffer(); - psr.nick.w = wszNick.GetBuffer(); - psr.firstName.w = wszFirst.GetBuffer(); - psr.lastName.w = wszLast.GetBuffer(); - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)pReq, LPARAM(&psr)); - } - - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)pReq); -} - -void CIcqProto::OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - IcqOwnMessage *ownMsg = (IcqOwnMessage*)pReq->pUserInfo; - - JsonReply root(pReply); - if (root.error() != 200) { - ProtoBroadcastAck(ownMsg->m_hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)ownMsg->m_msgid, 0); - - mir_cslock lck(m_csOwnIds); - m_arOwnIds.remove(ownMsg); - } - - if (g_bMessageState) - CallService(MS_MESSAGESTATE_UPDATE, ownMsg->m_hContact, MRD_TYPE_DELIVERED); - - const JSONNode &data = root.data(); - CMStringA reqId(root.requestId()); - CMStringA msgId(data["histMsgId"].as_mstring()); - CheckOwnMessage(reqId, msgId, false); - CheckLastId(ownMsg->m_hContact, data); -} - -void CIcqProto::OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) -{ - JsonReply root(pReply); - if (root.error() == 200) { - m_szAToken.Empty(); - delSetting(DB_KEY_ATOKEN); - - m_szSessionKey.Empty(); - delSetting(DB_KEY_SESSIONKEY); - - ShutdownSession(); - } -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#pragma comment(lib, "libcrypto.lib")
+
+void CIcqProto::CheckAvatarChange(MCONTACT hContact, const JSONNode &ev)
+{
+ CMStringW wszIconId(ev["bigIconId"].as_mstring());
+ if (wszIconId.IsEmpty())
+ wszIconId = ev["iconId"].as_mstring();
+
+ if (!wszIconId.IsEmpty()) {
+ CMStringW oldIconID(getMStringW(hContact, "IconId"));
+ if (wszIconId == oldIconID) {
+ wchar_t wszFullName[MAX_PATH];
+ GetAvatarFileName(hContact, wszFullName, _countof(wszFullName));
+ if (_waccess(wszFullName, 0) == 0)
+ return;
+ }
+
+ setWString(hContact, "IconId", wszIconId);
+
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/expressions/get", &CIcqProto::OnReceiveAvatar);
+ pReq << CHAR_PARAM("f", "native") << WCHAR_PARAM("t", GetUserId(hContact)) << CHAR_PARAM("type", "bigBuddyIcon");
+ pReq->hContact = hContact;
+ Push(pReq);
+ }
+ else delSetting(hContact, "IconId");
+}
+
+void CIcqProto::CheckLastId(MCONTACT hContact, const JSONNode &ev)
+{
+ __int64 msgId = _wtoi64(ev["histMsgId"].as_mstring());
+ __int64 lastId = getId(hContact, DB_KEY_LASTMSGID);
+ if (msgId > lastId)
+ setId(hContact, DB_KEY_LASTMSGID, msgId);
+}
+
+MCONTACT CIcqProto::CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove)
+{
+ IcqOwnMessage *pOwn = nullptr;
+ {
+ mir_cslock lck(m_csOwnIds);
+ for (auto &it : m_arOwnIds) {
+ if (reqId == it->m_guid) {
+ pOwn = it;
+ break;
+ }
+ }
+ }
+
+ if (pOwn == nullptr)
+ return 0;
+
+ ProtoBroadcastAck(pOwn->m_hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)pOwn->m_msgid, (LPARAM)msgId.c_str());
+
+ MCONTACT ret = pOwn->m_hContact;
+ if (bRemove) {
+ mir_cslock lck(m_csOwnIds);
+ m_arOwnIds.remove(pOwn);
+ }
+ return ret;
+}
+
+void CIcqProto::CheckPassword()
+{
+ char mirVer[100];
+ Miranda_GetVersionText(mirVer, _countof(mirVer));
+
+ m_szAToken = getMStringA(DB_KEY_ATOKEN);
+ m_iRClientId = getDword(DB_KEY_RCLIENTID);
+ m_szSessionKey = getMStringA(DB_KEY_SESSIONKEY);
+ if (!m_szAToken.IsEmpty() && !m_szSessionKey.IsEmpty()) {
+ StartSession();
+ return;
+ }
+
+ if (m_isMra) {
+ m_bError462 = false;
+ SendMrimLogin(nullptr);
+ }
+ else {
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "https://api.login.icq.net/auth/clientLogin", &CIcqProto::OnCheckPassword);
+ pReq << CHAR_PARAM("clientName", "Miranda NG") << CHAR_PARAM("clientVersion", mirVer) << CHAR_PARAM("devId", appId())
+ << CHAR_PARAM("f", "json") << CHAR_PARAM("tokenType", "longTerm") << WCHAR_PARAM("s", m_szOwnId) << CHAR_PARAM("pwd", m_szPassword);
+ #ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+ #endif
+ Push(pReq);
+ }
+}
+
+IcqFileInfo* CIcqProto::CheckFile(MCONTACT hContact, CMStringW &wszText, bool &bIsFile)
+{
+ CMStringW wszUrl(wszText.Mid(26));
+ int idx = wszUrl.Find(' ');
+ if (idx != -1)
+ wszUrl.Truncate(idx);
+
+ bIsFile = false;
+ IcqFileInfo *pFileInfo = nullptr;
+
+ // is it already downloaded sticker?
+ CMStringW wszLoadedPath(FORMAT, L"%s\\%S\\Stickers\\STK{%s}.png", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName, wszUrl.c_str());
+ if (!_waccess(wszLoadedPath, 0)) {
+ pFileInfo = (IcqFileInfo *)this;
+ wszText.Format(L"STK{%s}", wszUrl.c_str());
+ }
+ else {
+ // download file info
+ CMStringA szUrl(FORMAT, ICQ_FILE_SERVER "/info/%S/", wszUrl.c_str());
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnFileInfo);
+ pReq->hContact = hContact;
+ pReq->pUserInfo = &pFileInfo;
+ pReq << CHAR_PARAM("aimsid", m_aimsid) << CHAR_PARAM("previews", "192,600,xlarge");
+ if (!ExecuteRequest(pReq))
+ return nullptr;
+
+ // is it a sticker?
+ if (pFileInfo->bIsSticker) {
+ if (ServiceExists(MS_SMILEYADD_LOADCONTACTSMILEYS)) {
+ auto *pNew = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, pFileInfo->szUrl, &CIcqProto::OnGetSticker);
+ pNew->flags |= NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ pNew->pUserInfo = wszUrl.GetBuffer();
+ pNew->AddHeader("Sec-Fetch-User", "?1");
+ pNew->AddHeader("Sec-Fetch-Site", "cross-site");
+ pNew->AddHeader("Sec-Fetch-Mode", "navigate");
+ if (!ExecuteRequest(pNew))
+ return nullptr;
+
+ wszText.Format(L"STK{%s}", wszUrl.c_str());
+ delete pFileInfo;
+ }
+ else wszText = TranslateT("SmileyAdd plugin required to support stickers");
+ }
+ else bIsFile = true;
+ }
+
+ return pFileInfo;
+}
+
+void CIcqProto::CheckStatus()
+{
+ time_t now = time(0);
+ int diff1 = m_iTimeDiff1, diff2 = m_iTimeDiff2;
+
+ for (auto &it : m_arCache) {
+ // this contact is really offline and is on the first timer
+ // if the first timer is expired, we clear it and look for the second status
+ if (diff1 && it->m_timer1 && now - it->m_timer1 > diff1) {
+ it->m_timer1 = 0;
+
+ // if the second timer is set up, activate it
+ if (m_iTimeDiff2) {
+ setWord(it->m_hContact, "Status", m_iStatus2);
+ it->m_timer2 = now;
+ }
+ // if the second timer is not set, simply mark a contact as offline
+ else setWord(it->m_hContact, "Status", ID_STATUS_OFFLINE);
+ continue;
+ }
+
+ // if the second timer is expired, set status to offline
+ if (diff2 && it->m_timer2 && now - it->m_timer2 > m_iTimeDiff2) {
+ it->m_timer2 = 0;
+ setWord(it->m_hContact, "Status", ID_STATUS_OFFLINE);
+ }
+ }
+}
+
+void CIcqProto::ConnectionFailed(int iReason, int iErrorCode)
+{
+ debugLogA("ConnectionFailed -> reason %d", iReason);
+
+ if (m_bErrorPopups) {
+ POPUPDATAW Popup = {};
+ Popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR), true);
+ wcscpy_s(Popup.lpwzContactName, m_tszUserName);
+ switch (iReason) {
+ case LOGINERR_BADUSERID:
+ mir_snwprintf(Popup.lpwzText, TranslateT("You have not entered a login or password.\nConfigure this in Options -> Network -> ICQ and try again."));
+ break;
+ case LOGINERR_WRONGPASSWORD:
+ mir_snwprintf(Popup.lpwzText, TranslateT("Connection failed.\nYour login or password was rejected (%d)."), iErrorCode);
+ break;
+ case LOGINERR_NONETWORK:
+ case LOGINERR_NOSERVER:
+ mir_snwprintf(Popup.lpwzText, TranslateT("Connection failed.\nThe server is temporarily unavailable (%d)."), iErrorCode);
+ break;
+ default:
+ mir_snwprintf(Popup.lpwzText, TranslateT("Connection failed.\nUnknown error during sign on: %d"), iErrorCode);
+ break;
+ }
+ PUAddPopupW(&Popup);
+ }
+
+ ProtoBroadcastAck(0, ACKTYPE_LOGIN, ACKRESULT_FAILED, nullptr, iReason);
+ ShutdownSession();
+}
+
+void CIcqProto::MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup)
+{
+ // otherwise we'll get a server error
+ if (!mir_wstrlen(pwszGroup))
+ return;
+
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/buddylist/moveBuddy") << AIMSID(this) << WCHAR_PARAM("buddy", GetUserId(hContact))
+ << GROUP_PARAM("group", pwszGroup);
+ if (mir_wstrlen(pwszNewGroup))
+ pReq << GROUP_PARAM("newGroup", pwszNewGroup);
+ Push(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OnLoggedIn()
+{
+ debugLogA("CIcqProto::OnLoggedIn");
+ m_bOnline = true;
+ m_impl.m_heartBeat.Start(1000);
+
+ for (auto &it : m_arCache)
+ it->m_timer1 = it->m_timer2 = 0;
+
+ SetServerStatus(m_iDesiredStatus);
+ RetrieveUserInfo(0);
+ GetPermitDeny();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OnLoggedOut()
+{
+ debugLogA("CIcqProto::OnLoggedOut");
+ m_bOnline = false;
+ m_impl.m_heartBeat.Stop();
+
+ for (auto &it : m_arCache)
+ it->m_timer1 = it->m_timer2 = 0;
+
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE);
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::MarkAsRead(MCONTACT hContact)
+{
+ if (!m_bOnline)
+ return;
+
+ m_impl.m_markRead.Start(200);
+
+ auto *pCache = FindContactByUIN(GetUserId(hContact));
+ if (pCache) {
+ mir_cslock lck(m_csMarkReadQueue);
+ if (m_arMarkReadQueue.indexOf(pCache) == -1)
+ m_arMarkReadQueue.insert(pCache);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MCONTACT CIcqProto::ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact, bool bIsPartial)
+{
+ // user chat?
+ CMStringW wszId(buddy["aimId"].as_mstring());
+ if (IsChat(wszId)) {
+ CMStringW wszChatId(buddy["aimId"].as_mstring());
+ CMStringW wszChatName(buddy["friendly"].as_mstring());
+
+ auto *pContact = FindContactByUIN(wszId);
+ if (pContact && pContact->m_iApparentMode == ID_STATUS_OFFLINE)
+ return INVALID_CONTACT_ID;
+
+ auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszChatId, wszChatName);
+ if (si == nullptr)
+ return INVALID_CONTACT_ID;
+
+ Chat_AddGroup(si, TranslateT("admin"));
+ Chat_AddGroup(si, TranslateT("member"));
+ Chat_Control(m_szModuleName, wszChatId, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE);
+ Chat_Control(m_szModuleName, wszChatId, SESSION_ONLINE);
+ return si->hContact;
+ }
+
+ bool bIgnored = !IsValidType(buddy);
+ if (hContact == INVALID_CONTACT_ID) {
+ if (bIgnored)
+ return INVALID_CONTACT_ID;
+
+ hContact = CreateContact(wszId, false);
+ FindContactByUIN(wszId)->m_bInList = true;
+ }
+ else if (bIgnored) {
+ db_delete_contact(hContact);
+ return INVALID_CONTACT_ID;
+ }
+
+ CMStringA szVer;
+ bool bVersionDetected = false, bSecureIM = false;
+
+ for (auto &it : buddy["capabilities"]) {
+ CMStringW wszCap(it.as_mstring());
+ if (wszCap.GetLength() != 32)
+ continue;
+
+ uint8_t cap[16];
+ hex2binW(wszCap, cap, sizeof(cap));
+ if (!memcmp(cap, "MiNG", 4)) { // Miranda
+ int v[4];
+ if (4 == swscanf(wszCap.c_str() + 16, L"%04x%04x%04x%04x", &v[0], &v[1], &v[2], &v[3])) {
+ szVer.Format("Miranda NG %d.%d.%d.%d (ICQ %d.%d.%d.%d)", v[0], v[1], v[2], v[3], cap[4], cap[5], cap[6], cap[7]);
+ setString(hContact, "MirVer", szVer);
+ bVersionDetected = true;
+ }
+ }
+ else if (wszCap == _A2W(NG_CAP_SECUREIM)) {
+ bSecureIM = bVersionDetected = true;
+ }
+ else if (!memcmp(cap, "Mod by Mikanoshi", 16)) {
+ szVer = "R&Q build by Mikanoshi";
+ bVersionDetected = true;
+ }
+ else if (!memcmp(cap, "Mandarin IM", 11)) {
+ szVer = "Mandarin IM";
+ bVersionDetected = true;
+ }
+ }
+
+ if (bVersionDetected) {
+ if (bSecureIM)
+ szVer.Append(" + SecureIM");
+ setString(hContact, "MirVer", szVer);
+ }
+ else delSetting(hContact, "MirVer");
+
+ const JSONNode &var = buddy["friendly"];
+ if (var)
+ setWString(hContact, "Nick", var.as_mstring());
+
+ if (buddy["deleted"].as_bool()) {
+ setByte(hContact, "IcqDeleted", 1);
+ Contact::PutOnList(hContact);
+ }
+
+ Json2string(hContact, buddy, "emailId", "Email", bIsPartial);
+ Json2string(hContact, buddy, "cellNumber", "Cellular", bIsPartial);
+ Json2string(hContact, buddy, "workNumber", "CompanyPhone", bIsPartial);
+
+ // we shall not remove existing phone number anyhow
+ Json2string(hContact, buddy, "phoneNumber", DB_KEY_PHONE, true);
+
+ Json2int(hContact, buddy, "official", "Official", bIsPartial);
+ Json2int(hContact, buddy, "onlineTime", DB_KEY_ONLINETS, bIsPartial);
+ Json2int(hContact, buddy, "idleTime", "IdleTS", bIsPartial);
+ Json2int(hContact, buddy, "memberSince", DB_KEY_MEMBERSINCE, bIsPartial);
+
+ int iStatus = StatusFromPresence(buddy, hContact);
+ if (iStatus > 0)
+ setWord(hContact, "Status", iStatus);
+
+ const JSONNode &profile = buddy["profile"];
+ if (profile) {
+ Json2string(hContact, profile, "friendlyName", DB_KEY_ICQNICK, bIsPartial);
+ Json2string(hContact, profile, "firstName", "FirstName", bIsPartial);
+ Json2string(hContact, profile, "lastName", "LastName", bIsPartial);
+ Json2string(hContact, profile, "aboutMe", DB_KEY_ABOUT, bIsPartial);
+
+ ptrW wszNick(getWStringA(hContact, "Nick"));
+ if (!wszNick) {
+ CMStringW srvNick = profile["friendlyName"].as_mstring();
+ if (!srvNick.IsEmpty())
+ setWString(hContact, "Nick", srvNick);
+ }
+
+ time_t birthDate = profile["birthDate"].as_int();
+ if (birthDate != 0) {
+ struct tm *timeinfo = localtime(&birthDate);
+ if (timeinfo != nullptr) {
+ setWord(hContact, "BirthDay", timeinfo->tm_mday);
+ setWord(hContact, "BirthMonth", timeinfo->tm_mon+1);
+ setWord(hContact, "BirthYear", timeinfo->tm_year+1900);
+ }
+ }
+
+ CMStringW str = profile["gender"].as_mstring();
+ if (!str.IsEmpty()) {
+ if (str == "male")
+ setByte(hContact, "Gender", 'M');
+ else if (str == "female")
+ setByte(hContact, "Gender", 'F');
+ }
+
+ for (auto &it : profile["homeAddress"]) {
+ Json2string(hContact, it, "city", "City", bIsPartial);
+ Json2string(hContact, it, "state", "State", bIsPartial);
+ Json2string(hContact, it, "country", "Country", bIsPartial);
+ }
+ }
+
+ CMStringW str = buddy["statusMsg"].as_mstring();
+ if (str.IsEmpty())
+ db_unset(hContact, "CList", "StatusMsg");
+ else
+ db_set_ws(hContact, "CList", "StatusMsg", str);
+
+ CheckAvatarChange(hContact, buddy);
+ return hContact;
+}
+
+void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &it, bool bCreateRead, bool bLocalTime)
+{
+ CMStringA szMsgId(it["msgId"].as_mstring());
+ __int64 msgId = _atoi64(szMsgId);
+ if (msgId > lastMsgId)
+ lastMsgId = msgId;
+
+ CMStringW wszText;
+ const JSONNode &sticker = it["sticker"];
+ if (sticker) {
+ CMStringW wszUrl, wszSticker(sticker["id"].as_mstring());
+ int iCollectionId, iStickerId;
+ if (2 == swscanf(wszSticker, L"ext:%d:sticker:%d", &iCollectionId, &iStickerId))
+ wszUrl.Format(L"https://c.icq.com/store/stickers/%d/%d/medium", iCollectionId, iStickerId);
+ else
+ wszUrl = TranslateT("Unknown sticker");
+ wszText.Format(L"%s\n%s", TranslateT("User sent a sticker:"), wszUrl.c_str());
+ }
+ else {
+ wszText = it["text"].as_mstring();
+ wszText.TrimRight();
+
+ // user added you
+ if (it["class"].as_mstring() == L"event" && it["eventTypeId"].as_mstring() == L"27:33000") {
+ if (bLocalTime) {
+ CMStringA id = getMStringA(hContact, DB_KEY_ID);
+ int pos = id.Find('@');
+ CMStringA nick = (pos == -1) ? id : id.Left(pos);
+ DB::AUTH_BLOB blob(hContact, nick, nullptr, nullptr, id, nullptr);
+
+ PROTORECVEVENT pre = {};
+ pre.timestamp = (uint32_t)time(0);
+ pre.lParam = blob.size();
+ pre.szMessage = blob;
+ ProtoChainRecv(hContact, PSR_AUTH, 0, (LPARAM)&pre);
+ }
+ return;
+ }
+ }
+
+ int iMsgTime = (bLocalTime) ? time(0) : it["time"].as_int();
+ bool bIsOutgoing = it["outgoing"].as_bool(), bIsFileTransfer = false;
+ IcqFileInfo *pFileInfo = nullptr;
+
+ if (!bCreateRead && !bIsOutgoing && wszText.Left(26) == L"https://files.icq.net/get/") {
+ pFileInfo = CheckFile(hContact, wszText, bIsFileTransfer);
+ if (!pFileInfo)
+ return;
+
+ for (auto &jt : it["parts"]) {
+ CMStringW wszDescr(jt["captionedContent"]["caption"].as_mstring());
+ if (!wszDescr.IsEmpty())
+ pFileInfo->wszDescr = wszDescr;
+ }
+ }
+
+ if (isChatRoom(hContact)) {
+ CMStringA reqId(it["reqId"].as_mstring());
+ CheckOwnMessage(reqId, szMsgId, true);
+
+ CMStringW wszSender(it["chat"]["sender"].as_mstring());
+ CMStringW wszChatId(getMStringW(hContact, "ChatRoomID"));
+
+ if (bIsFileTransfer) {
+ wszText = pFileInfo->szUrl;
+ if (!pFileInfo->wszDescr)
+ wszText.AppendFormat(L"\r\n%s", pFileInfo->wszDescr.c_str());
+ delete pFileInfo;
+ }
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE };
+ gce.pszID.w = wszChatId;
+ gce.dwFlags = GCEF_ADDTOLOG;
+ gce.pszUID.w = wszSender;
+ gce.pszText.w = wszText;
+ gce.time = iMsgTime;
+ gce.bIsMe = wszSender == m_szOwnId;
+ Chat_Event(&gce);
+ return;
+ }
+
+ // skip own messages, just set the server msgid
+ CMStringA reqId(it["reqId"].as_mstring());
+ if (CheckOwnMessage(reqId, szMsgId, true)) {
+ debugLogA("Skipping our own message %s", szMsgId.c_str());
+ return;
+ }
+
+ // ignore duplicates
+ MEVENT hDbEvent = db_event_getById(m_szModuleName, szMsgId);
+ if (hDbEvent != 0) {
+ debugLogA("Message %s already exists", szMsgId.c_str());
+ return;
+ }
+
+ // convert a file info into Miranda's file transfer
+ if (bIsFileTransfer) {
+ auto *ft = new IcqFileTransfer(hContact, pFileInfo->szUrl);
+ ft->pfts.totalBytes = ft->pfts.currentFileSize = pFileInfo->dwFileSize;
+ ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer();
+
+ PROTORECVFILE pre = {};
+ pre.dwFlags = PRFF_UNICODE;
+ pre.fileCount = 1;
+ pre.timestamp = iMsgTime;
+ pre.files.w = &ft->m_wszShortName;
+ pre.descr.w = pFileInfo->wszDescr;
+ pre.lParam = (LPARAM)ft;
+ ProtoChainRecvFile(hContact, &pre);
+
+ delete pFileInfo;
+ return;
+ }
+
+ // suppress notifications for already loaded/processed messages
+ __int64 storedLastId = getId(hContact, DB_KEY_LASTMSGID);
+ if (msgId <= storedLastId) {
+ debugLogA("Parsing old/processed message with id %lld < %lld, setting CR to true", msgId, storedLastId);
+ bCreateRead = true;
+ }
+
+ debugLogA("Adding message %d:%lld (CR=%d)", hContact, msgId, bCreateRead);
+
+ ptrA szUtf(mir_utf8encodeW(wszText));
+
+ PROTORECVEVENT pre = {};
+ if (bIsOutgoing) pre.flags |= PREF_SENT;
+ if (bCreateRead) pre.flags |= PREF_CREATEREAD;
+ pre.szMsgId = szMsgId;
+ pre.timestamp = iMsgTime;
+ pre.szMessage = szUtf;
+ ProtoChainRecvMsg(hContact, &pre);
+}
+
+bool CIcqProto::RefreshRobustToken(AsyncHttpRequest *pOrigReq)
+{
+ if (!m_szRToken.IsEmpty())
+ return true;
+
+ auto *pReq = new AsyncHttpRequest(CONN_RAPI, REQUEST_POST, ICQ_ROBUST_SERVER "/genToken", &CIcqProto::OnGenToken);
+ #ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+ #endif
+
+ int ts = TS();
+ pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("nonce", CMStringA(FORMAT, "%d-%d", ts, rand() % 10)) << INT_PARAM("ts", ts);
+ CalcHash(pReq);
+
+ CMStringA szAgent(FORMAT, "%S Mail.ru Windows ICQ (version 10.0.1999)", (wchar_t*)m_szOwnId);
+ pReq->AddHeader("User-Agent", szAgent);
+ if (!ExecuteRequest(pReq)) {
+LBL_Error:
+ (this->*(pOrigReq->m_pFunc))(nullptr, pOrigReq);
+ return false;
+ }
+ if (m_szRToken.IsEmpty())
+ goto LBL_Error;
+
+ // now add this token
+ bool bRet = false;
+ pReq = new AsyncHttpRequest(CONN_RAPI, REQUEST_POST, ICQ_ROBUST_SERVER "/addClient", &CIcqProto::OnAddClient);
+ #ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+ #endif
+ pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("f", "json") << CHAR_PARAM("k", appId()) << INT_PARAM("ts", ts)
+ << CHAR_PARAM("client", "icq") << CHAR_PARAM("reqId", pReq->m_reqId) << CHAR_PARAM("authToken", m_szRToken);
+ pReq->pUserInfo = &bRet;
+ if (!ExecuteRequest(pReq))
+ goto LBL_Error;
+
+ return bRet;
+}
+
+void CIcqProto::RetrieveUserInfo(MCONTACT hContact)
+{
+ auto *pReq = new AsyncRapiRequest(this, "getUserInfo", &CIcqProto::OnGetUserInfo);
+ pReq->params << WCHAR_PARAM("sn", GetUserId(hContact));
+ pReq->hContact = hContact;
+ Push(pReq);
+}
+
+void CIcqProto::RetrieveUserHistory(MCONTACT hContact, __int64 startMsgId, bool bCreateRead)
+{
+ if (startMsgId == 0)
+ startMsgId = -1;
+
+ __int64 patchVer = getId(hContact, DB_KEY_PATCHVER);
+ if (patchVer == 0)
+ patchVer = 1;
+
+ auto *pReq = new AsyncRapiRequest(this, "getHistory", &CIcqProto::OnGetUserHistory);
+ #ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+ #endif
+ pReq->hContact = hContact;
+ pReq->pUserInfo = (bCreateRead) ? pReq : 0;
+ pReq->params << WCHAR_PARAM("sn", GetUserId(hContact)) << INT64_PARAM("fromMsgId", startMsgId) << INT_PARAM("count", 1000)
+ << SINT64_PARAM("patchVersion", patchVer) << CHAR_PARAM("language", "ru-ru");
+ Push(pReq);
+}
+
+void CIcqProto::SetServerStatus(int iStatus)
+{
+ const char *szStatus = "online";
+ int invisible = 0;
+
+ switch (iStatus) {
+ case ID_STATUS_OFFLINE: szStatus = "offline"; break;
+ case ID_STATUS_NA: szStatus = "occupied"; break;
+ case ID_STATUS_AWAY:
+ case ID_STATUS_DND: szStatus = "away"; break;
+ case ID_STATUS_INVISIBLE:
+ invisible = 1;
+ }
+
+ Push(new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/presence/setState")
+ << AIMSID(this) << CHAR_PARAM("view", szStatus) << INT_PARAM("invisible", invisible));
+
+ if (iStatus == ID_STATUS_OFFLINE && !getByte(DB_KEY_PHONEREG)) {
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, ICQ_API_SERVER "/aim/endSession", &CIcqProto::OnSessionEnd);
+ pReq << AIMSID(this) << INT_PARAM("invalidateToken", 1);
+ ExecuteRequest(pReq);
+ }
+
+ int iOldStatus = m_iStatus; m_iStatus = iStatus;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);
+}
+
+void CIcqProto::ShutdownSession()
+{
+ if (m_bTerminated)
+ return;
+
+ debugLogA("CIcqProto::ShutdownSession");
+
+ // shutdown all resources
+ DropQueue();
+
+ if (m_hWorkerThread)
+ SetEvent(m_evRequestsQueue);
+
+ OnLoggedOut();
+
+ for (auto &it : m_ConnPool) {
+ if (it.s) {
+ Netlib_Shutdown(it.s);
+ it.s = nullptr;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define EVENTS "myInfo,presence,buddylist,typing,dataIM,userAddedToBuddyList,mchat,hist,hiddenChat,diff,permitDeny,imState,notification,apps"
+#define FIELDS "aimId,buddyIcon,bigBuddyIcon,iconId,bigIconId,largeIconId,displayId,friendly,offlineMsg,state,statusMsg,userType,phoneNumber,cellNumber,smsNumber,workNumber,otherNumber,capabilities,ssl,abPhoneNumber,moodIcon,lastName,abPhones,abContactName,lastseen,mute,livechat,official"
+
+void CIcqProto::StartSession()
+{
+ ptrA szDeviceId(getStringA("DeviceId"));
+ if (szDeviceId == nullptr) {
+ UUID deviceId;
+ UuidCreate(&deviceId);
+ RPC_CSTR szId;
+ UuidToStringA(&deviceId, &szId);
+ szDeviceId = mir_strdup((char*)szId);
+ setString("DeviceId", szDeviceId);
+ RpcStringFreeA(&szId);
+ }
+
+ int ts = TS();
+ CMStringA nonce(FORMAT, "%d-2", ts);
+ CMStringA caps(WIM_CAP_UNIQ_REQ_ID "," WIM_CAP_EMOJI "," WIM_CAP_MAIL_NOTIFICATIONS "," WIM_CAP_INTRO_DLG_STATE);
+ if (g_bSecureIM) {
+ caps.AppendChar(',');
+ caps.Append(NG_CAP_SECUREIM);
+ }
+
+ MFileVersion v;
+ Miranda_GetFileVersion(&v);
+ caps.AppendFormat(",%02x%02x%02x%02x%02x%02x%02x%02x%04x%04x%04x%04x", 'M', 'i', 'N', 'G',
+ __MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM, v[0], v[1], v[2], v[3]);
+
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/aim/startSession", &CIcqProto::OnStartSession);
+ pReq << CHAR_PARAM("a", m_szAToken) << INT_PARAM("activeTimeout", 180) << CHAR_PARAM("assertCaps", caps)
+ << INT_PARAM("buildNumber", __BUILD_NUM) << CHAR_PARAM("deviceId", szDeviceId) << CHAR_PARAM("events", EVENTS)
+ << CHAR_PARAM("f", "json") << CHAR_PARAM("imf", "plain") << CHAR_PARAM("inactiveView", "offline")
+ << CHAR_PARAM("includePresenceFields", FIELDS) << CHAR_PARAM("invisible", "false")
+ << CHAR_PARAM("k", appId()) << INT_PARAM("mobile", 0) << CHAR_PARAM("nonce", nonce) << CHAR_PARAM("r", pReq->m_reqId)
+ << INT_PARAM("rawMsg", 0) << INT_PARAM("sessionTimeout", 7776000) << INT_PARAM("ts", ts) << CHAR_PARAM("view", "online");
+
+ CalcHash(pReq);
+ Push(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ JsonReply root(pReply);
+ if (root.error() != 200)
+ return;
+
+ CMStringW wszId = getMStringW(pReq->hContact, DB_KEY_ID);
+ for (auto &it : root.data()["results"]) {
+ if (it["buddy"].as_mstring() != wszId)
+ continue;
+
+ switch (int iResultCode = it["resultCode"].as_int()) {
+ case 0: // success
+ case 3: // already in contact list
+ break;
+
+ default:
+ debugLogA("Contact %d failed to add: error %d", pReq->hContact, iResultCode);
+
+ POPUPDATAW Popup = {};
+ Popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR));
+ wcsncpy_s(Popup.lpwzText, TranslateT("Buddy addition failed"), _TRUNCATE);
+ wcsncpy_s(Popup.lpwzContactName, Clist_GetContactDisplayName(pReq->hContact), _TRUNCATE);
+ Popup.iSeconds = 20;
+ PUAddPopupW(&Popup);
+
+ // Contact::RemoveFromList(pReq->hContact);
+ }
+
+ RetrieveUserInfo(pReq->hContact);
+ Contact::PutOnList(pReq->hContact);
+ }
+}
+
+void CIcqProto::OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ bool *pRet = (bool*)pReq->pUserInfo;
+
+ RobustReply reply(pReply);
+ if (reply.error() != 20000) {
+ *pRet = false;
+ return;
+ }
+
+ const JSONNode &results = reply.results();
+ m_iRClientId = results["clientId"].as_int();
+ setDword(DB_KEY_RCLIENTID, m_iRClientId);
+ *pRet = true;
+}
+
+void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ break;
+
+ case 330:
+ case 440:
+ ConnectionFailed(LOGINERR_WRONGPASSWORD, root.error());
+ return;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error());
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_szAToken = data["token"]["a"].as_mstring();
+ mir_urlDecode(m_szAToken.GetBuffer());
+ setString(DB_KEY_ATOKEN, m_szAToken);
+
+ CMStringA szSessionSecret = data["sessionSecret"].as_mstring();
+ CMStringA szPassTemp = m_szPassword;
+
+ unsigned int len;
+ uint8_t hashOut[MIR_SHA256_HASH_SIZE];
+ HMAC(EVP_sha256(), szPassTemp, szPassTemp.GetLength(), (uint8_t*)szSessionSecret.c_str(), szSessionSecret.GetLength(), hashOut, &len);
+ m_szSessionKey = ptrA(mir_base64_encode(hashOut, sizeof(hashOut)));
+ setString(DB_KEY_SESSIONKEY, m_szSessionKey);
+
+ CMStringW szUin = data["loginId"].as_mstring();
+ if (szUin)
+ m_szOwnId = szUin;
+
+ int srvTS = data["hostTime"].as_int();
+ m_iTimeShift = (srvTS) ? time(0) - srvTS : 0;
+
+ StartSession();
+}
+
+void CIcqProto::OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pOld)
+{
+ IcqFileTransfer *pTransfer = (IcqFileTransfer*)pOld->pUserInfo;
+ if (pTransfer->m_bCanceled) {
+LBL_Error:
+ ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, pTransfer);
+ delete pTransfer;
+ return;
+ }
+
+ switch (pReply->resultCode) {
+ case 200: // final ok
+ case 206: // partial ok
+ break;
+
+ default:
+ goto LBL_Error;
+ }
+
+ // file transfer succeeded?
+ if (pTransfer->pfts.currentFileProgress == pTransfer->pfts.currentFileSize) {
+ FileReply root(pReply);
+ if (root.error() != 200)
+ goto LBL_Error;
+
+ ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, pTransfer);
+
+ const JSONNode &data = root.data();
+ CMStringW wszUrl(data["static_url"].as_mstring());
+
+ JSONNode bundle, contents; contents.set_name("captionedContent");
+ contents << WCHAR_PARAM("caption", pTransfer->m_wszDescr) << WCHAR_PARAM("url", wszUrl);
+ bundle << CHAR_PARAM("mediaType", "text") << CHAR_PARAM("text", "") << contents;
+ CMStringW wszParts(FORMAT, L"[%s]", ptrW(json_write(&bundle)).get());
+
+ if (!pTransfer->m_wszDescr.IsEmpty())
+ wszUrl += L" " + pTransfer->m_wszDescr;
+
+ int id = InterlockedIncrement(&m_msgId);
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/im/sendIM", &CIcqProto::OnSendMessage);
+
+ auto *pOwn = new IcqOwnMessage(pTransfer->pfts.hContact, id, pReq->m_reqId);
+ pReq->pUserInfo = pOwn;
+ {
+ mir_cslock lck(m_csOwnIds);
+ m_arOwnIds.insert(pOwn);
+ }
+
+ pReq << AIMSID(this) << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("k", appId()) << CHAR_PARAM("mentions", "") << WCHAR_PARAM("message", wszUrl)
+ << CHAR_PARAM("offlineIM", "true") << WCHAR_PARAM("parts", wszParts) << WCHAR_PARAM("t", GetUserId(pTransfer->pfts.hContact)) << INT_PARAM("ts", TS());
+ Push(pReq);
+
+ delete pTransfer;
+ return;
+ }
+
+ // else send the next portion
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, pTransfer->m_szHost, &CIcqProto::OnFileContinue);
+ pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("client", "icq") << CHAR_PARAM("k", appId()) << INT_PARAM("ts", TS());
+ CalcHash(pReq);
+ pReq->m_szUrl.AppendChar('?');
+ pReq->m_szUrl += pReq->m_szParam; pReq->m_szParam.Empty();
+ pReq->pUserInfo = pTransfer;
+ pTransfer->FillHeaders(pReq);
+ Push(pReq);
+
+ pTransfer->pfts.currentFileTime = time(0);
+ ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, pTransfer, (LPARAM)&pTransfer->pfts);
+}
+
+void CIcqProto::OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pOld)
+{
+ IcqFileTransfer *pTransfer = (IcqFileTransfer*)pOld->pUserInfo;
+ if (pTransfer->m_bCanceled) {
+LBL_Error:
+ ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, pTransfer);
+ delete pTransfer;
+ return;
+ }
+
+ FileReply root(pReply);
+ if (root.error() != 200)
+ goto LBL_Error;
+
+ ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, pTransfer);
+ pTransfer->pfts.currentFileTime = time(0);
+
+ const JSONNode &data = root.data();
+ CMStringW wszHost(data["host"].as_mstring());
+ CMStringW wszUrl(data["url"].as_mstring());
+ pTransfer->m_szHost = L"https://" + wszHost + wszUrl;
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, pTransfer->m_szHost, &CIcqProto::OnFileContinue);
+ pReq << CHAR_PARAM("a", m_szAToken) << CHAR_PARAM("client", "icq") << CHAR_PARAM("k", appId()) << INT_PARAM("ts", TS());
+ CalcHash(pReq);
+ pReq->m_szUrl.AppendChar('?');
+ pReq->m_szUrl += pReq->m_szParam; pReq->m_szParam.Empty();
+ pReq->pUserInfo = pTransfer;
+ pTransfer->FillHeaders(pReq);
+ Push(pReq);
+
+ ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, pTransfer, (LPARAM)&pTransfer->pfts);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Support for stickers
+
+void CIcqProto::OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ if (pReply->resultCode != 200) {
+ debugLogA("Error getting sticker: %d", pReply->resultCode);
+ return;
+ }
+
+ CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName);
+ CreateDirectoryTreeW(wszPath);
+
+ CMStringW wszFileName(FORMAT, L"%s\\STK{%s}.png", wszPath.c_str(), pReq->pUserInfo);
+ FILE *out = _wfopen(wszFileName, L"wb");
+ fwrite(pReply->pData, 1, pReply->dataLength, out);
+ fclose(out);
+
+ SMADD_CONT cont = { 1, m_szModuleName, wszFileName };
+ CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// File info request
+
+void CIcqProto::OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ IcqFileInfo **res = (IcqFileInfo **)pReq->pUserInfo;
+ *res = nullptr;
+
+ RobustReply root(pReply);
+ if (root.error() != 200)
+ return;
+
+ auto &pData = root.result();
+ auto &pInfo = pData["info"] ;
+ std::string szUrl(pInfo["dlink"].as_string());
+ if (szUrl.empty())
+ return;
+
+ MarkAsRead(pReq->hContact);
+
+ bool bIsSticker;
+ CMStringW wszDescr(pInfo["file_name"].as_mstring());
+ if (!mir_wstrncmp(wszDescr, L"dnld", 4)) {
+ bIsSticker = true;
+
+ std::string szPreview = pData["previews"]["192"].as_string();
+ if (!szPreview.empty())
+ szUrl = szPreview;
+ }
+ else bIsSticker = false;
+
+ mir_urlDecode(&*szUrl.begin());
+
+ *res = new IcqFileInfo(szUrl, wszDescr, pInfo["file_size"].as_int());
+ res[0]->bIsSticker = bIsSticker;
+}
+
+void CIcqProto::OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ auto *ft = (IcqFileTransfer*)pReq->pUserInfo;
+
+ if (pReply->resultCode != 200) {
+LBL_Error:
+ FileCancel(pReq->hContact, ft);
+ return;
+ }
+
+ ft->hWaitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ if (ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, ft, (LPARAM)&ft->pfts))
+ WaitForSingleObject(ft->hWaitEvent, INFINITE);
+ CloseHandle(ft->hWaitEvent);
+
+ debugLogW(L"Saving to [%s]", ft->pfts.szCurrentFile.w);
+ int fileId = _wopen(ft->pfts.szCurrentFile.w, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE);
+ if (fileId == -1) {
+ debugLogW(L"Cannot open [%s] for writing", ft->pfts.szCurrentFile.w);
+ goto LBL_Error;
+ }
+
+ int result = _write(fileId, pReply->pData, pReply->dataLength);
+ _close(fileId);
+ if (result != pReply->dataLength) {
+ debugLogW(L"Error writing data into [%s]", ft->pfts.szCurrentFile.w);
+ goto LBL_Error;
+ }
+
+ ft->pfts.totalProgress += pReply->dataLength;
+ ft->pfts.currentFileProgress += pReply->dataLength;
+ ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->pfts);
+
+ ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft);
+ delete ft;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+{
+ RobustReply root(pReply);
+ if (root.error() != 20000)
+ return;
+
+ auto &results = root.results();
+ m_szRToken = results["authToken"].as_mstring();
+}
+
+void CIcqProto::OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ RobustReply root(pReply);
+ if (root.error() != 20000)
+ return;
+
+ __int64 lastMsgId = getId(pReq->hContact, DB_KEY_LASTMSGID);
+
+ int count = 0;
+ auto &results = root.results();
+ for (auto &it : results["messages"]) {
+ ParseMessage(pReq->hContact, lastMsgId, it, pReq->pUserInfo != nullptr, false);
+ count++;
+ }
+
+ setId(pReq->hContact, DB_KEY_LASTMSGID, lastMsgId);
+
+ if (count >= 999)
+ RetrieveUserHistory(pReq->hContact, lastMsgId, pReq->pUserInfo != nullptr);
+}
+
+void CIcqProto::OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ RobustReply root(pReply);
+ if (root.error() != 20000) {
+ ProtoBroadcastAck(pReq->hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, nullptr);
+ return;
+ }
+
+ ParseBuddyInfo(root.results(), pReq->hContact, true);
+
+ ProtoBroadcastAck(pReq->hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, nullptr);
+}
+
+void CIcqProto::OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ break;
+
+ case 451:
+ // session forcibly closed from site
+ delSetting(DB_KEY_ATOKEN);
+ delSetting(DB_KEY_SESSIONKEY);
+ CheckPassword();
+ return;
+
+ case 401:
+ delSetting(DB_KEY_ATOKEN);
+ delSetting(DB_KEY_SESSIONKEY);
+ if (root.detail() == 1002) // session expired
+ CheckPassword();
+ else
+ ConnectionFailed(LOGINERR_WRONGPASSWORD, root.error());
+ return;
+
+ case 400:
+ if (root.detail() == 1015 && m_iTimeShift == 0) { // wrong timestamp
+ JSONNode &data = root.data();
+ int srvTS = data["ts"].as_int();
+ m_iTimeShift = (srvTS) ? time(0) - srvTS : 0;
+ StartSession();
+ return;
+ }
+ __fallthrough;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error());
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_fetchBaseURL = data["fetchBaseURL"].as_mstring();
+ m_aimsid = data["aimsid"].as_mstring();
+
+ ProcessMyInfo(data["myInfo"]);
+
+ int srvTS = data["ts"].as_int();
+ m_iTimeShift = (srvTS) ? time(0) - srvTS : 0;
+
+ OnLoggedIn();
+
+ for (auto &it : data["events"])
+ ProcessEvent(it);
+
+ ForkThread(&CIcqProto::PollThread);
+}
+
+void CIcqProto::OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ PROTO_AVATAR_INFORMATION ai = {};
+ ai.hContact = pReq->hContact;
+
+ if (pReply->resultCode != 200 || pReply->pData == nullptr) {
+LBL_Error:
+ ProtoBroadcastAck(pReq->hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE(&ai), 0);
+ return;
+ }
+
+ const char *szContentType = Netlib_GetHeader(pReply, "Content-Type");
+ if (szContentType == nullptr)
+ szContentType = "image/jpeg";
+
+ ai.format = ProtoGetAvatarFormatByMimeType(szContentType);
+ setByte(pReq->hContact, "AvatarType", ai.format);
+ GetAvatarFileName(pReq->hContact, ai.filename, _countof(ai.filename));
+
+ FILE *out = _wfopen(ai.filename, L"wb");
+ if (out == nullptr)
+ goto LBL_Error;
+
+ fwrite(pReply->pData, pReply->dataLength, 1, out);
+ fclose(out);
+
+ if (pReq->hContact != 0) {
+ ProtoBroadcastAck(pReq->hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE(&ai), 0);
+ debugLogW(L"Broadcast new avatar: %s", ai.filename);
+ }
+ else ReportSelfAvatarChanged();
+}
+
+void CIcqProto::OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ RobustReply root(pReply);
+ if (root.error() != 20000) {
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)pReq, 0);
+ return;
+ }
+
+ const JSONNode &results = root.results();
+
+ PROTOSEARCHRESULT psr = {};
+ psr.cbSize = sizeof(psr);
+ psr.flags = PSR_UNICODE;
+ for (auto &it : results["persons"]) {
+ CMStringW wszId = it["sn"].as_mstring();
+ if (wszId == m_szOwnId)
+ continue;
+
+ CMStringW wszNick = it["friendly"].as_mstring();
+ CMStringW wszFirst = it["firstName"].as_mstring();
+ CMStringW wszLast = it["lastName"].as_mstring();
+
+ psr.id.w = wszId.GetBuffer();
+ psr.nick.w = wszNick.GetBuffer();
+ psr.firstName.w = wszFirst.GetBuffer();
+ psr.lastName.w = wszLast.GetBuffer();
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)pReq, LPARAM(&psr));
+ }
+
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)pReq);
+}
+
+void CIcqProto::OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ IcqOwnMessage *ownMsg = (IcqOwnMessage*)pReq->pUserInfo;
+
+ JsonReply root(pReply);
+ if (root.error() != 200) {
+ ProtoBroadcastAck(ownMsg->m_hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)ownMsg->m_msgid, 0);
+
+ mir_cslock lck(m_csOwnIds);
+ m_arOwnIds.remove(ownMsg);
+ }
+
+ if (g_bMessageState)
+ CallService(MS_MESSAGESTATE_UPDATE, ownMsg->m_hContact, MRD_TYPE_DELIVERED);
+
+ const JSONNode &data = root.data();
+ CMStringA reqId(root.requestId());
+ CMStringA msgId(data["histMsgId"].as_mstring());
+ CheckOwnMessage(reqId, msgId, false);
+ CheckLastId(ownMsg->m_hContact, data);
+}
+
+void CIcqProto::OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ JsonReply root(pReply);
+ if (root.error() == 200) {
+ m_szAToken.Empty();
+ delSetting(DB_KEY_ATOKEN);
+
+ m_szSessionKey.Empty();
+ delSetting(DB_KEY_SESSIONKEY);
+
+ ShutdownSession();
+ }
+}
diff --git a/protocols/ICQ-WIM/src/stdafx.cxx b/protocols/ICQ-WIM/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/protocols/ICQ-WIM/src/stdafx.cxx +++ b/protocols/ICQ-WIM/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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" +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/protocols/ICQ-WIM/src/stdafx.h b/protocols/ICQ-WIM/src/stdafx.h index 465a385f02..b45810a867 100644 --- a/protocols/ICQ-WIM/src/stdafx.h +++ b/protocols/ICQ-WIM/src/stdafx.h @@ -1,109 +1,109 @@ -// ---------------------------------------------------------------------------80 -// ICQ plugin for Miranda Instant Messenger -// ________________________________________ -// -// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede -// Copyright © 2001-2002 Jon Keating, Richard Hughes -// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater -// Copyright © 2004-2010 Joe Kucera -// Copyright © 2012-2022 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- -// DESCRIPTION: -// -// Includes all header files that should be precompiled to speed up compilation. -// ----------------------------------------------------------------------------- - -#pragma once - -// Windows includes -#include <windows.h> - -// Standard includes -#include <stdio.h> -#include <time.h> -#include <io.h> -#include <malloc.h> -#include <direct.h> -#include <fcntl.h> -#include <process.h> - -// Miranda IM SDK includes -#include <newpluginapi.h> // This must be included first -#include <m_avatars.h> -#include <m_chat_int.h> -#include <m_clistint.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_gui.h> -#include <m_idle.h> -#include <m_icolib.h> -#include <m_ignore.h> -#include <m_json.h> -#include <m_langpack.h> -#include <m_message.h> -#include <m_messagestate.h> -#include <m_netlib.h> -#include <m_protocols.h> -#include <m_protosvc.h> -#include <m_options.h> -#include <m_popup.h> -#include <m_skin.h> -#include <m_smileyadd.h> -#include <m_system.h> -#include <m_timezones.h> -#include <m_userinfo.h> -#include <m_utils.h> - -#include <openssl/evp.h> -#include <openssl/hmac.h> -#include <openssl/rand.h> -#include <openssl/sha.h> - -// Project resources -#include "resource.h" - -// ICQ plugin includes -#include "version.h" - -#define MODULENAME "ICQ" - -#define DB_KEY_ID "aimId" -#define DB_KEY_IDLE "IdleTS" -#define DB_KEY_ABOUT "About" -#define DB_KEY_PHONE "Phone" -#define DB_KEY_ATOKEN "AToken" -#define DB_KEY_ICQNICK "IcqNick" -#define DB_KEY_PHONEREG "PhoneReg" -#define DB_KEY_LASTSEEN "LastSeen" -#define DB_KEY_ONLINETS "OnlineTS" -#define DB_KEY_PATCHVER "PatchVersion" -#define DB_KEY_RCLIENTID "RClientID" -#define DB_KEY_LASTMSGID "LastMsgId" -#define DB_KEY_REMOTEREAD "RemoteReadId" -#define DB_KEY_SESSIONKEY "SessionKey" -#define DB_KEY_MEMBERSINCE "MemberSince" - -#include "http.h" -#include "proto.h" - -bool IsChat(const CMStringW &aimid); -bool IsValidType(const JSONNode &aimid); - -void RefreshGroups(void); -wchar_t* time2text(time_t time); - -extern bool g_bSecureIM, g_bMessageState; +// ---------------------------------------------------------------------------80
+// ICQ plugin for Miranda Instant Messenger
+// ________________________________________
+//
+// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede
+// Copyright © 2001-2002 Jon Keating, Richard Hughes
+// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater
+// Copyright © 2004-2010 Joe Kucera
+// Copyright © 2012-2023 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+// DESCRIPTION:
+//
+// Includes all header files that should be precompiled to speed up compilation.
+// -----------------------------------------------------------------------------
+
+#pragma once
+
+// Windows includes
+#include <windows.h>
+
+// Standard includes
+#include <stdio.h>
+#include <time.h>
+#include <io.h>
+#include <malloc.h>
+#include <direct.h>
+#include <fcntl.h>
+#include <process.h>
+
+// Miranda IM SDK includes
+#include <newpluginapi.h> // This must be included first
+#include <m_avatars.h>
+#include <m_chat_int.h>
+#include <m_clistint.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_gui.h>
+#include <m_idle.h>
+#include <m_icolib.h>
+#include <m_ignore.h>
+#include <m_json.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_messagestate.h>
+#include <m_netlib.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_skin.h>
+#include <m_smileyadd.h>
+#include <m_system.h>
+#include <m_timezones.h>
+#include <m_userinfo.h>
+#include <m_utils.h>
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+// Project resources
+#include "resource.h"
+
+// ICQ plugin includes
+#include "version.h"
+
+#define MODULENAME "ICQ"
+
+#define DB_KEY_ID "aimId"
+#define DB_KEY_IDLE "IdleTS"
+#define DB_KEY_ABOUT "About"
+#define DB_KEY_PHONE "Phone"
+#define DB_KEY_ATOKEN "AToken"
+#define DB_KEY_ICQNICK "IcqNick"
+#define DB_KEY_PHONEREG "PhoneReg"
+#define DB_KEY_LASTSEEN "LastSeen"
+#define DB_KEY_ONLINETS "OnlineTS"
+#define DB_KEY_PATCHVER "PatchVersion"
+#define DB_KEY_RCLIENTID "RClientID"
+#define DB_KEY_LASTMSGID "LastMsgId"
+#define DB_KEY_REMOTEREAD "RemoteReadId"
+#define DB_KEY_SESSIONKEY "SessionKey"
+#define DB_KEY_MEMBERSINCE "MemberSince"
+
+#include "http.h"
+#include "proto.h"
+
+bool IsChat(const CMStringW &aimid);
+bool IsValidType(const JSONNode &aimid);
+
+void RefreshGroups(void);
+wchar_t* time2text(time_t time);
+
+extern bool g_bSecureIM, g_bMessageState;
diff --git a/protocols/ICQ-WIM/src/userinfo.cpp b/protocols/ICQ-WIM/src/userinfo.cpp index 887d9c2d92..61f661a31b 100644 --- a/protocols/ICQ-WIM/src/userinfo.cpp +++ b/protocols/ICQ-WIM/src/userinfo.cpp @@ -1,64 +1,64 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -struct IcqUserInfoDlg : public CUserInfoPageDlg -{ - CIcqProto *ppro; - - IcqUserInfoDlg(CIcqProto *_ppro) : - CUserInfoPageDlg(g_plugin, IDD_INFO_ICQ), - ppro(_ppro) - { - } - - bool OnRefresh() override - { - SetDlgItemTextW(m_hwnd, IDC_UIN, ppro->GetUserId(m_hContact)); - SetDlgItemTextW(m_hwnd, IDC_NICK, ppro->getMStringW(m_hContact, DB_KEY_ICQNICK)); - SetDlgItemTextW(m_hwnd, IDC_PHONE, ppro->getMStringW(m_hContact, DB_KEY_PHONE)); - - SetDlgItemTextW(m_hwnd, IDC_IDLETIME, time2text(ppro->getDword(m_hContact, DB_KEY_IDLE))); - SetDlgItemTextW(m_hwnd, IDC_LASTSEEN, time2text(ppro->getDword(m_hContact, DB_KEY_LASTSEEN))); - SetDlgItemTextW(m_hwnd, IDC_MEMBERSINCE, time2text(ppro->getDword(m_hContact, DB_KEY_MEMBERSINCE))); - SetDlgItemTextW(m_hwnd, IDC_ONLINESINCE, time2text(time(0) - ppro->getDword(m_hContact, DB_KEY_ONLINETS))); - return false; - } -}; - -int CIcqProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact) -{ - if (hContact && mir_strcmp(Proto_GetBaseAccountName(hContact), m_szModuleName)) - return 0; - - if (isChatRoom(hContact)) - return 0; - - USERINFOPAGE uip = {}; - uip.flags = ODPF_UNICODE | ODPF_USERINFOTAB | ODPF_DONTTRANSLATE | ODPF_ICON; - uip.dwInitParam = (LPARAM)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE); - uip.szTitle.w = L"ICQ"; - uip.szGroup.w = m_tszUserName; - uip.position = -1900000000; - uip.pDialog = new IcqUserInfoDlg(this); - g_plugin.addUserInfo(wParam, &uip); - return 0; -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+struct IcqUserInfoDlg : public CUserInfoPageDlg
+{
+ CIcqProto *ppro;
+
+ IcqUserInfoDlg(CIcqProto *_ppro) :
+ CUserInfoPageDlg(g_plugin, IDD_INFO_ICQ),
+ ppro(_ppro)
+ {
+ }
+
+ bool OnRefresh() override
+ {
+ SetDlgItemTextW(m_hwnd, IDC_UIN, ppro->GetUserId(m_hContact));
+ SetDlgItemTextW(m_hwnd, IDC_NICK, ppro->getMStringW(m_hContact, DB_KEY_ICQNICK));
+ SetDlgItemTextW(m_hwnd, IDC_PHONE, ppro->getMStringW(m_hContact, DB_KEY_PHONE));
+
+ SetDlgItemTextW(m_hwnd, IDC_IDLETIME, time2text(ppro->getDword(m_hContact, DB_KEY_IDLE)));
+ SetDlgItemTextW(m_hwnd, IDC_LASTSEEN, time2text(ppro->getDword(m_hContact, DB_KEY_LASTSEEN)));
+ SetDlgItemTextW(m_hwnd, IDC_MEMBERSINCE, time2text(ppro->getDword(m_hContact, DB_KEY_MEMBERSINCE)));
+ SetDlgItemTextW(m_hwnd, IDC_ONLINESINCE, time2text(time(0) - ppro->getDword(m_hContact, DB_KEY_ONLINETS)));
+ return false;
+ }
+};
+
+int CIcqProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact)
+{
+ if (hContact && mir_strcmp(Proto_GetBaseAccountName(hContact), m_szModuleName))
+ return 0;
+
+ if (isChatRoom(hContact))
+ return 0;
+
+ USERINFOPAGE uip = {};
+ uip.flags = ODPF_UNICODE | ODPF_USERINFOTAB | ODPF_DONTTRANSLATE | ODPF_ICON;
+ uip.dwInitParam = (LPARAM)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE);
+ uip.szTitle.w = L"ICQ";
+ uip.szGroup.w = m_tszUserName;
+ uip.position = -1900000000;
+ uip.pDialog = new IcqUserInfoDlg(this);
+ g_plugin.addUserInfo(wParam, &uip);
+ return 0;
+}
diff --git a/protocols/ICQ-WIM/src/utils.cpp b/protocols/ICQ-WIM/src/utils.cpp index 4fa65d0302..dfa73b8fb4 100644 --- a/protocols/ICQ-WIM/src/utils.cpp +++ b/protocols/ICQ-WIM/src/utils.cpp @@ -1,396 +1,396 @@ -// ----------------------------------------------------------------------------- -// ICQ plugin for Miranda NG -// ----------------------------------------------------------------------------- -// Copyright © 2018-22 Miranda NG team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// ----------------------------------------------------------------------------- - -#include "stdafx.h" - -void CIcqProto::InitContactCache() -{ - mir_cslock l(m_csCache); - for (auto &it : AccContacts()) { - if (isChatRoom(it)) - continue; - - // that was previously an ICQ contact - ptrW wszUin(GetUIN(it)); - if (wszUin != nullptr) { - delSetting(it, "UIN"); - setWString(it, DB_KEY_ID, wszUin); - } - // that was previously a MRA contact - else { - CMStringW wszEmail(getMStringW(it, "e-mail")); - if (!wszEmail.IsEmpty()) { - delSetting(it, "e-mail"); - setWString(it, DB_KEY_ID, wszEmail); - } - } - - CMStringW wszId = GetUserId(it); - auto *pCache = FindContactByUIN(wszId); - if (pCache == nullptr) { - pCache = new IcqCacheItem(wszId, it); - m_arCache.insert(pCache); - } - pCache->m_iProcessedMsgId = getId(it, DB_KEY_LASTMSGID); - } -} - -IcqCacheItem* CIcqProto::FindContactByUIN(const CMStringW &wszId) -{ - IcqCacheItem tmp(wszId, -1); - - mir_cslock l(m_csCache); - return m_arCache.find(&tmp); -} - -wchar_t* CIcqProto::GetUIN(MCONTACT hContact) -{ - DBVARIANT dbv = {}; - if (!db_get(hContact, m_szModuleName, "UIN", &dbv)) { - switch (dbv.type) { - case DBVT_DWORD: - wchar_t buf[40], *ret; - _itow_s(dbv.dVal, buf, 10); - return mir_wstrdup(buf); - - case DBVT_ASCIIZ: - ret = mir_a2u(dbv.pszVal); - db_free(&dbv); - return ret; - - case DBVT_UTF8: - ret = mir_utf8decodeW(dbv.pszVal); - db_free(&dbv); - return ret; - - case DBVT_WCHAR: - return dbv.pwszVal; - } - db_free(&dbv); - } - return nullptr; -} - -MCONTACT CIcqProto::CreateContact(const CMStringW &wszId, bool bTemporary) -{ - auto *pCache = FindContactByUIN(wszId); - if (pCache != nullptr) - return pCache->m_hContact; - - MCONTACT hContact = db_add_contact(); - setWString(hContact, DB_KEY_ID, wszId); - Proto_AddToContact(hContact, m_szModuleName); - RetrieveUserInfo(hContact); - - if (bTemporary) - Contact::RemoveFromList(hContact); - - return hContact; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::CalcHash(AsyncHttpRequest *pReq) -{ - CMStringA hashData(FORMAT, "%s&%s&%s", - pReq->requestType == REQUEST_POST ? "POST" : "GET", - mir_urlEncode(pReq->m_szUrl).c_str(), mir_urlEncode(pReq->m_szParam).c_str()); - - unsigned int len; - uint8_t hashOut[MIR_SHA256_HASH_SIZE]; - HMAC(EVP_sha256(), m_szSessionKey, m_szSessionKey.GetLength(), (uint8_t*)hashData.c_str(), hashData.GetLength(), hashOut, &len); - pReq << CHAR_PARAM("sig_sha256", ptrA(mir_base64_encode(hashOut, sizeof(hashOut)))); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CIcqProto::Json2int(MCONTACT hContact, const JSONNode &node, const char *szJson, const char *szSetting, bool bIsPartial) -{ - const JSONNode &var = node[szJson]; - if (var) - setDword(hContact, szSetting, var.as_int()); - else if (!bIsPartial) - delSetting(hContact, szSetting); -} - -void CIcqProto::Json2string(MCONTACT hContact, const JSONNode &node, const char *szJson, const char *szSetting, bool bIsPartial) -{ - const JSONNode &var = node[szJson]; - if (var) { - CMStringW wszStr(var.as_mstring()); - if (wszStr == L"[deleted]") { - setByte(hContact, "IcqDeleted", 1); - Contact::PutOnList(hContact); - } - else setWString(hContact, szSetting, wszStr); - } - else if (!bIsPartial) - delSetting(hContact, szSetting); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Avatars - -void CIcqProto::GetAvatarFileName(MCONTACT hContact, wchar_t* pszDest, size_t cbLen) -{ - int tPathLen = mir_snwprintf(pszDest, cbLen, L"%s\\%S", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName); - CreateDirectoryTreeW(pszDest); - pszDest[tPathLen++] = '\\'; - - CMStringW wszFileName(getMStringW(hContact, "IconId")); - const wchar_t* szFileType = ProtoGetAvatarExtension(getByte(hContact, "AvatarType", PA_FORMAT_PNG)); - mir_snwprintf(pszDest + tPathLen, MAX_PATH - tPathLen, L"%s%s", wszFileName.c_str(), szFileType); -} - -INT_PTR __cdecl CIcqProto::GetAvatar(WPARAM wParam, LPARAM lParam) -{ - wchar_t *buf = (wchar_t*)wParam; - int size = (int)lParam; - if (buf == nullptr || size <= 0) - return -1; - - GetAvatarFileName(0, buf, size); - return 0; -} - -INT_PTR __cdecl CIcqProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam) -{ - switch (wParam) { - case AF_MAXSIZE: - ((POINT*)lParam)->x = -1; - ((POINT*)lParam)->y = -1; - return 0; - - case AF_FORMATSUPPORTED: // nobody - return 1; - - case AF_DELAYAFTERFAIL: - return 10 * 60 * 1000; - - case AF_ENABLED: - case AF_FETCHIFPROTONOTVISIBLE: - case AF_FETCHIFCONTACTOFFLINE: - return 1; - } - return 0; -} - -INT_PTR __cdecl CIcqProto::GetAvatarInfo(WPARAM, LPARAM lParam) -{ - PROTO_AVATAR_INFORMATION* pai = (PROTO_AVATAR_INFORMATION*)lParam; - - ptrW szIconId(getWStringA(pai->hContact, "IconId")); - if (szIconId == nullptr) { - debugLogA("No avatar"); - return GAIR_NOAVATAR; - } - - GetAvatarFileName(pai->hContact, pai->filename, _countof(pai->filename)); - pai->format = getByte(pai->hContact, "AvatarType", 0); - - if (::_waccess(pai->filename, 0) == 0) - return GAIR_SUCCESS; - - debugLogA("No avatar"); - return GAIR_NOAVATAR; -} - -INT_PTR __cdecl CIcqProto::SetAvatar(WPARAM, LPARAM lParam) -{ - wchar_t* pwszFileName = (wchar_t*)lParam; - - wchar_t wszOldName[MAX_PATH]; - GetAvatarFileName(0, wszOldName, _countof(wszOldName)); - _wremove(wszOldName); - - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/expressions/upload"); - pReq->m_szUrl.AppendFormat("?f=json&aimsid=%s&r=%s&type=largeBuddyIcon", mir_urlEncode(m_aimsid.c_str()).c_str(), pReq->m_reqId); - - if (pwszFileName == nullptr) - delSetting("AvatarHash"); - else { - int fileId = _wopen(pwszFileName, _O_RDONLY | _O_BINARY, _S_IREAD); - if (fileId < 0) { - delete pReq; - return 1; - } - - unsigned dwSize = (unsigned)_filelengthi64(fileId); - char* pData = (char*)mir_alloc(dwSize); - if (pData == nullptr) { - _close(fileId); - delete pReq; - return 2; - } - - _read(fileId, pData, dwSize); - _close(fileId); - - pReq->pData = pData; - pReq->dataLength = dwSize; - - int iAvatarType = ProtoGetBufferFormat(pData); - if (iAvatarType == PA_FORMAT_UNKNOWN) { - delete pReq; - delete pData; - return 3; - } - - pReq->AddHeader("Content-Type", ProtoGetAvatarMimeType(iAvatarType)); - } - Push(pReq); - - return 0; // TODO -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CMStringW CIcqProto::GetUserId(MCONTACT hContact) -{ - if (isChatRoom(hContact)) - return getMStringW(hContact, "ChatRoomID"); - - return getMStringW(hContact, DB_KEY_ID); -} - -bool IsChat(const CMStringW &aimid) -{ - return aimid.Right(11) == "@chat.agent"; -} - -bool IsValidType(const JSONNode &n) -{ - auto type = n["userType"].as_string(); - return type == "icq" || type == "aim" || type == "interop" || type == ""; -} - -int CIcqProto::StatusFromPresence(const JSONNode &presence, MCONTACT hContact) -{ - CMStringW wszStatus = presence["state"].as_mstring(); - int iStatus; - if (wszStatus == L"online") - iStatus = ID_STATUS_ONLINE; - else if (wszStatus == L"offline") - iStatus = ID_STATUS_OFFLINE; - else if (wszStatus == L"n/a") - iStatus = ID_STATUS_NA; - else if (wszStatus == L"away") - iStatus = ID_STATUS_AWAY; - else if (wszStatus == L"occupied") - iStatus = ID_STATUS_OCCUPIED; - else if (wszStatus == L"dnd") - iStatus = ID_STATUS_DND; - else - iStatus = -1; - - int iLastSeen = presence["lastseen"].as_int(); - if (iLastSeen != 0) - setDword(hContact, DB_KEY_LASTSEEN, iLastSeen); - else if (getDword(hContact, DB_KEY_ONLINETS)) - iStatus = ID_STATUS_ONLINE; - - return iStatus; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -__int64 CIcqProto::getId(MCONTACT hContact, const char *szSetting) -{ - DBVARIANT dbv; - dbv.type = DBVT_BLOB; - if (db_get(hContact, m_szModuleName, szSetting, &dbv)) - return 0; - - __int64 result = (dbv.cpbVal == sizeof(__int64)) ? *(__int64*)dbv.pbVal : 0; - db_free(&dbv); - return result; -} - -void CIcqProto::setId(MCONTACT hContact, const char *szSetting, __int64 iValue) -{ - __int64 oldVal = getId(hContact, szSetting); - if (oldVal != iValue) - db_set_blob(hContact, m_szModuleName, szSetting, &iValue, sizeof(iValue)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -wchar_t* time2text(time_t ts) -{ - if (ts == 0) - return L""; - - static wchar_t buf[100]; - TimeZone_PrintTimeStamp(NULL, ts, L"D t", buf, _countof(buf), 0); - return buf; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_CONTEXTMENU: - PUDeletePopup(hWnd); - break; - - case WM_COMMAND: - CIcqProto *ppro = (CIcqProto*)PUGetPluginData(hWnd); - CallProtoService(ppro->m_szModuleName, PS_GOTO_INBOX); - PUDeletePopup(hWnd); - break; - } - - return DefWindowProc(hWnd, message, wParam, lParam); -} - -void CIcqProto::EmailNotification(const wchar_t *pwszText) -{ - POPUPDATAW Popup = {}; - Popup.lchIcon = g_plugin.getIcon(IDI_INBOX); - wcsncpy_s(Popup.lpwzText, pwszText, _TRUNCATE); - wcsncpy_s(Popup.lpwzContactName, m_tszUserName, _TRUNCATE); - Popup.iSeconds = 20; - Popup.PluginData = this; - Popup.PluginWindowProc = PopupDlgProc; - PUAddPopupW(&Popup); - - if (m_bUseTrayIcon) { - char szServiceFunction[MAX_PATH]; - if (m_bLaunchMailbox) - mir_snprintf(szServiceFunction, "%s%s", m_szModuleName, PS_GOTO_INBOX); - else - mir_snprintf(szServiceFunction, "%s%s", m_szModuleName, PS_DUMMY); - - int i = 0; - while (CLISTEVENT *pcle = g_clistApi.pfnGetEvent(-1, i++)) - if (!mir_strcmp(pcle->pszService, szServiceFunction)) - return; - - CLISTEVENT cle = {}; - cle.hDbEvent = ICQ_FAKE_EVENT_ID; - cle.moduleName = m_szModuleName; - cle.hIcon = g_plugin.getIcon(IDI_INBOX); - cle.flags = CLEF_UNICODE | CLEF_PROTOCOLGLOBAL; - cle.pszService = szServiceFunction; - cle.szTooltip.w = pwszText; - g_clistApi.pfnAddEvent(&cle); - } -} +// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-23 Miranda NG team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+void CIcqProto::InitContactCache()
+{
+ mir_cslock l(m_csCache);
+ for (auto &it : AccContacts()) {
+ if (isChatRoom(it))
+ continue;
+
+ // that was previously an ICQ contact
+ ptrW wszUin(GetUIN(it));
+ if (wszUin != nullptr) {
+ delSetting(it, "UIN");
+ setWString(it, DB_KEY_ID, wszUin);
+ }
+ // that was previously a MRA contact
+ else {
+ CMStringW wszEmail(getMStringW(it, "e-mail"));
+ if (!wszEmail.IsEmpty()) {
+ delSetting(it, "e-mail");
+ setWString(it, DB_KEY_ID, wszEmail);
+ }
+ }
+
+ CMStringW wszId = GetUserId(it);
+ auto *pCache = FindContactByUIN(wszId);
+ if (pCache == nullptr) {
+ pCache = new IcqCacheItem(wszId, it);
+ m_arCache.insert(pCache);
+ }
+ pCache->m_iProcessedMsgId = getId(it, DB_KEY_LASTMSGID);
+ }
+}
+
+IcqCacheItem* CIcqProto::FindContactByUIN(const CMStringW &wszId)
+{
+ IcqCacheItem tmp(wszId, -1);
+
+ mir_cslock l(m_csCache);
+ return m_arCache.find(&tmp);
+}
+
+wchar_t* CIcqProto::GetUIN(MCONTACT hContact)
+{
+ DBVARIANT dbv = {};
+ if (!db_get(hContact, m_szModuleName, "UIN", &dbv)) {
+ switch (dbv.type) {
+ case DBVT_DWORD:
+ wchar_t buf[40], *ret;
+ _itow_s(dbv.dVal, buf, 10);
+ return mir_wstrdup(buf);
+
+ case DBVT_ASCIIZ:
+ ret = mir_a2u(dbv.pszVal);
+ db_free(&dbv);
+ return ret;
+
+ case DBVT_UTF8:
+ ret = mir_utf8decodeW(dbv.pszVal);
+ db_free(&dbv);
+ return ret;
+
+ case DBVT_WCHAR:
+ return dbv.pwszVal;
+ }
+ db_free(&dbv);
+ }
+ return nullptr;
+}
+
+MCONTACT CIcqProto::CreateContact(const CMStringW &wszId, bool bTemporary)
+{
+ auto *pCache = FindContactByUIN(wszId);
+ if (pCache != nullptr)
+ return pCache->m_hContact;
+
+ MCONTACT hContact = db_add_contact();
+ setWString(hContact, DB_KEY_ID, wszId);
+ Proto_AddToContact(hContact, m_szModuleName);
+ RetrieveUserInfo(hContact);
+
+ if (bTemporary)
+ Contact::RemoveFromList(hContact);
+
+ return hContact;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::CalcHash(AsyncHttpRequest *pReq)
+{
+ CMStringA hashData(FORMAT, "%s&%s&%s",
+ pReq->requestType == REQUEST_POST ? "POST" : "GET",
+ mir_urlEncode(pReq->m_szUrl).c_str(), mir_urlEncode(pReq->m_szParam).c_str());
+
+ unsigned int len;
+ uint8_t hashOut[MIR_SHA256_HASH_SIZE];
+ HMAC(EVP_sha256(), m_szSessionKey, m_szSessionKey.GetLength(), (uint8_t*)hashData.c_str(), hashData.GetLength(), hashOut, &len);
+ pReq << CHAR_PARAM("sig_sha256", ptrA(mir_base64_encode(hashOut, sizeof(hashOut))));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::Json2int(MCONTACT hContact, const JSONNode &node, const char *szJson, const char *szSetting, bool bIsPartial)
+{
+ const JSONNode &var = node[szJson];
+ if (var)
+ setDword(hContact, szSetting, var.as_int());
+ else if (!bIsPartial)
+ delSetting(hContact, szSetting);
+}
+
+void CIcqProto::Json2string(MCONTACT hContact, const JSONNode &node, const char *szJson, const char *szSetting, bool bIsPartial)
+{
+ const JSONNode &var = node[szJson];
+ if (var) {
+ CMStringW wszStr(var.as_mstring());
+ if (wszStr == L"[deleted]") {
+ setByte(hContact, "IcqDeleted", 1);
+ Contact::PutOnList(hContact);
+ }
+ else setWString(hContact, szSetting, wszStr);
+ }
+ else if (!bIsPartial)
+ delSetting(hContact, szSetting);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Avatars
+
+void CIcqProto::GetAvatarFileName(MCONTACT hContact, wchar_t* pszDest, size_t cbLen)
+{
+ int tPathLen = mir_snwprintf(pszDest, cbLen, L"%s\\%S", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName);
+ CreateDirectoryTreeW(pszDest);
+ pszDest[tPathLen++] = '\\';
+
+ CMStringW wszFileName(getMStringW(hContact, "IconId"));
+ const wchar_t* szFileType = ProtoGetAvatarExtension(getByte(hContact, "AvatarType", PA_FORMAT_PNG));
+ mir_snwprintf(pszDest + tPathLen, MAX_PATH - tPathLen, L"%s%s", wszFileName.c_str(), szFileType);
+}
+
+INT_PTR __cdecl CIcqProto::GetAvatar(WPARAM wParam, LPARAM lParam)
+{
+ wchar_t *buf = (wchar_t*)wParam;
+ int size = (int)lParam;
+ if (buf == nullptr || size <= 0)
+ return -1;
+
+ GetAvatarFileName(0, buf, size);
+ return 0;
+}
+
+INT_PTR __cdecl CIcqProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case AF_MAXSIZE:
+ ((POINT*)lParam)->x = -1;
+ ((POINT*)lParam)->y = -1;
+ return 0;
+
+ case AF_FORMATSUPPORTED: // nobody
+ return 1;
+
+ case AF_DELAYAFTERFAIL:
+ return 10 * 60 * 1000;
+
+ case AF_ENABLED:
+ case AF_FETCHIFPROTONOTVISIBLE:
+ case AF_FETCHIFCONTACTOFFLINE:
+ return 1;
+ }
+ return 0;
+}
+
+INT_PTR __cdecl CIcqProto::GetAvatarInfo(WPARAM, LPARAM lParam)
+{
+ PROTO_AVATAR_INFORMATION* pai = (PROTO_AVATAR_INFORMATION*)lParam;
+
+ ptrW szIconId(getWStringA(pai->hContact, "IconId"));
+ if (szIconId == nullptr) {
+ debugLogA("No avatar");
+ return GAIR_NOAVATAR;
+ }
+
+ GetAvatarFileName(pai->hContact, pai->filename, _countof(pai->filename));
+ pai->format = getByte(pai->hContact, "AvatarType", 0);
+
+ if (::_waccess(pai->filename, 0) == 0)
+ return GAIR_SUCCESS;
+
+ debugLogA("No avatar");
+ return GAIR_NOAVATAR;
+}
+
+INT_PTR __cdecl CIcqProto::SetAvatar(WPARAM, LPARAM lParam)
+{
+ wchar_t* pwszFileName = (wchar_t*)lParam;
+
+ wchar_t wszOldName[MAX_PATH];
+ GetAvatarFileName(0, wszOldName, _countof(wszOldName));
+ _wremove(wszOldName);
+
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/expressions/upload");
+ pReq->m_szUrl.AppendFormat("?f=json&aimsid=%s&r=%s&type=largeBuddyIcon", mir_urlEncode(m_aimsid.c_str()).c_str(), pReq->m_reqId);
+
+ if (pwszFileName == nullptr)
+ delSetting("AvatarHash");
+ else {
+ int fileId = _wopen(pwszFileName, _O_RDONLY | _O_BINARY, _S_IREAD);
+ if (fileId < 0) {
+ delete pReq;
+ return 1;
+ }
+
+ unsigned dwSize = (unsigned)_filelengthi64(fileId);
+ char* pData = (char*)mir_alloc(dwSize);
+ if (pData == nullptr) {
+ _close(fileId);
+ delete pReq;
+ return 2;
+ }
+
+ _read(fileId, pData, dwSize);
+ _close(fileId);
+
+ pReq->pData = pData;
+ pReq->dataLength = dwSize;
+
+ int iAvatarType = ProtoGetBufferFormat(pData);
+ if (iAvatarType == PA_FORMAT_UNKNOWN) {
+ delete pReq;
+ delete pData;
+ return 3;
+ }
+
+ pReq->AddHeader("Content-Type", ProtoGetAvatarMimeType(iAvatarType));
+ }
+ Push(pReq);
+
+ return 0; // TODO
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CMStringW CIcqProto::GetUserId(MCONTACT hContact)
+{
+ if (isChatRoom(hContact))
+ return getMStringW(hContact, "ChatRoomID");
+
+ return getMStringW(hContact, DB_KEY_ID);
+}
+
+bool IsChat(const CMStringW &aimid)
+{
+ return aimid.Right(11) == "@chat.agent";
+}
+
+bool IsValidType(const JSONNode &n)
+{
+ auto type = n["userType"].as_string();
+ return type == "icq" || type == "aim" || type == "interop" || type == "";
+}
+
+int CIcqProto::StatusFromPresence(const JSONNode &presence, MCONTACT hContact)
+{
+ CMStringW wszStatus = presence["state"].as_mstring();
+ int iStatus;
+ if (wszStatus == L"online")
+ iStatus = ID_STATUS_ONLINE;
+ else if (wszStatus == L"offline")
+ iStatus = ID_STATUS_OFFLINE;
+ else if (wszStatus == L"n/a")
+ iStatus = ID_STATUS_NA;
+ else if (wszStatus == L"away")
+ iStatus = ID_STATUS_AWAY;
+ else if (wszStatus == L"occupied")
+ iStatus = ID_STATUS_OCCUPIED;
+ else if (wszStatus == L"dnd")
+ iStatus = ID_STATUS_DND;
+ else
+ iStatus = -1;
+
+ int iLastSeen = presence["lastseen"].as_int();
+ if (iLastSeen != 0)
+ setDword(hContact, DB_KEY_LASTSEEN, iLastSeen);
+ else if (getDword(hContact, DB_KEY_ONLINETS))
+ iStatus = ID_STATUS_ONLINE;
+
+ return iStatus;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+__int64 CIcqProto::getId(MCONTACT hContact, const char *szSetting)
+{
+ DBVARIANT dbv;
+ dbv.type = DBVT_BLOB;
+ if (db_get(hContact, m_szModuleName, szSetting, &dbv))
+ return 0;
+
+ __int64 result = (dbv.cpbVal == sizeof(__int64)) ? *(__int64*)dbv.pbVal : 0;
+ db_free(&dbv);
+ return result;
+}
+
+void CIcqProto::setId(MCONTACT hContact, const char *szSetting, __int64 iValue)
+{
+ __int64 oldVal = getId(hContact, szSetting);
+ if (oldVal != iValue)
+ db_set_blob(hContact, m_szModuleName, szSetting, &iValue, sizeof(iValue));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+wchar_t* time2text(time_t ts)
+{
+ if (ts == 0)
+ return L"";
+
+ static wchar_t buf[100];
+ TimeZone_PrintTimeStamp(NULL, ts, L"D t", buf, _countof(buf), 0);
+ return buf;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_CONTEXTMENU:
+ PUDeletePopup(hWnd);
+ break;
+
+ case WM_COMMAND:
+ CIcqProto *ppro = (CIcqProto*)PUGetPluginData(hWnd);
+ CallProtoService(ppro->m_szModuleName, PS_GOTO_INBOX);
+ PUDeletePopup(hWnd);
+ break;
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+void CIcqProto::EmailNotification(const wchar_t *pwszText)
+{
+ POPUPDATAW Popup = {};
+ Popup.lchIcon = g_plugin.getIcon(IDI_INBOX);
+ wcsncpy_s(Popup.lpwzText, pwszText, _TRUNCATE);
+ wcsncpy_s(Popup.lpwzContactName, m_tszUserName, _TRUNCATE);
+ Popup.iSeconds = 20;
+ Popup.PluginData = this;
+ Popup.PluginWindowProc = PopupDlgProc;
+ PUAddPopupW(&Popup);
+
+ if (m_bUseTrayIcon) {
+ char szServiceFunction[MAX_PATH];
+ if (m_bLaunchMailbox)
+ mir_snprintf(szServiceFunction, "%s%s", m_szModuleName, PS_GOTO_INBOX);
+ else
+ mir_snprintf(szServiceFunction, "%s%s", m_szModuleName, PS_DUMMY);
+
+ int i = 0;
+ while (CLISTEVENT *pcle = g_clistApi.pfnGetEvent(-1, i++))
+ if (!mir_strcmp(pcle->pszService, szServiceFunction))
+ return;
+
+ CLISTEVENT cle = {};
+ cle.hDbEvent = ICQ_FAKE_EVENT_ID;
+ cle.moduleName = m_szModuleName;
+ cle.hIcon = g_plugin.getIcon(IDI_INBOX);
+ cle.flags = CLEF_UNICODE | CLEF_PROTOCOLGLOBAL;
+ cle.pszService = szServiceFunction;
+ cle.szTooltip.w = pwszText;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+}
diff --git a/protocols/ICQ-WIM/src/version.h b/protocols/ICQ-WIM/src/version.h index c1cb84630e..d5270a8de9 100644 --- a/protocols/ICQ-WIM/src/version.h +++ b/protocols/ICQ-WIM/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "ICQ protocol support for Miranda NG."
#define __AUTHOR "George Hazan"
#define __AUTHORWEB "https://miranda-ng.org/p/ICQ"
-#define __COPYRIGHT "© 2018-22 George Hazan"
+#define __COPYRIGHT "© 2018-23 George Hazan"
diff --git a/protocols/ICQCorp/src/stdafx.cxx b/protocols/ICQCorp/src/stdafx.cxx index 23ff0fd56d..2ee4e19cb6 100644 --- a/protocols/ICQCorp/src/stdafx.cxx +++ b/protocols/ICQCorp/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (c) 2014-22 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" +/*
+Copyright (c) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
diff --git a/protocols/ICQCorp/src/version.h b/protocols/ICQCorp/src/version.h index 4bc47a5b88..c6d8e28aa5 100644 --- a/protocols/ICQCorp/src/version.h +++ b/protocols/ICQCorp/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "ICQ corporate protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team, Eugene Tarasenko"
#define __AUTHORWEB "https://miranda-ng.org/p/ICQCorp"
-#define __COPYRIGHT "© 2014-22 Miranda NG team, 2003-2005 Eugene Tarasenko"
+#define __COPYRIGHT "© 2014-23 Miranda NG team, 2003-2005 Eugene Tarasenko"
diff --git a/protocols/IRCG/src/stdafx.cxx b/protocols/IRCG/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/protocols/IRCG/src/stdafx.cxx +++ b/protocols/IRCG/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/IRCG/src/version.h b/protocols/IRCG/src/version.h index 273e48ec22..e46b073e3c 100644 --- a/protocols/IRCG/src/version.h +++ b/protocols/IRCG/src/version.h @@ -7,7 +7,7 @@ #define __DESCRIPTION "Internet Relay Chat (IRC) protocol support for Miranda NG."
#define __AUTHOR "Miranda team"
-#define __COPYRIGHT "© 2003-22 Jurgen Persson, George Hazan"
+#define __COPYRIGHT "© 2003-23 Jurgen Persson, George Hazan"
#define __AUTHORWEB "https://miranda-ng.org/p/IRC"
#define __PLUGIN_NAME "IRC protocol"
diff --git a/protocols/JabberG/src/jabber.cpp b/protocols/JabberG/src/jabber.cpp index c0fdff4ccd..50dac791a6 100644 --- a/protocols/JabberG/src/jabber.cpp +++ b/protocols/JabberG/src/jabber.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_adhoc.cpp b/protocols/JabberG/src/jabber_adhoc.cpp index d5b6d429f1..1871262e16 100644 --- a/protocols/JabberG/src/jabber_adhoc.cpp +++ b/protocols/JabberG/src/jabber_adhoc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Artem Shpynov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
Module implements an XMPP protocol extension for reporting and executing ad-hoc,
human-oriented commands according to XEP-0050: Ad-Hoc Commands
diff --git a/protocols/JabberG/src/jabber_agent.cpp b/protocols/JabberG/src/jabber_agent.cpp index a198827672..a8720b5a56 100644 --- a/protocols/JabberG/src/jabber_agent.cpp +++ b/protocols/JabberG/src/jabber_agent.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_api.cpp b/protocols/JabberG/src/jabber_api.cpp index db750517f3..738e1bb439 100644 --- a/protocols/JabberG/src/jabber_api.cpp +++ b/protocols/JabberG/src/jabber_api.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_archive.cpp b/protocols/JabberG/src/jabber_archive.cpp index 97c76993ba..b60f98ac97 100644 --- a/protocols/JabberG/src/jabber_archive.cpp +++ b/protocols/JabberG/src/jabber_archive.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_bookmarks.cpp b/protocols/JabberG/src/jabber_bookmarks.cpp index 0c9944677a..48cc195e17 100644 --- a/protocols/JabberG/src/jabber_bookmarks.cpp +++ b/protocols/JabberG/src/jabber_bookmarks.cpp @@ -3,7 +3,7 @@ Jabber Protocol Plugin for Miranda NG
Copyright (c) 2007 Michael Stepura, George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_byte.cpp b/protocols/JabberG/src/jabber_byte.cpp index 685174347d..060b10ee6c 100644 --- a/protocols/JabberG/src/jabber_byte.cpp +++ b/protocols/JabberG/src/jabber_byte.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_byte.h b/protocols/JabberG/src/jabber_byte.h index 6e3f5b732b..5de0be588c 100644 --- a/protocols/JabberG/src/jabber_byte.h +++ b/protocols/JabberG/src/jabber_byte.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_caps.cpp b/protocols/JabberG/src/jabber_caps.cpp index e49f93d8a8..16ad003a75 100644 --- a/protocols/JabberG/src/jabber_caps.cpp +++ b/protocols/JabberG/src/jabber_caps.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h index fc37993fb0..e8ceccf4a0 100644 --- a/protocols/JabberG/src/jabber_caps.h +++ b/protocols/JabberG/src/jabber_caps.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_captcha.cpp b/protocols/JabberG/src/jabber_captcha.cpp index 641c310395..c2cc8ec2eb 100644 --- a/protocols/JabberG/src/jabber_captcha.cpp +++ b/protocols/JabberG/src/jabber_captcha.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_chat.cpp b/protocols/JabberG/src/jabber_chat.cpp index a32591e576..90bf730ac4 100644 --- a/protocols/JabberG/src/jabber_chat.cpp +++ b/protocols/JabberG/src/jabber_chat.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_console.cpp b/protocols/JabberG/src/jabber_console.cpp index 7f4ab9c19f..cbcfc0f19d 100644 --- a/protocols/JabberG/src/jabber_console.cpp +++ b/protocols/JabberG/src/jabber_console.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2007 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_disco.cpp b/protocols/JabberG/src/jabber_disco.cpp index cca24a8d29..50081898fd 100644 --- a/protocols/JabberG/src/jabber_disco.cpp +++ b/protocols/JabberG/src/jabber_disco.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_disco.h b/protocols/JabberG/src/jabber_disco.h index 48a01bfa93..5f3bd510df 100644 --- a/protocols/JabberG/src/jabber_disco.h +++ b/protocols/JabberG/src/jabber_disco.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2005-07 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_events.cpp b/protocols/JabberG/src/jabber_events.cpp index 043c438c95..da9f81c7bc 100644 --- a/protocols/JabberG/src/jabber_events.cpp +++ b/protocols/JabberG/src/jabber_events.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_file.cpp b/protocols/JabberG/src/jabber_file.cpp index b01b32f32a..f39cbbdf32 100644 --- a/protocols/JabberG/src/jabber_file.cpp +++ b/protocols/JabberG/src/jabber_file.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_form.cpp b/protocols/JabberG/src/jabber_form.cpp index e4e51658ea..e85e91566a 100644 --- a/protocols/JabberG/src/jabber_form.cpp +++ b/protocols/JabberG/src/jabber_form.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_ft.cpp b/protocols/JabberG/src/jabber_ft.cpp index 096351d713..0329d16747 100644 --- a/protocols/JabberG/src/jabber_ft.cpp +++ b/protocols/JabberG/src/jabber_ft.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_groupchat.cpp b/protocols/JabberG/src/jabber_groupchat.cpp index 7fd448657f..f4f3e6c008 100644 --- a/protocols/JabberG/src/jabber_groupchat.cpp +++ b/protocols/JabberG/src/jabber_groupchat.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_ibb.cpp b/protocols/JabberG/src/jabber_ibb.cpp index 22d1f2e41e..67ba5ba2c9 100644 --- a/protocols/JabberG/src/jabber_ibb.cpp +++ b/protocols/JabberG/src/jabber_ibb.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_ibb.h b/protocols/JabberG/src/jabber_ibb.h index b96ae971f9..f620b52ba4 100644 --- a/protocols/JabberG/src/jabber_ibb.h +++ b/protocols/JabberG/src/jabber_ibb.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_icolib.cpp b/protocols/JabberG/src/jabber_icolib.cpp index 4fe3a7d96c..7e08277418 100644 --- a/protocols/JabberG/src/jabber_icolib.cpp +++ b/protocols/JabberG/src/jabber_icolib.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
Idea & portions of code by Artem Shpynov
diff --git a/protocols/JabberG/src/jabber_icolib.h b/protocols/JabberG/src/jabber_icolib.h index 7d2c0b1ce2..9066560f1d 100644 --- a/protocols/JabberG/src/jabber_icolib.h +++ b/protocols/JabberG/src/jabber_icolib.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan
Copyright (c) 2007-09 Maxim Mluhov
Copyright (c) 2007-09 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_iq.cpp b/protocols/JabberG/src/jabber_iq.cpp index 9e1dc7dc20..850e755420 100644 --- a/protocols/JabberG/src/jabber_iq.cpp +++ b/protocols/JabberG/src/jabber_iq.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_iq.h b/protocols/JabberG/src/jabber_iq.h index 50694fc73e..01da599737 100644 --- a/protocols/JabberG/src/jabber_iq.h +++ b/protocols/JabberG/src/jabber_iq.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_iq_handlers.cpp b/protocols/JabberG/src/jabber_iq_handlers.cpp index d73785b9f5..bf9f7ad573 100644 --- a/protocols/JabberG/src/jabber_iq_handlers.cpp +++ b/protocols/JabberG/src/jabber_iq_handlers.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_iqid.cpp b/protocols/JabberG/src/jabber_iqid.cpp index df222552fa..68783083c7 100644 --- a/protocols/JabberG/src/jabber_iqid.cpp +++ b/protocols/JabberG/src/jabber_iqid.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_iqid_muc.cpp b/protocols/JabberG/src/jabber_iqid_muc.cpp index 8bf9621ab6..f811d7cb82 100644 --- a/protocols/JabberG/src/jabber_iqid_muc.cpp +++ b/protocols/JabberG/src/jabber_iqid_muc.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_libstr.cpp b/protocols/JabberG/src/jabber_libstr.cpp index 0c20ff4cc2..2b7e91aea5 100644 --- a/protocols/JabberG/src/jabber_libstr.cpp +++ b/protocols/JabberG/src/jabber_libstr.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_list.cpp b/protocols/JabberG/src/jabber_list.cpp index bbfbb1d35e..73abe09e2b 100644 --- a/protocols/JabberG/src/jabber_list.cpp +++ b/protocols/JabberG/src/jabber_list.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_list.h b/protocols/JabberG/src/jabber_list.h index 082d247350..e418131443 100644 --- a/protocols/JabberG/src/jabber_list.h +++ b/protocols/JabberG/src/jabber_list.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_mam.cpp b/protocols/JabberG/src/jabber_mam.cpp index 989630ac07..0665e5df2f 100644 --- a/protocols/JabberG/src/jabber_mam.cpp +++ b/protocols/JabberG/src/jabber_mam.cpp @@ -1,160 +1,160 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" -#include "jabber_iq.h" -#include "jabber_caps.h" - -void CJabberProto::OnIqResultMamInfo(const TiXmlElement *iqNode, CJabberIqInfo *pInfo) -{ - if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { - if (auto *n = XmlFirstChild(iqNode, "prefs")) { - m_bMamPrefsAvailable = true; - - if (auto *type = n->Attribute("default")) { - if (!strcmp(type, "never")) - m_iMamMode = 0; - else if (!strcmp(type, "roster")) - m_iMamMode = 1; - else - m_iMamMode = 2; - } - } - } - - // shall we retrieve missing messages? - if (pInfo->GetUserData()) - MamRetrieveMissingMessages(); -} - -void CJabberProto::MamSetMode(int iNewMode) -{ - if (!m_bEnableMam) - return; - - const char *szMode; - switch (iNewMode) { - case 0: szMode = "never"; break; - case 1: szMode = "roster"; break; - default: szMode = "always"; break; - } - - XmlNodeIq iq(AddIQ(&CJabberProto::OnIqResultMamInfo, JABBER_IQ_TYPE_SET)); - auto *node = iq << XCHILDNS("prefs", JABBER_FEAT_MAM) << XATTR("default", szMode); - node << XCHILD("always"); node << XCHILD("never"); - m_ThreadInfo->send(iq); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CJabberProto::MamRetrieveMissingMessages() -{ - CMStringA szLastId = getMStringA("LastMamId"); - - XmlNodeIq iq("set", SerialNext()); - auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM); - - if (szLastId.IsEmpty()) { - m_bMamDisableMessages = true; // our goal is to save message id, not to store messages - m_bMamCreateRead = false; - - char buf[100]; - time2str(time(0), buf, _countof(buf)); - - auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit"); - form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM); - form << XCHILD("field") << XATTR("var", "end") << XCHILD("value", buf); - } - else { - auto *set = query << XCHILDNS("set", "http://jabber.org/protocol/rsm"); - set << XCHILD("max", "1000"); - set << XCHILD("after", szLastId); - } - - m_ThreadInfo->send(iq); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Contact's history loader - -void CJabberProto::MamSendForm(const char *pszWith, const char *pszAfter) -{ - auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET); - pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM); - - XmlNodeIq iq(pReq); - auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM); - - auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit"); - form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM); - if (pszWith != nullptr) - form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", pszWith); - - auto *rsm = query << XCHILDNS("set", "http://jabber.org/protocol/rsm"); - rsm << XCHILD("max", "1000"); - if (pszAfter != nullptr) - rsm << XCHILD("after", pszAfter); - m_ThreadInfo->send(iq); -} - - -void CJabberProto::OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInfo) -{ - // even if that flag was enabled, unset it - m_bMamDisableMessages = false; - - if (auto *fin = XmlGetChildByTag(iqNode, "fin", "xmlns", JABBER_FEAT_MAM)) { - // if dataset is complete, there's nothing more to do - if (!mir_strcmp(XmlGetAttr(fin, "complete"), "true")) - return; - - if (auto *set = XmlGetChildByTag(fin, "set", "xmlns", "http://jabber.org/protocol/rsm")) - if (auto *lastId = XmlGetChildText(set, "last")) - MamSendForm(ptrA(getUStringA(pInfo->GetHContact(), "jid")), lastId); - } -} - -INT_PTR __cdecl CJabberProto::OnMenuLoadHistory(WPARAM hContact, LPARAM) -{ - if (hContact == 0 || !m_bEnableMam) - return 0; - - // wipe out old history first - if (IDYES == MessageBoxW(NULL, TranslateT("Do you want to erase local history before loading it from server?"), m_tszUserName, MB_YESNOCANCEL | MB_ICONQUESTION)) { - DB::ECPTR pCursor(DB::Events(hContact)); - while (pCursor.FetchNext()) - pCursor.DeleteEvent(); - } - - // load remaining items from server - if (m_bJabberOnline) { - ptrA jid(getUStringA(hContact, "jid")); - if (jid != nullptr) { - m_bMamCreateRead = true; - MamSendForm(jid); - } - } - return 0; -} +/*
+
+Jabber Protocol Plugin for Miranda NG
+
+Copyright (c) 2002-04 Santithorn Bunchua
+Copyright (c) 2005-12 George Hazan
+Copyright (c) 2007 Maxim Mluhov
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+void CJabberProto::OnIqResultMamInfo(const TiXmlElement *iqNode, CJabberIqInfo *pInfo)
+{
+ if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) {
+ if (auto *n = XmlFirstChild(iqNode, "prefs")) {
+ m_bMamPrefsAvailable = true;
+
+ if (auto *type = n->Attribute("default")) {
+ if (!strcmp(type, "never"))
+ m_iMamMode = 0;
+ else if (!strcmp(type, "roster"))
+ m_iMamMode = 1;
+ else
+ m_iMamMode = 2;
+ }
+ }
+ }
+
+ // shall we retrieve missing messages?
+ if (pInfo->GetUserData())
+ MamRetrieveMissingMessages();
+}
+
+void CJabberProto::MamSetMode(int iNewMode)
+{
+ if (!m_bEnableMam)
+ return;
+
+ const char *szMode;
+ switch (iNewMode) {
+ case 0: szMode = "never"; break;
+ case 1: szMode = "roster"; break;
+ default: szMode = "always"; break;
+ }
+
+ XmlNodeIq iq(AddIQ(&CJabberProto::OnIqResultMamInfo, JABBER_IQ_TYPE_SET));
+ auto *node = iq << XCHILDNS("prefs", JABBER_FEAT_MAM) << XATTR("default", szMode);
+ node << XCHILD("always"); node << XCHILD("never");
+ m_ThreadInfo->send(iq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CJabberProto::MamRetrieveMissingMessages()
+{
+ CMStringA szLastId = getMStringA("LastMamId");
+
+ XmlNodeIq iq("set", SerialNext());
+ auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM);
+
+ if (szLastId.IsEmpty()) {
+ m_bMamDisableMessages = true; // our goal is to save message id, not to store messages
+ m_bMamCreateRead = false;
+
+ char buf[100];
+ time2str(time(0), buf, _countof(buf));
+
+ auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit");
+ form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM);
+ form << XCHILD("field") << XATTR("var", "end") << XCHILD("value", buf);
+ }
+ else {
+ auto *set = query << XCHILDNS("set", "http://jabber.org/protocol/rsm");
+ set << XCHILD("max", "1000");
+ set << XCHILD("after", szLastId);
+ }
+
+ m_ThreadInfo->send(iq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Contact's history loader
+
+void CJabberProto::MamSendForm(const char *pszWith, const char *pszAfter)
+{
+ auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET);
+ pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM);
+
+ XmlNodeIq iq(pReq);
+ auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM);
+
+ auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit");
+ form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM);
+ if (pszWith != nullptr)
+ form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", pszWith);
+
+ auto *rsm = query << XCHILDNS("set", "http://jabber.org/protocol/rsm");
+ rsm << XCHILD("max", "1000");
+ if (pszAfter != nullptr)
+ rsm << XCHILD("after", pszAfter);
+ m_ThreadInfo->send(iq);
+}
+
+
+void CJabberProto::OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInfo)
+{
+ // even if that flag was enabled, unset it
+ m_bMamDisableMessages = false;
+
+ if (auto *fin = XmlGetChildByTag(iqNode, "fin", "xmlns", JABBER_FEAT_MAM)) {
+ // if dataset is complete, there's nothing more to do
+ if (!mir_strcmp(XmlGetAttr(fin, "complete"), "true"))
+ return;
+
+ if (auto *set = XmlGetChildByTag(fin, "set", "xmlns", "http://jabber.org/protocol/rsm"))
+ if (auto *lastId = XmlGetChildText(set, "last"))
+ MamSendForm(ptrA(getUStringA(pInfo->GetHContact(), "jid")), lastId);
+ }
+}
+
+INT_PTR __cdecl CJabberProto::OnMenuLoadHistory(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0 || !m_bEnableMam)
+ return 0;
+
+ // wipe out old history first
+ if (IDYES == MessageBoxW(NULL, TranslateT("Do you want to erase local history before loading it from server?"), m_tszUserName, MB_YESNOCANCEL | MB_ICONQUESTION)) {
+ DB::ECPTR pCursor(DB::Events(hContact));
+ while (pCursor.FetchNext())
+ pCursor.DeleteEvent();
+ }
+
+ // load remaining items from server
+ if (m_bJabberOnline) {
+ ptrA jid(getUStringA(hContact, "jid"));
+ if (jid != nullptr) {
+ m_bMamCreateRead = true;
+ MamSendForm(jid);
+ }
+ }
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_menu.cpp b/protocols/JabberG/src/jabber_menu.cpp index 920d390f1a..0d510f6859 100644 --- a/protocols/JabberG/src/jabber_menu.cpp +++ b/protocols/JabberG/src/jabber_menu.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_message_handlers.cpp b/protocols/JabberG/src/jabber_message_handlers.cpp index 4301a4a5c8..7610908dfe 100644 --- a/protocols/JabberG/src/jabber_message_handlers.cpp +++ b/protocols/JabberG/src/jabber_message_handlers.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_message_manager.cpp b/protocols/JabberG/src/jabber_message_manager.cpp index a794974a41..b1db1f299a 100644 --- a/protocols/JabberG/src/jabber_message_manager.cpp +++ b/protocols/JabberG/src/jabber_message_manager.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_message_manager.h b/protocols/JabberG/src/jabber_message_manager.h index 07d381e90f..f7ae0d82ee 100644 --- a/protocols/JabberG/src/jabber_message_manager.h +++ b/protocols/JabberG/src/jabber_message_manager.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_misc.cpp b/protocols/JabberG/src/jabber_misc.cpp index 88a6909eca..71115ef87a 100644 --- a/protocols/JabberG/src/jabber_misc.cpp +++ b/protocols/JabberG/src/jabber_misc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_notes.cpp b/protocols/JabberG/src/jabber_notes.cpp index 6734e7c26c..ac6272f5b7 100644 --- a/protocols/JabberG/src/jabber_notes.cpp +++ b/protocols/JabberG/src/jabber_notes.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan
Copyright (c) 2007-09 Maxim Mluhov
Copyright (c) 2007-09 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_notes.h b/protocols/JabberG/src/jabber_notes.h index d103a938f4..bb7cc22181 100644 --- a/protocols/JabberG/src/jabber_notes.h +++ b/protocols/JabberG/src/jabber_notes.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan
Copyright (c) 2007-09 Maxim Mluhov
Copyright (c) 2007-09 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_omemo.cpp b/protocols/JabberG/src/jabber_omemo.cpp index e5edb53d57..f51245d6bd 100644 --- a/protocols/JabberG/src/jabber_omemo.cpp +++ b/protocols/JabberG/src/jabber_omemo.cpp @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG
-Copyright (c) 2017-22 Miranda NG team
+Copyright (c) 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_omemo.h b/protocols/JabberG/src/jabber_omemo.h index 8563735c36..94b9fd8dbb 100644 --- a/protocols/JabberG/src/jabber_omemo.h +++ b/protocols/JabberG/src/jabber_omemo.h @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG
-Copyright (c) 2017-22 Miranda NG team
+Copyright (c) 2017-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_opt.cpp b/protocols/JabberG/src/jabber_opt.cpp index c3f735c223..c7e85211cd 100644 --- a/protocols/JabberG/src/jabber_opt.cpp +++ b/protocols/JabberG/src/jabber_opt.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_password.cpp b/protocols/JabberG/src/jabber_password.cpp index d39c6a5aa5..0fe0700998 100644 --- a/protocols/JabberG/src/jabber_password.cpp +++ b/protocols/JabberG/src/jabber_password.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_presence_manager.cpp b/protocols/JabberG/src/jabber_presence_manager.cpp index de0dabbe65..873b2a1076 100644 --- a/protocols/JabberG/src/jabber_presence_manager.cpp +++ b/protocols/JabberG/src/jabber_presence_manager.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_presence_manager.h b/protocols/JabberG/src/jabber_presence_manager.h index 4dab8a4991..0c488747c5 100644 --- a/protocols/JabberG/src/jabber_presence_manager.h +++ b/protocols/JabberG/src/jabber_presence_manager.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_privacy.cpp b/protocols/JabberG/src/jabber_privacy.cpp index 8a02af9bba..b1087fde09 100644 --- a/protocols/JabberG/src/jabber_privacy.cpp +++ b/protocols/JabberG/src/jabber_privacy.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan
Copyright (c) 2007-09 Maxim Mluhov
Copyright (c) 2007-09 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_privacy.h b/protocols/JabberG/src/jabber_privacy.h index 4ad6f4294a..d6918a5f1e 100644 --- a/protocols/JabberG/src/jabber_privacy.h +++ b/protocols/JabberG/src/jabber_privacy.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp index 8e2f349cb6..3811becab3 100644 --- a/protocols/JabberG/src/jabber_proto.cpp +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index 29b05e25c0..6037019f46 100644 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_rc.cpp b/protocols/JabberG/src/jabber_rc.cpp index 99e642d38d..a5bae9e944 100644 --- a/protocols/JabberG/src/jabber_rc.cpp +++ b/protocols/JabberG/src/jabber_rc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
XEP-0146 support for Miranda IM
diff --git a/protocols/JabberG/src/jabber_rc.h b/protocols/JabberG/src/jabber_rc.h index 430261602d..7549723872 100644 --- a/protocols/JabberG/src/jabber_rc.h +++ b/protocols/JabberG/src/jabber_rc.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
XEP-0146 support for Miranda IM
diff --git a/protocols/JabberG/src/jabber_roster.cpp b/protocols/JabberG/src/jabber_roster.cpp index 5072fe9ebb..03d9e9ffdd 100644 --- a/protocols/JabberG/src/jabber_roster.cpp +++ b/protocols/JabberG/src/jabber_roster.cpp @@ -1,551 +1,551 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" - -#include <io.h> - -////////////////////////////////////////////////////////////////////////// -// roster editor -// - -enum -{ - RRA_FILLLIST = 0, - RRA_SYNCROSTER, - RRA_SYNCDONE -}; - -struct ROSTEREDITDAT -{ - HWND hList; - int index; - int subindex; -}; - -static void _RosterItemEditEnd(HWND hEditor, ROSTEREDITDAT *edat, BOOL bCancel) -{ - if (!bCancel) { - int len = GetWindowTextLength(hEditor) + 1; - wchar_t *buff = (wchar_t*)mir_alloc(len*sizeof(wchar_t)); - if (buff) { - GetWindowText(hEditor, buff, len); - ListView_SetItemText(edat->hList, edat->index, edat->subindex, buff); - } - mir_free(buff); - } - DestroyWindow(hEditor); -} - -static LRESULT CALLBACK _RosterItemNewEditProc(HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam) -{ - ROSTEREDITDAT * edat = (ROSTEREDITDAT *)GetWindowLongPtr(hEditor, GWLP_USERDATA); - if (!edat) return 0; - switch (msg) { - case WM_KEYDOWN: - switch (wParam) { - case VK_RETURN: - _RosterItemEditEnd(hEditor, edat, FALSE); - return 0; - case VK_ESCAPE: - _RosterItemEditEnd(hEditor, edat, TRUE); - return 0; - } - break; - - case WM_GETDLGCODE: - if (lParam) { - MSG *msg2 = (MSG*)lParam; - if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB) return 0; - if (msg2->message == WM_CHAR && msg2->wParam == '\t') return 0; - } - return DLGC_WANTMESSAGE; - - case WM_KILLFOCUS: - _RosterItemEditEnd(hEditor, edat, FALSE); - return 0; - - case WM_DESTROY: - SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)0); - free(edat); - return 0; - } - - return mir_callNextSubclass(hEditor, _RosterItemNewEditProc, msg, wParam, lParam); -} - -static LRESULT CALLBACK _RosterNewListProc(HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_MOUSEWHEEL || msg == WM_NCLBUTTONDOWN || msg == WM_NCRBUTTONDOWN) - SetFocus(hList); - - if (msg == WM_LBUTTONDOWN) { - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hList, &pt); - - LVHITTESTINFO lvhti = { 0 }; - lvhti.pt = pt; - ListView_SubItemHitTest(hList, &lvhti); - if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem != 0) { - RECT rc; - wchar_t buff[260]; - ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS, &rc); - ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, _countof(buff)); - HWND hEditor = CreateWindow(TEXT("EDIT"), buff, WS_CHILD | ES_AUTOHSCROLL, rc.left + 3, rc.top + 2, rc.right - rc.left - 3, rc.bottom - rc.top - 3, hList, nullptr, g_plugin.getInst(), nullptr); - SendMessage(hEditor, WM_SETFONT, (WPARAM)SendMessage(hList, WM_GETFONT, 0, 0), 0); - ShowWindow(hEditor, SW_SHOW); - SetWindowText(hEditor, buff); - ClientToScreen(hList, &pt); - ScreenToClient(hEditor, &pt); - mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); - mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); - - ROSTEREDITDAT * edat = (ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT)); - mir_subclassWindow(hEditor, _RosterItemNewEditProc); - edat->hList = hList; - edat->index = lvhti.iItem; - edat->subindex = lvhti.iSubItem; - SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)edat); - } - } - return mir_callNextSubclass(hList, _RosterNewListProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberRosterOptDlgProc - advanced options dialog procedure - -class CRosterEditorDlg : public CJabberDlgBase -{ - friend struct CJabberProto; - typedef CJabberDlgBase CSuper; - - uint8_t m_bRRAction; - BOOL m_bReadyToDownload = true; - BOOL m_bReadyToUpload = false; - - void _RosterSendRequest(uint8_t rrAction) - { - m_bRRAction = rrAction; - - m_proto->m_ThreadInfo->send( - XmlNodeIq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_GET)) - << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER)); - } - - int _RosterInsertListItem(const char *jid, const char *nick, const char *group, const char *subscr, bool bChecked) - { - Utf2T wszJid(jid); - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_STATE; - item.iItem = m_list.GetItemCount(); - item.pszText = wszJid; - - int index = m_list.InsertItem(&item); - if (index < 0) - return index; - - m_list.SetCheckState(index, bChecked); - - m_list.SetItemText(index, 1, Utf2T(nick)); - m_list.SetItemText(index, 2, Utf2T(group)); - m_list.SetItemText(index, 3, TranslateW(Utf2T(subscr))); - return index; - } - - void _RosterListClear() - { - m_list.DeleteAllItems(); - while (m_list.GetColumnWidth(0) > 0) - m_list.DeleteColumn(0); - - LV_COLUMN column = { 0 }; - column.mask = LVCF_TEXT; - - column.pszText = TranslateT("JID"); - m_list.InsertColumn(1, &column); - - column.pszText = TranslateT("Nickname"); - m_list.InsertColumn(2, &column); - - column.pszText = TranslateT("Group"); - m_list.InsertColumn(3, &column); - - column.pszText = TranslateT("Subscription"); - m_list.InsertColumn(4, &column); - - RECT rc; - GetClientRect(m_list.GetHwnd(), &rc); - ResizeColumns(rc.right - rc.left); - } - - void ResizeColumns(int width) - { - m_list.SetColumnWidth(0, width * 40 / 100); - m_list.SetColumnWidth(1, width * 25 / 100); - m_list.SetColumnWidth(2, width * 20 / 100); - m_list.SetColumnWidth(3, width * 12 / 100); - } - - void OnChangeStatus() - { - int count = m_list.GetItemCount(); - btnDownload.Enable(m_proto->m_bJabberOnline); - btnUpload.Enable(count && m_proto->m_bJabberOnline); - btnExport.Enable(count > 0); - } - - CCtrlButton btnDownload, btnUpload, btnExport, btnImport; - CCtrlListView m_list; - -public: - CRosterEditorDlg(CJabberProto *m_proto) : - CSuper(m_proto, IDD_ROSTER_EDITOR), - m_list(this, IDC_ROSTER), - btnExport(this, IDC_EXPORT), - btnImport(this, IDC_IMPORT), - btnUpload(this, IDC_UPLOAD), - btnDownload(this, IDC_DOWNLOAD) - { - SetMinSize(550, 390); - - btnExport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Export); - btnImport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Import); - btnUpload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Upload); - btnDownload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Download); - } - - bool OnInitDialog() override - { - SetWindowTextW(m_hwnd, CMStringW(FORMAT, L"%s: %s", TranslateT("Roster Editor"), m_proto->m_tszUserName)); - - Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_AGENTS)); - - Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); - - m_list.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT | LVS_EX_GRIDLINES); - mir_subclassWindow(m_list.GetHwnd(), _RosterNewListProc); - _RosterListClear(); - OnChangeStatus(); - return true; - } - - void OnDestroy() override - { - m_proto->m_hwndRosterEditor = nullptr; - Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); - Window_FreeIcon_IcoLib(m_hwnd); - } - - int Resizer(UTILRESIZECONTROL *urc) override - { - switch (urc->wId) { - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - case IDC_ROSTER: - ResizeColumns(urc->rcItem.right - urc->rcItem.left + urc->dlgNewSize.cx - urc->dlgOriginalSize.cx); - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; - case IDC_DOWNLOAD: - case IDC_UPLOAD: - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - case IDC_EXPORT: - case IDC_IMPORT: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; - } - - void HandleNode(const TiXmlElement *node) - { - if (m_bRRAction == RRA_FILLLIST) { - _RosterListClear(); - auto *query = XmlFirstChild(node, "query"); - if (query == nullptr) return; - - for (auto *item : TiXmlFilter(query, "item")) { - const char *jid = XmlGetAttr(item, "jid"); - if (jid == nullptr) - continue; - - const char *name = XmlGetAttr(item, "name"); - const char *subscription = XmlGetAttr(item, "subscription"); - const char *group = XmlGetChildText(item, "group"); - _RosterInsertListItem(jid, name, group, subscription, true); - } - - // now it is require to process whole contact list to add not in roster contacts - for (auto &hContact : m_proto->AccContacts()) { - ptrW tszJid(m_proto->getWStringA(hContact, "jid")); - if (tszJid == nullptr) - continue; - - LVFINDINFO lvfi = { 0 }; - lvfi.flags = LVFI_STRING; - lvfi.psz = tszJid; - if (m_list.FindItem(-1, &lvfi) == -1) { - ptrA tszName(db_get_utfa(hContact, "CList", "MyHandle")); - ptrA tszGroup(db_get_utfa(hContact, "CList", "Group")); - _RosterInsertListItem(T2Utf(tszJid), tszName, tszGroup, nullptr, false); - } - } - m_bReadyToDownload = false; - m_bReadyToUpload = true; - btnDownload.SetText(TranslateT("Download")); - btnUpload.SetText(TranslateT("Upload")); - OnChangeStatus(); - return; - } - - if (m_bRRAction == RRA_SYNCROSTER) { - btnUpload.SetText(TranslateT("Uploading...")); - auto *queryRoster = XmlFirstChild(node, "query"); - if (!queryRoster) - return; - - int ListItemCount = m_list.GetItemCount(); - for (int index = 0; index < ListItemCount; index++) { - wchar_t jid[JABBER_MAX_JID_LEN] = L""; - wchar_t name[260]; - wchar_t group[260]; - wchar_t subscr[260]; - m_list.GetItemText(index, 0, jid, _countof(jid)); - m_list.GetItemText(index, 1, name, _countof(name)); - m_list.GetItemText(index, 2, group, _countof(group)); - m_list.GetItemText(index, 3, subscr, _countof(subscr)); - - T2Utf szJid(jid), szName(name), szGroup(group), szSubscr(subscr); - auto *itemRoster = XmlGetChildByTag(queryRoster, "item", "jid", szJid); - bool bRemove = !m_list.GetCheckState(index); - if (itemRoster && bRemove) { // delete item - XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); - iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item") << XATTR("jid", szJid) << XATTR("subscription", "remove"); - m_proto->m_ThreadInfo->send(iq); - } - else if (!bRemove) { - bool bPushed = itemRoster != 0; - const char *rosterName = 0, *rosterGroup = 0; - if (!bPushed) { - rosterName = XmlGetAttr(itemRoster, "name"); - if ((rosterName != nullptr || name[0] != 0) && mir_strcmpi(rosterName, szName)) - bPushed = true; - if (!bPushed) { - auto *szSub = XmlGetAttr(itemRoster, "subscription"); - if ((szSub != nullptr || subscr[0] != 0) && mir_strcmpi(szSub, szSubscr)) - bPushed = true; - } - if (!bPushed) { - rosterGroup = XmlGetChildText(itemRoster, "group"); - if (rosterGroup != nullptr && mir_strcmpi(rosterGroup, szGroup)) - bPushed = true; - } - } - if (bPushed) { - XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); - auto *item = iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item"); - if (rosterGroup || mir_strlen(szGroup)) - item << XCHILD("group", szGroup); - if (rosterName || mir_strlen(szName)) - item << XATTR("name", szName); - item << XATTR("jid", szJid) << XATTR("subscription", subscr[0] ? szSubscr : "none"); - m_proto->m_ThreadInfo->send(iq); - } - } - } - m_bRRAction = RRA_SYNCDONE; - _RosterSendRequest(RRA_FILLLIST); - return; - } - - btnUpload.SetText(TranslateT("Upload")); - onClick_Download(nullptr); - }; - - void onClick_Download(CCtrlButton*) - { - m_bReadyToUpload = m_bReadyToDownload = false; - OnChangeStatus(); - btnDownload.SetText(TranslateT("Downloading...")); - _RosterSendRequest(RRA_FILLLIST); - } - - void onClick_Upload(CCtrlButton*) - { - m_bReadyToUpload = false; - OnChangeStatus(); - btnUpload.SetText(TranslateT("Connecting...")); - _RosterSendRequest(RRA_SYNCROSTER); - } - - void onClick_Export(CCtrlButton*) - { - wchar_t filename[MAX_PATH] = { 0 }; - - wchar_t filter[MAX_PATH]; - mir_snwprintf(filter, L"%s (*.xml)%c*.xml%c%c", TranslateT("XML (UTF-8 encoded)"), 0, 0, 0); - OPENFILENAME ofn = {}; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = filter; - ofn.lpstrFile = filename; - ofn.Flags = OFN_HIDEREADONLY; - ofn.nMaxFile = _countof(filename); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = L"xml"; - if (!GetSaveFileNameW(&ofn)) - return; - - FILE * fp = _wfopen(filename, L"wb"); - if (!fp) - return; - - int ListItemCount = m_list.GetItemCount(); - - XmlNode root("Roster"); - - for (int index = 0; index < ListItemCount; index++) { - wchar_t jid[JABBER_MAX_JID_LEN] = L""; - wchar_t name[260] = L""; - wchar_t group[260] = L""; - wchar_t subscr[260] = L""; - m_list.GetItemText(index, 0, jid, _countof(jid)); - m_list.GetItemText(index, 1, name, _countof(name)); - m_list.GetItemText(index, 2, group, _countof(group)); - m_list.GetItemText(index, 3, subscr, _countof(subscr)); - root << XCHILD("Item") << XATTR("jid", T2Utf(jid)) << XATTR("name", T2Utf(name)) << XATTR("group", T2Utf(group)) << XATTR("subscription", T2Utf(subscr)); - } - - tinyxml2::XMLPrinter printer(fp); - root.Print(&printer); - fclose(fp); - } - - void onClick_Import(CCtrlButton*) - { - wchar_t filename[MAX_PATH] = {}; - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.hInstance = nullptr; - ofn.lpstrFilter = L"XML (UTF-8 encoded)(*.xml)\0*.xml\0\0"; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; - ofn.lpstrFile = filename; - ofn.nMaxFile = _countof(filename); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = L"xml"; - if (!GetOpenFileNameW(&ofn)) - return; - - FILE * fp = _wfopen(filename, L"rb"); - if (!fp) - return; - - TiXmlDocument doc; - int ret = doc.LoadFile(fp); - fclose(fp); - if (ret != 0) - return; - - _RosterListClear(); - - const TiXmlElement *Table = TiXmlConst(&doc)["Workbook"]["Worksheet"]["Table"].ToElement(); - if (Table) { - for (auto *Row : TiXmlFilter(Table, "Row")) { - bool bAdd = false; - const char *jid = nullptr; - const char *name = nullptr; - const char *group = nullptr; - const char *subscr = nullptr; - auto *Cell = XmlFirstChild(Row, "Cell"); - auto *Data = XmlFirstChild(Cell, "Data"); - if (Data) { - if (!mir_strcmpi(Data->GetText(), "+")) - bAdd = true; - else if (mir_strcmpi(Data->GetText(), "-")) - continue; - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) { - jid = Data->GetText(); - if (!jid || mir_strlen(jid) == 0) - continue; - } - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) name = Data->GetText(); - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) group = Data->GetText(); - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) subscr = Data->GetText(); - } - _RosterInsertListItem(jid, name, group, subscr, bAdd); - } - } - else if (Table = TiXmlConst(&doc)["Roster"].ToElement()) { - for (auto *Row : TiXmlFilter(Table, "Item")) { - auto *jid = Row->Attribute("jid"); - auto *name = Row->Attribute("name"); - auto *group = Row->Attribute("group"); - auto *subscr = Row->Attribute("subscription"); - _RosterInsertListItem(jid, name, group, subscr, true); - } - } - - OnChangeStatus(); - } -}; - -INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl(WPARAM, LPARAM) -{ - if (m_hwndRosterEditor) - SetForegroundWindow(m_hwndRosterEditor->GetHwnd()); - else { - m_hwndRosterEditor = new CRosterEditorDlg(this); - m_hwndRosterEditor->Show(); - } - - return 0; -} - -void CJabberProto::_RosterHandleGetRequest(const TiXmlElement *node, CJabberIqInfo*) -{ - if (m_hwndRosterEditor) - m_hwndRosterEditor->HandleNode(node); -} - -void CJabberProto::JabberUpdateDialogs(BOOL) -{ - if (m_hwndRosterEditor) - m_hwndRosterEditor->OnChangeStatus(); -} +/*
+
+Jabber Protocol Plugin for Miranda NG
+
+Copyright (c) 2002-04 Santithorn Bunchua
+Copyright (c) 2005-12 George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <io.h>
+
+//////////////////////////////////////////////////////////////////////////
+// roster editor
+//
+
+enum
+{
+ RRA_FILLLIST = 0,
+ RRA_SYNCROSTER,
+ RRA_SYNCDONE
+};
+
+struct ROSTEREDITDAT
+{
+ HWND hList;
+ int index;
+ int subindex;
+};
+
+static void _RosterItemEditEnd(HWND hEditor, ROSTEREDITDAT *edat, BOOL bCancel)
+{
+ if (!bCancel) {
+ int len = GetWindowTextLength(hEditor) + 1;
+ wchar_t *buff = (wchar_t*)mir_alloc(len*sizeof(wchar_t));
+ if (buff) {
+ GetWindowText(hEditor, buff, len);
+ ListView_SetItemText(edat->hList, edat->index, edat->subindex, buff);
+ }
+ mir_free(buff);
+ }
+ DestroyWindow(hEditor);
+}
+
+static LRESULT CALLBACK _RosterItemNewEditProc(HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ ROSTEREDITDAT * edat = (ROSTEREDITDAT *)GetWindowLongPtr(hEditor, GWLP_USERDATA);
+ if (!edat) return 0;
+ switch (msg) {
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_RETURN:
+ _RosterItemEditEnd(hEditor, edat, FALSE);
+ return 0;
+ case VK_ESCAPE:
+ _RosterItemEditEnd(hEditor, edat, TRUE);
+ return 0;
+ }
+ break;
+
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *msg2 = (MSG*)lParam;
+ if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB) return 0;
+ if (msg2->message == WM_CHAR && msg2->wParam == '\t') return 0;
+ }
+ return DLGC_WANTMESSAGE;
+
+ case WM_KILLFOCUS:
+ _RosterItemEditEnd(hEditor, edat, FALSE);
+ return 0;
+
+ case WM_DESTROY:
+ SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)0);
+ free(edat);
+ return 0;
+ }
+
+ return mir_callNextSubclass(hEditor, _RosterItemNewEditProc, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK _RosterNewListProc(HWND hList, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_MOUSEWHEEL || msg == WM_NCLBUTTONDOWN || msg == WM_NCRBUTTONDOWN)
+ SetFocus(hList);
+
+ if (msg == WM_LBUTTONDOWN) {
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hList, &pt);
+
+ LVHITTESTINFO lvhti = { 0 };
+ lvhti.pt = pt;
+ ListView_SubItemHitTest(hList, &lvhti);
+ if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem != 0) {
+ RECT rc;
+ wchar_t buff[260];
+ ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS, &rc);
+ ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, _countof(buff));
+ HWND hEditor = CreateWindow(TEXT("EDIT"), buff, WS_CHILD | ES_AUTOHSCROLL, rc.left + 3, rc.top + 2, rc.right - rc.left - 3, rc.bottom - rc.top - 3, hList, nullptr, g_plugin.getInst(), nullptr);
+ SendMessage(hEditor, WM_SETFONT, (WPARAM)SendMessage(hList, WM_GETFONT, 0, 0), 0);
+ ShowWindow(hEditor, SW_SHOW);
+ SetWindowText(hEditor, buff);
+ ClientToScreen(hList, &pt);
+ ScreenToClient(hEditor, &pt);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+
+ ROSTEREDITDAT * edat = (ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT));
+ mir_subclassWindow(hEditor, _RosterItemNewEditProc);
+ edat->hList = hList;
+ edat->index = lvhti.iItem;
+ edat->subindex = lvhti.iSubItem;
+ SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)edat);
+ }
+ }
+ return mir_callNextSubclass(hList, _RosterNewListProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberRosterOptDlgProc - advanced options dialog procedure
+
+class CRosterEditorDlg : public CJabberDlgBase
+{
+ friend struct CJabberProto;
+ typedef CJabberDlgBase CSuper;
+
+ uint8_t m_bRRAction;
+ BOOL m_bReadyToDownload = true;
+ BOOL m_bReadyToUpload = false;
+
+ void _RosterSendRequest(uint8_t rrAction)
+ {
+ m_bRRAction = rrAction;
+
+ m_proto->m_ThreadInfo->send(
+ XmlNodeIq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_GET))
+ << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER));
+ }
+
+ int _RosterInsertListItem(const char *jid, const char *nick, const char *group, const char *subscr, bool bChecked)
+ {
+ Utf2T wszJid(jid);
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_STATE;
+ item.iItem = m_list.GetItemCount();
+ item.pszText = wszJid;
+
+ int index = m_list.InsertItem(&item);
+ if (index < 0)
+ return index;
+
+ m_list.SetCheckState(index, bChecked);
+
+ m_list.SetItemText(index, 1, Utf2T(nick));
+ m_list.SetItemText(index, 2, Utf2T(group));
+ m_list.SetItemText(index, 3, TranslateW(Utf2T(subscr)));
+ return index;
+ }
+
+ void _RosterListClear()
+ {
+ m_list.DeleteAllItems();
+ while (m_list.GetColumnWidth(0) > 0)
+ m_list.DeleteColumn(0);
+
+ LV_COLUMN column = { 0 };
+ column.mask = LVCF_TEXT;
+
+ column.pszText = TranslateT("JID");
+ m_list.InsertColumn(1, &column);
+
+ column.pszText = TranslateT("Nickname");
+ m_list.InsertColumn(2, &column);
+
+ column.pszText = TranslateT("Group");
+ m_list.InsertColumn(3, &column);
+
+ column.pszText = TranslateT("Subscription");
+ m_list.InsertColumn(4, &column);
+
+ RECT rc;
+ GetClientRect(m_list.GetHwnd(), &rc);
+ ResizeColumns(rc.right - rc.left);
+ }
+
+ void ResizeColumns(int width)
+ {
+ m_list.SetColumnWidth(0, width * 40 / 100);
+ m_list.SetColumnWidth(1, width * 25 / 100);
+ m_list.SetColumnWidth(2, width * 20 / 100);
+ m_list.SetColumnWidth(3, width * 12 / 100);
+ }
+
+ void OnChangeStatus()
+ {
+ int count = m_list.GetItemCount();
+ btnDownload.Enable(m_proto->m_bJabberOnline);
+ btnUpload.Enable(count && m_proto->m_bJabberOnline);
+ btnExport.Enable(count > 0);
+ }
+
+ CCtrlButton btnDownload, btnUpload, btnExport, btnImport;
+ CCtrlListView m_list;
+
+public:
+ CRosterEditorDlg(CJabberProto *m_proto) :
+ CSuper(m_proto, IDD_ROSTER_EDITOR),
+ m_list(this, IDC_ROSTER),
+ btnExport(this, IDC_EXPORT),
+ btnImport(this, IDC_IMPORT),
+ btnUpload(this, IDC_UPLOAD),
+ btnDownload(this, IDC_DOWNLOAD)
+ {
+ SetMinSize(550, 390);
+
+ btnExport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Export);
+ btnImport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Import);
+ btnUpload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Upload);
+ btnDownload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Download);
+ }
+
+ bool OnInitDialog() override
+ {
+ SetWindowTextW(m_hwnd, CMStringW(FORMAT, L"%s: %s", TranslateT("Roster Editor"), m_proto->m_tszUserName));
+
+ Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_AGENTS));
+
+ Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_");
+
+ m_list.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT | LVS_EX_GRIDLINES);
+ mir_subclassWindow(m_list.GetHwnd(), _RosterNewListProc);
+ _RosterListClear();
+ OnChangeStatus();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ m_proto->m_hwndRosterEditor = nullptr;
+ Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_");
+ Window_FreeIcon_IcoLib(m_hwnd);
+ }
+
+ int Resizer(UTILRESIZECONTROL *urc) override
+ {
+ switch (urc->wId) {
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH;
+ case IDC_ROSTER:
+ ResizeColumns(urc->rcItem.right - urc->rcItem.left + urc->dlgNewSize.cx - urc->dlgOriginalSize.cx);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH;
+ case IDC_DOWNLOAD:
+ case IDC_UPLOAD:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM;
+ case IDC_EXPORT:
+ case IDC_IMPORT:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+ }
+
+ void HandleNode(const TiXmlElement *node)
+ {
+ if (m_bRRAction == RRA_FILLLIST) {
+ _RosterListClear();
+ auto *query = XmlFirstChild(node, "query");
+ if (query == nullptr) return;
+
+ for (auto *item : TiXmlFilter(query, "item")) {
+ const char *jid = XmlGetAttr(item, "jid");
+ if (jid == nullptr)
+ continue;
+
+ const char *name = XmlGetAttr(item, "name");
+ const char *subscription = XmlGetAttr(item, "subscription");
+ const char *group = XmlGetChildText(item, "group");
+ _RosterInsertListItem(jid, name, group, subscription, true);
+ }
+
+ // now it is require to process whole contact list to add not in roster contacts
+ for (auto &hContact : m_proto->AccContacts()) {
+ ptrW tszJid(m_proto->getWStringA(hContact, "jid"));
+ if (tszJid == nullptr)
+ continue;
+
+ LVFINDINFO lvfi = { 0 };
+ lvfi.flags = LVFI_STRING;
+ lvfi.psz = tszJid;
+ if (m_list.FindItem(-1, &lvfi) == -1) {
+ ptrA tszName(db_get_utfa(hContact, "CList", "MyHandle"));
+ ptrA tszGroup(db_get_utfa(hContact, "CList", "Group"));
+ _RosterInsertListItem(T2Utf(tszJid), tszName, tszGroup, nullptr, false);
+ }
+ }
+ m_bReadyToDownload = false;
+ m_bReadyToUpload = true;
+ btnDownload.SetText(TranslateT("Download"));
+ btnUpload.SetText(TranslateT("Upload"));
+ OnChangeStatus();
+ return;
+ }
+
+ if (m_bRRAction == RRA_SYNCROSTER) {
+ btnUpload.SetText(TranslateT("Uploading..."));
+ auto *queryRoster = XmlFirstChild(node, "query");
+ if (!queryRoster)
+ return;
+
+ int ListItemCount = m_list.GetItemCount();
+ for (int index = 0; index < ListItemCount; index++) {
+ wchar_t jid[JABBER_MAX_JID_LEN] = L"";
+ wchar_t name[260];
+ wchar_t group[260];
+ wchar_t subscr[260];
+ m_list.GetItemText(index, 0, jid, _countof(jid));
+ m_list.GetItemText(index, 1, name, _countof(name));
+ m_list.GetItemText(index, 2, group, _countof(group));
+ m_list.GetItemText(index, 3, subscr, _countof(subscr));
+
+ T2Utf szJid(jid), szName(name), szGroup(group), szSubscr(subscr);
+ auto *itemRoster = XmlGetChildByTag(queryRoster, "item", "jid", szJid);
+ bool bRemove = !m_list.GetCheckState(index);
+ if (itemRoster && bRemove) { // delete item
+ XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET));
+ iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item") << XATTR("jid", szJid) << XATTR("subscription", "remove");
+ m_proto->m_ThreadInfo->send(iq);
+ }
+ else if (!bRemove) {
+ bool bPushed = itemRoster != 0;
+ const char *rosterName = 0, *rosterGroup = 0;
+ if (!bPushed) {
+ rosterName = XmlGetAttr(itemRoster, "name");
+ if ((rosterName != nullptr || name[0] != 0) && mir_strcmpi(rosterName, szName))
+ bPushed = true;
+ if (!bPushed) {
+ auto *szSub = XmlGetAttr(itemRoster, "subscription");
+ if ((szSub != nullptr || subscr[0] != 0) && mir_strcmpi(szSub, szSubscr))
+ bPushed = true;
+ }
+ if (!bPushed) {
+ rosterGroup = XmlGetChildText(itemRoster, "group");
+ if (rosterGroup != nullptr && mir_strcmpi(rosterGroup, szGroup))
+ bPushed = true;
+ }
+ }
+ if (bPushed) {
+ XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET));
+ auto *item = iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item");
+ if (rosterGroup || mir_strlen(szGroup))
+ item << XCHILD("group", szGroup);
+ if (rosterName || mir_strlen(szName))
+ item << XATTR("name", szName);
+ item << XATTR("jid", szJid) << XATTR("subscription", subscr[0] ? szSubscr : "none");
+ m_proto->m_ThreadInfo->send(iq);
+ }
+ }
+ }
+ m_bRRAction = RRA_SYNCDONE;
+ _RosterSendRequest(RRA_FILLLIST);
+ return;
+ }
+
+ btnUpload.SetText(TranslateT("Upload"));
+ onClick_Download(nullptr);
+ };
+
+ void onClick_Download(CCtrlButton*)
+ {
+ m_bReadyToUpload = m_bReadyToDownload = false;
+ OnChangeStatus();
+ btnDownload.SetText(TranslateT("Downloading..."));
+ _RosterSendRequest(RRA_FILLLIST);
+ }
+
+ void onClick_Upload(CCtrlButton*)
+ {
+ m_bReadyToUpload = false;
+ OnChangeStatus();
+ btnUpload.SetText(TranslateT("Connecting..."));
+ _RosterSendRequest(RRA_SYNCROSTER);
+ }
+
+ void onClick_Export(CCtrlButton*)
+ {
+ wchar_t filename[MAX_PATH] = { 0 };
+
+ wchar_t filter[MAX_PATH];
+ mir_snwprintf(filter, L"%s (*.xml)%c*.xml%c%c", TranslateT("XML (UTF-8 encoded)"), 0, 0, 0);
+ OPENFILENAME ofn = {};
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = m_hwnd;
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = filename;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.nMaxFile = _countof(filename);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = L"xml";
+ if (!GetSaveFileNameW(&ofn))
+ return;
+
+ FILE * fp = _wfopen(filename, L"wb");
+ if (!fp)
+ return;
+
+ int ListItemCount = m_list.GetItemCount();
+
+ XmlNode root("Roster");
+
+ for (int index = 0; index < ListItemCount; index++) {
+ wchar_t jid[JABBER_MAX_JID_LEN] = L"";
+ wchar_t name[260] = L"";
+ wchar_t group[260] = L"";
+ wchar_t subscr[260] = L"";
+ m_list.GetItemText(index, 0, jid, _countof(jid));
+ m_list.GetItemText(index, 1, name, _countof(name));
+ m_list.GetItemText(index, 2, group, _countof(group));
+ m_list.GetItemText(index, 3, subscr, _countof(subscr));
+ root << XCHILD("Item") << XATTR("jid", T2Utf(jid)) << XATTR("name", T2Utf(name)) << XATTR("group", T2Utf(group)) << XATTR("subscription", T2Utf(subscr));
+ }
+
+ tinyxml2::XMLPrinter printer(fp);
+ root.Print(&printer);
+ fclose(fp);
+ }
+
+ void onClick_Import(CCtrlButton*)
+ {
+ wchar_t filename[MAX_PATH] = {};
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = m_hwnd;
+ ofn.hInstance = nullptr;
+ ofn.lpstrFilter = L"XML (UTF-8 encoded)(*.xml)\0*.xml\0\0";
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = _countof(filename);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = L"xml";
+ if (!GetOpenFileNameW(&ofn))
+ return;
+
+ FILE * fp = _wfopen(filename, L"rb");
+ if (!fp)
+ return;
+
+ TiXmlDocument doc;
+ int ret = doc.LoadFile(fp);
+ fclose(fp);
+ if (ret != 0)
+ return;
+
+ _RosterListClear();
+
+ const TiXmlElement *Table = TiXmlConst(&doc)["Workbook"]["Worksheet"]["Table"].ToElement();
+ if (Table) {
+ for (auto *Row : TiXmlFilter(Table, "Row")) {
+ bool bAdd = false;
+ const char *jid = nullptr;
+ const char *name = nullptr;
+ const char *group = nullptr;
+ const char *subscr = nullptr;
+ auto *Cell = XmlFirstChild(Row, "Cell");
+ auto *Data = XmlFirstChild(Cell, "Data");
+ if (Data) {
+ if (!mir_strcmpi(Data->GetText(), "+"))
+ bAdd = true;
+ else if (mir_strcmpi(Data->GetText(), "-"))
+ continue;
+
+ Cell = Cell->NextSiblingElement("Cell");
+ if (Cell) Data = XmlFirstChild(Cell, "Data");
+ else Data = nullptr;
+ if (Data) {
+ jid = Data->GetText();
+ if (!jid || mir_strlen(jid) == 0)
+ continue;
+ }
+
+ Cell = Cell->NextSiblingElement("Cell");
+ if (Cell) Data = XmlFirstChild(Cell, "Data");
+ else Data = nullptr;
+ if (Data) name = Data->GetText();
+
+ Cell = Cell->NextSiblingElement("Cell");
+ if (Cell) Data = XmlFirstChild(Cell, "Data");
+ else Data = nullptr;
+ if (Data) group = Data->GetText();
+
+ Cell = Cell->NextSiblingElement("Cell");
+ if (Cell) Data = XmlFirstChild(Cell, "Data");
+ else Data = nullptr;
+ if (Data) subscr = Data->GetText();
+ }
+ _RosterInsertListItem(jid, name, group, subscr, bAdd);
+ }
+ }
+ else if (Table = TiXmlConst(&doc)["Roster"].ToElement()) {
+ for (auto *Row : TiXmlFilter(Table, "Item")) {
+ auto *jid = Row->Attribute("jid");
+ auto *name = Row->Attribute("name");
+ auto *group = Row->Attribute("group");
+ auto *subscr = Row->Attribute("subscription");
+ _RosterInsertListItem(jid, name, group, subscr, true);
+ }
+ }
+
+ OnChangeStatus();
+ }
+};
+
+INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl(WPARAM, LPARAM)
+{
+ if (m_hwndRosterEditor)
+ SetForegroundWindow(m_hwndRosterEditor->GetHwnd());
+ else {
+ m_hwndRosterEditor = new CRosterEditorDlg(this);
+ m_hwndRosterEditor->Show();
+ }
+
+ return 0;
+}
+
+void CJabberProto::_RosterHandleGetRequest(const TiXmlElement *node, CJabberIqInfo*)
+{
+ if (m_hwndRosterEditor)
+ m_hwndRosterEditor->HandleNode(node);
+}
+
+void CJabberProto::JabberUpdateDialogs(BOOL)
+{
+ if (m_hwndRosterEditor)
+ m_hwndRosterEditor->OnChangeStatus();
+}
diff --git a/protocols/JabberG/src/jabber_search.cpp b/protocols/JabberG/src/jabber_search.cpp index 8beaf9fb7b..5102211399 100644 --- a/protocols/JabberG/src/jabber_search.cpp +++ b/protocols/JabberG/src/jabber_search.cpp @@ -1,765 +1,765 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (c) 2007 Artem Shpynov -Copyright (C) 2012-22 Miranda NG team - -Module implements a search according to XEP-0055: Jabber Search -http://www.xmpp.org/extensions/xep-0055.html - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" -#include <CommCtrl.h> -#include "jabber_iq.h" -#include "jabber_caps.h" - -/////////////////////////////////////////////////////////////////////////////// -// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling - -static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_COMMAND && lParam != 0) { - HWND hwndDlg = GetParent(hwnd); - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (dat && lParam) { - int pos = dat->curPos; - RECT MineRect; - RECT FrameRect; - GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &FrameRect); - GetWindowRect((HWND)lParam, &MineRect); - if (MineRect.top - 10 < FrameRect.top) { - pos = dat->curPos + (MineRect.top - 14 - FrameRect.top); - if (pos < 0) pos = 0; - } - else if (MineRect.bottom > FrameRect.bottom) { - pos = dat->curPos + (MineRect.bottom - FrameRect.bottom); - if (dat->frameHeight + pos > dat->CurrentHeight) - pos = dat->CurrentHeight - dat->frameHeight; - } - if (pos != dat->curPos) { - ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect)); - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE); - RECT Invalid = dat->frameRect; - if (dat->curPos - pos > 0) - Invalid.bottom = Invalid.top + (dat->curPos - pos); - else - Invalid.top = Invalid.bottom + (dat->curPos - pos); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - dat->curPos = pos; - } - } - - // Transmit focus set notification to parent window - if (HIWORD(wParam) == EN_SETFOCUS) - PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); - } - - if (msg == WM_PAINT) { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - FillRect(hdc, &(ps.rcPaint), GetSysColorBrush(COLOR_BTNFACE)); - EndPaint(hwnd, &ps); - } - - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -/////////////////////////////////////////////////////////////////////////////// -// Add Search field to form - -static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat) -{ - if (!FieldDat || !FieldDat->Label || !FieldDat->Var) - return FALSE; - - HFONT hFont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0); - HWND hwndParent = GetDlgItem(hwndDlg, IDC_FRAME); - LONG_PTR frameExStyle = GetWindowLongPtr(hwndParent, GWL_EXSTYLE); - frameExStyle |= WS_EX_CONTROLPARENT; - SetWindowLongPtr(hwndParent, GWL_EXSTYLE, frameExStyle); - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FRAME), GWLP_WNDPROC, (LONG_PTR)JabberSearchFrameProc); - - int CornerX = 1; - int CornerY = 1; - RECT rect; - GetClientRect(hwndParent, &rect); - int width = rect.right - 5 - CornerX; - - int Order = (FieldDat->bHidden) ? -1 : FieldDat->Order; - - HWND hwndLabel = CreateWindowEx(0, L"STATIC", (const wchar_t *)TranslateW(FieldDat->Label), WS_CHILD, CornerX, CornerY + Order * 40, width, 13, hwndParent, nullptr, g_plugin.getInst(), nullptr); - HWND hwndVar = CreateWindowEx(0 | WS_EX_CLIENTEDGE, L"EDIT", FieldDat->defValue, WS_CHILD | WS_TABSTOP, CornerX + 5, CornerY + Order * 40 + 14, width, 20, hwndParent, nullptr, g_plugin.getInst(), nullptr); - SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont, 0); - SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont, 0); - if (!FieldDat->bHidden) { - ShowWindow(hwndLabel, SW_SHOW); - ShowWindow(hwndVar, SW_SHOW); - EnableWindow(hwndLabel, !FieldDat->bReadOnly); - SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly, 0); - } - - // remade list - // reallocation - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (dat) { - dat->pJSInf = (JabberSearchFieldsInfo*)realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount + 1)); - dat->pJSInf[dat->nJSInfCount].hwndCaptionItem = hwndLabel; - dat->pJSInf[dat->nJSInfCount].hwndValueItem = hwndVar; - dat->pJSInf[dat->nJSInfCount].szFieldCaption = wcsdup(FieldDat->Label); - dat->pJSInf[dat->nJSInfCount].szFieldName = wcsdup(FieldDat->Var); - dat->nJSInfCount++; - } - return CornerY + Order * 40 + 14 + 20; -} - -//////////////////////////////////////////////////////////////////////////////// -// Available search field request result handler (XEP-0055. Examples 2, 7) - -void CJabberProto::OnIqResultGetSearchFields(const TiXmlElement *iqNode, CJabberIqInfo*) -{ - if (!searchHandleDlg) - return; - - const char *type = XmlGetAttr(iqNode, "type"); - if (type == nullptr) - return; - - if (!mir_strcmp(type, "result")) { - auto *queryNode = XmlFirstChild(iqNode, "query"); - auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS); - - ShowWindow(searchHandleDlg, SW_HIDE); - if (xNode) { - // 1. Form - PostMessage(searchHandleDlg, WM_USER + 11, (WPARAM)xNode, 0); - auto *xcNode = XmlFirstChild(xNode, "instructions"); - if (xcNode) - SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, xcNode->GetText()); - } - else { - int Order = 0; - for (auto *chNode : TiXmlEnum(queryNode)) { - if (!mir_strcmpi(chNode->Name(), "instructions") && chNode->GetText()) - SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateW(Utf2T(chNode->GetText()))); - else if (chNode->Name()) { - Data *MyData = (Data*)malloc(sizeof(Data)); - memset(MyData, 0, sizeof(Data)); - - MyData->Label = mir_utf8decodeW(chNode->Name()); - MyData->Var = mir_utf8decodeW(chNode->Name()); - MyData->defValue = mir_utf8decodeW(chNode->GetText()); - MyData->Order = Order; - if (MyData->defValue) - MyData->bReadOnly = true; - PostMessage(searchHandleDlg, WM_USER + 10, FALSE, (LPARAM)MyData); - Order++; - } - } - } - - const char *szFrom = XmlGetAttr(iqNode, "from"); - if (szFrom) - SearchAddToRecent(szFrom, searchHandleDlg); - PostMessage(searchHandleDlg, WM_USER + 10, 0, 0); - ShowWindow(searchHandleDlg, SW_SHOW); - } - else if (!mir_strcmp(type, "error")) { - const char *code = ""; - const char *description = ""; - auto *errorNode = XmlFirstChild(iqNode, "error"); - if (errorNode) { - code = XmlGetAttr(errorNode, "code"); - description = errorNode->GetText(); - } - - char buff[255]; - mir_snprintf(buff, TranslateU("Error %s %s\r\nPlease select other server"), code, description); - SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff); - } - else SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateT("Error: unknown reply received\r\nPlease select other server")); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// Return results to search dialog -// The pmFields is the pointer to map of <field Name, field Label> Not unical but ordered -// This can help to made result parser routines more simple - -static char *nickfields[] = { "nick", "nickname", "fullname", "name", "given", "first", "jid", nullptr }; - -static int TCharKeyCmp(const char *p1, const char *p2) -{ - return mir_strcmpi(p1, p2); -} - -static void SearchReturnResults(CJabberProto *ppro, HANDLE id, LIST<UNIQUE_MAP> &plUsersInfo, UNIQUE_MAP &pmAllFields) -{ - LIST<char> ListOfNonEmptyFields(20, TCharKeyCmp); - LIST<char> ListOfFields(20); - - // lets fill the ListOfNonEmptyFields but in users order - for (auto &pmUserData : plUsersInfo) { - int nUserFields = pmUserData->getCount(); - for (int j = 0; j < nUserFields; j++) { - char *var = pmUserData->getKeyName(j); - if (var && ListOfNonEmptyFields.getIndex(var) < 0) - ListOfNonEmptyFields.insert(var); - } - } - - // now fill the ListOfFields but order is from pmAllFields - int nAllCount = pmAllFields.getCount(); - for (int i = 0; i < nAllCount; i++) { - char *var = pmAllFields.getUnOrderedKeyName(i); - if (var && ListOfNonEmptyFields.getIndex(var) < 0) - continue; - ListOfFields.insert(var); - } - - // now lets transfer field names - int nFieldCount = ListOfFields.getCount(); - - CUSTOMSEARCHRESULTS Results = { 0 }; - Results.nSize = sizeof(Results); - Results.pszFields = (wchar_t**)mir_alloc(sizeof(wchar_t*)*nFieldCount); - Results.nFieldCount = nFieldCount; - - // Sending Columns Titles - for (int i = 0; i < nFieldCount; i++) { - char *var = ListOfFields[i]; - if (var) - Results.pszFields[i] = mir_utf8decodeW(pmAllFields[var]); - } - - Results.psr.cbSize = 0; // sending column names - ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results); - for (int i = 0; i < nFieldCount; i++) - replaceStrW(Results.pszFields[i], nullptr); - - // Sending Users Data - Results.psr.cbSize = sizeof(Results.psr); - - for (auto &pmUserData : plUsersInfo) { - for (int j = 0; j < nFieldCount; j++) { - char *var = ListOfFields[j]; - char *value = pmUserData->operator [](var); - Results.pszFields[j] = value ? mir_utf8decodeW(value) : mir_wstrdup(L" "); - if (!mir_strcmpi(var, "jid") && value) - Results.psr.id.w = Results.pszFields[j]; - } - - const char *nick = nullptr; - for (int k = 0; k < _countof(nickfields) && !nick; k++) - nick = pmUserData->operator [](nickfields[k]); - - if (nick) { - Utf2T wszNick(nick); - wchar_t buff[200]; - if (mir_wstrcmpi(wszNick, Results.psr.id.w)) - mir_snwprintf(buff, L"%s (%s)", wszNick.get(), Results.psr.id.w); - else - wcsncpy_s(buff, wszNick, _TRUNCATE); - - Results.psr.nick.w = buff; - } - else Results.psr.nick.w = L""; - Results.psr.flags = PSR_UNICODE; - - ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results); - for (int i = 0; i < nFieldCount; i++) - replaceStrW(Results.pszFields[i], nullptr); - } - - mir_free(Results.pszFields); -} - -//////////////////////////////////////////////////////////////////////////////// -// Search field request result handler (XEP-0055. Examples 3, 8) - -void CJabberProto::OnIqResultAdvancedSearch(const TiXmlElement *iqNode, CJabberIqInfo*) -{ - const char *type; - int id; - - UNIQUE_MAP mColumnsNames(10); - LIST<UNIQUE_MAP> SearchResults(2); - - if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = XmlGetAttr(iqNode, "type")) == nullptr)) { - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); - return; - } - - if (!mir_strcmp(type, "result")) { - auto *queryNode = XmlFirstChild(iqNode, "query"); - auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS); - if (xNode) { - // 1. Form search results info - for (auto *fieldNode : TiXmlFilter(XmlFirstChild(xNode, "reported"), "field")) { - auto *var = XmlGetAttr(fieldNode, "var"); - if (var) { - auto *label = XmlGetAttr(fieldNode, "label"); - mColumnsNames.insert(var, (label != nullptr) ? label : var); - } - } - - for (auto *itemNode : TiXmlFilter(xNode, "item")) { - UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10); - for (auto *fieldNode : TiXmlFilter(itemNode, "field")) { - if (auto *var = XmlGetAttr(fieldNode, "var")) { - if (auto *textNode = XmlFirstChild(fieldNode, "value")) { - if (!mColumnsNames[var]) - mColumnsNames.insert(var, var); - pUserColumn->insert(var, textNode->GetText()); - } - } - } - - SearchResults.insert(pUserColumn); - } - } - else { - // 2. Field list search results info - for (auto *itemNode : TiXmlFilter(queryNode, "item")) { - UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10); - - auto *jid = XmlGetAttr(itemNode, "jid"); - char *keyReturned; - mColumnsNames.insertCopyKey("jid", "jid", &keyReturned); - mColumnsNames.insert("jid", keyReturned); - pUserColumn->insertCopyKey("jid", jid, nullptr); - - for (auto *child : TiXmlEnum(itemNode)) { - const char *szColumnName = child->Name(); - if (szColumnName) { - const char *pszChild = child->GetText(); - if (pszChild && *pszChild) { - mColumnsNames.insertCopyKey(szColumnName, "", &keyReturned); - mColumnsNames.insert(szColumnName, keyReturned); - pUserColumn->insertCopyKey(szColumnName, pszChild, nullptr); - } - } - } - - SearchResults.insert(pUserColumn); - } - } - } - else if (!mir_strcmp(type, "error")) { - const char *code = ""; - const char *description = ""; - auto *errorNode = XmlFirstChild(iqNode, "error"); - if (errorNode) { - code = XmlGetAttr(errorNode, "code"); - description = errorNode->GetText(); - } - - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); - - char buff[255]; - mir_snprintf(buff, TranslateU("Error %s %s\r\nTry to specify more detailed"), code, description); - if (searchHandleDlg) - SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff); - else - MessageBox(nullptr, Utf2T(buff), TranslateT("Search error"), MB_OK | MB_ICONSTOP); - return; - } - - SearchReturnResults(this, (HANDLE)id, SearchResults, mColumnsNames); - - for (auto &it : SearchResults) - delete ((UNIQUE_MAP*)it); - - //send success to finish searching - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); -} - -static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM) -{ - DestroyWindow(hwnd); - return TRUE; -} - -static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat) -{ - if (!dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf) { - for (int i = 0; i < dat->nJSInfCount; i++) { - if (dat->pJSInf[i].hwndValueItem) - DestroyWindow(dat->pJSInf[i].hwndValueItem); - if (dat->pJSInf[i].hwndCaptionItem) - DestroyWindow(dat->pJSInf[i].hwndCaptionItem); - if (dat->pJSInf[i].szFieldCaption) - free(dat->pJSInf[i].szFieldCaption); - if (dat->pJSInf[i].szFieldName) - free(dat->pJSInf[i].szFieldName); - } - free(dat->pJSInf); - dat->pJSInf = nullptr; - } - else EnumChildWindows(GetDlgItem(hwndDlg, IDC_FRAME), DeleteChildWindowsProc, 0); - - SendDlgItemMessage(hwndDlg, IDC_FRAME, WM_SETFONT, (WPARAM)SendMessage(hwndDlg, WM_GETFONT, 0, 0), 0); - dat->nJSInfCount = 0; - ShowWindow(GetDlgItem(hwndDlg, IDC_VSCROLL), SW_HIDE); - SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, TranslateT("Select/type search service URL above and press <Go>")); -} - -static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData *dat) -{ - HWND hFrame = GetDlgItem(hwndDlg, IDC_FRAME); - HWND hwndScroll = GetDlgItem(hwndDlg, IDC_VSCROLL); - RECT rc; - GetClientRect(hFrame, &rc); - GetClientRect(hFrame, &dat->frameRect); - dat->frameHeight = rc.bottom - rc.top; - if (dat->frameHeight < dat->CurrentHeight) { - ShowWindow(hwndScroll, SW_SHOW); - EnableWindow(hwndScroll, TRUE); - } - else ShowWindow(hwndScroll, SW_HIDE); - - SetScrollRange(hwndScroll, SB_CTL, 0, dat->CurrentHeight - dat->frameHeight, FALSE); -} - -int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData *dat) -{ - wchar_t szServerName[100]; - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), FALSE); - GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName)); - dat->CurrentHeight = 0; - dat->curPos = 0; - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE); - - JabberSearchFreeData(hwndDlg, dat); - JabberSearchRefreshFrameScroll(hwndDlg, dat); - - SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server")); - - if (!m_bJabberOnline) - return 0; - - searchHandleDlg = hwndDlg; - - CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultGetSearchFields, JABBER_IQ_TYPE_GET, T2Utf(szServerName)); - m_ThreadInfo->send(XmlNodeIq(pInfo) << XQUERY(JABBER_FEAT_JUD)); - return pInfo->GetIqId(); -} - -static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const wchar_t *szAddr) -{ - int lResult = SendDlgItemMessage(hwndDlg, IDC_SERVER, (UINT)CB_FINDSTRING, 0, (LPARAM)szAddr); - if (lResult == -1) - SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szAddr); -} - -void CJabberProto::SearchDeleteFromRecent(const char *szAddr, bool deleteLastFromDB) -{ - // search in recent - for (int i = 0; i < 10; i++) { - char key[30]; - mir_snprintf(key, "RecentlySearched_%d", i); - ptrA szValue(getUStringA(key)); - if (szValue == nullptr || mir_strcmpi(szAddr, szValue)) - continue; - - for (int j = i; j < 10; j++) { - mir_snprintf(key, "RecentlySearched_%d", j + 1); - szValue = getUStringA(key); - if (szValue != nullptr) { - mir_snprintf(key, "RecentlySearched_%d", j); - setUString(0, key, szValue); - } - else { - if (deleteLastFromDB) { - mir_snprintf(key, "RecentlySearched_%d", j); - delSetting(0, key); - } - break; - } - } - break; - } -} - -void CJabberProto::SearchAddToRecent(const char *szAddr, HWND hwndDialog) -{ - char key[30]; - SearchDeleteFromRecent(szAddr, true); - - for (int j = 9; j > 0; j--) { - mir_snprintf(key, "RecentlySearched_%d", j - 1); - ptrW szValue(getWStringA(key)); - if (szValue != nullptr) { - mir_snprintf(key, "RecentlySearched_%d", j); - setWString(0, key, szValue); - } - } - - mir_snprintf(key, "RecentlySearched_%d", 0); - setUString(key, szAddr); - if (hwndDialog) - JabberSearchAddUrlToRecentCombo(hwndDialog, Utf2T(szAddr)); -} - -static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - dat = new JabberSearchData(); - dat->ppro = (CJabberProto *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - - /* Server Combo box */ - ptrA jud(dat->ppro->getStringA("Jud")); - if (jud != nullptr) { - SetDlgItemTextA(hwndDlg, IDC_SERVER, jud); - SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, jud); - } - - //TO DO: Add Transports here - for (auto &it : dat->ppro->m_lstTransports) - if (it != nullptr) - JabberSearchAddUrlToRecentCombo(hwndDlg, Utf2T(it)); - - for (int i = 0; i < 10; i++) { - char key[30]; - mir_snprintf(key, "RecentlySearched_%d", i); - ptrW szValue(dat->ppro->getWStringA(key)); - if (szValue != nullptr) - JabberSearchAddUrlToRecentCombo(hwndDlg, szValue); - } - - //TO DO: Add 4 recently used - dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg, dat); - } - return TRUE; - - case WM_COMMAND: - if (LOWORD(wParam) == IDC_SERVER) { - switch (HIWORD(wParam)) { - case CBN_SETFOCUS: - PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); - return TRUE; - - case CBN_EDITCHANGE: - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); - return TRUE; - - case CBN_EDITUPDATE: - JabberSearchFreeData(hwndDlg, dat); - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); - return TRUE; - - case CBN_SELENDOK: - EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE); - PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_GO, BN_CLICKED), 0); - return TRUE; - } - } - else if (LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED) { - dat->ppro->SearchRenewFields(hwndDlg, dat); - return TRUE; - } - break; - - case WM_SIZE: - { - //Resize IDC_FRAME to take full size - RECT rcForm; - GetWindowRect(hwndDlg, &rcForm); - RECT rcFrame; - GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame); - rcFrame.bottom = rcForm.bottom; - SetWindowPos(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, 0, 0, rcFrame.right - rcFrame.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE); - GetWindowRect(GetDlgItem(hwndDlg, IDC_VSCROLL), &rcForm); - SetWindowPos(GetDlgItem(hwndDlg, IDC_VSCROLL), nullptr, 0, 0, rcForm.right - rcForm.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE); - JabberSearchRefreshFrameScroll(hwndDlg, dat); - } - return TRUE; - - case WM_USER + 11: - { - dat->fSearchRequestIsXForm = TRUE; - if (dat->xNode) { - dat->doc.DeleteNode(dat->xNode); - dat->xNode = nullptr; - } - TiXmlElement *pNode = (TiXmlElement *)wParam; - if (pNode) { - dat->xNode = pNode->DeepClone(&dat->doc)->ToElement(); - JabberFormCreateUI(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight, TRUE); - } - ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW); - dat->nJSInfCount = 1; - } - return TRUE; - - case WM_USER + 10: - { - Data *MyDat = (Data *)lParam; - if (MyDat) { - dat->fSearchRequestIsXForm = (BOOL)wParam; - dat->CurrentHeight = JabberSearchAddField(hwndDlg, MyDat); - mir_free(MyDat->Label); - mir_free(MyDat->Var); - mir_free(MyDat->defValue); - free(MyDat); - } - else { - JabberSearchRefreshFrameScroll(hwndDlg, dat); - ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - 0, nullptr, &(dat->frameRect)); - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE); - dat->curPos = 0; - } - } - return TRUE; - - case WM_MOUSEWHEEL: - { - short zDelta = GET_WHEEL_DELTA_WPARAM(wParam); - if (zDelta) { - int nScrollLines = 0; - SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void *)&nScrollLines, 0); - for (int i = 0; i < (nScrollLines + 1) / 2; i++) - SendMessage(hwndDlg, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0); - } - } - return TRUE; - - case WM_VSCROLL: - { - int pos; - if (dat != nullptr) { - pos = dat->curPos; - switch (LOWORD(wParam)) { - case SB_LINEDOWN: - pos += 10; - break; - case SB_LINEUP: - pos -= 10; - break; - case SB_PAGEDOWN: - pos += (dat->CurrentHeight - 10); - break; - case SB_PAGEUP: - pos -= (dat->CurrentHeight - 10); - break; - case SB_THUMBTRACK: - pos = HIWORD(wParam); - break; - } - if (pos > (dat->CurrentHeight - dat->frameHeight)) - pos = dat->CurrentHeight - dat->frameHeight; - if (pos < 0) - pos = 0; - if (dat->curPos != pos) { - ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect)); - SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE); - RECT Invalid = dat->frameRect; - if (dat->curPos - pos > 0) - Invalid.bottom = Invalid.top + (dat->curPos - pos); - else - Invalid.top = Invalid.bottom + (dat->curPos - pos); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN); - dat->curPos = pos; - } - } - } - return TRUE; - - case WM_DESTROY: - JabberSearchFreeData(hwndDlg, dat); - JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME)); - delete dat; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - return TRUE; - } - return FALSE; -} - -HWND CJabberProto::CreateExtendedSearchUI(HWND parent) -{ - if (parent && g_plugin.getInst()) { - ptrW szServer(getWStringA("LoginServer")); - if (szServer == nullptr || mir_wstrcmpi(szServer, L"S.ms")) - return CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this); - } - - return nullptr; // Failure -} - -////////////////////////////////////////////////////////////////////////// -// The function formats request to server - -HWND CJabberProto::SearchAdvanced(HWND hwndDlg) -{ - if (!m_bJabberOnline || !hwndDlg) - return nullptr; //error - - JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (!dat) - return nullptr; //error - - // check if server connected (at least one field exists) - if (dat->nJSInfCount == 0) - return nullptr; - - // formating request - bool fRequestNotEmpty = false; - - // get server name - wchar_t szServerName[100]; - GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName)); - - // formating query - CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, T2Utf(szServerName)); - XmlNodeIq iq(pInfo); - TiXmlElement *query = iq << XQUERY(JABBER_FEAT_JUD); - - if (m_tszSelectedLang) - iq << XATTR("xml:lang", m_tszSelectedLang); // i'm sure :) - - // next can be 2 cases: - // Forms: XEP-0055 Example 7 - if (dat->fSearchRequestIsXForm) { - fRequestNotEmpty = true; - JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), query, dat->xNode); - } - else { //and Simple fields: XEP-0055 Example 3 - for (int i = 0; i < dat->nJSInfCount; i++) { - wchar_t szFieldValue[100]; - GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, _countof(szFieldValue)); - if (szFieldValue[0] != 0) { - XmlAddChildA(query, T2Utf(dat->pJSInf[i].szFieldName).get(), T2Utf(szFieldValue).get()); - fRequestNotEmpty = true; - } - } - } - - if (fRequestNotEmpty) { - m_ThreadInfo->send(iq); - return (HWND)pInfo->GetIqId(); - } - return nullptr; -} +/*
+
+Jabber Protocol Plugin for Miranda NG
+
+Copyright (c) 2002-04 Santithorn Bunchua
+Copyright (c) 2005-12 George Hazan
+Copyright (c) 2007 Artem Shpynov
+Copyright (C) 2012-23 Miranda NG team
+
+Module implements a search according to XEP-0055: Jabber Search
+http://www.xmpp.org/extensions/xep-0055.html
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+#include <CommCtrl.h>
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling
+
+static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_COMMAND && lParam != 0) {
+ HWND hwndDlg = GetParent(hwnd);
+ JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (dat && lParam) {
+ int pos = dat->curPos;
+ RECT MineRect;
+ RECT FrameRect;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &FrameRect);
+ GetWindowRect((HWND)lParam, &MineRect);
+ if (MineRect.top - 10 < FrameRect.top) {
+ pos = dat->curPos + (MineRect.top - 14 - FrameRect.top);
+ if (pos < 0) pos = 0;
+ }
+ else if (MineRect.bottom > FrameRect.bottom) {
+ pos = dat->curPos + (MineRect.bottom - FrameRect.bottom);
+ if (dat->frameHeight + pos > dat->CurrentHeight)
+ pos = dat->CurrentHeight - dat->frameHeight;
+ }
+ if (pos != dat->curPos) {
+ ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE);
+ RECT Invalid = dat->frameRect;
+ if (dat->curPos - pos > 0)
+ Invalid.bottom = Invalid.top + (dat->curPos - pos);
+ else
+ Invalid.top = Invalid.bottom + (dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ }
+ }
+
+ // Transmit focus set notification to parent window
+ if (HIWORD(wParam) == EN_SETFOCUS)
+ PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg);
+ }
+
+ if (msg == WM_PAINT) {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hwnd, &ps);
+ FillRect(hdc, &(ps.rcPaint), GetSysColorBrush(COLOR_BTNFACE));
+ EndPaint(hwnd, &ps);
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Add Search field to form
+
+static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat)
+{
+ if (!FieldDat || !FieldDat->Label || !FieldDat->Var)
+ return FALSE;
+
+ HFONT hFont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0);
+ HWND hwndParent = GetDlgItem(hwndDlg, IDC_FRAME);
+ LONG_PTR frameExStyle = GetWindowLongPtr(hwndParent, GWL_EXSTYLE);
+ frameExStyle |= WS_EX_CONTROLPARENT;
+ SetWindowLongPtr(hwndParent, GWL_EXSTYLE, frameExStyle);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_FRAME), GWLP_WNDPROC, (LONG_PTR)JabberSearchFrameProc);
+
+ int CornerX = 1;
+ int CornerY = 1;
+ RECT rect;
+ GetClientRect(hwndParent, &rect);
+ int width = rect.right - 5 - CornerX;
+
+ int Order = (FieldDat->bHidden) ? -1 : FieldDat->Order;
+
+ HWND hwndLabel = CreateWindowEx(0, L"STATIC", (const wchar_t *)TranslateW(FieldDat->Label), WS_CHILD, CornerX, CornerY + Order * 40, width, 13, hwndParent, nullptr, g_plugin.getInst(), nullptr);
+ HWND hwndVar = CreateWindowEx(0 | WS_EX_CLIENTEDGE, L"EDIT", FieldDat->defValue, WS_CHILD | WS_TABSTOP, CornerX + 5, CornerY + Order * 40 + 14, width, 20, hwndParent, nullptr, g_plugin.getInst(), nullptr);
+ SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont, 0);
+ SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont, 0);
+ if (!FieldDat->bHidden) {
+ ShowWindow(hwndLabel, SW_SHOW);
+ ShowWindow(hwndVar, SW_SHOW);
+ EnableWindow(hwndLabel, !FieldDat->bReadOnly);
+ SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly, 0);
+ }
+
+ // remade list
+ // reallocation
+ JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (dat) {
+ dat->pJSInf = (JabberSearchFieldsInfo*)realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount + 1));
+ dat->pJSInf[dat->nJSInfCount].hwndCaptionItem = hwndLabel;
+ dat->pJSInf[dat->nJSInfCount].hwndValueItem = hwndVar;
+ dat->pJSInf[dat->nJSInfCount].szFieldCaption = wcsdup(FieldDat->Label);
+ dat->pJSInf[dat->nJSInfCount].szFieldName = wcsdup(FieldDat->Var);
+ dat->nJSInfCount++;
+ }
+ return CornerY + Order * 40 + 14 + 20;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Available search field request result handler (XEP-0055. Examples 2, 7)
+
+void CJabberProto::OnIqResultGetSearchFields(const TiXmlElement *iqNode, CJabberIqInfo*)
+{
+ if (!searchHandleDlg)
+ return;
+
+ const char *type = XmlGetAttr(iqNode, "type");
+ if (type == nullptr)
+ return;
+
+ if (!mir_strcmp(type, "result")) {
+ auto *queryNode = XmlFirstChild(iqNode, "query");
+ auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
+
+ ShowWindow(searchHandleDlg, SW_HIDE);
+ if (xNode) {
+ // 1. Form
+ PostMessage(searchHandleDlg, WM_USER + 11, (WPARAM)xNode, 0);
+ auto *xcNode = XmlFirstChild(xNode, "instructions");
+ if (xcNode)
+ SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, xcNode->GetText());
+ }
+ else {
+ int Order = 0;
+ for (auto *chNode : TiXmlEnum(queryNode)) {
+ if (!mir_strcmpi(chNode->Name(), "instructions") && chNode->GetText())
+ SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateW(Utf2T(chNode->GetText())));
+ else if (chNode->Name()) {
+ Data *MyData = (Data*)malloc(sizeof(Data));
+ memset(MyData, 0, sizeof(Data));
+
+ MyData->Label = mir_utf8decodeW(chNode->Name());
+ MyData->Var = mir_utf8decodeW(chNode->Name());
+ MyData->defValue = mir_utf8decodeW(chNode->GetText());
+ MyData->Order = Order;
+ if (MyData->defValue)
+ MyData->bReadOnly = true;
+ PostMessage(searchHandleDlg, WM_USER + 10, FALSE, (LPARAM)MyData);
+ Order++;
+ }
+ }
+ }
+
+ const char *szFrom = XmlGetAttr(iqNode, "from");
+ if (szFrom)
+ SearchAddToRecent(szFrom, searchHandleDlg);
+ PostMessage(searchHandleDlg, WM_USER + 10, 0, 0);
+ ShowWindow(searchHandleDlg, SW_SHOW);
+ }
+ else if (!mir_strcmp(type, "error")) {
+ const char *code = "";
+ const char *description = "";
+ auto *errorNode = XmlFirstChild(iqNode, "error");
+ if (errorNode) {
+ code = XmlGetAttr(errorNode, "code");
+ description = errorNode->GetText();
+ }
+
+ char buff[255];
+ mir_snprintf(buff, TranslateU("Error %s %s\r\nPlease select other server"), code, description);
+ SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff);
+ }
+ else SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateT("Error: unknown reply received\r\nPlease select other server"));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Return results to search dialog
+// The pmFields is the pointer to map of <field Name, field Label> Not unical but ordered
+// This can help to made result parser routines more simple
+
+static char *nickfields[] = { "nick", "nickname", "fullname", "name", "given", "first", "jid", nullptr };
+
+static int TCharKeyCmp(const char *p1, const char *p2)
+{
+ return mir_strcmpi(p1, p2);
+}
+
+static void SearchReturnResults(CJabberProto *ppro, HANDLE id, LIST<UNIQUE_MAP> &plUsersInfo, UNIQUE_MAP &pmAllFields)
+{
+ LIST<char> ListOfNonEmptyFields(20, TCharKeyCmp);
+ LIST<char> ListOfFields(20);
+
+ // lets fill the ListOfNonEmptyFields but in users order
+ for (auto &pmUserData : plUsersInfo) {
+ int nUserFields = pmUserData->getCount();
+ for (int j = 0; j < nUserFields; j++) {
+ char *var = pmUserData->getKeyName(j);
+ if (var && ListOfNonEmptyFields.getIndex(var) < 0)
+ ListOfNonEmptyFields.insert(var);
+ }
+ }
+
+ // now fill the ListOfFields but order is from pmAllFields
+ int nAllCount = pmAllFields.getCount();
+ for (int i = 0; i < nAllCount; i++) {
+ char *var = pmAllFields.getUnOrderedKeyName(i);
+ if (var && ListOfNonEmptyFields.getIndex(var) < 0)
+ continue;
+ ListOfFields.insert(var);
+ }
+
+ // now lets transfer field names
+ int nFieldCount = ListOfFields.getCount();
+
+ CUSTOMSEARCHRESULTS Results = { 0 };
+ Results.nSize = sizeof(Results);
+ Results.pszFields = (wchar_t**)mir_alloc(sizeof(wchar_t*)*nFieldCount);
+ Results.nFieldCount = nFieldCount;
+
+ // Sending Columns Titles
+ for (int i = 0; i < nFieldCount; i++) {
+ char *var = ListOfFields[i];
+ if (var)
+ Results.pszFields[i] = mir_utf8decodeW(pmAllFields[var]);
+ }
+
+ Results.psr.cbSize = 0; // sending column names
+ ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results);
+ for (int i = 0; i < nFieldCount; i++)
+ replaceStrW(Results.pszFields[i], nullptr);
+
+ // Sending Users Data
+ Results.psr.cbSize = sizeof(Results.psr);
+
+ for (auto &pmUserData : plUsersInfo) {
+ for (int j = 0; j < nFieldCount; j++) {
+ char *var = ListOfFields[j];
+ char *value = pmUserData->operator [](var);
+ Results.pszFields[j] = value ? mir_utf8decodeW(value) : mir_wstrdup(L" ");
+ if (!mir_strcmpi(var, "jid") && value)
+ Results.psr.id.w = Results.pszFields[j];
+ }
+
+ const char *nick = nullptr;
+ for (int k = 0; k < _countof(nickfields) && !nick; k++)
+ nick = pmUserData->operator [](nickfields[k]);
+
+ if (nick) {
+ Utf2T wszNick(nick);
+ wchar_t buff[200];
+ if (mir_wstrcmpi(wszNick, Results.psr.id.w))
+ mir_snwprintf(buff, L"%s (%s)", wszNick.get(), Results.psr.id.w);
+ else
+ wcsncpy_s(buff, wszNick, _TRUNCATE);
+
+ Results.psr.nick.w = buff;
+ }
+ else Results.psr.nick.w = L"";
+ Results.psr.flags = PSR_UNICODE;
+
+ ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results);
+ for (int i = 0; i < nFieldCount; i++)
+ replaceStrW(Results.pszFields[i], nullptr);
+ }
+
+ mir_free(Results.pszFields);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Search field request result handler (XEP-0055. Examples 3, 8)
+
+void CJabberProto::OnIqResultAdvancedSearch(const TiXmlElement *iqNode, CJabberIqInfo*)
+{
+ const char *type;
+ int id;
+
+ UNIQUE_MAP mColumnsNames(10);
+ LIST<UNIQUE_MAP> SearchResults(2);
+
+ if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = XmlGetAttr(iqNode, "type")) == nullptr)) {
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
+ return;
+ }
+
+ if (!mir_strcmp(type, "result")) {
+ auto *queryNode = XmlFirstChild(iqNode, "query");
+ auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
+ if (xNode) {
+ // 1. Form search results info
+ for (auto *fieldNode : TiXmlFilter(XmlFirstChild(xNode, "reported"), "field")) {
+ auto *var = XmlGetAttr(fieldNode, "var");
+ if (var) {
+ auto *label = XmlGetAttr(fieldNode, "label");
+ mColumnsNames.insert(var, (label != nullptr) ? label : var);
+ }
+ }
+
+ for (auto *itemNode : TiXmlFilter(xNode, "item")) {
+ UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10);
+ for (auto *fieldNode : TiXmlFilter(itemNode, "field")) {
+ if (auto *var = XmlGetAttr(fieldNode, "var")) {
+ if (auto *textNode = XmlFirstChild(fieldNode, "value")) {
+ if (!mColumnsNames[var])
+ mColumnsNames.insert(var, var);
+ pUserColumn->insert(var, textNode->GetText());
+ }
+ }
+ }
+
+ SearchResults.insert(pUserColumn);
+ }
+ }
+ else {
+ // 2. Field list search results info
+ for (auto *itemNode : TiXmlFilter(queryNode, "item")) {
+ UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10);
+
+ auto *jid = XmlGetAttr(itemNode, "jid");
+ char *keyReturned;
+ mColumnsNames.insertCopyKey("jid", "jid", &keyReturned);
+ mColumnsNames.insert("jid", keyReturned);
+ pUserColumn->insertCopyKey("jid", jid, nullptr);
+
+ for (auto *child : TiXmlEnum(itemNode)) {
+ const char *szColumnName = child->Name();
+ if (szColumnName) {
+ const char *pszChild = child->GetText();
+ if (pszChild && *pszChild) {
+ mColumnsNames.insertCopyKey(szColumnName, "", &keyReturned);
+ mColumnsNames.insert(szColumnName, keyReturned);
+ pUserColumn->insertCopyKey(szColumnName, pszChild, nullptr);
+ }
+ }
+ }
+
+ SearchResults.insert(pUserColumn);
+ }
+ }
+ }
+ else if (!mir_strcmp(type, "error")) {
+ const char *code = "";
+ const char *description = "";
+ auto *errorNode = XmlFirstChild(iqNode, "error");
+ if (errorNode) {
+ code = XmlGetAttr(errorNode, "code");
+ description = errorNode->GetText();
+ }
+
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
+
+ char buff[255];
+ mir_snprintf(buff, TranslateU("Error %s %s\r\nTry to specify more detailed"), code, description);
+ if (searchHandleDlg)
+ SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff);
+ else
+ MessageBox(nullptr, Utf2T(buff), TranslateT("Search error"), MB_OK | MB_ICONSTOP);
+ return;
+ }
+
+ SearchReturnResults(this, (HANDLE)id, SearchResults, mColumnsNames);
+
+ for (auto &it : SearchResults)
+ delete ((UNIQUE_MAP*)it);
+
+ //send success to finish searching
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
+}
+
+static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM)
+{
+ DestroyWindow(hwnd);
+ return TRUE;
+}
+
+static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat)
+{
+ if (!dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf) {
+ for (int i = 0; i < dat->nJSInfCount; i++) {
+ if (dat->pJSInf[i].hwndValueItem)
+ DestroyWindow(dat->pJSInf[i].hwndValueItem);
+ if (dat->pJSInf[i].hwndCaptionItem)
+ DestroyWindow(dat->pJSInf[i].hwndCaptionItem);
+ if (dat->pJSInf[i].szFieldCaption)
+ free(dat->pJSInf[i].szFieldCaption);
+ if (dat->pJSInf[i].szFieldName)
+ free(dat->pJSInf[i].szFieldName);
+ }
+ free(dat->pJSInf);
+ dat->pJSInf = nullptr;
+ }
+ else EnumChildWindows(GetDlgItem(hwndDlg, IDC_FRAME), DeleteChildWindowsProc, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_FRAME, WM_SETFONT, (WPARAM)SendMessage(hwndDlg, WM_GETFONT, 0, 0), 0);
+ dat->nJSInfCount = 0;
+ ShowWindow(GetDlgItem(hwndDlg, IDC_VSCROLL), SW_HIDE);
+ SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, TranslateT("Select/type search service URL above and press <Go>"));
+}
+
+static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData *dat)
+{
+ HWND hFrame = GetDlgItem(hwndDlg, IDC_FRAME);
+ HWND hwndScroll = GetDlgItem(hwndDlg, IDC_VSCROLL);
+ RECT rc;
+ GetClientRect(hFrame, &rc);
+ GetClientRect(hFrame, &dat->frameRect);
+ dat->frameHeight = rc.bottom - rc.top;
+ if (dat->frameHeight < dat->CurrentHeight) {
+ ShowWindow(hwndScroll, SW_SHOW);
+ EnableWindow(hwndScroll, TRUE);
+ }
+ else ShowWindow(hwndScroll, SW_HIDE);
+
+ SetScrollRange(hwndScroll, SB_CTL, 0, dat->CurrentHeight - dat->frameHeight, FALSE);
+}
+
+int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData *dat)
+{
+ wchar_t szServerName[100];
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO), FALSE);
+ GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName));
+ dat->CurrentHeight = 0;
+ dat->curPos = 0;
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
+
+ JabberSearchFreeData(hwndDlg, dat);
+ JabberSearchRefreshFrameScroll(hwndDlg, dat);
+
+ SetDlgItemText(hwndDlg, IDC_INSTRUCTIONS, m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server"));
+
+ if (!m_bJabberOnline)
+ return 0;
+
+ searchHandleDlg = hwndDlg;
+
+ CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultGetSearchFields, JABBER_IQ_TYPE_GET, T2Utf(szServerName));
+ m_ThreadInfo->send(XmlNodeIq(pInfo) << XQUERY(JABBER_FEAT_JUD));
+ return pInfo->GetIqId();
+}
+
+static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const wchar_t *szAddr)
+{
+ int lResult = SendDlgItemMessage(hwndDlg, IDC_SERVER, (UINT)CB_FINDSTRING, 0, (LPARAM)szAddr);
+ if (lResult == -1)
+ SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szAddr);
+}
+
+void CJabberProto::SearchDeleteFromRecent(const char *szAddr, bool deleteLastFromDB)
+{
+ // search in recent
+ for (int i = 0; i < 10; i++) {
+ char key[30];
+ mir_snprintf(key, "RecentlySearched_%d", i);
+ ptrA szValue(getUStringA(key));
+ if (szValue == nullptr || mir_strcmpi(szAddr, szValue))
+ continue;
+
+ for (int j = i; j < 10; j++) {
+ mir_snprintf(key, "RecentlySearched_%d", j + 1);
+ szValue = getUStringA(key);
+ if (szValue != nullptr) {
+ mir_snprintf(key, "RecentlySearched_%d", j);
+ setUString(0, key, szValue);
+ }
+ else {
+ if (deleteLastFromDB) {
+ mir_snprintf(key, "RecentlySearched_%d", j);
+ delSetting(0, key);
+ }
+ break;
+ }
+ }
+ break;
+ }
+}
+
+void CJabberProto::SearchAddToRecent(const char *szAddr, HWND hwndDialog)
+{
+ char key[30];
+ SearchDeleteFromRecent(szAddr, true);
+
+ for (int j = 9; j > 0; j--) {
+ mir_snprintf(key, "RecentlySearched_%d", j - 1);
+ ptrW szValue(getWStringA(key));
+ if (szValue != nullptr) {
+ mir_snprintf(key, "RecentlySearched_%d", j);
+ setWString(0, key, szValue);
+ }
+ }
+
+ mir_snprintf(key, "RecentlySearched_%d", 0);
+ setUString(key, szAddr);
+ if (hwndDialog)
+ JabberSearchAddUrlToRecentCombo(hwndDialog, Utf2T(szAddr));
+}
+
+static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ dat = new JabberSearchData();
+ dat->ppro = (CJabberProto *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ /* Server Combo box */
+ ptrA jud(dat->ppro->getStringA("Jud"));
+ if (jud != nullptr) {
+ SetDlgItemTextA(hwndDlg, IDC_SERVER, jud);
+ SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, jud);
+ }
+
+ //TO DO: Add Transports here
+ for (auto &it : dat->ppro->m_lstTransports)
+ if (it != nullptr)
+ JabberSearchAddUrlToRecentCombo(hwndDlg, Utf2T(it));
+
+ for (int i = 0; i < 10; i++) {
+ char key[30];
+ mir_snprintf(key, "RecentlySearched_%d", i);
+ ptrW szValue(dat->ppro->getWStringA(key));
+ if (szValue != nullptr)
+ JabberSearchAddUrlToRecentCombo(hwndDlg, szValue);
+ }
+
+ //TO DO: Add 4 recently used
+ dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg, dat);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_SERVER) {
+ switch (HIWORD(wParam)) {
+ case CBN_SETFOCUS:
+ PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg);
+ return TRUE;
+
+ case CBN_EDITCHANGE:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE);
+ return TRUE;
+
+ case CBN_EDITUPDATE:
+ JabberSearchFreeData(hwndDlg, dat);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE);
+ return TRUE;
+
+ case CBN_SELENDOK:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO), TRUE);
+ PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_GO, BN_CLICKED), 0);
+ return TRUE;
+ }
+ }
+ else if (LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED) {
+ dat->ppro->SearchRenewFields(hwndDlg, dat);
+ return TRUE;
+ }
+ break;
+
+ case WM_SIZE:
+ {
+ //Resize IDC_FRAME to take full size
+ RECT rcForm;
+ GetWindowRect(hwndDlg, &rcForm);
+ RECT rcFrame;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame);
+ rcFrame.bottom = rcForm.bottom;
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, 0, 0, rcFrame.right - rcFrame.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE);
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_VSCROLL), &rcForm);
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_VSCROLL), nullptr, 0, 0, rcForm.right - rcForm.left, rcFrame.bottom - rcFrame.top, SWP_NOZORDER | SWP_NOMOVE);
+ JabberSearchRefreshFrameScroll(hwndDlg, dat);
+ }
+ return TRUE;
+
+ case WM_USER + 11:
+ {
+ dat->fSearchRequestIsXForm = TRUE;
+ if (dat->xNode) {
+ dat->doc.DeleteNode(dat->xNode);
+ dat->xNode = nullptr;
+ }
+ TiXmlElement *pNode = (TiXmlElement *)wParam;
+ if (pNode) {
+ dat->xNode = pNode->DeepClone(&dat->doc)->ToElement();
+ JabberFormCreateUI(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight, TRUE);
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW);
+ dat->nJSInfCount = 1;
+ }
+ return TRUE;
+
+ case WM_USER + 10:
+ {
+ Data *MyDat = (Data *)lParam;
+ if (MyDat) {
+ dat->fSearchRequestIsXForm = (BOOL)wParam;
+ dat->CurrentHeight = JabberSearchAddField(hwndDlg, MyDat);
+ mir_free(MyDat->Label);
+ mir_free(MyDat->Var);
+ mir_free(MyDat->defValue);
+ free(MyDat);
+ }
+ else {
+ JabberSearchRefreshFrameScroll(hwndDlg, dat);
+ ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - 0, nullptr, &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
+ dat->curPos = 0;
+ }
+ }
+ return TRUE;
+
+ case WM_MOUSEWHEEL:
+ {
+ short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+ if (zDelta) {
+ int nScrollLines = 0;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void *)&nScrollLines, 0);
+ for (int i = 0; i < (nScrollLines + 1) / 2; i++)
+ SendMessage(hwndDlg, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0);
+ }
+ }
+ return TRUE;
+
+ case WM_VSCROLL:
+ {
+ int pos;
+ if (dat != nullptr) {
+ pos = dat->curPos;
+ switch (LOWORD(wParam)) {
+ case SB_LINEDOWN:
+ pos += 10;
+ break;
+ case SB_LINEUP:
+ pos -= 10;
+ break;
+ case SB_PAGEDOWN:
+ pos += (dat->CurrentHeight - 10);
+ break;
+ case SB_PAGEUP:
+ pos -= (dat->CurrentHeight - 10);
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD(wParam);
+ break;
+ }
+ if (pos > (dat->CurrentHeight - dat->frameHeight))
+ pos = dat->CurrentHeight - dat->frameHeight;
+ if (pos < 0)
+ pos = 0;
+ if (dat->curPos != pos) {
+ ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, nullptr, &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE);
+ RECT Invalid = dat->frameRect;
+ if (dat->curPos - pos > 0)
+ Invalid.bottom = Invalid.top + (dat->curPos - pos);
+ else
+ Invalid.top = Invalid.bottom + (dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ }
+ }
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ JabberSearchFreeData(hwndDlg, dat);
+ JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME));
+ delete dat;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+HWND CJabberProto::CreateExtendedSearchUI(HWND parent)
+{
+ if (parent && g_plugin.getInst()) {
+ ptrW szServer(getWStringA("LoginServer"));
+ if (szServer == nullptr || mir_wstrcmpi(szServer, L"S.ms"))
+ return CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this);
+ }
+
+ return nullptr; // Failure
+}
+
+//////////////////////////////////////////////////////////////////////////
+// The function formats request to server
+
+HWND CJabberProto::SearchAdvanced(HWND hwndDlg)
+{
+ if (!m_bJabberOnline || !hwndDlg)
+ return nullptr; //error
+
+ JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (!dat)
+ return nullptr; //error
+
+ // check if server connected (at least one field exists)
+ if (dat->nJSInfCount == 0)
+ return nullptr;
+
+ // formating request
+ bool fRequestNotEmpty = false;
+
+ // get server name
+ wchar_t szServerName[100];
+ GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName));
+
+ // formating query
+ CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, T2Utf(szServerName));
+ XmlNodeIq iq(pInfo);
+ TiXmlElement *query = iq << XQUERY(JABBER_FEAT_JUD);
+
+ if (m_tszSelectedLang)
+ iq << XATTR("xml:lang", m_tszSelectedLang); // i'm sure :)
+
+ // next can be 2 cases:
+ // Forms: XEP-0055 Example 7
+ if (dat->fSearchRequestIsXForm) {
+ fRequestNotEmpty = true;
+ JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), query, dat->xNode);
+ }
+ else { //and Simple fields: XEP-0055 Example 3
+ for (int i = 0; i < dat->nJSInfCount; i++) {
+ wchar_t szFieldValue[100];
+ GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, _countof(szFieldValue));
+ if (szFieldValue[0] != 0) {
+ XmlAddChildA(query, T2Utf(dat->pJSInf[i].szFieldName).get(), T2Utf(szFieldValue).get());
+ fRequestNotEmpty = true;
+ }
+ }
+ }
+
+ if (fRequestNotEmpty) {
+ m_ThreadInfo->send(iq);
+ return (HWND)pInfo->GetIqId();
+ }
+ return nullptr;
+}
diff --git a/protocols/JabberG/src/jabber_search.h b/protocols/JabberG/src/jabber_search.h index 4916afb29a..3bfaa2df8a 100644 --- a/protocols/JabberG/src/jabber_search.h +++ b/protocols/JabberG/src/jabber_search.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Artem Shpynov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
Module implements a search according to XEP-0055: Jabber Search
http://www.xmpp.org/extensions/xep-0055.html
diff --git a/protocols/JabberG/src/jabber_secur.cpp b/protocols/JabberG/src/jabber_secur.cpp index a1cf3faccc..853016823d 100644 --- a/protocols/JabberG/src/jabber_secur.cpp +++ b/protocols/JabberG/src/jabber_secur.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_secur.h b/protocols/JabberG/src/jabber_secur.h index 133d55e840..90ad39bd60 100644 --- a/protocols/JabberG/src/jabber_secur.h +++ b/protocols/JabberG/src/jabber_secur.h @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_send_manager.cpp b/protocols/JabberG/src/jabber_send_manager.cpp index c70d4017af..67031c0567 100644 --- a/protocols/JabberG/src/jabber_send_manager.cpp +++ b/protocols/JabberG/src/jabber_send_manager.cpp @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_send_manager.h b/protocols/JabberG/src/jabber_send_manager.h index d223782694..9b98db0458 100644 --- a/protocols/JabberG/src/jabber_send_manager.h +++ b/protocols/JabberG/src/jabber_send_manager.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-08 George Hazan
Copyright (c) 2007 Maxim Mluhov
Copyright (c) 2008-09 Dmitriy Chervov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_strm_mgmt.cpp b/protocols/JabberG/src/jabber_strm_mgmt.cpp index e0f95eb2a8..a081570cfe 100644 --- a/protocols/JabberG/src/jabber_strm_mgmt.cpp +++ b/protocols/JabberG/src/jabber_strm_mgmt.cpp @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG
-Copyright (c) 2018-22 Miranda NG team
+Copyright (c) 2018-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_strm_mgmt.h b/protocols/JabberG/src/jabber_strm_mgmt.h index 412127d1d2..158c72fefb 100644 --- a/protocols/JabberG/src/jabber_strm_mgmt.h +++ b/protocols/JabberG/src/jabber_strm_mgmt.h @@ -2,7 +2,7 @@ Jabber Protocol Plugin for Miranda NG
-Copyright (c) 2018-22 Miranda NG team
+Copyright (c) 2018-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_svc.cpp b/protocols/JabberG/src/jabber_svc.cpp index 63fbd74e8b..5252c0865f 100644 --- a/protocols/JabberG/src/jabber_svc.cpp +++ b/protocols/JabberG/src/jabber_svc.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp index 212c6bdd6a..0ad67e79dd 100644 --- a/protocols/JabberG/src/jabber_thread.cpp +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_treelist.cpp b/protocols/JabberG/src/jabber_treelist.cpp index 21fcb1fbc5..35467cbd1c 100644 --- a/protocols/JabberG/src/jabber_treelist.cpp +++ b/protocols/JabberG/src/jabber_treelist.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_userinfo.cpp b/protocols/JabberG/src/jabber_userinfo.cpp index f164252fbd..0f9271481a 100644 --- a/protocols/JabberG/src/jabber_userinfo.cpp +++ b/protocols/JabberG/src/jabber_userinfo.cpp @@ -1,912 +1,912 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (c) 2007 Maxim Mluhov -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "stdafx.h" - -#include <fcntl.h> -#include <io.h> -#include <sys/stat.h> - -#include "jabber_list.h" - -static MWindowList hUserInfoList = nullptr; - -class JabberBaseUserInfoDlg : public CUserInfoPageDlg -{ - INT_PTR DoRefresh(UINT, WPARAM, LPARAM) - { - OnRefresh(); - return 0; - } - -protected: - UI_MESSAGE_MAP(JabberBaseUserInfoDlg, CUserInfoPageDlg); - UI_MESSAGE(WM_PROTO_REFRESH, DoRefresh); - UI_MESSAGE(WM_JABBER_REFRESH_VCARD, DoRefresh); - UI_MESSAGE_MAP_END(); - - CJabberProto *ppro; - - JabberBaseUserInfoDlg(CJabberProto *_ppro, int dlgId) : - CUserInfoPageDlg(g_plugin, dlgId), - ppro(_ppro) - {} -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserInfoDlgProc - main user info dialog - -enum -{ - INFOLINE_DELETE = 0x80000000, - INFOLINE_MASK = 0x7fffffff, - INFOLINE_BAD_ID = 0x7fffffff, - - INFOLINE_NAME = 1, - INFOLINE_MOOD, - INFOLINE_ACTIVITY, - INFOLINE_TUNE, - INFOLINE_OFFLINE, - INFOLINE_MESSAGE, - INFOLINE_SOFTWARE, - INFOLINE_VERSION, - INFOLINE_SYSTEM, - INFOLINE_PRIORITY, - INFOLINE_IDLE, - INFOLINE_CAPS, - INFOLINE_SOFTWARE_INFORMATION, - INFOLINE_SUBSCRIPTION, - INFOLINE_LOGOFF, - INFOLINE_LOGOFF_MSG, - INFOLINE_LASTACTIVE, -}; - -__forceinline uint32_t sttInfoLineId(uint32_t res, uint32_t type, uint32_t line = 0) -{ - return - (type << 24) & 0x7f000000 | - (res << 12) & 0x00fff000 | - (line) & 0x00000fff; -} - -class JabberUserInfoDlg : public JabberBaseUserInfoDlg -{ - JABBER_LIST_ITEM *item = nullptr; - int resourcesCount = -1; - - CCtrlTreeView m_tree; - - UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg); - UI_MESSAGE(WM_PROTO_CHECK_ONLINE, OnCheckOnline); - UI_MESSAGE_MAP_END(); - - INT_PTR OnCheckOnline(UINT, WPARAM, LPARAM) - { - if (!ppro->m_bJabberOnline) - item = nullptr; - else - OnRefresh(); - return 0; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // User information block - - void CleanupInfo(int stage) - { - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - - switch (stage) { - case 0: - tvi.lParam |= INFOLINE_DELETE; - m_tree.SetItem(&tvi); - break; - - case 1: - if (tvi.lParam & INFOLINE_DELETE) { - hItem = m_tree.GetNextSibling(hItem); - m_tree.DeleteItem(tvi.hItem); - continue; - } - break; - } - - HTREEITEM hItemTmp = nullptr; - if (hItemTmp = m_tree.GetChild(hItem)) - hItem = hItemTmp; - else if (hItemTmp = m_tree.GetNextSibling(hItem)) - hItem = hItemTmp; - else { - while (true) { - if (!(hItem = m_tree.GetParent(hItem))) break; - if (hItemTmp = m_tree.GetNextSibling(hItem)) { - hItem = hItemTmp; - break; - } - } - } - } - } - - HTREEITEM FindInfoLine(HTREEITEM htiRoot, LPARAM id = INFOLINE_BAD_ID) - { - if (id == INFOLINE_BAD_ID) return nullptr; - for (HTREEITEM hti = m_tree.GetChild(htiRoot); hti; hti = m_tree.GetNextSibling(hti)) { - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hti; - m_tree.GetItem(&tvi); - if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK)) - return hti; - } - return nullptr; - } - - HTREEITEM FillInfoLine(HTREEITEM htiRoot, HICON hIcon, const wchar_t *title, const char *value, LPARAM id = INFOLINE_BAD_ID, bool expand = false) - { - HTREEITEM hti = FindInfoLine(htiRoot, id); - - Utf2T wszValue(value); - const wchar_t *pwszValue = (value == nullptr) ? TranslateT("<not specified>") : wszValue; - wchar_t buf[256]; - if (title) - mir_snwprintf(buf, L"%s: %s", title, pwszValue); - else - mir_wstrncpy(buf, pwszValue, _countof(buf)); - - TVINSERTSTRUCT tvis = {}; - tvis.hParent = htiRoot; - tvis.hInsertAfter = TVI_LAST; - tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM; - tvis.itemex.pszText = buf; - tvis.itemex.lParam = id; - - if (hIcon) { - HIMAGELIST himl = m_tree.GetImageList(TVSIL_NORMAL); - tvis.itemex.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.itemex.iImage = - tvis.itemex.iSelectedImage = ImageList_AddIcon(himl, hIcon); - IcoLib_ReleaseIcon(hIcon); - } - - if (hti) { - tvis.itemex.mask |= TVIF_HANDLE; - tvis.itemex.hItem = hti; - m_tree.SetItem(&tvis.itemex); - } - else { - tvis.itemex.mask |= TVIF_STATE; - tvis.itemex.stateMask = TVIS_EXPANDED; - tvis.itemex.state = expand ? TVIS_EXPANDED : 0; - hti = m_tree.InsertItem(&tvis); - } - - return hti; - } - - void FillAdvStatusInfo(HTREEITEM htiRoot, uint32_t dwInfoLine, MCONTACT hContact, wchar_t *szTitle, char *pszSlot) - { - ptrA szAdvStatusIcon(ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON)); - ptrW szAdvStatusTitle(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE)); - ptrW szAdvStatusText(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT)); - - if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) { - wchar_t szText[2048]; - if (szAdvStatusText && *szAdvStatusText) - mir_snwprintf(szText, L"%s (%s)", TranslateW(szAdvStatusTitle), szAdvStatusText.get()); - else - wcsncpy_s(szText, TranslateW(szAdvStatusTitle), _TRUNCATE); - FillInfoLine(htiRoot, IcoLib_GetIcon(szAdvStatusIcon), szTitle, T2Utf(szText), dwInfoLine); - } - } - - void FillResourceInfo(HTREEITEM htiRoot, int resource) - { - HTREEITEM htiResource = htiRoot; - pResourceStatus r = resource ? item->arResources[resource - 1] : item->getTemp(); - - if (r->m_szResourceName && *r->m_szResourceName) - htiResource = FillInfoLine(htiRoot, Skin_LoadProtoIcon(ppro->m_szModuleName, r->m_iStatus), - TranslateT("Resource"), r->m_szResourceName, sttInfoLineId(resource, INFOLINE_NAME), true); - - // StatusMsg - FillInfoLine(htiResource, nullptr /*Skin_LoadIcon(SKINICON_EVENT_MESSAGE)*/, - TranslateT("Message"), r->m_szStatusMessage, - sttInfoLineId(resource, INFOLINE_MESSAGE)); - - // Software - if (CJabberClientPartialCaps *pCaps = r->m_pCaps) { - HICON hIcon = nullptr; - - if (ServiceExists(MS_FP_GETCLIENTICONT)) { - if (pCaps->GetSoft()) { - wchar_t buf[256]; - mir_snwprintf(buf, L"%s %s", pCaps->GetSoft(), pCaps->GetSoftVer()); - hIcon = Finger_GetClientIcon(buf, 0); - } - } - - FillInfoLine(htiResource, hIcon, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE)); - - // Version - FillInfoLine(htiResource, nullptr, TranslateT("Version"), pCaps->GetSoftMir() ? pCaps->GetSoftMir() : pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_VERSION)); - - // System - FillInfoLine(htiResource, nullptr, TranslateT("System"), pCaps->GetOsVer() ? pCaps->GetOsVer() : pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SYSTEM)); - - if (hIcon) - DestroyIcon(hIcon); - } - - // Resource priority - char buf[256]; - itoa(r->m_iPriority, buf, 10); - FillInfoLine(htiResource, nullptr, TranslateT("Resource priority"), buf, sttInfoLineId(resource, INFOLINE_PRIORITY)); - - // Idle - if (r->m_dwIdleStartTime != -1) { - if (r->m_dwIdleStartTime != 0) { - mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf)); - size_t len = mir_strlen(buf); - if (len > 0) - buf[len - 1] = 0; - } - else mir_strncpy(buf, TranslateU("<currently online>"), _countof(buf)); - - FillInfoLine(htiResource, nullptr, TranslateT("Last activity"), buf, sttInfoLineId(resource, INFOLINE_IDLE)); - } - - // caps - JabberCapsBits jcb = ppro->GetResourceCapabilities(MakeJid(item->jid, r->m_szResourceName), r); - if (!(jcb & JABBER_RESOURCE_CAPS_ERROR)) { - HTREEITEM htiCaps = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Client capabilities"), sttInfoLineId(resource, INFOLINE_CAPS)); - int i; - for (i = 0; i < g_cJabberFeatCapPairs; i++) - if (jcb & g_JabberFeatCapPairs[i].jcbCap) { - char szDescription[1024]; - if (g_JabberFeatCapPairs[i].tszDescription) - mir_snprintf(szDescription, "%s (%s)", TranslateU(g_JabberFeatCapPairs[i].tszDescription), g_JabberFeatCapPairs[i].szFeature); - else - strncpy_s(szDescription, g_JabberFeatCapPairs[i].szFeature, _TRUNCATE); - FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i)); - } - - for (auto &it : ppro->m_lstJabberFeatCapPairsDynamic) { - if (jcb & it->jcbCap) { - char szDescription[1024]; - if (it->szDescription) - mir_snprintf(szDescription, "%s (%s)", TranslateU(it->szDescription), it->szFeature); - else - strncpy_s(szDescription, it->szFeature, _TRUNCATE); - FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i++)); - } - } - } - - // Software info - HTREEITEM htiSoftwareInfo = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Software information"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION)); - int nLineId = 0; - if (CJabberClientPartialCaps *pCaps = r->m_pCaps) { - if (pCaps->GetOs()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system"), pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetOsVer()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system version"), pCaps->GetOsVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetSoft()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetSoftVer()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software version"), pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - if (pCaps->GetSoftMir()) - FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Miranda core version"), pCaps->GetSoftMir(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++)); - } - } - - void FillUserInfo() - { - m_tree.SendMsg(WM_SETREDRAW, FALSE, 0); - - CleanupInfo(0); - - HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", item->jid, sttInfoLineId(0, INFOLINE_NAME), true); - - if (MCONTACT hContact = ppro->HContactFromJID(item->jid)) { - FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD); - FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY); - FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE); - } - - // subscription - switch (item->subscription) { - case SUB_BOTH: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("both"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - case SUB_TO: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("to"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - case SUB_FROM: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("from"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - default: - FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("none"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION)); - break; - } - - // logoff - char buf[256]; - JABBER_RESOURCE_STATUS *r = item->getTemp(); - if (r->m_dwIdleStartTime != -1) { - if (r->m_dwIdleStartTime > 0) { - mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf)); - size_t len = mir_strlen(buf); - if (len > 0) - buf[len - 1] = 0; - } - else mir_strncpy(buf, TranslateU("<currently online>"), _countof(buf)); - - FillInfoLine(htiRoot, nullptr, - (item->jid && strchr(item->jid, '@')) ? TranslateT("Last logoff time") : TranslateT("Uptime"), buf, - sttInfoLineId(0, INFOLINE_LOGOFF)); - } - - if (r->m_szStatusMessage) - FillInfoLine(htiRoot, nullptr, TranslateT("Logoff message"), r->m_szStatusMessage, sttInfoLineId(0, INFOLINE_LOGOFF_MSG)); - - // activity - if (item->m_pLastSeenResource) - mir_strncpy(buf, item->m_pLastSeenResource->m_szResourceName, _countof(buf)); - else - mir_strncpy(buf, TranslateU("<no information available>"), _countof(buf)); - FillInfoLine(htiRoot, nullptr, TranslateT("Last active resource"), buf, sttInfoLineId(0, INFOLINE_LASTACTIVE)); - - // resources - if (item->arResources.getCount()) { - for (int i = 0; i < item->arResources.getCount(); i++) - FillResourceInfo(htiRoot, i + 1); - } - else if (!strchr(item->jid, '@') || (r->m_iStatus != ID_STATUS_OFFLINE)) - FillResourceInfo(htiRoot, 0); - - CleanupInfo(1); - m_tree.SendMsg(WM_SETREDRAW, TRUE, 0); - - RedrawWindow(m_tree.GetHwnd(), nullptr, nullptr, RDW_INVALIDATE); - } - -public: - JabberUserInfoDlg(CJabberProto *_ppro) : - JabberBaseUserInfoDlg(_ppro, IDD_INFO_JABBER), - m_tree(this, IDC_TV_INFO) - { - m_tree.OnBuildMenu = Callback(this, &JabberUserInfoDlg::onMenu_Tree); - } - - bool OnInitDialog() override - { - ppro->WindowSubscribe(m_hwnd); - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_USERDETAILS); - - RECT rc; - GetClientRect(m_hwnd, &rc); - MoveWindow(m_tree.GetHwnd(), 5, 5, rc.right - 10, rc.bottom - 10, TRUE); - - HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); - ImageList_AddSkinIcon(himl, SKINICON_OTHER_SMALLDOT); - TreeView_SetImageList(m_tree.GetHwnd(), himl, TVSIL_NORMAL); - - WindowList_Add(hUserInfoList, m_hwnd, m_hContact); - return true; - } - - void OnDestroy() override - { - ppro->WindowUnsubscribe(m_hwnd); - WindowList_Remove(hUserInfoList, m_hwnd); - ImageList_Destroy(m_tree.SetImageList(nullptr, TVSIL_NORMAL)); - Window_FreeIcon_IcoLib(m_hwnd); - } - - int Resizer(UTILRESIZECONTROL*) override - { - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - } - - bool OnRefresh() override - { - if (item == nullptr) { - ptrA jid(ppro->getUStringA(m_hContact, "jid")); - if (jid == nullptr) - return false; - - if (ppro->m_bJabberOnline) - if (!(item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid))) - item = ppro->ListGetItemPtr(LIST_ROSTER, jid); - - if (item == nullptr) { - m_tree.DeleteAllItems(); - HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", jid, sttInfoLineId(0, INFOLINE_NAME), true); - FillInfoLine(htiRoot, g_plugin.getIcon(IDI_VCARD), nullptr, TranslateU("Please switch online to see more details.")); - return false; - } - } - FillUserInfo(); - return false; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // Context menu - - void GetNodeText(HTREEITEM hti, CMStringW &buf, int indent = 0) - { - for (int i = 0; i < indent; i++) - buf.AppendChar('\t'); - - wchar_t wszText[256]; - TVITEMEX tvi = {}; - tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE; - tvi.hItem = hti; - tvi.cchTextMax = _countof(wszText); - tvi.pszText = wszText; - if (!m_tree.GetItem(&tvi)) // failure, maybe item was removed... - return; - - buf.Append(wszText); - buf.Append(L"\r\n"); - - if (tvi.state & TVIS_EXPANDED) - for (hti = m_tree.GetChild(hti); hti; hti = m_tree.GetNextSibling(hti)) - GetNodeText(hti, buf, indent + 1); - } - - void onMenu_Tree(CContextMenuPos *pos) - { - if (!pos->hItem) - return; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy")); - AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel")); - int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pos->pt.x, pos->pt.y, 0, m_hwnd, nullptr); - if (nReturnCmd == 1) { - CMStringW buf; - GetNodeText(pos->hItem, buf); - Utils_ClipboardCopy(buf); - } - else if (nReturnCmd == 2) { - wchar_t szBuffer[1024]; - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE; - tvi.hItem = pos->hItem; - tvi.cchTextMax = _countof(szBuffer); - tvi.pszText = szBuffer; - if (m_tree.GetItem(&tvi)) { - if (wchar_t *str = wcsstr(szBuffer, L": ")) - Utils_ClipboardCopy(str + 2); - else - Utils_ClipboardCopy(szBuffer); - } - } - DestroyMenu(hMenu); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserPhotoDlgProc - Jabber photo dialog - -class JabberUserPhotoDlg : public JabberBaseUserInfoDlg -{ - HBITMAP hBitmap = nullptr; - - CCtrlMButton btnSave; - - UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg); - UI_MESSAGE(WM_PAINT, OnPaint); - UI_MESSAGE_MAP_END(); - - char *GetFileName() const - { - ptrA jid(ppro->getUStringA(m_hContact, "jid")); - if (jid != nullptr) { - JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid); - if (item == nullptr) - item = ppro->ListGetItemPtr(LIST_ROSTER, jid); - if (item != nullptr) - return item->photoFileName; - } - - return nullptr; - } - -public: - JabberUserPhotoDlg(CJabberProto *_ppro) : - JabberBaseUserInfoDlg(_ppro, IDD_VCARD_PHOTO), - btnSave(this, IDC_SAVE, g_plugin.getIcon(IDI_SAVE), LPGEN("Save")) - { - btnSave.OnClick = Callback(this, &JabberUserPhotoDlg::onClick_Save); - } - - bool OnInitDialog() override - { - ShowWindow(GetDlgItem(m_hwnd, IDC_LOAD), SW_HIDE); - ShowWindow(GetDlgItem(m_hwnd, IDC_DELETE), SW_HIDE); - return true; - } - - void OnDestroy() override - { - if (hBitmap) { - ppro->debugLogA("Delete bitmap"); - DeleteObject(hBitmap); - } - } - - bool IsEmpty() const override - { - return mir_strlen(GetFileName()) == 0; - } - - bool OnRefresh() override - { - if (hBitmap) { - DeleteObject(hBitmap); - hBitmap = nullptr; - } - btnSave.Hide(); - - char *pszFileName = GetFileName(); - if (mir_strlen(pszFileName)) { - ppro->debugLogA("Showing picture from %s", pszFileName); - hBitmap = Bitmap_Load(Utf2T(pszFileName)); - FreeImage_Premultiply(hBitmap); - btnSave.Show(); - } - - InvalidateRect(m_hwnd, nullptr, TRUE); - UpdateWindow(m_hwnd); - return true; - } - - void onClick_Save(CCtrlButton *) - { - wchar_t szFilter[512]; - - ptrA jid(ppro->getUStringA(m_hContact, "jid")); - if (jid == nullptr) - return; - - JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid); - if (item == nullptr) - if ((item = ppro->ListGetItemPtr(LIST_ROSTER, jid)) == nullptr) - return; - - switch (ProtoGetAvatarFileFormat(Utf2T(item->photoFileName))) { - case PA_FORMAT_BMP: - mir_snwprintf(szFilter, L"BMP %s (*.bmp)%c*.BMP", TranslateT("format"), 0); - break; - - case PA_FORMAT_GIF: - mir_snwprintf(szFilter, L"GIF %s (*.gif)%c*.GIF", TranslateT("format"), 0); - break; - - case PA_FORMAT_JPEG: - mir_snwprintf(szFilter, L"JPEG %s (*.jpg;*.jpeg)%c*.JPG;*.JPEG", TranslateT("format"), 0); - break; - - default: - mir_snwprintf(szFilter, L"%s (*.*)%c*.*", TranslateT("Unknown format"), 0); - } - - wchar_t szFileName[MAX_PATH]; szFileName[0] = '\0'; - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = szFilter; - ofn.lpstrFile = szFileName; - ofn.nMaxFile = _MAX_PATH; - ofn.Flags = OFN_OVERWRITEPROMPT; - if (GetSaveFileName(&ofn)) { - ppro->debugLogW(L"File selected is %s", szFileName); - CopyFile(Utf2T(item->photoFileName), szFileName, FALSE); - } - } - - INT_PTR OnPaint(UINT, WPARAM, LPARAM) - { - if (!ppro->m_bJabberOnline) - SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("<Photo not available while offline>")); - else if (!hBitmap) - SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("<No photo>")); - else { - BITMAP bm; - POINT ptSize, ptOrg, pt, ptFitSize; - RECT rect; - - SetDlgItemTextA(m_hwnd, IDC_CANVAS, ""); - HWND hwndCanvas = GetDlgItem(m_hwnd, IDC_CANVAS); - HDC hdcCanvas = GetDC(hwndCanvas); - HDC hdcMem = CreateCompatibleDC(hdcCanvas); - SelectObject(hdcMem, hBitmap); - SetMapMode(hdcMem, GetMapMode(hdcCanvas)); - GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bm); - ptSize.x = bm.bmWidth; - ptSize.y = bm.bmHeight; - DPtoLP(hdcCanvas, &ptSize, 1); - ptOrg.x = ptOrg.y = 0; - DPtoLP(hdcMem, &ptOrg, 1); - GetClientRect(hwndCanvas, &rect); - InvalidateRect(hwndCanvas, nullptr, TRUE); - UpdateWindow(hwndCanvas); - if (ptSize.x <= rect.right && ptSize.y <= rect.bottom) { - pt.x = (rect.right - ptSize.x) / 2; - pt.y = (rect.bottom - ptSize.y) / 2; - ptFitSize = ptSize; - } - else { - if (((float)(ptSize.x - rect.right)) / ptSize.x > ((float)(ptSize.y - rect.bottom)) / ptSize.y) { - ptFitSize.x = rect.right; - ptFitSize.y = (ptSize.y * rect.right) / ptSize.x; - pt.x = 0; - pt.y = (rect.bottom - ptFitSize.y) / 2; - } - else { - ptFitSize.x = (ptSize.x * rect.bottom) / ptSize.y; - ptFitSize.y = rect.bottom; - pt.x = (rect.right - ptFitSize.x) / 2; - pt.y = 0; - } - } - - RECT rc; - if (IsThemeActive()) { - GetClientRect(hwndCanvas, &rc); - DrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc); - } - else { - GetClientRect(hwndCanvas, &rc); - FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE)); - } - - if (bm.bmBitsPixel == 32) { - BLENDFUNCTION bf = { 0 }; - bf.AlphaFormat = AC_SRC_ALPHA; - bf.BlendOp = AC_SRC_OVER; - bf.SourceConstantAlpha = 255; - GdiAlphaBlend(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf); - } - else { - SetStretchBltMode(hdcCanvas, COLORONCOLOR); - StretchBlt(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY); - } - - DeleteDC(hdcMem); - } - return FALSE; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserPhotoDlgProc - Jabber photo dialog - -static int EnumOwnSessions(const char *szSetting, void *param) -{ - auto *pList = (LIST<char>*)param; - - if (!memcmp(szSetting, omemo::DevicePrefix, sizeof(omemo::DevicePrefix)-1)) - if (szSetting[sizeof(omemo::DevicePrefix)] != 0) - pList->insert(mir_strdup(szSetting)); - - return 0; -} - -static int EnumOmemoSessions(const char *szSetting, void *param) -{ - auto *pList = (LIST<char>*)param; - - if (!memcmp(szSetting, omemo::IdentityPrefix, sizeof(omemo::IdentityPrefix) - 1)) - pList->insert(mir_strdup(szSetting + sizeof(omemo::IdentityPrefix))); - - return 0; -} - -class JabberUserOmemoDlg : public JabberBaseUserInfoDlg -{ - CCtrlListView m_list; - - void AddListItem(const CMStringA &pszStr1, const wchar_t *pszStr2, const CMStringA &pszStr3) - { - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT; - lvi.pszText = mir_a2u(pszStr1); - lvi.iItem = 100500; - int idx = m_list.InsertItem(&lvi); - mir_free(lvi.pszText); - - m_list.SetItemText(idx, 1, (wchar_t *)pszStr2); - CMStringW fp = omemo::FormatFingerprint(pszStr3); - m_list.SetItemText(idx, 2, fp.GetBuffer()); - } - -public: - JabberUserOmemoDlg(CJabberProto *_ppro) : - JabberBaseUserInfoDlg(_ppro, IDD_INFO_OMEMO), - m_list(this, IDC_LIST) - { - } - - bool OnInitDialog() override - { - LV_COLUMN lvc = {}; - lvc.mask = LVCF_TEXT | LVCF_WIDTH; - - lvc.cx = 90; - lvc.pszText = TranslateT("Device ID"); - m_list.InsertColumn(1, &lvc); - - lvc.cx = 80; - lvc.pszText = TranslateT("Status"); - m_list.InsertColumn(2, &lvc); - - lvc.cx = 550; - lvc.pszText = TranslateT("Fingerprint"); - m_list.InsertColumn(3, &lvc); - - if (m_hContact == 0) - OnRefresh(); - return true; - } - - int Resizer(UTILRESIZECONTROL*) override - { - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - } - - bool OnRefresh() override - { - m_list.DeleteAllItems(); - - if (m_hContact == 0) { - // GetOwnDeviceId() creates own keys if they don't exist - CMStringA str1(FORMAT, "%d", ppro->m_omemo.GetOwnDeviceId()); - CMStringA str2(ppro->getMStringA("OmemoFingerprintOwn")); - AddListItem(str1, TranslateT("Own device"), str2); - } - - for (int i = 0;; i++) { - CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i); - uint32_t device_id = ppro->getDword(m_hContact, szSetting, 0); - if (device_id == 0) - break; - - char *jiddev = ppro->getStringA(m_hContact, "jid"); - if (jiddev == 0) - continue; - - size_t len = strlen(jiddev); - jiddev = (char *)mir_realloc(jiddev, len + sizeof(int32_t)); - memcpy(jiddev + len, &device_id, sizeof(int32_t)); - - szSetting = omemo::IdentityPrefix; - szSetting.Append(ptrA(mir_base64_encode(jiddev, len + sizeof(int32_t)))); - mir_free(jiddev); - - const wchar_t *pwszStatus = L""; - CMStringA fp_hex; - DBVARIANT dbv = { 0 }; - dbv.type = DBVT_BLOB; - db_get(m_hContact, ppro->m_szModuleName, szSetting, &dbv); - if (dbv.cpbVal == 33) { - fp_hex.Truncate(33 * 2); - bin2hex(dbv.pbVal, 33, fp_hex.GetBuffer()); - uint8_t trusted = ppro->getByte(m_hContact, "OmemoFingerprintTrusted_" + fp_hex); - pwszStatus = trusted ? TranslateT("Trusted") : TranslateT("UNTRUSTED"); - //TODO: 3 states Trusted, Untrusted, TOFU - } - else if (dbv.cpbVal) - pwszStatus = TranslateT("Unknown"); - - db_free(&dbv); - - AddListItem(CMStringA(FORMAT, "%d", device_id), pwszStatus, fp_hex); - } - return false; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// OnInfoInit - initializes user info option dialogs - -int CJabberProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact) -{ - if (!Proto_GetAccount(m_szModuleName)) - return 0; - - if (hContact == 0) { - // Show our vcard - OnUserInfoInit_VCard(wParam, hContact); - return 0; - } - - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto != nullptr && !mir_strcmp(szProto, m_szModuleName)) { - USERINFOPAGE uip = {}; - uip.dwInitParam = (LPARAM)this; - uip.flags = ODPF_UNICODE | ODPF_USERINFOTAB | ODPF_ICON; - uip.szGroup.w = m_tszUserName; - uip.dwInitParam = (LPARAM)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE); - - uip.pDialog = new JabberUserInfoDlg(this); - uip.position = -2000000000; - uip.szTitle.w = LPGENW("Account"); - g_plugin.addUserInfo(wParam, &uip); - - uip.pDialog = new JabberUserPhotoDlg(this); - uip.position = 2000000000; - uip.szTitle.w = LPGENW("Photo"); - g_plugin.addUserInfo(wParam, &uip); - - CheckOmemoUserInfo(wParam, uip); - } - - return 0; -} - -void CJabberProto::CheckOmemoUserInfo(WPARAM wParam, USERINFOPAGE &uip) -{ - if (m_bUseOMEMO) { - uip.pDialog = new JabberUserOmemoDlg(this); - uip.position = 2000000001; - uip.szTitle.w = L"OMEMO"; - g_plugin.addUserInfo(wParam, &uip); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserInfoUpdate - -void JabberUserInfoInit() -{ - hUserInfoList = WindowList_Create(); -} - -void JabberUserInfoUninit() -{ - WindowList_Destroy(hUserInfoList); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberUserInfoUpdate - -void JabberUserInfoUpdate(MCONTACT hContact) -{ - if (!hContact) - WindowList_BroadcastAsync(hUserInfoList, WM_PROTO_REFRESH, 0, 0); - else if (HWND hwnd = WindowList_Find(hUserInfoList, hContact)) - PostMessage(hwnd, WM_PROTO_REFRESH, 0, 0); -} +/*
+
+Jabber Protocol Plugin for Miranda NG
+
+Copyright (c) 2002-04 Santithorn Bunchua
+Copyright (c) 2005-12 George Hazan
+Copyright (c) 2007 Maxim Mluhov
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+
+#include "jabber_list.h"
+
+static MWindowList hUserInfoList = nullptr;
+
+class JabberBaseUserInfoDlg : public CUserInfoPageDlg
+{
+ INT_PTR DoRefresh(UINT, WPARAM, LPARAM)
+ {
+ OnRefresh();
+ return 0;
+ }
+
+protected:
+ UI_MESSAGE_MAP(JabberBaseUserInfoDlg, CUserInfoPageDlg);
+ UI_MESSAGE(WM_PROTO_REFRESH, DoRefresh);
+ UI_MESSAGE(WM_JABBER_REFRESH_VCARD, DoRefresh);
+ UI_MESSAGE_MAP_END();
+
+ CJabberProto *ppro;
+
+ JabberBaseUserInfoDlg(CJabberProto *_ppro, int dlgId) :
+ CUserInfoPageDlg(g_plugin, dlgId),
+ ppro(_ppro)
+ {}
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoDlgProc - main user info dialog
+
+enum
+{
+ INFOLINE_DELETE = 0x80000000,
+ INFOLINE_MASK = 0x7fffffff,
+ INFOLINE_BAD_ID = 0x7fffffff,
+
+ INFOLINE_NAME = 1,
+ INFOLINE_MOOD,
+ INFOLINE_ACTIVITY,
+ INFOLINE_TUNE,
+ INFOLINE_OFFLINE,
+ INFOLINE_MESSAGE,
+ INFOLINE_SOFTWARE,
+ INFOLINE_VERSION,
+ INFOLINE_SYSTEM,
+ INFOLINE_PRIORITY,
+ INFOLINE_IDLE,
+ INFOLINE_CAPS,
+ INFOLINE_SOFTWARE_INFORMATION,
+ INFOLINE_SUBSCRIPTION,
+ INFOLINE_LOGOFF,
+ INFOLINE_LOGOFF_MSG,
+ INFOLINE_LASTACTIVE,
+};
+
+__forceinline uint32_t sttInfoLineId(uint32_t res, uint32_t type, uint32_t line = 0)
+{
+ return
+ (type << 24) & 0x7f000000 |
+ (res << 12) & 0x00fff000 |
+ (line) & 0x00000fff;
+}
+
+class JabberUserInfoDlg : public JabberBaseUserInfoDlg
+{
+ JABBER_LIST_ITEM *item = nullptr;
+ int resourcesCount = -1;
+
+ CCtrlTreeView m_tree;
+
+ UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg);
+ UI_MESSAGE(WM_PROTO_CHECK_ONLINE, OnCheckOnline);
+ UI_MESSAGE_MAP_END();
+
+ INT_PTR OnCheckOnline(UINT, WPARAM, LPARAM)
+ {
+ if (!ppro->m_bJabberOnline)
+ item = nullptr;
+ else
+ OnRefresh();
+ return 0;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // User information block
+
+ void CleanupInfo(int stage)
+ {
+ HTREEITEM hItem = m_tree.GetRoot();
+ while (hItem) {
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_tree.GetItem(&tvi);
+
+ switch (stage) {
+ case 0:
+ tvi.lParam |= INFOLINE_DELETE;
+ m_tree.SetItem(&tvi);
+ break;
+
+ case 1:
+ if (tvi.lParam & INFOLINE_DELETE) {
+ hItem = m_tree.GetNextSibling(hItem);
+ m_tree.DeleteItem(tvi.hItem);
+ continue;
+ }
+ break;
+ }
+
+ HTREEITEM hItemTmp = nullptr;
+ if (hItemTmp = m_tree.GetChild(hItem))
+ hItem = hItemTmp;
+ else if (hItemTmp = m_tree.GetNextSibling(hItem))
+ hItem = hItemTmp;
+ else {
+ while (true) {
+ if (!(hItem = m_tree.GetParent(hItem))) break;
+ if (hItemTmp = m_tree.GetNextSibling(hItem)) {
+ hItem = hItemTmp;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ HTREEITEM FindInfoLine(HTREEITEM htiRoot, LPARAM id = INFOLINE_BAD_ID)
+ {
+ if (id == INFOLINE_BAD_ID) return nullptr;
+ for (HTREEITEM hti = m_tree.GetChild(htiRoot); hti; hti = m_tree.GetNextSibling(hti)) {
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_tree.GetItem(&tvi);
+ if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK))
+ return hti;
+ }
+ return nullptr;
+ }
+
+ HTREEITEM FillInfoLine(HTREEITEM htiRoot, HICON hIcon, const wchar_t *title, const char *value, LPARAM id = INFOLINE_BAD_ID, bool expand = false)
+ {
+ HTREEITEM hti = FindInfoLine(htiRoot, id);
+
+ Utf2T wszValue(value);
+ const wchar_t *pwszValue = (value == nullptr) ? TranslateT("<not specified>") : wszValue;
+ wchar_t buf[256];
+ if (title)
+ mir_snwprintf(buf, L"%s: %s", title, pwszValue);
+ else
+ mir_wstrncpy(buf, pwszValue, _countof(buf));
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hParent = htiRoot;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM;
+ tvis.itemex.pszText = buf;
+ tvis.itemex.lParam = id;
+
+ if (hIcon) {
+ HIMAGELIST himl = m_tree.GetImageList(TVSIL_NORMAL);
+ tvis.itemex.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.itemex.iImage =
+ tvis.itemex.iSelectedImage = ImageList_AddIcon(himl, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ }
+
+ if (hti) {
+ tvis.itemex.mask |= TVIF_HANDLE;
+ tvis.itemex.hItem = hti;
+ m_tree.SetItem(&tvis.itemex);
+ }
+ else {
+ tvis.itemex.mask |= TVIF_STATE;
+ tvis.itemex.stateMask = TVIS_EXPANDED;
+ tvis.itemex.state = expand ? TVIS_EXPANDED : 0;
+ hti = m_tree.InsertItem(&tvis);
+ }
+
+ return hti;
+ }
+
+ void FillAdvStatusInfo(HTREEITEM htiRoot, uint32_t dwInfoLine, MCONTACT hContact, wchar_t *szTitle, char *pszSlot)
+ {
+ ptrA szAdvStatusIcon(ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON));
+ ptrW szAdvStatusTitle(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE));
+ ptrW szAdvStatusText(ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT));
+
+ if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) {
+ wchar_t szText[2048];
+ if (szAdvStatusText && *szAdvStatusText)
+ mir_snwprintf(szText, L"%s (%s)", TranslateW(szAdvStatusTitle), szAdvStatusText.get());
+ else
+ wcsncpy_s(szText, TranslateW(szAdvStatusTitle), _TRUNCATE);
+ FillInfoLine(htiRoot, IcoLib_GetIcon(szAdvStatusIcon), szTitle, T2Utf(szText), dwInfoLine);
+ }
+ }
+
+ void FillResourceInfo(HTREEITEM htiRoot, int resource)
+ {
+ HTREEITEM htiResource = htiRoot;
+ pResourceStatus r = resource ? item->arResources[resource - 1] : item->getTemp();
+
+ if (r->m_szResourceName && *r->m_szResourceName)
+ htiResource = FillInfoLine(htiRoot, Skin_LoadProtoIcon(ppro->m_szModuleName, r->m_iStatus),
+ TranslateT("Resource"), r->m_szResourceName, sttInfoLineId(resource, INFOLINE_NAME), true);
+
+ // StatusMsg
+ FillInfoLine(htiResource, nullptr /*Skin_LoadIcon(SKINICON_EVENT_MESSAGE)*/,
+ TranslateT("Message"), r->m_szStatusMessage,
+ sttInfoLineId(resource, INFOLINE_MESSAGE));
+
+ // Software
+ if (CJabberClientPartialCaps *pCaps = r->m_pCaps) {
+ HICON hIcon = nullptr;
+
+ if (ServiceExists(MS_FP_GETCLIENTICONT)) {
+ if (pCaps->GetSoft()) {
+ wchar_t buf[256];
+ mir_snwprintf(buf, L"%s %s", pCaps->GetSoft(), pCaps->GetSoftVer());
+ hIcon = Finger_GetClientIcon(buf, 0);
+ }
+ }
+
+ FillInfoLine(htiResource, hIcon, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE));
+
+ // Version
+ FillInfoLine(htiResource, nullptr, TranslateT("Version"), pCaps->GetSoftMir() ? pCaps->GetSoftMir() : pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_VERSION));
+
+ // System
+ FillInfoLine(htiResource, nullptr, TranslateT("System"), pCaps->GetOsVer() ? pCaps->GetOsVer() : pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SYSTEM));
+
+ if (hIcon)
+ DestroyIcon(hIcon);
+ }
+
+ // Resource priority
+ char buf[256];
+ itoa(r->m_iPriority, buf, 10);
+ FillInfoLine(htiResource, nullptr, TranslateT("Resource priority"), buf, sttInfoLineId(resource, INFOLINE_PRIORITY));
+
+ // Idle
+ if (r->m_dwIdleStartTime != -1) {
+ if (r->m_dwIdleStartTime != 0) {
+ mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf));
+ size_t len = mir_strlen(buf);
+ if (len > 0)
+ buf[len - 1] = 0;
+ }
+ else mir_strncpy(buf, TranslateU("<currently online>"), _countof(buf));
+
+ FillInfoLine(htiResource, nullptr, TranslateT("Last activity"), buf, sttInfoLineId(resource, INFOLINE_IDLE));
+ }
+
+ // caps
+ JabberCapsBits jcb = ppro->GetResourceCapabilities(MakeJid(item->jid, r->m_szResourceName), r);
+ if (!(jcb & JABBER_RESOURCE_CAPS_ERROR)) {
+ HTREEITEM htiCaps = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Client capabilities"), sttInfoLineId(resource, INFOLINE_CAPS));
+ int i;
+ for (i = 0; i < g_cJabberFeatCapPairs; i++)
+ if (jcb & g_JabberFeatCapPairs[i].jcbCap) {
+ char szDescription[1024];
+ if (g_JabberFeatCapPairs[i].tszDescription)
+ mir_snprintf(szDescription, "%s (%s)", TranslateU(g_JabberFeatCapPairs[i].tszDescription), g_JabberFeatCapPairs[i].szFeature);
+ else
+ strncpy_s(szDescription, g_JabberFeatCapPairs[i].szFeature, _TRUNCATE);
+ FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
+ }
+
+ for (auto &it : ppro->m_lstJabberFeatCapPairsDynamic) {
+ if (jcb & it->jcbCap) {
+ char szDescription[1024];
+ if (it->szDescription)
+ mir_snprintf(szDescription, "%s (%s)", TranslateU(it->szDescription), it->szFeature);
+ else
+ strncpy_s(szDescription, it->szFeature, _TRUNCATE);
+ FillInfoLine(htiCaps, nullptr, nullptr, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i++));
+ }
+ }
+ }
+
+ // Software info
+ HTREEITEM htiSoftwareInfo = FillInfoLine(htiResource, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), nullptr, TranslateU("Software information"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION));
+ int nLineId = 0;
+ if (CJabberClientPartialCaps *pCaps = r->m_pCaps) {
+ if (pCaps->GetOs())
+ FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system"), pCaps->GetOs(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (pCaps->GetOsVer())
+ FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Operating system version"), pCaps->GetOsVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (pCaps->GetSoft())
+ FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software"), pCaps->GetSoft(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (pCaps->GetSoftVer())
+ FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Software version"), pCaps->GetSoftVer(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (pCaps->GetSoftMir())
+ FillInfoLine(htiSoftwareInfo, nullptr, TranslateT("Miranda core version"), pCaps->GetSoftMir(), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ }
+ }
+
+ void FillUserInfo()
+ {
+ m_tree.SendMsg(WM_SETREDRAW, FALSE, 0);
+
+ CleanupInfo(0);
+
+ HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", item->jid, sttInfoLineId(0, INFOLINE_NAME), true);
+
+ if (MCONTACT hContact = ppro->HContactFromJID(item->jid)) {
+ FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD);
+ FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY);
+ FillAdvStatusInfo(htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE);
+ }
+
+ // subscription
+ switch (item->subscription) {
+ case SUB_BOTH:
+ FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("both"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ case SUB_TO:
+ FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("to"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ case SUB_FROM:
+ FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("from"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ default:
+ FillInfoLine(htiRoot, nullptr, TranslateT("Subscription"), TranslateU("none"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ }
+
+ // logoff
+ char buf[256];
+ JABBER_RESOURCE_STATUS *r = item->getTemp();
+ if (r->m_dwIdleStartTime != -1) {
+ if (r->m_dwIdleStartTime > 0) {
+ mir_strncpy(buf, ctime(&r->m_dwIdleStartTime), _countof(buf));
+ size_t len = mir_strlen(buf);
+ if (len > 0)
+ buf[len - 1] = 0;
+ }
+ else mir_strncpy(buf, TranslateU("<currently online>"), _countof(buf));
+
+ FillInfoLine(htiRoot, nullptr,
+ (item->jid && strchr(item->jid, '@')) ? TranslateT("Last logoff time") : TranslateT("Uptime"), buf,
+ sttInfoLineId(0, INFOLINE_LOGOFF));
+ }
+
+ if (r->m_szStatusMessage)
+ FillInfoLine(htiRoot, nullptr, TranslateT("Logoff message"), r->m_szStatusMessage, sttInfoLineId(0, INFOLINE_LOGOFF_MSG));
+
+ // activity
+ if (item->m_pLastSeenResource)
+ mir_strncpy(buf, item->m_pLastSeenResource->m_szResourceName, _countof(buf));
+ else
+ mir_strncpy(buf, TranslateU("<no information available>"), _countof(buf));
+ FillInfoLine(htiRoot, nullptr, TranslateT("Last active resource"), buf, sttInfoLineId(0, INFOLINE_LASTACTIVE));
+
+ // resources
+ if (item->arResources.getCount()) {
+ for (int i = 0; i < item->arResources.getCount(); i++)
+ FillResourceInfo(htiRoot, i + 1);
+ }
+ else if (!strchr(item->jid, '@') || (r->m_iStatus != ID_STATUS_OFFLINE))
+ FillResourceInfo(htiRoot, 0);
+
+ CleanupInfo(1);
+ m_tree.SendMsg(WM_SETREDRAW, TRUE, 0);
+
+ RedrawWindow(m_tree.GetHwnd(), nullptr, nullptr, RDW_INVALIDATE);
+ }
+
+public:
+ JabberUserInfoDlg(CJabberProto *_ppro) :
+ JabberBaseUserInfoDlg(_ppro, IDD_INFO_JABBER),
+ m_tree(this, IDC_TV_INFO)
+ {
+ m_tree.OnBuildMenu = Callback(this, &JabberUserInfoDlg::onMenu_Tree);
+ }
+
+ bool OnInitDialog() override
+ {
+ ppro->WindowSubscribe(m_hwnd);
+ Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_USERDETAILS);
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ MoveWindow(m_tree.GetHwnd(), 5, 5, rc.right - 10, rc.bottom - 10, TRUE);
+
+ HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1);
+ ImageList_AddSkinIcon(himl, SKINICON_OTHER_SMALLDOT);
+ TreeView_SetImageList(m_tree.GetHwnd(), himl, TVSIL_NORMAL);
+
+ WindowList_Add(hUserInfoList, m_hwnd, m_hContact);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ ppro->WindowUnsubscribe(m_hwnd);
+ WindowList_Remove(hUserInfoList, m_hwnd);
+ ImageList_Destroy(m_tree.SetImageList(nullptr, TVSIL_NORMAL));
+ Window_FreeIcon_IcoLib(m_hwnd);
+ }
+
+ int Resizer(UTILRESIZECONTROL*) override
+ {
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+ }
+
+ bool OnRefresh() override
+ {
+ if (item == nullptr) {
+ ptrA jid(ppro->getUStringA(m_hContact, "jid"));
+ if (jid == nullptr)
+ return false;
+
+ if (ppro->m_bJabberOnline)
+ if (!(item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid)))
+ item = ppro->ListGetItemPtr(LIST_ROSTER, jid);
+
+ if (item == nullptr) {
+ m_tree.DeleteAllItems();
+ HTREEITEM htiRoot = FillInfoLine(nullptr, IcoLib_GetIconByHandle(ppro->m_hProtoIcon), L"JID", jid, sttInfoLineId(0, INFOLINE_NAME), true);
+ FillInfoLine(htiRoot, g_plugin.getIcon(IDI_VCARD), nullptr, TranslateU("Please switch online to see more details."));
+ return false;
+ }
+ }
+ FillUserInfo();
+ return false;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Context menu
+
+ void GetNodeText(HTREEITEM hti, CMStringW &buf, int indent = 0)
+ {
+ for (int i = 0; i < indent; i++)
+ buf.AppendChar('\t');
+
+ wchar_t wszText[256];
+ TVITEMEX tvi = {};
+ tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE;
+ tvi.hItem = hti;
+ tvi.cchTextMax = _countof(wszText);
+ tvi.pszText = wszText;
+ if (!m_tree.GetItem(&tvi)) // failure, maybe item was removed...
+ return;
+
+ buf.Append(wszText);
+ buf.Append(L"\r\n");
+
+ if (tvi.state & TVIS_EXPANDED)
+ for (hti = m_tree.GetChild(hti); hti; hti = m_tree.GetNextSibling(hti))
+ GetNodeText(hti, buf, indent + 1);
+ }
+
+ void onMenu_Tree(CContextMenuPos *pos)
+ {
+ if (!pos->hItem)
+ return;
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy"));
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel"));
+ int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pos->pt.x, pos->pt.y, 0, m_hwnd, nullptr);
+ if (nReturnCmd == 1) {
+ CMStringW buf;
+ GetNodeText(pos->hItem, buf);
+ Utils_ClipboardCopy(buf);
+ }
+ else if (nReturnCmd == 2) {
+ wchar_t szBuffer[1024];
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE;
+ tvi.hItem = pos->hItem;
+ tvi.cchTextMax = _countof(szBuffer);
+ tvi.pszText = szBuffer;
+ if (m_tree.GetItem(&tvi)) {
+ if (wchar_t *str = wcsstr(szBuffer, L": "))
+ Utils_ClipboardCopy(str + 2);
+ else
+ Utils_ClipboardCopy(szBuffer);
+ }
+ }
+ DestroyMenu(hMenu);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserPhotoDlgProc - Jabber photo dialog
+
+class JabberUserPhotoDlg : public JabberBaseUserInfoDlg
+{
+ HBITMAP hBitmap = nullptr;
+
+ CCtrlMButton btnSave;
+
+ UI_MESSAGE_MAP(JabberUserInfoDlg, JabberBaseUserInfoDlg);
+ UI_MESSAGE(WM_PAINT, OnPaint);
+ UI_MESSAGE_MAP_END();
+
+ char *GetFileName() const
+ {
+ ptrA jid(ppro->getUStringA(m_hContact, "jid"));
+ if (jid != nullptr) {
+ JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid);
+ if (item == nullptr)
+ item = ppro->ListGetItemPtr(LIST_ROSTER, jid);
+ if (item != nullptr)
+ return item->photoFileName;
+ }
+
+ return nullptr;
+ }
+
+public:
+ JabberUserPhotoDlg(CJabberProto *_ppro) :
+ JabberBaseUserInfoDlg(_ppro, IDD_VCARD_PHOTO),
+ btnSave(this, IDC_SAVE, g_plugin.getIcon(IDI_SAVE), LPGEN("Save"))
+ {
+ btnSave.OnClick = Callback(this, &JabberUserPhotoDlg::onClick_Save);
+ }
+
+ bool OnInitDialog() override
+ {
+ ShowWindow(GetDlgItem(m_hwnd, IDC_LOAD), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_DELETE), SW_HIDE);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ if (hBitmap) {
+ ppro->debugLogA("Delete bitmap");
+ DeleteObject(hBitmap);
+ }
+ }
+
+ bool IsEmpty() const override
+ {
+ return mir_strlen(GetFileName()) == 0;
+ }
+
+ bool OnRefresh() override
+ {
+ if (hBitmap) {
+ DeleteObject(hBitmap);
+ hBitmap = nullptr;
+ }
+ btnSave.Hide();
+
+ char *pszFileName = GetFileName();
+ if (mir_strlen(pszFileName)) {
+ ppro->debugLogA("Showing picture from %s", pszFileName);
+ hBitmap = Bitmap_Load(Utf2T(pszFileName));
+ FreeImage_Premultiply(hBitmap);
+ btnSave.Show();
+ }
+
+ InvalidateRect(m_hwnd, nullptr, TRUE);
+ UpdateWindow(m_hwnd);
+ return true;
+ }
+
+ void onClick_Save(CCtrlButton *)
+ {
+ wchar_t szFilter[512];
+
+ ptrA jid(ppro->getUStringA(m_hContact, "jid"));
+ if (jid == nullptr)
+ return;
+
+ JABBER_LIST_ITEM *item = ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid);
+ if (item == nullptr)
+ if ((item = ppro->ListGetItemPtr(LIST_ROSTER, jid)) == nullptr)
+ return;
+
+ switch (ProtoGetAvatarFileFormat(Utf2T(item->photoFileName))) {
+ case PA_FORMAT_BMP:
+ mir_snwprintf(szFilter, L"BMP %s (*.bmp)%c*.BMP", TranslateT("format"), 0);
+ break;
+
+ case PA_FORMAT_GIF:
+ mir_snwprintf(szFilter, L"GIF %s (*.gif)%c*.GIF", TranslateT("format"), 0);
+ break;
+
+ case PA_FORMAT_JPEG:
+ mir_snwprintf(szFilter, L"JPEG %s (*.jpg;*.jpeg)%c*.JPG;*.JPEG", TranslateT("format"), 0);
+ break;
+
+ default:
+ mir_snwprintf(szFilter, L"%s (*.*)%c*.*", TranslateT("Unknown format"), 0);
+ }
+
+ wchar_t szFileName[MAX_PATH]; szFileName[0] = '\0';
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = m_hwnd;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.Flags = OFN_OVERWRITEPROMPT;
+ if (GetSaveFileName(&ofn)) {
+ ppro->debugLogW(L"File selected is %s", szFileName);
+ CopyFile(Utf2T(item->photoFileName), szFileName, FALSE);
+ }
+ }
+
+ INT_PTR OnPaint(UINT, WPARAM, LPARAM)
+ {
+ if (!ppro->m_bJabberOnline)
+ SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("<Photo not available while offline>"));
+ else if (!hBitmap)
+ SetDlgItemText(m_hwnd, IDC_CANVAS, TranslateT("<No photo>"));
+ else {
+ BITMAP bm;
+ POINT ptSize, ptOrg, pt, ptFitSize;
+ RECT rect;
+
+ SetDlgItemTextA(m_hwnd, IDC_CANVAS, "");
+ HWND hwndCanvas = GetDlgItem(m_hwnd, IDC_CANVAS);
+ HDC hdcCanvas = GetDC(hwndCanvas);
+ HDC hdcMem = CreateCompatibleDC(hdcCanvas);
+ SelectObject(hdcMem, hBitmap);
+ SetMapMode(hdcMem, GetMapMode(hdcCanvas));
+ GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bm);
+ ptSize.x = bm.bmWidth;
+ ptSize.y = bm.bmHeight;
+ DPtoLP(hdcCanvas, &ptSize, 1);
+ ptOrg.x = ptOrg.y = 0;
+ DPtoLP(hdcMem, &ptOrg, 1);
+ GetClientRect(hwndCanvas, &rect);
+ InvalidateRect(hwndCanvas, nullptr, TRUE);
+ UpdateWindow(hwndCanvas);
+ if (ptSize.x <= rect.right && ptSize.y <= rect.bottom) {
+ pt.x = (rect.right - ptSize.x) / 2;
+ pt.y = (rect.bottom - ptSize.y) / 2;
+ ptFitSize = ptSize;
+ }
+ else {
+ if (((float)(ptSize.x - rect.right)) / ptSize.x > ((float)(ptSize.y - rect.bottom)) / ptSize.y) {
+ ptFitSize.x = rect.right;
+ ptFitSize.y = (ptSize.y * rect.right) / ptSize.x;
+ pt.x = 0;
+ pt.y = (rect.bottom - ptFitSize.y) / 2;
+ }
+ else {
+ ptFitSize.x = (ptSize.x * rect.bottom) / ptSize.y;
+ ptFitSize.y = rect.bottom;
+ pt.x = (rect.right - ptFitSize.x) / 2;
+ pt.y = 0;
+ }
+ }
+
+ RECT rc;
+ if (IsThemeActive()) {
+ GetClientRect(hwndCanvas, &rc);
+ DrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc);
+ }
+ else {
+ GetClientRect(hwndCanvas, &rc);
+ FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE));
+ }
+
+ if (bm.bmBitsPixel == 32) {
+ BLENDFUNCTION bf = { 0 };
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.SourceConstantAlpha = 255;
+ GdiAlphaBlend(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf);
+ }
+ else {
+ SetStretchBltMode(hdcCanvas, COLORONCOLOR);
+ StretchBlt(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY);
+ }
+
+ DeleteDC(hdcMem);
+ }
+ return FALSE;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserPhotoDlgProc - Jabber photo dialog
+
+static int EnumOwnSessions(const char *szSetting, void *param)
+{
+ auto *pList = (LIST<char>*)param;
+
+ if (!memcmp(szSetting, omemo::DevicePrefix, sizeof(omemo::DevicePrefix)-1))
+ if (szSetting[sizeof(omemo::DevicePrefix)] != 0)
+ pList->insert(mir_strdup(szSetting));
+
+ return 0;
+}
+
+static int EnumOmemoSessions(const char *szSetting, void *param)
+{
+ auto *pList = (LIST<char>*)param;
+
+ if (!memcmp(szSetting, omemo::IdentityPrefix, sizeof(omemo::IdentityPrefix) - 1))
+ pList->insert(mir_strdup(szSetting + sizeof(omemo::IdentityPrefix)));
+
+ return 0;
+}
+
+class JabberUserOmemoDlg : public JabberBaseUserInfoDlg
+{
+ CCtrlListView m_list;
+
+ void AddListItem(const CMStringA &pszStr1, const wchar_t *pszStr2, const CMStringA &pszStr3)
+ {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_TEXT;
+ lvi.pszText = mir_a2u(pszStr1);
+ lvi.iItem = 100500;
+ int idx = m_list.InsertItem(&lvi);
+ mir_free(lvi.pszText);
+
+ m_list.SetItemText(idx, 1, (wchar_t *)pszStr2);
+ CMStringW fp = omemo::FormatFingerprint(pszStr3);
+ m_list.SetItemText(idx, 2, fp.GetBuffer());
+ }
+
+public:
+ JabberUserOmemoDlg(CJabberProto *_ppro) :
+ JabberBaseUserInfoDlg(_ppro, IDD_INFO_OMEMO),
+ m_list(this, IDC_LIST)
+ {
+ }
+
+ bool OnInitDialog() override
+ {
+ LV_COLUMN lvc = {};
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ lvc.cx = 90;
+ lvc.pszText = TranslateT("Device ID");
+ m_list.InsertColumn(1, &lvc);
+
+ lvc.cx = 80;
+ lvc.pszText = TranslateT("Status");
+ m_list.InsertColumn(2, &lvc);
+
+ lvc.cx = 550;
+ lvc.pszText = TranslateT("Fingerprint");
+ m_list.InsertColumn(3, &lvc);
+
+ if (m_hContact == 0)
+ OnRefresh();
+ return true;
+ }
+
+ int Resizer(UTILRESIZECONTROL*) override
+ {
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+ }
+
+ bool OnRefresh() override
+ {
+ m_list.DeleteAllItems();
+
+ if (m_hContact == 0) {
+ // GetOwnDeviceId() creates own keys if they don't exist
+ CMStringA str1(FORMAT, "%d", ppro->m_omemo.GetOwnDeviceId());
+ CMStringA str2(ppro->getMStringA("OmemoFingerprintOwn"));
+ AddListItem(str1, TranslateT("Own device"), str2);
+ }
+
+ for (int i = 0;; i++) {
+ CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i);
+ uint32_t device_id = ppro->getDword(m_hContact, szSetting, 0);
+ if (device_id == 0)
+ break;
+
+ char *jiddev = ppro->getStringA(m_hContact, "jid");
+ if (jiddev == 0)
+ continue;
+
+ size_t len = strlen(jiddev);
+ jiddev = (char *)mir_realloc(jiddev, len + sizeof(int32_t));
+ memcpy(jiddev + len, &device_id, sizeof(int32_t));
+
+ szSetting = omemo::IdentityPrefix;
+ szSetting.Append(ptrA(mir_base64_encode(jiddev, len + sizeof(int32_t))));
+ mir_free(jiddev);
+
+ const wchar_t *pwszStatus = L"";
+ CMStringA fp_hex;
+ DBVARIANT dbv = { 0 };
+ dbv.type = DBVT_BLOB;
+ db_get(m_hContact, ppro->m_szModuleName, szSetting, &dbv);
+ if (dbv.cpbVal == 33) {
+ fp_hex.Truncate(33 * 2);
+ bin2hex(dbv.pbVal, 33, fp_hex.GetBuffer());
+ uint8_t trusted = ppro->getByte(m_hContact, "OmemoFingerprintTrusted_" + fp_hex);
+ pwszStatus = trusted ? TranslateT("Trusted") : TranslateT("UNTRUSTED");
+ //TODO: 3 states Trusted, Untrusted, TOFU
+ }
+ else if (dbv.cpbVal)
+ pwszStatus = TranslateT("Unknown");
+
+ db_free(&dbv);
+
+ AddListItem(CMStringA(FORMAT, "%d", device_id), pwszStatus, fp_hex);
+ }
+ return false;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnInfoInit - initializes user info option dialogs
+
+int CJabberProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact)
+{
+ if (!Proto_GetAccount(m_szModuleName))
+ return 0;
+
+ if (hContact == 0) {
+ // Show our vcard
+ OnUserInfoInit_VCard(wParam, hContact);
+ return 0;
+ }
+
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto != nullptr && !mir_strcmp(szProto, m_szModuleName)) {
+ USERINFOPAGE uip = {};
+ uip.dwInitParam = (LPARAM)this;
+ uip.flags = ODPF_UNICODE | ODPF_USERINFOTAB | ODPF_ICON;
+ uip.szGroup.w = m_tszUserName;
+ uip.dwInitParam = (LPARAM)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE);
+
+ uip.pDialog = new JabberUserInfoDlg(this);
+ uip.position = -2000000000;
+ uip.szTitle.w = LPGENW("Account");
+ g_plugin.addUserInfo(wParam, &uip);
+
+ uip.pDialog = new JabberUserPhotoDlg(this);
+ uip.position = 2000000000;
+ uip.szTitle.w = LPGENW("Photo");
+ g_plugin.addUserInfo(wParam, &uip);
+
+ CheckOmemoUserInfo(wParam, uip);
+ }
+
+ return 0;
+}
+
+void CJabberProto::CheckOmemoUserInfo(WPARAM wParam, USERINFOPAGE &uip)
+{
+ if (m_bUseOMEMO) {
+ uip.pDialog = new JabberUserOmemoDlg(this);
+ uip.position = 2000000001;
+ uip.szTitle.w = L"OMEMO";
+ g_plugin.addUserInfo(wParam, &uip);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoUpdate
+
+void JabberUserInfoInit()
+{
+ hUserInfoList = WindowList_Create();
+}
+
+void JabberUserInfoUninit()
+{
+ WindowList_Destroy(hUserInfoList);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoUpdate
+
+void JabberUserInfoUpdate(MCONTACT hContact)
+{
+ if (!hContact)
+ WindowList_BroadcastAsync(hUserInfoList, WM_PROTO_REFRESH, 0, 0);
+ else if (HWND hwnd = WindowList_Find(hUserInfoList, hContact))
+ PostMessage(hwnd, WM_PROTO_REFRESH, 0, 0);
+}
diff --git a/protocols/JabberG/src/jabber_util.cpp b/protocols/JabberG/src/jabber_util.cpp index 3eebd4e2c7..a3e1c856f6 100644 --- a/protocols/JabberG/src/jabber_util.cpp +++ b/protocols/JabberG/src/jabber_util.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_vcard.cpp b/protocols/JabberG/src/jabber_vcard.cpp index 0d722aac37..aad0f8d9be 100644 --- a/protocols/JabberG/src/jabber_vcard.cpp +++ b/protocols/JabberG/src/jabber_vcard.cpp @@ -4,7 +4,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_xml.cpp b/protocols/JabberG/src/jabber_xml.cpp index b53ca07d45..c3a166017a 100644 --- a/protocols/JabberG/src/jabber_xml.cpp +++ b/protocols/JabberG/src/jabber_xml.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_xml.h b/protocols/JabberG/src/jabber_xml.h index 16f2626b1c..4ecdde53ea 100644 --- a/protocols/JabberG/src/jabber_xml.h +++ b/protocols/JabberG/src/jabber_xml.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_xstatus.cpp b/protocols/JabberG/src/jabber_xstatus.cpp index b67347f7e7..0a989d9dd6 100644 --- a/protocols/JabberG/src/jabber_xstatus.cpp +++ b/protocols/JabberG/src/jabber_xstatus.cpp @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_xstatus.h b/protocols/JabberG/src/jabber_xstatus.h index 0d2bbb266a..80358daca6 100644 --- a/protocols/JabberG/src/jabber_xstatus.h +++ b/protocols/JabberG/src/jabber_xstatus.h @@ -6,7 +6,7 @@ Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan
Copyright (c) 2007-09 Maxim Mluhov
Copyright (c) 2007-09 Victor Pavlychko
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/jabber_zstream.cpp b/protocols/JabberG/src/jabber_zstream.cpp index 0931e4fc34..87f21d44aa 100644 --- a/protocols/JabberG/src/jabber_zstream.cpp +++ b/protocols/JabberG/src/jabber_zstream.cpp @@ -6,7 +6,7 @@ XEP-0138 (Stream Compression) implementation Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Kostya Chukavin, Taras Zackrepa
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/stdafx.cxx b/protocols/JabberG/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/protocols/JabberG/src/stdafx.cxx +++ b/protocols/JabberG/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/stdafx.h b/protocols/JabberG/src/stdafx.h index da81c661c9..d848e5a215 100644 --- a/protocols/JabberG/src/stdafx.h +++ b/protocols/JabberG/src/stdafx.h @@ -5,7 +5,7 @@ Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua
Copyright (c) 2005-12 George Hazan
Copyright (c) 2007 Maxim Mluhov
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/JabberG/src/version.h b/protocols/JabberG/src/version.h index 29a9e971eb..1308d51095 100644 --- a/protocols/JabberG/src/version.h +++ b/protocols/JabberG/src/version.h @@ -9,5 +9,5 @@ #define __FILENAME "Jabber.dll"
#define __DESCRIPTION "Jabber (XMPP) protocol support for Miranda NG."
#define __AUTHOR "George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura"
-#define __COPYRIGHT "© 2005-22 George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura"
+#define __COPYRIGHT "© 2005-23 George Hazan, Maxim Mluhov, Victor Pavlychko, Artem Shpynov, Michael Stepura"
#define __AUTHORWEB "https://miranda-ng.org/p/Jabber"
diff --git a/protocols/LotusNotify/src/stdafx.cxx b/protocols/LotusNotify/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/LotusNotify/src/stdafx.cxx +++ b/protocols/LotusNotify/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/MinecraftDynmap/src/chat.cpp b/protocols/MinecraftDynmap/src/chat.cpp index a6f15cc115..c0c1f80542 100644 --- a/protocols/MinecraftDynmap/src/chat.cpp +++ b/protocols/MinecraftDynmap/src/chat.cpp @@ -1,185 +1,185 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "stdafx.h" - -void MinecraftDynmapProto::UpdateChat(const char *name, const char *message, const time_t timestamp, bool addtolog) -{ - // replace % to %% to not interfere with chat color codes - CMStringA szMessage(message); - szMessage.Replace("%", "%%"); - - GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_MESSAGE }; - gce.dwFlags = GCEF_UTF8; - gce.time = timestamp; - gce.pszText.a = szMessage.c_str(); - - if (name == NULL) { - gce.iType = GC_EVENT_INFORMATION; - name = TranslateU("Server"); - gce.bIsMe = false; - } - else gce.bIsMe = (m_nick == name); - - if (addtolog) - gce.dwFlags |= GCEF_ADDTOLOG; - - gce.pszUID.a = gce.pszNick.a = name; - Chat_Event(&gce); -} - -int MinecraftDynmapProto::OnChatEvent(WPARAM, LPARAM lParam) -{ - GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam); - if(strcmp(hook->si->pszModule,m_szModuleName)) - return 0; - - switch(hook->iType) { - case GC_USER_MESSAGE: - { - CMStringA szText(ptrA(mir_utf8encodeW(hook->ptszText))); - szText.Replace("%%", "%"); - if (szText.IsEmpty()) - break; - - // Outgoing message - debugLogA("**Chat - Outgoing message: %s", szText.c_str()); - ForkThread(&MinecraftDynmapProto::SendMsgWorker, new std::string(szText)); - } - break; - - case GC_SESSION_TERMINATE: - m_nick.clear(); - SetStatus(ID_STATUS_OFFLINE); - break; - } - - return 0; -} - -void MinecraftDynmapProto::AddChatContact(const char *name) -{ - GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_JOIN }; - gce.time = uint32_t(time(0)); - gce.dwFlags = GCEF_UTF8 + GCEF_ADDTOLOG; - gce.pszUID.a = gce.pszNick.a = name; - gce.bIsMe = (m_nick == name); - - if (gce.bIsMe) - gce.pszStatus.a = "Admin"; - else - gce.pszStatus.a = "Normal"; - - Chat_Event(&gce); -} - -void MinecraftDynmapProto::DeleteChatContact(const char *name) -{ - GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_PART }; - gce.dwFlags = GCEF_UTF8 + GCEF_ADDTOLOG; - gce.pszUID.a = gce.pszNick.a = name; - gce.time = uint32_t(time(0)); - gce.bIsMe = (m_nick == name); - Chat_Event(&gce); -} - -INT_PTR MinecraftDynmapProto::OnJoinChat(WPARAM,LPARAM suppress) -{ - ptrW tszTitle(mir_a2u_cp(m_title.c_str(), CP_UTF8)); - - // Create the group chat session - SESSION_INFO *si = Chat_NewSession(GCW_PRIVMESS, m_szModuleName, m_tszUserName, tszTitle); - if (!si || m_iStatus == ID_STATUS_OFFLINE) - return 0; - - // Create a group - Chat_AddGroup(si, TranslateT("Admin")); - Chat_AddGroup(si, TranslateT("Normal")); - - // Note: Initialization will finish up in SetChatStatus, called separately - if (!suppress) - SetChatStatus(m_iStatus); - - return 0; -} - -void MinecraftDynmapProto::SetTopic(const char *topic) -{ - GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_TOPIC }; - gce.dwFlags = GCEF_UTF8; - gce.time = ::time(0); - gce.pszText.a = topic; - Chat_Event( &gce); -} - -INT_PTR MinecraftDynmapProto::OnLeaveChat(WPARAM,LPARAM) -{ - Chat_Control(m_szModuleName, m_tszUserName, SESSION_OFFLINE); - Chat_Terminate(m_szModuleName, m_tszUserName); - return 0; -} - -void MinecraftDynmapProto::SetChatStatus(int status) -{ - if (status == ID_STATUS_ONLINE) - { - // Load actual name from database - ptrA nick(db_get_sa(0, m_szModuleName, MINECRAFTDYNMAP_KEY_NAME, Translate("You"))); - m_nick = nick; - - // Add self contact - AddChatContact(m_nick.c_str()); - - Chat_Control(m_szModuleName, m_tszUserName, SESSION_INITDONE); - Chat_Control(m_szModuleName, m_tszUserName, SESSION_ONLINE); - } - else Chat_Control(m_szModuleName, m_tszUserName, SESSION_OFFLINE); -} - -void MinecraftDynmapProto::ClearChat() -{ - Chat_Control(m_szModuleName, m_tszUserName, WINDOW_CLEARLOG); -} - -// TODO: Could this be done better? -MCONTACT MinecraftDynmapProto::GetChatHandle() -{ - /*if (chatHandle_ != NULL) - return chatHandle_; - - for (auto &hContact : AccContacts()) { - if (db_get_b(hContact, m_szModuleName, "ChatRoom", 0) > 0) { - ptrA id = db_get_sa(hContact, m_szModuleName, "ChatRoomId"); - if (id != NULL && !strcmp(id, m_szModuleName)) - return hContact; - } - } - - return NULL;*/ - - GC_INFO gci = {0}; - gci.Flags = GCF_HCONTACT; - gci.pszModule = m_szModuleName; - gci.pszID = m_tszUserName; - Chat_GetInfo(&gci); - - return gci.hContact; +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+void MinecraftDynmapProto::UpdateChat(const char *name, const char *message, const time_t timestamp, bool addtolog)
+{
+ // replace % to %% to not interfere with chat color codes
+ CMStringA szMessage(message);
+ szMessage.Replace("%", "%%");
+
+ GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_MESSAGE };
+ gce.dwFlags = GCEF_UTF8;
+ gce.time = timestamp;
+ gce.pszText.a = szMessage.c_str();
+
+ if (name == NULL) {
+ gce.iType = GC_EVENT_INFORMATION;
+ name = TranslateU("Server");
+ gce.bIsMe = false;
+ }
+ else gce.bIsMe = (m_nick == name);
+
+ if (addtolog)
+ gce.dwFlags |= GCEF_ADDTOLOG;
+
+ gce.pszUID.a = gce.pszNick.a = name;
+ Chat_Event(&gce);
+}
+
+int MinecraftDynmapProto::OnChatEvent(WPARAM, LPARAM lParam)
+{
+ GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
+ if(strcmp(hook->si->pszModule,m_szModuleName))
+ return 0;
+
+ switch(hook->iType) {
+ case GC_USER_MESSAGE:
+ {
+ CMStringA szText(ptrA(mir_utf8encodeW(hook->ptszText)));
+ szText.Replace("%%", "%");
+ if (szText.IsEmpty())
+ break;
+
+ // Outgoing message
+ debugLogA("**Chat - Outgoing message: %s", szText.c_str());
+ ForkThread(&MinecraftDynmapProto::SendMsgWorker, new std::string(szText));
+ }
+ break;
+
+ case GC_SESSION_TERMINATE:
+ m_nick.clear();
+ SetStatus(ID_STATUS_OFFLINE);
+ break;
+ }
+
+ return 0;
+}
+
+void MinecraftDynmapProto::AddChatContact(const char *name)
+{
+ GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_JOIN };
+ gce.time = uint32_t(time(0));
+ gce.dwFlags = GCEF_UTF8 + GCEF_ADDTOLOG;
+ gce.pszUID.a = gce.pszNick.a = name;
+ gce.bIsMe = (m_nick == name);
+
+ if (gce.bIsMe)
+ gce.pszStatus.a = "Admin";
+ else
+ gce.pszStatus.a = "Normal";
+
+ Chat_Event(&gce);
+}
+
+void MinecraftDynmapProto::DeleteChatContact(const char *name)
+{
+ GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_PART };
+ gce.dwFlags = GCEF_UTF8 + GCEF_ADDTOLOG;
+ gce.pszUID.a = gce.pszNick.a = name;
+ gce.time = uint32_t(time(0));
+ gce.bIsMe = (m_nick == name);
+ Chat_Event(&gce);
+}
+
+INT_PTR MinecraftDynmapProto::OnJoinChat(WPARAM,LPARAM suppress)
+{
+ ptrW tszTitle(mir_a2u_cp(m_title.c_str(), CP_UTF8));
+
+ // Create the group chat session
+ SESSION_INFO *si = Chat_NewSession(GCW_PRIVMESS, m_szModuleName, m_tszUserName, tszTitle);
+ if (!si || m_iStatus == ID_STATUS_OFFLINE)
+ return 0;
+
+ // Create a group
+ Chat_AddGroup(si, TranslateT("Admin"));
+ Chat_AddGroup(si, TranslateT("Normal"));
+
+ // Note: Initialization will finish up in SetChatStatus, called separately
+ if (!suppress)
+ SetChatStatus(m_iStatus);
+
+ return 0;
+}
+
+void MinecraftDynmapProto::SetTopic(const char *topic)
+{
+ GCEVENT gce = { m_szModuleName, szRoomName, GC_EVENT_TOPIC };
+ gce.dwFlags = GCEF_UTF8;
+ gce.time = ::time(0);
+ gce.pszText.a = topic;
+ Chat_Event( &gce);
+}
+
+INT_PTR MinecraftDynmapProto::OnLeaveChat(WPARAM,LPARAM)
+{
+ Chat_Control(m_szModuleName, m_tszUserName, SESSION_OFFLINE);
+ Chat_Terminate(m_szModuleName, m_tszUserName);
+ return 0;
+}
+
+void MinecraftDynmapProto::SetChatStatus(int status)
+{
+ if (status == ID_STATUS_ONLINE)
+ {
+ // Load actual name from database
+ ptrA nick(db_get_sa(0, m_szModuleName, MINECRAFTDYNMAP_KEY_NAME, Translate("You")));
+ m_nick = nick;
+
+ // Add self contact
+ AddChatContact(m_nick.c_str());
+
+ Chat_Control(m_szModuleName, m_tszUserName, SESSION_INITDONE);
+ Chat_Control(m_szModuleName, m_tszUserName, SESSION_ONLINE);
+ }
+ else Chat_Control(m_szModuleName, m_tszUserName, SESSION_OFFLINE);
+}
+
+void MinecraftDynmapProto::ClearChat()
+{
+ Chat_Control(m_szModuleName, m_tszUserName, WINDOW_CLEARLOG);
+}
+
+// TODO: Could this be done better?
+MCONTACT MinecraftDynmapProto::GetChatHandle()
+{
+ /*if (chatHandle_ != NULL)
+ return chatHandle_;
+
+ for (auto &hContact : AccContacts()) {
+ if (db_get_b(hContact, m_szModuleName, "ChatRoom", 0) > 0) {
+ ptrA id = db_get_sa(hContact, m_szModuleName, "ChatRoomId");
+ if (id != NULL && !strcmp(id, m_szModuleName))
+ return hContact;
+ }
+ }
+
+ return NULL;*/
+
+ GC_INFO gci = {0};
+ gci.Flags = GCF_HCONTACT;
+ gci.pszModule = m_szModuleName;
+ gci.pszID = m_tszUserName;
+ Chat_GetInfo(&gci);
+
+ return gci.hContact;
}
\ No newline at end of file diff --git a/protocols/MinecraftDynmap/src/communication.cpp b/protocols/MinecraftDynmap/src/communication.cpp index 7ca5f0a732..47e6ec4480 100644 --- a/protocols/MinecraftDynmap/src/communication.cpp +++ b/protocols/MinecraftDynmap/src/communication.cpp @@ -1,442 +1,442 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "stdafx.h" - -http::response MinecraftDynmapProto::sendRequest(const int request_type, std::string *post_data, std::string *get_data) -{ - http::response resp; - - // Prepare the request - NETLIBHTTPREQUEST nlhr = { sizeof(NETLIBHTTPREQUEST) }; - - // FIXME: get server - - // Set request URL - std::string url = m_server + chooseAction(request_type, get_data); - nlhr.szUrl = (char*)url.c_str(); - - // Set timeout (bigger for channel request) - nlhr.timeout = 1000 * ((request_type == MINECRAFTDYNMAP_REQUEST_EVENTS) ? 65 : 20); - - // Set request type (GET/POST) and eventually also POST data - if (post_data != nullptr) { - nlhr.requestType = REQUEST_POST; - nlhr.pData = (char*)(*post_data).c_str(); - nlhr.dataLength = (int)post_data->length(); - } - else { - nlhr.requestType = REQUEST_GET; - } - - // Set headers - it depends on requestType so it must be after setting that - nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount); - - // Set flags - nlhr.flags = NLHRF_HTTP11; - -#ifdef _DEBUG - nlhr.flags |= NLHRF_DUMPASTEXT; -#else - nlhr.flags |= NLHRF_NODUMP; -#endif - - // Set persistent connection (or not) - switch (request_type) - { - case MINECRAFTDYNMAP_REQUEST_HOME: - nlhr.nlc = nullptr; - break; - - case MINECRAFTDYNMAP_REQUEST_EVENTS: - nlhr.nlc = hEventsConnection; - nlhr.flags |= NLHRF_PERSISTENT; - break; - - default: - WaitForSingleObject(connection_lock_, INFINITE); - nlhr.nlc = hConnection; - nlhr.flags |= NLHRF_PERSISTENT; - break; - } - - debugLogA("@@@@@ Sending request to '%s'", nlhr.szUrl); - - // Send the request - NLHR_PTR pnlhr(Netlib_HttpTransaction(m_hNetlibUser, &nlhr)); - - mir_free(nlhr.headers); - - // Remember the persistent connection handle (or not) - switch (request_type) { - case MINECRAFTDYNMAP_REQUEST_HOME: - break; - - case MINECRAFTDYNMAP_REQUEST_EVENTS: - hEventsConnection = pnlhr ? pnlhr->nlc : nullptr; - break; - - default: - ReleaseMutex(connection_lock_); - hConnection = pnlhr ? pnlhr->nlc : nullptr; - break; - } - - // Check and copy response data - if (pnlhr != nullptr) - { - debugLogA("@@@@@ Got response with code %d", pnlhr->resultCode); - store_headers(&resp, pnlhr->headers, pnlhr->headersCount); - resp.code = pnlhr->resultCode; - resp.data = pnlhr->pData ? pnlhr->pData : ""; - - // debugLogA("&&&&& Got response: %s", resp.data.c_str()); - } else { - debugLogA("!!!!! No response from server (time-out)"); - resp.code = HTTP_CODE_FAKE_DISCONNECTED; - // Better to have something set explicitely as this value is compaired in all communication requests - } - - return resp; -} - -////////////////////////////////////////////////////////////////////////////// - -std::string MinecraftDynmapProto::chooseAction(int request_type, std::string *get_data) -{ - switch (request_type) { - case MINECRAFTDYNMAP_REQUEST_MESSAGE: - return "/up/sendmessage"; - - case MINECRAFTDYNMAP_REQUEST_CONFIGURATION: - return "/up/configuration"; - - case MINECRAFTDYNMAP_REQUEST_EVENTS: - return std::string("/up/world/world/") + (!m_timestamp.empty() ? m_timestamp : "0"); - - //case MINECRAFTDYNMAP_REQUEST_HOME: - default: - return "/" + *get_data; - } -} - - -NETLIBHTTPHEADER* MinecraftDynmapProto::get_request_headers(int request_type, int* headers_count) -{ - if (request_type == REQUEST_POST) - *headers_count = 5; - else - *headers_count = 4; - - NETLIBHTTPHEADER *headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER)*(*headers_count)); - - if (request_type == REQUEST_POST) { - headers[4].szName = "Content-Type"; - headers[4].szValue = "application/json; charset=utf-8"; - } - - headers[3].szName = "Cookie"; - headers[3].szValue = (char *)m_cookie.c_str(); - headers[2].szName = "User-Agent"; - headers[2].szValue = (char *)g_strUserAgent.c_str(); - headers[1].szName = "Accept"; - headers[1].szValue = "*/*"; - headers[0].szName = "Accept-Language"; - headers[0].szValue = "en,en-US;q=0.9"; - - return headers; -} - -void MinecraftDynmapProto::store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headersCount) -{ - for (size_t i = 0; i < (size_t)headersCount; i++) { - std::string header_name = headers[i].szName; - std::string header_value = headers[i].szValue; - - resp->headers[header_name] = header_value; - } -} - -////////////////////////////////////////////////////////////////////////////// - -bool MinecraftDynmapProto::doSignOn() -{ - handleEntry(__FUNCTION__); - - http::response resp = sendRequest(MINECRAFTDYNMAP_REQUEST_CONFIGURATION); - - if (resp.code != HTTP_CODE_OK) { - return handleError(__FUNCTION__, "Can't load configuration", true); - } - - JSONNode root = JSONNode::parse(resp.data.c_str()); - if (!root) - return false; - - /* - const JSONNode &allowchat_ = root["allowchat"]; // boolean - const JSONNode &allowwebchat_ = root["allowwebchat"]; // boolean - const JSONNode &loggedin_ = root["loggedin"]; // boolean - const JSONNode &loginEnabled_ = root["login-enabled"]; // boolean - const JSONNode &loginRequired_ = root["webchat-requires-login"]; // boolean - */ - - const JSONNode &title_ = root["title"]; // name of server - const JSONNode &interval_ = root["webchat-interval"]; // limit in seconds for sending messages - const JSONNode &rate_ = root["updaterate"]; // probably update rate for events request - - if (!title_ || !interval_ || !rate_) { - return handleError(__FUNCTION__, "No title, interval or rate in configuration", true); - } - - m_title = title_.as_string(); - m_interval = interval_.as_int(); - m_updateRate = rate_.as_int(); - m_cookie.clear(); - - if (resp.headers.find("Set-Cookie") != resp.headers.end()) { - // Load Session identifier - std::string cookies = resp.headers["Set-Cookie"]; - - const char *findStr = "JSESSIONID="; - std::string::size_type start = cookies.find(findStr); - - if (start != std::string::npos) { - m_cookie = cookies.substr(start, cookies.find(";") - start); - } - } - - if (m_cookie.empty()) { - return handleError(__FUNCTION__, "Empty session id", true); - } - - return handleSuccess(__FUNCTION__); -} - -bool MinecraftDynmapProto::doEvents() -{ - handleEntry(__FUNCTION__); - - // Get update - http::response resp = sendRequest(MINECRAFTDYNMAP_REQUEST_EVENTS); - - if (resp.code != HTTP_CODE_OK) - return handleError(__FUNCTION__, "Response is not code 200"); - - JSONNode root = JSONNode::parse(resp.data.c_str()); - if (!root) - return handleError(__FUNCTION__, "Invalid JSON response"); - - const JSONNode ×tamp_ = root["timestamp"]; - if (!timestamp_) - return handleError(__FUNCTION__, "Received no timestamp node"); - - m_timestamp = timestamp_.as_string(); - - const JSONNode &updates_ = root["updates"]; - if (!updates_) - return handleError(__FUNCTION__, "Received no updates node"); - - for (auto it = updates_.begin(); it != updates_.end(); ++it) { - const JSONNode &type_ = (*it)["type"]; - if (type_ && type_.as_string() == "chat") { - const JSONNode &time_ = (*it)["timestamp"]; - // const JSONNode &source_ = (*it)["source"]; // e.g. "web" - const JSONNode &playerName_ = (*it)["playerName"]; - const JSONNode &message_ = (*it)["message"]; - // TODO: there are also "channel" and "account" elements - - if (!time_ || !playerName_ || !message_) { - debugLogW(L"Error: No player name, time or text for message"); - continue; - } - - time_t timestamp = atoi(time_.as_string().c_str()); - std::string name = playerName_.as_string(); - std::string message = message_.as_string(); - - debugLogW(L"Received message: [%d] %s -> %s", timestamp, name.c_str(), message.c_str()); - UpdateChat(name.c_str(), message.c_str(), timestamp); - } - } - - return handleSuccess(__FUNCTION__); -} - -bool MinecraftDynmapProto::doSendMessage(const std::string &message_text) -{ - handleEntry(__FUNCTION__); - - JSONNode json(JSON_NODE); - json.push_back(JSONNode("name", m_nick.c_str())); - json.push_back(JSONNode("message", message_text.c_str())); - std::string data = json.write(); - - http::response resp = sendRequest(MINECRAFTDYNMAP_REQUEST_MESSAGE, &data); - - if (resp.code == HTTP_CODE_OK) { - JSONNode root = JSONNode::parse(resp.data.c_str()); - if (root) { - const JSONNode &error_ = root["error"]; - if (error_) { - std::string error = error_.as_string(); - if (error == "none") { - return handleSuccess(__FUNCTION__); - } - else if (error == "not-allowed") { - UpdateChat(nullptr, Translate("Message was not sent. Probably you are sending them too fast or chat is disabled completely.")); - } - } - } - } - - return handleError(__FUNCTION__); -} - -std::string MinecraftDynmapProto::doGetPage(const int request_type) -{ - handleEntry(__FUNCTION__); - - http::response resp = sendRequest(request_type); - - if (resp.code == HTTP_CODE_OK) { - handleSuccess(__FUNCTION__); - } else { - handleError(__FUNCTION__); - } - - return resp.data; -} - -void MinecraftDynmapProto::SignOnWorker(void*) -{ - SYSTEMTIME t; - GetLocalTime(&t); - debugLogA("[%d.%d.%d] Using Omegle Protocol %s", t.wDay, t.wMonth, t.wYear, __VERSION_STRING_DOTS); - - ScopedLock s(signon_lock_); - - int old_status = m_iStatus; - - // Load server from database - ptrA str(db_get_sa(0, m_szModuleName, MINECRAFTDYNMAP_KEY_SERVER)); - if (!str || !str[0]) { - MessageBox(nullptr, TranslateT("Set server address to connect."), m_tszUserName, MB_OK); - SetStatus(ID_STATUS_OFFLINE); - return; - } - m_server = str; - - // Fix format of given server - if (m_server.substr(0, 7) != "http://" && m_server.substr(0, 8) != "https://") - m_server = "http://" + m_server; - if (m_server.substr(m_server.length() - 1, 1) == "/") - m_server = m_server.substr(0, m_server.length() -1); - - if (doSignOn()) { - // Signed in, switch to online, create chatroom and start events loop - m_iStatus = m_iDesiredStatus; - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - - setDword("LogonTS", (uint32_t)time(0)); - ClearChat(); - OnJoinChat(0, false); - - ResetEvent(events_loop_event_); - - ForkThread(&MinecraftDynmapProto::EventsLoop, this); - } - else { - // Some error - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus); - } - -} - -void MinecraftDynmapProto::SignOffWorker(void*) -{ - ScopedLock s(signon_lock_); - - SetEvent(events_loop_event_); - - m_cookie.clear(); - m_title.clear(); - m_server.clear(); - m_timestamp.clear(); - - int old_status = m_iStatus; - m_iStatus = ID_STATUS_OFFLINE; - - Netlib_Shutdown(hEventsConnection); - - OnLeaveChat(NULL, NULL); - - delSetting("LogonTS"); - - ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - - if (hConnection) - Netlib_CloseHandle(hConnection); - hConnection = nullptr; - - if (hEventsConnection) - Netlib_CloseHandle(hEventsConnection); - hEventsConnection = nullptr; -} - -void MinecraftDynmapProto::EventsLoop(void *) -{ - ScopedLock s(events_loop_lock_); - - time_t tim = ::time(0); - debugLogA(">>>>> Entering %s[%d]", __FUNCTION__, tim); - - while (doEvents()) - { - if (!isOnline()) - break; - - if (WaitForSingleObjectEx(events_loop_event_, m_updateRate, true) != WAIT_TIMEOUT) // FIXME: correct timeout - break; - - debugLogA("***** %s[%d] refreshing...", __FUNCTION__, tim); - } - - ResetEvent(events_loop_event_); - ResetEvent(events_loop_lock_); - debugLogA("<<<<< Exiting %s[%d]", __FUNCTION__, tim); -} - -void MinecraftDynmapProto::SendMsgWorker(void *p) -{ - if (p == nullptr) - return; - - ScopedLock s(send_message_lock_); - - CMStringA data = ((std::string*)p)->c_str(); - delete (std::string*)p; - - data.TrimRight(); - - if (isOnline() && data.GetLength()) - doSendMessage(data.c_str()); -} +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+http::response MinecraftDynmapProto::sendRequest(const int request_type, std::string *post_data, std::string *get_data)
+{
+ http::response resp;
+
+ // Prepare the request
+ NETLIBHTTPREQUEST nlhr = { sizeof(NETLIBHTTPREQUEST) };
+
+ // FIXME: get server
+
+ // Set request URL
+ std::string url = m_server + chooseAction(request_type, get_data);
+ nlhr.szUrl = (char*)url.c_str();
+
+ // Set timeout (bigger for channel request)
+ nlhr.timeout = 1000 * ((request_type == MINECRAFTDYNMAP_REQUEST_EVENTS) ? 65 : 20);
+
+ // Set request type (GET/POST) and eventually also POST data
+ if (post_data != nullptr) {
+ nlhr.requestType = REQUEST_POST;
+ nlhr.pData = (char*)(*post_data).c_str();
+ nlhr.dataLength = (int)post_data->length();
+ }
+ else {
+ nlhr.requestType = REQUEST_GET;
+ }
+
+ // Set headers - it depends on requestType so it must be after setting that
+ nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount);
+
+ // Set flags
+ nlhr.flags = NLHRF_HTTP11;
+
+#ifdef _DEBUG
+ nlhr.flags |= NLHRF_DUMPASTEXT;
+#else
+ nlhr.flags |= NLHRF_NODUMP;
+#endif
+
+ // Set persistent connection (or not)
+ switch (request_type)
+ {
+ case MINECRAFTDYNMAP_REQUEST_HOME:
+ nlhr.nlc = nullptr;
+ break;
+
+ case MINECRAFTDYNMAP_REQUEST_EVENTS:
+ nlhr.nlc = hEventsConnection;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+
+ default:
+ WaitForSingleObject(connection_lock_, INFINITE);
+ nlhr.nlc = hConnection;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+ }
+
+ debugLogA("@@@@@ Sending request to '%s'", nlhr.szUrl);
+
+ // Send the request
+ NLHR_PTR pnlhr(Netlib_HttpTransaction(m_hNetlibUser, &nlhr));
+
+ mir_free(nlhr.headers);
+
+ // Remember the persistent connection handle (or not)
+ switch (request_type) {
+ case MINECRAFTDYNMAP_REQUEST_HOME:
+ break;
+
+ case MINECRAFTDYNMAP_REQUEST_EVENTS:
+ hEventsConnection = pnlhr ? pnlhr->nlc : nullptr;
+ break;
+
+ default:
+ ReleaseMutex(connection_lock_);
+ hConnection = pnlhr ? pnlhr->nlc : nullptr;
+ break;
+ }
+
+ // Check and copy response data
+ if (pnlhr != nullptr)
+ {
+ debugLogA("@@@@@ Got response with code %d", pnlhr->resultCode);
+ store_headers(&resp, pnlhr->headers, pnlhr->headersCount);
+ resp.code = pnlhr->resultCode;
+ resp.data = pnlhr->pData ? pnlhr->pData : "";
+
+ // debugLogA("&&&&& Got response: %s", resp.data.c_str());
+ } else {
+ debugLogA("!!!!! No response from server (time-out)");
+ resp.code = HTTP_CODE_FAKE_DISCONNECTED;
+ // Better to have something set explicitely as this value is compaired in all communication requests
+ }
+
+ return resp;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+std::string MinecraftDynmapProto::chooseAction(int request_type, std::string *get_data)
+{
+ switch (request_type) {
+ case MINECRAFTDYNMAP_REQUEST_MESSAGE:
+ return "/up/sendmessage";
+
+ case MINECRAFTDYNMAP_REQUEST_CONFIGURATION:
+ return "/up/configuration";
+
+ case MINECRAFTDYNMAP_REQUEST_EVENTS:
+ return std::string("/up/world/world/") + (!m_timestamp.empty() ? m_timestamp : "0");
+
+ //case MINECRAFTDYNMAP_REQUEST_HOME:
+ default:
+ return "/" + *get_data;
+ }
+}
+
+
+NETLIBHTTPHEADER* MinecraftDynmapProto::get_request_headers(int request_type, int* headers_count)
+{
+ if (request_type == REQUEST_POST)
+ *headers_count = 5;
+ else
+ *headers_count = 4;
+
+ NETLIBHTTPHEADER *headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER)*(*headers_count));
+
+ if (request_type == REQUEST_POST) {
+ headers[4].szName = "Content-Type";
+ headers[4].szValue = "application/json; charset=utf-8";
+ }
+
+ headers[3].szName = "Cookie";
+ headers[3].szValue = (char *)m_cookie.c_str();
+ headers[2].szName = "User-Agent";
+ headers[2].szValue = (char *)g_strUserAgent.c_str();
+ headers[1].szName = "Accept";
+ headers[1].szValue = "*/*";
+ headers[0].szName = "Accept-Language";
+ headers[0].szValue = "en,en-US;q=0.9";
+
+ return headers;
+}
+
+void MinecraftDynmapProto::store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headersCount)
+{
+ for (size_t i = 0; i < (size_t)headersCount; i++) {
+ std::string header_name = headers[i].szName;
+ std::string header_value = headers[i].szValue;
+
+ resp->headers[header_name] = header_value;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool MinecraftDynmapProto::doSignOn()
+{
+ handleEntry(__FUNCTION__);
+
+ http::response resp = sendRequest(MINECRAFTDYNMAP_REQUEST_CONFIGURATION);
+
+ if (resp.code != HTTP_CODE_OK) {
+ return handleError(__FUNCTION__, "Can't load configuration", true);
+ }
+
+ JSONNode root = JSONNode::parse(resp.data.c_str());
+ if (!root)
+ return false;
+
+ /*
+ const JSONNode &allowchat_ = root["allowchat"]; // boolean
+ const JSONNode &allowwebchat_ = root["allowwebchat"]; // boolean
+ const JSONNode &loggedin_ = root["loggedin"]; // boolean
+ const JSONNode &loginEnabled_ = root["login-enabled"]; // boolean
+ const JSONNode &loginRequired_ = root["webchat-requires-login"]; // boolean
+ */
+
+ const JSONNode &title_ = root["title"]; // name of server
+ const JSONNode &interval_ = root["webchat-interval"]; // limit in seconds for sending messages
+ const JSONNode &rate_ = root["updaterate"]; // probably update rate for events request
+
+ if (!title_ || !interval_ || !rate_) {
+ return handleError(__FUNCTION__, "No title, interval or rate in configuration", true);
+ }
+
+ m_title = title_.as_string();
+ m_interval = interval_.as_int();
+ m_updateRate = rate_.as_int();
+ m_cookie.clear();
+
+ if (resp.headers.find("Set-Cookie") != resp.headers.end()) {
+ // Load Session identifier
+ std::string cookies = resp.headers["Set-Cookie"];
+
+ const char *findStr = "JSESSIONID=";
+ std::string::size_type start = cookies.find(findStr);
+
+ if (start != std::string::npos) {
+ m_cookie = cookies.substr(start, cookies.find(";") - start);
+ }
+ }
+
+ if (m_cookie.empty()) {
+ return handleError(__FUNCTION__, "Empty session id", true);
+ }
+
+ return handleSuccess(__FUNCTION__);
+}
+
+bool MinecraftDynmapProto::doEvents()
+{
+ handleEntry(__FUNCTION__);
+
+ // Get update
+ http::response resp = sendRequest(MINECRAFTDYNMAP_REQUEST_EVENTS);
+
+ if (resp.code != HTTP_CODE_OK)
+ return handleError(__FUNCTION__, "Response is not code 200");
+
+ JSONNode root = JSONNode::parse(resp.data.c_str());
+ if (!root)
+ return handleError(__FUNCTION__, "Invalid JSON response");
+
+ const JSONNode ×tamp_ = root["timestamp"];
+ if (!timestamp_)
+ return handleError(__FUNCTION__, "Received no timestamp node");
+
+ m_timestamp = timestamp_.as_string();
+
+ const JSONNode &updates_ = root["updates"];
+ if (!updates_)
+ return handleError(__FUNCTION__, "Received no updates node");
+
+ for (auto it = updates_.begin(); it != updates_.end(); ++it) {
+ const JSONNode &type_ = (*it)["type"];
+ if (type_ && type_.as_string() == "chat") {
+ const JSONNode &time_ = (*it)["timestamp"];
+ // const JSONNode &source_ = (*it)["source"]; // e.g. "web"
+ const JSONNode &playerName_ = (*it)["playerName"];
+ const JSONNode &message_ = (*it)["message"];
+ // TODO: there are also "channel" and "account" elements
+
+ if (!time_ || !playerName_ || !message_) {
+ debugLogW(L"Error: No player name, time or text for message");
+ continue;
+ }
+
+ time_t timestamp = atoi(time_.as_string().c_str());
+ std::string name = playerName_.as_string();
+ std::string message = message_.as_string();
+
+ debugLogW(L"Received message: [%d] %s -> %s", timestamp, name.c_str(), message.c_str());
+ UpdateChat(name.c_str(), message.c_str(), timestamp);
+ }
+ }
+
+ return handleSuccess(__FUNCTION__);
+}
+
+bool MinecraftDynmapProto::doSendMessage(const std::string &message_text)
+{
+ handleEntry(__FUNCTION__);
+
+ JSONNode json(JSON_NODE);
+ json.push_back(JSONNode("name", m_nick.c_str()));
+ json.push_back(JSONNode("message", message_text.c_str()));
+ std::string data = json.write();
+
+ http::response resp = sendRequest(MINECRAFTDYNMAP_REQUEST_MESSAGE, &data);
+
+ if (resp.code == HTTP_CODE_OK) {
+ JSONNode root = JSONNode::parse(resp.data.c_str());
+ if (root) {
+ const JSONNode &error_ = root["error"];
+ if (error_) {
+ std::string error = error_.as_string();
+ if (error == "none") {
+ return handleSuccess(__FUNCTION__);
+ }
+ else if (error == "not-allowed") {
+ UpdateChat(nullptr, Translate("Message was not sent. Probably you are sending them too fast or chat is disabled completely."));
+ }
+ }
+ }
+ }
+
+ return handleError(__FUNCTION__);
+}
+
+std::string MinecraftDynmapProto::doGetPage(const int request_type)
+{
+ handleEntry(__FUNCTION__);
+
+ http::response resp = sendRequest(request_type);
+
+ if (resp.code == HTTP_CODE_OK) {
+ handleSuccess(__FUNCTION__);
+ } else {
+ handleError(__FUNCTION__);
+ }
+
+ return resp.data;
+}
+
+void MinecraftDynmapProto::SignOnWorker(void*)
+{
+ SYSTEMTIME t;
+ GetLocalTime(&t);
+ debugLogA("[%d.%d.%d] Using Omegle Protocol %s", t.wDay, t.wMonth, t.wYear, __VERSION_STRING_DOTS);
+
+ ScopedLock s(signon_lock_);
+
+ int old_status = m_iStatus;
+
+ // Load server from database
+ ptrA str(db_get_sa(0, m_szModuleName, MINECRAFTDYNMAP_KEY_SERVER));
+ if (!str || !str[0]) {
+ MessageBox(nullptr, TranslateT("Set server address to connect."), m_tszUserName, MB_OK);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+ m_server = str;
+
+ // Fix format of given server
+ if (m_server.substr(0, 7) != "http://" && m_server.substr(0, 8) != "https://")
+ m_server = "http://" + m_server;
+ if (m_server.substr(m_server.length() - 1, 1) == "/")
+ m_server = m_server.substr(0, m_server.length() -1);
+
+ if (doSignOn()) {
+ // Signed in, switch to online, create chatroom and start events loop
+ m_iStatus = m_iDesiredStatus;
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+
+ setDword("LogonTS", (uint32_t)time(0));
+ ClearChat();
+ OnJoinChat(0, false);
+
+ ResetEvent(events_loop_event_);
+
+ ForkThread(&MinecraftDynmapProto::EventsLoop, this);
+ }
+ else {
+ // Some error
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)old_status, m_iStatus);
+ }
+
+}
+
+void MinecraftDynmapProto::SignOffWorker(void*)
+{
+ ScopedLock s(signon_lock_);
+
+ SetEvent(events_loop_event_);
+
+ m_cookie.clear();
+ m_title.clear();
+ m_server.clear();
+ m_timestamp.clear();
+
+ int old_status = m_iStatus;
+ m_iStatus = ID_STATUS_OFFLINE;
+
+ Netlib_Shutdown(hEventsConnection);
+
+ OnLeaveChat(NULL, NULL);
+
+ delSetting("LogonTS");
+
+ ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+
+ if (hConnection)
+ Netlib_CloseHandle(hConnection);
+ hConnection = nullptr;
+
+ if (hEventsConnection)
+ Netlib_CloseHandle(hEventsConnection);
+ hEventsConnection = nullptr;
+}
+
+void MinecraftDynmapProto::EventsLoop(void *)
+{
+ ScopedLock s(events_loop_lock_);
+
+ time_t tim = ::time(0);
+ debugLogA(">>>>> Entering %s[%d]", __FUNCTION__, tim);
+
+ while (doEvents())
+ {
+ if (!isOnline())
+ break;
+
+ if (WaitForSingleObjectEx(events_loop_event_, m_updateRate, true) != WAIT_TIMEOUT) // FIXME: correct timeout
+ break;
+
+ debugLogA("***** %s[%d] refreshing...", __FUNCTION__, tim);
+ }
+
+ ResetEvent(events_loop_event_);
+ ResetEvent(events_loop_lock_);
+ debugLogA("<<<<< Exiting %s[%d]", __FUNCTION__, tim);
+}
+
+void MinecraftDynmapProto::SendMsgWorker(void *p)
+{
+ if (p == nullptr)
+ return;
+
+ ScopedLock s(send_message_lock_);
+
+ CMStringA data = ((std::string*)p)->c_str();
+ delete (std::string*)p;
+
+ data.TrimRight();
+
+ if (isOnline() && data.GetLength())
+ doSendMessage(data.c_str());
+}
diff --git a/protocols/MinecraftDynmap/src/constants.h b/protocols/MinecraftDynmap/src/constants.h index 42d1d2c33c..b8f454fe0f 100644 --- a/protocols/MinecraftDynmap/src/constants.h +++ b/protocols/MinecraftDynmap/src/constants.h @@ -1,46 +1,46 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -// Product management -#define MINECRAFTDYNMAP_NAME "Minecraft Dynmap" - -// Limits -#define MINECRAFTDYNMAP_TIMEOUTS_LIMIT 6 -// This is just guessed some wise limit -#define MINECRAFTDYNMAP_MESSAGE_LIMIT 256 -#define MINECRAFTDYNMAP_MESSAGE_LIMIT_TEXT "256" - -#define MINECRAFTDYNMAP_QUESTION_MIN_LENGTH 10 - -// Request types -#define MINECRAFTDYNMAP_REQUEST_HOME 100 // getting server homepage -#define MINECRAFTDYNMAP_REQUEST_CONFIGURATION 101 // getting server configuration -#define MINECRAFTDYNMAP_REQUEST_EVENTS 102 // receiving events -#define MINECRAFTDYNMAP_REQUEST_MESSAGE 103 // sending messages - -// DB settings -#define MINECRAFTDYNMAP_KEY_TIMEOUTS_LIMIT "TimeoutsLimit" // [HIDDEN] - -#define MINECRAFTDYNMAP_KEY_NAME "Nick" -#define MINECRAFTDYNMAP_KEY_SERVER "Server" +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+// Product management
+#define MINECRAFTDYNMAP_NAME "Minecraft Dynmap"
+
+// Limits
+#define MINECRAFTDYNMAP_TIMEOUTS_LIMIT 6
+// This is just guessed some wise limit
+#define MINECRAFTDYNMAP_MESSAGE_LIMIT 256
+#define MINECRAFTDYNMAP_MESSAGE_LIMIT_TEXT "256"
+
+#define MINECRAFTDYNMAP_QUESTION_MIN_LENGTH 10
+
+// Request types
+#define MINECRAFTDYNMAP_REQUEST_HOME 100 // getting server homepage
+#define MINECRAFTDYNMAP_REQUEST_CONFIGURATION 101 // getting server configuration
+#define MINECRAFTDYNMAP_REQUEST_EVENTS 102 // receiving events
+#define MINECRAFTDYNMAP_REQUEST_MESSAGE 103 // sending messages
+
+// DB settings
+#define MINECRAFTDYNMAP_KEY_TIMEOUTS_LIMIT "TimeoutsLimit" // [HIDDEN]
+
+#define MINECRAFTDYNMAP_KEY_NAME "Nick"
+#define MINECRAFTDYNMAP_KEY_SERVER "Server"
diff --git a/protocols/MinecraftDynmap/src/dialogs.cpp b/protocols/MinecraftDynmap/src/dialogs.cpp index 54ea975fae..38707c6870 100644 --- a/protocols/MinecraftDynmap/src/dialogs.cpp +++ b/protocols/MinecraftDynmap/src/dialogs.cpp @@ -1,98 +1,98 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -// Icons - -static IconItem iconList[] = -{ - { "proto", LPGEN("Protocol icon"), IDI_PROTO }, -}; - -static HANDLE hIconLibItem[_countof(iconList)]; - -void InitIcons(void) -{ - g_plugin.registerIcon("Protocols/MinecraftDynmap", iconList, "MinecraftDynmap"); -} - -// Dialogs - -static void LoadDBText(MinecraftDynmapProto* ppro, HWND hwnd, int idCtrl, const char* szSetting) -{ - ptrW tstr(db_get_wsa(0, ppro->m_szModuleName, szSetting)); - if (tstr) - SetDlgItemText(hwnd, idCtrl, tstr); -} - -static void StoreDBText(MinecraftDynmapProto* ppro, HWND hwnd, int idCtrl, const char* szSetting) -{ - wchar_t tstr[250 + 1]; - - GetDlgItemText(hwnd, idCtrl, tstr, _countof(tstr)); - if (tstr[0] != '\0') - db_set_ws(0, ppro->m_szModuleName, szSetting, tstr); - else - db_unset(0, ppro->m_szModuleName, szSetting); -} - - -INT_PTR CALLBACK MinecraftDynmapAccountProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) -{ - MinecraftDynmapProto *proto; - - switch (message) { - case WM_INITDIALOG: - TranslateDialogDefault(hwnd); - - proto = reinterpret_cast<MinecraftDynmapProto*>(lparam); - SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam); - - LoadDBText(proto, hwnd, IDC_SERVER, MINECRAFTDYNMAP_KEY_SERVER); - LoadDBText(proto, hwnd, IDC_NAME, MINECRAFTDYNMAP_KEY_NAME); - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wparam)) { - case IDC_NAME: - if (HIWORD(wparam) != EN_CHANGE || (HWND)lparam != GetFocus()) - return TRUE; - - SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0); - break; - } - break; - - case WM_NOTIFY: - if (reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY) { - proto = reinterpret_cast<MinecraftDynmapProto*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); - - StoreDBText(proto, hwnd, IDC_SERVER, MINECRAFTDYNMAP_KEY_SERVER); - StoreDBText(proto, hwnd, IDC_NAME, MINECRAFTDYNMAP_KEY_NAME); - - return TRUE; - } - break; - } - return FALSE; -} +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+// Icons
+
+static IconItem iconList[] =
+{
+ { "proto", LPGEN("Protocol icon"), IDI_PROTO },
+};
+
+static HANDLE hIconLibItem[_countof(iconList)];
+
+void InitIcons(void)
+{
+ g_plugin.registerIcon("Protocols/MinecraftDynmap", iconList, "MinecraftDynmap");
+}
+
+// Dialogs
+
+static void LoadDBText(MinecraftDynmapProto* ppro, HWND hwnd, int idCtrl, const char* szSetting)
+{
+ ptrW tstr(db_get_wsa(0, ppro->m_szModuleName, szSetting));
+ if (tstr)
+ SetDlgItemText(hwnd, idCtrl, tstr);
+}
+
+static void StoreDBText(MinecraftDynmapProto* ppro, HWND hwnd, int idCtrl, const char* szSetting)
+{
+ wchar_t tstr[250 + 1];
+
+ GetDlgItemText(hwnd, idCtrl, tstr, _countof(tstr));
+ if (tstr[0] != '\0')
+ db_set_ws(0, ppro->m_szModuleName, szSetting, tstr);
+ else
+ db_unset(0, ppro->m_szModuleName, szSetting);
+}
+
+
+INT_PTR CALLBACK MinecraftDynmapAccountProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ MinecraftDynmapProto *proto;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<MinecraftDynmapProto*>(lparam);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
+
+ LoadDBText(proto, hwnd, IDC_SERVER, MINECRAFTDYNMAP_KEY_SERVER);
+ LoadDBText(proto, hwnd, IDC_NAME, MINECRAFTDYNMAP_KEY_NAME);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wparam)) {
+ case IDC_NAME:
+ if (HIWORD(wparam) != EN_CHANGE || (HWND)lparam != GetFocus())
+ return TRUE;
+
+ SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY) {
+ proto = reinterpret_cast<MinecraftDynmapProto*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+
+ StoreDBText(proto, hwnd, IDC_SERVER, MINECRAFTDYNMAP_KEY_SERVER);
+ StoreDBText(proto, hwnd, IDC_NAME, MINECRAFTDYNMAP_KEY_NAME);
+
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
diff --git a/protocols/MinecraftDynmap/src/dialogs.h b/protocols/MinecraftDynmap/src/dialogs.h index 0af8617bae..f9d4586590 100644 --- a/protocols/MinecraftDynmap/src/dialogs.h +++ b/protocols/MinecraftDynmap/src/dialogs.h @@ -1,27 +1,27 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -void InitIcons(void); - -INT_PTR CALLBACK MinecraftDynmapAccountProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+void InitIcons(void);
+
+INT_PTR CALLBACK MinecraftDynmapAccountProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
diff --git a/protocols/MinecraftDynmap/src/main.cpp b/protocols/MinecraftDynmap/src/main.cpp index 5aa5c93460..a108fc6244 100644 --- a/protocols/MinecraftDynmap/src/main.cpp +++ b/protocols/MinecraftDynmap/src/main.cpp @@ -1,90 +1,90 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -CMPlugin g_plugin; - -std::string g_strUserAgent; - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {40DA5EBD-4F2D-4BEA-841C-EAB77BEE6F4F} - { 0x40da5ebd, 0x4f2d, 0x4bea, 0x84, 0x1c, 0xea, 0xb7, 0x7b, 0xee, 0x6f, 0x4f } -}; - -CMPlugin::CMPlugin() : - ACCPROTOPLUGIN<MinecraftDynmapProto>("MinecraftDynmap", pluginInfoEx) -{ - SetUniqueId("Nick"); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Interface information - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Load - -static HANDLE g_hEvents[1]; - -int CMPlugin::Load() -{ - InitIcons(); - - // Init native User-Agent - { - MFileVersion w; - Miranda_GetFileVersion(&w); - std::stringstream agent; - agent << "Miranda NG/" << w[0] << "." << w[1] << "." << w[2] << "." << w[3]; - #ifdef _WIN64 - agent << " Minecraft Dynmap Protocol x64/"; - #else - agent << " Minecraft Dynmap Protocol/"; - #endif - agent << __VERSION_STRING_DOTS; - g_strUserAgent = agent.str(); - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Unload - -int CMPlugin::Unload() -{ - for (size_t i=0; i < _countof(g_hEvents); i++) - UnhookEvent(g_hEvents[i]); - - return 0; -} - +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+CMPlugin g_plugin;
+
+std::string g_strUserAgent;
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {40DA5EBD-4F2D-4BEA-841C-EAB77BEE6F4F}
+ { 0x40da5ebd, 0x4f2d, 0x4bea, 0x84, 0x1c, 0xea, 0xb7, 0x7b, 0xee, 0x6f, 0x4f }
+};
+
+CMPlugin::CMPlugin() :
+ ACCPROTOPLUGIN<MinecraftDynmapProto>("MinecraftDynmap", pluginInfoEx)
+{
+ SetUniqueId("Nick");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Interface information
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load
+
+static HANDLE g_hEvents[1];
+
+int CMPlugin::Load()
+{
+ InitIcons();
+
+ // Init native User-Agent
+ {
+ MFileVersion w;
+ Miranda_GetFileVersion(&w);
+ std::stringstream agent;
+ agent << "Miranda NG/" << w[0] << "." << w[1] << "." << w[2] << "." << w[3];
+ #ifdef _WIN64
+ agent << " Minecraft Dynmap Protocol x64/";
+ #else
+ agent << " Minecraft Dynmap Protocol/";
+ #endif
+ agent << __VERSION_STRING_DOTS;
+ g_strUserAgent = agent.str();
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Unload
+
+int CMPlugin::Unload()
+{
+ for (size_t i=0; i < _countof(g_hEvents); i++)
+ UnhookEvent(g_hEvents[i]);
+
+ return 0;
+}
+
diff --git a/protocols/MinecraftDynmap/src/proto.cpp b/protocols/MinecraftDynmap/src/proto.cpp index e787b831da..47c05d5105 100644 --- a/protocols/MinecraftDynmap/src/proto.cpp +++ b/protocols/MinecraftDynmap/src/proto.cpp @@ -1,180 +1,180 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -MinecraftDynmapProto::MinecraftDynmapProto(const char* proto_name, const wchar_t* username) : - PROTO<MinecraftDynmapProto>(proto_name, username), m_interval(0), hConnection(nullptr), hEventsConnection(nullptr), - m_updateRate(5000), m_nick("") -{ - this->signon_lock_ = CreateMutex(nullptr, FALSE, nullptr); - this->send_message_lock_ = CreateMutex(nullptr, FALSE, nullptr); - this->connection_lock_ = CreateMutex(nullptr, FALSE, nullptr); - this->events_loop_lock_ = CreateMutex(nullptr, FALSE, nullptr); - this->events_loop_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr); - - // Group chats - CreateProtoService(PS_JOINCHAT, &MinecraftDynmapProto::OnJoinChat); - CreateProtoService(PS_LEAVECHAT, &MinecraftDynmapProto::OnLeaveChat); - - CreateProtoService(PS_CREATEACCMGRUI, &MinecraftDynmapProto::SvcCreateAccMgrUI); - - HookProtoEvent(ME_GC_EVENT, &MinecraftDynmapProto::OnChatEvent); - - // Create standard network connection - NETLIBUSER nlu = {}; - nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; - nlu.szSettingsModule = m_szModuleName; - nlu.szDescriptiveName.w = m_tszUserName; - m_hNetlibUser = Netlib_RegisterUser(&nlu); - if (m_hNetlibUser == nullptr) { - wchar_t error[200]; - mir_snwprintf(error, TranslateT("Unable to initialize Netlib for %s."), m_tszUserName); - MessageBox(nullptr, error, L"Miranda NG", MB_OK | MB_ICONERROR); - } - - // Register group chat - GCREGISTER gcr = {}; - gcr.pszModule = m_szModuleName; - gcr.ptszDispName = m_tszUserName; - gcr.iMaxText = MINECRAFTDYNMAP_MESSAGE_LIMIT; - Chat_Register(&gcr); - - // Client instantiation - this->error_count_ = 0; - this->chatHandle_ = nullptr; - this->szRoomName = mir_utf8encodeW(username); -} - -MinecraftDynmapProto::~MinecraftDynmapProto() -{ - Netlib_CloseHandle(m_hNetlibUser); - - WaitForSingleObject(this->signon_lock_, IGNORE); - WaitForSingleObject(this->send_message_lock_, IGNORE); - WaitForSingleObject(this->connection_lock_, IGNORE); - WaitForSingleObject(this->events_loop_lock_, IGNORE); - - CloseHandle(this->signon_lock_); - CloseHandle(this->send_message_lock_); - CloseHandle(this->connection_lock_); - CloseHandle(this->events_loop_lock_); - CloseHandle(this->events_loop_event_); -} - -////////////////////////////////////////////////////////////////////////////// - -INT_PTR MinecraftDynmapProto::GetCaps(int type, MCONTACT) -{ - switch(type) { - case PFLAGNUM_1: - return PF1_CHAT; - case PFLAGNUM_2: - return PF2_ONLINE; - case PFLAG_MAXLENOFMESSAGE: - return MINECRAFTDYNMAP_MESSAGE_LIMIT; - case PFLAG_UNIQUEIDTEXT: - return (INT_PTR) TranslateT("Visible name"); - } - return 0; -} - -////////////////////////////////////////////////////////////////////////////// - -int MinecraftDynmapProto::SetStatus(int new_status) -{ - // Routing not supported statuses - switch (new_status) { - case ID_STATUS_OFFLINE: - case ID_STATUS_CONNECTING: - new_status = ID_STATUS_OFFLINE; - break; - default: - new_status = ID_STATUS_ONLINE; - break; - } - - m_iDesiredStatus = new_status; - - if (new_status == m_iStatus) { - return 0; - } - - if (m_iStatus == ID_STATUS_CONNECTING && new_status != ID_STATUS_OFFLINE) { - return 0; - } - - if (new_status == ID_STATUS_OFFLINE) { - ForkThread(&MinecraftDynmapProto::SignOffWorker, this); - } else { - ForkThread(&MinecraftDynmapProto::SignOnWorker, this); - } - return 0; -} - -////////////////////////////////////////////////////////////////////////////// -// EVENTS - -INT_PTR MinecraftDynmapProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam) -{ - return (INT_PTR)CreateDialogParam(g_plugin.getInst(),MAKEINTRESOURCE(IDD_MinecraftDynmapACCOUNT), (HWND)lParam, MinecraftDynmapAccountProc, (LPARAM)this); -} - -void MinecraftDynmapProto::OnShutdown() -{ - SetStatus(ID_STATUS_OFFLINE); -} - -void MinecraftDynmapProto::OnContactDeleted(MCONTACT) -{ - OnLeaveChat(NULL, NULL); -} - -////////////////////////////////////////////////////////////////////////////// -// HELPERS - -bool MinecraftDynmapProto::handleEntry(const std::string &method) -{ - debugLogA(" >> Entering %s()", method.c_str()); - return true; -} - -bool MinecraftDynmapProto::handleSuccess(const std::string &method) -{ - debugLogA(" << Quitting %s()", method.c_str()); - reset_error(); - return true; -} - -bool MinecraftDynmapProto::handleError(const std::string &method, const std::string &error, bool force_disconnect) -{ - increment_error(); - debugLogA("!!!!! Quitting %s() with error: %s", method.c_str(), !error.empty() ? error.c_str() : "Something went wrong"); - - if (!force_disconnect && error_count_ <= (UINT)db_get_b(0, m_szModuleName, MINECRAFTDYNMAP_KEY_TIMEOUTS_LIMIT, MINECRAFTDYNMAP_TIMEOUTS_LIMIT)) { - return true; - } - - reset_error(); - SetStatus(ID_STATUS_OFFLINE); - return false; -} +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+MinecraftDynmapProto::MinecraftDynmapProto(const char* proto_name, const wchar_t* username) :
+ PROTO<MinecraftDynmapProto>(proto_name, username), m_interval(0), hConnection(nullptr), hEventsConnection(nullptr),
+ m_updateRate(5000), m_nick("")
+{
+ this->signon_lock_ = CreateMutex(nullptr, FALSE, nullptr);
+ this->send_message_lock_ = CreateMutex(nullptr, FALSE, nullptr);
+ this->connection_lock_ = CreateMutex(nullptr, FALSE, nullptr);
+ this->events_loop_lock_ = CreateMutex(nullptr, FALSE, nullptr);
+ this->events_loop_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+
+ // Group chats
+ CreateProtoService(PS_JOINCHAT, &MinecraftDynmapProto::OnJoinChat);
+ CreateProtoService(PS_LEAVECHAT, &MinecraftDynmapProto::OnLeaveChat);
+
+ CreateProtoService(PS_CREATEACCMGRUI, &MinecraftDynmapProto::SvcCreateAccMgrUI);
+
+ HookProtoEvent(ME_GC_EVENT, &MinecraftDynmapProto::OnChatEvent);
+
+ // Create standard network connection
+ NETLIBUSER nlu = {};
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE;
+ nlu.szSettingsModule = m_szModuleName;
+ nlu.szDescriptiveName.w = m_tszUserName;
+ m_hNetlibUser = Netlib_RegisterUser(&nlu);
+ if (m_hNetlibUser == nullptr) {
+ wchar_t error[200];
+ mir_snwprintf(error, TranslateT("Unable to initialize Netlib for %s."), m_tszUserName);
+ MessageBox(nullptr, error, L"Miranda NG", MB_OK | MB_ICONERROR);
+ }
+
+ // Register group chat
+ GCREGISTER gcr = {};
+ gcr.pszModule = m_szModuleName;
+ gcr.ptszDispName = m_tszUserName;
+ gcr.iMaxText = MINECRAFTDYNMAP_MESSAGE_LIMIT;
+ Chat_Register(&gcr);
+
+ // Client instantiation
+ this->error_count_ = 0;
+ this->chatHandle_ = nullptr;
+ this->szRoomName = mir_utf8encodeW(username);
+}
+
+MinecraftDynmapProto::~MinecraftDynmapProto()
+{
+ Netlib_CloseHandle(m_hNetlibUser);
+
+ WaitForSingleObject(this->signon_lock_, IGNORE);
+ WaitForSingleObject(this->send_message_lock_, IGNORE);
+ WaitForSingleObject(this->connection_lock_, IGNORE);
+ WaitForSingleObject(this->events_loop_lock_, IGNORE);
+
+ CloseHandle(this->signon_lock_);
+ CloseHandle(this->send_message_lock_);
+ CloseHandle(this->connection_lock_);
+ CloseHandle(this->events_loop_lock_);
+ CloseHandle(this->events_loop_event_);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+INT_PTR MinecraftDynmapProto::GetCaps(int type, MCONTACT)
+{
+ switch(type) {
+ case PFLAGNUM_1:
+ return PF1_CHAT;
+ case PFLAGNUM_2:
+ return PF2_ONLINE;
+ case PFLAG_MAXLENOFMESSAGE:
+ return MINECRAFTDYNMAP_MESSAGE_LIMIT;
+ case PFLAG_UNIQUEIDTEXT:
+ return (INT_PTR) TranslateT("Visible name");
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int MinecraftDynmapProto::SetStatus(int new_status)
+{
+ // Routing not supported statuses
+ switch (new_status) {
+ case ID_STATUS_OFFLINE:
+ case ID_STATUS_CONNECTING:
+ new_status = ID_STATUS_OFFLINE;
+ break;
+ default:
+ new_status = ID_STATUS_ONLINE;
+ break;
+ }
+
+ m_iDesiredStatus = new_status;
+
+ if (new_status == m_iStatus) {
+ return 0;
+ }
+
+ if (m_iStatus == ID_STATUS_CONNECTING && new_status != ID_STATUS_OFFLINE) {
+ return 0;
+ }
+
+ if (new_status == ID_STATUS_OFFLINE) {
+ ForkThread(&MinecraftDynmapProto::SignOffWorker, this);
+ } else {
+ ForkThread(&MinecraftDynmapProto::SignOnWorker, this);
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// EVENTS
+
+INT_PTR MinecraftDynmapProto::SvcCreateAccMgrUI(WPARAM, LPARAM lParam)
+{
+ return (INT_PTR)CreateDialogParam(g_plugin.getInst(),MAKEINTRESOURCE(IDD_MinecraftDynmapACCOUNT), (HWND)lParam, MinecraftDynmapAccountProc, (LPARAM)this);
+}
+
+void MinecraftDynmapProto::OnShutdown()
+{
+ SetStatus(ID_STATUS_OFFLINE);
+}
+
+void MinecraftDynmapProto::OnContactDeleted(MCONTACT)
+{
+ OnLeaveChat(NULL, NULL);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// HELPERS
+
+bool MinecraftDynmapProto::handleEntry(const std::string &method)
+{
+ debugLogA(" >> Entering %s()", method.c_str());
+ return true;
+}
+
+bool MinecraftDynmapProto::handleSuccess(const std::string &method)
+{
+ debugLogA(" << Quitting %s()", method.c_str());
+ reset_error();
+ return true;
+}
+
+bool MinecraftDynmapProto::handleError(const std::string &method, const std::string &error, bool force_disconnect)
+{
+ increment_error();
+ debugLogA("!!!!! Quitting %s() with error: %s", method.c_str(), !error.empty() ? error.c_str() : "Something went wrong");
+
+ if (!force_disconnect && error_count_ <= (UINT)db_get_b(0, m_szModuleName, MINECRAFTDYNMAP_KEY_TIMEOUTS_LIMIT, MINECRAFTDYNMAP_TIMEOUTS_LIMIT)) {
+ return true;
+ }
+
+ reset_error();
+ SetStatus(ID_STATUS_OFFLINE);
+ return false;
+}
diff --git a/protocols/MinecraftDynmap/src/proto.h b/protocols/MinecraftDynmap/src/proto.h index b863b2547c..852edaef74 100644 --- a/protocols/MinecraftDynmap/src/proto.h +++ b/protocols/MinecraftDynmap/src/proto.h @@ -1,136 +1,136 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -class MinecraftDynmapProto : public PROTO<MinecraftDynmapProto> -{ - ptrA szRoomName; - -public: - MinecraftDynmapProto(const char *proto_name, const wchar_t *username); - ~MinecraftDynmapProto(); - - inline const char* ModuleName() const { - return m_szModuleName; - } - - inline bool isOnline() { - return (m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING); - } - - inline bool isOffline() { - return (m_iStatus == ID_STATUS_OFFLINE); - } - - // PROTO_INTERFACE - INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override; - int SetStatus(int iNewStatus) override; - - void OnContactDeleted(MCONTACT) override; - void OnShutdown() override; - - // Services - INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); - - // Chat handling - int __cdecl OnChatEvent(WPARAM,LPARAM); - INT_PTR __cdecl OnJoinChat(WPARAM,LPARAM); - INT_PTR __cdecl OnLeaveChat(WPARAM,LPARAM); - - // Loops - void __cdecl EventsLoop(void*); - - // Worker threads - void __cdecl SignOnWorker(void*); - void __cdecl SignOffWorker(void*); - void __cdecl SendMsgWorker(void*); - - // Chat handling - void UpdateChat(const char *name, const char *message, const time_t timestamp = time(0), bool addtochat = true); - void AddChatContact(const char *nick); - void DeleteChatContact(const char *name); - void SetChatStatus(int); - void ClearChat(); - void SetTopic(const char *topic); - MCONTACT GetChatHandle(); - - // Locks - HANDLE signon_lock_; - HANDLE connection_lock_; - HANDLE send_message_lock_; - HANDLE events_loop_lock_; - - HANDLE events_loop_event_; - - // Handles - HNETLIBCONN hConnection; - HNETLIBCONN hEventsConnection; - HANDLE chatHandle_; - - // Data storage - void store_headers(http::response *resp, NETLIBHTTPHEADER *headers, int headers_count); - - std::string get_server(bool not_last = false); - std::string get_language(); - - // Connection handling - unsigned int error_count_; - - // Helpers - bool handleEntry(const std::string &method); - bool handleSuccess(const std::string &method); - bool handleError(const std::string &method, const std::string &error = "", bool force_disconnect = false); - - void __inline increment_error() { error_count_++; } - void __inline decrement_error() { if (error_count_ > 0) error_count_--; } - void __inline reset_error() { error_count_ = 0; } - - // HTTP communication - http::response sendRequest(const int request_type, std::string *post_data = nullptr, std::string *get_data = nullptr); - std::string chooseAction(int, std::string *get_data = nullptr); - NETLIBHTTPHEADER *get_request_headers(int request_type, int *headers_count); - - // Requests and processing - bool doSignOn(); - bool doEvents(); - bool doSendMessage(const std::string &message_text); - - std::string doGetPage(int); - - // Configuration - std::string m_nick; - std::string m_cookie; - std::string m_server; - std::string m_title; - std::string m_timestamp; - int m_interval; - int m_updateRate; -}; - -struct CMPlugin : public ACCPROTOPLUGIN<MinecraftDynmapProto> -{ - CMPlugin(); - - int Load() override; - int Unload() override; -}; +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+class MinecraftDynmapProto : public PROTO<MinecraftDynmapProto>
+{
+ ptrA szRoomName;
+
+public:
+ MinecraftDynmapProto(const char *proto_name, const wchar_t *username);
+ ~MinecraftDynmapProto();
+
+ inline const char* ModuleName() const {
+ return m_szModuleName;
+ }
+
+ inline bool isOnline() {
+ return (m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING);
+ }
+
+ inline bool isOffline() {
+ return (m_iStatus == ID_STATUS_OFFLINE);
+ }
+
+ // PROTO_INTERFACE
+ INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override;
+ int SetStatus(int iNewStatus) override;
+
+ void OnContactDeleted(MCONTACT) override;
+ void OnShutdown() override;
+
+ // Services
+ INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM);
+
+ // Chat handling
+ int __cdecl OnChatEvent(WPARAM,LPARAM);
+ INT_PTR __cdecl OnJoinChat(WPARAM,LPARAM);
+ INT_PTR __cdecl OnLeaveChat(WPARAM,LPARAM);
+
+ // Loops
+ void __cdecl EventsLoop(void*);
+
+ // Worker threads
+ void __cdecl SignOnWorker(void*);
+ void __cdecl SignOffWorker(void*);
+ void __cdecl SendMsgWorker(void*);
+
+ // Chat handling
+ void UpdateChat(const char *name, const char *message, const time_t timestamp = time(0), bool addtochat = true);
+ void AddChatContact(const char *nick);
+ void DeleteChatContact(const char *name);
+ void SetChatStatus(int);
+ void ClearChat();
+ void SetTopic(const char *topic);
+ MCONTACT GetChatHandle();
+
+ // Locks
+ HANDLE signon_lock_;
+ HANDLE connection_lock_;
+ HANDLE send_message_lock_;
+ HANDLE events_loop_lock_;
+
+ HANDLE events_loop_event_;
+
+ // Handles
+ HNETLIBCONN hConnection;
+ HNETLIBCONN hEventsConnection;
+ HANDLE chatHandle_;
+
+ // Data storage
+ void store_headers(http::response *resp, NETLIBHTTPHEADER *headers, int headers_count);
+
+ std::string get_server(bool not_last = false);
+ std::string get_language();
+
+ // Connection handling
+ unsigned int error_count_;
+
+ // Helpers
+ bool handleEntry(const std::string &method);
+ bool handleSuccess(const std::string &method);
+ bool handleError(const std::string &method, const std::string &error = "", bool force_disconnect = false);
+
+ void __inline increment_error() { error_count_++; }
+ void __inline decrement_error() { if (error_count_ > 0) error_count_--; }
+ void __inline reset_error() { error_count_ = 0; }
+
+ // HTTP communication
+ http::response sendRequest(const int request_type, std::string *post_data = nullptr, std::string *get_data = nullptr);
+ std::string chooseAction(int, std::string *get_data = nullptr);
+ NETLIBHTTPHEADER *get_request_headers(int request_type, int *headers_count);
+
+ // Requests and processing
+ bool doSignOn();
+ bool doEvents();
+ bool doSendMessage(const std::string &message_text);
+
+ std::string doGetPage(int);
+
+ // Configuration
+ std::string m_nick;
+ std::string m_cookie;
+ std::string m_server;
+ std::string m_title;
+ std::string m_timestamp;
+ int m_interval;
+ int m_updateRate;
+};
+
+struct CMPlugin : public ACCPROTOPLUGIN<MinecraftDynmapProto>
+{
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
diff --git a/protocols/MinecraftDynmap/src/stdafx.cxx b/protocols/MinecraftDynmap/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/MinecraftDynmap/src/stdafx.cxx +++ b/protocols/MinecraftDynmap/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/MinecraftDynmap/src/stdafx.h b/protocols/MinecraftDynmap/src/stdafx.h index 929267ae60..f5cc740bca 100644 --- a/protocols/MinecraftDynmap/src/stdafx.h +++ b/protocols/MinecraftDynmap/src/stdafx.h @@ -1,70 +1,70 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -#pragma warning(disable:4996) - -#include <string> -#include <cstring> -#include <sstream> -#include <fstream> -#include <map> -#include <stdarg.h> -#include <time.h> -#include <assert.h> -#include <io.h> - -#include <windows.h> -#include <commctrl.h> - -#include <newpluginapi.h> -#include <m_system.h> -#include <m_chat_int.h> -#include <m_clistint.h> -#include <m_langpack.h> -#include <m_netlib.h> -#include <m_options.h> -#include <m_popup.h> -#include <m_protocols.h> -#include <m_protosvc.h> -#include <m_protoint.h> -#include <m_skin.h> -#include <statusmodes.h> -#include <m_icolib.h> -#include <m_utils.h> -#include <m_hotkeys.h> -#include <m_message.h> -#include <m_json.h> -#include <m_http.h> - -#include "version.h" - -class MinecraftDynmapProto; - -#include "utils.h" -#include "proto.h" -#include "constants.h" -#include "dialogs.h" -#include "resource.h" - -extern std::string g_strUserAgent; +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#pragma warning(disable:4996)
+
+#include <string>
+#include <cstring>
+#include <sstream>
+#include <fstream>
+#include <map>
+#include <stdarg.h>
+#include <time.h>
+#include <assert.h>
+#include <io.h>
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_chat_int.h>
+#include <m_clistint.h>
+#include <m_langpack.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_skin.h>
+#include <statusmodes.h>
+#include <m_icolib.h>
+#include <m_utils.h>
+#include <m_hotkeys.h>
+#include <m_message.h>
+#include <m_json.h>
+#include <m_http.h>
+
+#include "version.h"
+
+class MinecraftDynmapProto;
+
+#include "utils.h"
+#include "proto.h"
+#include "constants.h"
+#include "dialogs.h"
+#include "resource.h"
+
+extern std::string g_strUserAgent;
diff --git a/protocols/MinecraftDynmap/src/utils.h b/protocols/MinecraftDynmap/src/utils.h index e4f3d3ff37..c42fb86d9e 100644 --- a/protocols/MinecraftDynmap/src/utils.h +++ b/protocols/MinecraftDynmap/src/utils.h @@ -1,63 +1,63 @@ -/* - -Minecraft Dynmap plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2015-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#pragma once - -// DB macros -#define getU8String(setting, dest) db_get_utf(NULL, m_szModuleName, setting, dest) -#define setU8String(setting, value) db_set_utf(NULL, m_szModuleName, setting, value) - -// HTTP constants and object -#define HTTP_CODE_FAKE_DISCONNECTED 0 -#define HTTP_CODE_FAKE_ERROR 1 - -namespace http -{ - struct response - { - response() : code(0) {} - int code; - std::map< std::string, std::string > headers; - std::string data; - }; -} - -class ScopedLock -{ -public: - ScopedLock(HANDLE h) : handle_(h) - { - WaitForSingleObject(handle_,INFINITE); - } - ~ScopedLock() - { - if (handle_) - ReleaseMutex(handle_); - } - void Unlock() - { - ReleaseMutex(handle_); - handle_ = nullptr; - } -private: - HANDLE handle_; -}; +/*
+
+Minecraft Dynmap plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2015-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+// DB macros
+#define getU8String(setting, dest) db_get_utf(NULL, m_szModuleName, setting, dest)
+#define setU8String(setting, value) db_set_utf(NULL, m_szModuleName, setting, value)
+
+// HTTP constants and object
+#define HTTP_CODE_FAKE_DISCONNECTED 0
+#define HTTP_CODE_FAKE_ERROR 1
+
+namespace http
+{
+ struct response
+ {
+ response() : code(0) {}
+ int code;
+ std::map< std::string, std::string > headers;
+ std::string data;
+ };
+}
+
+class ScopedLock
+{
+public:
+ ScopedLock(HANDLE h) : handle_(h)
+ {
+ WaitForSingleObject(handle_,INFINITE);
+ }
+ ~ScopedLock()
+ {
+ if (handle_)
+ ReleaseMutex(handle_);
+ }
+ void Unlock()
+ {
+ ReleaseMutex(handle_);
+ handle_ = nullptr;
+ }
+private:
+ HANDLE handle_;
+};
diff --git a/protocols/MinecraftDynmap/src/version.h b/protocols/MinecraftDynmap/src/version.h index fa021b8cae..1e71a749da 100644 --- a/protocols/MinecraftDynmap/src/version.h +++ b/protocols/MinecraftDynmap/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Minecraft Dynmap support for Miranda NG."
#define __AUTHOR "Robert Pösel"
#define __AUTHORWEB "https://miranda-ng.org/p/MinecraftDynmap"
-#define __COPYRIGHT "© 2015-17 Robert Pösel, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-17 Robert Pösel, 2017-23 Miranda NG team"
diff --git a/protocols/NewsAggregator/Src/stdafx.cxx b/protocols/NewsAggregator/Src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/NewsAggregator/Src/stdafx.cxx +++ b/protocols/NewsAggregator/Src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/NewsAggregator/Src/version.h b/protocols/NewsAggregator/Src/version.h index 8258430e90..e9d19559e7 100644 --- a/protocols/NewsAggregator/Src/version.h +++ b/protocols/NewsAggregator/Src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "RSS/Atom news aggregator."
#define __AUTHOR "Mataes, FREAK_THEMIGHTY"
#define __AUTHORWEB "https://miranda-ng.org/p/NewsAggregator"
-#define __COPYRIGHT "© 2012-22 Mataes, FREAK_THEMIGHTY"
+#define __COPYRIGHT "© 2012-23 Mataes, FREAK_THEMIGHTY"
diff --git a/protocols/Non-IM Contact/src/stdafx.cxx b/protocols/Non-IM Contact/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/Non-IM Contact/src/stdafx.cxx +++ b/protocols/Non-IM Contact/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/Omegle/src/chat.cpp b/protocols/Omegle/src/chat.cpp index 1e732b8004..484d7b0e34 100644 --- a/protocols/Omegle/src/chat.cpp +++ b/protocols/Omegle/src/chat.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/client.h b/protocols/Omegle/src/client.h index ae3da3ca3a..1a34b20629 100644 --- a/protocols/Omegle/src/client.h +++ b/protocols/Omegle/src/client.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/communication.cpp b/protocols/Omegle/src/communication.cpp index f033cd93a6..dfd1dc4dd4 100644 --- a/protocols/Omegle/src/communication.cpp +++ b/protocols/Omegle/src/communication.cpp @@ -1,762 +1,762 @@ -/* - -Omegle plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "stdafx.h" - -http::response Omegle_client::flap(const int request_type, std::string *post_data, std::string *get_data) -{ - http::response resp; - - // Prepare the request - NETLIBHTTPREQUEST nlhr = { sizeof(NETLIBHTTPREQUEST) }; - - // Set request URL - std::string url = choose_server(request_type) + choose_action(request_type, get_data); - nlhr.szUrl = (char*)url.c_str(); - - // Set timeout (bigger for channel request) - nlhr.timeout = 1000 * ((request_type == OMEGLE_REQUEST_EVENTS) ? 65 : 20); - - // Set request type (GET/POST) and eventually also POST data - if (post_data != nullptr) { - nlhr.requestType = REQUEST_POST; - nlhr.pData = (char*)(*post_data).c_str(); - nlhr.dataLength = (int)post_data->length(); - } - else { - nlhr.requestType = REQUEST_GET; - } - - // Set headers - it depends on requestType so it must be after setting that - nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount); - - // Set flags - nlhr.flags = NLHRF_HTTP11; - -#ifdef _DEBUG - nlhr.flags |= NLHRF_DUMPASTEXT; -#else - nlhr.flags |= NLHRF_NODUMP; -#endif - - // Set persistent connection (or not) - switch (request_type) - { - case OMEGLE_REQUEST_HOME: - nlhr.nlc = nullptr; - break; - - case OMEGLE_REQUEST_EVENTS: - nlhr.nlc = hEventsConnection; - nlhr.flags |= NLHRF_PERSISTENT; - break; - - default: - WaitForSingleObject(connection_lock_, INFINITE); - nlhr.nlc = hConnection; - nlhr.flags |= NLHRF_PERSISTENT; - break; - } - - parent->debugLogA("@@@@@ Sending request to '%s'", nlhr.szUrl); - - // Send the request - NLHR_PTR pnlhr(Netlib_HttpTransaction(handle_, &nlhr)); - - mir_free(nlhr.headers); - - // Remember the persistent connection handle (or not) - switch (request_type) - { - case OMEGLE_REQUEST_HOME: - break; - - case OMEGLE_REQUEST_EVENTS: - hEventsConnection = pnlhr ? pnlhr->nlc : nullptr; - break; - - default: - ReleaseMutex(connection_lock_); - hConnection = pnlhr ? pnlhr->nlc : nullptr; - break; - } - - // Check and copy response data - if (pnlhr != nullptr) - { - parent->debugLogA("@@@@@ Got response with code %d", pnlhr->resultCode); - store_headers(&resp, pnlhr->headers, pnlhr->headersCount); - resp.code = pnlhr->resultCode; - resp.data = pnlhr->pData ? pnlhr->pData : ""; - - // This error code gives us some error page, but we don't need that, so clear the response - if (pnlhr->resultCode == HTTP_CODE_WEB_SERVER_IS_DOWN) { - resp.data = ""; - } - - parent->debugLogA("&&&&& Got response: %s", resp.data.c_str()); - } - else { - parent->debugLogA("!!!!! No response from server (time-out)"); - resp.code = HTTP_CODE_FAKE_DISCONNECTED; - // Better to have something set explicitely as this value is compaired in all communication requests - } - - return resp; -} - -bool Omegle_client::handle_entry(const std::string &method) -{ - parent->debugLogA(" >> Entering %s()", method.c_str()); - return true; -} - -bool Omegle_client::handle_success(const std::string &method) -{ - parent->debugLogA(" << Quitting %s()", method.c_str()); - reset_error(); - return true; -} - -bool Omegle_client::handle_error(const std::string &method, bool force_disconnect) -{ - bool result; - increment_error(); - parent->debugLogA("!!!!! %s(): Something with Omegle went wrong", method.c_str()); - - if (force_disconnect) - result = false; - else if (error_count_ <= (UINT)db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_TIMEOUTS_LIMIT, OMEGLE_TIMEOUTS_LIMIT)) - result = true; - else - result = false; - - if (result == false) - { - reset_error(); - parent->UpdateChat(nullptr, TranslateT("Connection error.")); - parent->StopChat(false); - } - - return result; -} - -////////////////////////////////////////////////////////////////////////////// - -std::string Omegle_client::get_server(bool not_last) -{ - int q = not_last ? 1 : 0; - - int server = db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_SERVER, 0); - if (server < 0 || server >= (int)(_countof(servers) - q)) - server = 0; - - if (server == 0) { - srand(::time(0)); - server = (rand() % (_countof(servers) - 1 - q)) + 1; - } - - return servers[server]; -} - -std::string Omegle_client::get_language() -{ - int language = db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_LANGUAGE, 0); - if (language < 0 || language >= (_countof(languages))) - language = 0; - - return language > 0 ? languages[language].id : "en"; -} - -std::string Omegle_client::choose_server(int request_type) -{ - switch (request_type) - { - case OMEGLE_REQUEST_HOME: - return OMEGLE_SERVER_REGULAR; - - /* case OMEGLE_REQUEST_START: - case OMEGLE_REQUEST_STOP: - case OMEGLE_REQUEST_SEND: - case OMEGLE_REQUEST_EVENTS: - case OMEGLE_REQUEST_TYPING_START: - case OMEGLE_REQUEST_TYPING_STOP: - case OMEGLE_REQUEST_RECAPTCHA: - case OMEGLE_REQUEST_COUNT: - */ default: - std::string server = OMEGLE_SERVER_CHAT; - utils::text::replace_first(&server, "%s", this->server_); - return server; - } -} - -std::string Omegle_client::choose_action(int request_type, std::string* get_data) -{ - switch (request_type) - { - case OMEGLE_REQUEST_START: - { - std::string action = "/start?caps=recaptcha2,t&rcs=1&spid=&lang="; - action += get_language(); - if (get_data != nullptr) - action += (*get_data); - - return action; - } - - case OMEGLE_REQUEST_STOP: - return "/disconnect"; - - case OMEGLE_REQUEST_SEND: - return "/send"; - - case OMEGLE_REQUEST_EVENTS: - return "/events"; - - case OMEGLE_REQUEST_TYPING_START: - return "/typing"; - - case OMEGLE_REQUEST_TYPING_STOP: - return "/stoppedtyping"; - - case OMEGLE_REQUEST_RECAPTCHA: - return "/recaptcha"; - - case OMEGLE_REQUEST_COUNT: - return "/count"; - - // "/stoplookingforcommonlikes" - - /* case OMEGLE_REQUEST_HOME: - */ default: - return "/"; - } -} - - -NETLIBHTTPHEADER* Omegle_client::get_request_headers(int request_type, int* headers_count) -{ - if (request_type == REQUEST_POST) - *headers_count = 4; - else - *headers_count = 3; - - NETLIBHTTPHEADER *headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER)*(*headers_count)); - - if (request_type == REQUEST_POST) { - headers[3].szName = "Content-Type"; - headers[3].szValue = "application/x-www-form-urlencoded; charset=utf-8"; - } - - headers[2].szName = "User-Agent"; - headers[2].szValue = Netlib_GetUserAgent(); - headers[1].szName = "Accept"; - headers[1].szValue = "*/*"; - headers[0].szName = "Accept-Language"; - headers[0].szValue = "en,en-US;q=0.9"; - - return headers; -} - -void Omegle_client::store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headersCount) -{ - for (size_t i = 0; i < (size_t)headersCount; i++) - { - std::string header_name = headers[i].szName; - std::string header_value = headers[i].szValue; - - // TODO RM: (un)comment - //parent->debugLogA("----- Got header '%s': %s", header_name.c_str(), header_value.c_str()); - resp->headers[header_name] = header_value; - } -} - -////////////////////////////////////////////////////////////////////////////// - -bool Omegle_client::start() -{ - HANDLE_ENTRY; - - this->server_ = get_server(); - //parent->debugLogA("Chosing server %s", this->server_.c_str()); - //std::string log = Translate("Chosing server: ") + this->server_; - //parent->UpdateChat(NULL, log.c_str()); - - std::string data; - - if (this->spy_mode_) { - //// get last server from list, which is for spy mode - //this->server_ = servers[_countof(servers)-1]; - - if (this->question_.empty()) { - data = "&wantsspy=1"; - } - else { - data = "&ask=" + utils::url::encode(this->question_); - data += "&cansavequestion="; - data += db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_REUSE_QUESTION, 0) ? "1" : "0"; - } - } - else if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_MEET_COMMON, 0)) - { - DBVARIANT dbv; - if (!db_get_utf(NULL, parent->m_szModuleName, OMEGLE_KEY_INTERESTS, &dbv)) - { - std::string topics = dbv.pszVal; - std::string topic; - - db_free(&dbv); - - std::string::size_type pos = 0; - std::string::size_type pos2 = 0; - while ((pos2 = topics.find(",", pos)) != std::string::npos) { - topic = topics.substr(pos, pos2 - pos); - topic = utils::text::trim(topic); - - if (!topic.empty()) { - if (pos > 0) - data += ","; - - data += "\"" + topic + "\""; - } - - pos = pos2 + 1; - } - - topic = topics.substr(pos); - topic = utils::text::trim(topic); - if (!topic.empty()) { - if (pos > 0) - data += ","; - data += "\"" + topic + "\""; - } - - parent->debugLogA("TOPICS: %s", data.c_str()); - - if (!data.empty()) { - data = "[" + data + "]"; - data = "&topics=" + utils::url::encode(data); - } - - //// get any server but last, which is for spy mode - //this->server_ = get_server(true); - } - } - - if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_SERVER_INFO, 0)) - { - std::string count = get_page(OMEGLE_REQUEST_COUNT); - if (!count.empty()) { - char str[255]; - mir_snprintf(str, Translate("Connected to server %s. There are %s users online now."), server_.c_str(), count.c_str()); - - wchar_t *msg = mir_a2u(str); - parent->UpdateChat(nullptr, msg); - mir_free(msg); - } - } - else { - char str[255]; - mir_snprintf(str, Translate("Connected to server %s."), server_.c_str()); - - wchar_t *msg = mir_a2u(str); - parent->UpdateChat(nullptr, msg); - mir_free(msg); - } - - // Send validation - http::response resp = flap(OMEGLE_REQUEST_START, nullptr, &data); - - switch (resp.code) - { - case HTTP_CODE_FAKE_DISCONNECTED: - { - // If is is only timeout error, try login once more - if (HANDLE_ERROR(false)) - return start(); - else - return false; - } - - case HTTP_CODE_OK: - { - if (!resp.data.empty()) { - this->chat_id_ = resp.data.substr(1, resp.data.length() - 2); - this->state_ = STATE_WAITING; - - return HANDLE_SUCCESS; - } - else { - return HANDLE_ERROR(FORCE_DISCONNECT); - } - } - - default: - return HANDLE_ERROR(FORCE_DISCONNECT); - } -} - -bool Omegle_client::stop() -{ - if (parent->isOffline()) - return true; - - HANDLE_ENTRY; - - std::string data = "id=" + this->chat_id_; - - http::response resp = flap(OMEGLE_REQUEST_STOP, &data); - - Netlib_Shutdown(hConnection); - Netlib_Shutdown(hEventsConnection); - - return (resp.data == "win") ? HANDLE_SUCCESS : HANDLE_ERROR(false); -} - -bool Omegle_client::events() -{ - HANDLE_ENTRY; - - std::string post_data = "id=" + this->chat_id_; - - // Get update - http::response resp = flap(OMEGLE_REQUEST_EVENTS, &post_data); - - // Return - switch (resp.code) - { - case HTTP_CODE_OK: - { - if (resp.data == "null") { - // Everything is OK, no new message received -- OR it is a problem - // TODO: if we are waiting for Stranger with common likes, then we should try standard Stranger if this takes too long - return HANDLE_ERROR(false); - } - else if (resp.data == "fail") { - // Something went wrong - return HANDLE_ERROR(false); - } - - JSONROOT root(resp.data.c_str()); - if (root == nullptr) - return HANDLE_ERROR(false); - - bool newStranger = false; - bool waiting = false; - - for (size_t i = 0; i < json_size(root); i++) { - JSONNode *item = json_at(root, i); - if (item == nullptr) - continue; - - std::string name = _T2A(json_as_string(json_at(item, 0))); - - if (name == "waiting") { - // We are just waiting for new Stranger - waiting = true; - } - else if (name == "identDigests") { - // We get some comma separated hashes, I'm not sure what for - } - else if (name == "statusInfo") { - JSONNode *data = json_at(item, 1); - - // We got some object as second parameter - //data["antinudepercent"]; // probably 1 by default - //data["antinudeservers"]; // array of server names, like "waw3.omegle.com" - //data["rtmfp"]; // some rtmfp protocol address - //data["servers"]; // array of server names, like "front5.omegle.com" - //data["spyeeQueueTime"]; // some float number, e.g. 0.0701999903 - //data["spyQueueTime"]; // some float number, e.g. 4.7505000114 - //data["timestamp"]; // e.g. 1445336566.0196209 - - // We got info about count of connected people there - ptrW count(json_as_string(json_get(data, "count"))); - wchar_t strT[255]; - mir_snwprintf(strT, TranslateT("On whole Omegle are %s strangers online now."), count.get()); - - parent->UpdateChat(nullptr, strT); - } - else if (name == "serverMessage") { - ptrW message(json_as_string(json_at(item, 1))); - parent->UpdateChat(nullptr, TranslateW(message)); - } - else if (name == "connected") { - // Stranger connected - if (this->spy_mode_ && !this->question_.empty()) { - parent->AddChatContact(TranslateT("Stranger 1")); - parent->AddChatContact(TranslateT("Stranger 2")); - this->state_ = STATE_SPY; - } - else { - parent->AddChatContact(TranslateT("Stranger")); - this->state_ = STATE_ACTIVE; - } - - newStranger = true; - waiting = false; - } - else if (name == "commonLikes") { - std::wstring likes = TranslateT("You and the Stranger both like: "); - - JSONNode *items = json_at(item, 1); - size_t size = json_size(items); - for (size_t j = 0; j < size; j++) { - likes += ptrW(json_as_string(json_at(items, j))); - if (j < size - 1) - likes += L", "; - } - - parent->debugLogW(L"Got common likes: '%s'", likes.c_str()); - parent->SetTopic(likes.c_str()); - } - else if (name == "question") { - ptrW question(json_as_string(json_at(item, 1))); - parent->SetTopic(question); - } - else if (name == "typing" || name == "spyTyping") { - // Stranger is typing, not supported by chat module yet - Skin_PlaySound("StrangerTyp"); - - ptrW who(name == "spyTyping" ? json_as_string(json_at(item, 1)) : mir_wstrdup(L"Stranger")); - Srmm_SetStatusText(parent->GetChatHandle(), CMStringW(FORMAT, TranslateT("%s is typing."), TranslateW(who)), g_plugin.getIcon(IDI_TYPING_ON)); - } - else if (name == "stoppedTyping" || name == "spyStoppedTyping") { - // Stranger stopped typing, not supported by chat module yet - Skin_PlaySound("StrangerTypStop"); - - ptrW who(name == "spyTyping" ? json_as_string(json_at(item, 1)) : mir_wstrdup(L"Stranger")); - Srmm_SetStatusText(parent->GetChatHandle(), CMStringW(FORMAT, TranslateT("%s stopped typing."), TranslateW(who)), g_plugin.getIcon(IDI_TYPING_OFF)); - } - else if (name == "gotMessage") { - Srmm_SetStatusText(parent->GetChatHandle(), nullptr); - - // Play sound as we received message - Skin_PlaySound("StrangerMessage"); - - if (state_ == STATE_ACTIVE) { - ptrW msg(json_as_string(json_at(item, 1))); - parent->UpdateChat(TranslateT("Stranger"), msg); - } - } - else if (name == "spyMessage") { - Srmm_SetStatusText(parent->GetChatHandle(), nullptr); - - // Play sound as we received message - Skin_PlaySound("StrangerMessage"); - - if (state_ == STATE_SPY) { - ptrW stranger(json_as_string(json_at(item, 1))); - ptrW msg(json_as_string(json_at(item, 2))); - parent->UpdateChat(stranger, msg); - } - } - else if (name == "strangerDisconnected") { - Srmm_SetStatusText(parent->GetChatHandle(), nullptr); - - // Stranger disconnected - if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_DONT_STOP, 0)) - { - Skin_PlaySound("StrangerChange"); - parent->NewChat(); - } - else - parent->StopChat(false); - } - else if (name == "spyDisconnected") { - Srmm_SetStatusText(parent->GetChatHandle(), nullptr); - - ptrW stranger(json_as_string(json_at(item, 1))); - - wchar_t strT[255]; - mir_snwprintf(strT, TranslateT("%s disconnected."), TranslateW(stranger)); - parent->UpdateChat(nullptr, strT); - - // Stranger disconnected - if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_DONT_STOP, 0)) - { - Skin_PlaySound("StrangerChange"); - parent->NewChat(); - } - else - parent->StopChat(false); - } - else if (name == "recaptchaRequired") { - // Nothing to do with recaptcha - parent->UpdateChat(nullptr, TranslateT("Recaptcha is required.\nOpen http://omegle.com , solve Recaptcha and try again.")); - parent->StopChat(false); - } - else if (name == "recaptchaRejected") { - // Nothing to do with recaptcha - parent->StopChat(false); - } - else if (name == "error") { - ptrW error(json_as_string(json_at(item, 1))); - - wchar_t strT[255]; - mir_snwprintf(strT, TranslateT("Error: %s"), TranslateW(error)); - parent->UpdateChat(nullptr, strT); - } - } - - if (newStranger && !spy_mode_) { - // We got new stranger in this event, lets say him "Hi message" if enabled - if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_HI_ENABLED, 0)) { - DBVARIANT dbv; - if (!db_get_utf(NULL, parent->m_szModuleName, OMEGLE_KEY_HI, &dbv)) { - std::vector<std::string> messages; - utils::text::explode(std::string(dbv.pszVal), "\r\n", &messages); - db_free(&dbv); - - int pos = rand() % messages.size(); - std::string *message = new std::string(messages.at(pos)); - - parent->debugLogA("**Chat - saying Hi! message"); - parent->ForkThread(&OmegleProto::SendMsgWorker, message); - } - else parent->debugLogA("**Chat - Hi message is enabled but not used"); - } - } - - if (waiting) { - // If we are only waiting in this event... - parent->UpdateChat(nullptr, TranslateT("We are still waiting...")); - } - - return HANDLE_SUCCESS; - } - - case HTTP_CODE_FAKE_DISCONNECTED: - // timeout - return HANDLE_SUCCESS; - - default: - return HANDLE_ERROR(false); - } -} - -bool Omegle_client::send_message(const std::string &message_text) -{ - HANDLE_ENTRY; - - std::string data = "msg=" + utils::url::encode(message_text); - data += "&id=" + this->chat_id_; - - http::response resp = flap(OMEGLE_REQUEST_SEND, &data); - - switch (resp.code) - { - case HTTP_CODE_OK: - if (resp.data == "win") { - return HANDLE_SUCCESS; - } - - case HTTP_CODE_FAKE_DISCONNECTED: - default: - return HANDLE_ERROR(false); - } -} - -bool Omegle_client::typing_start() -{ - HANDLE_ENTRY; - - std::string data = "id=" + this->chat_id_; - - http::response resp = flap(OMEGLE_REQUEST_TYPING_START, &data); - - switch (resp.code) - { - case HTTP_CODE_OK: - if (resp.data == "win") { - return HANDLE_SUCCESS; - } - - case HTTP_CODE_FAKE_DISCONNECTED: - default: - return HANDLE_ERROR(false); - } -} - -bool Omegle_client::typing_stop() -{ - HANDLE_ENTRY; - - std::string data = "id=" + this->chat_id_; - - http::response resp = flap(OMEGLE_REQUEST_TYPING_STOP, &data); - - switch (resp.code) - { - case HTTP_CODE_OK: - if (resp.data == "win") { - return HANDLE_SUCCESS; - } - - case HTTP_CODE_FAKE_DISCONNECTED: - default: - return HANDLE_ERROR(false); - } -} - -bool Omegle_client::recaptcha() -{ - // TODO: Implement! - - HANDLE_ENTRY; - - // data:{id:this.clientID,challenge:b,response:a}} - //std::string data = "?id=...&challenge= ..., &response= ..."; - - http::response resp = flap(OMEGLE_REQUEST_RECAPTCHA); - - switch (resp.code) - { - case HTTP_CODE_OK: - /* if (resp.data == "win") { - return handle_success( "typing_start" ); - }*/ - - case HTTP_CODE_FAKE_DISCONNECTED: - default: - return HANDLE_ERROR(false); - } -} - -std::string Omegle_client::get_page(const int request_type) -{ - HANDLE_ENTRY; - - http::response resp = flap(request_type); - - switch (resp.code) - { - case HTTP_CODE_OK: - HANDLE_SUCCESS; - break; - - case HTTP_CODE_FAKE_DISCONNECTED: - default: - HANDLE_ERROR(false); - } - - return resp.data; -} +/*
+
+Omegle plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+http::response Omegle_client::flap(const int request_type, std::string *post_data, std::string *get_data)
+{
+ http::response resp;
+
+ // Prepare the request
+ NETLIBHTTPREQUEST nlhr = { sizeof(NETLIBHTTPREQUEST) };
+
+ // Set request URL
+ std::string url = choose_server(request_type) + choose_action(request_type, get_data);
+ nlhr.szUrl = (char*)url.c_str();
+
+ // Set timeout (bigger for channel request)
+ nlhr.timeout = 1000 * ((request_type == OMEGLE_REQUEST_EVENTS) ? 65 : 20);
+
+ // Set request type (GET/POST) and eventually also POST data
+ if (post_data != nullptr) {
+ nlhr.requestType = REQUEST_POST;
+ nlhr.pData = (char*)(*post_data).c_str();
+ nlhr.dataLength = (int)post_data->length();
+ }
+ else {
+ nlhr.requestType = REQUEST_GET;
+ }
+
+ // Set headers - it depends on requestType so it must be after setting that
+ nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount);
+
+ // Set flags
+ nlhr.flags = NLHRF_HTTP11;
+
+#ifdef _DEBUG
+ nlhr.flags |= NLHRF_DUMPASTEXT;
+#else
+ nlhr.flags |= NLHRF_NODUMP;
+#endif
+
+ // Set persistent connection (or not)
+ switch (request_type)
+ {
+ case OMEGLE_REQUEST_HOME:
+ nlhr.nlc = nullptr;
+ break;
+
+ case OMEGLE_REQUEST_EVENTS:
+ nlhr.nlc = hEventsConnection;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+
+ default:
+ WaitForSingleObject(connection_lock_, INFINITE);
+ nlhr.nlc = hConnection;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+ }
+
+ parent->debugLogA("@@@@@ Sending request to '%s'", nlhr.szUrl);
+
+ // Send the request
+ NLHR_PTR pnlhr(Netlib_HttpTransaction(handle_, &nlhr));
+
+ mir_free(nlhr.headers);
+
+ // Remember the persistent connection handle (or not)
+ switch (request_type)
+ {
+ case OMEGLE_REQUEST_HOME:
+ break;
+
+ case OMEGLE_REQUEST_EVENTS:
+ hEventsConnection = pnlhr ? pnlhr->nlc : nullptr;
+ break;
+
+ default:
+ ReleaseMutex(connection_lock_);
+ hConnection = pnlhr ? pnlhr->nlc : nullptr;
+ break;
+ }
+
+ // Check and copy response data
+ if (pnlhr != nullptr)
+ {
+ parent->debugLogA("@@@@@ Got response with code %d", pnlhr->resultCode);
+ store_headers(&resp, pnlhr->headers, pnlhr->headersCount);
+ resp.code = pnlhr->resultCode;
+ resp.data = pnlhr->pData ? pnlhr->pData : "";
+
+ // This error code gives us some error page, but we don't need that, so clear the response
+ if (pnlhr->resultCode == HTTP_CODE_WEB_SERVER_IS_DOWN) {
+ resp.data = "";
+ }
+
+ parent->debugLogA("&&&&& Got response: %s", resp.data.c_str());
+ }
+ else {
+ parent->debugLogA("!!!!! No response from server (time-out)");
+ resp.code = HTTP_CODE_FAKE_DISCONNECTED;
+ // Better to have something set explicitely as this value is compaired in all communication requests
+ }
+
+ return resp;
+}
+
+bool Omegle_client::handle_entry(const std::string &method)
+{
+ parent->debugLogA(" >> Entering %s()", method.c_str());
+ return true;
+}
+
+bool Omegle_client::handle_success(const std::string &method)
+{
+ parent->debugLogA(" << Quitting %s()", method.c_str());
+ reset_error();
+ return true;
+}
+
+bool Omegle_client::handle_error(const std::string &method, bool force_disconnect)
+{
+ bool result;
+ increment_error();
+ parent->debugLogA("!!!!! %s(): Something with Omegle went wrong", method.c_str());
+
+ if (force_disconnect)
+ result = false;
+ else if (error_count_ <= (UINT)db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_TIMEOUTS_LIMIT, OMEGLE_TIMEOUTS_LIMIT))
+ result = true;
+ else
+ result = false;
+
+ if (result == false)
+ {
+ reset_error();
+ parent->UpdateChat(nullptr, TranslateT("Connection error."));
+ parent->StopChat(false);
+ }
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+std::string Omegle_client::get_server(bool not_last)
+{
+ int q = not_last ? 1 : 0;
+
+ int server = db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_SERVER, 0);
+ if (server < 0 || server >= (int)(_countof(servers) - q))
+ server = 0;
+
+ if (server == 0) {
+ srand(::time(0));
+ server = (rand() % (_countof(servers) - 1 - q)) + 1;
+ }
+
+ return servers[server];
+}
+
+std::string Omegle_client::get_language()
+{
+ int language = db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_LANGUAGE, 0);
+ if (language < 0 || language >= (_countof(languages)))
+ language = 0;
+
+ return language > 0 ? languages[language].id : "en";
+}
+
+std::string Omegle_client::choose_server(int request_type)
+{
+ switch (request_type)
+ {
+ case OMEGLE_REQUEST_HOME:
+ return OMEGLE_SERVER_REGULAR;
+
+ /* case OMEGLE_REQUEST_START:
+ case OMEGLE_REQUEST_STOP:
+ case OMEGLE_REQUEST_SEND:
+ case OMEGLE_REQUEST_EVENTS:
+ case OMEGLE_REQUEST_TYPING_START:
+ case OMEGLE_REQUEST_TYPING_STOP:
+ case OMEGLE_REQUEST_RECAPTCHA:
+ case OMEGLE_REQUEST_COUNT:
+ */ default:
+ std::string server = OMEGLE_SERVER_CHAT;
+ utils::text::replace_first(&server, "%s", this->server_);
+ return server;
+ }
+}
+
+std::string Omegle_client::choose_action(int request_type, std::string* get_data)
+{
+ switch (request_type)
+ {
+ case OMEGLE_REQUEST_START:
+ {
+ std::string action = "/start?caps=recaptcha2,t&rcs=1&spid=&lang=";
+ action += get_language();
+ if (get_data != nullptr)
+ action += (*get_data);
+
+ return action;
+ }
+
+ case OMEGLE_REQUEST_STOP:
+ return "/disconnect";
+
+ case OMEGLE_REQUEST_SEND:
+ return "/send";
+
+ case OMEGLE_REQUEST_EVENTS:
+ return "/events";
+
+ case OMEGLE_REQUEST_TYPING_START:
+ return "/typing";
+
+ case OMEGLE_REQUEST_TYPING_STOP:
+ return "/stoppedtyping";
+
+ case OMEGLE_REQUEST_RECAPTCHA:
+ return "/recaptcha";
+
+ case OMEGLE_REQUEST_COUNT:
+ return "/count";
+
+ // "/stoplookingforcommonlikes"
+
+ /* case OMEGLE_REQUEST_HOME:
+ */ default:
+ return "/";
+ }
+}
+
+
+NETLIBHTTPHEADER* Omegle_client::get_request_headers(int request_type, int* headers_count)
+{
+ if (request_type == REQUEST_POST)
+ *headers_count = 4;
+ else
+ *headers_count = 3;
+
+ NETLIBHTTPHEADER *headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER)*(*headers_count));
+
+ if (request_type == REQUEST_POST) {
+ headers[3].szName = "Content-Type";
+ headers[3].szValue = "application/x-www-form-urlencoded; charset=utf-8";
+ }
+
+ headers[2].szName = "User-Agent";
+ headers[2].szValue = Netlib_GetUserAgent();
+ headers[1].szName = "Accept";
+ headers[1].szValue = "*/*";
+ headers[0].szName = "Accept-Language";
+ headers[0].szValue = "en,en-US;q=0.9";
+
+ return headers;
+}
+
+void Omegle_client::store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headersCount)
+{
+ for (size_t i = 0; i < (size_t)headersCount; i++)
+ {
+ std::string header_name = headers[i].szName;
+ std::string header_value = headers[i].szValue;
+
+ // TODO RM: (un)comment
+ //parent->debugLogA("----- Got header '%s': %s", header_name.c_str(), header_value.c_str());
+ resp->headers[header_name] = header_value;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool Omegle_client::start()
+{
+ HANDLE_ENTRY;
+
+ this->server_ = get_server();
+ //parent->debugLogA("Chosing server %s", this->server_.c_str());
+ //std::string log = Translate("Chosing server: ") + this->server_;
+ //parent->UpdateChat(NULL, log.c_str());
+
+ std::string data;
+
+ if (this->spy_mode_) {
+ //// get last server from list, which is for spy mode
+ //this->server_ = servers[_countof(servers)-1];
+
+ if (this->question_.empty()) {
+ data = "&wantsspy=1";
+ }
+ else {
+ data = "&ask=" + utils::url::encode(this->question_);
+ data += "&cansavequestion=";
+ data += db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_REUSE_QUESTION, 0) ? "1" : "0";
+ }
+ }
+ else if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_MEET_COMMON, 0))
+ {
+ DBVARIANT dbv;
+ if (!db_get_utf(NULL, parent->m_szModuleName, OMEGLE_KEY_INTERESTS, &dbv))
+ {
+ std::string topics = dbv.pszVal;
+ std::string topic;
+
+ db_free(&dbv);
+
+ std::string::size_type pos = 0;
+ std::string::size_type pos2 = 0;
+ while ((pos2 = topics.find(",", pos)) != std::string::npos) {
+ topic = topics.substr(pos, pos2 - pos);
+ topic = utils::text::trim(topic);
+
+ if (!topic.empty()) {
+ if (pos > 0)
+ data += ",";
+
+ data += "\"" + topic + "\"";
+ }
+
+ pos = pos2 + 1;
+ }
+
+ topic = topics.substr(pos);
+ topic = utils::text::trim(topic);
+ if (!topic.empty()) {
+ if (pos > 0)
+ data += ",";
+ data += "\"" + topic + "\"";
+ }
+
+ parent->debugLogA("TOPICS: %s", data.c_str());
+
+ if (!data.empty()) {
+ data = "[" + data + "]";
+ data = "&topics=" + utils::url::encode(data);
+ }
+
+ //// get any server but last, which is for spy mode
+ //this->server_ = get_server(true);
+ }
+ }
+
+ if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_SERVER_INFO, 0))
+ {
+ std::string count = get_page(OMEGLE_REQUEST_COUNT);
+ if (!count.empty()) {
+ char str[255];
+ mir_snprintf(str, Translate("Connected to server %s. There are %s users online now."), server_.c_str(), count.c_str());
+
+ wchar_t *msg = mir_a2u(str);
+ parent->UpdateChat(nullptr, msg);
+ mir_free(msg);
+ }
+ }
+ else {
+ char str[255];
+ mir_snprintf(str, Translate("Connected to server %s."), server_.c_str());
+
+ wchar_t *msg = mir_a2u(str);
+ parent->UpdateChat(nullptr, msg);
+ mir_free(msg);
+ }
+
+ // Send validation
+ http::response resp = flap(OMEGLE_REQUEST_START, nullptr, &data);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ {
+ // If is is only timeout error, try login once more
+ if (HANDLE_ERROR(false))
+ return start();
+ else
+ return false;
+ }
+
+ case HTTP_CODE_OK:
+ {
+ if (!resp.data.empty()) {
+ this->chat_id_ = resp.data.substr(1, resp.data.length() - 2);
+ this->state_ = STATE_WAITING;
+
+ return HANDLE_SUCCESS;
+ }
+ else {
+ return HANDLE_ERROR(FORCE_DISCONNECT);
+ }
+ }
+
+ default:
+ return HANDLE_ERROR(FORCE_DISCONNECT);
+ }
+}
+
+bool Omegle_client::stop()
+{
+ if (parent->isOffline())
+ return true;
+
+ HANDLE_ENTRY;
+
+ std::string data = "id=" + this->chat_id_;
+
+ http::response resp = flap(OMEGLE_REQUEST_STOP, &data);
+
+ Netlib_Shutdown(hConnection);
+ Netlib_Shutdown(hEventsConnection);
+
+ return (resp.data == "win") ? HANDLE_SUCCESS : HANDLE_ERROR(false);
+}
+
+bool Omegle_client::events()
+{
+ HANDLE_ENTRY;
+
+ std::string post_data = "id=" + this->chat_id_;
+
+ // Get update
+ http::response resp = flap(OMEGLE_REQUEST_EVENTS, &post_data);
+
+ // Return
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ {
+ if (resp.data == "null") {
+ // Everything is OK, no new message received -- OR it is a problem
+ // TODO: if we are waiting for Stranger with common likes, then we should try standard Stranger if this takes too long
+ return HANDLE_ERROR(false);
+ }
+ else if (resp.data == "fail") {
+ // Something went wrong
+ return HANDLE_ERROR(false);
+ }
+
+ JSONROOT root(resp.data.c_str());
+ if (root == nullptr)
+ return HANDLE_ERROR(false);
+
+ bool newStranger = false;
+ bool waiting = false;
+
+ for (size_t i = 0; i < json_size(root); i++) {
+ JSONNode *item = json_at(root, i);
+ if (item == nullptr)
+ continue;
+
+ std::string name = _T2A(json_as_string(json_at(item, 0)));
+
+ if (name == "waiting") {
+ // We are just waiting for new Stranger
+ waiting = true;
+ }
+ else if (name == "identDigests") {
+ // We get some comma separated hashes, I'm not sure what for
+ }
+ else if (name == "statusInfo") {
+ JSONNode *data = json_at(item, 1);
+
+ // We got some object as second parameter
+ //data["antinudepercent"]; // probably 1 by default
+ //data["antinudeservers"]; // array of server names, like "waw3.omegle.com"
+ //data["rtmfp"]; // some rtmfp protocol address
+ //data["servers"]; // array of server names, like "front5.omegle.com"
+ //data["spyeeQueueTime"]; // some float number, e.g. 0.0701999903
+ //data["spyQueueTime"]; // some float number, e.g. 4.7505000114
+ //data["timestamp"]; // e.g. 1445336566.0196209
+
+ // We got info about count of connected people there
+ ptrW count(json_as_string(json_get(data, "count")));
+ wchar_t strT[255];
+ mir_snwprintf(strT, TranslateT("On whole Omegle are %s strangers online now."), count.get());
+
+ parent->UpdateChat(nullptr, strT);
+ }
+ else if (name == "serverMessage") {
+ ptrW message(json_as_string(json_at(item, 1)));
+ parent->UpdateChat(nullptr, TranslateW(message));
+ }
+ else if (name == "connected") {
+ // Stranger connected
+ if (this->spy_mode_ && !this->question_.empty()) {
+ parent->AddChatContact(TranslateT("Stranger 1"));
+ parent->AddChatContact(TranslateT("Stranger 2"));
+ this->state_ = STATE_SPY;
+ }
+ else {
+ parent->AddChatContact(TranslateT("Stranger"));
+ this->state_ = STATE_ACTIVE;
+ }
+
+ newStranger = true;
+ waiting = false;
+ }
+ else if (name == "commonLikes") {
+ std::wstring likes = TranslateT("You and the Stranger both like: ");
+
+ JSONNode *items = json_at(item, 1);
+ size_t size = json_size(items);
+ for (size_t j = 0; j < size; j++) {
+ likes += ptrW(json_as_string(json_at(items, j)));
+ if (j < size - 1)
+ likes += L", ";
+ }
+
+ parent->debugLogW(L"Got common likes: '%s'", likes.c_str());
+ parent->SetTopic(likes.c_str());
+ }
+ else if (name == "question") {
+ ptrW question(json_as_string(json_at(item, 1)));
+ parent->SetTopic(question);
+ }
+ else if (name == "typing" || name == "spyTyping") {
+ // Stranger is typing, not supported by chat module yet
+ Skin_PlaySound("StrangerTyp");
+
+ ptrW who(name == "spyTyping" ? json_as_string(json_at(item, 1)) : mir_wstrdup(L"Stranger"));
+ Srmm_SetStatusText(parent->GetChatHandle(), CMStringW(FORMAT, TranslateT("%s is typing."), TranslateW(who)), g_plugin.getIcon(IDI_TYPING_ON));
+ }
+ else if (name == "stoppedTyping" || name == "spyStoppedTyping") {
+ // Stranger stopped typing, not supported by chat module yet
+ Skin_PlaySound("StrangerTypStop");
+
+ ptrW who(name == "spyTyping" ? json_as_string(json_at(item, 1)) : mir_wstrdup(L"Stranger"));
+ Srmm_SetStatusText(parent->GetChatHandle(), CMStringW(FORMAT, TranslateT("%s stopped typing."), TranslateW(who)), g_plugin.getIcon(IDI_TYPING_OFF));
+ }
+ else if (name == "gotMessage") {
+ Srmm_SetStatusText(parent->GetChatHandle(), nullptr);
+
+ // Play sound as we received message
+ Skin_PlaySound("StrangerMessage");
+
+ if (state_ == STATE_ACTIVE) {
+ ptrW msg(json_as_string(json_at(item, 1)));
+ parent->UpdateChat(TranslateT("Stranger"), msg);
+ }
+ }
+ else if (name == "spyMessage") {
+ Srmm_SetStatusText(parent->GetChatHandle(), nullptr);
+
+ // Play sound as we received message
+ Skin_PlaySound("StrangerMessage");
+
+ if (state_ == STATE_SPY) {
+ ptrW stranger(json_as_string(json_at(item, 1)));
+ ptrW msg(json_as_string(json_at(item, 2)));
+ parent->UpdateChat(stranger, msg);
+ }
+ }
+ else if (name == "strangerDisconnected") {
+ Srmm_SetStatusText(parent->GetChatHandle(), nullptr);
+
+ // Stranger disconnected
+ if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_DONT_STOP, 0))
+ {
+ Skin_PlaySound("StrangerChange");
+ parent->NewChat();
+ }
+ else
+ parent->StopChat(false);
+ }
+ else if (name == "spyDisconnected") {
+ Srmm_SetStatusText(parent->GetChatHandle(), nullptr);
+
+ ptrW stranger(json_as_string(json_at(item, 1)));
+
+ wchar_t strT[255];
+ mir_snwprintf(strT, TranslateT("%s disconnected."), TranslateW(stranger));
+ parent->UpdateChat(nullptr, strT);
+
+ // Stranger disconnected
+ if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_DONT_STOP, 0))
+ {
+ Skin_PlaySound("StrangerChange");
+ parent->NewChat();
+ }
+ else
+ parent->StopChat(false);
+ }
+ else if (name == "recaptchaRequired") {
+ // Nothing to do with recaptcha
+ parent->UpdateChat(nullptr, TranslateT("Recaptcha is required.\nOpen http://omegle.com , solve Recaptcha and try again."));
+ parent->StopChat(false);
+ }
+ else if (name == "recaptchaRejected") {
+ // Nothing to do with recaptcha
+ parent->StopChat(false);
+ }
+ else if (name == "error") {
+ ptrW error(json_as_string(json_at(item, 1)));
+
+ wchar_t strT[255];
+ mir_snwprintf(strT, TranslateT("Error: %s"), TranslateW(error));
+ parent->UpdateChat(nullptr, strT);
+ }
+ }
+
+ if (newStranger && !spy_mode_) {
+ // We got new stranger in this event, lets say him "Hi message" if enabled
+ if (db_get_b(0, parent->m_szModuleName, OMEGLE_KEY_HI_ENABLED, 0)) {
+ DBVARIANT dbv;
+ if (!db_get_utf(NULL, parent->m_szModuleName, OMEGLE_KEY_HI, &dbv)) {
+ std::vector<std::string> messages;
+ utils::text::explode(std::string(dbv.pszVal), "\r\n", &messages);
+ db_free(&dbv);
+
+ int pos = rand() % messages.size();
+ std::string *message = new std::string(messages.at(pos));
+
+ parent->debugLogA("**Chat - saying Hi! message");
+ parent->ForkThread(&OmegleProto::SendMsgWorker, message);
+ }
+ else parent->debugLogA("**Chat - Hi message is enabled but not used");
+ }
+ }
+
+ if (waiting) {
+ // If we are only waiting in this event...
+ parent->UpdateChat(nullptr, TranslateT("We are still waiting..."));
+ }
+
+ return HANDLE_SUCCESS;
+ }
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ // timeout
+ return HANDLE_SUCCESS;
+
+ default:
+ return HANDLE_ERROR(false);
+ }
+}
+
+bool Omegle_client::send_message(const std::string &message_text)
+{
+ HANDLE_ENTRY;
+
+ std::string data = "msg=" + utils::url::encode(message_text);
+ data += "&id=" + this->chat_id_;
+
+ http::response resp = flap(OMEGLE_REQUEST_SEND, &data);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ if (resp.data == "win") {
+ return HANDLE_SUCCESS;
+ }
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return HANDLE_ERROR(false);
+ }
+}
+
+bool Omegle_client::typing_start()
+{
+ HANDLE_ENTRY;
+
+ std::string data = "id=" + this->chat_id_;
+
+ http::response resp = flap(OMEGLE_REQUEST_TYPING_START, &data);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ if (resp.data == "win") {
+ return HANDLE_SUCCESS;
+ }
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return HANDLE_ERROR(false);
+ }
+}
+
+bool Omegle_client::typing_stop()
+{
+ HANDLE_ENTRY;
+
+ std::string data = "id=" + this->chat_id_;
+
+ http::response resp = flap(OMEGLE_REQUEST_TYPING_STOP, &data);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ if (resp.data == "win") {
+ return HANDLE_SUCCESS;
+ }
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return HANDLE_ERROR(false);
+ }
+}
+
+bool Omegle_client::recaptcha()
+{
+ // TODO: Implement!
+
+ HANDLE_ENTRY;
+
+ // data:{id:this.clientID,challenge:b,response:a}}
+ //std::string data = "?id=...&challenge= ..., &response= ...";
+
+ http::response resp = flap(OMEGLE_REQUEST_RECAPTCHA);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ /* if (resp.data == "win") {
+ return handle_success( "typing_start" );
+ }*/
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return HANDLE_ERROR(false);
+ }
+}
+
+std::string Omegle_client::get_page(const int request_type)
+{
+ HANDLE_ENTRY;
+
+ http::response resp = flap(request_type);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ HANDLE_SUCCESS;
+ break;
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ HANDLE_ERROR(false);
+ }
+
+ return resp.data;
+}
diff --git a/protocols/Omegle/src/connection.cpp b/protocols/Omegle/src/connection.cpp index 7750fe9038..db2b73970c 100644 --- a/protocols/Omegle/src/connection.cpp +++ b/protocols/Omegle/src/connection.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/constants.h b/protocols/Omegle/src/constants.h index 67ce350240..5bc923f4a3 100644 --- a/protocols/Omegle/src/constants.h +++ b/protocols/Omegle/src/constants.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/db.h b/protocols/Omegle/src/db.h index 3798d0af78..45d3478828 100644 --- a/protocols/Omegle/src/db.h +++ b/protocols/Omegle/src/db.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/dialogs.cpp b/protocols/Omegle/src/dialogs.cpp index e71b6aa542..ee243053c0 100644 --- a/protocols/Omegle/src/dialogs.cpp +++ b/protocols/Omegle/src/dialogs.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/dialogs.h b/protocols/Omegle/src/dialogs.h index 4685b9de4c..080b36c07a 100644 --- a/protocols/Omegle/src/dialogs.h +++ b/protocols/Omegle/src/dialogs.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/http.cpp b/protocols/Omegle/src/http.cpp index b21f5e8602..45bb2a62a2 100644 --- a/protocols/Omegle/src/http.cpp +++ b/protocols/Omegle/src/http.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/http.h b/protocols/Omegle/src/http.h index aeffb16f51..b107aaf3a0 100644 --- a/protocols/Omegle/src/http.h +++ b/protocols/Omegle/src/http.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/main.cpp b/protocols/Omegle/src/main.cpp index 20de90c123..72b1edc3cd 100644 --- a/protocols/Omegle/src/main.cpp +++ b/protocols/Omegle/src/main.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/messages.cpp b/protocols/Omegle/src/messages.cpp index cd92e02be9..fd7a79e4ae 100644 --- a/protocols/Omegle/src/messages.cpp +++ b/protocols/Omegle/src/messages.cpp @@ -1,74 +1,74 @@ -/* - -Omegle plugin for Miranda Instant Messenger -_____________________________________________ - -Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "stdafx.h" - -void OmegleProto::SendMsgWorker(void *p) -{ - if (p == nullptr) - return; - - std::string data = *(std::string*)p; - delete (std::string*)p; - - data = utils::text::trim(data); - - if (facy.state_ == STATE_ACTIVE && data.length() && facy.send_message(data)) { - wchar_t *msg = mir_a2u_cp(data.c_str(), CP_UTF8); - UpdateChat(facy.nick_, msg); - mir_free(msg); - } -} - -void OmegleProto::SendTypingWorker(void *p) -{ - if (p == nullptr) - return; - - // Save typing info - bool typ = (*static_cast<int*>(p) == PROTOTYPE_SELFTYPING_ON); - delete (int*)p; - - // Ignore same typing info - if (facy.typing_ == typ) - return; - - if (facy.state_ != STATE_ACTIVE) - return; - - facy.typing_ = typ; - - if (typ) - facy.typing_start(); - else - facy.typing_stop(); -} - -void OmegleProto::NewChatWorker(void*) -{ - NewChat(); -} - -void OmegleProto::StopChatWorker(void*) -{ - StopChat(); -} +/*
+
+Omegle plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "stdafx.h"
+
+void OmegleProto::SendMsgWorker(void *p)
+{
+ if (p == nullptr)
+ return;
+
+ std::string data = *(std::string*)p;
+ delete (std::string*)p;
+
+ data = utils::text::trim(data);
+
+ if (facy.state_ == STATE_ACTIVE && data.length() && facy.send_message(data)) {
+ wchar_t *msg = mir_a2u_cp(data.c_str(), CP_UTF8);
+ UpdateChat(facy.nick_, msg);
+ mir_free(msg);
+ }
+}
+
+void OmegleProto::SendTypingWorker(void *p)
+{
+ if (p == nullptr)
+ return;
+
+ // Save typing info
+ bool typ = (*static_cast<int*>(p) == PROTOTYPE_SELFTYPING_ON);
+ delete (int*)p;
+
+ // Ignore same typing info
+ if (facy.typing_ == typ)
+ return;
+
+ if (facy.state_ != STATE_ACTIVE)
+ return;
+
+ facy.typing_ = typ;
+
+ if (typ)
+ facy.typing_start();
+ else
+ facy.typing_stop();
+}
+
+void OmegleProto::NewChatWorker(void*)
+{
+ NewChat();
+}
+
+void OmegleProto::StopChatWorker(void*)
+{
+ StopChat();
+}
diff --git a/protocols/Omegle/src/proto.cpp b/protocols/Omegle/src/proto.cpp index ccc0cb9173..036dc41d6f 100644 --- a/protocols/Omegle/src/proto.cpp +++ b/protocols/Omegle/src/proto.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/proto.h b/protocols/Omegle/src/proto.h index 388e94299a..839d6ecb25 100644 --- a/protocols/Omegle/src/proto.h +++ b/protocols/Omegle/src/proto.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/stdafx.cxx b/protocols/Omegle/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/protocols/Omegle/src/stdafx.cxx +++ b/protocols/Omegle/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Omegle/src/stdafx.h b/protocols/Omegle/src/stdafx.h index d10351511d..81425082c5 100644 --- a/protocols/Omegle/src/stdafx.h +++ b/protocols/Omegle/src/stdafx.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/theme.cpp b/protocols/Omegle/src/theme.cpp index ce96ff5224..f9899adbe9 100644 --- a/protocols/Omegle/src/theme.cpp +++ b/protocols/Omegle/src/theme.cpp @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/theme.h b/protocols/Omegle/src/theme.h index 5b50f4648e..0418ca4268 100644 --- a/protocols/Omegle/src/theme.h +++ b/protocols/Omegle/src/theme.h @@ -3,7 +3,7 @@ Omegle plugin for Miranda Instant Messenger
_____________________________________________
-Copyright © 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/protocols/Omegle/src/version.h b/protocols/Omegle/src/version.h index d30f57170c..3780281020 100644 --- a/protocols/Omegle/src/version.h +++ b/protocols/Omegle/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Omegle protocol support for Miranda NG."
#define __AUTHOR "Robert Pösel"
#define __AUTHORWEB "https://miranda-ng.org/p/Omegle"
-#define __COPYRIGHT "© 2011-17 Robert Pösel, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2011-17 Robert Pösel, 2017-23 Miranda NG team"
diff --git a/protocols/Sametime/src/version.h b/protocols/Sametime/src/version.h index a09e825f76..87628ff6ab 100644 --- a/protocols/Sametime/src/version.h +++ b/protocols/Sametime/src/version.h @@ -9,5 +9,5 @@ #define __FILENAME "Sametime.dll"
#define __DESCRIPTION "Implementation of instant messaging for the Lotus Sametime protocol."
#define __AUTHOR "Scott Ellis, Szymon Tokarz"
-#define __COPYRIGHT "© 2005 Scott Ellis, 2014-22 wsx22"
+#define __COPYRIGHT "© 2005 Scott Ellis, 2014-23 wsx22"
#define __AUTHORWEB "https://miranda-ng.org/p/Sametime"
diff --git a/protocols/SkypeWeb/src/main.cpp b/protocols/SkypeWeb/src/main.cpp index 3e3c36210b..b1e3ddfb5b 100644 --- a/protocols/SkypeWeb/src/main.cpp +++ b/protocols/SkypeWeb/src/main.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/request_queue.cpp b/protocols/SkypeWeb/src/request_queue.cpp index c7ab39d344..d6240e7cee 100644 --- a/protocols/SkypeWeb/src/request_queue.cpp +++ b/protocols/SkypeWeb/src/request_queue.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/avatars.h b/protocols/SkypeWeb/src/requests/avatars.h index fd015e1bed..cdf5ac7b6e 100644 --- a/protocols/SkypeWeb/src/requests/avatars.h +++ b/protocols/SkypeWeb/src/requests/avatars.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/capabilities.h b/protocols/SkypeWeb/src/requests/capabilities.h index 17b823a575..3170ca6d66 100644 --- a/protocols/SkypeWeb/src/requests/capabilities.h +++ b/protocols/SkypeWeb/src/requests/capabilities.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/chatrooms.h b/protocols/SkypeWeb/src/requests/chatrooms.h index 2e12729862..2fc4e435ff 100644 --- a/protocols/SkypeWeb/src/requests/chatrooms.h +++ b/protocols/SkypeWeb/src/requests/chatrooms.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/contacts.h b/protocols/SkypeWeb/src/requests/contacts.h index 4a62c67441..f9a3c13fa0 100644 --- a/protocols/SkypeWeb/src/requests/contacts.h +++ b/protocols/SkypeWeb/src/requests/contacts.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/endpoint.h b/protocols/SkypeWeb/src/requests/endpoint.h index 8c351144c8..c0abe99a43 100644 --- a/protocols/SkypeWeb/src/requests/endpoint.h +++ b/protocols/SkypeWeb/src/requests/endpoint.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/history.h b/protocols/SkypeWeb/src/requests/history.h index 75b7dc93d4..4438288479 100644 --- a/protocols/SkypeWeb/src/requests/history.h +++ b/protocols/SkypeWeb/src/requests/history.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/login.h b/protocols/SkypeWeb/src/requests/login.h index e06cb28d40..e0053745d4 100644 --- a/protocols/SkypeWeb/src/requests/login.h +++ b/protocols/SkypeWeb/src/requests/login.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/messages.h b/protocols/SkypeWeb/src/requests/messages.h index 4a71b79445..4e41e38dd4 100644 --- a/protocols/SkypeWeb/src/requests/messages.h +++ b/protocols/SkypeWeb/src/requests/messages.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/oauth.h b/protocols/SkypeWeb/src/requests/oauth.h index 5d26982a8d..cc9842a0d2 100644 --- a/protocols/SkypeWeb/src/requests/oauth.h +++ b/protocols/SkypeWeb/src/requests/oauth.h @@ -1,74 +1,74 @@ -/* -Copyright (c) 2015-22 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_OAUTH_H_ -#define _SKYPE_REQUEST_OAUTH_H_ - -struct OAuthRequest : public AsyncHttpRequest -{ - OAuthRequest() : - AsyncHttpRequest(REQUEST_GET, HOST_OTHER, "https://login.live.com/login.srf", &CSkypeProto::OnOAuthStart) - { - flags |= NLHRF_REDIRECT; - - this << CHAR_PARAM("wa", "wsignin1.0") << CHAR_PARAM("wp", "MBI_SSL") - << CHAR_PARAM("wreply", "https://lw.skype.com/login/oauth/proxy?site_name=lw.skype.com") - << CHAR_PARAM("cobrandid", "90010"); - } - - OAuthRequest(const char *login, const char *password, const char *cookies, const char *ppft) : - AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://login.live.com/ppsecure/post.srf", &CSkypeProto::OnOAuthConfirm) - { - this << CHAR_PARAM("wa", "wsignin1.0") << CHAR_PARAM("wp", "MBI_SSL") - << CHAR_PARAM("wreply", "https://lw.skype.com/login/oauth/proxy?site_name=lw.skype.com") - << CHAR_PARAM("cobrandid", "90010"); - m_szUrl.AppendFormat("?%s", m_szParam.c_str()); - m_szParam.Empty(); - - AddHeader("Cookie", cookies); - - if (auto *delim = strchr(login, ':')) - login = delim + 1; - - this << CHAR_PARAM("login", login) << CHAR_PARAM("passwd", password) << CHAR_PARAM("PPFT", ppft); - } - - OAuthRequest(const char *cookies, const char* ppft, const char* opid) : - AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://login.live.com/ppsecure/post.srf", &CSkypeProto::OnOAuthAuthorize) - { - this << CHAR_PARAM("wa", "wsignin1.0") << CHAR_PARAM("wp", "MBI_SSL") - << CHAR_PARAM("wreply", "https://lw.skype.com/login/oauth/proxy?site_name=lw.skype.com") - << CHAR_PARAM("cobrandid", "90010") - << CHAR_PARAM("id", "293290") - << CHAR_PARAM("opid", opid); - - m_szUrl.AppendFormat("?%s", m_szParam.c_str()); - m_szParam.Empty(); - - AddHeader("Cookie", cookies); - - this << CHAR_PARAM("type", "28") << CHAR_PARAM("PPFT", ppft); - } - - OAuthRequest(const char *t) : - AsyncHttpRequest(REQUEST_POST, HOST_LOGIN, "/login/microsoft", &CSkypeProto::OnOAuthEnd) - { - this << CHAR_PARAM ("t", t) << CHAR_PARAM("site_name", "lw.skype.com") << INT_PARAM ("oauthPartner", 999); - } -}; - -#endif //_SKYPE_REQUEST_OAUTH_H_ +/*
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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_OAUTH_H_
+#define _SKYPE_REQUEST_OAUTH_H_
+
+struct OAuthRequest : public AsyncHttpRequest
+{
+ OAuthRequest() :
+ AsyncHttpRequest(REQUEST_GET, HOST_OTHER, "https://login.live.com/login.srf", &CSkypeProto::OnOAuthStart)
+ {
+ flags |= NLHRF_REDIRECT;
+
+ this << CHAR_PARAM("wa", "wsignin1.0") << CHAR_PARAM("wp", "MBI_SSL")
+ << CHAR_PARAM("wreply", "https://lw.skype.com/login/oauth/proxy?site_name=lw.skype.com")
+ << CHAR_PARAM("cobrandid", "90010");
+ }
+
+ OAuthRequest(const char *login, const char *password, const char *cookies, const char *ppft) :
+ AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://login.live.com/ppsecure/post.srf", &CSkypeProto::OnOAuthConfirm)
+ {
+ this << CHAR_PARAM("wa", "wsignin1.0") << CHAR_PARAM("wp", "MBI_SSL")
+ << CHAR_PARAM("wreply", "https://lw.skype.com/login/oauth/proxy?site_name=lw.skype.com")
+ << CHAR_PARAM("cobrandid", "90010");
+ m_szUrl.AppendFormat("?%s", m_szParam.c_str());
+ m_szParam.Empty();
+
+ AddHeader("Cookie", cookies);
+
+ if (auto *delim = strchr(login, ':'))
+ login = delim + 1;
+
+ this << CHAR_PARAM("login", login) << CHAR_PARAM("passwd", password) << CHAR_PARAM("PPFT", ppft);
+ }
+
+ OAuthRequest(const char *cookies, const char* ppft, const char* opid) :
+ AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://login.live.com/ppsecure/post.srf", &CSkypeProto::OnOAuthAuthorize)
+ {
+ this << CHAR_PARAM("wa", "wsignin1.0") << CHAR_PARAM("wp", "MBI_SSL")
+ << CHAR_PARAM("wreply", "https://lw.skype.com/login/oauth/proxy?site_name=lw.skype.com")
+ << CHAR_PARAM("cobrandid", "90010")
+ << CHAR_PARAM("id", "293290")
+ << CHAR_PARAM("opid", opid);
+
+ m_szUrl.AppendFormat("?%s", m_szParam.c_str());
+ m_szParam.Empty();
+
+ AddHeader("Cookie", cookies);
+
+ this << CHAR_PARAM("type", "28") << CHAR_PARAM("PPFT", ppft);
+ }
+
+ OAuthRequest(const char *t) :
+ AsyncHttpRequest(REQUEST_POST, HOST_LOGIN, "/login/microsoft", &CSkypeProto::OnOAuthEnd)
+ {
+ this << CHAR_PARAM ("t", t) << CHAR_PARAM("site_name", "lw.skype.com") << INT_PARAM ("oauthPartner", 999);
+ }
+};
+
+#endif //_SKYPE_REQUEST_OAUTH_H_
diff --git a/protocols/SkypeWeb/src/requests/poll.h b/protocols/SkypeWeb/src/requests/poll.h index ce4b32cb4c..4475ec041e 100644 --- a/protocols/SkypeWeb/src/requests/poll.h +++ b/protocols/SkypeWeb/src/requests/poll.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/profile.h b/protocols/SkypeWeb/src/requests/profile.h index 426087a63a..c255f9166b 100644 --- a/protocols/SkypeWeb/src/requests/profile.h +++ b/protocols/SkypeWeb/src/requests/profile.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/search.h b/protocols/SkypeWeb/src/requests/search.h index d47562ff9c..357ad470bc 100644 --- a/protocols/SkypeWeb/src/requests/search.h +++ b/protocols/SkypeWeb/src/requests/search.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/status.h b/protocols/SkypeWeb/src/requests/status.h index 58ebd23dcd..74e35960cd 100644 --- a/protocols/SkypeWeb/src/requests/status.h +++ b/protocols/SkypeWeb/src/requests/status.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/requests/subscriptions.h b/protocols/SkypeWeb/src/requests/subscriptions.h index b0f71046e8..b1ddc03a6e 100644 --- a/protocols/SkypeWeb/src/requests/subscriptions.h +++ b/protocols/SkypeWeb/src/requests/subscriptions.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_avatars.cpp b/protocols/SkypeWeb/src/skype_avatars.cpp index 934180e929..1ac07d6d33 100644 --- a/protocols/SkypeWeb/src/skype_avatars.cpp +++ b/protocols/SkypeWeb/src/skype_avatars.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_chatrooms.cpp b/protocols/SkypeWeb/src/skype_chatrooms.cpp index 3087321ea2..4f27b13c5c 100644 --- a/protocols/SkypeWeb/src/skype_chatrooms.cpp +++ b/protocols/SkypeWeb/src/skype_chatrooms.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_contacts.cpp b/protocols/SkypeWeb/src/skype_contacts.cpp index cd1a2a13a1..75827daaf6 100644 --- a/protocols/SkypeWeb/src/skype_contacts.cpp +++ b/protocols/SkypeWeb/src/skype_contacts.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_db.cpp b/protocols/SkypeWeb/src/skype_db.cpp index 3417b15c01..8d815088d1 100644 --- a/protocols/SkypeWeb/src/skype_db.cpp +++ b/protocols/SkypeWeb/src/skype_db.cpp @@ -1,119 +1,119 @@ -/* -Copyright (c) 2015-22 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" - -struct { int type; char *name; uint32_t flags; } g_SkypeDBTypes[] = -{ - { SKYPE_DB_EVENT_TYPE_INCOMING_CALL, LPGEN("Incoming call"), DETF_NONOTIFY }, - { SKYPE_DB_EVENT_TYPE_EDITED_MESSAGE, LPGEN("Edited message"), 0 }, - { SKYPE_DB_EVENT_TYPE_ACTION, LPGEN("Action"), 0 }, - { SKYPE_DB_EVENT_TYPE_CALL_INFO, LPGEN("Call information"), 0 }, - { SKYPE_DB_EVENT_TYPE_FILETRANSFER_INFO, LPGEN("File transfer information"), 0 }, - { SKYPE_DB_EVENT_TYPE_URIOBJ, LPGEN("URI object"), 0 }, - { SKYPE_DB_EVENT_TYPE_MOJI, LPGEN("Moji"), 0 }, - { SKYPE_DB_EVENT_TYPE_FILE, LPGEN("File"), 0 }, - { SKYPE_DB_EVENT_TYPE_UNKNOWN, LPGEN("Unknown event"), 0 }, -}; - -MEVENT CSkypeProto::GetMessageFromDb(const char *messageId) -{ - if (messageId == nullptr) - return NULL; - - return db_event_getById(m_szModuleName, messageId); -} - -MEVENT CSkypeProto::AddDbEvent(uint16_t type, MCONTACT hContact, uint32_t timestamp, uint32_t flags, const CMStringW &content, const CMStringA &msgId) -{ - if (MEVENT hDbEvent = GetMessageFromDb(msgId)) - return hDbEvent; - - T2Utf szMsg(content); - DBEVENTINFO dbei = {}; - dbei.szModule = m_szModuleName; - dbei.timestamp = timestamp; - dbei.eventType = type; - dbei.cbBlob = (uint32_t)mir_strlen(szMsg) + 1; - dbei.pBlob = (uint8_t *)szMsg; - dbei.flags = flags; - dbei.szId = msgId; - return db_event_add(hContact, &dbei); -} - -void CSkypeProto::EditEvent(MCONTACT hContact, MEVENT hEvent, const CMStringW &szContent, time_t edit_time) -{ - mir_cslock lck(m_AppendMessageLock); - - DB::EventInfo dbei; - dbei.cbBlob = -1; - if (db_event_get(hEvent, &dbei)) - return; - - JSONNode jMsg = JSONNode::parse((char*)dbei.pBlob); - if (jMsg) { - JSONNode &jEdits = jMsg["edits"]; - if (jEdits) { - for (auto &it : jEdits) - if (it["time"].as_int() == edit_time) - return; - - JSONNode jEdit; - jEdit << INT_PARAM("time", (long)edit_time) << WCHAR_PARAM("text", szContent); - jEdits << jEdit; - } - } - else { - JSONNode jOriginalMsg; jOriginalMsg.set_name("original_message"); - jOriginalMsg << INT_PARAM("time", (long)dbei.timestamp) << CHAR_PARAM("text", (char *)dbei.pBlob); - - jMsg = JSONNode(); - jMsg << jOriginalMsg; - - JSONNode jEdit; - jEdit << INT_PARAM("time", (long)edit_time) << WCHAR_PARAM("text", szContent); - - JSONNode jEdits(JSON_ARRAY); jEdits.set_name("edits"); - jEdits << jEdit; - jMsg << jEdits; - } - - std::string newMsg = jMsg.write().c_str(); - dbei.cbBlob = int(newMsg.size() + 1); - dbei.pBlob = (uint8_t*)newMsg.c_str(); - db_event_edit(hContact, hEvent, &dbei); -} - -void CSkypeProto::InitDBEvents() -{ - // custom event - DBEVENTTYPEDESCR dbEventType = {}; - dbEventType.module = m_szModuleName; - dbEventType.flags = DETF_HISTORY | DETF_MSGWINDOW; - dbEventType.iconService = MODULE "/GetEventIcon"; - dbEventType.textService = MODULE "/GetEventText"; - - for (auto &cur : g_SkypeDBTypes) { - dbEventType.eventType = cur.type; - dbEventType.descr = Translate(cur.name); - dbEventType.flags |= cur.flags; - - DbEvent_RegisterType(&dbEventType); - - dbEventType.flags &= (~cur.flags); - } -} +/*
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+struct { int type; char *name; uint32_t flags; } g_SkypeDBTypes[] =
+{
+ { SKYPE_DB_EVENT_TYPE_INCOMING_CALL, LPGEN("Incoming call"), DETF_NONOTIFY },
+ { SKYPE_DB_EVENT_TYPE_EDITED_MESSAGE, LPGEN("Edited message"), 0 },
+ { SKYPE_DB_EVENT_TYPE_ACTION, LPGEN("Action"), 0 },
+ { SKYPE_DB_EVENT_TYPE_CALL_INFO, LPGEN("Call information"), 0 },
+ { SKYPE_DB_EVENT_TYPE_FILETRANSFER_INFO, LPGEN("File transfer information"), 0 },
+ { SKYPE_DB_EVENT_TYPE_URIOBJ, LPGEN("URI object"), 0 },
+ { SKYPE_DB_EVENT_TYPE_MOJI, LPGEN("Moji"), 0 },
+ { SKYPE_DB_EVENT_TYPE_FILE, LPGEN("File"), 0 },
+ { SKYPE_DB_EVENT_TYPE_UNKNOWN, LPGEN("Unknown event"), 0 },
+};
+
+MEVENT CSkypeProto::GetMessageFromDb(const char *messageId)
+{
+ if (messageId == nullptr)
+ return NULL;
+
+ return db_event_getById(m_szModuleName, messageId);
+}
+
+MEVENT CSkypeProto::AddDbEvent(uint16_t type, MCONTACT hContact, uint32_t timestamp, uint32_t flags, const CMStringW &content, const CMStringA &msgId)
+{
+ if (MEVENT hDbEvent = GetMessageFromDb(msgId))
+ return hDbEvent;
+
+ T2Utf szMsg(content);
+ DBEVENTINFO dbei = {};
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = timestamp;
+ dbei.eventType = type;
+ dbei.cbBlob = (uint32_t)mir_strlen(szMsg) + 1;
+ dbei.pBlob = (uint8_t *)szMsg;
+ dbei.flags = flags;
+ dbei.szId = msgId;
+ return db_event_add(hContact, &dbei);
+}
+
+void CSkypeProto::EditEvent(MCONTACT hContact, MEVENT hEvent, const CMStringW &szContent, time_t edit_time)
+{
+ mir_cslock lck(m_AppendMessageLock);
+
+ DB::EventInfo dbei;
+ dbei.cbBlob = -1;
+ if (db_event_get(hEvent, &dbei))
+ return;
+
+ JSONNode jMsg = JSONNode::parse((char*)dbei.pBlob);
+ if (jMsg) {
+ JSONNode &jEdits = jMsg["edits"];
+ if (jEdits) {
+ for (auto &it : jEdits)
+ if (it["time"].as_int() == edit_time)
+ return;
+
+ JSONNode jEdit;
+ jEdit << INT_PARAM("time", (long)edit_time) << WCHAR_PARAM("text", szContent);
+ jEdits << jEdit;
+ }
+ }
+ else {
+ JSONNode jOriginalMsg; jOriginalMsg.set_name("original_message");
+ jOriginalMsg << INT_PARAM("time", (long)dbei.timestamp) << CHAR_PARAM("text", (char *)dbei.pBlob);
+
+ jMsg = JSONNode();
+ jMsg << jOriginalMsg;
+
+ JSONNode jEdit;
+ jEdit << INT_PARAM("time", (long)edit_time) << WCHAR_PARAM("text", szContent);
+
+ JSONNode jEdits(JSON_ARRAY); jEdits.set_name("edits");
+ jEdits << jEdit;
+ jMsg << jEdits;
+ }
+
+ std::string newMsg = jMsg.write().c_str();
+ dbei.cbBlob = int(newMsg.size() + 1);
+ dbei.pBlob = (uint8_t*)newMsg.c_str();
+ db_event_edit(hContact, hEvent, &dbei);
+}
+
+void CSkypeProto::InitDBEvents()
+{
+ // custom event
+ DBEVENTTYPEDESCR dbEventType = {};
+ dbEventType.module = m_szModuleName;
+ dbEventType.flags = DETF_HISTORY | DETF_MSGWINDOW;
+ dbEventType.iconService = MODULE "/GetEventIcon";
+ dbEventType.textService = MODULE "/GetEventText";
+
+ for (auto &cur : g_SkypeDBTypes) {
+ dbEventType.eventType = cur.type;
+ dbEventType.descr = Translate(cur.name);
+ dbEventType.flags |= cur.flags;
+
+ DbEvent_RegisterType(&dbEventType);
+
+ dbEventType.flags &= (~cur.flags);
+ }
+}
diff --git a/protocols/SkypeWeb/src/skype_db.h b/protocols/SkypeWeb/src/skype_db.h index 8a0d58a049..ad9a7e2ff7 100644 --- a/protocols/SkypeWeb/src/skype_db.h +++ b/protocols/SkypeWeb/src/skype_db.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_events.cpp b/protocols/SkypeWeb/src/skype_events.cpp index a2f13e8187..49a93a53a0 100644 --- a/protocols/SkypeWeb/src/skype_events.cpp +++ b/protocols/SkypeWeb/src/skype_events.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_history_sync.cpp b/protocols/SkypeWeb/src/skype_history_sync.cpp index a034722bbd..ae72cdb920 100644 --- a/protocols/SkypeWeb/src/skype_history_sync.cpp +++ b/protocols/SkypeWeb/src/skype_history_sync.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_icons.cpp b/protocols/SkypeWeb/src/skype_icons.cpp index 8bd77f324a..15934a3d23 100644 --- a/protocols/SkypeWeb/src/skype_icons.cpp +++ b/protocols/SkypeWeb/src/skype_icons.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_login.cpp b/protocols/SkypeWeb/src/skype_login.cpp index d003c4ee71..94faff30d8 100644 --- a/protocols/SkypeWeb/src/skype_login.cpp +++ b/protocols/SkypeWeb/src/skype_login.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_menus.cpp b/protocols/SkypeWeb/src/skype_menus.cpp index 2d52b1a790..faf99da486 100644 --- a/protocols/SkypeWeb/src/skype_menus.cpp +++ b/protocols/SkypeWeb/src/skype_menus.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_menus.h b/protocols/SkypeWeb/src/skype_menus.h index 3e8c380d6f..b4e74aa6a3 100644 --- a/protocols/SkypeWeb/src/skype_menus.h +++ b/protocols/SkypeWeb/src/skype_menus.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_messages.cpp b/protocols/SkypeWeb/src/skype_messages.cpp index c6974a071c..01a0235caa 100644 --- a/protocols/SkypeWeb/src/skype_messages.cpp +++ b/protocols/SkypeWeb/src/skype_messages.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_oauth.cpp b/protocols/SkypeWeb/src/skype_oauth.cpp index dee5e92387..0a1f8908d3 100644 --- a/protocols/SkypeWeb/src/skype_oauth.cpp +++ b/protocols/SkypeWeb/src/skype_oauth.cpp @@ -1,165 +1,165 @@ -/* -Copyright (c) 2015-22 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" - -static std::string sub(const std::string &str, const char *start, const char *end) -{ - size_t i1 = str.find(start); - if (i1 == -1) - return ""; - - i1 += strlen(start); - size_t i2 = str.find(end, i1); - return (i2 == -1) ? "" : str.substr(i1, i2 - i1); -} - -void CSkypeProto::OnOAuthStart(NETLIBHTTPREQUEST *response, AsyncHttpRequest*) -{ - if (response == nullptr || response->pData == nullptr) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - - std::regex regex; - std::smatch match; - std::string content = response->pData; - - regex = "<input.+?type=\"hidden\".+?name=\"PPFT\".+?id=\"i0327\".+?value=\"(.+?)\".*?/>"; - - if (!std::regex_search(content, match, regex)) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - std::string PPFT = match[1]; - - std::map<std::string, std::string> scookies; - regex = "^(.+?)=(.*?);"; - - for (int i = 0; i < response->headersCount; i++) { - if (mir_strcmpi(response->headers[i].szName, "Set-Cookie")) - continue; - - content = response->headers[i].szValue; - if (std::regex_search(content, match, regex)) - scookies[match[1]] = match[2]; - } - - ptrA login(getStringA(SKYPE_SETTINGS_ID)); - ptrA password(getStringA(SKYPE_SETTINGS_PASSWORD)); - CMStringA mscookies(FORMAT, "MSPRequ=%s;MSPOK=%s;CkTst=G%lld;", scookies["MSPRequ"].c_str(), scookies["MSPOK"].c_str(), time(NULL)); - - cookies["MSPRequ"] = scookies["MSPRequ"]; - - PushRequest(new OAuthRequest(login, password, mscookies.c_str(), PPFT.c_str())); -} - -bool CSkypeProto::CheckOauth(const char *szResponse) -{ - std::string content = szResponse; - std::smatch match; - if (!std::regex_search(content, match, std::regex("<input.+?type=\"hidden\".+?name=\"t\".+?id=\"t\".+?value=\"(.+?)\".*?>"))) - if (!std::regex_search(content, match, std::regex("<input.+?type=\"hidden\".+?name=\"ipt\".+?id=\"ipt\".+?value=\"(.+?)\".*?>"))) - return false; - - std::string t = match[1]; - PushRequest(new OAuthRequest(t.c_str())); - return true; -} - -void CSkypeProto::OnOAuthConfirm(NETLIBHTTPREQUEST *response, AsyncHttpRequest *) -{ - if (response == nullptr || response->pData == nullptr) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - - if (CheckOauth(response->pData)) - return; - - std::string content = response->pData; - std::string PPFT = sub(content, "sFT:'", "'"); - std::string opid = sub(content, "opid=", "&"); - if (PPFT.empty() || opid.empty()) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - - std::regex regex("^(.+?=.*?;)"); - std::smatch match; - CMStringA mscookies; - - for (int i = 0; i < response->headersCount; i++) { - if (mir_strcmpi(response->headers[i].szName, "Set-Cookie")) - continue; - - content = response->headers[i].szValue; - if (std::regex_search(content, match, regex)) - mscookies.Append(match[1].str().c_str()); - } - - PushRequest(new OAuthRequest(mscookies.c_str(), PPFT.c_str(), opid.c_str())); -} - -void CSkypeProto::OnOAuthAuthorize(NETLIBHTTPREQUEST *response, AsyncHttpRequest*) -{ - if (response == nullptr || response->pData == nullptr) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - - if (!CheckOauth(response->pData)) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - } -} - -void CSkypeProto::OnOAuthEnd(NETLIBHTTPREQUEST *response, AsyncHttpRequest*) -{ - if (response == nullptr || response->pData == nullptr) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - - std::regex regex; - std::smatch match; - std::string content = response->pData; - - regex = "<input.+?type=\"hidden\".+?name=\"skypetoken\".+?value=\"(.+?)\".*?/>"; - if (!std::regex_search(content, match, regex)) { - ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN); - SetStatus(ID_STATUS_OFFLINE); - return; - } - std::string token = match[1]; - setString("TokenSecret", token.c_str()); - regex = "<input.+?type=\"hidden\".+?name=\"expires_in\".+?value=\"(.+?)\".*?/>"; - - if (std::regex_search(content, match, regex)) { - std::string expiresIn = match[1]; - int seconds = atoi(expiresIn.c_str()); - setDword("TokenExpiresIn", time(NULL) + seconds); - } - - OnLoginSuccess(); -} +/*
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+static std::string sub(const std::string &str, const char *start, const char *end)
+{
+ size_t i1 = str.find(start);
+ if (i1 == -1)
+ return "";
+
+ i1 += strlen(start);
+ size_t i2 = str.find(end, i1);
+ return (i2 == -1) ? "" : str.substr(i1, i2 - i1);
+}
+
+void CSkypeProto::OnOAuthStart(NETLIBHTTPREQUEST *response, AsyncHttpRequest*)
+{
+ if (response == nullptr || response->pData == nullptr) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ std::regex regex;
+ std::smatch match;
+ std::string content = response->pData;
+
+ regex = "<input.+?type=\"hidden\".+?name=\"PPFT\".+?id=\"i0327\".+?value=\"(.+?)\".*?/>";
+
+ if (!std::regex_search(content, match, regex)) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+ std::string PPFT = match[1];
+
+ std::map<std::string, std::string> scookies;
+ regex = "^(.+?)=(.*?);";
+
+ for (int i = 0; i < response->headersCount; i++) {
+ if (mir_strcmpi(response->headers[i].szName, "Set-Cookie"))
+ continue;
+
+ content = response->headers[i].szValue;
+ if (std::regex_search(content, match, regex))
+ scookies[match[1]] = match[2];
+ }
+
+ ptrA login(getStringA(SKYPE_SETTINGS_ID));
+ ptrA password(getStringA(SKYPE_SETTINGS_PASSWORD));
+ CMStringA mscookies(FORMAT, "MSPRequ=%s;MSPOK=%s;CkTst=G%lld;", scookies["MSPRequ"].c_str(), scookies["MSPOK"].c_str(), time(NULL));
+
+ cookies["MSPRequ"] = scookies["MSPRequ"];
+
+ PushRequest(new OAuthRequest(login, password, mscookies.c_str(), PPFT.c_str()));
+}
+
+bool CSkypeProto::CheckOauth(const char *szResponse)
+{
+ std::string content = szResponse;
+ std::smatch match;
+ if (!std::regex_search(content, match, std::regex("<input.+?type=\"hidden\".+?name=\"t\".+?id=\"t\".+?value=\"(.+?)\".*?>")))
+ if (!std::regex_search(content, match, std::regex("<input.+?type=\"hidden\".+?name=\"ipt\".+?id=\"ipt\".+?value=\"(.+?)\".*?>")))
+ return false;
+
+ std::string t = match[1];
+ PushRequest(new OAuthRequest(t.c_str()));
+ return true;
+}
+
+void CSkypeProto::OnOAuthConfirm(NETLIBHTTPREQUEST *response, AsyncHttpRequest *)
+{
+ if (response == nullptr || response->pData == nullptr) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ if (CheckOauth(response->pData))
+ return;
+
+ std::string content = response->pData;
+ std::string PPFT = sub(content, "sFT:'", "'");
+ std::string opid = sub(content, "opid=", "&");
+ if (PPFT.empty() || opid.empty()) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ std::regex regex("^(.+?=.*?;)");
+ std::smatch match;
+ CMStringA mscookies;
+
+ for (int i = 0; i < response->headersCount; i++) {
+ if (mir_strcmpi(response->headers[i].szName, "Set-Cookie"))
+ continue;
+
+ content = response->headers[i].szValue;
+ if (std::regex_search(content, match, regex))
+ mscookies.Append(match[1].str().c_str());
+ }
+
+ PushRequest(new OAuthRequest(mscookies.c_str(), PPFT.c_str(), opid.c_str()));
+}
+
+void CSkypeProto::OnOAuthAuthorize(NETLIBHTTPREQUEST *response, AsyncHttpRequest*)
+{
+ if (response == nullptr || response->pData == nullptr) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ if (!CheckOauth(response->pData)) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ }
+}
+
+void CSkypeProto::OnOAuthEnd(NETLIBHTTPREQUEST *response, AsyncHttpRequest*)
+{
+ if (response == nullptr || response->pData == nullptr) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ std::regex regex;
+ std::smatch match;
+ std::string content = response->pData;
+
+ regex = "<input.+?type=\"hidden\".+?name=\"skypetoken\".+?value=\"(.+?)\".*?/>";
+ if (!std::regex_search(content, match, regex)) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGIN_ERROR_UNKNOWN);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+ std::string token = match[1];
+ setString("TokenSecret", token.c_str());
+ regex = "<input.+?type=\"hidden\".+?name=\"expires_in\".+?value=\"(.+?)\".*?/>";
+
+ if (std::regex_search(content, match, regex)) {
+ std::string expiresIn = match[1];
+ int seconds = atoi(expiresIn.c_str());
+ setDword("TokenExpiresIn", time(NULL) + seconds);
+ }
+
+ OnLoginSuccess();
+}
diff --git a/protocols/SkypeWeb/src/skype_options.cpp b/protocols/SkypeWeb/src/skype_options.cpp index baaff8b7ab..07f15c215a 100644 --- a/protocols/SkypeWeb/src/skype_options.cpp +++ b/protocols/SkypeWeb/src/skype_options.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_polling.cpp b/protocols/SkypeWeb/src/skype_polling.cpp index b868e18bcd..0000de3b20 100644 --- a/protocols/SkypeWeb/src/skype_polling.cpp +++ b/protocols/SkypeWeb/src/skype_polling.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_profile.cpp b/protocols/SkypeWeb/src/skype_profile.cpp index 5612dc9619..f8fbaa2fbe 100644 --- a/protocols/SkypeWeb/src/skype_profile.cpp +++ b/protocols/SkypeWeb/src/skype_profile.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_proto.cpp b/protocols/SkypeWeb/src/skype_proto.cpp index d99fe64b61..5eb23d11ca 100644 --- a/protocols/SkypeWeb/src/skype_proto.cpp +++ b/protocols/SkypeWeb/src/skype_proto.cpp @@ -1,343 +1,343 @@ -/* -Copyright (c) 2015-22 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" - -CSkypeProto::CSkypeProto(const char* protoName, const wchar_t* userName) : - PROTO<CSkypeProto>(protoName, userName), - m_PopupClasses(1), - m_OutMessages(3, PtrKeySortT), - m_bThreadsTerminated(false), - m_impl(*this), - m_requests(1), - bAutoHistorySync(this, "AutoSync", true), - bMarkAllAsUnread(this, "MarkMesUnread", true), - bUseHostnameAsPlace(this, "UseHostName", true), - bUseBBCodes(this, "UseBBCodes", true), - bUseServerTime(this, "UseServerTime", false), - wstrCListGroup(this, SKYPE_SETTINGS_GROUP, L"Skype"), - wstrPlace(this, "Place", L"") -{ - NETLIBUSER nlu = {}; - nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE; - nlu.szDescriptiveName.w = m_tszUserName; - nlu.szSettingsModule = m_szModuleName; - m_hNetlibUser = Netlib_RegisterUser(&nlu); - - CreateProtoService(PS_CREATEACCMGRUI, &CSkypeProto::OnAccountManagerInit); - CreateProtoService(PS_GETAVATARINFO, &CSkypeProto::SvcGetAvatarInfo); - CreateProtoService(PS_GETAVATARCAPS, &CSkypeProto::SvcGetAvatarCaps); - CreateProtoService(PS_GETMYAVATAR, &CSkypeProto::SvcGetMyAvatar); - CreateProtoService(PS_SETMYAVATAR, &CSkypeProto::SvcSetMyAvatar); - - CreateProtoService(PS_MENU_REQAUTH, &CSkypeProto::OnRequestAuth); - CreateProtoService(PS_MENU_GRANTAUTH, &CSkypeProto::OnGrantAuth); - CreateProtoService(PS_MENU_LOADHISTORY, &CSkypeProto::GetContactHistory); - - HookProtoEvent(ME_OPT_INITIALISE, &CSkypeProto::OnOptionsInit); - HookProtoEvent(ME_DB_EVENT_MARKED_READ, &CSkypeProto::OnDbEventRead); - - m_tszAvatarFolder = std::wstring(VARSW(L"%miranda_avatarcache%")) + L"\\" + m_tszUserName; - CreateDirectoryTreeW(m_tszAvatarFolder.c_str()); - - //sounds - g_plugin.addSound("skype_inc_call", L"SkypeWeb", LPGENW("Incoming call")); - g_plugin.addSound("skype_call_canceled", L"SkypeWeb", LPGENW("Incoming call canceled")); - - m_hPollingThread = ForkThreadEx(&CSkypeProto::PollingThread, NULL, NULL); - - m_szSkypename = getMStringA(SKYPE_SETTINGS_ID); - if (m_szSkypename.IsEmpty()) { - m_szSkypename = getMStringA(SKYPE_SETTINGS_LOGIN); - if (!m_szSkypename.IsEmpty()) { // old settings format, need to update all settings - m_szSkypename.Insert(0, "8:"); - setString(SKYPE_SETTINGS_ID, m_szSkypename); - - for (auto &hContact : AccContacts()) { - CMStringA id(ptrA(getUStringA(hContact, "Skypename"))); - if (!id.IsEmpty()) - setString(hContact, SKYPE_SETTINGS_ID, (isChatRoom(hContact)) ? "19:"+id : "8:"+id); - - ptrW wszNick(getWStringA(hContact, "Nick")); - if (wszNick == nullptr) - setUString(hContact, "Nick", id); - - delSetting(hContact, "Skypename"); - } - } - } - - InitGroupChatModule(); -} - -CSkypeProto::~CSkypeProto() -{ - StopQueue(); - if (m_hRequestQueueThread) { - WaitForSingleObject(m_hRequestQueueThread, INFINITE); - m_hRequestQueueThread = nullptr; - } - - UninitPopups(); - - if (m_hPollingThread) { - WaitForSingleObject(m_hPollingThread, INFINITE); - m_hPollingThread = nullptr; - } -} - -void CSkypeProto::OnModulesLoaded() -{ - setAllContactStatuses(ID_STATUS_OFFLINE, false); - - HookProtoEvent(ME_MSG_PRECREATEEVENT, &CSkypeProto::OnPreCreateMessage); - - InitDBEvents(); - InitPopups(); -} - -void CSkypeProto::OnShutdown() -{ - debugLogA(__FUNCTION__); - - StopQueue(); - - m_bThreadsTerminated = true; - - m_hPollingEvent.Set(); - m_hTrouterEvent.Set(); -} - -INT_PTR CSkypeProto::GetCaps(int type, MCONTACT) -{ - switch (type) { - case PFLAGNUM_1: - return PF1_IM | PF1_AUTHREQ | PF1_CHAT | PF1_BASICSEARCH | PF1_MODEMSG | PF1_FILE; - case PFLAGNUM_2: - return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_HEAVYDND; - case PFLAGNUM_3: - return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_HEAVYDND; - case PFLAGNUM_4: - return PF4_NOAUTHDENYREASON | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_IMSENDOFFLINE | PF4_OFFLINEFILES | PF4_SERVERMSGID; - case PFLAG_UNIQUEIDTEXT: - return (INT_PTR)TranslateT("Skypename"); - } - return 0; -} - -int CSkypeProto::SetAwayMsg(int, const wchar_t *msg) -{ - if (IsOnline()) - PushRequest(new SetStatusMsgRequest(msg ? T2Utf(msg) : "")); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSkypeProto::OnReceiveAwayMsg(NETLIBHTTPREQUEST *response, AsyncHttpRequest *pRequest) -{ - JsonReply reply(response); - if (reply.error()) - return; - - MCONTACT hContact = DWORD_PTR(pRequest->pUserInfo); - auto &root = reply.data(); - if (JSONNode &mood = root["mood"]) { - CMStringW str = mood.as_mstring(); - ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)str.c_str()); - } - else { - ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, 0); - } -} - -HANDLE CSkypeProto::GetAwayMsg(MCONTACT hContact) -{ - auto *pReq = new GetProfileRequest(this, hContact); - pReq->m_pFunc = &CSkypeProto::OnReceiveAwayMsg; - return (HANDLE)1; -} - -MCONTACT CSkypeProto::AddToList(int, PROTOSEARCHRESULT *psr) -{ - debugLogA(__FUNCTION__); - - if (psr->id.a == nullptr) - return NULL; - - MCONTACT hContact; - if (psr->flags & PSR_UNICODE) - hContact = AddContact(T2Utf(psr->id.w), T2Utf(psr->nick.w)); - else - hContact = AddContact(psr->id.a, psr->nick.a); - - return hContact; -} - -MCONTACT CSkypeProto::AddToListByEvent(int, int, MEVENT hDbEvent) -{ - debugLogA(__FUNCTION__); - - DB::EventInfo dbei; - dbei.cbBlob = -1; - if (db_event_get(hDbEvent, &dbei)) - return NULL; - if (mir_strcmp(dbei.szModule, m_szModuleName)) - return NULL; - if (dbei.eventType != EVENTTYPE_AUTHREQUEST) - return NULL; - - DB::AUTH_BLOB blob(dbei.pBlob); - return AddContact(blob.get_email(), blob.get_nick()); -} - -int CSkypeProto::Authorize(MEVENT hDbEvent) -{ - MCONTACT hContact = GetContactFromAuthEvent(hDbEvent); - if (hContact == INVALID_CONTACT_ID) - return 1; - - PushRequest(new AuthAcceptRequest(getId(hContact))); - return 0; -} - -int CSkypeProto::AuthDeny(MEVENT hDbEvent, const wchar_t*) -{ - MCONTACT hContact = GetContactFromAuthEvent(hDbEvent); - if (hContact == INVALID_CONTACT_ID) - return 1; - - PushRequest(new AuthDeclineRequest(getId(hContact))); - return 0; -} - -int CSkypeProto::AuthRecv(MCONTACT, PROTORECVEVENT* pre) -{ - return Proto_AuthRecv(m_szModuleName, pre); -} - -int CSkypeProto::AuthRequest(MCONTACT hContact, const wchar_t *szMessage) -{ - if (hContact == INVALID_CONTACT_ID) - return 1; - - PushRequest(new AddContactRequest(getId(hContact), T2Utf(szMessage))); - return 0; -} - -int CSkypeProto::GetInfo(MCONTACT hContact, int) -{ - if (isChatRoom(hContact)) - return 1; - - PushRequest(new GetProfileRequest(this, hContact)); - return 0; -} - -int CSkypeProto::SendMsg(MCONTACT hContact, int flags, const char *msg) -{ - return OnSendMessage(hContact, flags, msg); -} - -int CSkypeProto::SetStatus(int iNewStatus) -{ - if (iNewStatus == m_iDesiredStatus) - return 0; - - switch (iNewStatus) { - case ID_STATUS_FREECHAT: iNewStatus = ID_STATUS_ONLINE; break; - case ID_STATUS_NA: iNewStatus = ID_STATUS_AWAY; break; - case ID_STATUS_OCCUPIED: iNewStatus = ID_STATUS_DND; break; - } - - debugLogA(__FUNCTION__ ": changing status from %i to %i", m_iStatus, iNewStatus); - - int old_status = m_iStatus; - m_iDesiredStatus = iNewStatus; - - if (iNewStatus == ID_STATUS_OFFLINE) { - if (m_iStatus > ID_STATUS_CONNECTING + 1 && m_szId) - PushRequest(new DeleteEndpointRequest(this)); - - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - // logout - StopQueue(); - - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, ID_STATUS_OFFLINE); - - m_impl.m_heartBeat.StopSafe(); - - if (!Miranda_IsTerminated()) - setAllContactStatuses(ID_STATUS_OFFLINE, false); - return 0; - } - else { - if (old_status == ID_STATUS_CONNECTING) - return 0; - - if (old_status == ID_STATUS_OFFLINE && m_iStatus == ID_STATUS_OFFLINE) - Login(); - else - PushRequest(new SetStatusRequest(MirandaToSkypeStatus(m_iDesiredStatus))); - } - - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - return 0; -} - -int CSkypeProto::UserIsTyping(MCONTACT hContact, int type) -{ - PushRequest(new SendTypingRequest(getId(hContact), type)); - return 0; -} - -int CSkypeProto::RecvContacts(MCONTACT hContact, PROTORECVEVENT* pre) -{ - PROTOSEARCHRESULT **isrList = (PROTOSEARCHRESULT**)pre->szMessage; - - int nCount = *((LPARAM*)pre->lParam); - char* szMessageId = ((char*)pre->lParam + sizeof(LPARAM)); - - //if (GetMessageFromDb(hContact, szMessageId, pre->timestamp)) return 0; - - uint32_t cbBlob = 0; - for (int i = 0; i < nCount; i++) - cbBlob += int(/*mir_wstrlen(isrList[i]->nick.w)*/0 + 2 + mir_wstrlen(isrList[i]->id.w) + mir_strlen(szMessageId)); - - uint8_t *pBlob = (uint8_t*)mir_calloc(cbBlob); - uint8_t *pCurBlob = pBlob; - - for (int i = 0; i < nCount; i++) { - //mir_strcpy((char*)pCurBlob, _T2A(isrList[i]->nick.w)); - pCurBlob += mir_strlen((PCHAR)pCurBlob) + 1; - - mir_strcpy((char*)pCurBlob, _T2A(isrList[i]->id.w)); - pCurBlob += mir_strlen((char*)pCurBlob) + 1; - } - - DBEVENTINFO dbei = {}; - dbei.szModule = m_szModuleName; - dbei.timestamp = pre->timestamp; - dbei.eventType = EVENTTYPE_CONTACTS; - dbei.cbBlob = cbBlob; - dbei.pBlob = pBlob; - dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0; - db_event_add(hContact, &dbei); - - mir_free(pBlob); - return 0; -} +/*
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+CSkypeProto::CSkypeProto(const char* protoName, const wchar_t* userName) :
+ PROTO<CSkypeProto>(protoName, userName),
+ m_PopupClasses(1),
+ m_OutMessages(3, PtrKeySortT),
+ m_bThreadsTerminated(false),
+ m_impl(*this),
+ m_requests(1),
+ bAutoHistorySync(this, "AutoSync", true),
+ bMarkAllAsUnread(this, "MarkMesUnread", true),
+ bUseHostnameAsPlace(this, "UseHostName", true),
+ bUseBBCodes(this, "UseBBCodes", true),
+ bUseServerTime(this, "UseServerTime", false),
+ wstrCListGroup(this, SKYPE_SETTINGS_GROUP, L"Skype"),
+ wstrPlace(this, "Place", L"")
+{
+ NETLIBUSER nlu = {};
+ nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE;
+ nlu.szDescriptiveName.w = m_tszUserName;
+ nlu.szSettingsModule = m_szModuleName;
+ m_hNetlibUser = Netlib_RegisterUser(&nlu);
+
+ CreateProtoService(PS_CREATEACCMGRUI, &CSkypeProto::OnAccountManagerInit);
+ CreateProtoService(PS_GETAVATARINFO, &CSkypeProto::SvcGetAvatarInfo);
+ CreateProtoService(PS_GETAVATARCAPS, &CSkypeProto::SvcGetAvatarCaps);
+ CreateProtoService(PS_GETMYAVATAR, &CSkypeProto::SvcGetMyAvatar);
+ CreateProtoService(PS_SETMYAVATAR, &CSkypeProto::SvcSetMyAvatar);
+
+ CreateProtoService(PS_MENU_REQAUTH, &CSkypeProto::OnRequestAuth);
+ CreateProtoService(PS_MENU_GRANTAUTH, &CSkypeProto::OnGrantAuth);
+ CreateProtoService(PS_MENU_LOADHISTORY, &CSkypeProto::GetContactHistory);
+
+ HookProtoEvent(ME_OPT_INITIALISE, &CSkypeProto::OnOptionsInit);
+ HookProtoEvent(ME_DB_EVENT_MARKED_READ, &CSkypeProto::OnDbEventRead);
+
+ m_tszAvatarFolder = std::wstring(VARSW(L"%miranda_avatarcache%")) + L"\\" + m_tszUserName;
+ CreateDirectoryTreeW(m_tszAvatarFolder.c_str());
+
+ //sounds
+ g_plugin.addSound("skype_inc_call", L"SkypeWeb", LPGENW("Incoming call"));
+ g_plugin.addSound("skype_call_canceled", L"SkypeWeb", LPGENW("Incoming call canceled"));
+
+ m_hPollingThread = ForkThreadEx(&CSkypeProto::PollingThread, NULL, NULL);
+
+ m_szSkypename = getMStringA(SKYPE_SETTINGS_ID);
+ if (m_szSkypename.IsEmpty()) {
+ m_szSkypename = getMStringA(SKYPE_SETTINGS_LOGIN);
+ if (!m_szSkypename.IsEmpty()) { // old settings format, need to update all settings
+ m_szSkypename.Insert(0, "8:");
+ setString(SKYPE_SETTINGS_ID, m_szSkypename);
+
+ for (auto &hContact : AccContacts()) {
+ CMStringA id(ptrA(getUStringA(hContact, "Skypename")));
+ if (!id.IsEmpty())
+ setString(hContact, SKYPE_SETTINGS_ID, (isChatRoom(hContact)) ? "19:"+id : "8:"+id);
+
+ ptrW wszNick(getWStringA(hContact, "Nick"));
+ if (wszNick == nullptr)
+ setUString(hContact, "Nick", id);
+
+ delSetting(hContact, "Skypename");
+ }
+ }
+ }
+
+ InitGroupChatModule();
+}
+
+CSkypeProto::~CSkypeProto()
+{
+ StopQueue();
+ if (m_hRequestQueueThread) {
+ WaitForSingleObject(m_hRequestQueueThread, INFINITE);
+ m_hRequestQueueThread = nullptr;
+ }
+
+ UninitPopups();
+
+ if (m_hPollingThread) {
+ WaitForSingleObject(m_hPollingThread, INFINITE);
+ m_hPollingThread = nullptr;
+ }
+}
+
+void CSkypeProto::OnModulesLoaded()
+{
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
+
+ HookProtoEvent(ME_MSG_PRECREATEEVENT, &CSkypeProto::OnPreCreateMessage);
+
+ InitDBEvents();
+ InitPopups();
+}
+
+void CSkypeProto::OnShutdown()
+{
+ debugLogA(__FUNCTION__);
+
+ StopQueue();
+
+ m_bThreadsTerminated = true;
+
+ m_hPollingEvent.Set();
+ m_hTrouterEvent.Set();
+}
+
+INT_PTR CSkypeProto::GetCaps(int type, MCONTACT)
+{
+ switch (type) {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_AUTHREQ | PF1_CHAT | PF1_BASICSEARCH | PF1_MODEMSG | PF1_FILE;
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_HEAVYDND;
+ case PFLAGNUM_3:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_HEAVYDND;
+ case PFLAGNUM_4:
+ return PF4_NOAUTHDENYREASON | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_IMSENDOFFLINE | PF4_OFFLINEFILES | PF4_SERVERMSGID;
+ case PFLAG_UNIQUEIDTEXT:
+ return (INT_PTR)TranslateT("Skypename");
+ }
+ return 0;
+}
+
+int CSkypeProto::SetAwayMsg(int, const wchar_t *msg)
+{
+ if (IsOnline())
+ PushRequest(new SetStatusMsgRequest(msg ? T2Utf(msg) : ""));
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSkypeProto::OnReceiveAwayMsg(NETLIBHTTPREQUEST *response, AsyncHttpRequest *pRequest)
+{
+ JsonReply reply(response);
+ if (reply.error())
+ return;
+
+ MCONTACT hContact = DWORD_PTR(pRequest->pUserInfo);
+ auto &root = reply.data();
+ if (JSONNode &mood = root["mood"]) {
+ CMStringW str = mood.as_mstring();
+ ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)str.c_str());
+ }
+ else {
+ ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, 0);
+ }
+}
+
+HANDLE CSkypeProto::GetAwayMsg(MCONTACT hContact)
+{
+ auto *pReq = new GetProfileRequest(this, hContact);
+ pReq->m_pFunc = &CSkypeProto::OnReceiveAwayMsg;
+ return (HANDLE)1;
+}
+
+MCONTACT CSkypeProto::AddToList(int, PROTOSEARCHRESULT *psr)
+{
+ debugLogA(__FUNCTION__);
+
+ if (psr->id.a == nullptr)
+ return NULL;
+
+ MCONTACT hContact;
+ if (psr->flags & PSR_UNICODE)
+ hContact = AddContact(T2Utf(psr->id.w), T2Utf(psr->nick.w));
+ else
+ hContact = AddContact(psr->id.a, psr->nick.a);
+
+ return hContact;
+}
+
+MCONTACT CSkypeProto::AddToListByEvent(int, int, MEVENT hDbEvent)
+{
+ debugLogA(__FUNCTION__);
+
+ DB::EventInfo dbei;
+ dbei.cbBlob = -1;
+ if (db_event_get(hDbEvent, &dbei))
+ return NULL;
+ if (mir_strcmp(dbei.szModule, m_szModuleName))
+ return NULL;
+ if (dbei.eventType != EVENTTYPE_AUTHREQUEST)
+ return NULL;
+
+ DB::AUTH_BLOB blob(dbei.pBlob);
+ return AddContact(blob.get_email(), blob.get_nick());
+}
+
+int CSkypeProto::Authorize(MEVENT hDbEvent)
+{
+ MCONTACT hContact = GetContactFromAuthEvent(hDbEvent);
+ if (hContact == INVALID_CONTACT_ID)
+ return 1;
+
+ PushRequest(new AuthAcceptRequest(getId(hContact)));
+ return 0;
+}
+
+int CSkypeProto::AuthDeny(MEVENT hDbEvent, const wchar_t*)
+{
+ MCONTACT hContact = GetContactFromAuthEvent(hDbEvent);
+ if (hContact == INVALID_CONTACT_ID)
+ return 1;
+
+ PushRequest(new AuthDeclineRequest(getId(hContact)));
+ return 0;
+}
+
+int CSkypeProto::AuthRecv(MCONTACT, PROTORECVEVENT* pre)
+{
+ return Proto_AuthRecv(m_szModuleName, pre);
+}
+
+int CSkypeProto::AuthRequest(MCONTACT hContact, const wchar_t *szMessage)
+{
+ if (hContact == INVALID_CONTACT_ID)
+ return 1;
+
+ PushRequest(new AddContactRequest(getId(hContact), T2Utf(szMessage)));
+ return 0;
+}
+
+int CSkypeProto::GetInfo(MCONTACT hContact, int)
+{
+ if (isChatRoom(hContact))
+ return 1;
+
+ PushRequest(new GetProfileRequest(this, hContact));
+ return 0;
+}
+
+int CSkypeProto::SendMsg(MCONTACT hContact, int flags, const char *msg)
+{
+ return OnSendMessage(hContact, flags, msg);
+}
+
+int CSkypeProto::SetStatus(int iNewStatus)
+{
+ if (iNewStatus == m_iDesiredStatus)
+ return 0;
+
+ switch (iNewStatus) {
+ case ID_STATUS_FREECHAT: iNewStatus = ID_STATUS_ONLINE; break;
+ case ID_STATUS_NA: iNewStatus = ID_STATUS_AWAY; break;
+ case ID_STATUS_OCCUPIED: iNewStatus = ID_STATUS_DND; break;
+ }
+
+ debugLogA(__FUNCTION__ ": changing status from %i to %i", m_iStatus, iNewStatus);
+
+ int old_status = m_iStatus;
+ m_iDesiredStatus = iNewStatus;
+
+ if (iNewStatus == ID_STATUS_OFFLINE) {
+ if (m_iStatus > ID_STATUS_CONNECTING + 1 && m_szId)
+ PushRequest(new DeleteEndpointRequest(this));
+
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+ // logout
+ StopQueue();
+
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, ID_STATUS_OFFLINE);
+
+ m_impl.m_heartBeat.StopSafe();
+
+ if (!Miranda_IsTerminated())
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
+ return 0;
+ }
+ else {
+ if (old_status == ID_STATUS_CONNECTING)
+ return 0;
+
+ if (old_status == ID_STATUS_OFFLINE && m_iStatus == ID_STATUS_OFFLINE)
+ Login();
+ else
+ PushRequest(new SetStatusRequest(MirandaToSkypeStatus(m_iDesiredStatus)));
+ }
+
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+ return 0;
+}
+
+int CSkypeProto::UserIsTyping(MCONTACT hContact, int type)
+{
+ PushRequest(new SendTypingRequest(getId(hContact), type));
+ return 0;
+}
+
+int CSkypeProto::RecvContacts(MCONTACT hContact, PROTORECVEVENT* pre)
+{
+ PROTOSEARCHRESULT **isrList = (PROTOSEARCHRESULT**)pre->szMessage;
+
+ int nCount = *((LPARAM*)pre->lParam);
+ char* szMessageId = ((char*)pre->lParam + sizeof(LPARAM));
+
+ //if (GetMessageFromDb(hContact, szMessageId, pre->timestamp)) return 0;
+
+ uint32_t cbBlob = 0;
+ for (int i = 0; i < nCount; i++)
+ cbBlob += int(/*mir_wstrlen(isrList[i]->nick.w)*/0 + 2 + mir_wstrlen(isrList[i]->id.w) + mir_strlen(szMessageId));
+
+ uint8_t *pBlob = (uint8_t*)mir_calloc(cbBlob);
+ uint8_t *pCurBlob = pBlob;
+
+ for (int i = 0; i < nCount; i++) {
+ //mir_strcpy((char*)pCurBlob, _T2A(isrList[i]->nick.w));
+ pCurBlob += mir_strlen((PCHAR)pCurBlob) + 1;
+
+ mir_strcpy((char*)pCurBlob, _T2A(isrList[i]->id.w));
+ pCurBlob += mir_strlen((char*)pCurBlob) + 1;
+ }
+
+ DBEVENTINFO dbei = {};
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = pre->timestamp;
+ dbei.eventType = EVENTTYPE_CONTACTS;
+ dbei.cbBlob = cbBlob;
+ dbei.pBlob = pBlob;
+ dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0;
+ db_event_add(hContact, &dbei);
+
+ mir_free(pBlob);
+ return 0;
+}
diff --git a/protocols/SkypeWeb/src/skype_proto.h b/protocols/SkypeWeb/src/skype_proto.h index 4630cde68f..3517ae742a 100644 --- a/protocols/SkypeWeb/src/skype_proto.h +++ b/protocols/SkypeWeb/src/skype_proto.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_search.cpp b/protocols/SkypeWeb/src/skype_search.cpp index e47eba7243..51a4952021 100644 --- a/protocols/SkypeWeb/src/skype_search.cpp +++ b/protocols/SkypeWeb/src/skype_search.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_trouter.cpp b/protocols/SkypeWeb/src/skype_trouter.cpp index bb8a74385b..65e38d4d41 100644 --- a/protocols/SkypeWeb/src/skype_trouter.cpp +++ b/protocols/SkypeWeb/src/skype_trouter.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/skype_utils.cpp b/protocols/SkypeWeb/src/skype_utils.cpp index 903309054b..fc7a4bb6b1 100644 --- a/protocols/SkypeWeb/src/skype_utils.cpp +++ b/protocols/SkypeWeb/src/skype_utils.cpp @@ -1,679 +1,679 @@ -/* -Copyright (c) 2015-22 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" - -#pragma warning(disable:4566) - -time_t CSkypeProto::IsoToUnixTime(const std::string &stamp) -{ - char date[9]; - int i, y; - - if (stamp.empty()) - return 0; - - char *p = NEWSTR_ALLOCA(stamp.c_str()); - - // skip '-' chars - int si = 0, sj = 0; - while (true) { - if (p[si] == '-') - si++; - else if (!(p[sj++] = p[si++])) - break; - } - - // Get the date part - for (i = 0; *p != '\0' && i < 8 && isdigit(*p); p++, i++) - date[i] = *p; - - // Parse year - if (i == 6) { - // 2-digit year (1970-2069) - y = (date[0] - '0') * 10 + (date[1] - '0'); - if (y < 70) y += 100; - } - else if (i == 8) { - // 4-digit year - y = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0'; - y -= 1900; - } - else return 0; - - struct tm timestamp; - timestamp.tm_year = y; - - // Parse month - timestamp.tm_mon = (date[i - 4] - '0') * 10 + date[i - 3] - '0' - 1; - - // Parse date - timestamp.tm_mday = (date[i - 2] - '0') * 10 + date[i - 1] - '0'; - - // Skip any date/time delimiter - for (; *p != '\0' && !isdigit(*p); p++); - - // Parse time - if (sscanf(p, "%d:%d:%d", ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3) - return (time_t)0; - - timestamp.tm_isdst = 0; // DST is already present in _timezone below - time_t t = mktime(×tamp); - - _tzset(); - t -= _timezone; - return (t >= 0) ? t : 0; -} - -struct HtmlEntity -{ - const char *entity; - const char *symbol; -}; - -const HtmlEntity htmlEntities[] = -{ - { "AElig", "\u00C6" }, - { "Aacute", "\u00C1" }, - { "Acirc", "\u00C2" }, - { "Agrave", "\u00C0" }, - { "Alpha", "\u0391" }, - { "Aring", "\u00C5" }, - { "Atilde", "\u00C3" }, - { "Auml", "\u00C4" }, - { "Beta", "\u0392" }, - { "Ccedil", "\u00C7" }, - { "Chi", "\u03A7" }, - { "Dagger", "‡" }, - { "Delta", "\u0394" }, - { "ETH", "\u00D0" }, - { "Eacute", "\u00C9" }, - { "Ecirc", "\u00CA" }, - { "Egrave", "\u00C8" }, - { "Epsilon", "\u0395" }, - { "Eta", "\u0397" }, - { "Euml", "\u00CB" }, - { "Gamma", "\u0393" }, - { "Iacute", "\u00CD" }, - { "Icirc", "\u00CE" }, - { "Igrave", "\u00CC" }, - { "Iota", "\u0399" }, - { "Iuml", "\u00CF" }, - { "Kappa", "\u039A" }, - { "Lambda", "\u039B" }, - { "Mu", "\u039C" }, - { "Ntilde", "\u00D1" }, - { "Nu", "\u039D" }, - { "OElig", "\u0152" }, - { "Oacute", "\u00D3" }, - { "Ocirc", "\u00D4" }, - { "Ograve", "\u00D2" }, - { "Omega", "\u03A9" }, - { "Omicron", "\u039F" }, - { "Oslash", "\u00D8" }, - { "Otilde", "\u00D5" }, - { "Ouml", "\u00D6" }, - { "Phi", "\u03A6" }, - { "Pi", "\u03A0" }, - { "Prime", "\u2033" }, - { "Psi", "\u03A8" }, - { "Rho", "\u03A1" }, - { "Scaron", "Š" }, - { "Sigma", "Σ" }, - { "THORN", "Þ" }, - { "Tau", "Τ" }, - { "Theta", "Θ" }, - { "Uacute", "Ú" }, - { "Ucirc", "Û" }, - { "Ugrave", "Ù" }, - { "Upsilon", "Υ" }, - { "Uuml", "Ü" }, - { "Xi", "Ξ" }, - { "Yacute", "Ý" }, - { "Yuml", "Ÿ" }, - { "Zeta", "Ζ" }, - { "aacute", "á" }, - { "acirc", "â" }, - { "acute", "´" }, - { "aelig", "æ" }, - { "agrave", "à" }, - { "alefsym", "ℵ" }, - { "alpha", "α" }, - { "amp", "&" }, - { "and", "∧" }, - { "ang", "∠" }, - { "apos", "'" }, - { "aring", "å" }, - { "asymp", "≈" }, - { "atilde", "ã" }, - { "auml", "ä" }, - { "bdquo", "„" }, - { "beta", "β" }, - { "brvbar", "¦" }, - { "bull", "•" }, - { "cap", "∩" }, - { "ccedil", "ç" }, - { "cedil", "¸" }, - { "cent", "¢" }, - { "chi", "χ" }, - { "circ", "ˆ" }, - { "clubs", "♣" }, - { "cong", "≅" }, - { "copy", "©" }, - { "crarr", "↵" }, - { "cup", "∪" }, - { "curren", "¤" }, - { "dArr", "⇓" }, - { "dagger", "†" }, - { "darr", "↓" }, - { "deg", "°" }, - { "delta", "δ" }, - { "diams", "♦" }, - { "divide", "÷" }, - { "eacute", "é" }, - { "ecirc", "ê" }, - { "egrave", "è" }, - { "empty", "∅" }, - { "emsp", " " }, - { "ensp", " " }, - { "epsilon", "ε" }, - { "equiv", "≡" }, - { "eta", "η" }, - { "eth", "ð" }, - { "euml", "ë" }, - { "euro", "€" }, - { "exist", "∃" }, - { "fnof", "ƒ" }, - { "forall", "∀" }, - { "frac12", "½" }, - { "frac14", "¼" }, - { "frac34", "¾" }, - { "frasl", "⁄" }, - { "gamma", "γ" }, - { "ge", "≥" }, - { "gt", ">" }, - { "hArr", "⇔" }, - { "harr", "↔" }, - { "hearts", "♥" }, - { "hellip", "…" }, - { "iacute", "í" }, - { "icirc", "î" }, - { "iexcl", "¡" }, - { "igrave", "ì" }, - { "image", "ℑ" }, - { "infin", "∞" }, - { "int", "∫" }, - { "iota", "ι" }, - { "iquest", "¿" }, - { "isin", "∈" }, - { "iuml", "ï" }, - { "kappa", "κ" }, - { "lArr", "⇐" }, - { "lambda", "λ" }, - { "lang", "〈" }, - { "laquo", "«" }, - { "larr", "←" }, - { "lceil", "⌈" }, - { "ldquo", "“" }, - { "le", "≤" }, - { "lfloor", "⌊" }, - { "lowast", "∗" }, - { "loz", "◊" }, - { "lrm", "\xE2\x80\x8E" }, - { "lsaquo", "‹" }, - { "lsquo", "‘" }, - { "lt", "<" }, - { "macr", "¯" }, - { "mdash", "—" }, - { "micro", "µ" }, - { "middot", "·" }, - { "minus", "−" }, - { "mu", "μ" }, - { "nabla", "∇" }, - { "nbsp", " " }, - { "ndash", "–" }, - { "ne", "≠" }, - { "ni", "∋" }, - { "not", "¬" }, - { "notin", "∉" }, - { "nsub", "⊄" }, - { "ntilde", "ñ" }, - { "nu", "ν" }, - { "oacute", "ó" }, - { "ocirc", "ô" }, - { "oelig", "œ" }, - { "ograve", "ò" }, - { "oline", "‾" }, - { "omega", "ω" }, - { "omicron", "ο" }, - { "oplus", "⊕" }, - { "or", "∨" }, - { "ordf", "ª" }, - { "ordm", "º" }, - { "oslash", "ø" }, - { "otilde", "õ" }, - { "otimes", "⊗" }, - { "ouml", "ö" }, - { "para", "¶" }, - { "part", "∂" }, - { "permil", "‰" }, - { "perp", "⊥" }, - { "phi", "φ" }, - { "pi", "π" }, - { "piv", "ϖ" }, - { "plusmn", "±" }, - { "pound", "£" }, - { "prime", "′" }, - { "prod", "∏" }, - { "prop", "∝" }, - { "psi", "ψ" }, - { "quot", "\"" }, - { "rArr", "⇒" }, - { "radic", "√" }, - { "rang", "〉" }, - { "raquo", "»" }, - { "rarr", "→" }, - { "rceil", "⌉" }, - { "rdquo", "”" }, - { "real", "ℜ" }, - { "reg", "®" }, - { "rfloor", "⌋" }, - { "rho", "ρ" }, - { "rlm", "\xE2\x80\x8F" }, - { "rsaquo", "›" }, - { "rsquo", "’" }, - { "sbquo", "‚" }, - { "scaron", "š" }, - { "sdot", "⋅" }, - { "sect", "§" }, - { "shy", "\xC2\xAD" }, - { "sigma", "σ" }, - { "sigmaf", "ς" }, - { "sim", "∼" }, - { "spades", "♠" }, - { "sub", "⊂" }, - { "sube", "⊆" }, - { "sum", "∑" }, - { "sup", "⊃" }, - { "sup1", "¹" }, - { "sup2", "²" }, - { "sup3", "³" }, - { "supe", "⊇" }, - { "szlig", "ß" }, - { "tau", "τ" }, - { "there4", "∴" }, - { "theta", "θ" }, - { "thetasym", "ϑ" }, - { "thinsp", " " }, - { "thorn", "þ" }, - { "tilde", "˜" }, - { "times", "×" }, - { "trade", "™" }, - { "uArr", "⇑" }, - { "uacute", "ú" }, - { "uarr", "↑" }, - { "ucirc", "û" }, - { "ugrave", "ù" }, - { "uml", "¨" }, - { "upsih", "ϒ" }, - { "upsilon", "υ" }, - { "uuml", "ü" }, - { "weierp", "℘" }, - { "xi", "ξ" }, - { "yacute", "ý" }, - { "yen", "¥" }, - { "yuml", "ÿ" }, - { "zeta", "ζ" }, - { "zwj", "\xE2\x80\x8D" }, - { "zwnj", "\xE2\x80\x8C" } -}; - -CMStringW RemoveHtml(const CMStringW &data) -{ - CMStringW new_string; - - for (int i = 0; i < data.GetLength(); i++) { - wchar_t c = data[i]; - if (c == '<') { - i = data.Find('>', i); - if (i == -1) - break; - - continue; - } - - // special character - if (c == '&') { - int begin = i; - i = data.Find(';', i); - if (i == -1) - i = begin; - else { - CMStringW entity = data.Mid(begin + 1, i - begin - 1); - - bool found = false; - if (entity.GetLength() > 1 && entity[0] == '#') { - // Numeric replacement - bool hex = false; - if (entity[1] == 'x') { - hex = true; - entity.Delete(0, 2); - } - else entity.Delete(0, 1); - - if (!entity.IsEmpty()) { - found = true; - errno = 0; - unsigned long value = wcstoul(entity, nullptr, hex ? 16 : 10); - if (errno != 0) { // error with conversion in strtoul, ignore the result - found = false; - } - else if (value <= 127) { // U+0000 .. U+007F - new_string += (char)value; - } - else if (value >= 128 && value <= 2047) { // U+0080 .. U+07FF - new_string += (char)(192 + (value / 64)); - new_string += (char)(128 + (value % 64)); - } - else if (value >= 2048 && value <= 65535) { // U+0800 .. U+FFFF - new_string += (char)(224 + (value / 4096)); - new_string += (char)(128 + ((value / 64) % 64)); - new_string += (char)(128 + (value % 64)); - } - else { - new_string += (char)((value >> 24) & 0xFF); - new_string += (char)((value >> 16) & 0xFF); - new_string += (char)((value >> 8) & 0xFF); - new_string += (char)((value) & 0xFF); - } - } - } - else { - // Keyword replacement - CMStringA tmp = entity; - for (auto &it : htmlEntities) { - if (!mir_strcmpi(tmp, it.entity)) { - new_string += it.symbol; - found = true; - break; - } - } - } - - if (found) - continue; - else - i = begin; - } - } - - new_string.AppendChar(c); - } - - return new_string; -} - -const char* CSkypeProto::MirandaToSkypeStatus(int status) -{ - switch (status) { - case ID_STATUS_AWAY: - return "Away"; - - case ID_STATUS_DND: - return "Busy"; - - case ID_STATUS_IDLE: - return "Idle"; - - case ID_STATUS_INVISIBLE: - return "Hidden"; - } - return "Online"; -} - -int CSkypeProto::SkypeToMirandaStatus(const char *status) -{ - if (!mir_strcmpi(status, "Online")) - return ID_STATUS_ONLINE; - else if (!mir_strcmpi(status, "Hidden")) - return ID_STATUS_INVISIBLE; - else if (!mir_strcmpi(status, "Away")) - return ID_STATUS_AWAY; - else if (!mir_strcmpi(status, "Idle")) - return ID_STATUS_AWAY; - else if (!mir_strcmpi(status, "Busy")) - return ID_STATUS_DND; - else - return ID_STATUS_OFFLINE; -} - -bool CSkypeProto::IsFileExists(std::wstring path) -{ - return _waccess(path.c_str(), 0) == 0; -} - -const char* GetSkypeNick(const char *szSkypeId) -{ - if (auto *p = strchr(szSkypeId, ':')) - return p + 1; - return szSkypeId; -} - -const wchar_t* GetSkypeNick(const wchar_t *szSkypeId) -{ - if (auto *p = wcsrchr(szSkypeId, ':')) - return p + 1; - return szSkypeId; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// url parsing - -CMStringA ParseUrl(const char *url, const char *token) -{ - if (url == nullptr) - return CMStringA(); - - auto *start = strstr(url, token); - if (start == nullptr) - return CMStringA(); - - auto *end = strchr(++start, '/'); - if (end == nullptr) - return CMStringA(start); - return CMStringA(start, end - start); -} - -CMStringW ParseUrl(const wchar_t *url, const wchar_t *token) -{ - if (url == nullptr) - return CMStringW(); - - auto *start = wcsstr(url, token); - if (start == nullptr) - return CMStringW(); - - auto *end = wcschr(++start, '/'); - if (end == nullptr) - return CMStringW(start); - return CMStringW(start, end - start); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int possibleTypes[] = { 1, 2, 8, 19 }; - -CMStringA UrlToSkypeId(const char *url, int *pUserType) -{ - int userType = -1; - CMStringA szResult; - - if (url != nullptr) { - for (auto &it : possibleTypes) { - char tmp[10]; - sprintf_s(tmp, "/%d:", it); - if (strstr(url, tmp)) { - userType = it; - szResult = ParseUrl(url, tmp); - break; - } - } - } - - if (pUserType) - *pUserType = userType; - - return szResult; -} - -CMStringW UrlToSkypeId(const wchar_t *url, int *pUserType) -{ - int userType = -1; - CMStringW szResult; - - if (url != nullptr) { - for (auto &it : possibleTypes) { - wchar_t tmp[10]; - swprintf_s(tmp, L"/%d:", it); - if (wcsstr(url, tmp)) { - userType = it; - szResult = ParseUrl(url, tmp); - break; - } - } - } - - if (pUserType) - *pUserType = userType; - - return szResult; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CSkypeProto::ParseSkypeUriService(WPARAM, LPARAM lParam) -{ - wchar_t *arg = (wchar_t *)lParam; - if (arg == nullptr) - return 1; - - // skip leading prefix - wchar_t szUri[1024]; - wcsncpy_s(szUri, arg, _TRUNCATE); - wchar_t *szJid = wcschr(szUri, ':'); - if (szJid == nullptr) - return 1; - - // empty jid? - if (!*szJid) - return 1; - - // command code - wchar_t *szCommand = szJid; - szCommand = wcschr(szCommand, '?'); - if (szCommand) - *(szCommand++) = 0; - - // parameters - wchar_t *szSecondParam = szCommand ? wcschr(szCommand, '&') : nullptr; - if (szSecondParam) - *(szSecondParam++) = 0; - - // no command or message command - if (!szCommand || !mir_wstrcmpi(szCommand, L"chat")) { - if (szSecondParam) { - wchar_t *szChatId = wcsstr(szSecondParam, L"id="); - if (szChatId) { - szChatId += 5; - StartChatRoom(szChatId, szChatId); - return 0; - } - } - MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true); - CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, NULL); - return 0; - } - - if (!mir_wstrcmpi(szCommand, L"call")) { - MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true); - NotifyEventHooks(g_hCallEvent, (WPARAM)hContact, (LPARAM)0); - return 0; - } - - if (!mir_wstrcmpi(szCommand, L"userinfo")) - return 0; - - if (!mir_wstrcmpi(szCommand, L"add")) { - MCONTACT hContact = FindContact(_T2A(szJid)); - if (hContact == NULL) { - PROTOSEARCHRESULT psr = { 0 }; - psr.cbSize = sizeof(psr); - psr.id.w = mir_wstrdup(szJid); - psr.nick.w = mir_wstrdup(szJid); - psr.flags = PSR_UNICODE; - Contact::AddBySearch(m_szModuleName, &psr); - } - return 0; - } - - if (!mir_wstrcmpi(szCommand, L"sendfile")) { - MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true); - CallService(MS_FILE_SENDFILE, hContact, NULL); - return 1; - } - - if (!mir_wstrcmpi(szCommand, L"voicemail")) - return 1; - - return 1; -} - -INT_PTR CSkypeProto::GlobalParseSkypeUriService(WPARAM wParam, LPARAM lParam) -{ - for (auto &it : CMPlugin::g_arInstances) - if (it->IsOnline()) - return it->ParseSkypeUriService(wParam, lParam); - - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply) -{ - if (pReply == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = pReply->resultCode; - if (m_errorCode != 200) - return; - - m_root = json_parse(pReply->pData); - if (m_root == nullptr) { - m_errorCode = 500; - return; - } - - m_errorCode = (*m_root)["status"]["code"].as_int(); -} - -JsonReply::~JsonReply() -{ - json_delete(m_root); -} +/*
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+#pragma warning(disable:4566)
+
+time_t CSkypeProto::IsoToUnixTime(const std::string &stamp)
+{
+ char date[9];
+ int i, y;
+
+ if (stamp.empty())
+ return 0;
+
+ char *p = NEWSTR_ALLOCA(stamp.c_str());
+
+ // skip '-' chars
+ int si = 0, sj = 0;
+ while (true) {
+ if (p[si] == '-')
+ si++;
+ else if (!(p[sj++] = p[si++]))
+ break;
+ }
+
+ // Get the date part
+ for (i = 0; *p != '\0' && i < 8 && isdigit(*p); p++, i++)
+ date[i] = *p;
+
+ // Parse year
+ if (i == 6) {
+ // 2-digit year (1970-2069)
+ y = (date[0] - '0') * 10 + (date[1] - '0');
+ if (y < 70) y += 100;
+ }
+ else if (i == 8) {
+ // 4-digit year
+ y = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0';
+ y -= 1900;
+ }
+ else return 0;
+
+ struct tm timestamp;
+ timestamp.tm_year = y;
+
+ // Parse month
+ timestamp.tm_mon = (date[i - 4] - '0') * 10 + date[i - 3] - '0' - 1;
+
+ // Parse date
+ timestamp.tm_mday = (date[i - 2] - '0') * 10 + date[i - 1] - '0';
+
+ // Skip any date/time delimiter
+ for (; *p != '\0' && !isdigit(*p); p++);
+
+ // Parse time
+ if (sscanf(p, "%d:%d:%d", ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3)
+ return (time_t)0;
+
+ timestamp.tm_isdst = 0; // DST is already present in _timezone below
+ time_t t = mktime(×tamp);
+
+ _tzset();
+ t -= _timezone;
+ return (t >= 0) ? t : 0;
+}
+
+struct HtmlEntity
+{
+ const char *entity;
+ const char *symbol;
+};
+
+const HtmlEntity htmlEntities[] =
+{
+ { "AElig", "\u00C6" },
+ { "Aacute", "\u00C1" },
+ { "Acirc", "\u00C2" },
+ { "Agrave", "\u00C0" },
+ { "Alpha", "\u0391" },
+ { "Aring", "\u00C5" },
+ { "Atilde", "\u00C3" },
+ { "Auml", "\u00C4" },
+ { "Beta", "\u0392" },
+ { "Ccedil", "\u00C7" },
+ { "Chi", "\u03A7" },
+ { "Dagger", "‡" },
+ { "Delta", "\u0394" },
+ { "ETH", "\u00D0" },
+ { "Eacute", "\u00C9" },
+ { "Ecirc", "\u00CA" },
+ { "Egrave", "\u00C8" },
+ { "Epsilon", "\u0395" },
+ { "Eta", "\u0397" },
+ { "Euml", "\u00CB" },
+ { "Gamma", "\u0393" },
+ { "Iacute", "\u00CD" },
+ { "Icirc", "\u00CE" },
+ { "Igrave", "\u00CC" },
+ { "Iota", "\u0399" },
+ { "Iuml", "\u00CF" },
+ { "Kappa", "\u039A" },
+ { "Lambda", "\u039B" },
+ { "Mu", "\u039C" },
+ { "Ntilde", "\u00D1" },
+ { "Nu", "\u039D" },
+ { "OElig", "\u0152" },
+ { "Oacute", "\u00D3" },
+ { "Ocirc", "\u00D4" },
+ { "Ograve", "\u00D2" },
+ { "Omega", "\u03A9" },
+ { "Omicron", "\u039F" },
+ { "Oslash", "\u00D8" },
+ { "Otilde", "\u00D5" },
+ { "Ouml", "\u00D6" },
+ { "Phi", "\u03A6" },
+ { "Pi", "\u03A0" },
+ { "Prime", "\u2033" },
+ { "Psi", "\u03A8" },
+ { "Rho", "\u03A1" },
+ { "Scaron", "Š" },
+ { "Sigma", "Σ" },
+ { "THORN", "Þ" },
+ { "Tau", "Τ" },
+ { "Theta", "Θ" },
+ { "Uacute", "Ú" },
+ { "Ucirc", "Û" },
+ { "Ugrave", "Ù" },
+ { "Upsilon", "Υ" },
+ { "Uuml", "Ü" },
+ { "Xi", "Ξ" },
+ { "Yacute", "Ý" },
+ { "Yuml", "Ÿ" },
+ { "Zeta", "Ζ" },
+ { "aacute", "á" },
+ { "acirc", "â" },
+ { "acute", "´" },
+ { "aelig", "æ" },
+ { "agrave", "à" },
+ { "alefsym", "ℵ" },
+ { "alpha", "α" },
+ { "amp", "&" },
+ { "and", "∧" },
+ { "ang", "∠" },
+ { "apos", "'" },
+ { "aring", "å" },
+ { "asymp", "≈" },
+ { "atilde", "ã" },
+ { "auml", "ä" },
+ { "bdquo", "„" },
+ { "beta", "β" },
+ { "brvbar", "¦" },
+ { "bull", "•" },
+ { "cap", "∩" },
+ { "ccedil", "ç" },
+ { "cedil", "¸" },
+ { "cent", "¢" },
+ { "chi", "χ" },
+ { "circ", "ˆ" },
+ { "clubs", "♣" },
+ { "cong", "≅" },
+ { "copy", "©" },
+ { "crarr", "↵" },
+ { "cup", "∪" },
+ { "curren", "¤" },
+ { "dArr", "⇓" },
+ { "dagger", "†" },
+ { "darr", "↓" },
+ { "deg", "°" },
+ { "delta", "δ" },
+ { "diams", "♦" },
+ { "divide", "÷" },
+ { "eacute", "é" },
+ { "ecirc", "ê" },
+ { "egrave", "è" },
+ { "empty", "∅" },
+ { "emsp", " " },
+ { "ensp", " " },
+ { "epsilon", "ε" },
+ { "equiv", "≡" },
+ { "eta", "η" },
+ { "eth", "ð" },
+ { "euml", "ë" },
+ { "euro", "€" },
+ { "exist", "∃" },
+ { "fnof", "ƒ" },
+ { "forall", "∀" },
+ { "frac12", "½" },
+ { "frac14", "¼" },
+ { "frac34", "¾" },
+ { "frasl", "⁄" },
+ { "gamma", "γ" },
+ { "ge", "≥" },
+ { "gt", ">" },
+ { "hArr", "⇔" },
+ { "harr", "↔" },
+ { "hearts", "♥" },
+ { "hellip", "…" },
+ { "iacute", "í" },
+ { "icirc", "î" },
+ { "iexcl", "¡" },
+ { "igrave", "ì" },
+ { "image", "ℑ" },
+ { "infin", "∞" },
+ { "int", "∫" },
+ { "iota", "ι" },
+ { "iquest", "¿" },
+ { "isin", "∈" },
+ { "iuml", "ï" },
+ { "kappa", "κ" },
+ { "lArr", "⇐" },
+ { "lambda", "λ" },
+ { "lang", "〈" },
+ { "laquo", "«" },
+ { "larr", "←" },
+ { "lceil", "⌈" },
+ { "ldquo", "“" },
+ { "le", "≤" },
+ { "lfloor", "⌊" },
+ { "lowast", "∗" },
+ { "loz", "◊" },
+ { "lrm", "\xE2\x80\x8E" },
+ { "lsaquo", "‹" },
+ { "lsquo", "‘" },
+ { "lt", "<" },
+ { "macr", "¯" },
+ { "mdash", "—" },
+ { "micro", "µ" },
+ { "middot", "·" },
+ { "minus", "−" },
+ { "mu", "μ" },
+ { "nabla", "∇" },
+ { "nbsp", " " },
+ { "ndash", "–" },
+ { "ne", "≠" },
+ { "ni", "∋" },
+ { "not", "¬" },
+ { "notin", "∉" },
+ { "nsub", "⊄" },
+ { "ntilde", "ñ" },
+ { "nu", "ν" },
+ { "oacute", "ó" },
+ { "ocirc", "ô" },
+ { "oelig", "œ" },
+ { "ograve", "ò" },
+ { "oline", "‾" },
+ { "omega", "ω" },
+ { "omicron", "ο" },
+ { "oplus", "⊕" },
+ { "or", "∨" },
+ { "ordf", "ª" },
+ { "ordm", "º" },
+ { "oslash", "ø" },
+ { "otilde", "õ" },
+ { "otimes", "⊗" },
+ { "ouml", "ö" },
+ { "para", "¶" },
+ { "part", "∂" },
+ { "permil", "‰" },
+ { "perp", "⊥" },
+ { "phi", "φ" },
+ { "pi", "π" },
+ { "piv", "ϖ" },
+ { "plusmn", "±" },
+ { "pound", "£" },
+ { "prime", "′" },
+ { "prod", "∏" },
+ { "prop", "∝" },
+ { "psi", "ψ" },
+ { "quot", "\"" },
+ { "rArr", "⇒" },
+ { "radic", "√" },
+ { "rang", "〉" },
+ { "raquo", "»" },
+ { "rarr", "→" },
+ { "rceil", "⌉" },
+ { "rdquo", "”" },
+ { "real", "ℜ" },
+ { "reg", "®" },
+ { "rfloor", "⌋" },
+ { "rho", "ρ" },
+ { "rlm", "\xE2\x80\x8F" },
+ { "rsaquo", "›" },
+ { "rsquo", "’" },
+ { "sbquo", "‚" },
+ { "scaron", "š" },
+ { "sdot", "⋅" },
+ { "sect", "§" },
+ { "shy", "\xC2\xAD" },
+ { "sigma", "σ" },
+ { "sigmaf", "ς" },
+ { "sim", "∼" },
+ { "spades", "♠" },
+ { "sub", "⊂" },
+ { "sube", "⊆" },
+ { "sum", "∑" },
+ { "sup", "⊃" },
+ { "sup1", "¹" },
+ { "sup2", "²" },
+ { "sup3", "³" },
+ { "supe", "⊇" },
+ { "szlig", "ß" },
+ { "tau", "τ" },
+ { "there4", "∴" },
+ { "theta", "θ" },
+ { "thetasym", "ϑ" },
+ { "thinsp", " " },
+ { "thorn", "þ" },
+ { "tilde", "˜" },
+ { "times", "×" },
+ { "trade", "™" },
+ { "uArr", "⇑" },
+ { "uacute", "ú" },
+ { "uarr", "↑" },
+ { "ucirc", "û" },
+ { "ugrave", "ù" },
+ { "uml", "¨" },
+ { "upsih", "ϒ" },
+ { "upsilon", "υ" },
+ { "uuml", "ü" },
+ { "weierp", "℘" },
+ { "xi", "ξ" },
+ { "yacute", "ý" },
+ { "yen", "¥" },
+ { "yuml", "ÿ" },
+ { "zeta", "ζ" },
+ { "zwj", "\xE2\x80\x8D" },
+ { "zwnj", "\xE2\x80\x8C" }
+};
+
+CMStringW RemoveHtml(const CMStringW &data)
+{
+ CMStringW new_string;
+
+ for (int i = 0; i < data.GetLength(); i++) {
+ wchar_t c = data[i];
+ if (c == '<') {
+ i = data.Find('>', i);
+ if (i == -1)
+ break;
+
+ continue;
+ }
+
+ // special character
+ if (c == '&') {
+ int begin = i;
+ i = data.Find(';', i);
+ if (i == -1)
+ i = begin;
+ else {
+ CMStringW entity = data.Mid(begin + 1, i - begin - 1);
+
+ bool found = false;
+ if (entity.GetLength() > 1 && entity[0] == '#') {
+ // Numeric replacement
+ bool hex = false;
+ if (entity[1] == 'x') {
+ hex = true;
+ entity.Delete(0, 2);
+ }
+ else entity.Delete(0, 1);
+
+ if (!entity.IsEmpty()) {
+ found = true;
+ errno = 0;
+ unsigned long value = wcstoul(entity, nullptr, hex ? 16 : 10);
+ if (errno != 0) { // error with conversion in strtoul, ignore the result
+ found = false;
+ }
+ else if (value <= 127) { // U+0000 .. U+007F
+ new_string += (char)value;
+ }
+ else if (value >= 128 && value <= 2047) { // U+0080 .. U+07FF
+ new_string += (char)(192 + (value / 64));
+ new_string += (char)(128 + (value % 64));
+ }
+ else if (value >= 2048 && value <= 65535) { // U+0800 .. U+FFFF
+ new_string += (char)(224 + (value / 4096));
+ new_string += (char)(128 + ((value / 64) % 64));
+ new_string += (char)(128 + (value % 64));
+ }
+ else {
+ new_string += (char)((value >> 24) & 0xFF);
+ new_string += (char)((value >> 16) & 0xFF);
+ new_string += (char)((value >> 8) & 0xFF);
+ new_string += (char)((value) & 0xFF);
+ }
+ }
+ }
+ else {
+ // Keyword replacement
+ CMStringA tmp = entity;
+ for (auto &it : htmlEntities) {
+ if (!mir_strcmpi(tmp, it.entity)) {
+ new_string += it.symbol;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (found)
+ continue;
+ else
+ i = begin;
+ }
+ }
+
+ new_string.AppendChar(c);
+ }
+
+ return new_string;
+}
+
+const char* CSkypeProto::MirandaToSkypeStatus(int status)
+{
+ switch (status) {
+ case ID_STATUS_AWAY:
+ return "Away";
+
+ case ID_STATUS_DND:
+ return "Busy";
+
+ case ID_STATUS_IDLE:
+ return "Idle";
+
+ case ID_STATUS_INVISIBLE:
+ return "Hidden";
+ }
+ return "Online";
+}
+
+int CSkypeProto::SkypeToMirandaStatus(const char *status)
+{
+ if (!mir_strcmpi(status, "Online"))
+ return ID_STATUS_ONLINE;
+ else if (!mir_strcmpi(status, "Hidden"))
+ return ID_STATUS_INVISIBLE;
+ else if (!mir_strcmpi(status, "Away"))
+ return ID_STATUS_AWAY;
+ else if (!mir_strcmpi(status, "Idle"))
+ return ID_STATUS_AWAY;
+ else if (!mir_strcmpi(status, "Busy"))
+ return ID_STATUS_DND;
+ else
+ return ID_STATUS_OFFLINE;
+}
+
+bool CSkypeProto::IsFileExists(std::wstring path)
+{
+ return _waccess(path.c_str(), 0) == 0;
+}
+
+const char* GetSkypeNick(const char *szSkypeId)
+{
+ if (auto *p = strchr(szSkypeId, ':'))
+ return p + 1;
+ return szSkypeId;
+}
+
+const wchar_t* GetSkypeNick(const wchar_t *szSkypeId)
+{
+ if (auto *p = wcsrchr(szSkypeId, ':'))
+ return p + 1;
+ return szSkypeId;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// url parsing
+
+CMStringA ParseUrl(const char *url, const char *token)
+{
+ if (url == nullptr)
+ return CMStringA();
+
+ auto *start = strstr(url, token);
+ if (start == nullptr)
+ return CMStringA();
+
+ auto *end = strchr(++start, '/');
+ if (end == nullptr)
+ return CMStringA(start);
+ return CMStringA(start, end - start);
+}
+
+CMStringW ParseUrl(const wchar_t *url, const wchar_t *token)
+{
+ if (url == nullptr)
+ return CMStringW();
+
+ auto *start = wcsstr(url, token);
+ if (start == nullptr)
+ return CMStringW();
+
+ auto *end = wcschr(++start, '/');
+ if (end == nullptr)
+ return CMStringW(start);
+ return CMStringW(start, end - start);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int possibleTypes[] = { 1, 2, 8, 19 };
+
+CMStringA UrlToSkypeId(const char *url, int *pUserType)
+{
+ int userType = -1;
+ CMStringA szResult;
+
+ if (url != nullptr) {
+ for (auto &it : possibleTypes) {
+ char tmp[10];
+ sprintf_s(tmp, "/%d:", it);
+ if (strstr(url, tmp)) {
+ userType = it;
+ szResult = ParseUrl(url, tmp);
+ break;
+ }
+ }
+ }
+
+ if (pUserType)
+ *pUserType = userType;
+
+ return szResult;
+}
+
+CMStringW UrlToSkypeId(const wchar_t *url, int *pUserType)
+{
+ int userType = -1;
+ CMStringW szResult;
+
+ if (url != nullptr) {
+ for (auto &it : possibleTypes) {
+ wchar_t tmp[10];
+ swprintf_s(tmp, L"/%d:", it);
+ if (wcsstr(url, tmp)) {
+ userType = it;
+ szResult = ParseUrl(url, tmp);
+ break;
+ }
+ }
+ }
+
+ if (pUserType)
+ *pUserType = userType;
+
+ return szResult;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CSkypeProto::ParseSkypeUriService(WPARAM, LPARAM lParam)
+{
+ wchar_t *arg = (wchar_t *)lParam;
+ if (arg == nullptr)
+ return 1;
+
+ // skip leading prefix
+ wchar_t szUri[1024];
+ wcsncpy_s(szUri, arg, _TRUNCATE);
+ wchar_t *szJid = wcschr(szUri, ':');
+ if (szJid == nullptr)
+ return 1;
+
+ // empty jid?
+ if (!*szJid)
+ return 1;
+
+ // command code
+ wchar_t *szCommand = szJid;
+ szCommand = wcschr(szCommand, '?');
+ if (szCommand)
+ *(szCommand++) = 0;
+
+ // parameters
+ wchar_t *szSecondParam = szCommand ? wcschr(szCommand, '&') : nullptr;
+ if (szSecondParam)
+ *(szSecondParam++) = 0;
+
+ // no command or message command
+ if (!szCommand || !mir_wstrcmpi(szCommand, L"chat")) {
+ if (szSecondParam) {
+ wchar_t *szChatId = wcsstr(szSecondParam, L"id=");
+ if (szChatId) {
+ szChatId += 5;
+ StartChatRoom(szChatId, szChatId);
+ return 0;
+ }
+ }
+ MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true);
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, NULL);
+ return 0;
+ }
+
+ if (!mir_wstrcmpi(szCommand, L"call")) {
+ MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true);
+ NotifyEventHooks(g_hCallEvent, (WPARAM)hContact, (LPARAM)0);
+ return 0;
+ }
+
+ if (!mir_wstrcmpi(szCommand, L"userinfo"))
+ return 0;
+
+ if (!mir_wstrcmpi(szCommand, L"add")) {
+ MCONTACT hContact = FindContact(_T2A(szJid));
+ if (hContact == NULL) {
+ PROTOSEARCHRESULT psr = { 0 };
+ psr.cbSize = sizeof(psr);
+ psr.id.w = mir_wstrdup(szJid);
+ psr.nick.w = mir_wstrdup(szJid);
+ psr.flags = PSR_UNICODE;
+ Contact::AddBySearch(m_szModuleName, &psr);
+ }
+ return 0;
+ }
+
+ if (!mir_wstrcmpi(szCommand, L"sendfile")) {
+ MCONTACT hContact = AddContact(_T2A(szJid), nullptr, true);
+ CallService(MS_FILE_SENDFILE, hContact, NULL);
+ return 1;
+ }
+
+ if (!mir_wstrcmpi(szCommand, L"voicemail"))
+ return 1;
+
+ return 1;
+}
+
+INT_PTR CSkypeProto::GlobalParseSkypeUriService(WPARAM wParam, LPARAM lParam)
+{
+ for (auto &it : CMPlugin::g_arInstances)
+ if (it->IsOnline())
+ return it->ParseSkypeUriService(wParam, lParam);
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = pReply->resultCode;
+ if (m_errorCode != 200)
+ return;
+
+ m_root = json_parse(pReply->pData);
+ if (m_root == nullptr) {
+ m_errorCode = 500;
+ return;
+ }
+
+ m_errorCode = (*m_root)["status"]["code"].as_int();
+}
+
+JsonReply::~JsonReply()
+{
+ json_delete(m_root);
+}
diff --git a/protocols/SkypeWeb/src/skype_utils.h b/protocols/SkypeWeb/src/skype_utils.h index 6234abf8b6..e8a1f05836 100644 --- a/protocols/SkypeWeb/src/skype_utils.h +++ b/protocols/SkypeWeb/src/skype_utils.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/stdafx.cxx b/protocols/SkypeWeb/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/protocols/SkypeWeb/src/stdafx.cxx +++ b/protocols/SkypeWeb/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/stdafx.h b/protocols/SkypeWeb/src/stdafx.h index 059d5cb9d6..6dbb1da1c3 100644 --- a/protocols/SkypeWeb/src/stdafx.h +++ b/protocols/SkypeWeb/src/stdafx.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2015-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2015-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/SkypeWeb/src/version.h b/protocols/SkypeWeb/src/version.h index b41d65df1e..991de1af6d 100644 --- a/protocols/SkypeWeb/src/version.h +++ b/protocols/SkypeWeb/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Skype protocol support for Miranda NG. Based on new Skype for Web."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/SkypeWeb"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/protocols/Steam/src/stdafx.cxx b/protocols/Steam/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/protocols/Steam/src/stdafx.cxx +++ b/protocols/Steam/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Steam/src/version.h b/protocols/Steam/src/version.h index e9ed560cb2..43a5d81451 100644 --- a/protocols/Steam/src/version.h +++ b/protocols/Steam/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Steam protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team, Robert Pösel"
#define __AUTHORWEB "https://miranda-ng.org/p/Steam"
-#define __COPYRIGHT "© 2014-17 Robert Pösel, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2014-17 Robert Pösel, 2017-23 Miranda NG team"
diff --git a/protocols/Telegram/src/auth.cpp b/protocols/Telegram/src/auth.cpp index 22254ea460..3778ff1039 100644 --- a/protocols/Telegram/src/auth.cpp +++ b/protocols/Telegram/src/auth.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/Telegram/src/options.cpp b/protocols/Telegram/src/options.cpp index d830c2122a..9159d7304e 100644 --- a/protocols/Telegram/src/options.cpp +++ b/protocols/Telegram/src/options.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/Telegram/src/server.cpp b/protocols/Telegram/src/server.cpp index 8792000d59..2dccbc0281 100644 --- a/protocols/Telegram/src/server.cpp +++ b/protocols/Telegram/src/server.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/Telegram/src/stdafx.cxx b/protocols/Telegram/src/stdafx.cxx index d265a4c02e..b08670e67f 100644 --- a/protocols/Telegram/src/stdafx.cxx +++ b/protocols/Telegram/src/stdafx.cxx @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/Telegram/src/utils.cpp b/protocols/Telegram/src/utils.cpp index 3ea60b4c19..6113870bd3 100644 --- a/protocols/Telegram/src/utils.cpp +++ b/protocols/Telegram/src/utils.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/protocols/Telegram/src/version.h b/protocols/Telegram/src/version.h index 46df8e9330..7e46257aab 100644 --- a/protocols/Telegram/src/version.h +++ b/protocols/Telegram/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Telegram protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Telegram"
-#define __COPYRIGHT "© 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2018-23 Miranda NG team"
diff --git a/protocols/Tox/src/stdafx.cxx b/protocols/Tox/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/protocols/Tox/src/stdafx.cxx +++ b/protocols/Tox/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/Tox/src/version.h b/protocols/Tox/src/version.h index 837b6a836d..e4140a30e9 100644 --- a/protocols/Tox/src/version.h +++ b/protocols/Tox/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Tox protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Tox"
-#define __COPYRIGHT "© 2014-22 Miranda NG team"
+#define __COPYRIGHT "© 2014-23 Miranda NG team"
diff --git a/protocols/Twitter/src/StringUtil.cpp b/protocols/Twitter/src/StringUtil.cpp index f2e0bf7189..39c65c5319 100644 --- a/protocols/Twitter/src/StringUtil.cpp +++ b/protocols/Twitter/src/StringUtil.cpp @@ -1,5 +1,5 @@ /*
-Copyright 2012-22 Miranda NG team
+Copyright 2012-23 Miranda NG team
Copyright 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/chat.cpp b/protocols/Twitter/src/chat.cpp index 0d0426c0f4..40b344ada0 100644 --- a/protocols/Twitter/src/chat.cpp +++ b/protocols/Twitter/src/chat.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/connection.cpp b/protocols/Twitter/src/connection.cpp index f2622b29fa..d25dbcb919 100644 --- a/protocols/Twitter/src/connection.cpp +++ b/protocols/Twitter/src/connection.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/contacts.cpp b/protocols/Twitter/src/contacts.cpp index 2a15a4bd48..d17263cccf 100644 --- a/protocols/Twitter/src/contacts.cpp +++ b/protocols/Twitter/src/contacts.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/http.h b/protocols/Twitter/src/http.h index ed1732f082..db0f2204b0 100644 --- a/protocols/Twitter/src/http.h +++ b/protocols/Twitter/src/http.h @@ -1,6 +1,6 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/main.cpp b/protocols/Twitter/src/main.cpp index 0726ac15ac..5862b8a667 100644 --- a/protocols/Twitter/src/main.cpp +++ b/protocols/Twitter/src/main.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/oauth.cpp b/protocols/Twitter/src/oauth.cpp index 584284ad9f..16fc13e609 100644 --- a/protocols/Twitter/src/oauth.cpp +++ b/protocols/Twitter/src/oauth.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/proto.cpp b/protocols/Twitter/src/proto.cpp index df7fa2f46b..ab569e9d5b 100644 --- a/protocols/Twitter/src/proto.cpp +++ b/protocols/Twitter/src/proto.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/proto.h b/protocols/Twitter/src/proto.h index 4ea8b0241a..69fca0130a 100644 --- a/protocols/Twitter/src/proto.h +++ b/protocols/Twitter/src/proto.h @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/stdafx.cxx b/protocols/Twitter/src/stdafx.cxx index a8bf7b4e5f..d1bfe5009a 100644 --- a/protocols/Twitter/src/stdafx.cxx +++ b/protocols/Twitter/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/theme.cpp b/protocols/Twitter/src/theme.cpp index 162073b20f..b7b8c01e97 100644 --- a/protocols/Twitter/src/theme.cpp +++ b/protocols/Twitter/src/theme.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/theme.h b/protocols/Twitter/src/theme.h index 181fa6e486..0f0875fab2 100644 --- a/protocols/Twitter/src/theme.h +++ b/protocols/Twitter/src/theme.h @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/twitter.cpp b/protocols/Twitter/src/twitter.cpp index b8308c1fb4..fa6d441363 100644 --- a/protocols/Twitter/src/twitter.cpp +++ b/protocols/Twitter/src/twitter.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/ui.cpp b/protocols/Twitter/src/ui.cpp index 404a59ed29..978f791f83 100644 --- a/protocols/Twitter/src/ui.cpp +++ b/protocols/Twitter/src/ui.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/ui.h b/protocols/Twitter/src/ui.h index 067baeeffd..8762886cc8 100644 --- a/protocols/Twitter/src/ui.h +++ b/protocols/Twitter/src/ui.h @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/utility.cpp b/protocols/Twitter/src/utility.cpp index 24f32a5315..06b8c10c65 100644 --- a/protocols/Twitter/src/utility.cpp +++ b/protocols/Twitter/src/utility.cpp @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/utility.h b/protocols/Twitter/src/utility.h index 6fa12846e6..58a6f9028f 100644 --- a/protocols/Twitter/src/utility.h +++ b/protocols/Twitter/src/utility.h @@ -1,5 +1,5 @@ /*
-Copyright © 2012-22 Miranda NG team
+Copyright © 2012-23 Miranda NG team
Copyright © 2009 Jim Porter
This program is free software: you can redistribute it and/or modify
diff --git a/protocols/Twitter/src/version.h b/protocols/Twitter/src/version.h index de7fb6516b..a3d829c1de 100644 --- a/protocols/Twitter/src/version.h +++ b/protocols/Twitter/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Twitter protocol support for Miranda NG."
#define __AUTHOR "dentist, omniwolf, Thief"
#define __AUTHORWEB "https://miranda-ng.org/p/Twitter"
-#define __COPYRIGHT "© 2009-2010 dentist, 2010-2012 omniwolf and Thief, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2009-2010 dentist, 2010-2012 omniwolf and Thief, 2012-23 Miranda NG team"
diff --git a/protocols/VKontakte/src/main.cpp b/protocols/VKontakte/src/main.cpp index 07fa2a6a6b..ee2ccc09c2 100644 --- a/protocols/VKontakte/src/main.cpp +++ b/protocols/VKontakte/src/main.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/misc.cpp b/protocols/VKontakte/src/misc.cpp index a671733f58..c60dbb36ad 100644 --- a/protocols/VKontakte/src/misc.cpp +++ b/protocols/VKontakte/src/misc.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/stdafx.cxx b/protocols/VKontakte/src/stdafx.cxx index 07c147c9ca..7b26873eaa 100644 --- a/protocols/VKontakte/src/stdafx.cxx +++ b/protocols/VKontakte/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/stdafx.h b/protocols/VKontakte/src/stdafx.h index f0030969d0..04c0aae0c4 100644 --- a/protocols/VKontakte/src/stdafx.h +++ b/protocols/VKontakte/src/stdafx.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/version.h b/protocols/VKontakte/src/version.h index d23dfb98db..38b974c8c1 100644 --- a/protocols/VKontakte/src/version.h +++ b/protocols/VKontakte/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "VKontakte protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/VKontakte"
-#define __COPYRIGHT "© 2013-22 Miranda NG team"
+#define __COPYRIGHT "© 2013-23 Miranda NG team"
diff --git a/protocols/VKontakte/src/vk.h b/protocols/VKontakte/src/vk.h index fa463a8a0a..f1af226f8a 100644 --- a/protocols/VKontakte/src/vk.h +++ b/protocols/VKontakte/src/vk.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_avatars.cpp b/protocols/VKontakte/src/vk_avatars.cpp index 6553366518..b4ee6cc7cb 100644 --- a/protocols/VKontakte/src/vk_avatars.cpp +++ b/protocols/VKontakte/src/vk_avatars.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_captcha.cpp b/protocols/VKontakte/src/vk_captcha.cpp index 5337e20181..e82d1a3400 100644 --- a/protocols/VKontakte/src/vk_captcha.cpp +++ b/protocols/VKontakte/src/vk_captcha.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_chats.cpp b/protocols/VKontakte/src/vk_chats.cpp index 6b05135d02..1771b5d474 100644 --- a/protocols/VKontakte/src/vk_chats.cpp +++ b/protocols/VKontakte/src/vk_chats.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_dialogs.cpp b/protocols/VKontakte/src/vk_dialogs.cpp index 49adcbc308..79ca3854ef 100644 --- a/protocols/VKontakte/src/vk_dialogs.cpp +++ b/protocols/VKontakte/src/vk_dialogs.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_dialogs.h b/protocols/VKontakte/src/vk_dialogs.h index 279b876232..ee3f7893cd 100644 --- a/protocols/VKontakte/src/vk_dialogs.h +++ b/protocols/VKontakte/src/vk_dialogs.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_feed.cpp b/protocols/VKontakte/src/vk_feed.cpp index 478071e34e..787565d9f5 100644 --- a/protocols/VKontakte/src/vk_feed.cpp +++ b/protocols/VKontakte/src/vk_feed.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_files.cpp b/protocols/VKontakte/src/vk_files.cpp index 9fa8195ed5..a7306c1568 100644 --- a/protocols/VKontakte/src/vk_files.cpp +++ b/protocols/VKontakte/src/vk_files.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_history.cpp b/protocols/VKontakte/src/vk_history.cpp index 839b3c669c..00457abd62 100644 --- a/protocols/VKontakte/src/vk_history.cpp +++ b/protocols/VKontakte/src/vk_history.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_messages.cpp b/protocols/VKontakte/src/vk_messages.cpp index 846142a72b..e6f31c2256 100644 --- a/protocols/VKontakte/src/vk_messages.cpp +++ b/protocols/VKontakte/src/vk_messages.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_options.cpp b/protocols/VKontakte/src/vk_options.cpp index 3b24c08d24..8342842cd3 100644 --- a/protocols/VKontakte/src/vk_options.cpp +++ b/protocols/VKontakte/src/vk_options.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_options.h b/protocols/VKontakte/src/vk_options.h index 3e5228b476..cf7385dfbb 100644 --- a/protocols/VKontakte/src/vk_options.h +++ b/protocols/VKontakte/src/vk_options.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_pollserver.cpp b/protocols/VKontakte/src/vk_pollserver.cpp index 8ee4e31a6f..0b0218e38b 100644 --- a/protocols/VKontakte/src/vk_pollserver.cpp +++ b/protocols/VKontakte/src/vk_pollserver.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_proto.cpp b/protocols/VKontakte/src/vk_proto.cpp index d361fdee6f..b894b36a64 100644 --- a/protocols/VKontakte/src/vk_proto.cpp +++ b/protocols/VKontakte/src/vk_proto.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_proto.h b/protocols/VKontakte/src/vk_proto.h index f5bf492d6c..fd680580a6 100644 --- a/protocols/VKontakte/src/vk_proto.h +++ b/protocols/VKontakte/src/vk_proto.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_queue.cpp b/protocols/VKontakte/src/vk_queue.cpp index cd9912410c..ecf98468d0 100644 --- a/protocols/VKontakte/src/vk_queue.cpp +++ b/protocols/VKontakte/src/vk_queue.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_search.cpp b/protocols/VKontakte/src/vk_search.cpp index 7f95b792e7..c7e929c063 100644 --- a/protocols/VKontakte/src/vk_search.cpp +++ b/protocols/VKontakte/src/vk_search.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_status.cpp b/protocols/VKontakte/src/vk_status.cpp index 34d41bc55a..4fb2405aa2 100644 --- a/protocols/VKontakte/src/vk_status.cpp +++ b/protocols/VKontakte/src/vk_status.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_struct.cpp b/protocols/VKontakte/src/vk_struct.cpp index c021850374..dffe7a2341 100644 --- a/protocols/VKontakte/src/vk_struct.cpp +++ b/protocols/VKontakte/src/vk_struct.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_struct.h b/protocols/VKontakte/src/vk_struct.h index e3b6e5f66e..f6b1da600d 100644 --- a/protocols/VKontakte/src/vk_struct.h +++ b/protocols/VKontakte/src/vk_struct.h @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_thread.cpp b/protocols/VKontakte/src/vk_thread.cpp index ccf9a4a7eb..bafba46074 100644 --- a/protocols/VKontakte/src/vk_thread.cpp +++ b/protocols/VKontakte/src/vk_thread.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vk_wallpost.cpp b/protocols/VKontakte/src/vk_wallpost.cpp index 3d2f43bbb2..28fe3fcbd9 100644 --- a/protocols/VKontakte/src/vk_wallpost.cpp +++ b/protocols/VKontakte/src/vk_wallpost.cpp @@ -1,5 +1,5 @@ /*
-Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/protocols/VKontakte/src/vkjs.js b/protocols/VKontakte/src/vkjs.js index 2392c2eeb9..8f1ed1185f 100644 --- a/protocols/VKontakte/src/vkjs.js +++ b/protocols/VKontakte/src/vkjs.js @@ -1,4 +1,4 @@ -// Copyright (c) 2013-22 Miranda NG team (https://miranda-ng.org) +// Copyright (c) 2013-23 Miranda NG team (https://miranda-ng.org) // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation version 2 diff --git a/protocols/Weather/src/stdafx.cxx b/protocols/Weather/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/Weather/src/stdafx.cxx +++ b/protocols/Weather/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/Weather/src/stdafx.h b/protocols/Weather/src/stdafx.h index 88376fee8f..8c5abe874f 100644 --- a/protocols/Weather/src/stdafx.h +++ b/protocols/Weather/src/stdafx.h @@ -1,547 +1,547 @@ -/* -Weather Protocol plugin for Miranda NG -Copyright (C) 2012-22 Miranda NG team -Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved -Copyright (c) 2002-2005 Calvin Che - -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/>. -*/ - -/* This file contains the includes, weather constants/declarations, - the structs, and the primitives for some of the functions. -*/ - -#pragma once - -//============ THE INCLUDES =========== - -#include <share.h> -#include <time.h> -#include <windows.h> -#include <commctrl.h> -#include <richedit.h> -#include <malloc.h> - -#include <newpluginapi.h> -#include <m_acc.h> -#include <m_avatars.h> -#include <m_button.h> -#include <m_clc.h> -#include <m_cluiframes.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_findadd.h> -#include <m_fontservice.h> -#include <m_history.h> -#include <m_icolib.h> -#include <m_ignore.h> -#include <m_langpack.h> -#include <m_netlib.h> -#include <m_options.h> -#include <m_popup.h> -#include <m_protosvc.h> -#include <m_skin.h> -#include <m_skin_eng.h> -#include <m_userinfo.h> -#include <m_xstatus.h> - -#include <m_tipper.h> -#include <m_weather.h> -#include <m_toptoolbar.h> - -#include "resource.h" -#include "version.h" - -//============ CONSTANTS ============ - -// name -#define MODULENAME "Weather" -#define WEATHERPROTOTEXT "Weather" -#define DEFCURRENTWEATHER "WeatherCondition" -#define WEATHERCONDITION "Current" - -// weather conditions -enum EWeatherCondition -{ - SUNNY, - NA, - PCLOUDY, - CLOUDY, - RAIN, - RSHOWER, - FOG, - SNOW, - SSHOWER, - LIGHT, - MAX_COND -}; - -// status -#define NOSTATUSDATA 1 - -// limits -#define MAX_TEXT_SIZE 4096 -#define MAX_DATA_LEN 1024 - -// db info mangement mode -#define WDBM_REMOVE 1 -#define WDBM_DETAILDISPLAY 2 - -// more info list column width -#define LIST_COLUMN 150 - -// others -#define NODATA TranslateT("N/A") -#define UM_SETCONTACT 40000 - -// weather update error codes -#define INVALID_ID_FORMAT 10 -#define INVALID_SVC 11 -#define INVALID_ID 12 -#define SVC_NOT_FOUND 20 -#define NETLIB_ERROR 30 -#define DATA_EMPTY 40 -#define DOC_NOT_FOUND 42 -#define DOC_TOO_SHORT 43 -#define UNKNOWN_ERROR 99 - -// weather update error text -#define E10 TranslateT("Invalid ID format, missing \"/\" (10)") -#define E11 TranslateT("Invalid service (11)") -#define E12 TranslateT("Invalid station (12)") -#define E20 TranslateT("Weather service ini for this station is not found (20)") -#define E30 TranslateT("Netlib error - check your internet connection (30)") -#define E40 TranslateT("Empty data is retrieved (40)") -#define E42 TranslateT("Document not found (42)") -#define E43 TranslateT("Document too short to contain any weather data (43)") -#define E99 TranslateT("Unknown error (99)") - -// HTTP error... not all translated -// 100 Continue -// 101 Switching Protocols -// 200 OK -// 201 Created -// 202 Accepted -// 203 Non-Authoritative Information -#define E204 TranslateT("HTTP Error: No content (204)") -// 205 Reset Content -// 206 Partial Content -// 300 Multiple Choices -#define E301 TranslateT("HTTP Error: Data moved (301)") -// 302 Found -// 303 See Other -// 304 Not Modified -#define E305 TranslateT("HTTP Error: Use proxy (305)") -// 306 (Unused) -#define E307 TranslateT("HTTP Error: Temporary redirect (307)") -#define E400 TranslateT("HTTP Error: Bad request (400)") -#define E401 TranslateT("HTTP Error: Unauthorized (401)") -#define E402 TranslateT("HTTP Error: Payment required (402)") -#define E403 TranslateT("HTTP Error: Forbidden (403)") -#define E404 TranslateT("HTTP Error: Not found (404)") -#define E405 TranslateT("HTTP Error: Method not allowed (405)") -// 406 Not Acceptable -#define E407 TranslateT("HTTP Error: Proxy authentication required (407)") -// 408 Request Timeout -// 409 Conflict -#define E410 TranslateT("HTTP Error: Gone (410)") -// 411 Length Required -// 412 Precondition Failed -// 413 Request Entity Too Large -// 414 Request-URI Too Long -// 415 Unsupported Media Type -// 416 Requested Range Not Satisfiable -// 417 Expectation Failed -#define E500 TranslateT("HTTP Error: Internal server error (500)") -// 501 Not Implemented -#define E502 TranslateT("HTTP Error: Bad gateway (502)") -#define E503 TranslateT("HTTP Error: Service unavailable (503)") -#define E504 TranslateT("HTTP Error: Gateway timeout (504)") -// 505 HTTP Version Not Supported - -// defaults constants -#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") - -//============ OPTION STRUCT ============ - -// option struct -struct MYOPTIONS -{ - // main options - uint8_t AutoUpdate; - uint8_t CAutoUpdate; - uint8_t StartupUpdate; - uint8_t NoProtoCondition; - uint8_t UpdateOnlyConditionChanged; - uint8_t RemoveOldData; - uint8_t MakeItalic; - - uint16_t UpdateTime; - uint16_t AvatarSize; - - // units - uint16_t tUnit; - uint16_t wUnit; - uint16_t vUnit; - uint16_t pUnit; - uint16_t dUnit; - uint16_t eUnit; - wchar_t DegreeSign[4]; - uint8_t DoNotAppendUnit; - uint8_t NoFrac; - - // advanced - uint8_t DisCondIcon; - - // popup options - uint8_t UpdatePopup; - uint8_t AlertPopup; - uint8_t PopupOnChange; - uint8_t ShowWarnings; - - // popup colors - uint8_t UseWinColors; - COLORREF BGColour; - COLORREF TextColour; - - // popup actions - uint32_t LeftClickAction; - uint32_t RightClickAction; - - // popup delay - uint32_t pDelay; - - // other misc stuff - wchar_t Default[64]; - MCONTACT DefStn; -}; - -//============ STRUCT USED TO MAKE AN UPDATE LIST ============ -struct WCONTACTLIST { - MCONTACT hContact; - struct WCONTACTLIST *next; -}; - -typedef struct WCONTACTLIST UPDATELIST; - -extern UPDATELIST *UpdateListHead, *UpdateListTail; - -void DestroyUpdateList(void); - -//============ DATA FORMAT STRUCT ============ - -#define WID_NORMAL 0 -#define WID_SET 1 -#define WID_BREAK 2 - -struct WIDATAITEM -{ - wchar_t *Name; - wchar_t *Start; - wchar_t *End; - wchar_t *Unit; - char *Url; - wchar_t *Break; - int Type; -}; - -struct WITEMLIST -{ - WIDATAITEM Item; - struct WITEMLIST *Next; -}; - -typedef struct WITEMLIST WIDATAITEMLIST; - -struct WIIDSEARCH -{ - BOOL Available; - char *SearchURL; - wchar_t *NotFoundStr; - WIDATAITEM Name; -}; - -struct WINAMESEARCHTYPE -{ - BOOL Available; - wchar_t *First; - WIDATAITEM Name; - WIDATAITEM ID; -}; - -struct WINAMESEARCH -{ - char *SearchURL; - wchar_t *NotFoundStr; - wchar_t *SingleStr; - WINAMESEARCHTYPE Single; - WINAMESEARCHTYPE Multiple; -}; - -struct STRLIST -{ - wchar_t *Item; - struct STRLIST *Next; -}; - -typedef struct STRLIST WICONDITEM; - -struct WICONDLIST -{ - WICONDITEM *Head; - WICONDITEM *Tail; -}; - -struct WIDATA -{ - wchar_t *FileName; - wchar_t *ShortFileName; - BOOL Enabled; - - // header - wchar_t *DisplayName; - wchar_t *InternalName; - wchar_t *Description; - wchar_t *Author; - wchar_t *Version; - int InternalVer; - size_t MemUsed; - - // default - char *DefaultURL; - wchar_t *DefaultMap; - char *UpdateURL; - char *UpdateURL2; - char *UpdateURL3; - char *UpdateURL4; - char *Cookie; - char *UserAgent; - - // items - int UpdateDataCount; - WIDATAITEMLIST *UpdateData; - WIDATAITEMLIST *UpdateDataTail; - WIIDSEARCH IDSearch; - WINAMESEARCH NameSearch; - WICONDLIST CondList[MAX_COND]; -}; - -//============ DATA LIST (LINKED LIST) ============ - -struct DATALIST -{ - WIDATA Data; - struct DATALIST *next; -}; - -typedef struct DATALIST WIDATALIST; - -//============ GLOBAL VARIABLES ============ - -extern WIDATALIST *WIHead, *WITail; - -extern HWND hPopupWindow, hWndSetup; - -extern MYOPTIONS opt; - -extern unsigned status, old_status; - -extern MWindowList hDataWindowList, hWindowList; - -extern HNETLIBUSER hNetlibUser; -extern HANDLE hHookWeatherUpdated, hHookWeatherError, hTBButton, hUpdateMutex; -extern UINT_PTR timerId; - -extern HGENMENU hMwinMenu; - -// check if weather is currently updating -extern BOOL ThreadRunning; -extern bool g_bIsUtf; - -//============ FUNCTION PRIMITIVES ============ - -// functions in weather_addstn.c -INT_PTR WeatherAddToList(WPARAM wParam,LPARAM lParam); -BOOL CheckSearch(); - -int IDSearch(wchar_t *id, const int searchId); -int NameSearch(wchar_t *name, const int searchId); - -INT_PTR WeatherBasicSearch(WPARAM wParam,LPARAM lParam); -INT_PTR WeatherCreateAdvancedSearchUI(WPARAM wParam, LPARAM lParam); -INT_PTR WeatherAdvancedSearch(WPARAM wParam, LPARAM lParam); - -int WeatherAdd(WPARAM wParam, LPARAM lParam); - -// functions used in weather_contacts.c -INT_PTR ViewLog(WPARAM wParam,LPARAM lParam); -INT_PTR LoadForecast(WPARAM wParam,LPARAM lParam); -INT_PTR WeatherMap(WPARAM wParam,LPARAM lParam); -INT_PTR EditSettings(WPARAM wParam,LPARAM lParam); - -int ContactDeleted(WPARAM wParam,LPARAM lParam); - -BOOL IsMyContact(MCONTACT hContact); - -// functions in weather_conv.c -void GetTemp(wchar_t *tempchar, wchar_t *unit, wchar_t *str); -void GetSpeed(wchar_t *tempchar, wchar_t *unit, wchar_t *str); -void GetPressure(wchar_t *tempchar, wchar_t *unit, wchar_t *str); -void GetDist(wchar_t *tempchar, wchar_t *unit, wchar_t *str); -void GetElev(wchar_t *tempchar, wchar_t *unit, wchar_t *str); - -void ClearStatusIcons(); -int MapCondToStatus(MCONTACT hContact); -HICON GetStatusIcon(MCONTACT hContact); -HICON GetStatusIconBig(MCONTACT hContact); - -uint16_t GetIcon(const wchar_t* cond, WIDATA *Data); -void CaseConv(wchar_t *str); -void TrimString(char *str); -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); -INT_PTR GetDisplaySvcFunc(WPARAM wParam, LPARAM lParam); - -void GetSvc(wchar_t *pszID); -void GetID(wchar_t *pszID); - -wchar_t *GetError(int code); - -// functions in weather_data.c -void GetStationID(MCONTACT hContact, wchar_t* id, int idlen); -WEATHERINFO LoadWeatherInfo(MCONTACT Change); -int DBGetData(MCONTACT hContact, char *setting, DBVARIANT *dbv); - -void EraseAllInfo(void); - -void GetDataValue(WIDATAITEM *UpdateData, wchar_t *Data, wchar_t** szInfo); -void ConvertDataValue(WIDATAITEM *UpdateData, wchar_t *Data); -void wSetData(char *&Data, const char *Value); -void wSetData(wchar_t *&Data, const char *Value); -void wSetData(wchar_t *&Data, const wchar_t *Value); -void wfree(char *&Data); -void wfree(wchar_t *&Data); - -void DBDataManage(MCONTACT hContact, uint16_t Mode, WPARAM wParam, LPARAM lParam); - -// functions in weather_http.c -int InternetDownloadFile (char *szUrl, char *cookie, char *userAgent, wchar_t** szData); -void NetlibInit(); - -// functions in weather_ini.c -WIDATA* GetWIData(wchar_t *pszServ); - -bool IsContainedInCondList(const wchar_t *pszStr, WICONDLIST *List); - -void DestroyWIList(); -bool LoadWIData(bool dial); - -INT_PTR CALLBACK DlgPopupOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); - -// functions in weather_info.c -void GetINIInfo(wchar_t *pszSvc); -wchar_t* GetINIVersionNum(int iVersion); - -void MoreVarList(); - -// functions in weather_opt.c -void LoadOptions(); -void SaveOptions(); - -int OptInit(WPARAM wParam,LPARAM lParam); - -CMStringW GetTextValue(int c); -const wchar_t* GetDefaultText(int c); - -// functions in weather_popup.c -int WeatherPopup(WPARAM wParam, LPARAM lParam); -int WeatherError(WPARAM wParam, LPARAM lParam); -int WPShowMessage(const wchar_t* lpzText, uint16_t kind); - -LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - -// functions in weather_svcs.c -void InitServices(void); - -INT_PTR WeatherSetStatus(WPARAM new_status, LPARAM lParam); -INT_PTR WeatherGetCaps(WPARAM wParam, LPARAM lParam); -INT_PTR WeatherGetName(WPARAM wParam, LPARAM lParam); -INT_PTR WeatherGetStatus(WPARAM wParam, LPARAM lParam); -INT_PTR WeatherLoadIcon(WPARAM wParam, LPARAM lParam); - -void UpdateMenu(BOOL State); -void UpdatePopupMenu(BOOL State); -void AddMenuItems(); -void AvatarDownloaded(MCONTACT hContact); - -// functions in weather_update.c -int UpdateWeather(MCONTACT hContact); - -void UpdateAll(BOOL AutoUpdate, BOOL RemoveOld); -INT_PTR UpdateSingleStation(WPARAM wParam,LPARAM lParam); -INT_PTR UpdateAllInfo(WPARAM wParam,LPARAM lParam); -INT_PTR UpdateSingleRemove(WPARAM wParam,LPARAM lParam); -INT_PTR UpdateAllRemove(WPARAM wParam,LPARAM lParam); - -int GetWeatherData(MCONTACT hContact); - -void CALLBACK timerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); -void CALLBACK timerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); - -// function from multiwin module -void InitMwin(void); -void DestroyMwin(void); -INT_PTR Mwin_MenuClicked(WPARAM wParam, LPARAM lParam); -int BuildContactMenu(WPARAM wparam, LPARAM lparam); -void UpdateMwinData(MCONTACT hContact); -void removeWindow(MCONTACT hContact); - -// functions in weather_userinfo.c -int UserInfoInit(WPARAM wParam, LPARAM lParam); - -#define WM_UPDATEDATA WM_USER + 2687 - -int BriefInfo(WPARAM wParam, LPARAM lParam); - -/////////////////////////////////////////////////////////////////////////////// -// UI Classes - -class WeatherMyDetailsDlg : public CUserInfoPageDlg -{ - CCtrlButton btnReload; - -public: - WeatherMyDetailsDlg(); - - bool OnInitDialog() override; - - void onClick_Reload(CCtrlButton *); -}; - -//============ Plugin Class ============ - -struct CMPlugin : public PLUGIN<CMPlugin> -{ - CMPlugin(); - - HINSTANCE hIconsDll = nullptr; - CMOption<bool> bPopups; - - int Load() override; - int Unload() override; -}; - +/*
+Weather Protocol plugin for Miranda NG
+Copyright (C) 2012-23 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+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/>.
+*/
+
+/* This file contains the includes, weather constants/declarations,
+ the structs, and the primitives for some of the functions.
+*/
+
+#pragma once
+
+//============ THE INCLUDES ===========
+
+#include <share.h>
+#include <time.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <richedit.h>
+#include <malloc.h>
+
+#include <newpluginapi.h>
+#include <m_acc.h>
+#include <m_avatars.h>
+#include <m_button.h>
+#include <m_clc.h>
+#include <m_cluiframes.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_findadd.h>
+#include <m_fontservice.h>
+#include <m_history.h>
+#include <m_icolib.h>
+#include <m_ignore.h>
+#include <m_langpack.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_protosvc.h>
+#include <m_skin.h>
+#include <m_skin_eng.h>
+#include <m_userinfo.h>
+#include <m_xstatus.h>
+
+#include <m_tipper.h>
+#include <m_weather.h>
+#include <m_toptoolbar.h>
+
+#include "resource.h"
+#include "version.h"
+
+//============ CONSTANTS ============
+
+// name
+#define MODULENAME "Weather"
+#define WEATHERPROTOTEXT "Weather"
+#define DEFCURRENTWEATHER "WeatherCondition"
+#define WEATHERCONDITION "Current"
+
+// weather conditions
+enum EWeatherCondition
+{
+ SUNNY,
+ NA,
+ PCLOUDY,
+ CLOUDY,
+ RAIN,
+ RSHOWER,
+ FOG,
+ SNOW,
+ SSHOWER,
+ LIGHT,
+ MAX_COND
+};
+
+// status
+#define NOSTATUSDATA 1
+
+// limits
+#define MAX_TEXT_SIZE 4096
+#define MAX_DATA_LEN 1024
+
+// db info mangement mode
+#define WDBM_REMOVE 1
+#define WDBM_DETAILDISPLAY 2
+
+// more info list column width
+#define LIST_COLUMN 150
+
+// others
+#define NODATA TranslateT("N/A")
+#define UM_SETCONTACT 40000
+
+// weather update error codes
+#define INVALID_ID_FORMAT 10
+#define INVALID_SVC 11
+#define INVALID_ID 12
+#define SVC_NOT_FOUND 20
+#define NETLIB_ERROR 30
+#define DATA_EMPTY 40
+#define DOC_NOT_FOUND 42
+#define DOC_TOO_SHORT 43
+#define UNKNOWN_ERROR 99
+
+// weather update error text
+#define E10 TranslateT("Invalid ID format, missing \"/\" (10)")
+#define E11 TranslateT("Invalid service (11)")
+#define E12 TranslateT("Invalid station (12)")
+#define E20 TranslateT("Weather service ini for this station is not found (20)")
+#define E30 TranslateT("Netlib error - check your internet connection (30)")
+#define E40 TranslateT("Empty data is retrieved (40)")
+#define E42 TranslateT("Document not found (42)")
+#define E43 TranslateT("Document too short to contain any weather data (43)")
+#define E99 TranslateT("Unknown error (99)")
+
+// HTTP error... not all translated
+// 100 Continue
+// 101 Switching Protocols
+// 200 OK
+// 201 Created
+// 202 Accepted
+// 203 Non-Authoritative Information
+#define E204 TranslateT("HTTP Error: No content (204)")
+// 205 Reset Content
+// 206 Partial Content
+// 300 Multiple Choices
+#define E301 TranslateT("HTTP Error: Data moved (301)")
+// 302 Found
+// 303 See Other
+// 304 Not Modified
+#define E305 TranslateT("HTTP Error: Use proxy (305)")
+// 306 (Unused)
+#define E307 TranslateT("HTTP Error: Temporary redirect (307)")
+#define E400 TranslateT("HTTP Error: Bad request (400)")
+#define E401 TranslateT("HTTP Error: Unauthorized (401)")
+#define E402 TranslateT("HTTP Error: Payment required (402)")
+#define E403 TranslateT("HTTP Error: Forbidden (403)")
+#define E404 TranslateT("HTTP Error: Not found (404)")
+#define E405 TranslateT("HTTP Error: Method not allowed (405)")
+// 406 Not Acceptable
+#define E407 TranslateT("HTTP Error: Proxy authentication required (407)")
+// 408 Request Timeout
+// 409 Conflict
+#define E410 TranslateT("HTTP Error: Gone (410)")
+// 411 Length Required
+// 412 Precondition Failed
+// 413 Request Entity Too Large
+// 414 Request-URI Too Long
+// 415 Unsupported Media Type
+// 416 Requested Range Not Satisfiable
+// 417 Expectation Failed
+#define E500 TranslateT("HTTP Error: Internal server error (500)")
+// 501 Not Implemented
+#define E502 TranslateT("HTTP Error: Bad gateway (502)")
+#define E503 TranslateT("HTTP Error: Service unavailable (503)")
+#define E504 TranslateT("HTTP Error: Gateway timeout (504)")
+// 505 HTTP Version Not Supported
+
+// defaults constants
+#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")
+
+//============ OPTION STRUCT ============
+
+// option struct
+struct MYOPTIONS
+{
+ // main options
+ uint8_t AutoUpdate;
+ uint8_t CAutoUpdate;
+ uint8_t StartupUpdate;
+ uint8_t NoProtoCondition;
+ uint8_t UpdateOnlyConditionChanged;
+ uint8_t RemoveOldData;
+ uint8_t MakeItalic;
+
+ uint16_t UpdateTime;
+ uint16_t AvatarSize;
+
+ // units
+ uint16_t tUnit;
+ uint16_t wUnit;
+ uint16_t vUnit;
+ uint16_t pUnit;
+ uint16_t dUnit;
+ uint16_t eUnit;
+ wchar_t DegreeSign[4];
+ uint8_t DoNotAppendUnit;
+ uint8_t NoFrac;
+
+ // advanced
+ uint8_t DisCondIcon;
+
+ // popup options
+ uint8_t UpdatePopup;
+ uint8_t AlertPopup;
+ uint8_t PopupOnChange;
+ uint8_t ShowWarnings;
+
+ // popup colors
+ uint8_t UseWinColors;
+ COLORREF BGColour;
+ COLORREF TextColour;
+
+ // popup actions
+ uint32_t LeftClickAction;
+ uint32_t RightClickAction;
+
+ // popup delay
+ uint32_t pDelay;
+
+ // other misc stuff
+ wchar_t Default[64];
+ MCONTACT DefStn;
+};
+
+//============ STRUCT USED TO MAKE AN UPDATE LIST ============
+struct WCONTACTLIST {
+ MCONTACT hContact;
+ struct WCONTACTLIST *next;
+};
+
+typedef struct WCONTACTLIST UPDATELIST;
+
+extern UPDATELIST *UpdateListHead, *UpdateListTail;
+
+void DestroyUpdateList(void);
+
+//============ DATA FORMAT STRUCT ============
+
+#define WID_NORMAL 0
+#define WID_SET 1
+#define WID_BREAK 2
+
+struct WIDATAITEM
+{
+ wchar_t *Name;
+ wchar_t *Start;
+ wchar_t *End;
+ wchar_t *Unit;
+ char *Url;
+ wchar_t *Break;
+ int Type;
+};
+
+struct WITEMLIST
+{
+ WIDATAITEM Item;
+ struct WITEMLIST *Next;
+};
+
+typedef struct WITEMLIST WIDATAITEMLIST;
+
+struct WIIDSEARCH
+{
+ BOOL Available;
+ char *SearchURL;
+ wchar_t *NotFoundStr;
+ WIDATAITEM Name;
+};
+
+struct WINAMESEARCHTYPE
+{
+ BOOL Available;
+ wchar_t *First;
+ WIDATAITEM Name;
+ WIDATAITEM ID;
+};
+
+struct WINAMESEARCH
+{
+ char *SearchURL;
+ wchar_t *NotFoundStr;
+ wchar_t *SingleStr;
+ WINAMESEARCHTYPE Single;
+ WINAMESEARCHTYPE Multiple;
+};
+
+struct STRLIST
+{
+ wchar_t *Item;
+ struct STRLIST *Next;
+};
+
+typedef struct STRLIST WICONDITEM;
+
+struct WICONDLIST
+{
+ WICONDITEM *Head;
+ WICONDITEM *Tail;
+};
+
+struct WIDATA
+{
+ wchar_t *FileName;
+ wchar_t *ShortFileName;
+ BOOL Enabled;
+
+ // header
+ wchar_t *DisplayName;
+ wchar_t *InternalName;
+ wchar_t *Description;
+ wchar_t *Author;
+ wchar_t *Version;
+ int InternalVer;
+ size_t MemUsed;
+
+ // default
+ char *DefaultURL;
+ wchar_t *DefaultMap;
+ char *UpdateURL;
+ char *UpdateURL2;
+ char *UpdateURL3;
+ char *UpdateURL4;
+ char *Cookie;
+ char *UserAgent;
+
+ // items
+ int UpdateDataCount;
+ WIDATAITEMLIST *UpdateData;
+ WIDATAITEMLIST *UpdateDataTail;
+ WIIDSEARCH IDSearch;
+ WINAMESEARCH NameSearch;
+ WICONDLIST CondList[MAX_COND];
+};
+
+//============ DATA LIST (LINKED LIST) ============
+
+struct DATALIST
+{
+ WIDATA Data;
+ struct DATALIST *next;
+};
+
+typedef struct DATALIST WIDATALIST;
+
+//============ GLOBAL VARIABLES ============
+
+extern WIDATALIST *WIHead, *WITail;
+
+extern HWND hPopupWindow, hWndSetup;
+
+extern MYOPTIONS opt;
+
+extern unsigned status, old_status;
+
+extern MWindowList hDataWindowList, hWindowList;
+
+extern HNETLIBUSER hNetlibUser;
+extern HANDLE hHookWeatherUpdated, hHookWeatherError, hTBButton, hUpdateMutex;
+extern UINT_PTR timerId;
+
+extern HGENMENU hMwinMenu;
+
+// check if weather is currently updating
+extern BOOL ThreadRunning;
+extern bool g_bIsUtf;
+
+//============ FUNCTION PRIMITIVES ============
+
+// functions in weather_addstn.c
+INT_PTR WeatherAddToList(WPARAM wParam,LPARAM lParam);
+BOOL CheckSearch();
+
+int IDSearch(wchar_t *id, const int searchId);
+int NameSearch(wchar_t *name, const int searchId);
+
+INT_PTR WeatherBasicSearch(WPARAM wParam,LPARAM lParam);
+INT_PTR WeatherCreateAdvancedSearchUI(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherAdvancedSearch(WPARAM wParam, LPARAM lParam);
+
+int WeatherAdd(WPARAM wParam, LPARAM lParam);
+
+// functions used in weather_contacts.c
+INT_PTR ViewLog(WPARAM wParam,LPARAM lParam);
+INT_PTR LoadForecast(WPARAM wParam,LPARAM lParam);
+INT_PTR WeatherMap(WPARAM wParam,LPARAM lParam);
+INT_PTR EditSettings(WPARAM wParam,LPARAM lParam);
+
+int ContactDeleted(WPARAM wParam,LPARAM lParam);
+
+BOOL IsMyContact(MCONTACT hContact);
+
+// functions in weather_conv.c
+void GetTemp(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetSpeed(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetPressure(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetDist(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetElev(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+
+void ClearStatusIcons();
+int MapCondToStatus(MCONTACT hContact);
+HICON GetStatusIcon(MCONTACT hContact);
+HICON GetStatusIconBig(MCONTACT hContact);
+
+uint16_t GetIcon(const wchar_t* cond, WIDATA *Data);
+void CaseConv(wchar_t *str);
+void TrimString(char *str);
+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);
+INT_PTR GetDisplaySvcFunc(WPARAM wParam, LPARAM lParam);
+
+void GetSvc(wchar_t *pszID);
+void GetID(wchar_t *pszID);
+
+wchar_t *GetError(int code);
+
+// functions in weather_data.c
+void GetStationID(MCONTACT hContact, wchar_t* id, int idlen);
+WEATHERINFO LoadWeatherInfo(MCONTACT Change);
+int DBGetData(MCONTACT hContact, char *setting, DBVARIANT *dbv);
+
+void EraseAllInfo(void);
+
+void GetDataValue(WIDATAITEM *UpdateData, wchar_t *Data, wchar_t** szInfo);
+void ConvertDataValue(WIDATAITEM *UpdateData, wchar_t *Data);
+void wSetData(char *&Data, const char *Value);
+void wSetData(wchar_t *&Data, const char *Value);
+void wSetData(wchar_t *&Data, const wchar_t *Value);
+void wfree(char *&Data);
+void wfree(wchar_t *&Data);
+
+void DBDataManage(MCONTACT hContact, uint16_t Mode, WPARAM wParam, LPARAM lParam);
+
+// functions in weather_http.c
+int InternetDownloadFile (char *szUrl, char *cookie, char *userAgent, wchar_t** szData);
+void NetlibInit();
+
+// functions in weather_ini.c
+WIDATA* GetWIData(wchar_t *pszServ);
+
+bool IsContainedInCondList(const wchar_t *pszStr, WICONDLIST *List);
+
+void DestroyWIList();
+bool LoadWIData(bool dial);
+
+INT_PTR CALLBACK DlgPopupOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// functions in weather_info.c
+void GetINIInfo(wchar_t *pszSvc);
+wchar_t* GetINIVersionNum(int iVersion);
+
+void MoreVarList();
+
+// functions in weather_opt.c
+void LoadOptions();
+void SaveOptions();
+
+int OptInit(WPARAM wParam,LPARAM lParam);
+
+CMStringW GetTextValue(int c);
+const wchar_t* GetDefaultText(int c);
+
+// functions in weather_popup.c
+int WeatherPopup(WPARAM wParam, LPARAM lParam);
+int WeatherError(WPARAM wParam, LPARAM lParam);
+int WPShowMessage(const wchar_t* lpzText, uint16_t kind);
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+// functions in weather_svcs.c
+void InitServices(void);
+
+INT_PTR WeatherSetStatus(WPARAM new_status, LPARAM lParam);
+INT_PTR WeatherGetCaps(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherGetName(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherGetStatus(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherLoadIcon(WPARAM wParam, LPARAM lParam);
+
+void UpdateMenu(BOOL State);
+void UpdatePopupMenu(BOOL State);
+void AddMenuItems();
+void AvatarDownloaded(MCONTACT hContact);
+
+// functions in weather_update.c
+int UpdateWeather(MCONTACT hContact);
+
+void UpdateAll(BOOL AutoUpdate, BOOL RemoveOld);
+INT_PTR UpdateSingleStation(WPARAM wParam,LPARAM lParam);
+INT_PTR UpdateAllInfo(WPARAM wParam,LPARAM lParam);
+INT_PTR UpdateSingleRemove(WPARAM wParam,LPARAM lParam);
+INT_PTR UpdateAllRemove(WPARAM wParam,LPARAM lParam);
+
+int GetWeatherData(MCONTACT hContact);
+
+void CALLBACK timerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+void CALLBACK timerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+
+// function from multiwin module
+void InitMwin(void);
+void DestroyMwin(void);
+INT_PTR Mwin_MenuClicked(WPARAM wParam, LPARAM lParam);
+int BuildContactMenu(WPARAM wparam, LPARAM lparam);
+void UpdateMwinData(MCONTACT hContact);
+void removeWindow(MCONTACT hContact);
+
+// functions in weather_userinfo.c
+int UserInfoInit(WPARAM wParam, LPARAM lParam);
+
+#define WM_UPDATEDATA WM_USER + 2687
+
+int BriefInfo(WPARAM wParam, LPARAM lParam);
+
+///////////////////////////////////////////////////////////////////////////////
+// UI Classes
+
+class WeatherMyDetailsDlg : public CUserInfoPageDlg
+{
+ CCtrlButton btnReload;
+
+public:
+ WeatherMyDetailsDlg();
+
+ bool OnInitDialog() override;
+
+ void onClick_Reload(CCtrlButton *);
+};
+
+//============ Plugin Class ============
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ HINSTANCE hIconsDll = nullptr;
+ CMOption<bool> bPopups;
+
+ int Load() override;
+ int Unload() override;
+};
+
diff --git a/protocols/Weather/src/version.h b/protocols/Weather/src/version.h index bb157edd18..446326f779 100644 --- a/protocols/Weather/src/version.h +++ b/protocols/Weather/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "Retrieves weather information and displays it in your contact list."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Weather"
-#define __COPYRIGHT "© 2002-2005 NoName, 2005-2010 Boris Krasnovskiy, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2002-2005 NoName, 2005-2010 Boris Krasnovskiy, 2012-23 Miranda NG team"
diff --git a/protocols/WebView/src/main.cpp b/protocols/WebView/src/main.cpp index 1d5fe29cb6..858422a4c8 100644 --- a/protocols/WebView/src/main.cpp +++ b/protocols/WebView/src/main.cpp @@ -1,274 +1,274 @@ -/* - * A plugin for Miranda IM which displays web page text in a window. - * Copyright (C) 2005 Vincent Joyce. - * - * Miranda IM: the free icq client for MS Windows - * Copyright (C) 2000-22 Richard Hughes, Roland Rabien & Tristan Van de Vreede - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "stdafx.h" -#include "webview.h" - -MWindowList hWindowList; -HNETLIBUSER hNetlibUser; -HANDLE hHookDisplayDataAlert, hHookAlertPopup, hHookAlertWPopup, hHookErrorPopup, hHookAlertOSD; - -CMPlugin g_plugin; - -static HMODULE hRichEd = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {CD5427FB-5320-4f65-B4BF-86B7CF7B5087} - {0xcd5427fb, 0x5320, 0x4f65, {0xb4, 0xbf, 0x86, 0xb7, 0xcf, 0x7b, 0x50, 0x87}} -}; - -CMPlugin::CMPlugin() : - PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx) -{ - RegisterProtocol(PROTOTYPE_PROTOCOL); - SetUniqueId("PreserveName"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void InitServices() -{ - CreateProtoServiceFunction(MODULENAME, PS_GETCAPS, GetCaps); - CreateProtoServiceFunction(MODULENAME, PS_GETNAME, GetName); - CreateProtoServiceFunction(MODULENAME, PS_LOADICON, BPLoadIcon); - CreateProtoServiceFunction(MODULENAME, PS_SETSTATUS, SetStatus); - CreateProtoServiceFunction(MODULENAME, PS_GETSTATUS, GetStatus); - CreateProtoServiceFunction(MODULENAME, PS_BASICSEARCH, BasicSearch); - CreateProtoServiceFunction(MODULENAME, PS_ADDTOLIST, AddToList); - CreateProtoServiceFunction(MODULENAME, PSS_GETINFO, GetInfo); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void ChangeContactStatus(int con_stat) -{ - uint16_t status_code = 0; - if (con_stat == 0) - status_code = ID_STATUS_OFFLINE; - if (con_stat == 1) - status_code = ID_STATUS_ONLINE; - if (con_stat == 2) - status_code = ID_STATUS_AWAY; - if (con_stat == 3) - status_code = ID_STATUS_NA; - - for (auto &hContact : Contacts(MODULENAME)) - g_plugin.setWord(hContact, "Status", status_code); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPlugin::Load() -{ - HookEvent(ME_CLIST_DOUBLECLICKED, Doubleclick); - - hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); - hRichEd = LoadLibrary(L"Msftedit.dll"); - - /*TIMERS*/ - if ((g_plugin.getDword(REFRESH_KEY, TIME) != 0)) { - timerId = SetTimer(nullptr, 0, ((g_plugin.getDword(REFRESH_KEY, TIME)) * MINUTE), timerfunc); - g_plugin.setDword(COUNTDOWN_KEY, 0); - Countdown = SetTimer(nullptr, 0, MINUTE, Countdownfunc); - } - - InitialiseGlobals(); - - // register netlib handle - NETLIBUSER nlu = {}; - nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS; - nlu.szSettingsModule = MODULENAME; - nlu.szDescriptiveName.a = MODULENAME; - hNetlibUser = Netlib_RegisterUser(&nlu); - - //protocol services - InitServices(); - - //add sound event to options - g_plugin.addSound("webviewalert", _A2W(MODULENAME), LPGENW("Alert event")); - - CMenuItem mi(&g_plugin); - mi.flags = CMIF_UNICODE; - mi.root = g_plugin.addRootMenu(MO_MAIN, _A2W(MODULENAME), 20200001); - Menu_ConfigureItem(mi.root, MCI_OPT_UID, "403BE07B-7954-4F3E-B318-4301571776B8"); - - /*DISABLE WEBVIEW*/ - SET_UID(mi, 0xdedeb697, 0xfc10, 0x4622, 0x8b, 0x97, 0x74, 0x39, 0x32, 0x68, 0xa7, 0x7b); - CreateServiceFunction("DisableWebview", AutoUpdateMCmd); - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_SITE)); - if (g_plugin.getByte(DISABLE_AUTOUPDATE_KEY, 0)) - mi.name.w = LPGENW("Auto update disabled"); - else - mi.name.w = LPGENW("Auto update enabled"); - mi.pszService = "DisableWebview"; - hMenuItem1 = Menu_AddMainMenuItem(&mi); - - // Update all webview contacts - SET_UID(mi, 0xf324ede, 0xfdf, 0x498a, 0x8f, 0x49, 0x6d, 0x2a, 0x9f, 0xda, 0x58, 0x6); - CreateServiceFunction("UpdateAll", UpdateAllMenuCommand); - mi.position = 500090002; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_UPDATEALL)); - mi.name.w = LPGENW("Update all Webview sites"); - mi.pszService = "UpdateAll"; - Menu_AddMainMenuItem(&mi); - - // Mark All Webview Sites As Read - SET_UID(mi, 0x1fa5fa21, 0x2ee1, 0x4372, 0xae, 0x3e, 0x3b, 0x96, 0xac, 0xd, 0xe8, 0x49); - CreateServiceFunction("MarkAllSitesRead", MarkAllReadMenuCommand); - mi.position = 500090099; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_MARKALLREAD)); - mi.name.w = LPGENW("Mark all Webview sites as read"); - mi.pszService = "MarkAllSitesRead"; - Menu_AddMainMenuItem(&mi); - - // open cache directory - SET_UID(mi, 0xfed046a8, 0xaae5, 0x4cbe, 0xa8, 0xc, 0x3c, 0x50, 0x3e, 0x3e, 0x9b, 0x15); - CreateServiceFunction("OpenCacheFolder", OpenCacheDir); - mi.position = 500090099; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_FOLDER)); - mi.name.w = LPGENW("Open cache folder"); - mi.pszService = "OpenCacheFolder"; - Menu_AddMainMenuItem(&mi); - - // Countdown test - SET_UID(mi, 0xbb1a94a9, 0xca63, 0x4966, 0x98, 0x48, 0x8b, 0x3f, 0x9d, 0xac, 0x6a, 0x10); - CreateServiceFunction("Countdown", CountdownMenuCommand); - mi.flags |= CMIF_KEEPUNTRANSLATED; - wchar_t countername[100]; - mir_snwprintf(countername, TranslateT("%d minutes to update"), g_plugin.getDword(COUNTDOWN_KEY, 0)); - mi.position = 600090099; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_UPDATEALL)); - mi.name.w = countername; - mi.pszService = "Countdown"; - hMenuItemCountdown = Menu_AddMainMenuItem(&mi); - - // contact menu - mi.flags = CMIF_UNICODE; - - SET_UID(mi, 0xadc6a9a4, 0xdf7, 0x4f63, 0x89, 0x11, 0x8e, 0x42, 0x1d, 0xd6, 0x29, 0x31); - CreateServiceFunction("Open web page", WebsiteMenuCommand); - mi.position = 100; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_URL)); - mi.pszService = "Open web page"; - mi.name.w = LPGENW("Open web page"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - SET_UID(mi, 0x9d803e61, 0xc929, 0x4c6e, 0x9e, 0x7, 0x93, 0x0, 0xab, 0x14, 0x13, 0x50); - CreateServiceFunction("OpenClose Window", DataWndMenuCommand); - mi.pszService = "OpenClose Window"; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_SHOW_HIDE)); - mi.name.w = LPGENW("Open/Close window"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - SET_UID(mi, 0x3840cc71, 0xcc85, 0x448d, 0xb5, 0xc8, 0x1a, 0x7d, 0xfe, 0xf0, 0x8, 0x85); - mi.position = 2222220; - mi.pszService = "UpdateData"; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_UPDATE)); - mi.name.w = LPGENW("Update data"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - SET_UID(mi, 0xd1ab586c, 0x2c71, 0x429c, 0xb1, 0x79, 0x7b, 0x3a, 0x1d, 0x4a, 0xc1, 0x7d); - CreateServiceFunction("ContactOptions", CntOptionsMenuCommand); - mi.pszService = "ContactOptions"; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_OPTIONS)); - mi.name.w = LPGENW("Contact options"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - SET_UID(mi, 0xe4cda597, 0x9def, 0x4f54, 0x8a, 0xc6, 0x69, 0x3b, 0x5a, 0x7d, 0x77, 0xb6); - CreateServiceFunction("ContactAlertOpts", CntAlertMenuCommand); - mi.pszService = "ContactAlertOpts"; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_ALERT)); - mi.name.w = LPGENW("Contact alert options"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - SET_UID(mi, 0x63fdeed8, 0xf880, 0x423f, 0x95, 0xae, 0x20, 0x8c, 0x86, 0x3c, 0x5, 0xd8); - CreateServiceFunction("PingWebsite", PingWebsiteMenuCommand); - mi.pszService = "PingWebsite"; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_PING)); - mi.name.w = LPGENW("Ping web site"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - SET_UID(mi, 0x28fd36de, 0x6ce1, 0x43d0, 0xa1, 0x6e, 0x98, 0x71, 0x53, 0xe8, 0xc9, 0xf4); - CreateServiceFunction("StopDataProcessing", StpPrcssMenuCommand); - mi.pszService = "StopDataProcessing"; - mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_STOP)); - mi.name.w = LPGENW("Stop data processing"); - Menu_AddContactMenuItem(&mi, MODULENAME); - - hWindowList = WindowList_Create(); - - HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); - HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DBSettingChanged); - HookEvent(ME_DB_CONTACT_DELETED, SiteDeleted); - HookEvent(ME_OPT_INITIALISE, OptInitialise); - - g_plugin.setByte(HAS_CRASHED_KEY, 1); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPlugin::Unload() -{ - ChangeContactStatus(0); - - KillTimer(nullptr, timerId); - KillTimer(nullptr, Countdown); - - g_plugin.setByte(HAS_CRASHED_KEY, 0); - SavewinSettings(); - if (hRichEd) - FreeLibrary(hRichEd); - - if (hNetlibUser) { - Netlib_CloseHandle(hNetlibUser); - hNetlibUser = nullptr; - } - - if (hHookDisplayDataAlert) - DestroyHookableEvent(hHookDisplayDataAlert); - if (hHookAlertPopup) - DestroyHookableEvent(hHookAlertPopup); - if (hHookAlertWPopup) - DestroyHookableEvent(hHookAlertWPopup); - - if (h_font != nullptr) - DeleteObject(h_font); - if (hMenu) - DestroyMenu(hMenu); - WindowList_Destroy(hWindowList); - return 0; -} +/*
+ * A plugin for Miranda IM which displays web page text in a window.
+ * Copyright (C) 2005 Vincent Joyce.
+ *
+ * Miranda IM: the free icq client for MS Windows
+ * Copyright (C) 2000-23 Richard Hughes, Roland Rabien & Tristan Van de Vreede
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "stdafx.h"
+#include "webview.h"
+
+MWindowList hWindowList;
+HNETLIBUSER hNetlibUser;
+HANDLE hHookDisplayDataAlert, hHookAlertPopup, hHookAlertWPopup, hHookErrorPopup, hHookAlertOSD;
+
+CMPlugin g_plugin;
+
+static HMODULE hRichEd = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {CD5427FB-5320-4f65-B4BF-86B7CF7B5087}
+ {0xcd5427fb, 0x5320, 0x4f65, {0xb4, 0xbf, 0x86, 0xb7, 0xcf, 0x7b, 0x50, 0x87}}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx)
+{
+ RegisterProtocol(PROTOTYPE_PROTOCOL);
+ SetUniqueId("PreserveName");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void InitServices()
+{
+ CreateProtoServiceFunction(MODULENAME, PS_GETCAPS, GetCaps);
+ CreateProtoServiceFunction(MODULENAME, PS_GETNAME, GetName);
+ CreateProtoServiceFunction(MODULENAME, PS_LOADICON, BPLoadIcon);
+ CreateProtoServiceFunction(MODULENAME, PS_SETSTATUS, SetStatus);
+ CreateProtoServiceFunction(MODULENAME, PS_GETSTATUS, GetStatus);
+ CreateProtoServiceFunction(MODULENAME, PS_BASICSEARCH, BasicSearch);
+ CreateProtoServiceFunction(MODULENAME, PS_ADDTOLIST, AddToList);
+ CreateProtoServiceFunction(MODULENAME, PSS_GETINFO, GetInfo);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ChangeContactStatus(int con_stat)
+{
+ uint16_t status_code = 0;
+ if (con_stat == 0)
+ status_code = ID_STATUS_OFFLINE;
+ if (con_stat == 1)
+ status_code = ID_STATUS_ONLINE;
+ if (con_stat == 2)
+ status_code = ID_STATUS_AWAY;
+ if (con_stat == 3)
+ status_code = ID_STATUS_NA;
+
+ for (auto &hContact : Contacts(MODULENAME))
+ g_plugin.setWord(hContact, "Status", status_code);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Load()
+{
+ HookEvent(ME_CLIST_DOUBLECLICKED, Doubleclick);
+
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
+ hRichEd = LoadLibrary(L"Msftedit.dll");
+
+ /*TIMERS*/
+ if ((g_plugin.getDword(REFRESH_KEY, TIME) != 0)) {
+ timerId = SetTimer(nullptr, 0, ((g_plugin.getDword(REFRESH_KEY, TIME)) * MINUTE), timerfunc);
+ g_plugin.setDword(COUNTDOWN_KEY, 0);
+ Countdown = SetTimer(nullptr, 0, MINUTE, Countdownfunc);
+ }
+
+ InitialiseGlobals();
+
+ // register netlib handle
+ NETLIBUSER nlu = {};
+ nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS;
+ nlu.szSettingsModule = MODULENAME;
+ nlu.szDescriptiveName.a = MODULENAME;
+ hNetlibUser = Netlib_RegisterUser(&nlu);
+
+ //protocol services
+ InitServices();
+
+ //add sound event to options
+ g_plugin.addSound("webviewalert", _A2W(MODULENAME), LPGENW("Alert event"));
+
+ CMenuItem mi(&g_plugin);
+ mi.flags = CMIF_UNICODE;
+ mi.root = g_plugin.addRootMenu(MO_MAIN, _A2W(MODULENAME), 20200001);
+ Menu_ConfigureItem(mi.root, MCI_OPT_UID, "403BE07B-7954-4F3E-B318-4301571776B8");
+
+ /*DISABLE WEBVIEW*/
+ SET_UID(mi, 0xdedeb697, 0xfc10, 0x4622, 0x8b, 0x97, 0x74, 0x39, 0x32, 0x68, 0xa7, 0x7b);
+ CreateServiceFunction("DisableWebview", AutoUpdateMCmd);
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_SITE));
+ if (g_plugin.getByte(DISABLE_AUTOUPDATE_KEY, 0))
+ mi.name.w = LPGENW("Auto update disabled");
+ else
+ mi.name.w = LPGENW("Auto update enabled");
+ mi.pszService = "DisableWebview";
+ hMenuItem1 = Menu_AddMainMenuItem(&mi);
+
+ // Update all webview contacts
+ SET_UID(mi, 0xf324ede, 0xfdf, 0x498a, 0x8f, 0x49, 0x6d, 0x2a, 0x9f, 0xda, 0x58, 0x6);
+ CreateServiceFunction("UpdateAll", UpdateAllMenuCommand);
+ mi.position = 500090002;
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_UPDATEALL));
+ mi.name.w = LPGENW("Update all Webview sites");
+ mi.pszService = "UpdateAll";
+ Menu_AddMainMenuItem(&mi);
+
+ // Mark All Webview Sites As Read
+ SET_UID(mi, 0x1fa5fa21, 0x2ee1, 0x4372, 0xae, 0x3e, 0x3b, 0x96, 0xac, 0xd, 0xe8, 0x49);
+ CreateServiceFunction("MarkAllSitesRead", MarkAllReadMenuCommand);
+ mi.position = 500090099;
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_MARKALLREAD));
+ mi.name.w = LPGENW("Mark all Webview sites as read");
+ mi.pszService = "MarkAllSitesRead";
+ Menu_AddMainMenuItem(&mi);
+
+ // open cache directory
+ SET_UID(mi, 0xfed046a8, 0xaae5, 0x4cbe, 0xa8, 0xc, 0x3c, 0x50, 0x3e, 0x3e, 0x9b, 0x15);
+ CreateServiceFunction("OpenCacheFolder", OpenCacheDir);
+ mi.position = 500090099;
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_FOLDER));
+ mi.name.w = LPGENW("Open cache folder");
+ mi.pszService = "OpenCacheFolder";
+ Menu_AddMainMenuItem(&mi);
+
+ // Countdown test
+ SET_UID(mi, 0xbb1a94a9, 0xca63, 0x4966, 0x98, 0x48, 0x8b, 0x3f, 0x9d, 0xac, 0x6a, 0x10);
+ CreateServiceFunction("Countdown", CountdownMenuCommand);
+ mi.flags |= CMIF_KEEPUNTRANSLATED;
+ wchar_t countername[100];
+ mir_snwprintf(countername, TranslateT("%d minutes to update"), g_plugin.getDword(COUNTDOWN_KEY, 0));
+ mi.position = 600090099;
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_UPDATEALL));
+ mi.name.w = countername;
+ mi.pszService = "Countdown";
+ hMenuItemCountdown = Menu_AddMainMenuItem(&mi);
+
+ // contact menu
+ mi.flags = CMIF_UNICODE;
+
+ SET_UID(mi, 0xadc6a9a4, 0xdf7, 0x4f63, 0x89, 0x11, 0x8e, 0x42, 0x1d, 0xd6, 0x29, 0x31);
+ CreateServiceFunction("Open web page", WebsiteMenuCommand);
+ mi.position = 100;
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_URL));
+ mi.pszService = "Open web page";
+ mi.name.w = LPGENW("Open web page");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x9d803e61, 0xc929, 0x4c6e, 0x9e, 0x7, 0x93, 0x0, 0xab, 0x14, 0x13, 0x50);
+ CreateServiceFunction("OpenClose Window", DataWndMenuCommand);
+ mi.pszService = "OpenClose Window";
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_SHOW_HIDE));
+ mi.name.w = LPGENW("Open/Close window");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x3840cc71, 0xcc85, 0x448d, 0xb5, 0xc8, 0x1a, 0x7d, 0xfe, 0xf0, 0x8, 0x85);
+ mi.position = 2222220;
+ mi.pszService = "UpdateData";
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_UPDATE));
+ mi.name.w = LPGENW("Update data");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0xd1ab586c, 0x2c71, 0x429c, 0xb1, 0x79, 0x7b, 0x3a, 0x1d, 0x4a, 0xc1, 0x7d);
+ CreateServiceFunction("ContactOptions", CntOptionsMenuCommand);
+ mi.pszService = "ContactOptions";
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_OPTIONS));
+ mi.name.w = LPGENW("Contact options");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0xe4cda597, 0x9def, 0x4f54, 0x8a, 0xc6, 0x69, 0x3b, 0x5a, 0x7d, 0x77, 0xb6);
+ CreateServiceFunction("ContactAlertOpts", CntAlertMenuCommand);
+ mi.pszService = "ContactAlertOpts";
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_ALERT));
+ mi.name.w = LPGENW("Contact alert options");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x63fdeed8, 0xf880, 0x423f, 0x95, 0xae, 0x20, 0x8c, 0x86, 0x3c, 0x5, 0xd8);
+ CreateServiceFunction("PingWebsite", PingWebsiteMenuCommand);
+ mi.pszService = "PingWebsite";
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_PING));
+ mi.name.w = LPGENW("Ping web site");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x28fd36de, 0x6ce1, 0x43d0, 0xa1, 0x6e, 0x98, 0x71, 0x53, 0xe8, 0xc9, 0xf4);
+ CreateServiceFunction("StopDataProcessing", StpPrcssMenuCommand);
+ mi.pszService = "StopDataProcessing";
+ mi.hIcolibItem = LoadIcon(g_plugin.getInst(), MAKEINTRESOURCE(IDI_STOP));
+ mi.name.w = LPGENW("Stop data processing");
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ hWindowList = WindowList_Create();
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DBSettingChanged);
+ HookEvent(ME_DB_CONTACT_DELETED, SiteDeleted);
+ HookEvent(ME_OPT_INITIALISE, OptInitialise);
+
+ g_plugin.setByte(HAS_CRASHED_KEY, 1);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+ ChangeContactStatus(0);
+
+ KillTimer(nullptr, timerId);
+ KillTimer(nullptr, Countdown);
+
+ g_plugin.setByte(HAS_CRASHED_KEY, 0);
+ SavewinSettings();
+ if (hRichEd)
+ FreeLibrary(hRichEd);
+
+ if (hNetlibUser) {
+ Netlib_CloseHandle(hNetlibUser);
+ hNetlibUser = nullptr;
+ }
+
+ if (hHookDisplayDataAlert)
+ DestroyHookableEvent(hHookDisplayDataAlert);
+ if (hHookAlertPopup)
+ DestroyHookableEvent(hHookAlertPopup);
+ if (hHookAlertWPopup)
+ DestroyHookableEvent(hHookAlertWPopup);
+
+ if (h_font != nullptr)
+ DeleteObject(h_font);
+ if (hMenu)
+ DestroyMenu(hMenu);
+ WindowList_Destroy(hWindowList);
+ return 0;
+}
diff --git a/protocols/WebView/src/stdafx.cxx b/protocols/WebView/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/WebView/src/stdafx.cxx +++ b/protocols/WebView/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/protocols/WhatsApp/src/appsync.cpp b/protocols/WhatsApp/src/appsync.cpp index 42e911a3b7..d298f880d2 100644 --- a/protocols/WhatsApp/src/appsync.cpp +++ b/protocols/WhatsApp/src/appsync.cpp @@ -1,7 +1,7 @@ /* WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan +Copyright © 2019-23 George Hazan */ diff --git a/protocols/WhatsApp/src/avatars.cpp b/protocols/WhatsApp/src/avatars.cpp index d2eda4014a..d648bb3801 100644 --- a/protocols/WhatsApp/src/avatars.cpp +++ b/protocols/WhatsApp/src/avatars.cpp @@ -1,169 +1,169 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -void WhatsAppProto::OnIqGetAvatar(const WANode &node) -{ - auto *pUser = FindUser(node.getAttr("from")); - if (pUser == nullptr) - return; - - PROTO_AVATAR_INFORMATION ai = {}; - ai.hContact = pUser->hContact; - ai.format = PA_FORMAT_JPEG; - wcsncpy_s(ai.filename, GetAvatarFileName(pUser->hContact), _TRUNCATE); - - auto *pNode = node.getChild("picture"); - - DWORD dwLastChangeTime = pNode->getAttrInt("id"); - - CMStringA szUrl(pNode->getAttr("url")); - if (szUrl.IsEmpty()) { - setDword(pUser->hContact, DBKEY_AVATAR_TAG, 0); // avatar doesn't exist, don't check it later - -LBL_Error: - ProtoBroadcastAck(pUser->hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE(&ai)); - return; - } - - // if avatar was changed or not present at all, download it - if (dwLastChangeTime > getDword(pUser->hContact, DBKEY_AVATAR_TAG)) { - if (!g_plugin.SaveFile(szUrl, ai)) - goto LBL_Error; - - // set timestamp of avatar being saved - setDword(pUser->hContact, DBKEY_AVATAR_TAG, dwLastChangeTime); - } - ProtoBroadcastAck(pUser->hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE(&ai)); -} - -INT_PTR WhatsAppProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam) -{ - PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION*)lParam; - - ptrA jid(getStringA(pai->hContact, isChatRoom(pai->hContact) ? "ChatRoomID" : DBKEY_ID)); - if (jid == NULL) - return GAIR_NOAVATAR; - - CMStringW tszFileName(GetAvatarFileName(pai->hContact)); - wcsncpy_s(pai->filename, tszFileName.c_str(), _TRUNCATE); - pai->format = PA_FORMAT_JPEG; - - DWORD dwTag = getDword(pai->hContact, DBKEY_AVATAR_TAG, -1); - if (dwTag == -1 || (wParam & GAIF_FORCE) != 0) - if (pai->hContact != NULL && isOnline()) { - ServerFetchAvatar(jid); - return GAIR_WAITFOR; - } - - debugLogA("No avatar"); - return GAIR_NOAVATAR; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR WhatsAppProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam) -{ - switch (wParam) { - case AF_PROPORTION: - return PIP_SQUARE; - - case AF_FORMATSUPPORTED: // Jabber supports avatars of virtually all formats - return PA_FORMAT_JPEG; - - case AF_ENABLED: - return TRUE; - - case AF_MAXSIZE: - POINT *size = (POINT*)lParam; - if (size) - size->x = size->y = 640; - return 0; - } - return -1; -} - -CMStringW WhatsAppProto::GetAvatarFileName(MCONTACT hContact) -{ - CMStringW result = m_tszAvatarFolder + L"\\"; - - CMStringA jid; - if (hContact != NULL) { - ptrA szId(getStringA(hContact, isChatRoom(hContact) ? "ChatRoomID" : DBKEY_ID)); - if (szId == NULL) - return L""; - - jid = szId; - } - else jid = m_szJid; - - return result + _A2T(jid.c_str()) + L".jpg"; -} - -INT_PTR WhatsAppProto::GetMyAvatar(WPARAM wParam, LPARAM lParam) -{ - std::wstring tszOwnAvatar(m_tszAvatarFolder + L"\\myavatar.jpg"); - wcsncpy_s((wchar_t*)wParam, lParam, tszOwnAvatar.c_str(), _TRUNCATE); - return 0; -} - -INT_PTR WhatsAppProto::SetMyAvatar(WPARAM, LPARAM) -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::ServerFetchAvatar(const char *jid) -{ - WANodeIq iq(IQ::GET, "w:profile:picture", jid); - *iq.addChild("picture") << CHAR_PARAM("type", "preview") << CHAR_PARAM("query", "url"); - WSSendNode(iq, &WhatsAppProto::OnIqGetAvatar); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CMPlugin::SaveFile(const char *pszUrl, PROTO_AVATAR_INFORMATION &ai) -{ - NETLIBHTTPREQUEST req = {}; - req.cbSize = sizeof(req); - req.flags = NLHRF_NODUMP | NLHRF_PERSISTENT | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; - req.requestType = REQUEST_GET; - req.szUrl = (char*)pszUrl; - req.nlc = hAvatarConn; - - NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(hAvatarUser, &req); - if (pReply == nullptr) { - hAvatarConn = nullptr; - debugLogA("Failed to retrieve avatar from url: %s", pszUrl); - return false; - } - - hAvatarConn = pReply->nlc; - - bool bSuccess = false; - if (pReply->resultCode == 200 && pReply->pData && pReply->dataLength) { - if (auto *pszHdr = Netlib_GetHeader(pReply, "Content-Type")) - ai.format = ProtoGetAvatarFormatByMimeType(pszHdr); - - if (ai.format != PA_FORMAT_UNKNOWN) { - FILE *fout = _wfopen(ai.filename, L"wb"); - if (fout) { - fwrite(pReply->pData, 1, pReply->dataLength, fout); - fclose(fout); - bSuccess = true; - } - else debugLogA("Error saving avatar to file %S", ai.filename); - } - else debugLogA("unknown avatar mime type"); - } - else debugLogA("Error %d reading avatar from url: %s", pReply->resultCode, pszUrl); - - Netlib_FreeHttpRequest(pReply); - return bSuccess; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+void WhatsAppProto::OnIqGetAvatar(const WANode &node)
+{
+ auto *pUser = FindUser(node.getAttr("from"));
+ if (pUser == nullptr)
+ return;
+
+ PROTO_AVATAR_INFORMATION ai = {};
+ ai.hContact = pUser->hContact;
+ ai.format = PA_FORMAT_JPEG;
+ wcsncpy_s(ai.filename, GetAvatarFileName(pUser->hContact), _TRUNCATE);
+
+ auto *pNode = node.getChild("picture");
+
+ DWORD dwLastChangeTime = pNode->getAttrInt("id");
+
+ CMStringA szUrl(pNode->getAttr("url"));
+ if (szUrl.IsEmpty()) {
+ setDword(pUser->hContact, DBKEY_AVATAR_TAG, 0); // avatar doesn't exist, don't check it later
+
+LBL_Error:
+ ProtoBroadcastAck(pUser->hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE(&ai));
+ return;
+ }
+
+ // if avatar was changed or not present at all, download it
+ if (dwLastChangeTime > getDword(pUser->hContact, DBKEY_AVATAR_TAG)) {
+ if (!g_plugin.SaveFile(szUrl, ai))
+ goto LBL_Error;
+
+ // set timestamp of avatar being saved
+ setDword(pUser->hContact, DBKEY_AVATAR_TAG, dwLastChangeTime);
+ }
+ ProtoBroadcastAck(pUser->hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE(&ai));
+}
+
+INT_PTR WhatsAppProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam)
+{
+ PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION*)lParam;
+
+ ptrA jid(getStringA(pai->hContact, isChatRoom(pai->hContact) ? "ChatRoomID" : DBKEY_ID));
+ if (jid == NULL)
+ return GAIR_NOAVATAR;
+
+ CMStringW tszFileName(GetAvatarFileName(pai->hContact));
+ wcsncpy_s(pai->filename, tszFileName.c_str(), _TRUNCATE);
+ pai->format = PA_FORMAT_JPEG;
+
+ DWORD dwTag = getDword(pai->hContact, DBKEY_AVATAR_TAG, -1);
+ if (dwTag == -1 || (wParam & GAIF_FORCE) != 0)
+ if (pai->hContact != NULL && isOnline()) {
+ ServerFetchAvatar(jid);
+ return GAIR_WAITFOR;
+ }
+
+ debugLogA("No avatar");
+ return GAIR_NOAVATAR;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR WhatsAppProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case AF_PROPORTION:
+ return PIP_SQUARE;
+
+ case AF_FORMATSUPPORTED: // Jabber supports avatars of virtually all formats
+ return PA_FORMAT_JPEG;
+
+ case AF_ENABLED:
+ return TRUE;
+
+ case AF_MAXSIZE:
+ POINT *size = (POINT*)lParam;
+ if (size)
+ size->x = size->y = 640;
+ return 0;
+ }
+ return -1;
+}
+
+CMStringW WhatsAppProto::GetAvatarFileName(MCONTACT hContact)
+{
+ CMStringW result = m_tszAvatarFolder + L"\\";
+
+ CMStringA jid;
+ if (hContact != NULL) {
+ ptrA szId(getStringA(hContact, isChatRoom(hContact) ? "ChatRoomID" : DBKEY_ID));
+ if (szId == NULL)
+ return L"";
+
+ jid = szId;
+ }
+ else jid = m_szJid;
+
+ return result + _A2T(jid.c_str()) + L".jpg";
+}
+
+INT_PTR WhatsAppProto::GetMyAvatar(WPARAM wParam, LPARAM lParam)
+{
+ std::wstring tszOwnAvatar(m_tszAvatarFolder + L"\\myavatar.jpg");
+ wcsncpy_s((wchar_t*)wParam, lParam, tszOwnAvatar.c_str(), _TRUNCATE);
+ return 0;
+}
+
+INT_PTR WhatsAppProto::SetMyAvatar(WPARAM, LPARAM)
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::ServerFetchAvatar(const char *jid)
+{
+ WANodeIq iq(IQ::GET, "w:profile:picture", jid);
+ *iq.addChild("picture") << CHAR_PARAM("type", "preview") << CHAR_PARAM("query", "url");
+ WSSendNode(iq, &WhatsAppProto::OnIqGetAvatar);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CMPlugin::SaveFile(const char *pszUrl, PROTO_AVATAR_INFORMATION &ai)
+{
+ NETLIBHTTPREQUEST req = {};
+ req.cbSize = sizeof(req);
+ req.flags = NLHRF_NODUMP | NLHRF_PERSISTENT | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ req.requestType = REQUEST_GET;
+ req.szUrl = (char*)pszUrl;
+ req.nlc = hAvatarConn;
+
+ NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(hAvatarUser, &req);
+ if (pReply == nullptr) {
+ hAvatarConn = nullptr;
+ debugLogA("Failed to retrieve avatar from url: %s", pszUrl);
+ return false;
+ }
+
+ hAvatarConn = pReply->nlc;
+
+ bool bSuccess = false;
+ if (pReply->resultCode == 200 && pReply->pData && pReply->dataLength) {
+ if (auto *pszHdr = Netlib_GetHeader(pReply, "Content-Type"))
+ ai.format = ProtoGetAvatarFormatByMimeType(pszHdr);
+
+ if (ai.format != PA_FORMAT_UNKNOWN) {
+ FILE *fout = _wfopen(ai.filename, L"wb");
+ if (fout) {
+ fwrite(pReply->pData, 1, pReply->dataLength, fout);
+ fclose(fout);
+ bSuccess = true;
+ }
+ else debugLogA("Error saving avatar to file %S", ai.filename);
+ }
+ else debugLogA("unknown avatar mime type");
+ }
+ else debugLogA("Error %d reading avatar from url: %s", pReply->resultCode, pszUrl);
+
+ Netlib_FreeHttpRequest(pReply);
+ return bSuccess;
+}
diff --git a/protocols/WhatsApp/src/chats.cpp b/protocols/WhatsApp/src/chats.cpp index 5fbe0cf1ef..59f98d4548 100644 --- a/protocols/WhatsApp/src/chats.cpp +++ b/protocols/WhatsApp/src/chats.cpp @@ -1,7 +1,7 @@ /* WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan +Copyright © 2019-23 George Hazan */ diff --git a/protocols/WhatsApp/src/crypt.cpp b/protocols/WhatsApp/src/crypt.cpp index 20e8ddd1bd..049a75844c 100644 --- a/protocols/WhatsApp/src/crypt.cpp +++ b/protocols/WhatsApp/src/crypt.cpp @@ -1,177 +1,177 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -MBinBuffer aesDecrypt( - const EVP_CIPHER *cipher, - const uint8_t *key, - const uint8_t *iv, - const void *data, size_t dataLen, - const void *additionalData, size_t additionalLen) -{ - int tag_len = 0, dec_len = 0, final_len = 0; - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv); - - if (additionalLen) - EVP_DecryptUpdate(ctx, nullptr, &tag_len, (uint8_t *)additionalData, (int)additionalLen); - - if (cipher == EVP_aes_256_gcm()) { - dataLen -= 16; - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (uint8_t *)data + dataLen); - } - - MBinBuffer ret; - uint8_t outbuf[2000]; - for (size_t len = 0; len < dataLen; len += 1024) { - size_t portionSize = dataLen - len; - EVP_DecryptUpdate(ctx, outbuf, &dec_len, (uint8_t *)data + len, (int)min(portionSize, 1024)); - ret.append(outbuf, dec_len); - } - - EVP_DecryptFinal_ex(ctx, outbuf, &final_len); - if (final_len) - ret.append(outbuf, final_len); - - EVP_CIPHER_CTX_free(ctx); - return ret; -} - -MBinBuffer aesEncrypt( - const EVP_CIPHER *cipher, - const uint8_t *key, - const uint8_t *iv, - const void *data, size_t dataLen, - const void *additionalData, size_t additionalLen) -{ - int tag_len = 0, dec_len = 0, final_len = 0; - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv); - - if (additionalLen) - EVP_EncryptUpdate(ctx, nullptr, &tag_len, (uint8_t *)additionalData, (int)additionalLen); - - if (cipher == EVP_aes_256_gcm()) { - dataLen -= 16; - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (uint8_t *)data + dataLen); - } - - MBinBuffer ret; - uint8_t outbuf[2000]; - for (size_t len = 0; len < dataLen; len += 1024) { - size_t portionSize = dataLen - len; - EVP_EncryptUpdate(ctx, outbuf, &dec_len, (uint8_t *)data + len, (int)min(portionSize, 1024)); - ret.append(outbuf, dec_len); - } - - EVP_EncryptFinal_ex(ctx, outbuf, &final_len); - if (final_len) - ret.append(outbuf, final_len); - - EVP_CIPHER_CTX_free(ctx); - return ret; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static unsigned char *HKDF_Extract(const EVP_MD *evp_md, - const unsigned char *salt, size_t salt_len, - const unsigned char *key, size_t key_len, - unsigned char *prk, size_t *prk_len) -{ - unsigned int tmp_len; - - if (!HMAC(evp_md, salt, (int)salt_len, key, (int)key_len, prk, &tmp_len)) - return NULL; - - *prk_len = tmp_len; - return prk; -} - -static unsigned char *HKDF_Expand(const EVP_MD *evp_md, - const unsigned char *prk, size_t prk_len, - const unsigned char *info, size_t info_len, - unsigned char *okm, size_t okm_len) -{ - HMAC_CTX *hmac; - unsigned char *ret = NULL; - - unsigned int i; - - unsigned char prev[EVP_MAX_MD_SIZE]; - - size_t done_len = 0, dig_len = EVP_MD_size(evp_md); - - size_t n = okm_len / dig_len; - if (okm_len % dig_len) - n++; - - if (n > 255 || okm == NULL) - return NULL; - - if ((hmac = HMAC_CTX_new()) == NULL) - return NULL; - - if (!HMAC_Init_ex(hmac, prk, (int)prk_len, evp_md, NULL)) - goto err; - - for (i = 1; i <= n; i++) { - size_t copy_len; - const unsigned char ctr = i; - - if (i > 1) { - if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL)) - goto err; - - if (!HMAC_Update(hmac, prev, dig_len)) - goto err; - } - - if (!HMAC_Update(hmac, info, info_len)) - goto err; - - if (!HMAC_Update(hmac, &ctr, 1)) - goto err; - - if (!HMAC_Final(hmac, prev, NULL)) - goto err; - - copy_len = (done_len + dig_len > okm_len) ? - okm_len - done_len : - dig_len; - - memcpy(okm + done_len, prev, copy_len); - - done_len += copy_len; - } - ret = okm; - -err: - OPENSSL_cleanse(prev, sizeof(prev)); - HMAC_CTX_free(hmac); - return ret; -} - -unsigned char *HKDF(const EVP_MD *evp_md, - const unsigned char *salt, size_t salt_len, - const unsigned char *key, size_t key_len, - const unsigned char *info, size_t info_len, - unsigned char *okm, size_t okm_len) -{ - unsigned char prk[EVP_MAX_MD_SIZE]; - unsigned char *ret; - size_t prk_len; - - if (!HKDF_Extract(evp_md, salt, salt_len, key, key_len, prk, &prk_len)) - return NULL; - - ret = HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len); - OPENSSL_cleanse(prk, sizeof(prk)); - - return ret; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+MBinBuffer aesDecrypt(
+ const EVP_CIPHER *cipher,
+ const uint8_t *key,
+ const uint8_t *iv,
+ const void *data, size_t dataLen,
+ const void *additionalData, size_t additionalLen)
+{
+ int tag_len = 0, dec_len = 0, final_len = 0;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv);
+
+ if (additionalLen)
+ EVP_DecryptUpdate(ctx, nullptr, &tag_len, (uint8_t *)additionalData, (int)additionalLen);
+
+ if (cipher == EVP_aes_256_gcm()) {
+ dataLen -= 16;
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (uint8_t *)data + dataLen);
+ }
+
+ MBinBuffer ret;
+ uint8_t outbuf[2000];
+ for (size_t len = 0; len < dataLen; len += 1024) {
+ size_t portionSize = dataLen - len;
+ EVP_DecryptUpdate(ctx, outbuf, &dec_len, (uint8_t *)data + len, (int)min(portionSize, 1024));
+ ret.append(outbuf, dec_len);
+ }
+
+ EVP_DecryptFinal_ex(ctx, outbuf, &final_len);
+ if (final_len)
+ ret.append(outbuf, final_len);
+
+ EVP_CIPHER_CTX_free(ctx);
+ return ret;
+}
+
+MBinBuffer aesEncrypt(
+ const EVP_CIPHER *cipher,
+ const uint8_t *key,
+ const uint8_t *iv,
+ const void *data, size_t dataLen,
+ const void *additionalData, size_t additionalLen)
+{
+ int tag_len = 0, dec_len = 0, final_len = 0;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
+
+ if (additionalLen)
+ EVP_EncryptUpdate(ctx, nullptr, &tag_len, (uint8_t *)additionalData, (int)additionalLen);
+
+ if (cipher == EVP_aes_256_gcm()) {
+ dataLen -= 16;
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (uint8_t *)data + dataLen);
+ }
+
+ MBinBuffer ret;
+ uint8_t outbuf[2000];
+ for (size_t len = 0; len < dataLen; len += 1024) {
+ size_t portionSize = dataLen - len;
+ EVP_EncryptUpdate(ctx, outbuf, &dec_len, (uint8_t *)data + len, (int)min(portionSize, 1024));
+ ret.append(outbuf, dec_len);
+ }
+
+ EVP_EncryptFinal_ex(ctx, outbuf, &final_len);
+ if (final_len)
+ ret.append(outbuf, final_len);
+
+ EVP_CIPHER_CTX_free(ctx);
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static unsigned char *HKDF_Extract(const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *key, size_t key_len,
+ unsigned char *prk, size_t *prk_len)
+{
+ unsigned int tmp_len;
+
+ if (!HMAC(evp_md, salt, (int)salt_len, key, (int)key_len, prk, &tmp_len))
+ return NULL;
+
+ *prk_len = tmp_len;
+ return prk;
+}
+
+static unsigned char *HKDF_Expand(const EVP_MD *evp_md,
+ const unsigned char *prk, size_t prk_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len)
+{
+ HMAC_CTX *hmac;
+ unsigned char *ret = NULL;
+
+ unsigned int i;
+
+ unsigned char prev[EVP_MAX_MD_SIZE];
+
+ size_t done_len = 0, dig_len = EVP_MD_size(evp_md);
+
+ size_t n = okm_len / dig_len;
+ if (okm_len % dig_len)
+ n++;
+
+ if (n > 255 || okm == NULL)
+ return NULL;
+
+ if ((hmac = HMAC_CTX_new()) == NULL)
+ return NULL;
+
+ if (!HMAC_Init_ex(hmac, prk, (int)prk_len, evp_md, NULL))
+ goto err;
+
+ for (i = 1; i <= n; i++) {
+ size_t copy_len;
+ const unsigned char ctr = i;
+
+ if (i > 1) {
+ if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL))
+ goto err;
+
+ if (!HMAC_Update(hmac, prev, dig_len))
+ goto err;
+ }
+
+ if (!HMAC_Update(hmac, info, info_len))
+ goto err;
+
+ if (!HMAC_Update(hmac, &ctr, 1))
+ goto err;
+
+ if (!HMAC_Final(hmac, prev, NULL))
+ goto err;
+
+ copy_len = (done_len + dig_len > okm_len) ?
+ okm_len - done_len :
+ dig_len;
+
+ memcpy(okm + done_len, prev, copy_len);
+
+ done_len += copy_len;
+ }
+ ret = okm;
+
+err:
+ OPENSSL_cleanse(prev, sizeof(prev));
+ HMAC_CTX_free(hmac);
+ return ret;
+}
+
+unsigned char *HKDF(const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len)
+{
+ unsigned char prk[EVP_MAX_MD_SIZE];
+ unsigned char *ret;
+ size_t prk_len;
+
+ if (!HKDF_Extract(evp_md, salt, salt_len, key, key_len, prk, &prk_len))
+ return NULL;
+
+ ret = HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len);
+ OPENSSL_cleanse(prk, sizeof(prk));
+
+ return ret;
+}
diff --git a/protocols/WhatsApp/src/db.h b/protocols/WhatsApp/src/db.h index cb562715f4..1e4bebc6a8 100644 --- a/protocols/WhatsApp/src/db.h +++ b/protocols/WhatsApp/src/db.h @@ -1,43 +1,43 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan - -*/ - -#define MODULENAME "WhatsApp" - -// DB keys -#define DBKEY_ID "ID" -#define DBKEY_DEVICE_ID "DeviceId" -#define DBKEY_EPHEMERAL_TS "EphemeralTS" -#define DBKEY_EPHEMERAL_EXPIRE "EphemeralExpire" - -#define DBKEY_NOISE_PUB "NoisePublicKey" -#define DBKEY_NOISE_PRIV "NoisePrivateKey" - -#define DBKEY_SIGNED_IDENTITY_PUB "SignedIdentityPublicKey" -#define DBKEY_SIGNED_IDENTITY_PRIV "SignedIdentityPrivateKey" - -#define DBKEY_PREKEY "SignedPreKey1" -#define DBKEY_PREKEY_NEXT_ID "PrekeyNextId" -#define DBKEY_PREKEY_UPLOAD_ID "PrekeyUploadId" - -#define DBKEY_REG_ID "RegistrationId" -#define DBKEY_SECRET_KEY "AdvSecretKey" - -#define DBKEY_NICK "Nick" -#define DBKEY_DEF_GROUP "DefaultGroup" -#define DBKEY_AUTORUNCHATS "AutoRunChats" -#define DBKEY_AVATAR_TAG "AvatarTag" - -#define DBKEY_EVENT_CLIENT_COLBACK "PopupClientColorBack" -#define DBKEY_EVENT_CLIENT_COLTEXT "PopupClientColorText" -#define DBKEY_EVENT_CLIENT_TIMEOUT "PopupClientTimeout" -#define DBKEY_EVENT_CLIENT_DEFAULT "PopupClientColorDefault" - -#define DBKEY_EVENT_OTHER_COLBACK "PopupOtherColorBack" -#define DBKEY_EVENT_OTHER_COLTEXT "PopupOtherColorText" -#define DBKEY_EVENT_OTHER_TIMEOUT "PopupOtherTimeout" -#define DBKEY_EVENT_OTHER_DEFAULT "PopupOtherColorDefault" - +/*
+
+WhatsApp plugin for Miranda NG
+Copyright 2019-23 George Hazan
+
+*/
+
+#define MODULENAME "WhatsApp"
+
+// DB keys
+#define DBKEY_ID "ID"
+#define DBKEY_DEVICE_ID "DeviceId"
+#define DBKEY_EPHEMERAL_TS "EphemeralTS"
+#define DBKEY_EPHEMERAL_EXPIRE "EphemeralExpire"
+
+#define DBKEY_NOISE_PUB "NoisePublicKey"
+#define DBKEY_NOISE_PRIV "NoisePrivateKey"
+
+#define DBKEY_SIGNED_IDENTITY_PUB "SignedIdentityPublicKey"
+#define DBKEY_SIGNED_IDENTITY_PRIV "SignedIdentityPrivateKey"
+
+#define DBKEY_PREKEY "SignedPreKey1"
+#define DBKEY_PREKEY_NEXT_ID "PrekeyNextId"
+#define DBKEY_PREKEY_UPLOAD_ID "PrekeyUploadId"
+
+#define DBKEY_REG_ID "RegistrationId"
+#define DBKEY_SECRET_KEY "AdvSecretKey"
+
+#define DBKEY_NICK "Nick"
+#define DBKEY_DEF_GROUP "DefaultGroup"
+#define DBKEY_AUTORUNCHATS "AutoRunChats"
+#define DBKEY_AVATAR_TAG "AvatarTag"
+
+#define DBKEY_EVENT_CLIENT_COLBACK "PopupClientColorBack"
+#define DBKEY_EVENT_CLIENT_COLTEXT "PopupClientColorText"
+#define DBKEY_EVENT_CLIENT_TIMEOUT "PopupClientTimeout"
+#define DBKEY_EVENT_CLIENT_DEFAULT "PopupClientColorDefault"
+
+#define DBKEY_EVENT_OTHER_COLBACK "PopupOtherColorBack"
+#define DBKEY_EVENT_OTHER_COLTEXT "PopupOtherColorText"
+#define DBKEY_EVENT_OTHER_TIMEOUT "PopupOtherTimeout"
+#define DBKEY_EVENT_OTHER_DEFAULT "PopupOtherColorDefault"
+
diff --git a/protocols/WhatsApp/src/dicts.h b/protocols/WhatsApp/src/dicts.h index 7b84487d14..ab37f10abb 100644 --- a/protocols/WhatsApp/src/dicts.h +++ b/protocols/WhatsApp/src/dicts.h @@ -1,194 +1,194 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan - -*/ - -static char *SingleByteTokens[] = { - "", "xmlstreamstart", "xmlstreamend", "s.whatsapp.net", "type", "participant", "from", "receipt", "id", "broadcast", "status", "message", "notification", "notify", "to", "jid", "user", - "class", "offline", "g.us", "result", "mediatype", "enc", "skmsg", "off_cnt", "xmlns", "presence", "participants", "ack", "t", "iq", "device_hash", "read", "value", "media", "picture", - "chatstate", "unavailable", "text", "urn:xmpp:whatsapp:push", "devices", "verified_name", "contact", "composing", "edge_routing", "routing_info", "item", "image", "verified_level", - "get", "fallback_hostname", "2", "media_conn", "1", "v", "handshake", "fallback_class", "count", "config", "offline_preview", "download_buckets", "w:profile:picture", "set", - "creation", "location", "fallback_ip4", "msg", "urn:xmpp:ping", "fallback_ip6", "call-creator", "relaylatency", "success", "subscribe", "video", "business_hours_config", - "platform", "hostname", "version", "unknown", "0", "ping", "hash", "edit", "subject", "max_buckets", "download", "delivery", "props", "sticker", "name", "last", "contacts", - "business", "primary", "preview", "w:p", "pkmsg", "call-id", "retry", "prop", "call", "auth_ttl", "available", "relay_id", "last_id", "day_of_week", "w", "host", "seen", - "bits", "list", "atn", "upload", "is_new", "w:stats", "key", "paused", "specific_hours", "multicast", "stream:error", "mmg.whatsapp.net", "code", "deny", "played", "profile", - "fna", "device-list", "close_time", "latency", "gcm", "pop", "audio", "26", "w:web", "open_time", "error", "auth", "ip4", "update", "profile_options", "config_value", "category", - "catalog_not_created", "00", "config_code", "mode", "catalog_status", "ip6", "blocklist", "registration", "7", "web", "fail", "w:m", "cart_enabled", "ttl", "gif", "300", - "device_orientation", "identity", "query", "401", "media-gig2-1.cdn.whatsapp.net", "in", "3", "te2", "add", "fallback", "categories", "ptt", "encrypt", "notice", - "thumbnail-document", "item-not-found", "12", "thumbnail-image", "stage", "thumbnail-link", "usync", "out", "thumbnail-video", "8", "01", "context", "sidelist", - "thumbnail-gif", "terminate", "not-authorized", "orientation", "dhash", "capability", "side_list", "md-app-state", "description", "serial", "readreceipts", "te", - "business_hours", "md-msg-hist", "tag", "attribute_padding", "document", "open_24h", "delete", "expiration", "active", "prev_v_id", "true", "passive", "index", "4", - "conflict", "remove", "w:gp2", "config_expo_key", "screen_height", "replaced", "02", "screen_width", "uploadfieldstat", "2:47DEQpj8", "media-bog1-1.cdn.whatsapp.net", - "encopt", "url", "catalog_exists", "keygen", "rate", "offer", "opus", "media-mia3-1.cdn.whatsapp.net", "privacy", "media-mia3-2.cdn.whatsapp.net", "signature", - "preaccept", "token_id", "media-eze1-1.cdn.whatsapp.net" -}; - -static char *dict0[] = { - "media-for1-1.cdn.whatsapp.net", "relay", "media-gru2-2.cdn.whatsapp.net", "uncompressed", "medium", "voip_settings", "device", "reason", - "media-lim1-1.cdn.whatsapp.net", "media-qro1-2.cdn.whatsapp.net", "media-gru1-2.cdn.whatsapp.net", "action", "features", "media-gru2-1.cdn.whatsapp.net", - "media-gru1-1.cdn.whatsapp.net", "media-otp1-1.cdn.whatsapp.net", "kyc-id", "priority", "phash", "mute", "token", "100", "media-qro1-1.cdn.whatsapp.net", - "none", "media-mrs2-2.cdn.whatsapp.net", "sign_credential", "03", "media-mrs2-1.cdn.whatsapp.net", "protocol", "timezone", "transport", "eph_setting", "1080", - "original_dimensions", "media-frx5-1.cdn.whatsapp.net", "background", "disable", "original_image_url", "5", "transaction-id", "direct_path", "103", "appointment_only", - "request_image_url", "peer_pid", "address", "105", "104", "102", "media-cdt1-1.cdn.whatsapp.net", "101", "109", "110", "106", "background_location", "v_id", "sync", - "status-old", "111", "107", "ppic", "media-scl2-1.cdn.whatsapp.net", "business_profile", "108", "invite", "04", "audio_duration", "media-mct1-1.cdn.whatsapp.net", - "media-cdg2-1.cdn.whatsapp.net", "media-los2-1.cdn.whatsapp.net", "invis", "net", "voip_payload_type", "status-revoke-delay", "404", "state", "use_correct_order_for_hmac_sha1", - "ver", "media-mad1-1.cdn.whatsapp.net", "order", "540", "skey", "blinded_credential", "android", "contact_remove", "enable_downlink_relay_latency_only", "duration", - "enable_vid_one_way_codec_nego", "6", "media-sof1-1.cdn.whatsapp.net", "accept", "all", "signed_credential", "media-atl3-1.cdn.whatsapp.net", "media-lhr8-1.cdn.whatsapp.net", - "website", "05", "latitude", "media-dfw5-1.cdn.whatsapp.net", "forbidden", "enable_audio_piggyback_network_mtu_fix", "media-dfw5-2.cdn.whatsapp.net", "note.m4r", - "media-atl3-2.cdn.whatsapp.net", "jb_nack_discard_count_fix", "longitude", "Opening.m4r", "media-arn2-1.cdn.whatsapp.net", "email", "timestamp", "admin", - "media-pmo1-1.cdn.whatsapp.net", "America/Sao_Paulo", "contact_add", "media-sin6-1.cdn.whatsapp.net", "interactive", "8000", "acs_public_key", - "sigquit_anr_detector_release_rollover_percent", "media.fmed1-2.fna.whatsapp.net", "groupadd", "enabled_for_video_upgrade", "latency_update_threshold", - "media-frt3-2.cdn.whatsapp.net", "calls_row_constraint_layout", "media.fgbb2-1.fna.whatsapp.net", "mms4_media_retry_notification_encryption_enabled", "timeout", - "media-sin6-3.cdn.whatsapp.net", "audio_nack_jitter_multiplier", "jb_discard_count_adjust_pct_rc", "audio_reserve_bps", "delta", "account_sync", "default", - "media.fjed4-6.fna.whatsapp.net", "06", "lock_video_orientation", "media-frt3-1.cdn.whatsapp.net", "w:g2", "media-sin6-2.cdn.whatsapp.net", "audio_nack_algo_mask", - "media.fgbb2-2.fna.whatsapp.net", "media.fmed1-1.fna.whatsapp.net", "cond_range_target_bitrate", "mms4_server_error_receipt_encryption_enabled", "vid_rc_dyn", "fri", - "cart_v1_1_order_message_changes_enabled", "reg_push", "jb_hist_deposit_value", "privatestats", "media.fist7-2.fna.whatsapp.net", "thu", "jb_discard_count_adjust_pct", - "mon", "group_call_video_maximization_enabled", "mms_cat_v1_forward_hot_override_enabled", "audio_nack_new_rtt", "media.fsub2-3.fna.whatsapp.net", - "media_upload_aggressive_retry_exponential_backoff_enabled", "tue", "wed", "media.fruh4-2.fna.whatsapp.net", "audio_nack_max_seq_req", "max_rtp_audio_packet_resends", - "jb_hist_max_cdf_value", "07", "audio_nack_max_jb_delay", "mms_forward_partially_downloaded_video", "media-lcy1-1.cdn.whatsapp.net", "resume", "jb_inband_fec_aware", - "new_commerce_entry_point_enabled", "480", "payments_upi_generate_qr_amount_limit", "sigquit_anr_detector_rollover_percent", "media.fsdu2-1.fna.whatsapp.net", "fbns", - "aud_pkt_reorder_pct", "dec", "stop_probing_before_accept_send", "media_upload_max_aggressive_retries", "edit_business_profile_new_mode_enabled", - "media.fhex4-1.fna.whatsapp.net", "media.fjed4-3.fna.whatsapp.net", "sigquit_anr_detector_64bit_rollover_percent", "cond_range_ema_jb_last_delay", - "watls_enable_early_data_http_get", "media.fsdu2-2.fna.whatsapp.net", "message_qr_disambiguation_enabled", "media-mxp1-1.cdn.whatsapp.net", "sat", "vertical", - "media.fruh4-5.fna.whatsapp.net", "200", "media-sof1-2.cdn.whatsapp.net", "-1", "height", "product_catalog_hide_show_items_enabled", "deep_copy_frm_last", - "tsoffline", "vp8/h.264", "media.fgye5-3.fna.whatsapp.net", "media.ftuc1-2.fna.whatsapp.net", "smb_upsell_chat_banner_enabled", "canonical", "08", "9", ".", - "media.fgyd4-4.fna.whatsapp.net", "media.fsti4-1.fna.whatsapp.net", "mms_vcache_aggregation_enabled", "mms_hot_content_timespan_in_seconds", "nse_ver", "rte", - "third_party_sticker_web_sync", "cond_range_target_total_bitrate", "media_upload_aggressive_retry_enabled", "instrument_spam_report_enabled", "disable_reconnect_tone", - "move_media_folder_from_sister_app", "one_tap_calling_in_group_chat_size", "10", "storage_mgmt_banner_threshold_mb", "enable_backup_passive_mode", "sharechat_inline_player_enabled", - "media.fcnq2-1.fna.whatsapp.net", "media.fhex4-2.fna.whatsapp.net", "media.fist6-3.fna.whatsapp.net", "ephemeral_drop_column_stage", "reconnecting_after_network_change_threshold_ms", - "media-lhr8-2.cdn.whatsapp.net", "cond_jb_last_delay_ema_alpha", "entry_point_block_logging_enabled", "critical_event_upload_log_config", "respect_initial_bitrate_estimate", - "smaller_image_thumbs_status_enabled", "media.fbtz1-4.fna.whatsapp.net", "media.fjed4-1.fna.whatsapp.net", "width", "720", "enable_frame_dropper", "enable_one_side_mode", - "urn:xmpp:whatsapp:dirty", "new_sticker_animation_behavior_v2", "media.flim3-2.fna.whatsapp.net", "media.fuio6-2.fna.whatsapp.net", "skip_forced_signaling", "dleq_proof", - "status_video_max_bitrate", "lazy_send_probing_req", "enhanced_storage_management", "android_privatestats_endpoint_dit_enabled", "media.fscl13-2.fna.whatsapp.net", "video_duration" -}; - -static char *dict1[] = { - "group_call_discoverability_enabled", "media.faep9-2.fna.whatsapp.net", "msgr", "bloks_loggedin_access_app_id", "db_status_migration_step", "watls_prefer_ip6", - "jabber:iq:privacy", "68", "media.fsaw1-11.fna.whatsapp.net", "mms4_media_conn_persist_enabled", "animated_stickers_thread_clean_up", "media.fcgk3-2.fna.whatsapp.net", - "media.fcgk4-6.fna.whatsapp.net", "media.fgye5-2.fna.whatsapp.net", "media.flpb1-1.fna.whatsapp.net", "media.fsub2-1.fna.whatsapp.net", "media.fuio6-3.fna.whatsapp.net", - "not-allowed", "partial_pjpeg_bw_threshold", "cap_estimated_bitrate", "mms_chatd_resume_check_over_thrift", "smb_upsell_business_profile_enabled", "product_catalog_webclient", - "groups", "sigquit_anr_detector_release_updated_rollout", "syncd_key_rotation_enabled", "media.fdmm2-1.fna.whatsapp.net", "media-hou1-1.cdn.whatsapp.net", - "remove_old_chat_notifications", "smb_biztools_deeplink_enabled", "use_downloadable_filters_int", "group_qr_codes_enabled", "max_receipt_processing_time", - "optimistic_image_processing_enabled", "smaller_video_thumbs_status_enabled", "watls_early_data", "reconnecting_before_relay_failover_threshold_ms", "cond_range_packet_loss_pct", - "groups_privacy_blacklist", "status-revoke-drop", "stickers_animated_thumbnail_download", "dedupe_transcode_shared_images", "dedupe_transcode_shared_videos", - "media.fcnq2-2.fna.whatsapp.net", "media.fgyd4-1.fna.whatsapp.net", "media.fist7-1.fna.whatsapp.net", "media.flim3-3.fna.whatsapp.net", "add_contact_by_qr_enabled", - "https://faq.whatsapp.com/payments", "multicast_limit_global", "sticker_notification_preview", "smb_better_catalog_list_adapters_enabled", "bloks_use_minscript_android", - "pen_smoothing_enabled", "media.fcgk4-5.fna.whatsapp.net", "media.fevn1-3.fna.whatsapp.net", "media.fpoj7-1.fna.whatsapp.net", "media-arn2-2.cdn.whatsapp.net", - "reconnecting_before_network_change_threshold_ms", "android_media_use_fresco_for_gifs", "cond_in_congestion", "status_image_max_edge", "sticker_search_enabled", - "starred_stickers_web_sync", "db_blank_me_jid_migration_step", "media.fist6-2.fna.whatsapp.net", "media.ftuc1-1.fna.whatsapp.net", "09", "anr_fast_logs_upload_rollout", - "camera_core_integration_enabled", "11", "third_party_sticker_caching", "thread_dump_contact_support", "wam_privatestats_enabled", "vcard_as_document_size_kb", "maxfpp", - "fbip", "ephemeral_allow_group_members", "media-bom1-2.cdn.whatsapp.net", "media-xsp1-1.cdn.whatsapp.net", "disable_prewarm", "frequently_forwarded_max", - "media.fbtz1-5.fna.whatsapp.net", "media.fevn7-1.fna.whatsapp.net", "media.fgyd4-2.fna.whatsapp.net", "sticker_tray_animation_fully_visible_items", - "green_alert_banner_duration", "reconnecting_after_p2p_failover_threshold_ms", "connected", "share_biz_vcard_enabled", "stickers_animation", "0a", "1200", "WhatsApp", - "group_description_length", "p_v_id", "payments_upi_intent_transaction_limit", "frequently_forwarded_messages", "media-xsp1-2.cdn.whatsapp.net", - "media.faep8-1.fna.whatsapp.net", "media.faep8-2.fna.whatsapp.net", "media.faep9-1.fna.whatsapp.net", "media.fdmm2-2.fna.whatsapp.net", "media.fgzt3-1.fna.whatsapp.net", - "media.flim4-2.fna.whatsapp.net", "media.frao1-1.fna.whatsapp.net", "media.fscl9-2.fna.whatsapp.net", "media.fsub2-2.fna.whatsapp.net", "superadmin", - "media.fbog10-1.fna.whatsapp.net", "media.fcgh28-1.fna.whatsapp.net", "media.fjdo10-1.fna.whatsapp.net", "third_party_animated_sticker_import", "delay_fec", - "attachment_picker_refresh", "android_linked_devices_re_auth_enabled", "rc_dyn", "green_alert_block_jitter", "add_contact_logging_enabled", "biz_message_logging_enabled", - "conversation_media_preview_v2", "media-jnb1-1.cdn.whatsapp.net", "ab_key", "media.fcgk4-2.fna.whatsapp.net", "media.fevn1-1.fna.whatsapp.net", "media.fist6-1.fna.whatsapp.net", - "media.fruh4-4.fna.whatsapp.net", "media.fsti4-2.fna.whatsapp.net", "mms_vcard_autodownload_size_kb", "watls_enabled", "notif_ch_override_off", "media.fsaw1-14.fna.whatsapp.net", - "media.fscl13-1.fna.whatsapp.net", "db_group_participant_migration_step", "1020", "cond_range_sterm_rtt", "invites_logging_enabled", "triggered_block_enabled", - "group_call_max_participants", "media-iad3-1.cdn.whatsapp.net", "product_catalog_open_deeplink", "shops_required_tos_version", "image_max_kbytes", - "cond_low_quality_vid_mode", "db_receipt_migration_step", "jb_early_prob_hist_shrink", "media.fdmm2-3.fna.whatsapp.net", "media.fdmm2-4.fna.whatsapp.net", - "media.fruh4-1.fna.whatsapp.net", "media.fsaw2-2.fna.whatsapp.net", "remove_geolocation_videos", "new_animation_behavior", "fieldstats_beacon_chance", "403", - "authkey_reset_on_ban", "continuous_ptt_playback", "reconnecting_after_relay_failover_threshold_ms", "false", "group", "sun", "conversation_swipe_to_reply", - "ephemeral_messages_setting", "smaller_video_thumbs_enabled", "md_device_sync_enabled", "bloks_shops_pdp_url_regex", "lasso_integration_enabled", - "media-bom1-1.cdn.whatsapp.net", "new_backup_format_enabled", "256", "media.faep6-1.fna.whatsapp.net", "media.fasr1-1.fna.whatsapp.net", "media.fbtz1-7.fna.whatsapp.net", - "media.fesb4-1.fna.whatsapp.net", "media.fjdo1-2.fna.whatsapp.net", "media.frba2-1.fna.whatsapp.net", "watls_no_dns", "600", "db_broadcast_me_jid_migration_step", - "new_wam_runtime_enabled", "group_update", "enhanced_block_enabled", "sync_wifi_threshold_kb", "mms_download_nc_cat", "bloks_minification_enabled", - "ephemeral_messages_enabled", "reject", "voip_outgoing_xml_signaling", "creator", "dl_bw", "payments_request_messages", "target_bitrate", "bloks_rendercore_enabled", - "media-hbe1-1.cdn.whatsapp.net", "media-hel3-1.cdn.whatsapp.net", "media-kut2-2.cdn.whatsapp.net", "media-lax3-1.cdn.whatsapp.net", "media-lax3-2.cdn.whatsapp.net", - "sticker_pack_deeplink_enabled", "hq_image_bw_threshold", "status_info", "voip", "dedupe_transcode_videos", "grp_uii_cleanup", "linked_device_max_count", - "media.flim1-1.fna.whatsapp.net", "media.fsaw2-1.fna.whatsapp.net", "reconnecting_after_call_active_threshold_ms", "1140", "catalog_pdp_new_design", - "media.fbtz1-10.fna.whatsapp.net", "media.fsaw1-15.fna.whatsapp.net", "0b", "consumer_rc_provider", "mms_async_fast_forward_ttl", "jb_eff_size_fix", - "voip_incoming_xml_signaling", "media_provider_share_by_uuid", "suspicious_links", "dedupe_transcode_images", "green_alert_modal_start", "media-cgk1-1.cdn.whatsapp.net", - "media-lga3-1.cdn.whatsapp.net", "template_doc_mime_types", "important_messages", "user_add", "vcard_max_size_kb", "media.fada2-1.fna.whatsapp.net", - "media.fbog2-5.fna.whatsapp.net", "media.fbtz1-3.fna.whatsapp.net", "media.fcgk3-1.fna.whatsapp.net", "media.fcgk7-1.fna.whatsapp.net", "media.flim1-3.fna.whatsapp.net", - "media.fscl9-1.fna.whatsapp.net", "ctwa_context_enterprise_enabled", "media.fsaw1-13.fna.whatsapp.net", "media.fuio11-2.fna.whatsapp.net", "status_collapse_muted", - "db_migration_level_force", "recent_stickers_web_sync", "bloks_session_state", "bloks_shops_enabled", "green_alert_setting_deep_links_enabled", "restrict_groups", - "battery", "green_alert_block_start", "refresh", "ctwa_context_enabled", "md_messaging_enabled", "status_image_quality", "md_blocklist_v2_server", - "media-del1-1.cdn.whatsapp.net", "13", "userrate", "a_v_id", "cond_rtt_ema_alpha", "invalid" -}; - -static char *dict2[] = { - "media.fada1-1.fna.whatsapp.net", "media.fadb3-2.fna.whatsapp.net", "media.fbhz2-1.fna.whatsapp.net", "media.fcor2-1.fna.whatsapp.net", "media.fjed4-2.fna.whatsapp.net", - "media.flhe4-1.fna.whatsapp.net", "media.frak1-2.fna.whatsapp.net", "media.fsub6-3.fna.whatsapp.net", "media.fsub6-7.fna.whatsapp.net", "media.fvvi1-1.fna.whatsapp.net", - "search_v5_eligible", "wam_real_time_enabled", "report_disk_event", "max_tx_rott_based_bitrate", "product", "media.fjdo10-2.fna.whatsapp.net", "video_frame_crc_sample_interval", - "media_max_autodownload", "15", "h.264", "wam_privatestats_buffer_count", "md_phash_v2_enabled", "account_transfer_enabled", "business_product_catalog", - "enable_non_dyn_codec_param_fix", "is_user_under_epd_jurisdiction", "media.fbog2-4.fna.whatsapp.net", "media.fbtz1-2.fna.whatsapp.net", "media.fcfc1-1.fna.whatsapp.net", - "media.fjed4-5.fna.whatsapp.net", "media.flhe4-2.fna.whatsapp.net", "media.flim1-2.fna.whatsapp.net", "media.flos5-1.fna.whatsapp.net", "android_key_store_auth_ver", - "010", "anr_process_monitor", "delete_old_auth_key", "media.fcor10-3.fna.whatsapp.net", "storage_usage_enabled", "android_camera2_support_level", "dirty", - "consumer_content_provider", "status_video_max_duration", "0c", "bloks_cache_enabled", "media.fadb2-2.fna.whatsapp.net", "media.fbko1-1.fna.whatsapp.net", - "media.fbtz1-9.fna.whatsapp.net", "media.fcgk4-4.fna.whatsapp.net", "media.fesb4-2.fna.whatsapp.net", "media.fevn1-2.fna.whatsapp.net", "media.fist2-4.fna.whatsapp.net", - "media.fjdo1-1.fna.whatsapp.net", "media.fruh4-6.fna.whatsapp.net", "media.fsrg5-1.fna.whatsapp.net", "media.fsub6-6.fna.whatsapp.net", "minfpp", "5000", "locales", - "video_max_bitrate", "use_new_auth_key", "bloks_http_enabled", "heartbeat_interval", "media.fbog11-1.fna.whatsapp.net", "ephemeral_group_query_ts", "fec_nack", - "search_in_storage_usage", "c", "media-amt2-1.cdn.whatsapp.net", "linked_devices_ui_enabled", "14", "async_data_load_on_startup", "voip_incoming_xml_ack", "16", - "db_migration_step", "init_bwe", "max_participants", "wam_buffer_count", "media.fada2-2.fna.whatsapp.net", "media.fadb3-1.fna.whatsapp.net", "media.fcor2-2.fna.whatsapp.net", - "media.fdiy1-2.fna.whatsapp.net", "media.frba3-2.fna.whatsapp.net", "media.fsaw2-3.fna.whatsapp.net", "1280", "status_grid_enabled", "w:biz", "product_catalog_deeplink", - "media.fgye10-2.fna.whatsapp.net", "media.fuio11-1.fna.whatsapp.net", "optimistic_upload", "work_manager_init", "lc", "catalog_message", "cond_net_medium", - "enable_periodical_aud_rr_processing", "cond_range_ema_rtt", "media-tir2-1.cdn.whatsapp.net", "frame_ms", "group_invite_sending", "payments_web_enabled", - "wallpapers_v2", "0d", "browser", "hq_image_max_edge", "image_edit_zoom", "linked_devices_re_auth_enabled", "media.faly3-2.fna.whatsapp.net", - "media.fdoh5-3.fna.whatsapp.net", "media.fesb3-1.fna.whatsapp.net", "media.fknu1-1.fna.whatsapp.net", "media.fmex3-1.fna.whatsapp.net", "media.fruh4-3.fna.whatsapp.net", - "255", "web_upgrade_to_md_modal", "audio_piggyback_timeout_msec", "enable_audio_oob_fec_feature", "from_ip", "image_max_edge", "message_qr_enabled", "powersave", - "receipt_pre_acking", "video_max_edge", "full", "011", "012", "enable_audio_oob_fec_for_sender", "md_voip_enabled", "enable_privatestats", "max_fec_ratio", - "payments_cs_faq_url", "media-xsp1-3.cdn.whatsapp.net", "hq_image_quality", "media.fasr1-2.fna.whatsapp.net", "media.fbog3-1.fna.whatsapp.net", - "media.ffjr1-6.fna.whatsapp.net", "media.fist2-3.fna.whatsapp.net", "media.flim4-3.fna.whatsapp.net", "media.fpbc2-4.fna.whatsapp.net", "media.fpku1-1.fna.whatsapp.net", - "media.frba1-1.fna.whatsapp.net", "media.fudi1-1.fna.whatsapp.net", "media.fvvi1-2.fna.whatsapp.net", "gcm_fg_service", "enable_dec_ltr_size_check", "clear", "lg", - "media.fgru11-1.fna.whatsapp.net", "18", "media-lga3-2.cdn.whatsapp.net", "pkey", "0e", "max_subject", "cond_range_lterm_rtt", "announcement_groups", "biz_profile_options", - "s_t", "media.fabv2-1.fna.whatsapp.net", "media.fcai3-1.fna.whatsapp.net", "media.fcgh1-1.fna.whatsapp.net", "media.fctg1-4.fna.whatsapp.net", "media.fdiy1-1.fna.whatsapp.net", - "media.fisb4-1.fna.whatsapp.net", "media.fpku1-2.fna.whatsapp.net", "media.fros9-1.fna.whatsapp.net", "status_v3_text", "usync_sidelist", "17", "announcement", "...", - "md_group_notification", "0f", "animated_pack_in_store", "013", "America/Mexico_City", "1260", "media-ams4-1.cdn.whatsapp.net", "media-cgk1-2.cdn.whatsapp.net", - "media-cpt1-1.cdn.whatsapp.net", "media-maa2-1.cdn.whatsapp.net", "media.fgye10-1.fna.whatsapp.net", "e", "catalog_cart", "hfm_string_changes", "init_bitrate", - "packless_hsm", "group_info", "America/Belem", "50", "960", "cond_range_bwe", "decode", "encode", "media.fada1-8.fna.whatsapp.net", "media.fadb1-2.fna.whatsapp.net", - "media.fasu6-1.fna.whatsapp.net", "media.fbog4-1.fna.whatsapp.net", "media.fcgk9-2.fna.whatsapp.net", "media.fdoh5-2.fna.whatsapp.net", "media.ffjr1-2.fna.whatsapp.net", - "media.fgua1-1.fna.whatsapp.net", "media.fgye1-1.fna.whatsapp.net", "media.fist1-4.fna.whatsapp.net", "media.fpbc2-2.fna.whatsapp.net", "media.fres2-1.fna.whatsapp.net", - "media.fsdq1-2.fna.whatsapp.net", "media.fsub6-5.fna.whatsapp.net", "profilo_enabled", "template_hsm", "use_disorder_prefetching_timer", "video_codec_priority", - "vpx_max_qp", "ptt_reduce_recording_delay", "25", "iphone", "Windows", "s_o", "Africa/Lagos", "abt", "media-kut2-1.cdn.whatsapp.net", "media-mba1-1.cdn.whatsapp.net", - "media-mxp1-2.cdn.whatsapp.net", "md_blocklist_v2", "url_text", "enable_short_offset", "group_join_permissions", "enable_audio_piggyback_feature", "image_quality", - "media.fcgk7-2.fna.whatsapp.net", "media.fcgk8-2.fna.whatsapp.net", "media.fclo7-1.fna.whatsapp.net", "media.fcmn1-1.fna.whatsapp.net", "media.feoh1-1.fna.whatsapp.net", - "media.fgyd4-3.fna.whatsapp.net", "media.fjed4-4.fna.whatsapp.net", "media.flim1-4.fna.whatsapp.net", "media.flim2-4.fna.whatsapp.net", "media.fplu6-1.fna.whatsapp.net", - "media.frak1-1.fna.whatsapp.net", "media.fsdq1-1.fna.whatsapp.net", "to_ip", "015", "vp8", "19", "21", "1320", "auth_key_ver", "message_processing_dedup", "server-error", - "wap4_enabled", "420", "014", "cond_range_rtt", "ptt_fast_lock_enabled", "media-ort2-1.cdn.whatsapp.net", "fwd_ui_start_ts" -}; - -static char *dict3[] = { - "contact_blacklist", "Asia/Jakarta", "media.fepa10-1.fna.whatsapp.net", "media.fmex10-3.fna.whatsapp.net", "disorder_prefetching_start_when_empty", "America/Bogota", - "use_local_probing_rx_bitrate", "America/Argentina/Buenos_Aires", "cross_post", "media.fabb1-1.fna.whatsapp.net", "media.fbog4-2.fna.whatsapp.net", "media.fcgk9-1.fna.whatsapp.net", - "media.fcmn2-1.fna.whatsapp.net", "media.fdel3-1.fna.whatsapp.net", "media.ffjr1-1.fna.whatsapp.net", "media.fgdl5-1.fna.whatsapp.net", "media.flpb1-2.fna.whatsapp.net", - "media.fmex2-1.fna.whatsapp.net", "media.frba2-2.fna.whatsapp.net", "media.fros2-2.fna.whatsapp.net", "media.fruh2-1.fna.whatsapp.net", "media.fybz2-2.fna.whatsapp.net", - "options", "20", "a", "017", "018", "mute_always", "user_notice", "Asia/Kolkata", "gif_provider", "locked", "media-gua1-1.cdn.whatsapp.net", "piggyback_exclude_force_flush", - "24", "media.frec39-1.fna.whatsapp.net", "user_remove", "file_max_size", "cond_packet_loss_pct_ema_alpha", "media.facc1-1.fna.whatsapp.net", "media.fadb2-1.fna.whatsapp.net", - "media.faly3-1.fna.whatsapp.net", "media.fbdo6-2.fna.whatsapp.net", "media.fcmn2-2.fna.whatsapp.net", "media.fctg1-3.fna.whatsapp.net", "media.ffez1-2.fna.whatsapp.net", - "media.fist1-3.fna.whatsapp.net", "media.fist2-2.fna.whatsapp.net", "media.flim2-2.fna.whatsapp.net", "media.fmct2-3.fna.whatsapp.net", "media.fpei3-1.fna.whatsapp.net", - "media.frba3-1.fna.whatsapp.net", "media.fsdu8-2.fna.whatsapp.net", "media.fstu2-1.fna.whatsapp.net", "media_type", "receipt_agg", "016", "enable_pli_for_crc_mismatch", - "live", "enc_rekey", "frskmsg", "d", "media.fdel11-2.fna.whatsapp.net", "proto", "2250", "audio_piggyback_enable_cache", "skip_nack_if_ltrp_sent", "mark_dtx_jb_frames", - "web_service_delay", "7282", "catalog_send_all", "outgoing", "360", "30", "LIMITED", "019", "audio_picker", "bpv2_phase", "media.fada1-7.fna.whatsapp.net", - "media.faep7-1.fna.whatsapp.net", "media.fbko1-2.fna.whatsapp.net", "media.fbni1-2.fna.whatsapp.net", "media.fbtz1-1.fna.whatsapp.net", "media.fbtz1-8.fna.whatsapp.net", - "media.fcjs3-1.fna.whatsapp.net", "media.fesb3-2.fna.whatsapp.net", "media.fgdl5-4.fna.whatsapp.net", "media.fist2-1.fna.whatsapp.net", "media.flhe2-2.fna.whatsapp.net", - "media.flim2-1.fna.whatsapp.net", "media.fmex1-1.fna.whatsapp.net", "media.fpat3-2.fna.whatsapp.net", "media.fpat3-3.fna.whatsapp.net", "media.fros2-1.fna.whatsapp.net", - "media.fsdu8-1.fna.whatsapp.net", "media.fsub3-2.fna.whatsapp.net", "payments_chat_plugin", "cond_congestion_no_rtcp_thr", "green_alert", "not-a-biz", "..", - "shops_pdp_urls_config", "source", "media-dus1-1.cdn.whatsapp.net", "mute_video", "01b", "currency", "max_keys", "resume_check", "contact_array", "qr_scanning", "23", - "b", "media.fbfh15-1.fna.whatsapp.net", "media.flim22-1.fna.whatsapp.net", "media.fsdu11-1.fna.whatsapp.net", "media.fsdu15-1.fna.whatsapp.net", "Chrome", "fts_version", - "60", "media.fada1-6.fna.whatsapp.net", "media.faep4-2.fna.whatsapp.net", "media.fbaq5-1.fna.whatsapp.net", "media.fbni1-1.fna.whatsapp.net", "media.fcai3-2.fna.whatsapp.net", - "media.fdel3-2.fna.whatsapp.net", "media.fdmm3-2.fna.whatsapp.net", "media.fhex3-1.fna.whatsapp.net", "media.fisb4-2.fna.whatsapp.net", "media.fkhi5-2.fna.whatsapp.net", - "media.flos2-1.fna.whatsapp.net", "media.fmct2-1.fna.whatsapp.net", "media.fntr7-1.fna.whatsapp.net", "media.frak3-1.fna.whatsapp.net", "media.fruh5-2.fna.whatsapp.net", - "media.fsub6-1.fna.whatsapp.net", "media.fuab1-2.fna.whatsapp.net", "media.fuio1-1.fna.whatsapp.net", "media.fver1-1.fna.whatsapp.net", "media.fymy1-1.fna.whatsapp.net", - "product_catalog", "1380", "audio_oob_fec_max_pkts", "22", "254", "media-ort2-2.cdn.whatsapp.net", "media-sjc3-1.cdn.whatsapp.net", "1600", "01a", "01c", "405", - "key_frame_interval", "body", "media.fcgh20-1.fna.whatsapp.net", "media.fesb10-2.fna.whatsapp.net", "125", "2000", "media.fbsb1-1.fna.whatsapp.net", - "media.fcmn3-2.fna.whatsapp.net", "media.fcpq1-1.fna.whatsapp.net", "media.fdel1-2.fna.whatsapp.net", "media.ffor2-1.fna.whatsapp.net", "media.fgdl1-4.fna.whatsapp.net", - "media.fhex2-1.fna.whatsapp.net", "media.fist1-2.fna.whatsapp.net", "media.fjed5-2.fna.whatsapp.net", "media.flim6-4.fna.whatsapp.net", "media.flos2-2.fna.whatsapp.net", - "media.fntr6-2.fna.whatsapp.net", "media.fpku3-2.fna.whatsapp.net", "media.fros8-1.fna.whatsapp.net", "media.fymy1-2.fna.whatsapp.net", "ul_bw", "ltrp_qp_offset", - "request", "nack", "dtx_delay_state_reset", "timeoffline", "28", "01f", "32", "enable_ltr_pool", "wa_msys_crypto", "01d", "58", "dtx_freeze_hg_update", - "nack_if_rpsi_throttled", "253", "840", "media.famd15-1.fna.whatsapp.net", "media.fbog17-2.fna.whatsapp.net", "media.fcai19-2.fna.whatsapp.net", "media.fcai21-4.fna.whatsapp.net", - "media.fesb10-4.fna.whatsapp.net", "media.fesb10-5.fna.whatsapp.net", "media.fmaa12-1.fna.whatsapp.net", "media.fmex11-3.fna.whatsapp.net", "media.fpoa33-1.fna.whatsapp.net", - "1050", "021", "clean", "cond_range_ema_packet_loss_pct", "media.fadb6-5.fna.whatsapp.net", "media.faqp4-1.fna.whatsapp.net", "media.fbaq3-1.fna.whatsapp.net", - "media.fbel2-1.fna.whatsapp.net", "media.fblr4-2.fna.whatsapp.net", "media.fclo8-1.fna.whatsapp.net", "media.fcoo1-2.fna.whatsapp.net", "media.ffjr1-4.fna.whatsapp.net", - "media.ffor9-1.fna.whatsapp.net", "media.fisb3-1.fna.whatsapp.net", "media.fkhi2-2.fna.whatsapp.net", "media.fkhi4-1.fna.whatsapp.net", "media.fpbc1-2.fna.whatsapp.net", - "media.fruh2-2.fna.whatsapp.net", "media.fruh5-1.fna.whatsapp.net", "media.fsub3-1.fna.whatsapp.net", "payments_transaction_limit", "252", "27", "29", "tintagel", "01e", - "237", "780", "callee_updated_payload", "020", "257", "price", "025", "239", "payments_cs_phone_number", "mediaretry", "w:auth:backup:token", "Glass.caf", "max_bitrate", - "240", "251", "660", "media.fbog16-1.fna.whatsapp.net", "media.fcgh21-1.fna.whatsapp.net", "media.fkul19-2.fna.whatsapp.net", "media.flim21-2.fna.whatsapp.net", - "media.fmex10-4.fna.whatsapp.net", "64", "33", "34", "35", "interruption", "media.fabv3-1.fna.whatsapp.net", "media.fadb6-1.fna.whatsapp.net", "media.fagr1-1.fna.whatsapp.net", - "media.famd1-1.fna.whatsapp.net", "media.famm6-1.fna.whatsapp.net", "media.faqp2-3.fna.whatsapp.net" -}; +/*
+
+WhatsApp plugin for Miranda NG
+Copyright 2019-23 George Hazan
+
+*/
+
+static char *SingleByteTokens[] = {
+ "", "xmlstreamstart", "xmlstreamend", "s.whatsapp.net", "type", "participant", "from", "receipt", "id", "broadcast", "status", "message", "notification", "notify", "to", "jid", "user",
+ "class", "offline", "g.us", "result", "mediatype", "enc", "skmsg", "off_cnt", "xmlns", "presence", "participants", "ack", "t", "iq", "device_hash", "read", "value", "media", "picture",
+ "chatstate", "unavailable", "text", "urn:xmpp:whatsapp:push", "devices", "verified_name", "contact", "composing", "edge_routing", "routing_info", "item", "image", "verified_level",
+ "get", "fallback_hostname", "2", "media_conn", "1", "v", "handshake", "fallback_class", "count", "config", "offline_preview", "download_buckets", "w:profile:picture", "set",
+ "creation", "location", "fallback_ip4", "msg", "urn:xmpp:ping", "fallback_ip6", "call-creator", "relaylatency", "success", "subscribe", "video", "business_hours_config",
+ "platform", "hostname", "version", "unknown", "0", "ping", "hash", "edit", "subject", "max_buckets", "download", "delivery", "props", "sticker", "name", "last", "contacts",
+ "business", "primary", "preview", "w:p", "pkmsg", "call-id", "retry", "prop", "call", "auth_ttl", "available", "relay_id", "last_id", "day_of_week", "w", "host", "seen",
+ "bits", "list", "atn", "upload", "is_new", "w:stats", "key", "paused", "specific_hours", "multicast", "stream:error", "mmg.whatsapp.net", "code", "deny", "played", "profile",
+ "fna", "device-list", "close_time", "latency", "gcm", "pop", "audio", "26", "w:web", "open_time", "error", "auth", "ip4", "update", "profile_options", "config_value", "category",
+ "catalog_not_created", "00", "config_code", "mode", "catalog_status", "ip6", "blocklist", "registration", "7", "web", "fail", "w:m", "cart_enabled", "ttl", "gif", "300",
+ "device_orientation", "identity", "query", "401", "media-gig2-1.cdn.whatsapp.net", "in", "3", "te2", "add", "fallback", "categories", "ptt", "encrypt", "notice",
+ "thumbnail-document", "item-not-found", "12", "thumbnail-image", "stage", "thumbnail-link", "usync", "out", "thumbnail-video", "8", "01", "context", "sidelist",
+ "thumbnail-gif", "terminate", "not-authorized", "orientation", "dhash", "capability", "side_list", "md-app-state", "description", "serial", "readreceipts", "te",
+ "business_hours", "md-msg-hist", "tag", "attribute_padding", "document", "open_24h", "delete", "expiration", "active", "prev_v_id", "true", "passive", "index", "4",
+ "conflict", "remove", "w:gp2", "config_expo_key", "screen_height", "replaced", "02", "screen_width", "uploadfieldstat", "2:47DEQpj8", "media-bog1-1.cdn.whatsapp.net",
+ "encopt", "url", "catalog_exists", "keygen", "rate", "offer", "opus", "media-mia3-1.cdn.whatsapp.net", "privacy", "media-mia3-2.cdn.whatsapp.net", "signature",
+ "preaccept", "token_id", "media-eze1-1.cdn.whatsapp.net"
+};
+
+static char *dict0[] = {
+ "media-for1-1.cdn.whatsapp.net", "relay", "media-gru2-2.cdn.whatsapp.net", "uncompressed", "medium", "voip_settings", "device", "reason",
+ "media-lim1-1.cdn.whatsapp.net", "media-qro1-2.cdn.whatsapp.net", "media-gru1-2.cdn.whatsapp.net", "action", "features", "media-gru2-1.cdn.whatsapp.net",
+ "media-gru1-1.cdn.whatsapp.net", "media-otp1-1.cdn.whatsapp.net", "kyc-id", "priority", "phash", "mute", "token", "100", "media-qro1-1.cdn.whatsapp.net",
+ "none", "media-mrs2-2.cdn.whatsapp.net", "sign_credential", "03", "media-mrs2-1.cdn.whatsapp.net", "protocol", "timezone", "transport", "eph_setting", "1080",
+ "original_dimensions", "media-frx5-1.cdn.whatsapp.net", "background", "disable", "original_image_url", "5", "transaction-id", "direct_path", "103", "appointment_only",
+ "request_image_url", "peer_pid", "address", "105", "104", "102", "media-cdt1-1.cdn.whatsapp.net", "101", "109", "110", "106", "background_location", "v_id", "sync",
+ "status-old", "111", "107", "ppic", "media-scl2-1.cdn.whatsapp.net", "business_profile", "108", "invite", "04", "audio_duration", "media-mct1-1.cdn.whatsapp.net",
+ "media-cdg2-1.cdn.whatsapp.net", "media-los2-1.cdn.whatsapp.net", "invis", "net", "voip_payload_type", "status-revoke-delay", "404", "state", "use_correct_order_for_hmac_sha1",
+ "ver", "media-mad1-1.cdn.whatsapp.net", "order", "540", "skey", "blinded_credential", "android", "contact_remove", "enable_downlink_relay_latency_only", "duration",
+ "enable_vid_one_way_codec_nego", "6", "media-sof1-1.cdn.whatsapp.net", "accept", "all", "signed_credential", "media-atl3-1.cdn.whatsapp.net", "media-lhr8-1.cdn.whatsapp.net",
+ "website", "05", "latitude", "media-dfw5-1.cdn.whatsapp.net", "forbidden", "enable_audio_piggyback_network_mtu_fix", "media-dfw5-2.cdn.whatsapp.net", "note.m4r",
+ "media-atl3-2.cdn.whatsapp.net", "jb_nack_discard_count_fix", "longitude", "Opening.m4r", "media-arn2-1.cdn.whatsapp.net", "email", "timestamp", "admin",
+ "media-pmo1-1.cdn.whatsapp.net", "America/Sao_Paulo", "contact_add", "media-sin6-1.cdn.whatsapp.net", "interactive", "8000", "acs_public_key",
+ "sigquit_anr_detector_release_rollover_percent", "media.fmed1-2.fna.whatsapp.net", "groupadd", "enabled_for_video_upgrade", "latency_update_threshold",
+ "media-frt3-2.cdn.whatsapp.net", "calls_row_constraint_layout", "media.fgbb2-1.fna.whatsapp.net", "mms4_media_retry_notification_encryption_enabled", "timeout",
+ "media-sin6-3.cdn.whatsapp.net", "audio_nack_jitter_multiplier", "jb_discard_count_adjust_pct_rc", "audio_reserve_bps", "delta", "account_sync", "default",
+ "media.fjed4-6.fna.whatsapp.net", "06", "lock_video_orientation", "media-frt3-1.cdn.whatsapp.net", "w:g2", "media-sin6-2.cdn.whatsapp.net", "audio_nack_algo_mask",
+ "media.fgbb2-2.fna.whatsapp.net", "media.fmed1-1.fna.whatsapp.net", "cond_range_target_bitrate", "mms4_server_error_receipt_encryption_enabled", "vid_rc_dyn", "fri",
+ "cart_v1_1_order_message_changes_enabled", "reg_push", "jb_hist_deposit_value", "privatestats", "media.fist7-2.fna.whatsapp.net", "thu", "jb_discard_count_adjust_pct",
+ "mon", "group_call_video_maximization_enabled", "mms_cat_v1_forward_hot_override_enabled", "audio_nack_new_rtt", "media.fsub2-3.fna.whatsapp.net",
+ "media_upload_aggressive_retry_exponential_backoff_enabled", "tue", "wed", "media.fruh4-2.fna.whatsapp.net", "audio_nack_max_seq_req", "max_rtp_audio_packet_resends",
+ "jb_hist_max_cdf_value", "07", "audio_nack_max_jb_delay", "mms_forward_partially_downloaded_video", "media-lcy1-1.cdn.whatsapp.net", "resume", "jb_inband_fec_aware",
+ "new_commerce_entry_point_enabled", "480", "payments_upi_generate_qr_amount_limit", "sigquit_anr_detector_rollover_percent", "media.fsdu2-1.fna.whatsapp.net", "fbns",
+ "aud_pkt_reorder_pct", "dec", "stop_probing_before_accept_send", "media_upload_max_aggressive_retries", "edit_business_profile_new_mode_enabled",
+ "media.fhex4-1.fna.whatsapp.net", "media.fjed4-3.fna.whatsapp.net", "sigquit_anr_detector_64bit_rollover_percent", "cond_range_ema_jb_last_delay",
+ "watls_enable_early_data_http_get", "media.fsdu2-2.fna.whatsapp.net", "message_qr_disambiguation_enabled", "media-mxp1-1.cdn.whatsapp.net", "sat", "vertical",
+ "media.fruh4-5.fna.whatsapp.net", "200", "media-sof1-2.cdn.whatsapp.net", "-1", "height", "product_catalog_hide_show_items_enabled", "deep_copy_frm_last",
+ "tsoffline", "vp8/h.264", "media.fgye5-3.fna.whatsapp.net", "media.ftuc1-2.fna.whatsapp.net", "smb_upsell_chat_banner_enabled", "canonical", "08", "9", ".",
+ "media.fgyd4-4.fna.whatsapp.net", "media.fsti4-1.fna.whatsapp.net", "mms_vcache_aggregation_enabled", "mms_hot_content_timespan_in_seconds", "nse_ver", "rte",
+ "third_party_sticker_web_sync", "cond_range_target_total_bitrate", "media_upload_aggressive_retry_enabled", "instrument_spam_report_enabled", "disable_reconnect_tone",
+ "move_media_folder_from_sister_app", "one_tap_calling_in_group_chat_size", "10", "storage_mgmt_banner_threshold_mb", "enable_backup_passive_mode", "sharechat_inline_player_enabled",
+ "media.fcnq2-1.fna.whatsapp.net", "media.fhex4-2.fna.whatsapp.net", "media.fist6-3.fna.whatsapp.net", "ephemeral_drop_column_stage", "reconnecting_after_network_change_threshold_ms",
+ "media-lhr8-2.cdn.whatsapp.net", "cond_jb_last_delay_ema_alpha", "entry_point_block_logging_enabled", "critical_event_upload_log_config", "respect_initial_bitrate_estimate",
+ "smaller_image_thumbs_status_enabled", "media.fbtz1-4.fna.whatsapp.net", "media.fjed4-1.fna.whatsapp.net", "width", "720", "enable_frame_dropper", "enable_one_side_mode",
+ "urn:xmpp:whatsapp:dirty", "new_sticker_animation_behavior_v2", "media.flim3-2.fna.whatsapp.net", "media.fuio6-2.fna.whatsapp.net", "skip_forced_signaling", "dleq_proof",
+ "status_video_max_bitrate", "lazy_send_probing_req", "enhanced_storage_management", "android_privatestats_endpoint_dit_enabled", "media.fscl13-2.fna.whatsapp.net", "video_duration"
+};
+
+static char *dict1[] = {
+ "group_call_discoverability_enabled", "media.faep9-2.fna.whatsapp.net", "msgr", "bloks_loggedin_access_app_id", "db_status_migration_step", "watls_prefer_ip6",
+ "jabber:iq:privacy", "68", "media.fsaw1-11.fna.whatsapp.net", "mms4_media_conn_persist_enabled", "animated_stickers_thread_clean_up", "media.fcgk3-2.fna.whatsapp.net",
+ "media.fcgk4-6.fna.whatsapp.net", "media.fgye5-2.fna.whatsapp.net", "media.flpb1-1.fna.whatsapp.net", "media.fsub2-1.fna.whatsapp.net", "media.fuio6-3.fna.whatsapp.net",
+ "not-allowed", "partial_pjpeg_bw_threshold", "cap_estimated_bitrate", "mms_chatd_resume_check_over_thrift", "smb_upsell_business_profile_enabled", "product_catalog_webclient",
+ "groups", "sigquit_anr_detector_release_updated_rollout", "syncd_key_rotation_enabled", "media.fdmm2-1.fna.whatsapp.net", "media-hou1-1.cdn.whatsapp.net",
+ "remove_old_chat_notifications", "smb_biztools_deeplink_enabled", "use_downloadable_filters_int", "group_qr_codes_enabled", "max_receipt_processing_time",
+ "optimistic_image_processing_enabled", "smaller_video_thumbs_status_enabled", "watls_early_data", "reconnecting_before_relay_failover_threshold_ms", "cond_range_packet_loss_pct",
+ "groups_privacy_blacklist", "status-revoke-drop", "stickers_animated_thumbnail_download", "dedupe_transcode_shared_images", "dedupe_transcode_shared_videos",
+ "media.fcnq2-2.fna.whatsapp.net", "media.fgyd4-1.fna.whatsapp.net", "media.fist7-1.fna.whatsapp.net", "media.flim3-3.fna.whatsapp.net", "add_contact_by_qr_enabled",
+ "https://faq.whatsapp.com/payments", "multicast_limit_global", "sticker_notification_preview", "smb_better_catalog_list_adapters_enabled", "bloks_use_minscript_android",
+ "pen_smoothing_enabled", "media.fcgk4-5.fna.whatsapp.net", "media.fevn1-3.fna.whatsapp.net", "media.fpoj7-1.fna.whatsapp.net", "media-arn2-2.cdn.whatsapp.net",
+ "reconnecting_before_network_change_threshold_ms", "android_media_use_fresco_for_gifs", "cond_in_congestion", "status_image_max_edge", "sticker_search_enabled",
+ "starred_stickers_web_sync", "db_blank_me_jid_migration_step", "media.fist6-2.fna.whatsapp.net", "media.ftuc1-1.fna.whatsapp.net", "09", "anr_fast_logs_upload_rollout",
+ "camera_core_integration_enabled", "11", "third_party_sticker_caching", "thread_dump_contact_support", "wam_privatestats_enabled", "vcard_as_document_size_kb", "maxfpp",
+ "fbip", "ephemeral_allow_group_members", "media-bom1-2.cdn.whatsapp.net", "media-xsp1-1.cdn.whatsapp.net", "disable_prewarm", "frequently_forwarded_max",
+ "media.fbtz1-5.fna.whatsapp.net", "media.fevn7-1.fna.whatsapp.net", "media.fgyd4-2.fna.whatsapp.net", "sticker_tray_animation_fully_visible_items",
+ "green_alert_banner_duration", "reconnecting_after_p2p_failover_threshold_ms", "connected", "share_biz_vcard_enabled", "stickers_animation", "0a", "1200", "WhatsApp",
+ "group_description_length", "p_v_id", "payments_upi_intent_transaction_limit", "frequently_forwarded_messages", "media-xsp1-2.cdn.whatsapp.net",
+ "media.faep8-1.fna.whatsapp.net", "media.faep8-2.fna.whatsapp.net", "media.faep9-1.fna.whatsapp.net", "media.fdmm2-2.fna.whatsapp.net", "media.fgzt3-1.fna.whatsapp.net",
+ "media.flim4-2.fna.whatsapp.net", "media.frao1-1.fna.whatsapp.net", "media.fscl9-2.fna.whatsapp.net", "media.fsub2-2.fna.whatsapp.net", "superadmin",
+ "media.fbog10-1.fna.whatsapp.net", "media.fcgh28-1.fna.whatsapp.net", "media.fjdo10-1.fna.whatsapp.net", "third_party_animated_sticker_import", "delay_fec",
+ "attachment_picker_refresh", "android_linked_devices_re_auth_enabled", "rc_dyn", "green_alert_block_jitter", "add_contact_logging_enabled", "biz_message_logging_enabled",
+ "conversation_media_preview_v2", "media-jnb1-1.cdn.whatsapp.net", "ab_key", "media.fcgk4-2.fna.whatsapp.net", "media.fevn1-1.fna.whatsapp.net", "media.fist6-1.fna.whatsapp.net",
+ "media.fruh4-4.fna.whatsapp.net", "media.fsti4-2.fna.whatsapp.net", "mms_vcard_autodownload_size_kb", "watls_enabled", "notif_ch_override_off", "media.fsaw1-14.fna.whatsapp.net",
+ "media.fscl13-1.fna.whatsapp.net", "db_group_participant_migration_step", "1020", "cond_range_sterm_rtt", "invites_logging_enabled", "triggered_block_enabled",
+ "group_call_max_participants", "media-iad3-1.cdn.whatsapp.net", "product_catalog_open_deeplink", "shops_required_tos_version", "image_max_kbytes",
+ "cond_low_quality_vid_mode", "db_receipt_migration_step", "jb_early_prob_hist_shrink", "media.fdmm2-3.fna.whatsapp.net", "media.fdmm2-4.fna.whatsapp.net",
+ "media.fruh4-1.fna.whatsapp.net", "media.fsaw2-2.fna.whatsapp.net", "remove_geolocation_videos", "new_animation_behavior", "fieldstats_beacon_chance", "403",
+ "authkey_reset_on_ban", "continuous_ptt_playback", "reconnecting_after_relay_failover_threshold_ms", "false", "group", "sun", "conversation_swipe_to_reply",
+ "ephemeral_messages_setting", "smaller_video_thumbs_enabled", "md_device_sync_enabled", "bloks_shops_pdp_url_regex", "lasso_integration_enabled",
+ "media-bom1-1.cdn.whatsapp.net", "new_backup_format_enabled", "256", "media.faep6-1.fna.whatsapp.net", "media.fasr1-1.fna.whatsapp.net", "media.fbtz1-7.fna.whatsapp.net",
+ "media.fesb4-1.fna.whatsapp.net", "media.fjdo1-2.fna.whatsapp.net", "media.frba2-1.fna.whatsapp.net", "watls_no_dns", "600", "db_broadcast_me_jid_migration_step",
+ "new_wam_runtime_enabled", "group_update", "enhanced_block_enabled", "sync_wifi_threshold_kb", "mms_download_nc_cat", "bloks_minification_enabled",
+ "ephemeral_messages_enabled", "reject", "voip_outgoing_xml_signaling", "creator", "dl_bw", "payments_request_messages", "target_bitrate", "bloks_rendercore_enabled",
+ "media-hbe1-1.cdn.whatsapp.net", "media-hel3-1.cdn.whatsapp.net", "media-kut2-2.cdn.whatsapp.net", "media-lax3-1.cdn.whatsapp.net", "media-lax3-2.cdn.whatsapp.net",
+ "sticker_pack_deeplink_enabled", "hq_image_bw_threshold", "status_info", "voip", "dedupe_transcode_videos", "grp_uii_cleanup", "linked_device_max_count",
+ "media.flim1-1.fna.whatsapp.net", "media.fsaw2-1.fna.whatsapp.net", "reconnecting_after_call_active_threshold_ms", "1140", "catalog_pdp_new_design",
+ "media.fbtz1-10.fna.whatsapp.net", "media.fsaw1-15.fna.whatsapp.net", "0b", "consumer_rc_provider", "mms_async_fast_forward_ttl", "jb_eff_size_fix",
+ "voip_incoming_xml_signaling", "media_provider_share_by_uuid", "suspicious_links", "dedupe_transcode_images", "green_alert_modal_start", "media-cgk1-1.cdn.whatsapp.net",
+ "media-lga3-1.cdn.whatsapp.net", "template_doc_mime_types", "important_messages", "user_add", "vcard_max_size_kb", "media.fada2-1.fna.whatsapp.net",
+ "media.fbog2-5.fna.whatsapp.net", "media.fbtz1-3.fna.whatsapp.net", "media.fcgk3-1.fna.whatsapp.net", "media.fcgk7-1.fna.whatsapp.net", "media.flim1-3.fna.whatsapp.net",
+ "media.fscl9-1.fna.whatsapp.net", "ctwa_context_enterprise_enabled", "media.fsaw1-13.fna.whatsapp.net", "media.fuio11-2.fna.whatsapp.net", "status_collapse_muted",
+ "db_migration_level_force", "recent_stickers_web_sync", "bloks_session_state", "bloks_shops_enabled", "green_alert_setting_deep_links_enabled", "restrict_groups",
+ "battery", "green_alert_block_start", "refresh", "ctwa_context_enabled", "md_messaging_enabled", "status_image_quality", "md_blocklist_v2_server",
+ "media-del1-1.cdn.whatsapp.net", "13", "userrate", "a_v_id", "cond_rtt_ema_alpha", "invalid"
+};
+
+static char *dict2[] = {
+ "media.fada1-1.fna.whatsapp.net", "media.fadb3-2.fna.whatsapp.net", "media.fbhz2-1.fna.whatsapp.net", "media.fcor2-1.fna.whatsapp.net", "media.fjed4-2.fna.whatsapp.net",
+ "media.flhe4-1.fna.whatsapp.net", "media.frak1-2.fna.whatsapp.net", "media.fsub6-3.fna.whatsapp.net", "media.fsub6-7.fna.whatsapp.net", "media.fvvi1-1.fna.whatsapp.net",
+ "search_v5_eligible", "wam_real_time_enabled", "report_disk_event", "max_tx_rott_based_bitrate", "product", "media.fjdo10-2.fna.whatsapp.net", "video_frame_crc_sample_interval",
+ "media_max_autodownload", "15", "h.264", "wam_privatestats_buffer_count", "md_phash_v2_enabled", "account_transfer_enabled", "business_product_catalog",
+ "enable_non_dyn_codec_param_fix", "is_user_under_epd_jurisdiction", "media.fbog2-4.fna.whatsapp.net", "media.fbtz1-2.fna.whatsapp.net", "media.fcfc1-1.fna.whatsapp.net",
+ "media.fjed4-5.fna.whatsapp.net", "media.flhe4-2.fna.whatsapp.net", "media.flim1-2.fna.whatsapp.net", "media.flos5-1.fna.whatsapp.net", "android_key_store_auth_ver",
+ "010", "anr_process_monitor", "delete_old_auth_key", "media.fcor10-3.fna.whatsapp.net", "storage_usage_enabled", "android_camera2_support_level", "dirty",
+ "consumer_content_provider", "status_video_max_duration", "0c", "bloks_cache_enabled", "media.fadb2-2.fna.whatsapp.net", "media.fbko1-1.fna.whatsapp.net",
+ "media.fbtz1-9.fna.whatsapp.net", "media.fcgk4-4.fna.whatsapp.net", "media.fesb4-2.fna.whatsapp.net", "media.fevn1-2.fna.whatsapp.net", "media.fist2-4.fna.whatsapp.net",
+ "media.fjdo1-1.fna.whatsapp.net", "media.fruh4-6.fna.whatsapp.net", "media.fsrg5-1.fna.whatsapp.net", "media.fsub6-6.fna.whatsapp.net", "minfpp", "5000", "locales",
+ "video_max_bitrate", "use_new_auth_key", "bloks_http_enabled", "heartbeat_interval", "media.fbog11-1.fna.whatsapp.net", "ephemeral_group_query_ts", "fec_nack",
+ "search_in_storage_usage", "c", "media-amt2-1.cdn.whatsapp.net", "linked_devices_ui_enabled", "14", "async_data_load_on_startup", "voip_incoming_xml_ack", "16",
+ "db_migration_step", "init_bwe", "max_participants", "wam_buffer_count", "media.fada2-2.fna.whatsapp.net", "media.fadb3-1.fna.whatsapp.net", "media.fcor2-2.fna.whatsapp.net",
+ "media.fdiy1-2.fna.whatsapp.net", "media.frba3-2.fna.whatsapp.net", "media.fsaw2-3.fna.whatsapp.net", "1280", "status_grid_enabled", "w:biz", "product_catalog_deeplink",
+ "media.fgye10-2.fna.whatsapp.net", "media.fuio11-1.fna.whatsapp.net", "optimistic_upload", "work_manager_init", "lc", "catalog_message", "cond_net_medium",
+ "enable_periodical_aud_rr_processing", "cond_range_ema_rtt", "media-tir2-1.cdn.whatsapp.net", "frame_ms", "group_invite_sending", "payments_web_enabled",
+ "wallpapers_v2", "0d", "browser", "hq_image_max_edge", "image_edit_zoom", "linked_devices_re_auth_enabled", "media.faly3-2.fna.whatsapp.net",
+ "media.fdoh5-3.fna.whatsapp.net", "media.fesb3-1.fna.whatsapp.net", "media.fknu1-1.fna.whatsapp.net", "media.fmex3-1.fna.whatsapp.net", "media.fruh4-3.fna.whatsapp.net",
+ "255", "web_upgrade_to_md_modal", "audio_piggyback_timeout_msec", "enable_audio_oob_fec_feature", "from_ip", "image_max_edge", "message_qr_enabled", "powersave",
+ "receipt_pre_acking", "video_max_edge", "full", "011", "012", "enable_audio_oob_fec_for_sender", "md_voip_enabled", "enable_privatestats", "max_fec_ratio",
+ "payments_cs_faq_url", "media-xsp1-3.cdn.whatsapp.net", "hq_image_quality", "media.fasr1-2.fna.whatsapp.net", "media.fbog3-1.fna.whatsapp.net",
+ "media.ffjr1-6.fna.whatsapp.net", "media.fist2-3.fna.whatsapp.net", "media.flim4-3.fna.whatsapp.net", "media.fpbc2-4.fna.whatsapp.net", "media.fpku1-1.fna.whatsapp.net",
+ "media.frba1-1.fna.whatsapp.net", "media.fudi1-1.fna.whatsapp.net", "media.fvvi1-2.fna.whatsapp.net", "gcm_fg_service", "enable_dec_ltr_size_check", "clear", "lg",
+ "media.fgru11-1.fna.whatsapp.net", "18", "media-lga3-2.cdn.whatsapp.net", "pkey", "0e", "max_subject", "cond_range_lterm_rtt", "announcement_groups", "biz_profile_options",
+ "s_t", "media.fabv2-1.fna.whatsapp.net", "media.fcai3-1.fna.whatsapp.net", "media.fcgh1-1.fna.whatsapp.net", "media.fctg1-4.fna.whatsapp.net", "media.fdiy1-1.fna.whatsapp.net",
+ "media.fisb4-1.fna.whatsapp.net", "media.fpku1-2.fna.whatsapp.net", "media.fros9-1.fna.whatsapp.net", "status_v3_text", "usync_sidelist", "17", "announcement", "...",
+ "md_group_notification", "0f", "animated_pack_in_store", "013", "America/Mexico_City", "1260", "media-ams4-1.cdn.whatsapp.net", "media-cgk1-2.cdn.whatsapp.net",
+ "media-cpt1-1.cdn.whatsapp.net", "media-maa2-1.cdn.whatsapp.net", "media.fgye10-1.fna.whatsapp.net", "e", "catalog_cart", "hfm_string_changes", "init_bitrate",
+ "packless_hsm", "group_info", "America/Belem", "50", "960", "cond_range_bwe", "decode", "encode", "media.fada1-8.fna.whatsapp.net", "media.fadb1-2.fna.whatsapp.net",
+ "media.fasu6-1.fna.whatsapp.net", "media.fbog4-1.fna.whatsapp.net", "media.fcgk9-2.fna.whatsapp.net", "media.fdoh5-2.fna.whatsapp.net", "media.ffjr1-2.fna.whatsapp.net",
+ "media.fgua1-1.fna.whatsapp.net", "media.fgye1-1.fna.whatsapp.net", "media.fist1-4.fna.whatsapp.net", "media.fpbc2-2.fna.whatsapp.net", "media.fres2-1.fna.whatsapp.net",
+ "media.fsdq1-2.fna.whatsapp.net", "media.fsub6-5.fna.whatsapp.net", "profilo_enabled", "template_hsm", "use_disorder_prefetching_timer", "video_codec_priority",
+ "vpx_max_qp", "ptt_reduce_recording_delay", "25", "iphone", "Windows", "s_o", "Africa/Lagos", "abt", "media-kut2-1.cdn.whatsapp.net", "media-mba1-1.cdn.whatsapp.net",
+ "media-mxp1-2.cdn.whatsapp.net", "md_blocklist_v2", "url_text", "enable_short_offset", "group_join_permissions", "enable_audio_piggyback_feature", "image_quality",
+ "media.fcgk7-2.fna.whatsapp.net", "media.fcgk8-2.fna.whatsapp.net", "media.fclo7-1.fna.whatsapp.net", "media.fcmn1-1.fna.whatsapp.net", "media.feoh1-1.fna.whatsapp.net",
+ "media.fgyd4-3.fna.whatsapp.net", "media.fjed4-4.fna.whatsapp.net", "media.flim1-4.fna.whatsapp.net", "media.flim2-4.fna.whatsapp.net", "media.fplu6-1.fna.whatsapp.net",
+ "media.frak1-1.fna.whatsapp.net", "media.fsdq1-1.fna.whatsapp.net", "to_ip", "015", "vp8", "19", "21", "1320", "auth_key_ver", "message_processing_dedup", "server-error",
+ "wap4_enabled", "420", "014", "cond_range_rtt", "ptt_fast_lock_enabled", "media-ort2-1.cdn.whatsapp.net", "fwd_ui_start_ts"
+};
+
+static char *dict3[] = {
+ "contact_blacklist", "Asia/Jakarta", "media.fepa10-1.fna.whatsapp.net", "media.fmex10-3.fna.whatsapp.net", "disorder_prefetching_start_when_empty", "America/Bogota",
+ "use_local_probing_rx_bitrate", "America/Argentina/Buenos_Aires", "cross_post", "media.fabb1-1.fna.whatsapp.net", "media.fbog4-2.fna.whatsapp.net", "media.fcgk9-1.fna.whatsapp.net",
+ "media.fcmn2-1.fna.whatsapp.net", "media.fdel3-1.fna.whatsapp.net", "media.ffjr1-1.fna.whatsapp.net", "media.fgdl5-1.fna.whatsapp.net", "media.flpb1-2.fna.whatsapp.net",
+ "media.fmex2-1.fna.whatsapp.net", "media.frba2-2.fna.whatsapp.net", "media.fros2-2.fna.whatsapp.net", "media.fruh2-1.fna.whatsapp.net", "media.fybz2-2.fna.whatsapp.net",
+ "options", "20", "a", "017", "018", "mute_always", "user_notice", "Asia/Kolkata", "gif_provider", "locked", "media-gua1-1.cdn.whatsapp.net", "piggyback_exclude_force_flush",
+ "24", "media.frec39-1.fna.whatsapp.net", "user_remove", "file_max_size", "cond_packet_loss_pct_ema_alpha", "media.facc1-1.fna.whatsapp.net", "media.fadb2-1.fna.whatsapp.net",
+ "media.faly3-1.fna.whatsapp.net", "media.fbdo6-2.fna.whatsapp.net", "media.fcmn2-2.fna.whatsapp.net", "media.fctg1-3.fna.whatsapp.net", "media.ffez1-2.fna.whatsapp.net",
+ "media.fist1-3.fna.whatsapp.net", "media.fist2-2.fna.whatsapp.net", "media.flim2-2.fna.whatsapp.net", "media.fmct2-3.fna.whatsapp.net", "media.fpei3-1.fna.whatsapp.net",
+ "media.frba3-1.fna.whatsapp.net", "media.fsdu8-2.fna.whatsapp.net", "media.fstu2-1.fna.whatsapp.net", "media_type", "receipt_agg", "016", "enable_pli_for_crc_mismatch",
+ "live", "enc_rekey", "frskmsg", "d", "media.fdel11-2.fna.whatsapp.net", "proto", "2250", "audio_piggyback_enable_cache", "skip_nack_if_ltrp_sent", "mark_dtx_jb_frames",
+ "web_service_delay", "7282", "catalog_send_all", "outgoing", "360", "30", "LIMITED", "019", "audio_picker", "bpv2_phase", "media.fada1-7.fna.whatsapp.net",
+ "media.faep7-1.fna.whatsapp.net", "media.fbko1-2.fna.whatsapp.net", "media.fbni1-2.fna.whatsapp.net", "media.fbtz1-1.fna.whatsapp.net", "media.fbtz1-8.fna.whatsapp.net",
+ "media.fcjs3-1.fna.whatsapp.net", "media.fesb3-2.fna.whatsapp.net", "media.fgdl5-4.fna.whatsapp.net", "media.fist2-1.fna.whatsapp.net", "media.flhe2-2.fna.whatsapp.net",
+ "media.flim2-1.fna.whatsapp.net", "media.fmex1-1.fna.whatsapp.net", "media.fpat3-2.fna.whatsapp.net", "media.fpat3-3.fna.whatsapp.net", "media.fros2-1.fna.whatsapp.net",
+ "media.fsdu8-1.fna.whatsapp.net", "media.fsub3-2.fna.whatsapp.net", "payments_chat_plugin", "cond_congestion_no_rtcp_thr", "green_alert", "not-a-biz", "..",
+ "shops_pdp_urls_config", "source", "media-dus1-1.cdn.whatsapp.net", "mute_video", "01b", "currency", "max_keys", "resume_check", "contact_array", "qr_scanning", "23",
+ "b", "media.fbfh15-1.fna.whatsapp.net", "media.flim22-1.fna.whatsapp.net", "media.fsdu11-1.fna.whatsapp.net", "media.fsdu15-1.fna.whatsapp.net", "Chrome", "fts_version",
+ "60", "media.fada1-6.fna.whatsapp.net", "media.faep4-2.fna.whatsapp.net", "media.fbaq5-1.fna.whatsapp.net", "media.fbni1-1.fna.whatsapp.net", "media.fcai3-2.fna.whatsapp.net",
+ "media.fdel3-2.fna.whatsapp.net", "media.fdmm3-2.fna.whatsapp.net", "media.fhex3-1.fna.whatsapp.net", "media.fisb4-2.fna.whatsapp.net", "media.fkhi5-2.fna.whatsapp.net",
+ "media.flos2-1.fna.whatsapp.net", "media.fmct2-1.fna.whatsapp.net", "media.fntr7-1.fna.whatsapp.net", "media.frak3-1.fna.whatsapp.net", "media.fruh5-2.fna.whatsapp.net",
+ "media.fsub6-1.fna.whatsapp.net", "media.fuab1-2.fna.whatsapp.net", "media.fuio1-1.fna.whatsapp.net", "media.fver1-1.fna.whatsapp.net", "media.fymy1-1.fna.whatsapp.net",
+ "product_catalog", "1380", "audio_oob_fec_max_pkts", "22", "254", "media-ort2-2.cdn.whatsapp.net", "media-sjc3-1.cdn.whatsapp.net", "1600", "01a", "01c", "405",
+ "key_frame_interval", "body", "media.fcgh20-1.fna.whatsapp.net", "media.fesb10-2.fna.whatsapp.net", "125", "2000", "media.fbsb1-1.fna.whatsapp.net",
+ "media.fcmn3-2.fna.whatsapp.net", "media.fcpq1-1.fna.whatsapp.net", "media.fdel1-2.fna.whatsapp.net", "media.ffor2-1.fna.whatsapp.net", "media.fgdl1-4.fna.whatsapp.net",
+ "media.fhex2-1.fna.whatsapp.net", "media.fist1-2.fna.whatsapp.net", "media.fjed5-2.fna.whatsapp.net", "media.flim6-4.fna.whatsapp.net", "media.flos2-2.fna.whatsapp.net",
+ "media.fntr6-2.fna.whatsapp.net", "media.fpku3-2.fna.whatsapp.net", "media.fros8-1.fna.whatsapp.net", "media.fymy1-2.fna.whatsapp.net", "ul_bw", "ltrp_qp_offset",
+ "request", "nack", "dtx_delay_state_reset", "timeoffline", "28", "01f", "32", "enable_ltr_pool", "wa_msys_crypto", "01d", "58", "dtx_freeze_hg_update",
+ "nack_if_rpsi_throttled", "253", "840", "media.famd15-1.fna.whatsapp.net", "media.fbog17-2.fna.whatsapp.net", "media.fcai19-2.fna.whatsapp.net", "media.fcai21-4.fna.whatsapp.net",
+ "media.fesb10-4.fna.whatsapp.net", "media.fesb10-5.fna.whatsapp.net", "media.fmaa12-1.fna.whatsapp.net", "media.fmex11-3.fna.whatsapp.net", "media.fpoa33-1.fna.whatsapp.net",
+ "1050", "021", "clean", "cond_range_ema_packet_loss_pct", "media.fadb6-5.fna.whatsapp.net", "media.faqp4-1.fna.whatsapp.net", "media.fbaq3-1.fna.whatsapp.net",
+ "media.fbel2-1.fna.whatsapp.net", "media.fblr4-2.fna.whatsapp.net", "media.fclo8-1.fna.whatsapp.net", "media.fcoo1-2.fna.whatsapp.net", "media.ffjr1-4.fna.whatsapp.net",
+ "media.ffor9-1.fna.whatsapp.net", "media.fisb3-1.fna.whatsapp.net", "media.fkhi2-2.fna.whatsapp.net", "media.fkhi4-1.fna.whatsapp.net", "media.fpbc1-2.fna.whatsapp.net",
+ "media.fruh2-2.fna.whatsapp.net", "media.fruh5-1.fna.whatsapp.net", "media.fsub3-1.fna.whatsapp.net", "payments_transaction_limit", "252", "27", "29", "tintagel", "01e",
+ "237", "780", "callee_updated_payload", "020", "257", "price", "025", "239", "payments_cs_phone_number", "mediaretry", "w:auth:backup:token", "Glass.caf", "max_bitrate",
+ "240", "251", "660", "media.fbog16-1.fna.whatsapp.net", "media.fcgh21-1.fna.whatsapp.net", "media.fkul19-2.fna.whatsapp.net", "media.flim21-2.fna.whatsapp.net",
+ "media.fmex10-4.fna.whatsapp.net", "64", "33", "34", "35", "interruption", "media.fabv3-1.fna.whatsapp.net", "media.fadb6-1.fna.whatsapp.net", "media.fagr1-1.fna.whatsapp.net",
+ "media.famd1-1.fna.whatsapp.net", "media.famm6-1.fna.whatsapp.net", "media.faqp2-3.fna.whatsapp.net"
+};
diff --git a/protocols/WhatsApp/src/iq.cpp b/protocols/WhatsApp/src/iq.cpp index b9cf5bd7d2..24513dd06b 100644 --- a/protocols/WhatsApp/src/iq.cpp +++ b/protocols/WhatsApp/src/iq.cpp @@ -1,570 +1,570 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -void WhatsAppProto::OnAccountSync(const WANode &node) -{ - if (auto *pList = node.getChild("devices")) { - auto *pUser = FindUser(m_szJid); - pUser->arDevices.destroy(); - - for (auto &it : pList->getChildren()) - if (it->title == "device") - pUser->arDevices.insert(new WAJid(it->getAttr("jid"), it->getAttrInt("key-index"))); - } - - if (auto *pList = node.getChild("blocklist")) - for (auto &it : pList->getChildren()) - if (it->title == "item") { - auto *pUser = AddUser(it->getAttr("jid"), false); - Contact::Hide(pUser->hContact, 0 == mir_strcmp(it->getAttr("action"), "block")); - } - - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqBlockList(const WANode &node) -{ - for (auto &it : node.getChild("list")->getChildren()) { - auto *pUser = AddUser(it->getAttr("jid"), false); - Contact::Hide(pUser->hContact); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqCountPrekeys(const WANode &node) -{ - m_bUpdatedPrekeys = true; - - int iCount = node.getChild("count")->getAttrInt("value"); - if (iCount < 5) - UploadMorePrekeys(); -} - -void WhatsAppProto::UploadMorePrekeys() -{ - WANodeIq iq(IQ::SET, "encrypt"); - - auto regId = encodeBigEndian(getDword(DBKEY_REG_ID)); - iq.addChild("registration")->content.append(regId.c_str(), regId.size()); - - iq.addChild("type")->content.append(KEY_BUNDLE_TYPE, 1); - iq.addChild("identity")->content.append(m_signalStore.signedIdentity.pub); - - const int PORTION = 10; - m_signalStore.generatePrekeys(PORTION); - - int iStart = getDword(DBKEY_PREKEY_UPLOAD_ID, 1); - auto *n = iq.addChild("list"); - for (int i = 0; i < PORTION; i++) { - auto *nKey = n->addChild("key"); - - int keyId = iStart + i; - auto encId = encodeBigEndian(keyId, 3); - nKey->addChild("id")->content.append(encId.c_str(), encId.size()); - nKey->addChild("value")->content.append(getBlob(CMStringA(FORMAT, "PreKey%dPublic", keyId))); - } - setDword(DBKEY_PREKEY_UPLOAD_ID, iStart + PORTION); - - auto *skey = iq.addChild("skey"); - - auto encId = encodeBigEndian(m_signalStore.preKey.keyid, 3); - skey->addChild("id")->content.append(encId.c_str(), encId.size()); - skey->addChild("value")->content.append(m_signalStore.preKey.pub); - skey->addChild("signature")->content.append(m_signalStore.preKey.signature); - - WSSendNode(iq, &WhatsAppProto::OnIqDoNothing); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqDoNothing(const WANode&) -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqGetKeys(const WANode &node, void *pUserInfo) -{ - for (auto &it : node.getChild("list")->getChildren()) - if (it->title == "user") - m_signalStore.injectSession(it->getAttr("jid"), it, it); - - // don't forget to send delayed message when all keys are retrieved - if (pUserInfo) - FinishTask((WASendTask *)pUserInfo); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqGetUsync(const WANode &node, void *pUserInfo) -{ - WANodeIq iq(IQ::GET, "encrypt"); - auto *pKey = iq.addChild("key"); - - for (auto *nUser : node.getChild("usync")->getChild("list")->getChildren()) { - auto *pszJid = nUser->getAttr("jid"); - - auto *pUser = AddUser(pszJid, false); - pUser->bDeviceInit = true; - pUser->arDevices.destroy(); - - if (auto *pList = nUser->getChild("devices")->getChild("device-list")) - for (auto &it : pList->getChildren()) - if (it->title == "device") - pUser->arDevices.insert(new WAJid(pszJid, it->getAttrInt("id"))); - - for (auto &it : pUser->arDevices) { - auto blob = getBlob(MSignalSession(it->user, it->device).getSetting()); - if (blob.isEmpty()) - pKey->addChild("user")->addAttr("jid", it->toString()); - } - } - - if (pKey->getChildren().getCount() > 0) - WSSendNode(iq, &WhatsAppProto::OnIqGetKeys, pUserInfo); - else if (pUserInfo) - FinishTask((WASendTask *)pUserInfo); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqPairDevice(const WANode &node) -{ - WSSendNode(WANodeIq(IQ::RESULT) << CHAR_PARAM("id", node.getAttr("id"))); - - if (auto *pRef = node.getChild("pair-device")->getChild("ref")) { - ShowQrCode(pRef->getBody()); - } - else { - debugLogA("OnIqPairDevice: got reply without ref, exiting"); - ShutdownSession(); - } -} - -void WhatsAppProto::OnIqPairSuccess(const WANode &node) -{ - CloseQrDialog(); - - auto *pRoot = node.getChild("pair-success"); - - try { - if (auto *pPlatform = pRoot->getChild("platform")) - debugLogA("Got response from platform: %s", pPlatform->getBody().c_str()); - - if (auto *pBiz = pRoot->getChild("biz")) - if (auto *pszName = pBiz->getAttr("name")) - setUString("Nick", pszName); - - if (auto *pDevice = pRoot->getChild("device")) { - if (auto *pszJid = pDevice->getAttr("jid")) { - WAJid jid(pszJid); - m_szJid = jid.user + "@" + jid.server; - m_arUsers.insert(new WAUser(0, m_szJid, false)); - - setUString(DBKEY_ID, m_szJid); - setDword(DBKEY_DEVICE_ID, jid.device); - } - } - else throw "OnIqPairSuccess: got reply without device info, exiting"; - - if (auto *pIdentity = pRoot->getChild("device-identity")) { - proto::ADVSignedDeviceIdentityHMAC payload(pIdentity->content); - - auto &hmac = payload->hmac; - auto &details = payload->details; - { - // check details signature using HMAC - uint8_t signature[32]; - unsigned int out_len = sizeof(signature); - MBinBuffer secret(getBlob(DBKEY_SECRET_KEY)); - HMAC(EVP_sha256(), secret.data(), (int)secret.length(), details.data, (int)details.len, signature, &out_len); - if (memcmp(hmac.data, signature, sizeof(signature))) - throw "OnIqPairSuccess: got reply with invalid details signature, exiting"; - } - - proto::ADVSignedDeviceIdentity account(details); - - auto &deviceDetails = account->details; - auto &accountSignature = account->accountsignature; - auto &accountSignatureKey = account->accountsignaturekey; - { - MBinBuffer buf; - buf.append("\x06\x00", 2); - buf.append(deviceDetails.data, deviceDetails.len); - buf.append(m_signalStore.signedIdentity.pub); - - ec_public_key key = {}; - memcpy(key.data, accountSignatureKey.data, sizeof(key.data)); - if (1 != curve_verify_signature(&key, buf.data(), buf.length(), accountSignature.data, accountSignature.len)) - throw "OnIqPairSuccess: got reply with invalid account signature, exiting"; - } - debugLogA("Received valid account signature"); - { - MBinBuffer buf; - buf.append("\x06\x01", 2); - buf.append(deviceDetails.data, deviceDetails.len); - buf.append(m_signalStore.signedIdentity.pub); - buf.append(accountSignatureKey.data, accountSignatureKey.len); - - signal_buffer *result; - ec_private_key key = {}; - memcpy(key.data, m_signalStore.signedIdentity.priv.data(), m_signalStore.signedIdentity.priv.length()); - if (curve_calculate_signature(m_signalStore.CTX(), &result, &key, buf.data(), buf.length()) != 0) - throw "OnIqPairSuccess: cannot calculate account signature, exiting"; - - account->devicesignature = proto::SetBinary(result->data, result->len); - account->has_devicesignature = true; - signal_buffer_free(result); - } - - { - MBinBuffer key; - if (accountSignatureKey.len == 32) - key.append(KEY_BUNDLE_TYPE, 1); - key.append(accountSignatureKey.data, accountSignatureKey.len); - db_set_blob(0, m_szModuleName, "SignalIdentifierKey", key.data(), (int)key.length()); - } - - proto::CleanBinary(account->accountsignaturekey); account->has_accountsignaturekey = false; - MBinBuffer accountEnc(proto::Serialize(account)); - db_set_blob(0, m_szModuleName, "WAAccount", accountEnc.data(), (int)accountEnc.length()); - - proto::ADVDeviceIdentity deviceIdentity(deviceDetails); - - WANodeIq reply(IQ::RESULT); reply << CHAR_PARAM("id", node.getAttr("id")); - - WANode *nodePair = reply.addChild("pair-device-sign"); - - WANode *nodeDeviceIdentity = nodePair->addChild("device-identity"); - nodeDeviceIdentity->addAttr("key-index", deviceIdentity->keyindex); - nodeDeviceIdentity->content.append(accountEnc.data(), accountEnc.length()); - WSSendNode(reply); - } - else throw "OnIqPairSuccess: got reply without identity, exiting"; - } - catch (const char *pErrMsg) { - debugLogA(pErrMsg); - ShutdownSession(); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnIqResult(const WANode &node) -{ - if (auto *pszId = node.getAttr("id")) { - for (auto &it : m_arPacketQueue) { - if (it->szPacketId == pszId) { - it->Execute(this, node); - m_arPacketQueue.remove(it); - break; - } - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnNotifyAny(const WANode &node) -{ - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnReceiveChatState(const WANode &node) -{ - if (auto *pUser = FindUser(node.getAttr("from"))) { - if (node.getChild("composing")) { - pUser->m_timer1 = time(0); - pUser->m_timer2 = 0; - setWord(pUser->hContact, "Status", ID_STATUS_ONLINE); - - CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, 60); - } - else if (node.getChild("paused")) - CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, PROTOTYPE_CONTACTTYPING_OFF); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnNotifyDevices(const WANode &node) -{ - if (!mir_strcmp(node.getAttr("jid"), m_szJid)) - debugLogA("received list of my own devices"); - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnNotifyEncrypt(const WANode &node) -{ - if (!mir_strcmp(node.getAttr("from"), S_WHATSAPP_NET)) - OnIqCountPrekeys(node); - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnNotifyPicture(const WANode &node) -{ - if (auto *pszFrom = node.getAttr("from")) - if (m_szJid != pszFrom) - if (auto *pszUser = FindUser(pszFrom)) - ServerFetchAvatar(pszFrom); - - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnProcessHandshake(const uint8_t *pData, int cbLen) -{ - proto::HandshakeMessage msg(pData, cbLen); - if (!msg) { - debugLogA("Error parsing data, exiting"); - -LBL_Error: - ShutdownSession(); - return; - } - - auto &static_ = msg->serverhello->static_; - auto &payload_ = msg->serverhello->payload; - auto &ephemeral_ = msg->serverhello->ephemeral; - - m_noise->updateHash(ephemeral_.data, ephemeral_.len); - m_noise->mixIntoKey(m_noise->ephemeral.priv.data(), ephemeral_.data); - - MBinBuffer decryptedStatic = m_noise->decrypt(static_.data, static_.len); - m_noise->mixIntoKey(m_noise->ephemeral.priv.data(), decryptedStatic.data()); - - proto::CertChain cert(m_noise->decrypt(payload_.data, payload_.len)); - proto::CertChain__NoiseCertificate__Details details(cert->intermediate->details); - if (details->issuerserial != 0) { - debugLogA("Invalid certificate serial number, exiting"); - goto LBL_Error; - } - - MBinBuffer encryptedPub = m_noise->encrypt(m_noise->noiseKeys.pub.data(), m_noise->noiseKeys.pub.length()); - m_noise->mixIntoKey(m_noise->noiseKeys.priv.data(), ephemeral_.data); - - // create reply - Wa__ClientPayload node; - Wa__ClientPayload__DevicePairingRegistrationData pairingData; - Wa__DeviceProps companion; - Wa__DeviceProps__AppVersion appVersion; - T2Utf devName(m_wszDeviceName); - - MFileVersion v; - Miranda_GetFileVersion(&v); - - // not received our jid from server? generate registration packet then - if (m_szJid.IsEmpty()) { - uint8_t buildHash[16]; - mir_md5_hash((BYTE *)APP_VERSION, sizeof(APP_VERSION) - 1, buildHash); - - appVersion.primary = v[0]; appVersion.has_primary = true; - appVersion.secondary = v[1]; appVersion.has_secondary = true; - appVersion.tertiary = v[2]; appVersion.has_tertiary = true; - appVersion.quaternary = v[3]; appVersion.has_quaternary = true; - - companion.os = devName.get(); - companion.version = &appVersion; - companion.platformtype = WA__DEVICE_PROPS__PLATFORM_TYPE__DESKTOP; companion.has_platformtype = true; - companion.requirefullsync = false; companion.has_requirefullsync = true; - - MBinBuffer buf(proto::Serialize(&companion)); - auto szRegId(encodeBigEndian(getDword(DBKEY_REG_ID))); - auto szKeyId(encodeBigEndian(m_signalStore.preKey.keyid)); - - pairingData.deviceprops = proto::SetBinary(buf.data(), buf.length()); pairingData.has_deviceprops = true; - pairingData.buildhash = proto::SetBinary(buildHash, sizeof(buildHash)); pairingData.has_buildhash = true; - pairingData.eregid = proto::SetBinary(szRegId.c_str(), szRegId.size()); pairingData.has_eregid = true; - pairingData.ekeytype = proto::SetBinary(KEY_BUNDLE_TYPE, 1); pairingData.has_ekeytype = true; - pairingData.eident = proto::SetBinary(m_signalStore.signedIdentity.pub.data(), m_signalStore.signedIdentity.pub.length()); pairingData.has_eident = true; - pairingData.eskeyid = proto::SetBinary(szKeyId.c_str(), szKeyId.size()); pairingData.has_eskeyid = true; - pairingData.eskeyval = proto::SetBinary(m_signalStore.preKey.pub.data(), m_signalStore.preKey.pub.length()); pairingData.has_eskeyval = true; - pairingData.eskeysig = proto::SetBinary(m_signalStore.preKey.signature.data(), m_signalStore.preKey.signature.length()); pairingData.has_eskeysig = true; - node.devicepairingdata = &pairingData; - - node.passive = false; node.has_passive = true; - } - // generate login packet - else { - WAJid jid(m_szJid); - node.username = _atoi64(jid.user); node.has_username = true; - node.device = getDword(DBKEY_DEVICE_ID); node.has_device = true; - node.passive = true; node.has_passive = true; - } - - Wa__ClientPayload__UserAgent__AppVersion userVersion; - userVersion.primary = 2; userVersion.has_primary = true; - userVersion.secondary = 2230; userVersion.has_secondary = true; - userVersion.tertiary = 15; userVersion.has_tertiary = true; - - Wa__ClientPayload__UserAgent userAgent; - userAgent.appversion = &userVersion; - userAgent.platform = WA__CLIENT_PAYLOAD__USER_AGENT__PLATFORM__WEB; userAgent.has_platform = true; - userAgent.releasechannel = WA__CLIENT_PAYLOAD__USER_AGENT__RELEASE_CHANNEL__RELEASE; userAgent.has_releasechannel = true; - userAgent.mcc = "000"; - userAgent.mnc = "000"; - userAgent.osversion = "0.1"; - userAgent.osbuildnumber = "0.1"; - userAgent.manufacturer = ""; - userAgent.device = "Desktop"; - userAgent.localelanguageiso6391 = "en"; - userAgent.localecountryiso31661alpha2 = "US"; - - Wa__ClientPayload__WebInfo webInfo; - webInfo.websubplatform = WA__CLIENT_PAYLOAD__WEB_INFO__WEB_SUB_PLATFORM__WEB_BROWSER; webInfo.has_websubplatform = true; - - node.connecttype = WA__CLIENT_PAYLOAD__CONNECT_TYPE__WIFI_UNKNOWN; node.has_connecttype = true; - node.connectreason = WA__CLIENT_PAYLOAD__CONNECT_REASON__USER_ACTIVATED; node.has_connectreason = true; - node.useragent = &userAgent; - node.webinfo = &webInfo; - - MBinBuffer payload(proto::Serialize(&node)); - MBinBuffer payloadEnc = m_noise->encrypt(payload.data(), payload.length()); - - Wa__HandshakeMessage__ClientFinish finish; - finish.payload = {payloadEnc.length(), payloadEnc.data()}; finish.has_payload = true; - finish.static_ = {encryptedPub.length(), encryptedPub.data()}; finish.has_static_ = true; - - Wa__HandshakeMessage handshake; - handshake.clientfinish = &finish; - WSSend(handshake); - - m_noise->finish(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnReceiveFailure(const WANode &node) -{ - m_bTerminated = true; - - ProcessFailure(node.getAttrInt("reason")); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnReceiveInfo(const WANode &node) -{ - if (auto *pChild = node.getFirstChild()) { - if (pChild->title == "offline") { - debugLogA("Processed %d offline events", pChild->getAttrInt("count")); - - // retrieve loaded prekeys count - if (!m_bUpdatedPrekeys) - WSSendNode(WANodeIq(IQ::GET, "encrypt") << XCHILD("count"), &WhatsAppProto::OnIqCountPrekeys); - - auto *pUser = FindUser(m_szJid); - if (pUser->arDevices.getCount() == 0) { - LIST<char> jids(1); - jids.insert(m_szJid.GetBuffer()); - SendUsync(jids, nullptr); - } - - for (auto &it : m_arCollections) { - if (it->version == 0) { - m_impl.m_resyncApp.Stop(); - m_impl.m_resyncApp.Start(1000); - break; - } - } - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::ProcessReceipt(MCONTACT hContact, const char *msgId, bool bRead) -{ - MEVENT hEvent = db_event_getById(m_szModuleName, msgId); - if (hEvent == 0) - return; - - if (g_plugin.bHasMessageState) - CallService(MS_MESSAGESTATE_UPDATE, hContact, bRead ? MRD_TYPE_READ : MRD_TYPE_DELIVERED); - - if (bRead) - db_event_markRead(hContact, hEvent); -} - -void WhatsAppProto::OnReceiveReceipt(const WANode &node) -{ - if (!mir_strcmp(node.getAttr("type"), "retry")) { - for (auto &it : node.getChildren()) - if (it->title == "keys") - m_signalStore.injectSession(node.getAttr("from"), &node, it); - return; - } - - if (auto *pUser = FindUser(node.getAttr("from"))) { - bool bRead = mir_strcmp(node.getAttr("type"), "read") == 0; - ProcessReceipt(pUser->hContact, node.getAttr("id"), bRead); - - if (auto *pList = node.getChild("list")) - for (auto &it : pList->getChildren()) - if (it->title == "item") - ProcessReceipt(pUser->hContact, it->getAttr("id"), bRead); - } - - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnStreamError(const WANode &node) -{ - m_bTerminated = true; - - if (auto *pszCode = node.getAttr("code")) - ProcessFailure(atoi(pszCode)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnSuccess(const WANode &) -{ - OnLoggedIn(); - - WSSendNode(WANodeIq(IQ::SET, "passive") << XCHILD("active"), &WhatsAppProto::OnIqDoNothing); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::InitPersistentHandlers() -{ - m_arPersistent.insert(new WAPersistentHandler("iq", "set", "md", "pair-device", &WhatsAppProto::OnIqPairDevice)); - m_arPersistent.insert(new WAPersistentHandler("iq", "set", "md", "pair-success", &WhatsAppProto::OnIqPairSuccess)); - - m_arPersistent.insert(new WAPersistentHandler("notification", "devices", 0, 0, &WhatsAppProto::OnNotifyDevices)); - m_arPersistent.insert(new WAPersistentHandler("notification", "encrypt", 0, 0, &WhatsAppProto::OnNotifyEncrypt)); - m_arPersistent.insert(new WAPersistentHandler("notification", "picture", 0, 0, &WhatsAppProto::OnNotifyPicture)); - m_arPersistent.insert(new WAPersistentHandler("notification", "account_sync", 0, 0, &WhatsAppProto::OnAccountSync)); - m_arPersistent.insert(new WAPersistentHandler("notification", "server_sync", 0, 0, &WhatsAppProto::OnServerSync)); - m_arPersistent.insert(new WAPersistentHandler("notification", 0, 0, 0, &WhatsAppProto::OnNotifyAny)); - - m_arPersistent.insert(new WAPersistentHandler("ack", 0, 0, 0, &WhatsAppProto::OnReceiveAck)); - m_arPersistent.insert(new WAPersistentHandler("ib", 0, 0, 0, &WhatsAppProto::OnReceiveInfo)); - m_arPersistent.insert(new WAPersistentHandler("failure", 0, 0, 0, &WhatsAppProto::OnReceiveFailure)); - m_arPersistent.insert(new WAPersistentHandler("message", 0, 0, 0, &WhatsAppProto::OnReceiveMessage)); - m_arPersistent.insert(new WAPersistentHandler("receipt", 0, 0, 0, &WhatsAppProto::OnReceiveReceipt)); - m_arPersistent.insert(new WAPersistentHandler("chatstates", 0, 0, 0, &WhatsAppProto::OnReceiveChatState)); - m_arPersistent.insert(new WAPersistentHandler("stream:error", 0, 0, 0, &WhatsAppProto::OnStreamError)); - m_arPersistent.insert(new WAPersistentHandler("success", 0, 0, 0, &WhatsAppProto::OnSuccess)); - - m_arPersistent.insert(new WAPersistentHandler(0, "result", 0, 0, &WhatsAppProto::OnIqResult)); -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+void WhatsAppProto::OnAccountSync(const WANode &node)
+{
+ if (auto *pList = node.getChild("devices")) {
+ auto *pUser = FindUser(m_szJid);
+ pUser->arDevices.destroy();
+
+ for (auto &it : pList->getChildren())
+ if (it->title == "device")
+ pUser->arDevices.insert(new WAJid(it->getAttr("jid"), it->getAttrInt("key-index")));
+ }
+
+ if (auto *pList = node.getChild("blocklist"))
+ for (auto &it : pList->getChildren())
+ if (it->title == "item") {
+ auto *pUser = AddUser(it->getAttr("jid"), false);
+ Contact::Hide(pUser->hContact, 0 == mir_strcmp(it->getAttr("action"), "block"));
+ }
+
+ SendAck(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqBlockList(const WANode &node)
+{
+ for (auto &it : node.getChild("list")->getChildren()) {
+ auto *pUser = AddUser(it->getAttr("jid"), false);
+ Contact::Hide(pUser->hContact);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqCountPrekeys(const WANode &node)
+{
+ m_bUpdatedPrekeys = true;
+
+ int iCount = node.getChild("count")->getAttrInt("value");
+ if (iCount < 5)
+ UploadMorePrekeys();
+}
+
+void WhatsAppProto::UploadMorePrekeys()
+{
+ WANodeIq iq(IQ::SET, "encrypt");
+
+ auto regId = encodeBigEndian(getDword(DBKEY_REG_ID));
+ iq.addChild("registration")->content.append(regId.c_str(), regId.size());
+
+ iq.addChild("type")->content.append(KEY_BUNDLE_TYPE, 1);
+ iq.addChild("identity")->content.append(m_signalStore.signedIdentity.pub);
+
+ const int PORTION = 10;
+ m_signalStore.generatePrekeys(PORTION);
+
+ int iStart = getDword(DBKEY_PREKEY_UPLOAD_ID, 1);
+ auto *n = iq.addChild("list");
+ for (int i = 0; i < PORTION; i++) {
+ auto *nKey = n->addChild("key");
+
+ int keyId = iStart + i;
+ auto encId = encodeBigEndian(keyId, 3);
+ nKey->addChild("id")->content.append(encId.c_str(), encId.size());
+ nKey->addChild("value")->content.append(getBlob(CMStringA(FORMAT, "PreKey%dPublic", keyId)));
+ }
+ setDword(DBKEY_PREKEY_UPLOAD_ID, iStart + PORTION);
+
+ auto *skey = iq.addChild("skey");
+
+ auto encId = encodeBigEndian(m_signalStore.preKey.keyid, 3);
+ skey->addChild("id")->content.append(encId.c_str(), encId.size());
+ skey->addChild("value")->content.append(m_signalStore.preKey.pub);
+ skey->addChild("signature")->content.append(m_signalStore.preKey.signature);
+
+ WSSendNode(iq, &WhatsAppProto::OnIqDoNothing);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqDoNothing(const WANode&)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqGetKeys(const WANode &node, void *pUserInfo)
+{
+ for (auto &it : node.getChild("list")->getChildren())
+ if (it->title == "user")
+ m_signalStore.injectSession(it->getAttr("jid"), it, it);
+
+ // don't forget to send delayed message when all keys are retrieved
+ if (pUserInfo)
+ FinishTask((WASendTask *)pUserInfo);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqGetUsync(const WANode &node, void *pUserInfo)
+{
+ WANodeIq iq(IQ::GET, "encrypt");
+ auto *pKey = iq.addChild("key");
+
+ for (auto *nUser : node.getChild("usync")->getChild("list")->getChildren()) {
+ auto *pszJid = nUser->getAttr("jid");
+
+ auto *pUser = AddUser(pszJid, false);
+ pUser->bDeviceInit = true;
+ pUser->arDevices.destroy();
+
+ if (auto *pList = nUser->getChild("devices")->getChild("device-list"))
+ for (auto &it : pList->getChildren())
+ if (it->title == "device")
+ pUser->arDevices.insert(new WAJid(pszJid, it->getAttrInt("id")));
+
+ for (auto &it : pUser->arDevices) {
+ auto blob = getBlob(MSignalSession(it->user, it->device).getSetting());
+ if (blob.isEmpty())
+ pKey->addChild("user")->addAttr("jid", it->toString());
+ }
+ }
+
+ if (pKey->getChildren().getCount() > 0)
+ WSSendNode(iq, &WhatsAppProto::OnIqGetKeys, pUserInfo);
+ else if (pUserInfo)
+ FinishTask((WASendTask *)pUserInfo);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqPairDevice(const WANode &node)
+{
+ WSSendNode(WANodeIq(IQ::RESULT) << CHAR_PARAM("id", node.getAttr("id")));
+
+ if (auto *pRef = node.getChild("pair-device")->getChild("ref")) {
+ ShowQrCode(pRef->getBody());
+ }
+ else {
+ debugLogA("OnIqPairDevice: got reply without ref, exiting");
+ ShutdownSession();
+ }
+}
+
+void WhatsAppProto::OnIqPairSuccess(const WANode &node)
+{
+ CloseQrDialog();
+
+ auto *pRoot = node.getChild("pair-success");
+
+ try {
+ if (auto *pPlatform = pRoot->getChild("platform"))
+ debugLogA("Got response from platform: %s", pPlatform->getBody().c_str());
+
+ if (auto *pBiz = pRoot->getChild("biz"))
+ if (auto *pszName = pBiz->getAttr("name"))
+ setUString("Nick", pszName);
+
+ if (auto *pDevice = pRoot->getChild("device")) {
+ if (auto *pszJid = pDevice->getAttr("jid")) {
+ WAJid jid(pszJid);
+ m_szJid = jid.user + "@" + jid.server;
+ m_arUsers.insert(new WAUser(0, m_szJid, false));
+
+ setUString(DBKEY_ID, m_szJid);
+ setDword(DBKEY_DEVICE_ID, jid.device);
+ }
+ }
+ else throw "OnIqPairSuccess: got reply without device info, exiting";
+
+ if (auto *pIdentity = pRoot->getChild("device-identity")) {
+ proto::ADVSignedDeviceIdentityHMAC payload(pIdentity->content);
+
+ auto &hmac = payload->hmac;
+ auto &details = payload->details;
+ {
+ // check details signature using HMAC
+ uint8_t signature[32];
+ unsigned int out_len = sizeof(signature);
+ MBinBuffer secret(getBlob(DBKEY_SECRET_KEY));
+ HMAC(EVP_sha256(), secret.data(), (int)secret.length(), details.data, (int)details.len, signature, &out_len);
+ if (memcmp(hmac.data, signature, sizeof(signature)))
+ throw "OnIqPairSuccess: got reply with invalid details signature, exiting";
+ }
+
+ proto::ADVSignedDeviceIdentity account(details);
+
+ auto &deviceDetails = account->details;
+ auto &accountSignature = account->accountsignature;
+ auto &accountSignatureKey = account->accountsignaturekey;
+ {
+ MBinBuffer buf;
+ buf.append("\x06\x00", 2);
+ buf.append(deviceDetails.data, deviceDetails.len);
+ buf.append(m_signalStore.signedIdentity.pub);
+
+ ec_public_key key = {};
+ memcpy(key.data, accountSignatureKey.data, sizeof(key.data));
+ if (1 != curve_verify_signature(&key, buf.data(), buf.length(), accountSignature.data, accountSignature.len))
+ throw "OnIqPairSuccess: got reply with invalid account signature, exiting";
+ }
+ debugLogA("Received valid account signature");
+ {
+ MBinBuffer buf;
+ buf.append("\x06\x01", 2);
+ buf.append(deviceDetails.data, deviceDetails.len);
+ buf.append(m_signalStore.signedIdentity.pub);
+ buf.append(accountSignatureKey.data, accountSignatureKey.len);
+
+ signal_buffer *result;
+ ec_private_key key = {};
+ memcpy(key.data, m_signalStore.signedIdentity.priv.data(), m_signalStore.signedIdentity.priv.length());
+ if (curve_calculate_signature(m_signalStore.CTX(), &result, &key, buf.data(), buf.length()) != 0)
+ throw "OnIqPairSuccess: cannot calculate account signature, exiting";
+
+ account->devicesignature = proto::SetBinary(result->data, result->len);
+ account->has_devicesignature = true;
+ signal_buffer_free(result);
+ }
+
+ {
+ MBinBuffer key;
+ if (accountSignatureKey.len == 32)
+ key.append(KEY_BUNDLE_TYPE, 1);
+ key.append(accountSignatureKey.data, accountSignatureKey.len);
+ db_set_blob(0, m_szModuleName, "SignalIdentifierKey", key.data(), (int)key.length());
+ }
+
+ proto::CleanBinary(account->accountsignaturekey); account->has_accountsignaturekey = false;
+ MBinBuffer accountEnc(proto::Serialize(account));
+ db_set_blob(0, m_szModuleName, "WAAccount", accountEnc.data(), (int)accountEnc.length());
+
+ proto::ADVDeviceIdentity deviceIdentity(deviceDetails);
+
+ WANodeIq reply(IQ::RESULT); reply << CHAR_PARAM("id", node.getAttr("id"));
+
+ WANode *nodePair = reply.addChild("pair-device-sign");
+
+ WANode *nodeDeviceIdentity = nodePair->addChild("device-identity");
+ nodeDeviceIdentity->addAttr("key-index", deviceIdentity->keyindex);
+ nodeDeviceIdentity->content.append(accountEnc.data(), accountEnc.length());
+ WSSendNode(reply);
+ }
+ else throw "OnIqPairSuccess: got reply without identity, exiting";
+ }
+ catch (const char *pErrMsg) {
+ debugLogA(pErrMsg);
+ ShutdownSession();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnIqResult(const WANode &node)
+{
+ if (auto *pszId = node.getAttr("id")) {
+ for (auto &it : m_arPacketQueue) {
+ if (it->szPacketId == pszId) {
+ it->Execute(this, node);
+ m_arPacketQueue.remove(it);
+ break;
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnNotifyAny(const WANode &node)
+{
+ SendAck(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnReceiveChatState(const WANode &node)
+{
+ if (auto *pUser = FindUser(node.getAttr("from"))) {
+ if (node.getChild("composing")) {
+ pUser->m_timer1 = time(0);
+ pUser->m_timer2 = 0;
+ setWord(pUser->hContact, "Status", ID_STATUS_ONLINE);
+
+ CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, 60);
+ }
+ else if (node.getChild("paused"))
+ CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, PROTOTYPE_CONTACTTYPING_OFF);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnNotifyDevices(const WANode &node)
+{
+ if (!mir_strcmp(node.getAttr("jid"), m_szJid))
+ debugLogA("received list of my own devices");
+ SendAck(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnNotifyEncrypt(const WANode &node)
+{
+ if (!mir_strcmp(node.getAttr("from"), S_WHATSAPP_NET))
+ OnIqCountPrekeys(node);
+ SendAck(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnNotifyPicture(const WANode &node)
+{
+ if (auto *pszFrom = node.getAttr("from"))
+ if (m_szJid != pszFrom)
+ if (auto *pszUser = FindUser(pszFrom))
+ ServerFetchAvatar(pszFrom);
+
+ SendAck(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnProcessHandshake(const uint8_t *pData, int cbLen)
+{
+ proto::HandshakeMessage msg(pData, cbLen);
+ if (!msg) {
+ debugLogA("Error parsing data, exiting");
+
+LBL_Error:
+ ShutdownSession();
+ return;
+ }
+
+ auto &static_ = msg->serverhello->static_;
+ auto &payload_ = msg->serverhello->payload;
+ auto &ephemeral_ = msg->serverhello->ephemeral;
+
+ m_noise->updateHash(ephemeral_.data, ephemeral_.len);
+ m_noise->mixIntoKey(m_noise->ephemeral.priv.data(), ephemeral_.data);
+
+ MBinBuffer decryptedStatic = m_noise->decrypt(static_.data, static_.len);
+ m_noise->mixIntoKey(m_noise->ephemeral.priv.data(), decryptedStatic.data());
+
+ proto::CertChain cert(m_noise->decrypt(payload_.data, payload_.len));
+ proto::CertChain__NoiseCertificate__Details details(cert->intermediate->details);
+ if (details->issuerserial != 0) {
+ debugLogA("Invalid certificate serial number, exiting");
+ goto LBL_Error;
+ }
+
+ MBinBuffer encryptedPub = m_noise->encrypt(m_noise->noiseKeys.pub.data(), m_noise->noiseKeys.pub.length());
+ m_noise->mixIntoKey(m_noise->noiseKeys.priv.data(), ephemeral_.data);
+
+ // create reply
+ Wa__ClientPayload node;
+ Wa__ClientPayload__DevicePairingRegistrationData pairingData;
+ Wa__DeviceProps companion;
+ Wa__DeviceProps__AppVersion appVersion;
+ T2Utf devName(m_wszDeviceName);
+
+ MFileVersion v;
+ Miranda_GetFileVersion(&v);
+
+ // not received our jid from server? generate registration packet then
+ if (m_szJid.IsEmpty()) {
+ uint8_t buildHash[16];
+ mir_md5_hash((BYTE *)APP_VERSION, sizeof(APP_VERSION) - 1, buildHash);
+
+ appVersion.primary = v[0]; appVersion.has_primary = true;
+ appVersion.secondary = v[1]; appVersion.has_secondary = true;
+ appVersion.tertiary = v[2]; appVersion.has_tertiary = true;
+ appVersion.quaternary = v[3]; appVersion.has_quaternary = true;
+
+ companion.os = devName.get();
+ companion.version = &appVersion;
+ companion.platformtype = WA__DEVICE_PROPS__PLATFORM_TYPE__DESKTOP; companion.has_platformtype = true;
+ companion.requirefullsync = false; companion.has_requirefullsync = true;
+
+ MBinBuffer buf(proto::Serialize(&companion));
+ auto szRegId(encodeBigEndian(getDword(DBKEY_REG_ID)));
+ auto szKeyId(encodeBigEndian(m_signalStore.preKey.keyid));
+
+ pairingData.deviceprops = proto::SetBinary(buf.data(), buf.length()); pairingData.has_deviceprops = true;
+ pairingData.buildhash = proto::SetBinary(buildHash, sizeof(buildHash)); pairingData.has_buildhash = true;
+ pairingData.eregid = proto::SetBinary(szRegId.c_str(), szRegId.size()); pairingData.has_eregid = true;
+ pairingData.ekeytype = proto::SetBinary(KEY_BUNDLE_TYPE, 1); pairingData.has_ekeytype = true;
+ pairingData.eident = proto::SetBinary(m_signalStore.signedIdentity.pub.data(), m_signalStore.signedIdentity.pub.length()); pairingData.has_eident = true;
+ pairingData.eskeyid = proto::SetBinary(szKeyId.c_str(), szKeyId.size()); pairingData.has_eskeyid = true;
+ pairingData.eskeyval = proto::SetBinary(m_signalStore.preKey.pub.data(), m_signalStore.preKey.pub.length()); pairingData.has_eskeyval = true;
+ pairingData.eskeysig = proto::SetBinary(m_signalStore.preKey.signature.data(), m_signalStore.preKey.signature.length()); pairingData.has_eskeysig = true;
+ node.devicepairingdata = &pairingData;
+
+ node.passive = false; node.has_passive = true;
+ }
+ // generate login packet
+ else {
+ WAJid jid(m_szJid);
+ node.username = _atoi64(jid.user); node.has_username = true;
+ node.device = getDword(DBKEY_DEVICE_ID); node.has_device = true;
+ node.passive = true; node.has_passive = true;
+ }
+
+ Wa__ClientPayload__UserAgent__AppVersion userVersion;
+ userVersion.primary = 2; userVersion.has_primary = true;
+ userVersion.secondary = 2230; userVersion.has_secondary = true;
+ userVersion.tertiary = 15; userVersion.has_tertiary = true;
+
+ Wa__ClientPayload__UserAgent userAgent;
+ userAgent.appversion = &userVersion;
+ userAgent.platform = WA__CLIENT_PAYLOAD__USER_AGENT__PLATFORM__WEB; userAgent.has_platform = true;
+ userAgent.releasechannel = WA__CLIENT_PAYLOAD__USER_AGENT__RELEASE_CHANNEL__RELEASE; userAgent.has_releasechannel = true;
+ userAgent.mcc = "000";
+ userAgent.mnc = "000";
+ userAgent.osversion = "0.1";
+ userAgent.osbuildnumber = "0.1";
+ userAgent.manufacturer = "";
+ userAgent.device = "Desktop";
+ userAgent.localelanguageiso6391 = "en";
+ userAgent.localecountryiso31661alpha2 = "US";
+
+ Wa__ClientPayload__WebInfo webInfo;
+ webInfo.websubplatform = WA__CLIENT_PAYLOAD__WEB_INFO__WEB_SUB_PLATFORM__WEB_BROWSER; webInfo.has_websubplatform = true;
+
+ node.connecttype = WA__CLIENT_PAYLOAD__CONNECT_TYPE__WIFI_UNKNOWN; node.has_connecttype = true;
+ node.connectreason = WA__CLIENT_PAYLOAD__CONNECT_REASON__USER_ACTIVATED; node.has_connectreason = true;
+ node.useragent = &userAgent;
+ node.webinfo = &webInfo;
+
+ MBinBuffer payload(proto::Serialize(&node));
+ MBinBuffer payloadEnc = m_noise->encrypt(payload.data(), payload.length());
+
+ Wa__HandshakeMessage__ClientFinish finish;
+ finish.payload = {payloadEnc.length(), payloadEnc.data()}; finish.has_payload = true;
+ finish.static_ = {encryptedPub.length(), encryptedPub.data()}; finish.has_static_ = true;
+
+ Wa__HandshakeMessage handshake;
+ handshake.clientfinish = &finish;
+ WSSend(handshake);
+
+ m_noise->finish();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnReceiveFailure(const WANode &node)
+{
+ m_bTerminated = true;
+
+ ProcessFailure(node.getAttrInt("reason"));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnReceiveInfo(const WANode &node)
+{
+ if (auto *pChild = node.getFirstChild()) {
+ if (pChild->title == "offline") {
+ debugLogA("Processed %d offline events", pChild->getAttrInt("count"));
+
+ // retrieve loaded prekeys count
+ if (!m_bUpdatedPrekeys)
+ WSSendNode(WANodeIq(IQ::GET, "encrypt") << XCHILD("count"), &WhatsAppProto::OnIqCountPrekeys);
+
+ auto *pUser = FindUser(m_szJid);
+ if (pUser->arDevices.getCount() == 0) {
+ LIST<char> jids(1);
+ jids.insert(m_szJid.GetBuffer());
+ SendUsync(jids, nullptr);
+ }
+
+ for (auto &it : m_arCollections) {
+ if (it->version == 0) {
+ m_impl.m_resyncApp.Stop();
+ m_impl.m_resyncApp.Start(1000);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::ProcessReceipt(MCONTACT hContact, const char *msgId, bool bRead)
+{
+ MEVENT hEvent = db_event_getById(m_szModuleName, msgId);
+ if (hEvent == 0)
+ return;
+
+ if (g_plugin.bHasMessageState)
+ CallService(MS_MESSAGESTATE_UPDATE, hContact, bRead ? MRD_TYPE_READ : MRD_TYPE_DELIVERED);
+
+ if (bRead)
+ db_event_markRead(hContact, hEvent);
+}
+
+void WhatsAppProto::OnReceiveReceipt(const WANode &node)
+{
+ if (!mir_strcmp(node.getAttr("type"), "retry")) {
+ for (auto &it : node.getChildren())
+ if (it->title == "keys")
+ m_signalStore.injectSession(node.getAttr("from"), &node, it);
+ return;
+ }
+
+ if (auto *pUser = FindUser(node.getAttr("from"))) {
+ bool bRead = mir_strcmp(node.getAttr("type"), "read") == 0;
+ ProcessReceipt(pUser->hContact, node.getAttr("id"), bRead);
+
+ if (auto *pList = node.getChild("list"))
+ for (auto &it : pList->getChildren())
+ if (it->title == "item")
+ ProcessReceipt(pUser->hContact, it->getAttr("id"), bRead);
+ }
+
+ SendAck(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnStreamError(const WANode &node)
+{
+ m_bTerminated = true;
+
+ if (auto *pszCode = node.getAttr("code"))
+ ProcessFailure(atoi(pszCode));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnSuccess(const WANode &)
+{
+ OnLoggedIn();
+
+ WSSendNode(WANodeIq(IQ::SET, "passive") << XCHILD("active"), &WhatsAppProto::OnIqDoNothing);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::InitPersistentHandlers()
+{
+ m_arPersistent.insert(new WAPersistentHandler("iq", "set", "md", "pair-device", &WhatsAppProto::OnIqPairDevice));
+ m_arPersistent.insert(new WAPersistentHandler("iq", "set", "md", "pair-success", &WhatsAppProto::OnIqPairSuccess));
+
+ m_arPersistent.insert(new WAPersistentHandler("notification", "devices", 0, 0, &WhatsAppProto::OnNotifyDevices));
+ m_arPersistent.insert(new WAPersistentHandler("notification", "encrypt", 0, 0, &WhatsAppProto::OnNotifyEncrypt));
+ m_arPersistent.insert(new WAPersistentHandler("notification", "picture", 0, 0, &WhatsAppProto::OnNotifyPicture));
+ m_arPersistent.insert(new WAPersistentHandler("notification", "account_sync", 0, 0, &WhatsAppProto::OnAccountSync));
+ m_arPersistent.insert(new WAPersistentHandler("notification", "server_sync", 0, 0, &WhatsAppProto::OnServerSync));
+ m_arPersistent.insert(new WAPersistentHandler("notification", 0, 0, 0, &WhatsAppProto::OnNotifyAny));
+
+ m_arPersistent.insert(new WAPersistentHandler("ack", 0, 0, 0, &WhatsAppProto::OnReceiveAck));
+ m_arPersistent.insert(new WAPersistentHandler("ib", 0, 0, 0, &WhatsAppProto::OnReceiveInfo));
+ m_arPersistent.insert(new WAPersistentHandler("failure", 0, 0, 0, &WhatsAppProto::OnReceiveFailure));
+ m_arPersistent.insert(new WAPersistentHandler("message", 0, 0, 0, &WhatsAppProto::OnReceiveMessage));
+ m_arPersistent.insert(new WAPersistentHandler("receipt", 0, 0, 0, &WhatsAppProto::OnReceiveReceipt));
+ m_arPersistent.insert(new WAPersistentHandler("chatstates", 0, 0, 0, &WhatsAppProto::OnReceiveChatState));
+ m_arPersistent.insert(new WAPersistentHandler("stream:error", 0, 0, 0, &WhatsAppProto::OnStreamError));
+ m_arPersistent.insert(new WAPersistentHandler("success", 0, 0, 0, &WhatsAppProto::OnSuccess));
+
+ m_arPersistent.insert(new WAPersistentHandler(0, "result", 0, 0, &WhatsAppProto::OnIqResult));
+}
diff --git a/protocols/WhatsApp/src/main.cpp b/protocols/WhatsApp/src/main.cpp index dc4c252927..318a027b76 100644 --- a/protocols/WhatsApp/src/main.cpp +++ b/protocols/WhatsApp/src/main.cpp @@ -1,71 +1,71 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan - -*/ - -#include "stdafx.h" -#include "version.h" - -CMPlugin g_plugin; - -PLUGININFOEX pluginInfo = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, //not transient - // {008B9CE1-154B-44E4-9823-97C1AAB00C3C} - { 0x8b9ce1, 0x154b, 0x44e4, { 0x98, 0x23, 0x97, 0xc1, 0xaa, 0xb0, 0xc, 0x3c }} -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Interface information - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; - -///////////////////////////////////////////////////////////////////////////////////////// - -CMPlugin::CMPlugin() : - ACCPROTOPLUGIN<WhatsAppProto>(MODULENAME, pluginInfo) -{ - SetUniqueId(DBKEY_ID); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Load - -static int OnPluginLoaded(WPARAM, LPARAM) -{ - g_plugin.bHasMessageState = ServiceExists(MS_MESSAGESTATE_UPDATE); - return 0; -} - -int CMPlugin::Load() -{ - HookEvent(ME_SYSTEM_MODULELOAD, OnPluginLoaded); - HookEvent(ME_SYSTEM_MODULEUNLOAD, OnPluginLoaded); - OnPluginLoaded(0, 0); - - // special netlib user for reading avatars, blobs etc via HTTP protocol - NETLIBUSER nlu = {}; - nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; - nlu.szSettingsModule = "WhatsApp"; - nlu.szDescriptiveName.w = TranslateT("WhatsApp (HTTP)"); - hAvatarUser = Netlib_RegisterUser(&nlu); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Unload - -int CMPlugin::Unload() -{ - Netlib_CloseHandle(hAvatarConn); - Netlib_CloseHandle(hAvatarUser); - return 0; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+#include "version.h"
+
+CMPlugin g_plugin;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE, //not transient
+ // {008B9CE1-154B-44E4-9823-97C1AAB00C3C}
+ { 0x8b9ce1, 0x154b, 0x44e4, { 0x98, 0x23, 0x97, 0xc1, 0xaa, 0xb0, 0xc, 0x3c }}
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Interface information
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CMPlugin::CMPlugin() :
+ ACCPROTOPLUGIN<WhatsAppProto>(MODULENAME, pluginInfo)
+{
+ SetUniqueId(DBKEY_ID);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load
+
+static int OnPluginLoaded(WPARAM, LPARAM)
+{
+ g_plugin.bHasMessageState = ServiceExists(MS_MESSAGESTATE_UPDATE);
+ return 0;
+}
+
+int CMPlugin::Load()
+{
+ HookEvent(ME_SYSTEM_MODULELOAD, OnPluginLoaded);
+ HookEvent(ME_SYSTEM_MODULEUNLOAD, OnPluginLoaded);
+ OnPluginLoaded(0, 0);
+
+ // special netlib user for reading avatars, blobs etc via HTTP protocol
+ NETLIBUSER nlu = {};
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE;
+ nlu.szSettingsModule = "WhatsApp";
+ nlu.szDescriptiveName.w = TranslateT("WhatsApp (HTTP)");
+ hAvatarUser = Netlib_RegisterUser(&nlu);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Unload
+
+int CMPlugin::Unload()
+{
+ Netlib_CloseHandle(hAvatarConn);
+ Netlib_CloseHandle(hAvatarUser);
+ return 0;
+}
diff --git a/protocols/WhatsApp/src/message.cpp b/protocols/WhatsApp/src/message.cpp index e8d2b775ec..250a63e4d1 100644 --- a/protocols/WhatsApp/src/message.cpp +++ b/protocols/WhatsApp/src/message.cpp @@ -1,502 +1,502 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -void WhatsAppProto::OnReceiveMessage(const WANode &node) -{ - auto *msgId = node.getAttr("id"); - auto *msgType = node.getAttr("type"); - auto *msgFrom = node.getAttr("from"); - auto *category = node.getAttr("category"); - auto *recipient = node.getAttr("recipient"); - auto *participant = node.getAttr("participant"); - - if (msgType == nullptr || msgFrom == nullptr || msgId == nullptr) { - debugLogA("bad message received: <%s> <%s> <%s>", msgType, msgFrom, msgId); - return; - } - - SendAck(node); - - MEVENT hEvent = db_event_getById(m_szModuleName, msgId); - if (hEvent) { - debugLogA("this message is already processed: %s", msgId); - return; - } - - WAMSG type; - WAJid jid(msgFrom); - CMStringA szAuthor, szChatId; - - if (node.getAttr("offline")) - type.bOffline = true; - - // message from one user to another - if (jid.isUser()) { - if (recipient) { - if (m_szJid != msgFrom) { - debugLogA("strange message: with recipient, but not from me"); - return; - } - szChatId = recipient; - } - else szChatId = msgFrom; - - type.bPrivateChat = true; - szAuthor = msgFrom; - } - else if (jid.isGroup()) { - if (!participant) { - debugLogA("strange message: from group, but without participant"); - return; - } - - type.bGroupChat = true; - szAuthor = participant; - szChatId = msgFrom; - } - else if (jid.isBroadcast()) { - if (!participant) { - debugLogA("strange message: from group, but without participant"); - return; - } - - bool bIsMe = m_szJid == participant; - if (jid.isStatusBroadcast()) { - if (bIsMe) - type.bDirectStatus = true; - else - type.bOtherStatus = true; - } - else { - if (bIsMe) - type.bPeerBroadcast = true; - else - type.bOtherBroadcast = true; - } - szChatId = msgFrom; - szAuthor = participant; - } - else { - debugLogA("invalid message type"); - return; - } - - CMStringA szSender = (type.bPrivateChat) ? szAuthor : szChatId; - bool bFromMe = (m_szJid == msgFrom); - if (!bFromMe && participant) - bFromMe = m_szJid == participant; - - Wa__MessageKey key; - key.remotejid = szChatId.GetBuffer(); - key.id = (char*)msgId; - key.fromme = bFromMe; key.has_fromme = true; - if (participant) - key.participant = (char*)participant; - - Wa__WebMessageInfo msg; - msg.key = &key; - msg.messagetimestamp = _atoi64(node.getAttr("t")); msg.has_messagetimestamp = true; - msg.pushname = (char*)node.getAttr("notify"); - if (bFromMe) - msg.status = WA__WEB_MESSAGE_INFO__STATUS__SERVER_ACK, msg.has_status = true; - - int iDecryptable = 0; - - for (auto &it : node.getChildren()) { - if (it->title != "enc" || it->content.length() == 0) - continue; - - MBinBuffer msgBody; - auto *pszType = it->getAttr("type"); - try { - if (!mir_strcmp(pszType, "pkmsg") || !mir_strcmp(pszType, "msg")) { - CMStringA szUser = (WAJid(szSender).isUser()) ? szSender : szAuthor; - msgBody = m_signalStore.decryptSignalProto(szUser, pszType, it->content); - } - else if (!mir_strcmp(pszType, "skmsg")) { - msgBody = m_signalStore.decryptGroupSignalProto(szSender, szAuthor, it->content); - } - else throw "Invalid e2e type"; - - if (msgBody.isEmpty()) - throw "Invalid e2e message"; - - iDecryptable++; - - proto::Message encMsg(unpadBuffer16(msgBody)); - if (!encMsg) - throw "Invalid decoded message"; - - if (encMsg->devicesentmessage) - msg.message = encMsg->devicesentmessage->message; - else - msg.message = encMsg; - - if (encMsg->senderkeydistributionmessage) - m_signalStore.processSenderKeyMessage(szAuthor, encMsg->senderkeydistributionmessage); - - ProcessMessage(type, msg); - - // send receipt - const char *pszReceiptType = nullptr, *pszReceiptTo = participant; - if (!mir_strcmp(category, "peer")) - pszReceiptType = "peer_msg"; - else if (bFromMe) { - // message was sent by me from a different device - pszReceiptType = "sender"; - if (WAJid(szChatId).isUser()) - pszReceiptTo = szAuthor; - } - else if (!m_hServerConn) - pszReceiptType = "inactive"; - - SendReceipt(szChatId, pszReceiptTo, msgId, pszReceiptType); - } - catch (const char *pszError) { - debugLogA("Message decryption failed with error: %s", pszError); - } - - if (!iDecryptable) { - debugLogA("Nothing to decrypt"); - return; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static const Wa__Message* getBody(const Wa__Message *message) -{ - if (message->ephemeralmessage) { - auto *pMsg = message->ephemeralmessage->message; - return (pMsg->viewoncemessage) ? pMsg->viewoncemessage->message : pMsg; - } - - if (message->viewoncemessage) - return message->viewoncemessage->message; - - return message; -} - -void WhatsAppProto::ProcessMessage(WAMSG type, const Wa__WebMessageInfo &msg) -{ - auto *key = msg.key; - auto *body = getBody(msg.message); - - debugLogA("Got a message: %s", protobuf_c_text_to_string(&msg).c_str()); - - uint32_t timestamp = msg.messagetimestamp; - char *participant = key->participant, *chatId; - auto *msgId = key->id; - - if (type.bPrivateChat || type.bGroupChat) - chatId = key->remotejid; - else - chatId = (participant) ? participant : key->remotejid; - - WAJid jidFrom(chatId); jidFrom.device = 0; - WAUser *pUser = AddUser(jidFrom.toString(), false); - - if (!key->fromme && msg.pushname && pUser && !pUser->bIsGroupChat) - setUString(pUser->hContact, "Nick", msg.pushname); - - // try to extract some text - if (pUser) { - CMStringA szMessageText(GetMessageText(body)); - if (!szMessageText.IsEmpty()) { - // for chats & group chats store message in profile - if (type.bPrivateChat || type.bGroupChat) { - PROTORECVEVENT pre = {}; - pre.timestamp = timestamp; - pre.szMessage = szMessageText.GetBuffer(); - pre.szMsgId = msgId; - if (type.bOffline) - pre.flags |= PREF_CREATEREAD; - if (key->fromme) - pre.flags |= PREF_SENT; - ProtoChainRecvMsg(pUser->hContact, &pre); - - if (pUser->bIsGroupChat) { - GCEVENT gce = {m_szModuleName, 0, GC_EVENT_MESSAGE}; - gce.dwFlags = GCEF_UTF8; - gce.pszID.a = pUser->szId; - gce.pszUID.a = participant; - gce.bIsMe = key->fromme; - gce.pszText.a = szMessageText.GetBuffer(); - gce.time = timestamp; - Chat_Event(&gce); - } - } - // translate statuses into status messages - else if (type.bOtherStatus || type.bDirectStatus || type.bPeerBroadcast || type.bOtherBroadcast) { - setUString(pUser->hContact, "StatusMsg", szMessageText); - } - } - } - - if (body->protocolmessage) { - auto *protoMsg = body->protocolmessage; - switch (protoMsg->type) { - case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__APP_STATE_SYNC_KEY_SHARE: - for (int i = 0; i < protoMsg->appstatesynckeyshare->n_keys; i++) { - auto *it = protoMsg->appstatesynckeyshare->keys[i]; - auto &keyid = it->keyid->keyid; - auto &keydata = it->keydata->keydata; - - CMStringA szSetting(FORMAT, "AppSyncKey%d", decodeBigEndian(keyid)); - db_set_blob(0, m_szModuleName, szSetting, keydata.data, (unsigned)keydata.len); - } - break; - - case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__APP_STATE_FATAL_EXCEPTION_NOTIFICATION: - m_impl.m_resyncApp.Stop(); - m_impl.m_resyncApp.Start(10000); - break; - - case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__HISTORY_SYNC_NOTIFICATION: - debugLogA("History sync notification"); - if (auto *pHist = protoMsg->historysyncnotification) { - MBinBuffer buf(DownloadEncryptedFile(directPath2url(pHist->directpath), pHist->mediakey, "History")); - if (!buf.isEmpty()) { - MBinBuffer inflate(unzip(unpadBuffer16(buf))); - - proto::HistorySync sync(inflate); - if (sync) - ProcessHistorySync(sync); - } - } - break; - - case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__REVOKE: - break; - - case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__EPHEMERAL_SETTING: - if (pUser) { - setDword(pUser->hContact, DBKEY_EPHEMERAL_TS, timestamp); - setDword(pUser->hContact, DBKEY_EPHEMERAL_EXPIRE, protoMsg->ephemeralexpiration); - } - break; - } - } - else if (body->reactionmessage) { - debugLogA("Got a reaction to a message"); - } - else if (msg.has_messagestubtype) { - switch (msg.messagestubtype) { - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_LEAVE: - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_REMOVE: - debugLogA("Participant %s removed from chat", participant); - break; - - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_ADD: - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_INVITE: - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_ADD_REQUEST_JOIN: - debugLogA("Participant %s added to chat", participant); - break; - - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_DEMOTE: - debugLogA("Participant %s demoted", participant); - break; - - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_PROMOTE: - debugLogA("Participant %s promoted", participant); - break; - - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_CHANGE_ANNOUNCE: - debugLogA("Groupchat announce", participant); - break; - - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_CHANGE_RESTRICT: - debugLogA("Groupchat restriction", participant); - break; - - case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_CHANGE_SUBJECT: - debugLogA("Groupchat subject was changed", participant); - break; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnReceiveAck(const WANode &node) -{ - auto *pUser = FindUser(node.getAttr("from")); - if (pUser == nullptr) - return; - - if (!mir_strcmp(node.getAttr("class"), "message")) { - WAOwnMessage tmp(0, 0, node.getAttr("id")); - { - mir_cslock lck(m_csOwnMessages); - if (auto *pOwn = m_arOwnMsgs.find(&tmp)) { - tmp.pktId = pOwn->pktId; - m_arOwnMsgs.remove(pOwn); - } - else return; - } - ProtoBroadcastAck(pUser->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)tmp.pktId, (LPARAM)tmp.szMessageId.c_str()); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool WhatsAppProto::CreateMsgParticipant(WANode *pParticipants, const WAJid &jid, const MBinBuffer &orig) -{ - int type = 0; - - try { - MBinBuffer pBuffer(m_signalStore.encryptSignalProto(jid, orig, type)); - - auto *pNode = pParticipants->addChild("to"); - pNode->addAttr("jid", jid.toString()); - - auto *pEnc = pNode->addChild("enc"); - *pEnc << CHAR_PARAM("v", "2") << CHAR_PARAM("type", (type == 3) ? "pkmsg" : "msg"); - pEnc->content.assign(pBuffer.data(), pBuffer.length()); - } - catch (const char *) { - } - - return type == 3; -} - -int WhatsAppProto::SendTextMessage(const char *jid, const char *pszMsg) -{ - WAJid toJid(jid); - - // send task creation - auto *pTask = new WASendTask(jid); - - // basic message - Wa__Message__ExtendedTextMessage extMessage; - extMessage.text = (char *)pszMsg; - - Wa__Message body; - body.extendedtextmessage = &extMessage; - - LIST<char> arCheckList(1); - if (toJid.isGroup()) { - MBinBuffer encodedMsg(proto::Serialize(&body)); - padBuffer16(encodedMsg); - - MBinBuffer skmsgKey; - MBinBuffer cipherText(m_signalStore.encryptSenderKey(toJid, m_szJid, encodedMsg, skmsgKey)); - - auto *pEnc = pTask->payLoad.addChild("enc"); - *pEnc << CHAR_PARAM("v", "2") << CHAR_PARAM("type", "skmsg"); - pEnc->content.append(cipherText.data(), cipherText.length()); - - Wa__Message__SenderKeyDistributionMessage sentBody; - sentBody.axolotlsenderkeydistributionmessage.data = skmsgKey.data(); - sentBody.axolotlsenderkeydistributionmessage.len = skmsgKey.length(); - sentBody.has_axolotlsenderkeydistributionmessage = true; - sentBody.groupid = (char*)jid; - - Wa__Message msg; - msg.senderkeydistributionmessage = &sentBody; - - pTask->content.append(proto::Serialize(&msg)); - - if (auto *pChatUser = FindUser(jid)) { - if (pChatUser->si) { - for (auto &it : pChatUser->si->arUsers) { - T2Utf userJid(it->pszUID); - auto *pUser = FindUser(jid); - if (pUser == nullptr) - m_arUsers.insert(pUser = new WAUser(INVALID_CONTACT_ID, userJid, false)); - if (pUser->bDeviceInit) { - for (auto &jt : pUser->arDevices) - pTask->arDest.insert(new WAJid(*jt)); - } - else arCheckList.insert(mir_strdup(userJid)); - } - } - } - } - else { - Wa__Message__DeviceSentMessage sentBody; - sentBody.message = &body; - sentBody.destinationjid = (char*)jid; - - Wa__Message msg; - msg.devicesentmessage = &sentBody; - - pTask->content.append(proto::Serialize(&msg)); - - if (auto *pUser = FindUser(jid)) { - if (pUser->bDeviceInit) { - for (auto &it : pUser->arDevices) - pTask->arDest.insert(new WAJid(*it)); - } - else arCheckList.insert(mir_strdup(jid));; - } - } - - padBuffer16(pTask->content); - - auto *pOwnUser = FindUser(m_szJid); - for (auto &it : pOwnUser->arDevices) - if (it->device != (int)getDword(DBKEY_DEVICE_ID)) - pTask->arDest.insert(new WAJid(*it)); - - // generate & reserve packet id - int pktId; - { - mir_cslock lck(m_csOwnMessages); - pktId = m_iPacketId++; - m_arOwnMsgs.insert(new WAOwnMessage(pktId, jid, pTask->szMsgId)); - } - - // if some keys are missing, schedule task for execution & retrieve keys - if (arCheckList.getCount()) { - SendUsync(arCheckList, pTask); - for (auto &it : arCheckList) - mir_free(it); - } - else // otherwise simply execute the task - SendTask(pTask); - - return pktId; -} - -void WhatsAppProto::FinishTask(WASendTask *pTask) -{ - if (auto *pUser = FindUser(pTask->payLoad.getAttr("to"))) { - if (pUser->bIsGroupChat) { - for (auto &it : pUser->si->getUserList()) - if (auto *pChatUser = FindUser(T2Utf(it->pszUID))) - for (auto &cc: pChatUser->arDevices) - pTask->arDest.insert(new WAJid(*cc)); - } - else for (auto &it : pUser->arDevices) - pTask->arDest.insert(new WAJid(*it)); - } - - SendTask(pTask); -} - -void WhatsAppProto::SendTask(WASendTask *pTask) -{ - // pack all data and send the whole payload - bool shouldIncludeIdentity = false; - auto *pParticipants = pTask->payLoad.addChild("participants"); - - for (auto &it : pTask->arDest) - shouldIncludeIdentity |= CreateMsgParticipant(pParticipants, *it, pTask->content); - - if (shouldIncludeIdentity) { - MBinBuffer encIdentity(m_signalStore.encodeSignedIdentity(true)); - auto *pNode = pTask->payLoad.addChild("device-identity"); - pNode->content.assign(encIdentity.data(), encIdentity.length()); - } - - WSSendNode(pTask->payLoad); - delete pTask; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+void WhatsAppProto::OnReceiveMessage(const WANode &node)
+{
+ auto *msgId = node.getAttr("id");
+ auto *msgType = node.getAttr("type");
+ auto *msgFrom = node.getAttr("from");
+ auto *category = node.getAttr("category");
+ auto *recipient = node.getAttr("recipient");
+ auto *participant = node.getAttr("participant");
+
+ if (msgType == nullptr || msgFrom == nullptr || msgId == nullptr) {
+ debugLogA("bad message received: <%s> <%s> <%s>", msgType, msgFrom, msgId);
+ return;
+ }
+
+ SendAck(node);
+
+ MEVENT hEvent = db_event_getById(m_szModuleName, msgId);
+ if (hEvent) {
+ debugLogA("this message is already processed: %s", msgId);
+ return;
+ }
+
+ WAMSG type;
+ WAJid jid(msgFrom);
+ CMStringA szAuthor, szChatId;
+
+ if (node.getAttr("offline"))
+ type.bOffline = true;
+
+ // message from one user to another
+ if (jid.isUser()) {
+ if (recipient) {
+ if (m_szJid != msgFrom) {
+ debugLogA("strange message: with recipient, but not from me");
+ return;
+ }
+ szChatId = recipient;
+ }
+ else szChatId = msgFrom;
+
+ type.bPrivateChat = true;
+ szAuthor = msgFrom;
+ }
+ else if (jid.isGroup()) {
+ if (!participant) {
+ debugLogA("strange message: from group, but without participant");
+ return;
+ }
+
+ type.bGroupChat = true;
+ szAuthor = participant;
+ szChatId = msgFrom;
+ }
+ else if (jid.isBroadcast()) {
+ if (!participant) {
+ debugLogA("strange message: from group, but without participant");
+ return;
+ }
+
+ bool bIsMe = m_szJid == participant;
+ if (jid.isStatusBroadcast()) {
+ if (bIsMe)
+ type.bDirectStatus = true;
+ else
+ type.bOtherStatus = true;
+ }
+ else {
+ if (bIsMe)
+ type.bPeerBroadcast = true;
+ else
+ type.bOtherBroadcast = true;
+ }
+ szChatId = msgFrom;
+ szAuthor = participant;
+ }
+ else {
+ debugLogA("invalid message type");
+ return;
+ }
+
+ CMStringA szSender = (type.bPrivateChat) ? szAuthor : szChatId;
+ bool bFromMe = (m_szJid == msgFrom);
+ if (!bFromMe && participant)
+ bFromMe = m_szJid == participant;
+
+ Wa__MessageKey key;
+ key.remotejid = szChatId.GetBuffer();
+ key.id = (char*)msgId;
+ key.fromme = bFromMe; key.has_fromme = true;
+ if (participant)
+ key.participant = (char*)participant;
+
+ Wa__WebMessageInfo msg;
+ msg.key = &key;
+ msg.messagetimestamp = _atoi64(node.getAttr("t")); msg.has_messagetimestamp = true;
+ msg.pushname = (char*)node.getAttr("notify");
+ if (bFromMe)
+ msg.status = WA__WEB_MESSAGE_INFO__STATUS__SERVER_ACK, msg.has_status = true;
+
+ int iDecryptable = 0;
+
+ for (auto &it : node.getChildren()) {
+ if (it->title != "enc" || it->content.length() == 0)
+ continue;
+
+ MBinBuffer msgBody;
+ auto *pszType = it->getAttr("type");
+ try {
+ if (!mir_strcmp(pszType, "pkmsg") || !mir_strcmp(pszType, "msg")) {
+ CMStringA szUser = (WAJid(szSender).isUser()) ? szSender : szAuthor;
+ msgBody = m_signalStore.decryptSignalProto(szUser, pszType, it->content);
+ }
+ else if (!mir_strcmp(pszType, "skmsg")) {
+ msgBody = m_signalStore.decryptGroupSignalProto(szSender, szAuthor, it->content);
+ }
+ else throw "Invalid e2e type";
+
+ if (msgBody.isEmpty())
+ throw "Invalid e2e message";
+
+ iDecryptable++;
+
+ proto::Message encMsg(unpadBuffer16(msgBody));
+ if (!encMsg)
+ throw "Invalid decoded message";
+
+ if (encMsg->devicesentmessage)
+ msg.message = encMsg->devicesentmessage->message;
+ else
+ msg.message = encMsg;
+
+ if (encMsg->senderkeydistributionmessage)
+ m_signalStore.processSenderKeyMessage(szAuthor, encMsg->senderkeydistributionmessage);
+
+ ProcessMessage(type, msg);
+
+ // send receipt
+ const char *pszReceiptType = nullptr, *pszReceiptTo = participant;
+ if (!mir_strcmp(category, "peer"))
+ pszReceiptType = "peer_msg";
+ else if (bFromMe) {
+ // message was sent by me from a different device
+ pszReceiptType = "sender";
+ if (WAJid(szChatId).isUser())
+ pszReceiptTo = szAuthor;
+ }
+ else if (!m_hServerConn)
+ pszReceiptType = "inactive";
+
+ SendReceipt(szChatId, pszReceiptTo, msgId, pszReceiptType);
+ }
+ catch (const char *pszError) {
+ debugLogA("Message decryption failed with error: %s", pszError);
+ }
+
+ if (!iDecryptable) {
+ debugLogA("Nothing to decrypt");
+ return;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static const Wa__Message* getBody(const Wa__Message *message)
+{
+ if (message->ephemeralmessage) {
+ auto *pMsg = message->ephemeralmessage->message;
+ return (pMsg->viewoncemessage) ? pMsg->viewoncemessage->message : pMsg;
+ }
+
+ if (message->viewoncemessage)
+ return message->viewoncemessage->message;
+
+ return message;
+}
+
+void WhatsAppProto::ProcessMessage(WAMSG type, const Wa__WebMessageInfo &msg)
+{
+ auto *key = msg.key;
+ auto *body = getBody(msg.message);
+
+ debugLogA("Got a message: %s", protobuf_c_text_to_string(&msg).c_str());
+
+ uint32_t timestamp = msg.messagetimestamp;
+ char *participant = key->participant, *chatId;
+ auto *msgId = key->id;
+
+ if (type.bPrivateChat || type.bGroupChat)
+ chatId = key->remotejid;
+ else
+ chatId = (participant) ? participant : key->remotejid;
+
+ WAJid jidFrom(chatId); jidFrom.device = 0;
+ WAUser *pUser = AddUser(jidFrom.toString(), false);
+
+ if (!key->fromme && msg.pushname && pUser && !pUser->bIsGroupChat)
+ setUString(pUser->hContact, "Nick", msg.pushname);
+
+ // try to extract some text
+ if (pUser) {
+ CMStringA szMessageText(GetMessageText(body));
+ if (!szMessageText.IsEmpty()) {
+ // for chats & group chats store message in profile
+ if (type.bPrivateChat || type.bGroupChat) {
+ PROTORECVEVENT pre = {};
+ pre.timestamp = timestamp;
+ pre.szMessage = szMessageText.GetBuffer();
+ pre.szMsgId = msgId;
+ if (type.bOffline)
+ pre.flags |= PREF_CREATEREAD;
+ if (key->fromme)
+ pre.flags |= PREF_SENT;
+ ProtoChainRecvMsg(pUser->hContact, &pre);
+
+ if (pUser->bIsGroupChat) {
+ GCEVENT gce = {m_szModuleName, 0, GC_EVENT_MESSAGE};
+ gce.dwFlags = GCEF_UTF8;
+ gce.pszID.a = pUser->szId;
+ gce.pszUID.a = participant;
+ gce.bIsMe = key->fromme;
+ gce.pszText.a = szMessageText.GetBuffer();
+ gce.time = timestamp;
+ Chat_Event(&gce);
+ }
+ }
+ // translate statuses into status messages
+ else if (type.bOtherStatus || type.bDirectStatus || type.bPeerBroadcast || type.bOtherBroadcast) {
+ setUString(pUser->hContact, "StatusMsg", szMessageText);
+ }
+ }
+ }
+
+ if (body->protocolmessage) {
+ auto *protoMsg = body->protocolmessage;
+ switch (protoMsg->type) {
+ case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__APP_STATE_SYNC_KEY_SHARE:
+ for (int i = 0; i < protoMsg->appstatesynckeyshare->n_keys; i++) {
+ auto *it = protoMsg->appstatesynckeyshare->keys[i];
+ auto &keyid = it->keyid->keyid;
+ auto &keydata = it->keydata->keydata;
+
+ CMStringA szSetting(FORMAT, "AppSyncKey%d", decodeBigEndian(keyid));
+ db_set_blob(0, m_szModuleName, szSetting, keydata.data, (unsigned)keydata.len);
+ }
+ break;
+
+ case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__APP_STATE_FATAL_EXCEPTION_NOTIFICATION:
+ m_impl.m_resyncApp.Stop();
+ m_impl.m_resyncApp.Start(10000);
+ break;
+
+ case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__HISTORY_SYNC_NOTIFICATION:
+ debugLogA("History sync notification");
+ if (auto *pHist = protoMsg->historysyncnotification) {
+ MBinBuffer buf(DownloadEncryptedFile(directPath2url(pHist->directpath), pHist->mediakey, "History"));
+ if (!buf.isEmpty()) {
+ MBinBuffer inflate(unzip(unpadBuffer16(buf)));
+
+ proto::HistorySync sync(inflate);
+ if (sync)
+ ProcessHistorySync(sync);
+ }
+ }
+ break;
+
+ case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__REVOKE:
+ break;
+
+ case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__EPHEMERAL_SETTING:
+ if (pUser) {
+ setDword(pUser->hContact, DBKEY_EPHEMERAL_TS, timestamp);
+ setDword(pUser->hContact, DBKEY_EPHEMERAL_EXPIRE, protoMsg->ephemeralexpiration);
+ }
+ break;
+ }
+ }
+ else if (body->reactionmessage) {
+ debugLogA("Got a reaction to a message");
+ }
+ else if (msg.has_messagestubtype) {
+ switch (msg.messagestubtype) {
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_LEAVE:
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_REMOVE:
+ debugLogA("Participant %s removed from chat", participant);
+ break;
+
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_ADD:
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_INVITE:
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_ADD_REQUEST_JOIN:
+ debugLogA("Participant %s added to chat", participant);
+ break;
+
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_DEMOTE:
+ debugLogA("Participant %s demoted", participant);
+ break;
+
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_PARTICIPANT_PROMOTE:
+ debugLogA("Participant %s promoted", participant);
+ break;
+
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_CHANGE_ANNOUNCE:
+ debugLogA("Groupchat announce", participant);
+ break;
+
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_CHANGE_RESTRICT:
+ debugLogA("Groupchat restriction", participant);
+ break;
+
+ case WA__WEB_MESSAGE_INFO__STUB_TYPE__GROUP_CHANGE_SUBJECT:
+ debugLogA("Groupchat subject was changed", participant);
+ break;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::OnReceiveAck(const WANode &node)
+{
+ auto *pUser = FindUser(node.getAttr("from"));
+ if (pUser == nullptr)
+ return;
+
+ if (!mir_strcmp(node.getAttr("class"), "message")) {
+ WAOwnMessage tmp(0, 0, node.getAttr("id"));
+ {
+ mir_cslock lck(m_csOwnMessages);
+ if (auto *pOwn = m_arOwnMsgs.find(&tmp)) {
+ tmp.pktId = pOwn->pktId;
+ m_arOwnMsgs.remove(pOwn);
+ }
+ else return;
+ }
+ ProtoBroadcastAck(pUser->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)tmp.pktId, (LPARAM)tmp.szMessageId.c_str());
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool WhatsAppProto::CreateMsgParticipant(WANode *pParticipants, const WAJid &jid, const MBinBuffer &orig)
+{
+ int type = 0;
+
+ try {
+ MBinBuffer pBuffer(m_signalStore.encryptSignalProto(jid, orig, type));
+
+ auto *pNode = pParticipants->addChild("to");
+ pNode->addAttr("jid", jid.toString());
+
+ auto *pEnc = pNode->addChild("enc");
+ *pEnc << CHAR_PARAM("v", "2") << CHAR_PARAM("type", (type == 3) ? "pkmsg" : "msg");
+ pEnc->content.assign(pBuffer.data(), pBuffer.length());
+ }
+ catch (const char *) {
+ }
+
+ return type == 3;
+}
+
+int WhatsAppProto::SendTextMessage(const char *jid, const char *pszMsg)
+{
+ WAJid toJid(jid);
+
+ // send task creation
+ auto *pTask = new WASendTask(jid);
+
+ // basic message
+ Wa__Message__ExtendedTextMessage extMessage;
+ extMessage.text = (char *)pszMsg;
+
+ Wa__Message body;
+ body.extendedtextmessage = &extMessage;
+
+ LIST<char> arCheckList(1);
+ if (toJid.isGroup()) {
+ MBinBuffer encodedMsg(proto::Serialize(&body));
+ padBuffer16(encodedMsg);
+
+ MBinBuffer skmsgKey;
+ MBinBuffer cipherText(m_signalStore.encryptSenderKey(toJid, m_szJid, encodedMsg, skmsgKey));
+
+ auto *pEnc = pTask->payLoad.addChild("enc");
+ *pEnc << CHAR_PARAM("v", "2") << CHAR_PARAM("type", "skmsg");
+ pEnc->content.append(cipherText.data(), cipherText.length());
+
+ Wa__Message__SenderKeyDistributionMessage sentBody;
+ sentBody.axolotlsenderkeydistributionmessage.data = skmsgKey.data();
+ sentBody.axolotlsenderkeydistributionmessage.len = skmsgKey.length();
+ sentBody.has_axolotlsenderkeydistributionmessage = true;
+ sentBody.groupid = (char*)jid;
+
+ Wa__Message msg;
+ msg.senderkeydistributionmessage = &sentBody;
+
+ pTask->content.append(proto::Serialize(&msg));
+
+ if (auto *pChatUser = FindUser(jid)) {
+ if (pChatUser->si) {
+ for (auto &it : pChatUser->si->arUsers) {
+ T2Utf userJid(it->pszUID);
+ auto *pUser = FindUser(jid);
+ if (pUser == nullptr)
+ m_arUsers.insert(pUser = new WAUser(INVALID_CONTACT_ID, userJid, false));
+ if (pUser->bDeviceInit) {
+ for (auto &jt : pUser->arDevices)
+ pTask->arDest.insert(new WAJid(*jt));
+ }
+ else arCheckList.insert(mir_strdup(userJid));
+ }
+ }
+ }
+ }
+ else {
+ Wa__Message__DeviceSentMessage sentBody;
+ sentBody.message = &body;
+ sentBody.destinationjid = (char*)jid;
+
+ Wa__Message msg;
+ msg.devicesentmessage = &sentBody;
+
+ pTask->content.append(proto::Serialize(&msg));
+
+ if (auto *pUser = FindUser(jid)) {
+ if (pUser->bDeviceInit) {
+ for (auto &it : pUser->arDevices)
+ pTask->arDest.insert(new WAJid(*it));
+ }
+ else arCheckList.insert(mir_strdup(jid));;
+ }
+ }
+
+ padBuffer16(pTask->content);
+
+ auto *pOwnUser = FindUser(m_szJid);
+ for (auto &it : pOwnUser->arDevices)
+ if (it->device != (int)getDword(DBKEY_DEVICE_ID))
+ pTask->arDest.insert(new WAJid(*it));
+
+ // generate & reserve packet id
+ int pktId;
+ {
+ mir_cslock lck(m_csOwnMessages);
+ pktId = m_iPacketId++;
+ m_arOwnMsgs.insert(new WAOwnMessage(pktId, jid, pTask->szMsgId));
+ }
+
+ // if some keys are missing, schedule task for execution & retrieve keys
+ if (arCheckList.getCount()) {
+ SendUsync(arCheckList, pTask);
+ for (auto &it : arCheckList)
+ mir_free(it);
+ }
+ else // otherwise simply execute the task
+ SendTask(pTask);
+
+ return pktId;
+}
+
+void WhatsAppProto::FinishTask(WASendTask *pTask)
+{
+ if (auto *pUser = FindUser(pTask->payLoad.getAttr("to"))) {
+ if (pUser->bIsGroupChat) {
+ for (auto &it : pUser->si->getUserList())
+ if (auto *pChatUser = FindUser(T2Utf(it->pszUID)))
+ for (auto &cc: pChatUser->arDevices)
+ pTask->arDest.insert(new WAJid(*cc));
+ }
+ else for (auto &it : pUser->arDevices)
+ pTask->arDest.insert(new WAJid(*it));
+ }
+
+ SendTask(pTask);
+}
+
+void WhatsAppProto::SendTask(WASendTask *pTask)
+{
+ // pack all data and send the whole payload
+ bool shouldIncludeIdentity = false;
+ auto *pParticipants = pTask->payLoad.addChild("participants");
+
+ for (auto &it : pTask->arDest)
+ shouldIncludeIdentity |= CreateMsgParticipant(pParticipants, *it, pTask->content);
+
+ if (shouldIncludeIdentity) {
+ MBinBuffer encIdentity(m_signalStore.encodeSignedIdentity(true));
+ auto *pNode = pTask->payLoad.addChild("device-identity");
+ pNode->content.assign(encIdentity.data(), encIdentity.length());
+ }
+
+ WSSendNode(pTask->payLoad);
+ delete pTask;
+}
diff --git a/protocols/WhatsApp/src/noise.cpp b/protocols/WhatsApp/src/noise.cpp index 004b71cdef..18ae206519 100644 --- a/protocols/WhatsApp/src/noise.cpp +++ b/protocols/WhatsApp/src/noise.cpp @@ -1,201 +1,201 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -WANoise class implementation - -*/ - -#include "stdafx.h" - -static uint8_t intro_header[] = {87, 65, 6, DICT_VERSION}; -static uint8_t noise_init[] = "Noise_XX_25519_AESGCM_SHA256\0\0\0\0"; - -WANoise::WANoise(WhatsAppProto *_ppro) : - ppro(_ppro) -{ - salt.assign(noise_init, 32); - encKey.assign(noise_init, 32); - decKey.assign(noise_init, 32); - - // generate ephemeral keys: public & private - ec_key_pair *pKeys; - curve_generate_key_pair(ppro->m_signalStore.CTX(), &pKeys); - - auto *pPubKey = ec_key_pair_get_public(pKeys); - ephemeral.pub.assign(pPubKey->data, sizeof(pPubKey->data)); - - auto *pPrivKey = ec_key_pair_get_private(pKeys); - ephemeral.priv.assign(pPrivKey->data, sizeof(pPrivKey->data)); - ec_key_pair_destroy((signal_type_base*)pKeys); - - // prepare hash - memcpy(hash, noise_init, 32); - updateHash(intro_header, 4); - updateHash(ephemeral.pub.data(), ephemeral.pub.length()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// libsignal data initialization - -void WANoise::init() -{ - // no data? generate them - if (ppro->getDword(DBKEY_REG_ID, 0xFFFF) == 0xFFFF) { - // generate registration id - uint32_t regId; - Utils_GetRandom(®Id, sizeof(regId)); - ppro->setDword(DBKEY_REG_ID, regId & 0x3FFF); - - // generate secret key - uint8_t secretKey[32]; - Utils_GetRandom(secretKey, sizeof(secretKey)); - db_set_blob(0, ppro->m_szModuleName, DBKEY_SECRET_KEY, secretKey, sizeof(secretKey)); - - // generate noise keys (private & public) - ec_key_pair *pKeys; - curve_generate_key_pair(ppro->m_signalStore.CTX(), &pKeys); - - auto *pPubKey = ec_key_pair_get_public(pKeys); - db_set_blob(0, ppro->m_szModuleName, DBKEY_NOISE_PUB, pPubKey->data, sizeof(pPubKey->data)); - - auto *pPrivKey = ec_key_pair_get_private(pKeys); - db_set_blob(0, ppro->m_szModuleName, DBKEY_NOISE_PRIV, pPrivKey->data, sizeof(pPrivKey->data)); - ec_key_pair_destroy((signal_type_base *)pKeys); - } - - noiseKeys.pub = ppro->getBlob(DBKEY_NOISE_PUB); - noiseKeys.priv = ppro->getBlob(DBKEY_NOISE_PRIV); -} - -void WANoise::finish() -{ - deriveKey("", 0, encKey, decKey); - readCounter = writeCounter = 0; - memset(hash, 0, sizeof(hash)); - bInitFinished = true; -} - -void WANoise::deriveKey(const void *pData, size_t cbLen, MBinBuffer &write, MBinBuffer &read) -{ - size_t outlen = 64; - uint8_t out[64]; - HKDF(EVP_sha256(), salt.data(), (int)salt.length(), (BYTE *)pData, (int)cbLen, (BYTE *)"", 0, out, outlen); - - write.assign(out, 32); - read.assign(out + 32, 32); -} - -void WANoise::mixIntoKey(const void *n, const void *p) -{ - uint8_t tmp[32]; - curve25519_donna((unsigned char *)tmp, (const unsigned char *)n, (const unsigned char *)p); - - deriveKey(tmp, sizeof(tmp), salt, encKey); - decKey.assign(encKey.data(), encKey.length()); - readCounter = writeCounter = 0; -} - -MBinBuffer WANoise::decrypt(const void *pData, size_t cbLen) -{ - uint8_t iv[12]; - generateIV(iv, (bInitFinished) ? readCounter : writeCounter); - - MBinBuffer res; - if (!bInitFinished) - res = aesDecrypt(EVP_aes_256_gcm(), decKey.data(), iv, pData, cbLen, hash, sizeof(hash)); - else - res = aesDecrypt(EVP_aes_256_gcm(), decKey.data(), iv, pData, cbLen); - - updateHash(pData, cbLen); - return res; -} - -size_t WANoise::decodeFrame(const uint8_t *&p, size_t &cbLen) -{ - if (cbLen < 3) - return 0; - - size_t payloadLen = 0; - for (int i = 0; i < 3; i++) { - payloadLen <<= 8; - payloadLen += p[i]; - } - - // ppro->debugLogA("got payload of size %d", payloadLen); - - cbLen -= 3; - if (payloadLen > cbLen) { - ppro->debugLogA("payload length %d exceeds capacity %d", payloadLen, cbLen); - return 0; - } - - p += 3; - return payloadLen; -} - -MBinBuffer WANoise::encodeFrame(const void *pData, size_t cbLen) -{ - MBinBuffer res; - if (!bSendIntro) { - bSendIntro = true; - res.append(intro_header, 4); - } - - uint8_t buf[3]; - size_t foo = cbLen; - for (int i = 0; i < 3; i++) { - buf[2 - i] = foo & 0xFF; - foo >>= 8; - } - res.append(buf, 3); - res.append(pData, cbLen); - return res; -} - -MBinBuffer WANoise::encrypt(const void *pData, size_t cbLen) -{ - uint8_t iv[12]; - generateIV(iv, writeCounter); - - MBinBuffer res; - uint8_t outbuf[1024 + 64]; - - int enc_len = 0, final_len = 0; - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, encKey.data(), iv); - - if (!bInitFinished) - EVP_EncryptUpdate(ctx, NULL, &enc_len, hash, sizeof(hash)); - - for (size_t len = 0; len < cbLen; len += 1024) { - size_t portionSize = cbLen - len; - EVP_EncryptUpdate(ctx, outbuf, &enc_len, (BYTE *)pData + len, (int)min(portionSize, 1024)); - res.append(outbuf, enc_len); - } - EVP_EncryptFinal_ex(ctx, outbuf, &final_len); - if (final_len) - res.append(outbuf, final_len); - - uint8_t tag[16]; - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof(tag), tag); - res.append(tag, sizeof(tag)); - - EVP_CIPHER_CTX_free(ctx); - - updateHash(res.data(), res.length()); - return res; -} - -void WANoise::updateHash(const void *pData, size_t cbLen) -{ - if (bInitFinished) - return; - - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, hash, sizeof(hash)); - SHA256_Update(&ctx, pData, cbLen); - SHA256_Final(hash, &ctx); -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+WANoise class implementation
+
+*/
+
+#include "stdafx.h"
+
+static uint8_t intro_header[] = {87, 65, 6, DICT_VERSION};
+static uint8_t noise_init[] = "Noise_XX_25519_AESGCM_SHA256\0\0\0\0";
+
+WANoise::WANoise(WhatsAppProto *_ppro) :
+ ppro(_ppro)
+{
+ salt.assign(noise_init, 32);
+ encKey.assign(noise_init, 32);
+ decKey.assign(noise_init, 32);
+
+ // generate ephemeral keys: public & private
+ ec_key_pair *pKeys;
+ curve_generate_key_pair(ppro->m_signalStore.CTX(), &pKeys);
+
+ auto *pPubKey = ec_key_pair_get_public(pKeys);
+ ephemeral.pub.assign(pPubKey->data, sizeof(pPubKey->data));
+
+ auto *pPrivKey = ec_key_pair_get_private(pKeys);
+ ephemeral.priv.assign(pPrivKey->data, sizeof(pPrivKey->data));
+ ec_key_pair_destroy((signal_type_base*)pKeys);
+
+ // prepare hash
+ memcpy(hash, noise_init, 32);
+ updateHash(intro_header, 4);
+ updateHash(ephemeral.pub.data(), ephemeral.pub.length());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// libsignal data initialization
+
+void WANoise::init()
+{
+ // no data? generate them
+ if (ppro->getDword(DBKEY_REG_ID, 0xFFFF) == 0xFFFF) {
+ // generate registration id
+ uint32_t regId;
+ Utils_GetRandom(®Id, sizeof(regId));
+ ppro->setDword(DBKEY_REG_ID, regId & 0x3FFF);
+
+ // generate secret key
+ uint8_t secretKey[32];
+ Utils_GetRandom(secretKey, sizeof(secretKey));
+ db_set_blob(0, ppro->m_szModuleName, DBKEY_SECRET_KEY, secretKey, sizeof(secretKey));
+
+ // generate noise keys (private & public)
+ ec_key_pair *pKeys;
+ curve_generate_key_pair(ppro->m_signalStore.CTX(), &pKeys);
+
+ auto *pPubKey = ec_key_pair_get_public(pKeys);
+ db_set_blob(0, ppro->m_szModuleName, DBKEY_NOISE_PUB, pPubKey->data, sizeof(pPubKey->data));
+
+ auto *pPrivKey = ec_key_pair_get_private(pKeys);
+ db_set_blob(0, ppro->m_szModuleName, DBKEY_NOISE_PRIV, pPrivKey->data, sizeof(pPrivKey->data));
+ ec_key_pair_destroy((signal_type_base *)pKeys);
+ }
+
+ noiseKeys.pub = ppro->getBlob(DBKEY_NOISE_PUB);
+ noiseKeys.priv = ppro->getBlob(DBKEY_NOISE_PRIV);
+}
+
+void WANoise::finish()
+{
+ deriveKey("", 0, encKey, decKey);
+ readCounter = writeCounter = 0;
+ memset(hash, 0, sizeof(hash));
+ bInitFinished = true;
+}
+
+void WANoise::deriveKey(const void *pData, size_t cbLen, MBinBuffer &write, MBinBuffer &read)
+{
+ size_t outlen = 64;
+ uint8_t out[64];
+ HKDF(EVP_sha256(), salt.data(), (int)salt.length(), (BYTE *)pData, (int)cbLen, (BYTE *)"", 0, out, outlen);
+
+ write.assign(out, 32);
+ read.assign(out + 32, 32);
+}
+
+void WANoise::mixIntoKey(const void *n, const void *p)
+{
+ uint8_t tmp[32];
+ curve25519_donna((unsigned char *)tmp, (const unsigned char *)n, (const unsigned char *)p);
+
+ deriveKey(tmp, sizeof(tmp), salt, encKey);
+ decKey.assign(encKey.data(), encKey.length());
+ readCounter = writeCounter = 0;
+}
+
+MBinBuffer WANoise::decrypt(const void *pData, size_t cbLen)
+{
+ uint8_t iv[12];
+ generateIV(iv, (bInitFinished) ? readCounter : writeCounter);
+
+ MBinBuffer res;
+ if (!bInitFinished)
+ res = aesDecrypt(EVP_aes_256_gcm(), decKey.data(), iv, pData, cbLen, hash, sizeof(hash));
+ else
+ res = aesDecrypt(EVP_aes_256_gcm(), decKey.data(), iv, pData, cbLen);
+
+ updateHash(pData, cbLen);
+ return res;
+}
+
+size_t WANoise::decodeFrame(const uint8_t *&p, size_t &cbLen)
+{
+ if (cbLen < 3)
+ return 0;
+
+ size_t payloadLen = 0;
+ for (int i = 0; i < 3; i++) {
+ payloadLen <<= 8;
+ payloadLen += p[i];
+ }
+
+ // ppro->debugLogA("got payload of size %d", payloadLen);
+
+ cbLen -= 3;
+ if (payloadLen > cbLen) {
+ ppro->debugLogA("payload length %d exceeds capacity %d", payloadLen, cbLen);
+ return 0;
+ }
+
+ p += 3;
+ return payloadLen;
+}
+
+MBinBuffer WANoise::encodeFrame(const void *pData, size_t cbLen)
+{
+ MBinBuffer res;
+ if (!bSendIntro) {
+ bSendIntro = true;
+ res.append(intro_header, 4);
+ }
+
+ uint8_t buf[3];
+ size_t foo = cbLen;
+ for (int i = 0; i < 3; i++) {
+ buf[2 - i] = foo & 0xFF;
+ foo >>= 8;
+ }
+ res.append(buf, 3);
+ res.append(pData, cbLen);
+ return res;
+}
+
+MBinBuffer WANoise::encrypt(const void *pData, size_t cbLen)
+{
+ uint8_t iv[12];
+ generateIV(iv, writeCounter);
+
+ MBinBuffer res;
+ uint8_t outbuf[1024 + 64];
+
+ int enc_len = 0, final_len = 0;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, encKey.data(), iv);
+
+ if (!bInitFinished)
+ EVP_EncryptUpdate(ctx, NULL, &enc_len, hash, sizeof(hash));
+
+ for (size_t len = 0; len < cbLen; len += 1024) {
+ size_t portionSize = cbLen - len;
+ EVP_EncryptUpdate(ctx, outbuf, &enc_len, (BYTE *)pData + len, (int)min(portionSize, 1024));
+ res.append(outbuf, enc_len);
+ }
+ EVP_EncryptFinal_ex(ctx, outbuf, &final_len);
+ if (final_len)
+ res.append(outbuf, final_len);
+
+ uint8_t tag[16];
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof(tag), tag);
+ res.append(tag, sizeof(tag));
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ updateHash(res.data(), res.length());
+ return res;
+}
+
+void WANoise::updateHash(const void *pData, size_t cbLen)
+{
+ if (bInitFinished)
+ return;
+
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, hash, sizeof(hash));
+ SHA256_Update(&ctx, pData, cbLen);
+ SHA256_Final(hash, &ctx);
+}
diff --git a/protocols/WhatsApp/src/options.cpp b/protocols/WhatsApp/src/options.cpp index 3df8e69343..9945da04e5 100644 --- a/protocols/WhatsApp/src/options.cpp +++ b/protocols/WhatsApp/src/options.cpp @@ -1,95 +1,95 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -class COptionsDlg : public CProtoDlgBase<WhatsAppProto> -{ - CCtrlCheck chkHideChats, chkBbcodes; - CCtrlEdit edtGroup, edtNick, edtDevName; - CCtrlButton btnUnregister; - ptrW m_wszOldGroup; - -public: - COptionsDlg(WhatsAppProto *ppro, int iDlgID, bool bFullDlg) : - CProtoDlgBase<WhatsAppProto>(ppro, iDlgID), - chkBbcodes(this, IDC_USEBBCODES), - chkHideChats(this, IDC_HIDECHATS), - edtNick(this, IDC_NICK), - edtGroup(this, IDC_DEFGROUP), - edtDevName(this, IDC_DEVICE_NAME), - btnUnregister(this, IDC_UNREGISTER), - m_wszOldGroup(mir_wstrdup(ppro->m_wszDefaultGroup)) - { - CreateLink(edtNick, ppro->m_wszNick); - CreateLink(edtGroup, ppro->m_wszDefaultGroup); - CreateLink(edtDevName, ppro->m_wszDeviceName); - - if (bFullDlg) { - CreateLink(chkHideChats, ppro->m_bHideGroupchats); - CreateLink(chkBbcodes, ppro->m_bUseBbcodes); - } - - btnUnregister.OnClick = Callback(this, &COptionsDlg::onClick_Unregister); - } - - bool OnInitDialog() override - { - if (!m_proto->getMStringA(DBKEY_ID).IsEmpty()) - edtDevName.Disable(); - return true; - } - - bool OnApply() override - { - if (mir_wstrlen(m_proto->m_wszNick)) { - SetFocus(edtNick.GetHwnd()); - return false; - } - - if (mir_wstrcmp(m_proto->m_wszDefaultGroup, m_wszOldGroup)) - Clist_GroupCreate(0, m_proto->m_wszDefaultGroup); - return true; - } - - void onClick_Unregister(CCtrlButton *) - { - if (IDYES != MessageBoxW(0, TranslateT("Do you really want to unregister Miranda?"), m_proto->m_tszUserName, MB_ICONQUESTION | MB_YESNO)) - return; - - if (m_proto->isOnline()) - m_proto->SendUnregister(); - else - m_proto->OnErase(); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR WhatsAppProto::SvcCreateAccMgrUI(WPARAM, LPARAM hwndParent) -{ - auto *pDlg = new COptionsDlg(this, IDD_ACCMGRUI, false); - pDlg->SetParent((HWND)hwndParent); - pDlg->Create(); - return (INT_PTR)pDlg->GetHwnd(); -} - -int WhatsAppProto::OnOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szTitle.w = m_tszUserName; - odp.flags = ODPF_UNICODE; - odp.szGroup.w = LPGENW("Network"); - - odp.position = 1; - odp.szTab.w = LPGENW("Account"); - odp.pDialog = new COptionsDlg(this, IDD_OPTIONS, true); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class COptionsDlg : public CProtoDlgBase<WhatsAppProto>
+{
+ CCtrlCheck chkHideChats, chkBbcodes;
+ CCtrlEdit edtGroup, edtNick, edtDevName;
+ CCtrlButton btnUnregister;
+ ptrW m_wszOldGroup;
+
+public:
+ COptionsDlg(WhatsAppProto *ppro, int iDlgID, bool bFullDlg) :
+ CProtoDlgBase<WhatsAppProto>(ppro, iDlgID),
+ chkBbcodes(this, IDC_USEBBCODES),
+ chkHideChats(this, IDC_HIDECHATS),
+ edtNick(this, IDC_NICK),
+ edtGroup(this, IDC_DEFGROUP),
+ edtDevName(this, IDC_DEVICE_NAME),
+ btnUnregister(this, IDC_UNREGISTER),
+ m_wszOldGroup(mir_wstrdup(ppro->m_wszDefaultGroup))
+ {
+ CreateLink(edtNick, ppro->m_wszNick);
+ CreateLink(edtGroup, ppro->m_wszDefaultGroup);
+ CreateLink(edtDevName, ppro->m_wszDeviceName);
+
+ if (bFullDlg) {
+ CreateLink(chkHideChats, ppro->m_bHideGroupchats);
+ CreateLink(chkBbcodes, ppro->m_bUseBbcodes);
+ }
+
+ btnUnregister.OnClick = Callback(this, &COptionsDlg::onClick_Unregister);
+ }
+
+ bool OnInitDialog() override
+ {
+ if (!m_proto->getMStringA(DBKEY_ID).IsEmpty())
+ edtDevName.Disable();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (mir_wstrlen(m_proto->m_wszNick)) {
+ SetFocus(edtNick.GetHwnd());
+ return false;
+ }
+
+ if (mir_wstrcmp(m_proto->m_wszDefaultGroup, m_wszOldGroup))
+ Clist_GroupCreate(0, m_proto->m_wszDefaultGroup);
+ return true;
+ }
+
+ void onClick_Unregister(CCtrlButton *)
+ {
+ if (IDYES != MessageBoxW(0, TranslateT("Do you really want to unregister Miranda?"), m_proto->m_tszUserName, MB_ICONQUESTION | MB_YESNO))
+ return;
+
+ if (m_proto->isOnline())
+ m_proto->SendUnregister();
+ else
+ m_proto->OnErase();
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR WhatsAppProto::SvcCreateAccMgrUI(WPARAM, LPARAM hwndParent)
+{
+ auto *pDlg = new COptionsDlg(this, IDD_ACCMGRUI, false);
+ pDlg->SetParent((HWND)hwndParent);
+ pDlg->Create();
+ return (INT_PTR)pDlg->GetHwnd();
+}
+
+int WhatsAppProto::OnOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.szTitle.w = m_tszUserName;
+ odp.flags = ODPF_UNICODE;
+ odp.szGroup.w = LPGENW("Network");
+
+ odp.position = 1;
+ odp.szTab.w = LPGENW("Account");
+ odp.pDialog = new COptionsDlg(this, IDD_OPTIONS, true);
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/protocols/WhatsApp/src/proto.cpp b/protocols/WhatsApp/src/proto.cpp index f97aa989e8..006f78dfc0 100644 --- a/protocols/WhatsApp/src/proto.cpp +++ b/protocols/WhatsApp/src/proto.cpp @@ -1,7 +1,7 @@ /* WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan +Copyright 2019-23 George Hazan */ diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h index e6088cd4b8..dbcf57b597 100644 --- a/protocols/WhatsApp/src/proto.h +++ b/protocols/WhatsApp/src/proto.h @@ -1,7 +1,7 @@ /* WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan +Copyright 2019-23 George Hazan */ diff --git a/protocols/WhatsApp/src/qrcode.cpp b/protocols/WhatsApp/src/qrcode.cpp index 18edfc3e91..22c9e1da95 100644 --- a/protocols/WhatsApp/src/qrcode.cpp +++ b/protocols/WhatsApp/src/qrcode.cpp @@ -1,137 +1,137 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -class CWhatsAppQRDlg : public CProtoDlgBase<WhatsAppProto> -{ -public: - CWhatsAppQRDlg(WhatsAppProto *ppro) : - CProtoDlgBase<WhatsAppProto>(ppro, IDD_SHOWQR) - {} - - void OnDestroy() override - { - m_proto->m_pQRDlg = nullptr; - - if (!m_bSucceeded) - m_proto->ShutdownSession(); - } - - void SetSuccess() - { - m_bSucceeded = true; - } - - void SetData(const CMStringA &str) - { - auto *pQR = QRcode_encodeString(str, 0, QR_ECLEVEL_L, QR_MODE_8, 1); - - HWND hwndRc = GetDlgItem(m_hwnd, IDC_QRPIC); - RECT rc; - GetClientRect(hwndRc, &rc); - - ::SetForegroundWindow(m_hwnd); - - int scale = 8; // (rc.bottom - rc.top) / pQR->width; - int rowLen = pQR->width * scale * 3; - if (rowLen % 4) - rowLen = (rowLen / 4 + 1) * 4; - int dataLen = rowLen * pQR->width * scale; - - mir_ptr<BYTE> pData((BYTE *)mir_alloc(dataLen)); - if (pData == nullptr) { - QRcode_free(pQR); - return; - } - - memset(pData, 0xFF, dataLen); // white background by default - - const BYTE *s = pQR->data; - for (int y = 0; y < pQR->width; y++) { - BYTE *d = pData.get() + rowLen * y * scale; - for (int x = 0; x < pQR->width; x++) { - if (*s & 1) - for (int i = 0; i < scale; i++) - for (int j = 0; j < scale; j++) { - d[j * 3 + i * rowLen] = 0; - d[1 + j * 3 + i * rowLen] = 0; - d[2 + j * 3 + i * rowLen] = 0; - } - - d += scale * 3; - s++; - } - } - - BITMAPFILEHEADER fih = {}; - fih.bfType = 0x4d42; // "BM" - fih.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dataLen; - fih.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); - - BITMAPINFOHEADER bih = {}; - bih.biSize = sizeof(BITMAPINFOHEADER); - bih.biWidth = pQR->width * scale; - bih.biHeight = -bih.biWidth; - bih.biPlanes = 1; - bih.biBitCount = 24; - bih.biCompression = BI_RGB; - - wchar_t wszTempPath[MAX_PATH], wszTempFile[MAX_PATH]; - GetTempPathW(_countof(wszTempPath), wszTempPath); - GetTempFileNameW(wszTempPath, L"wa_", TRUE, wszTempFile); - FILE *f = _wfopen(wszTempFile, L"wb"); - fwrite(&fih, sizeof(BITMAPFILEHEADER), 1, f); - fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, f); - fwrite(pData, sizeof(unsigned char), dataLen, f); - fclose(f); - - SendMessage(hwndRc, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)Image_Load(wszTempFile)); - - DeleteFileW(wszTempFile); - QRcode_free(pQR); - } -}; - -static INT_PTR __stdcall sttShowDialog(void *param) -{ - WhatsAppProto *ppro = (WhatsAppProto *)param; - - if (ppro->m_pQRDlg == nullptr) { - ppro->m_pQRDlg = new CWhatsAppQRDlg(ppro); - ppro->m_pQRDlg->Show(); - } - else { - SetForegroundWindow(ppro->m_pQRDlg->GetHwnd()); - SetActiveWindow(ppro->m_pQRDlg->GetHwnd()); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::CloseQrDialog() -{ - if (m_pQRDlg) { - m_pQRDlg->SetSuccess(); - m_pQRDlg->Close(); - } -} - -bool WhatsAppProto::ShowQrCode(const CMStringA &ref) -{ - CallFunctionSync(sttShowDialog, this); - - MBinBuffer secret(getBlob(DBKEY_SECRET_KEY)); - - ptrA s1(mir_base64_encode(m_noise->noiseKeys.pub)); - ptrA s2(mir_base64_encode(m_signalStore.signedIdentity.pub)); - ptrA s3(mir_base64_encode(secret)); - CMStringA szQrData(FORMAT, "%s,%s,%s,%s", ref.c_str(), s1.get(), s2.get(), s3.get()); - m_pQRDlg->SetData(szQrData); - return true; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+class CWhatsAppQRDlg : public CProtoDlgBase<WhatsAppProto>
+{
+public:
+ CWhatsAppQRDlg(WhatsAppProto *ppro) :
+ CProtoDlgBase<WhatsAppProto>(ppro, IDD_SHOWQR)
+ {}
+
+ void OnDestroy() override
+ {
+ m_proto->m_pQRDlg = nullptr;
+
+ if (!m_bSucceeded)
+ m_proto->ShutdownSession();
+ }
+
+ void SetSuccess()
+ {
+ m_bSucceeded = true;
+ }
+
+ void SetData(const CMStringA &str)
+ {
+ auto *pQR = QRcode_encodeString(str, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
+
+ HWND hwndRc = GetDlgItem(m_hwnd, IDC_QRPIC);
+ RECT rc;
+ GetClientRect(hwndRc, &rc);
+
+ ::SetForegroundWindow(m_hwnd);
+
+ int scale = 8; // (rc.bottom - rc.top) / pQR->width;
+ int rowLen = pQR->width * scale * 3;
+ if (rowLen % 4)
+ rowLen = (rowLen / 4 + 1) * 4;
+ int dataLen = rowLen * pQR->width * scale;
+
+ mir_ptr<BYTE> pData((BYTE *)mir_alloc(dataLen));
+ if (pData == nullptr) {
+ QRcode_free(pQR);
+ return;
+ }
+
+ memset(pData, 0xFF, dataLen); // white background by default
+
+ const BYTE *s = pQR->data;
+ for (int y = 0; y < pQR->width; y++) {
+ BYTE *d = pData.get() + rowLen * y * scale;
+ for (int x = 0; x < pQR->width; x++) {
+ if (*s & 1)
+ for (int i = 0; i < scale; i++)
+ for (int j = 0; j < scale; j++) {
+ d[j * 3 + i * rowLen] = 0;
+ d[1 + j * 3 + i * rowLen] = 0;
+ d[2 + j * 3 + i * rowLen] = 0;
+ }
+
+ d += scale * 3;
+ s++;
+ }
+ }
+
+ BITMAPFILEHEADER fih = {};
+ fih.bfType = 0x4d42; // "BM"
+ fih.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dataLen;
+ fih.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
+
+ BITMAPINFOHEADER bih = {};
+ bih.biSize = sizeof(BITMAPINFOHEADER);
+ bih.biWidth = pQR->width * scale;
+ bih.biHeight = -bih.biWidth;
+ bih.biPlanes = 1;
+ bih.biBitCount = 24;
+ bih.biCompression = BI_RGB;
+
+ wchar_t wszTempPath[MAX_PATH], wszTempFile[MAX_PATH];
+ GetTempPathW(_countof(wszTempPath), wszTempPath);
+ GetTempFileNameW(wszTempPath, L"wa_", TRUE, wszTempFile);
+ FILE *f = _wfopen(wszTempFile, L"wb");
+ fwrite(&fih, sizeof(BITMAPFILEHEADER), 1, f);
+ fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, f);
+ fwrite(pData, sizeof(unsigned char), dataLen, f);
+ fclose(f);
+
+ SendMessage(hwndRc, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)Image_Load(wszTempFile));
+
+ DeleteFileW(wszTempFile);
+ QRcode_free(pQR);
+ }
+};
+
+static INT_PTR __stdcall sttShowDialog(void *param)
+{
+ WhatsAppProto *ppro = (WhatsAppProto *)param;
+
+ if (ppro->m_pQRDlg == nullptr) {
+ ppro->m_pQRDlg = new CWhatsAppQRDlg(ppro);
+ ppro->m_pQRDlg->Show();
+ }
+ else {
+ SetForegroundWindow(ppro->m_pQRDlg->GetHwnd());
+ SetActiveWindow(ppro->m_pQRDlg->GetHwnd());
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WhatsAppProto::CloseQrDialog()
+{
+ if (m_pQRDlg) {
+ m_pQRDlg->SetSuccess();
+ m_pQRDlg->Close();
+ }
+}
+
+bool WhatsAppProto::ShowQrCode(const CMStringA &ref)
+{
+ CallFunctionSync(sttShowDialog, this);
+
+ MBinBuffer secret(getBlob(DBKEY_SECRET_KEY));
+
+ ptrA s1(mir_base64_encode(m_noise->noiseKeys.pub));
+ ptrA s2(mir_base64_encode(m_signalStore.signedIdentity.pub));
+ ptrA s3(mir_base64_encode(secret));
+ CMStringA szQrData(FORMAT, "%s,%s,%s,%s", ref.c_str(), s1.get(), s2.get(), s3.get());
+ m_pQRDlg->SetData(szQrData);
+ return true;
+}
diff --git a/protocols/WhatsApp/src/server.cpp b/protocols/WhatsApp/src/server.cpp index 0afe96662d..7512b05caf 100644 --- a/protocols/WhatsApp/src/server.cpp +++ b/protocols/WhatsApp/src/server.cpp @@ -1,7 +1,7 @@ /* WhatsApp plugin for Miranda NG -Copyright 2019-22 George Hazan +Copyright 2019-23 George Hazan */ diff --git a/protocols/WhatsApp/src/signal.cpp b/protocols/WhatsApp/src/signal.cpp index b94a3452e4..d8a57b0222 100644 --- a/protocols/WhatsApp/src/signal.cpp +++ b/protocols/WhatsApp/src/signal.cpp @@ -1,788 +1,788 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// MSignalStore members - -static int CompareSessions(const MSignalSession *p1, const MSignalSession *p2) -{ - if (int ret = mir_strcmp(p1->szName, p2->szName)) - return ret; - - return p1->getDeviceId() - p2->getDeviceId(); -} - -MSignalStore::MSignalStore(PROTO_INTERFACE *_1, const char *_2) : - pProto(_1), - prefix(_2), - arSessions(1, &CompareSessions) -{ - init(); -} - -MSignalStore::~MSignalStore() -{ - signal_protocol_store_context_destroy(m_pStore); - signal_context_destroy(m_pContext); -} - -void MSignalStore::logError(int err, const char *pszMessage) -{ - if (err < 0) { - pProto->debugLogA("libsignal error %d", err); - throw pszMessage; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void log_func(int level, const char *pmsg, size_t /*msgLen*/, void *pUserData) -{ - auto *pStore = (MSignalStore *)pUserData; - pStore->pProto->debugLogA("libsignal {%d}: %s", level, pmsg); -} - -static int hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *) -{ - HMAC_CTX *ctx = HMAC_CTX_new(); - *hmac_context = ctx; - HMAC_Init(ctx, key, (int)key_len, EVP_sha256()); - return 0; -} - -static int hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *) -{ - return HMAC_Update((HMAC_CTX *)hmac_context, data, data_len); -} - -static int hmac_sha256_final(void *hmac_context, signal_buffer **output, void *) -{ - BYTE data[200]; - unsigned len = 0; - if (!HMAC_Final((HMAC_CTX *)hmac_context, data, &len)) - return 1; - - *output = signal_buffer_create(data, len); - return 0; -} - -static void hmac_sha256_cleanup(void *hmac_context, void *) -{ - HMAC_CTX_free((HMAC_CTX *)hmac_context); -} - -static int random_func(uint8_t *pData, size_t size, void *) -{ - Utils_GetRandom(pData, size); - return 0; -} - -static int decrypt_func(signal_buffer **output, - int /*cipher*/, - const uint8_t *key, size_t /*key_len*/, - const uint8_t *iv, size_t /*iv_len*/, - const uint8_t *ciphertext, size_t ciphertext_len, - void * /*user_data*/) -{ - MBinBuffer res = aesDecrypt(EVP_aes_256_cbc(), key, iv, ciphertext, ciphertext_len); - *output = signal_buffer_create(res.data(), res.length()); - return SG_SUCCESS; -} - -static int encrypt_func(signal_buffer **output, - int /*cipher*/, - const uint8_t *key, size_t /*key_len*/, - const uint8_t *iv, size_t /*iv_len*/, - const uint8_t *ciphertext, size_t ciphertext_len, - void * /*user_data*/) -{ - MBinBuffer res = aesEncrypt(EVP_aes_256_cbc(), key, iv, ciphertext, ciphertext_len); - *output = signal_buffer_create(res.data(), res.length()); - return SG_SUCCESS; -} - -static int contains_session_func(const signal_protocol_address *address, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - auto *pSession = pStore->getSession(address); - return pSession != nullptr; -} - -static int delete_all_sessions_func(const char *name, size_t name_len, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - auto &pList = pStore->arSessions; - - int count = 0; - for (auto &it : pList.rev_iter()) { - if (it->hasAddress(name, name_len)) { - pStore->pProto->delSetting(it->getSetting()); - pList.remove(pList.indexOf(&it)); - count++; - } - } - return count; -} - -int delete_session_func(const signal_protocol_address *address, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - auto &pList = pStore->arSessions; - - MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id); - int idx = pList.getIndex(&tmp); - if (idx != -1) { - pStore->pProto->delSetting(tmp.getSetting()); - pList.remove(idx); - } - return 0; -} - -static int get_sub_device_sessions_func(signal_int_list **sessions, const char *name, size_t name_len, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - CMStringA szName(name, (int)name_len); - - signal_int_list *l = signal_int_list_alloc(); - unsigned int array_size = 0; - - for (auto &it : pStore->arSessions) - if (it->szName == szName) { - array_size++; - signal_int_list_push_back(l, it->getDeviceId()); - } - - *sessions = l; - return array_size; -} - -static void destroy_func(void *) -{} - -int load_session_func(signal_buffer **record, signal_buffer **user_data_storage, const signal_protocol_address *address, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - auto *pSession = pStore->getSession(address); - if (pSession == nullptr) - return 0; - - *record = signal_buffer_create(pSession->sessionData.data(), pSession->sessionData.length()); - *user_data_storage = 0; - return 1; -} - -static int store_session_func(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *, size_t, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id); - auto *pSession = pStore->arSessions.find(&tmp); - if (pSession == nullptr) { - pSession = new MSignalSession(tmp); - pStore->arSessions.insert(pSession); - } - - pSession->sessionData.assign(record, record_len); - db_set_blob(0, pStore->pProto->m_szModuleName, pSession->getSetting(), record, (unsigned)record_len); - return 0; -} - -static int contains_pre_key(uint32_t pre_key_id, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id); - MBinBuffer blob(pStore->pProto->getBlob(szSetting)); - return (blob.data() != 0); -} - -static int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id); - MBinBuffer blob(pStore->pProto->getBlob(szSetting)); - if (blob.isEmpty()) { - pStore->pProto->debugLogA("Prekey #%d not found", pre_key_id); - return SG_ERR_INVALID_KEY_ID; - } - - *record = signal_buffer_create(blob.data(), blob.length()); - return SG_SUCCESS; //key exists and succesfully loaded -} - -static int remove_pre_key(uint32_t pre_key_id, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - pStore->pProto->debugLogA("Request to remove prekey #%d", pre_key_id); - - /* - CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id); - pStore->pProto->delSetting(szSetting); - - szSetting.Format("PreKey%uPublic", pre_key_id); - pStore->pProto->delSetting(szSetting); - - szSetting.Format("PreKey%uPrivate", pre_key_id); - pStore->pProto->delSetting(szSetting); - */ - return 0; -} - -static int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id); - db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned int)record_len); - - session_pre_key *prekey = nullptr; - session_pre_key_deserialize(&prekey, record, record_len, pStore->CTX()); //TODO: handle error - if (prekey) { - ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(prekey); - - SignalBuffer key_buf(ec_key_pair_get_public(pre_key_pair)); - szSetting.Format("PreKey%uPublic", pre_key_id); - db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, key_buf.data(), key_buf.len()); - } - - return 0; -} - -static int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id); - MBinBuffer blob(pStore->pProto->getBlob(szSetting)); - return blob.data() != 0; -} - -static int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - if (signed_pre_key_id == 0) - signed_pre_key_id = 1; - - CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id); - MBinBuffer blob(pStore->pProto->getBlob(szSetting)); - if (blob.isEmpty()) { - pStore->pProto->debugLogA("Signed prekey #%d not found", signed_pre_key_id); - return SG_ERR_INVALID_KEY_ID; - } - - *record = signal_buffer_create(blob.data(), blob.length()); - return SG_SUCCESS; //key exist and succesfully loaded -} - -static int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id); - db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned int)record_len); - return 0; -} - -static int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id); - pStore->pProto->delSetting(szSetting); - return 0; -} - -static int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - MBinBuffer buf; - buf.append(KEY_BUNDLE_TYPE, 1); - buf.append(pStore->signedIdentity.pub); - *public_data = signal_buffer_create(buf.data(), (int)buf.length()); - - *private_data = signal_buffer_create(pStore->signedIdentity.priv.data(), (int)pStore->signedIdentity.priv.length()); - return 0; -} - -static int get_local_registration_id(void *user_data, uint32_t *registration_id) -{ - auto *pStore = (MSignalStore *)user_data; - *registration_id = pStore->pProto->getDword(DBKEY_REG_ID); - return 0; -} - -static int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(FORMAT, "%s_%s_%d", "SignalIdentity", CMStringA(address->name, (int)address->name_len).c_str(), address->device_id); - if (key_data != nullptr) - db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, key_data, (unsigned int)key_len); //TODO: check return value - else - pStore->pProto->delSetting(szSetting); - return 0; -} - -static int is_trusted_identity(const signal_protocol_address * /*address*/, uint8_t * /*key_data*/, size_t /*key_len*/, void * /*user_data*/) -{ - return 1; -} - -static CMStringA get_sender_setting(const signal_protocol_sender_key_name *skn) -{ - WAJid jid(CMStringA(skn->sender.name, (int)skn->sender.name_len)); - return CMStringA(FORMAT, "SenderKey_%*s_%s_%d", (unsigned)skn->group_id_len, skn->group_id, jid.user.c_str(), skn->sender.device_id); -} - -static int load_sender_key(signal_buffer **record, signal_buffer **, const signal_protocol_sender_key_name *skn, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(get_sender_setting(skn)); - MBinBuffer blob(pStore->pProto->getBlob(szSetting)); - if (blob.isEmpty()) - return 0; - - *record = signal_buffer_create(blob.data(), blob.length()); - return 1; -} - -static int store_sender_key(const signal_protocol_sender_key_name *skn, uint8_t *record, size_t record_len, uint8_t*, size_t, void *user_data) -{ - auto *pStore = (MSignalStore *)user_data; - - CMStringA szSetting(get_sender_setting(skn)); - db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned)record_len); - return 0; -} - -void MSignalStore::init() -{ - signal_context_create(&m_pContext, this); - signal_context_set_log_function(m_pContext, log_func); - - signal_crypto_provider prov; - memset(&prov, 0xFF, sizeof(prov)); - prov.hmac_sha256_init_func = hmac_sha256_init; - prov.hmac_sha256_final_func = hmac_sha256_final; - prov.hmac_sha256_update_func = hmac_sha256_update; - prov.hmac_sha256_cleanup_func = hmac_sha256_cleanup; - prov.random_func = random_func; - prov.decrypt_func = decrypt_func; - prov.encrypt_func = encrypt_func; - signal_context_set_crypto_provider(m_pContext, &prov); - - // read resident data from database - MBinBuffer blob(pProto->getBlob(DBKEY_PREKEY)); - if (blob.isEmpty()) { - // nothing? generate signed identity keys (private & public) - ratchet_identity_key_pair *keyPair; - signal_protocol_key_helper_generate_identity_key_pair(&keyPair, m_pContext); - - auto *pPubKey = ratchet_identity_key_pair_get_public(keyPair); - db_set_blob(0, pProto->m_szModuleName, DBKEY_SIGNED_IDENTITY_PUB, pPubKey->data, sizeof(pPubKey->data)); - - auto *pPrivKey = ratchet_identity_key_pair_get_private(keyPair); - db_set_blob(0, pProto->m_szModuleName, DBKEY_SIGNED_IDENTITY_PRIV, pPrivKey->data, sizeof(pPrivKey->data)); - - session_signed_pre_key *signed_pre_key; - signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, keyPair, 1, time(0), m_pContext); - - SignalBuffer prekeyBuf(signed_pre_key); - db_set_blob(0, pProto->m_szModuleName, DBKEY_PREKEY, prekeyBuf.data(), prekeyBuf.len()); - blob.assign(prekeyBuf.data(), prekeyBuf.len()); - - SIGNAL_UNREF(signed_pre_key); - SIGNAL_UNREF(keyPair); - } - - session_signed_pre_key *signed_pre_key; - session_signed_pre_key_deserialize(&signed_pre_key, blob.data(), blob.length(), m_pContext); - - ec_key_pair *pKeys = session_signed_pre_key_get_key_pair(signed_pre_key); - auto *pPubKey = ec_key_pair_get_public(pKeys); - preKey.pub.assign(pPubKey->data, sizeof(pPubKey->data)); - - auto *pPrivKey = ec_key_pair_get_private(pKeys); - preKey.priv.assign(pPrivKey->data, sizeof(pPrivKey->data)); - - preKey.signature.assign(session_signed_pre_key_get_signature(signed_pre_key), session_signed_pre_key_get_signature_len(signed_pre_key)); - preKey.keyid = session_signed_pre_key_get_id(signed_pre_key); - SIGNAL_UNREF(signed_pre_key); - - signedIdentity.pub = pProto->getBlob(DBKEY_SIGNED_IDENTITY_PUB); - signedIdentity.priv = pProto->getBlob(DBKEY_SIGNED_IDENTITY_PRIV); - - // create store with callbacks - signal_protocol_store_context_create(&m_pStore, m_pContext); - - signal_protocol_session_store ss; - ss.contains_session_func = &contains_session_func; - ss.delete_all_sessions_func = &delete_all_sessions_func; - ss.delete_session_func = &delete_session_func; - ss.destroy_func = &destroy_func; - ss.get_sub_device_sessions_func = &get_sub_device_sessions_func; - ss.load_session_func = &load_session_func; - ss.store_session_func = &store_session_func; - ss.user_data = this; - signal_protocol_store_context_set_session_store(m_pStore, &ss); - - signal_protocol_pre_key_store sp; - sp.contains_pre_key = &contains_pre_key; - sp.destroy_func = &destroy_func; - sp.load_pre_key = &load_pre_key; - sp.remove_pre_key = &remove_pre_key; - sp.store_pre_key = &store_pre_key; - sp.user_data = this; - signal_protocol_store_context_set_pre_key_store(m_pStore, &sp); - - signal_protocol_sender_key_store sk; - sk.destroy_func = destroy_func; - sk.load_sender_key = load_sender_key; - sk.store_sender_key = store_sender_key; - sk.user_data = this; - signal_protocol_store_context_set_sender_key_store(m_pStore, &sk); - - signal_protocol_signed_pre_key_store ssp; - ssp.contains_signed_pre_key = &contains_signed_pre_key; - ssp.destroy_func = &destroy_func; - ssp.load_signed_pre_key = &load_signed_pre_key; - ssp.remove_signed_pre_key = &remove_signed_pre_key; - ssp.store_signed_pre_key = &store_signed_pre_key; - ssp.user_data = this; - signal_protocol_store_context_set_signed_pre_key_store(m_pStore, &ssp); - - signal_protocol_identity_key_store sip; - sip.destroy_func = &destroy_func; - sip.get_identity_key_pair = &get_identity_key_pair; - sip.get_local_registration_id = &get_local_registration_id; - sip.is_trusted_identity = &is_trusted_identity; - sip.save_identity = &save_identity; - sip.user_data = this; - signal_protocol_store_context_set_identity_key_store(m_pStore, &sip); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// MSignalSession members - -MSignalSession::MSignalSession(const CMStringA &_1, int _2) : - szName(_1) -{ - address.name = szName.GetBuffer(); - address.name_len = szName.GetLength(); - address.device_id = _2; -} - -MSignalSession::~MSignalSession() -{ - session_cipher_free(cipher); -} - -bool MSignalSession::hasAddress(const char *name, size_t name_len) const -{ - if (address.name_len != name_len) - return false; - return memcmp(address.name, name, name_len) == 0; -} - -CMStringA MSignalSession::getSetting() const -{ - return CMStringA(FORMAT, "SignalSession_%s_%d", szName.c_str(), getDeviceId()); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MSignalSession* MSignalStore::createSession(const CMStringA &szName, int deviceId) -{ - signal_protocol_address tmp = {szName.c_str(), (unsigned)szName.GetLength(), deviceId}; - auto *pSession = getSession(&tmp); - if (pSession == nullptr) { - pSession = new MSignalSession(szName, deviceId); - arSessions.insert(pSession); - } - - if (pSession->cipher == nullptr) - logError( - session_cipher_create(&pSession->cipher, m_pStore, &pSession->address, m_pContext), - "session_cipher_create failure"); - - return pSession; -} - -MSignalSession* MSignalStore::getSession(const signal_protocol_address *address) -{ - MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id); - auto *pSession = arSessions.find(&tmp); - if (pSession == nullptr) { - MBinBuffer blob(pProto->getBlob(tmp.getSetting())); - if (blob.isEmpty()) - return nullptr; - - pSession = new MSignalSession(tmp); - pSession->sessionData.assign(blob.data(), blob.length()); - arSessions.insert(pSession); - } - - return pSession; -} - -void MSignalStore::importPublicKey(ec_public_key **result, MBinBuffer &buf) -{ - buf.appendBefore("\x05", 1); - curve_decode_point(result, buf.data(), buf.length(), m_pContext); -} - -void MSignalStore::injectSession(const char *szJid, const WANode *pNode, const WANode *pKey) -{ - WAJid jid(szJid); - auto *signedKey = pKey->getChild("skey"); - auto *key = pKey->getChild("key"); - auto *identity = pKey->getChild("identity"); - auto *registration = pNode->getChild("registration"); - if (!signedKey || !key || !identity || !registration) { - pProto->debugLogA("Bad key data for %s", jid.toString().c_str()); - return; - } - - signal_protocol_address address = {jid.user.c_str(), (unsigned)jid.user.GetLength(), jid.device}; - - session_builder *builder; - logError( - session_builder_create(&builder, m_pStore, &address, m_pContext), - "unable to create session cipher"); - - int regId = decodeBigEndian(registration->content); - int preKeyId = decodeBigEndian(key->getChild("id")->content); - int signedPreKeyId = decodeBigEndian(signedKey->getChild("id")->content); - - ec_public_key *preKeyPub, *signedPreKeyPub, *identityKey; - importPublicKey(&preKeyPub, key->getChild("value")->content); - importPublicKey(&identityKey, identity->content); - importPublicKey(&signedPreKeyPub, signedKey->getChild("value")->content); - - auto &sign = signedKey->getChild("signature")->content; - - session_pre_key_bundle *bundle; - logError( - session_pre_key_bundle_create(&bundle, regId, jid.device, preKeyId, preKeyPub, signedPreKeyId, signedPreKeyPub, sign.data(), sign.length(), identityKey), - "unable to create pre key bundle"); - - logError( - session_builder_process_pre_key_bundle(builder, bundle), - "unable to process pre key bundle"); - - session_pre_key_bundle_destroy((signal_type_base*)bundle); - session_builder_free(builder); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MBinBuffer MSignalStore::decryptSignalProto(const CMStringA &from, const char *pszType, const MBinBuffer &encrypted) -{ - WAJid jid(from); - auto *pSession = createSession(jid.user, jid.device); - - signal_buffer *result = nullptr; - if (!mir_strcmp(pszType, "pkmsg")) { - pre_key_signal_message *pMsg; - logError( - pre_key_signal_message_deserialize(&pMsg, encrypted.data(), encrypted.length(), m_pContext), - "unable to deserialize prekey message"); - - logError( - session_cipher_decrypt_pre_key_signal_message(pSession->getCipher(), pMsg, this, &result), - "unable to decrypt prekey message"); - - pre_key_signal_message_destroy((signal_type_base*)pMsg); - } - else { - signal_message *pMsg; - logError( - signal_message_deserialize(&pMsg, encrypted.data(), encrypted.length(), m_pContext), - "unable to deserialize signal message"); - - logError( - session_cipher_decrypt_signal_message(pSession->getCipher(), pMsg, this, &result), - "unable to decrypt signal message"); - - signal_message_destroy((signal_type_base *)pMsg); - } - - MBinBuffer res; - res.assign(result->data, result->len); - signal_buffer_free(result); - return res; -} - -MBinBuffer MSignalStore::decryptGroupSignalProto(const CMStringA &group, const CMStringA &sender, const MBinBuffer &encrypted) -{ - WAJid jid(sender); - signal_protocol_sender_key_name senderKeyName; - senderKeyName.group_id = group.c_str(); - senderKeyName.group_id_len = group.GetLength(); - senderKeyName.sender.device_id = 0; - senderKeyName.sender.name = jid.user.c_str(); - senderKeyName.sender.name_len = jid.user.GetLength(); - - group_cipher *cipher; - logError( - group_cipher_create(&cipher, m_pStore, &senderKeyName, m_pContext), - "unable to create group cipher"); - - sender_key_message *skmsg; - logError( - sender_key_message_deserialize(&skmsg, encrypted.data(), encrypted.length(), m_pContext), - "unable to deserialize skmsg"); - - signal_buffer *result = nullptr; - logError( - group_cipher_decrypt(cipher, skmsg, this, &result), - "unable to decrypt skmsg"); - - sender_key_message_destroy((signal_type_base *)skmsg); - group_cipher_free(cipher); - - MBinBuffer res; - res.assign(result->data, result->len); - signal_buffer_free(result); - return res; -} - -void MSignalStore::processSenderKeyMessage(const CMStringA &author, const Wa__Message__SenderKeyDistributionMessage *msg) -{ - WAJid jid(author); - signal_protocol_sender_key_name senderKeyName; - senderKeyName.group_id = msg->groupid; - senderKeyName.group_id_len = mir_strlen(msg->groupid); - senderKeyName.sender.device_id = 0; - senderKeyName.sender.name = jid.user.c_str(); - senderKeyName.sender.name_len = jid.user.GetLength(); - - group_session_builder *builder; - logError( - group_session_builder_create(&builder, m_pStore, m_pContext), - "unable to create session builder"); - - sender_key_distribution_message *skmsg; - logError( - sender_key_distribution_message_deserialize(&skmsg, msg->axolotlsenderkeydistributionmessage.data, msg->axolotlsenderkeydistributionmessage.len, m_pContext), - "unable to decode skd message"); - - logError( - group_session_builder_process_session(builder, &senderKeyName, skmsg), - "unable to process skd message"); - - sender_key_distribution_message_destroy((signal_type_base *)skmsg); - group_session_builder_free(builder); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// encryption - -MBinBuffer MSignalStore::encryptSenderKey(const WAJid &to, const CMStringA &from, const MBinBuffer &buf, MBinBuffer &skmsgKey) -{ - signal_protocol_sender_key_name senderKeyName; - senderKeyName.group_id = to.user.c_str(); - senderKeyName.group_id_len = to.user.GetLength(); - senderKeyName.sender.device_id = 0; - senderKeyName.sender.name = from.c_str(); - senderKeyName.sender.name_len = from.GetLength(); - - group_session_builder *builder; - logError( - group_session_builder_create(&builder, m_pStore, m_pContext), - "unable to create session builder"); - - sender_key_distribution_message *skmsg; - logError( - group_session_builder_create_session(builder, &skmsg, &senderKeyName), - "unable to create session"); - - group_cipher *cipher; - logError( - group_cipher_create(&cipher, m_pStore, &senderKeyName, m_pContext), - "unable to create group cipher"); - - ciphertext_message *encMessage; - logError( - group_cipher_encrypt(cipher, buf.data(), buf.length(), &encMessage), - "unable to encrypt group message"); - - MBinBuffer res; - auto *cipherText = ciphertext_message_get_serialized(encMessage); - res.assign(cipherText->data, cipherText->len); - - auto *pKey = sender_key_distribution_message_get_signature_key(skmsg); - skmsgKey.assign(pKey->data, sizeof(pKey->data)); - - sender_key_distribution_message_destroy((signal_type_base*)skmsg); - group_cipher_free(cipher); - group_session_builder_free(builder); - return res; -} - -MBinBuffer MSignalStore::encryptSignalProto(const WAJid &to, const MBinBuffer &buf, int &type) -{ - auto *pSession = createSession(to.user, to.device); - - ciphertext_message *pEncrypted; - logError( - session_cipher_encrypt(pSession->getCipher(), buf.data(), buf.length(), &pEncrypted), - "unable to encrypt signal message"); - - type = ciphertext_message_get_type(pEncrypted); - - MBinBuffer res; - auto *encBuf = ciphertext_message_get_serialized(pEncrypted); - res.assign(encBuf->data, encBuf->len); - SIGNAL_UNREF(pEncrypted); - return res; -} - -MBinBuffer MSignalStore::encodeSignedIdentity(bool bIncludeSignatureKey) -{ - proto::ADVSignedDeviceIdentity identity(pProto->getBlob("WAAccount")); - - if (!bIncludeSignatureKey) - proto::CleanBinary(identity->accountsignaturekey), identity->has_accountsignaturekey = false; - - return proto::Serialize(identity); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// generate and save pre keys set - -void MSignalStore::generatePrekeys(int count) -{ - int iNextKeyId = pProto->getDword(DBKEY_PREKEY_NEXT_ID, 1); - - CMStringA szSetting; - signal_protocol_key_helper_pre_key_list_node *keys_root; - signal_protocol_key_helper_generate_pre_keys(&keys_root, iNextKeyId, count, m_pContext); - for (auto *it = keys_root; it; it = signal_protocol_key_helper_key_list_next(it)) { - session_pre_key *pre_key = signal_protocol_key_helper_key_list_element(it); - uint32_t pre_key_id = session_pre_key_get_id(pre_key); - - SignalBuffer buf(pre_key); - szSetting.Format("PreKey%d", pre_key_id); - db_set_blob(0, pProto->m_szModuleName, szSetting, buf.data(), buf.len()); - - ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(pre_key); - auto *pPubKey = ec_key_pair_get_public(pre_key_pair); - szSetting.Format("PreKey%dPublic", pre_key_id); - db_set_blob(0, pProto->m_szModuleName, szSetting, pPubKey->data, sizeof(pPubKey->data)); - } - signal_protocol_key_helper_key_list_free(keys_root); - - pProto->setDword(DBKEY_PREKEY_NEXT_ID, iNextKeyId + count); -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MSignalStore members
+
+static int CompareSessions(const MSignalSession *p1, const MSignalSession *p2)
+{
+ if (int ret = mir_strcmp(p1->szName, p2->szName))
+ return ret;
+
+ return p1->getDeviceId() - p2->getDeviceId();
+}
+
+MSignalStore::MSignalStore(PROTO_INTERFACE *_1, const char *_2) :
+ pProto(_1),
+ prefix(_2),
+ arSessions(1, &CompareSessions)
+{
+ init();
+}
+
+MSignalStore::~MSignalStore()
+{
+ signal_protocol_store_context_destroy(m_pStore);
+ signal_context_destroy(m_pContext);
+}
+
+void MSignalStore::logError(int err, const char *pszMessage)
+{
+ if (err < 0) {
+ pProto->debugLogA("libsignal error %d", err);
+ throw pszMessage;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void log_func(int level, const char *pmsg, size_t /*msgLen*/, void *pUserData)
+{
+ auto *pStore = (MSignalStore *)pUserData;
+ pStore->pProto->debugLogA("libsignal {%d}: %s", level, pmsg);
+}
+
+static int hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *)
+{
+ HMAC_CTX *ctx = HMAC_CTX_new();
+ *hmac_context = ctx;
+ HMAC_Init(ctx, key, (int)key_len, EVP_sha256());
+ return 0;
+}
+
+static int hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *)
+{
+ return HMAC_Update((HMAC_CTX *)hmac_context, data, data_len);
+}
+
+static int hmac_sha256_final(void *hmac_context, signal_buffer **output, void *)
+{
+ BYTE data[200];
+ unsigned len = 0;
+ if (!HMAC_Final((HMAC_CTX *)hmac_context, data, &len))
+ return 1;
+
+ *output = signal_buffer_create(data, len);
+ return 0;
+}
+
+static void hmac_sha256_cleanup(void *hmac_context, void *)
+{
+ HMAC_CTX_free((HMAC_CTX *)hmac_context);
+}
+
+static int random_func(uint8_t *pData, size_t size, void *)
+{
+ Utils_GetRandom(pData, size);
+ return 0;
+}
+
+static int decrypt_func(signal_buffer **output,
+ int /*cipher*/,
+ const uint8_t *key, size_t /*key_len*/,
+ const uint8_t *iv, size_t /*iv_len*/,
+ const uint8_t *ciphertext, size_t ciphertext_len,
+ void * /*user_data*/)
+{
+ MBinBuffer res = aesDecrypt(EVP_aes_256_cbc(), key, iv, ciphertext, ciphertext_len);
+ *output = signal_buffer_create(res.data(), res.length());
+ return SG_SUCCESS;
+}
+
+static int encrypt_func(signal_buffer **output,
+ int /*cipher*/,
+ const uint8_t *key, size_t /*key_len*/,
+ const uint8_t *iv, size_t /*iv_len*/,
+ const uint8_t *ciphertext, size_t ciphertext_len,
+ void * /*user_data*/)
+{
+ MBinBuffer res = aesEncrypt(EVP_aes_256_cbc(), key, iv, ciphertext, ciphertext_len);
+ *output = signal_buffer_create(res.data(), res.length());
+ return SG_SUCCESS;
+}
+
+static int contains_session_func(const signal_protocol_address *address, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ auto *pSession = pStore->getSession(address);
+ return pSession != nullptr;
+}
+
+static int delete_all_sessions_func(const char *name, size_t name_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ auto &pList = pStore->arSessions;
+
+ int count = 0;
+ for (auto &it : pList.rev_iter()) {
+ if (it->hasAddress(name, name_len)) {
+ pStore->pProto->delSetting(it->getSetting());
+ pList.remove(pList.indexOf(&it));
+ count++;
+ }
+ }
+ return count;
+}
+
+int delete_session_func(const signal_protocol_address *address, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ auto &pList = pStore->arSessions;
+
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+ int idx = pList.getIndex(&tmp);
+ if (idx != -1) {
+ pStore->pProto->delSetting(tmp.getSetting());
+ pList.remove(idx);
+ }
+ return 0;
+}
+
+static int get_sub_device_sessions_func(signal_int_list **sessions, const char *name, size_t name_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ CMStringA szName(name, (int)name_len);
+
+ signal_int_list *l = signal_int_list_alloc();
+ unsigned int array_size = 0;
+
+ for (auto &it : pStore->arSessions)
+ if (it->szName == szName) {
+ array_size++;
+ signal_int_list_push_back(l, it->getDeviceId());
+ }
+
+ *sessions = l;
+ return array_size;
+}
+
+static void destroy_func(void *)
+{}
+
+int load_session_func(signal_buffer **record, signal_buffer **user_data_storage, const signal_protocol_address *address, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ auto *pSession = pStore->getSession(address);
+ if (pSession == nullptr)
+ return 0;
+
+ *record = signal_buffer_create(pSession->sessionData.data(), pSession->sessionData.length());
+ *user_data_storage = 0;
+ return 1;
+}
+
+static int store_session_func(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *, size_t, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+ auto *pSession = pStore->arSessions.find(&tmp);
+ if (pSession == nullptr) {
+ pSession = new MSignalSession(tmp);
+ pStore->arSessions.insert(pSession);
+ }
+
+ pSession->sessionData.assign(record, record_len);
+ db_set_blob(0, pStore->pProto->m_szModuleName, pSession->getSetting(), record, (unsigned)record_len);
+ return 0;
+}
+
+static int contains_pre_key(uint32_t pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id);
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ return (blob.data() != 0);
+}
+
+static int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id);
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ if (blob.isEmpty()) {
+ pStore->pProto->debugLogA("Prekey #%d not found", pre_key_id);
+ return SG_ERR_INVALID_KEY_ID;
+ }
+
+ *record = signal_buffer_create(blob.data(), blob.length());
+ return SG_SUCCESS; //key exists and succesfully loaded
+}
+
+static int remove_pre_key(uint32_t pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ pStore->pProto->debugLogA("Request to remove prekey #%d", pre_key_id);
+
+ /*
+ CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+
+ szSetting.Format("PreKey%uPublic", pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+
+ szSetting.Format("PreKey%uPrivate", pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+ */
+ return 0;
+}
+
+static int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "PreKey", pre_key_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned int)record_len);
+
+ session_pre_key *prekey = nullptr;
+ session_pre_key_deserialize(&prekey, record, record_len, pStore->CTX()); //TODO: handle error
+ if (prekey) {
+ ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(prekey);
+
+ SignalBuffer key_buf(ec_key_pair_get_public(pre_key_pair));
+ szSetting.Format("PreKey%uPublic", pre_key_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, key_buf.data(), key_buf.len());
+ }
+
+ return 0;
+}
+
+static int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id);
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ return blob.data() != 0;
+}
+
+static int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ if (signed_pre_key_id == 0)
+ signed_pre_key_id = 1;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id);
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ if (blob.isEmpty()) {
+ pStore->pProto->debugLogA("Signed prekey #%d not found", signed_pre_key_id);
+ return SG_ERR_INVALID_KEY_ID;
+ }
+
+ *record = signal_buffer_create(blob.data(), blob.length());
+ return SG_SUCCESS; //key exist and succesfully loaded
+}
+
+static int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned int)record_len);
+ return 0;
+}
+
+static int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignedPreKey", signed_pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+ return 0;
+}
+
+static int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ MBinBuffer buf;
+ buf.append(KEY_BUNDLE_TYPE, 1);
+ buf.append(pStore->signedIdentity.pub);
+ *public_data = signal_buffer_create(buf.data(), (int)buf.length());
+
+ *private_data = signal_buffer_create(pStore->signedIdentity.priv.data(), (int)pStore->signedIdentity.priv.length());
+ return 0;
+}
+
+static int get_local_registration_id(void *user_data, uint32_t *registration_id)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ *registration_id = pStore->pProto->getDword(DBKEY_REG_ID);
+ return 0;
+}
+
+static int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s_%s_%d", "SignalIdentity", CMStringA(address->name, (int)address->name_len).c_str(), address->device_id);
+ if (key_data != nullptr)
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, key_data, (unsigned int)key_len); //TODO: check return value
+ else
+ pStore->pProto->delSetting(szSetting);
+ return 0;
+}
+
+static int is_trusted_identity(const signal_protocol_address * /*address*/, uint8_t * /*key_data*/, size_t /*key_len*/, void * /*user_data*/)
+{
+ return 1;
+}
+
+static CMStringA get_sender_setting(const signal_protocol_sender_key_name *skn)
+{
+ WAJid jid(CMStringA(skn->sender.name, (int)skn->sender.name_len));
+ return CMStringA(FORMAT, "SenderKey_%*s_%s_%d", (unsigned)skn->group_id_len, skn->group_id, jid.user.c_str(), skn->sender.device_id);
+}
+
+static int load_sender_key(signal_buffer **record, signal_buffer **, const signal_protocol_sender_key_name *skn, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(get_sender_setting(skn));
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ if (blob.isEmpty())
+ return 0;
+
+ *record = signal_buffer_create(blob.data(), blob.length());
+ return 1;
+}
+
+static int store_sender_key(const signal_protocol_sender_key_name *skn, uint8_t *record, size_t record_len, uint8_t*, size_t, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(get_sender_setting(skn));
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned)record_len);
+ return 0;
+}
+
+void MSignalStore::init()
+{
+ signal_context_create(&m_pContext, this);
+ signal_context_set_log_function(m_pContext, log_func);
+
+ signal_crypto_provider prov;
+ memset(&prov, 0xFF, sizeof(prov));
+ prov.hmac_sha256_init_func = hmac_sha256_init;
+ prov.hmac_sha256_final_func = hmac_sha256_final;
+ prov.hmac_sha256_update_func = hmac_sha256_update;
+ prov.hmac_sha256_cleanup_func = hmac_sha256_cleanup;
+ prov.random_func = random_func;
+ prov.decrypt_func = decrypt_func;
+ prov.encrypt_func = encrypt_func;
+ signal_context_set_crypto_provider(m_pContext, &prov);
+
+ // read resident data from database
+ MBinBuffer blob(pProto->getBlob(DBKEY_PREKEY));
+ if (blob.isEmpty()) {
+ // nothing? generate signed identity keys (private & public)
+ ratchet_identity_key_pair *keyPair;
+ signal_protocol_key_helper_generate_identity_key_pair(&keyPair, m_pContext);
+
+ auto *pPubKey = ratchet_identity_key_pair_get_public(keyPair);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_SIGNED_IDENTITY_PUB, pPubKey->data, sizeof(pPubKey->data));
+
+ auto *pPrivKey = ratchet_identity_key_pair_get_private(keyPair);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_SIGNED_IDENTITY_PRIV, pPrivKey->data, sizeof(pPrivKey->data));
+
+ session_signed_pre_key *signed_pre_key;
+ signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, keyPair, 1, time(0), m_pContext);
+
+ SignalBuffer prekeyBuf(signed_pre_key);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_PREKEY, prekeyBuf.data(), prekeyBuf.len());
+ blob.assign(prekeyBuf.data(), prekeyBuf.len());
+
+ SIGNAL_UNREF(signed_pre_key);
+ SIGNAL_UNREF(keyPair);
+ }
+
+ session_signed_pre_key *signed_pre_key;
+ session_signed_pre_key_deserialize(&signed_pre_key, blob.data(), blob.length(), m_pContext);
+
+ ec_key_pair *pKeys = session_signed_pre_key_get_key_pair(signed_pre_key);
+ auto *pPubKey = ec_key_pair_get_public(pKeys);
+ preKey.pub.assign(pPubKey->data, sizeof(pPubKey->data));
+
+ auto *pPrivKey = ec_key_pair_get_private(pKeys);
+ preKey.priv.assign(pPrivKey->data, sizeof(pPrivKey->data));
+
+ preKey.signature.assign(session_signed_pre_key_get_signature(signed_pre_key), session_signed_pre_key_get_signature_len(signed_pre_key));
+ preKey.keyid = session_signed_pre_key_get_id(signed_pre_key);
+ SIGNAL_UNREF(signed_pre_key);
+
+ signedIdentity.pub = pProto->getBlob(DBKEY_SIGNED_IDENTITY_PUB);
+ signedIdentity.priv = pProto->getBlob(DBKEY_SIGNED_IDENTITY_PRIV);
+
+ // create store with callbacks
+ signal_protocol_store_context_create(&m_pStore, m_pContext);
+
+ signal_protocol_session_store ss;
+ ss.contains_session_func = &contains_session_func;
+ ss.delete_all_sessions_func = &delete_all_sessions_func;
+ ss.delete_session_func = &delete_session_func;
+ ss.destroy_func = &destroy_func;
+ ss.get_sub_device_sessions_func = &get_sub_device_sessions_func;
+ ss.load_session_func = &load_session_func;
+ ss.store_session_func = &store_session_func;
+ ss.user_data = this;
+ signal_protocol_store_context_set_session_store(m_pStore, &ss);
+
+ signal_protocol_pre_key_store sp;
+ sp.contains_pre_key = &contains_pre_key;
+ sp.destroy_func = &destroy_func;
+ sp.load_pre_key = &load_pre_key;
+ sp.remove_pre_key = &remove_pre_key;
+ sp.store_pre_key = &store_pre_key;
+ sp.user_data = this;
+ signal_protocol_store_context_set_pre_key_store(m_pStore, &sp);
+
+ signal_protocol_sender_key_store sk;
+ sk.destroy_func = destroy_func;
+ sk.load_sender_key = load_sender_key;
+ sk.store_sender_key = store_sender_key;
+ sk.user_data = this;
+ signal_protocol_store_context_set_sender_key_store(m_pStore, &sk);
+
+ signal_protocol_signed_pre_key_store ssp;
+ ssp.contains_signed_pre_key = &contains_signed_pre_key;
+ ssp.destroy_func = &destroy_func;
+ ssp.load_signed_pre_key = &load_signed_pre_key;
+ ssp.remove_signed_pre_key = &remove_signed_pre_key;
+ ssp.store_signed_pre_key = &store_signed_pre_key;
+ ssp.user_data = this;
+ signal_protocol_store_context_set_signed_pre_key_store(m_pStore, &ssp);
+
+ signal_protocol_identity_key_store sip;
+ sip.destroy_func = &destroy_func;
+ sip.get_identity_key_pair = &get_identity_key_pair;
+ sip.get_local_registration_id = &get_local_registration_id;
+ sip.is_trusted_identity = &is_trusted_identity;
+ sip.save_identity = &save_identity;
+ sip.user_data = this;
+ signal_protocol_store_context_set_identity_key_store(m_pStore, &sip);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MSignalSession members
+
+MSignalSession::MSignalSession(const CMStringA &_1, int _2) :
+ szName(_1)
+{
+ address.name = szName.GetBuffer();
+ address.name_len = szName.GetLength();
+ address.device_id = _2;
+}
+
+MSignalSession::~MSignalSession()
+{
+ session_cipher_free(cipher);
+}
+
+bool MSignalSession::hasAddress(const char *name, size_t name_len) const
+{
+ if (address.name_len != name_len)
+ return false;
+ return memcmp(address.name, name, name_len) == 0;
+}
+
+CMStringA MSignalSession::getSetting() const
+{
+ return CMStringA(FORMAT, "SignalSession_%s_%d", szName.c_str(), getDeviceId());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MSignalSession* MSignalStore::createSession(const CMStringA &szName, int deviceId)
+{
+ signal_protocol_address tmp = {szName.c_str(), (unsigned)szName.GetLength(), deviceId};
+ auto *pSession = getSession(&tmp);
+ if (pSession == nullptr) {
+ pSession = new MSignalSession(szName, deviceId);
+ arSessions.insert(pSession);
+ }
+
+ if (pSession->cipher == nullptr)
+ logError(
+ session_cipher_create(&pSession->cipher, m_pStore, &pSession->address, m_pContext),
+ "session_cipher_create failure");
+
+ return pSession;
+}
+
+MSignalSession* MSignalStore::getSession(const signal_protocol_address *address)
+{
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+ auto *pSession = arSessions.find(&tmp);
+ if (pSession == nullptr) {
+ MBinBuffer blob(pProto->getBlob(tmp.getSetting()));
+ if (blob.isEmpty())
+ return nullptr;
+
+ pSession = new MSignalSession(tmp);
+ pSession->sessionData.assign(blob.data(), blob.length());
+ arSessions.insert(pSession);
+ }
+
+ return pSession;
+}
+
+void MSignalStore::importPublicKey(ec_public_key **result, MBinBuffer &buf)
+{
+ buf.appendBefore("\x05", 1);
+ curve_decode_point(result, buf.data(), buf.length(), m_pContext);
+}
+
+void MSignalStore::injectSession(const char *szJid, const WANode *pNode, const WANode *pKey)
+{
+ WAJid jid(szJid);
+ auto *signedKey = pKey->getChild("skey");
+ auto *key = pKey->getChild("key");
+ auto *identity = pKey->getChild("identity");
+ auto *registration = pNode->getChild("registration");
+ if (!signedKey || !key || !identity || !registration) {
+ pProto->debugLogA("Bad key data for %s", jid.toString().c_str());
+ return;
+ }
+
+ signal_protocol_address address = {jid.user.c_str(), (unsigned)jid.user.GetLength(), jid.device};
+
+ session_builder *builder;
+ logError(
+ session_builder_create(&builder, m_pStore, &address, m_pContext),
+ "unable to create session cipher");
+
+ int regId = decodeBigEndian(registration->content);
+ int preKeyId = decodeBigEndian(key->getChild("id")->content);
+ int signedPreKeyId = decodeBigEndian(signedKey->getChild("id")->content);
+
+ ec_public_key *preKeyPub, *signedPreKeyPub, *identityKey;
+ importPublicKey(&preKeyPub, key->getChild("value")->content);
+ importPublicKey(&identityKey, identity->content);
+ importPublicKey(&signedPreKeyPub, signedKey->getChild("value")->content);
+
+ auto &sign = signedKey->getChild("signature")->content;
+
+ session_pre_key_bundle *bundle;
+ logError(
+ session_pre_key_bundle_create(&bundle, regId, jid.device, preKeyId, preKeyPub, signedPreKeyId, signedPreKeyPub, sign.data(), sign.length(), identityKey),
+ "unable to create pre key bundle");
+
+ logError(
+ session_builder_process_pre_key_bundle(builder, bundle),
+ "unable to process pre key bundle");
+
+ session_pre_key_bundle_destroy((signal_type_base*)bundle);
+ session_builder_free(builder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MBinBuffer MSignalStore::decryptSignalProto(const CMStringA &from, const char *pszType, const MBinBuffer &encrypted)
+{
+ WAJid jid(from);
+ auto *pSession = createSession(jid.user, jid.device);
+
+ signal_buffer *result = nullptr;
+ if (!mir_strcmp(pszType, "pkmsg")) {
+ pre_key_signal_message *pMsg;
+ logError(
+ pre_key_signal_message_deserialize(&pMsg, encrypted.data(), encrypted.length(), m_pContext),
+ "unable to deserialize prekey message");
+
+ logError(
+ session_cipher_decrypt_pre_key_signal_message(pSession->getCipher(), pMsg, this, &result),
+ "unable to decrypt prekey message");
+
+ pre_key_signal_message_destroy((signal_type_base*)pMsg);
+ }
+ else {
+ signal_message *pMsg;
+ logError(
+ signal_message_deserialize(&pMsg, encrypted.data(), encrypted.length(), m_pContext),
+ "unable to deserialize signal message");
+
+ logError(
+ session_cipher_decrypt_signal_message(pSession->getCipher(), pMsg, this, &result),
+ "unable to decrypt signal message");
+
+ signal_message_destroy((signal_type_base *)pMsg);
+ }
+
+ MBinBuffer res;
+ res.assign(result->data, result->len);
+ signal_buffer_free(result);
+ return res;
+}
+
+MBinBuffer MSignalStore::decryptGroupSignalProto(const CMStringA &group, const CMStringA &sender, const MBinBuffer &encrypted)
+{
+ WAJid jid(sender);
+ signal_protocol_sender_key_name senderKeyName;
+ senderKeyName.group_id = group.c_str();
+ senderKeyName.group_id_len = group.GetLength();
+ senderKeyName.sender.device_id = 0;
+ senderKeyName.sender.name = jid.user.c_str();
+ senderKeyName.sender.name_len = jid.user.GetLength();
+
+ group_cipher *cipher;
+ logError(
+ group_cipher_create(&cipher, m_pStore, &senderKeyName, m_pContext),
+ "unable to create group cipher");
+
+ sender_key_message *skmsg;
+ logError(
+ sender_key_message_deserialize(&skmsg, encrypted.data(), encrypted.length(), m_pContext),
+ "unable to deserialize skmsg");
+
+ signal_buffer *result = nullptr;
+ logError(
+ group_cipher_decrypt(cipher, skmsg, this, &result),
+ "unable to decrypt skmsg");
+
+ sender_key_message_destroy((signal_type_base *)skmsg);
+ group_cipher_free(cipher);
+
+ MBinBuffer res;
+ res.assign(result->data, result->len);
+ signal_buffer_free(result);
+ return res;
+}
+
+void MSignalStore::processSenderKeyMessage(const CMStringA &author, const Wa__Message__SenderKeyDistributionMessage *msg)
+{
+ WAJid jid(author);
+ signal_protocol_sender_key_name senderKeyName;
+ senderKeyName.group_id = msg->groupid;
+ senderKeyName.group_id_len = mir_strlen(msg->groupid);
+ senderKeyName.sender.device_id = 0;
+ senderKeyName.sender.name = jid.user.c_str();
+ senderKeyName.sender.name_len = jid.user.GetLength();
+
+ group_session_builder *builder;
+ logError(
+ group_session_builder_create(&builder, m_pStore, m_pContext),
+ "unable to create session builder");
+
+ sender_key_distribution_message *skmsg;
+ logError(
+ sender_key_distribution_message_deserialize(&skmsg, msg->axolotlsenderkeydistributionmessage.data, msg->axolotlsenderkeydistributionmessage.len, m_pContext),
+ "unable to decode skd message");
+
+ logError(
+ group_session_builder_process_session(builder, &senderKeyName, skmsg),
+ "unable to process skd message");
+
+ sender_key_distribution_message_destroy((signal_type_base *)skmsg);
+ group_session_builder_free(builder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// encryption
+
+MBinBuffer MSignalStore::encryptSenderKey(const WAJid &to, const CMStringA &from, const MBinBuffer &buf, MBinBuffer &skmsgKey)
+{
+ signal_protocol_sender_key_name senderKeyName;
+ senderKeyName.group_id = to.user.c_str();
+ senderKeyName.group_id_len = to.user.GetLength();
+ senderKeyName.sender.device_id = 0;
+ senderKeyName.sender.name = from.c_str();
+ senderKeyName.sender.name_len = from.GetLength();
+
+ group_session_builder *builder;
+ logError(
+ group_session_builder_create(&builder, m_pStore, m_pContext),
+ "unable to create session builder");
+
+ sender_key_distribution_message *skmsg;
+ logError(
+ group_session_builder_create_session(builder, &skmsg, &senderKeyName),
+ "unable to create session");
+
+ group_cipher *cipher;
+ logError(
+ group_cipher_create(&cipher, m_pStore, &senderKeyName, m_pContext),
+ "unable to create group cipher");
+
+ ciphertext_message *encMessage;
+ logError(
+ group_cipher_encrypt(cipher, buf.data(), buf.length(), &encMessage),
+ "unable to encrypt group message");
+
+ MBinBuffer res;
+ auto *cipherText = ciphertext_message_get_serialized(encMessage);
+ res.assign(cipherText->data, cipherText->len);
+
+ auto *pKey = sender_key_distribution_message_get_signature_key(skmsg);
+ skmsgKey.assign(pKey->data, sizeof(pKey->data));
+
+ sender_key_distribution_message_destroy((signal_type_base*)skmsg);
+ group_cipher_free(cipher);
+ group_session_builder_free(builder);
+ return res;
+}
+
+MBinBuffer MSignalStore::encryptSignalProto(const WAJid &to, const MBinBuffer &buf, int &type)
+{
+ auto *pSession = createSession(to.user, to.device);
+
+ ciphertext_message *pEncrypted;
+ logError(
+ session_cipher_encrypt(pSession->getCipher(), buf.data(), buf.length(), &pEncrypted),
+ "unable to encrypt signal message");
+
+ type = ciphertext_message_get_type(pEncrypted);
+
+ MBinBuffer res;
+ auto *encBuf = ciphertext_message_get_serialized(pEncrypted);
+ res.assign(encBuf->data, encBuf->len);
+ SIGNAL_UNREF(pEncrypted);
+ return res;
+}
+
+MBinBuffer MSignalStore::encodeSignedIdentity(bool bIncludeSignatureKey)
+{
+ proto::ADVSignedDeviceIdentity identity(pProto->getBlob("WAAccount"));
+
+ if (!bIncludeSignatureKey)
+ proto::CleanBinary(identity->accountsignaturekey), identity->has_accountsignaturekey = false;
+
+ return proto::Serialize(identity);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// generate and save pre keys set
+
+void MSignalStore::generatePrekeys(int count)
+{
+ int iNextKeyId = pProto->getDword(DBKEY_PREKEY_NEXT_ID, 1);
+
+ CMStringA szSetting;
+ signal_protocol_key_helper_pre_key_list_node *keys_root;
+ signal_protocol_key_helper_generate_pre_keys(&keys_root, iNextKeyId, count, m_pContext);
+ for (auto *it = keys_root; it; it = signal_protocol_key_helper_key_list_next(it)) {
+ session_pre_key *pre_key = signal_protocol_key_helper_key_list_element(it);
+ uint32_t pre_key_id = session_pre_key_get_id(pre_key);
+
+ SignalBuffer buf(pre_key);
+ szSetting.Format("PreKey%d", pre_key_id);
+ db_set_blob(0, pProto->m_szModuleName, szSetting, buf.data(), buf.len());
+
+ ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(pre_key);
+ auto *pPubKey = ec_key_pair_get_public(pre_key_pair);
+ szSetting.Format("PreKey%dPublic", pre_key_id);
+ db_set_blob(0, pProto->m_szModuleName, szSetting, pPubKey->data, sizeof(pPubKey->data));
+ }
+ signal_protocol_key_helper_key_list_free(keys_root);
+
+ pProto->setDword(DBKEY_PREKEY_NEXT_ID, iNextKeyId + count);
+}
diff --git a/protocols/WhatsApp/src/stdafx.cxx b/protocols/WhatsApp/src/stdafx.cxx index 7942e82b45..5146fe9a0a 100644 --- a/protocols/WhatsApp/src/stdafx.cxx +++ b/protocols/WhatsApp/src/stdafx.cxx @@ -1,8 +1,8 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
diff --git a/protocols/WhatsApp/src/stdafx.h b/protocols/WhatsApp/src/stdafx.h index 945e385af2..3b1326e277 100644 --- a/protocols/WhatsApp/src/stdafx.h +++ b/protocols/WhatsApp/src/stdafx.h @@ -1,72 +1,72 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#pragma once -#pragma warning(disable:4996 4290 4200 4239 4324) - -#include <fcntl.h> -#include <malloc.h> -#include <io.h> -#include <time.h> -#include <windows.h> - -#include <list> -#include <map> -#include <memory> -#include <string> - -#include <newpluginapi.h> -#include <m_avatars.h> -#include <m_chat_int.h> -#include <m_clist.h> -#include <m_contacts.h> -#include <m_database.h> -#include <m_history.h> -#include <m_imgsrvc.h> -#include <m_ignore.h> -#include <m_json.h> -#include <m_langpack.h> -#include <m_message.h> -#include <m_netlib.h> -#include <m_options.h> -#include <m_popup.h> -#include <m_protocols.h> -#include <m_protosvc.h> -#include <m_protoint.h> -#include <m_skin.h> -#include <m_string.h> -#include <statusmodes.h> -#include <m_userinfo.h> -#include <m_icolib.h> -#include <m_utils.h> -#include <m_xml.h> -#include <m_hotkeys.h> -#include <m_folders.h> -#include <m_json.h> -#include <m_gui.h> -#include <m_messagestate.h> - -#include <openssl/evp.h> -#include <openssl/hmac.h> -#include <openssl/sha.h> -#include <openssl/kdf.h> - -#include "../../libs/libqrencode/src/qrencode.h" -#include "../../libs/zlib/src/zlib.h" - -#include "../../utils/mir_signal.h" - -#include "pmsg.proto.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -#include "db.h" -#include "utils.h" -#include "proto.h" -#include "resource.h" - -#pragma comment(lib, "libcrypto.lib") +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#pragma once
+#pragma warning(disable:4996 4290 4200 4239 4324)
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <io.h>
+#include <time.h>
+#include <windows.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <newpluginapi.h>
+#include <m_avatars.h>
+#include <m_chat_int.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_history.h>
+#include <m_imgsrvc.h>
+#include <m_ignore.h>
+#include <m_json.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_skin.h>
+#include <m_string.h>
+#include <statusmodes.h>
+#include <m_userinfo.h>
+#include <m_icolib.h>
+#include <m_utils.h>
+#include <m_xml.h>
+#include <m_hotkeys.h>
+#include <m_folders.h>
+#include <m_json.h>
+#include <m_gui.h>
+#include <m_messagestate.h>
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+#include <openssl/kdf.h>
+
+#include "../../libs/libqrencode/src/qrencode.h"
+#include "../../libs/zlib/src/zlib.h"
+
+#include "../../utils/mir_signal.h"
+
+#include "pmsg.proto.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include "db.h"
+#include "utils.h"
+#include "proto.h"
+#include "resource.h"
+
+#pragma comment(lib, "libcrypto.lib")
diff --git a/protocols/WhatsApp/src/utils.cpp b/protocols/WhatsApp/src/utils.cpp index 05e9d2c3bc..1e2d39f553 100644 --- a/protocols/WhatsApp/src/utils.cpp +++ b/protocols/WhatsApp/src/utils.cpp @@ -1,589 +1,589 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" - -WAJid::WAJid(const char *pszUser, const char *pszServer, int iDevice, int iAgent) : - user(pszUser ? pszUser : ""), - server(pszServer ? pszServer : ""), - device(iDevice), - agent(iAgent) -{} - -WAJid::WAJid(const char *pszJid, int _id) -{ - if (pszJid == nullptr) - pszJid = ""; - - auto *tmp = NEWSTR_ALLOCA(pszJid); - auto *p = strrchr(tmp, '@'); - if (p) { - *p = 0; - server = p + 1; - } - - if (p = strrchr(tmp, ':')) { - *p = 0; - device = atoi(p + 1); - } - else device = _id; - - if (p = strrchr(tmp, '_')) { - *p = 0; - agent = atoi(p + 1); - } - else agent = 0; - - user = tmp; -} - -bool WAJid::isUser() const -{ return server == "s.whatsapp.net"; -} - -bool WAJid::isGroup() const -{ return server == "g.us"; -} - -bool WAJid::isBroadcast() const -{ - return server == "broadcast"; -} - -bool WAJid::isStatusBroadcast() const -{ - return isBroadcast() && user == "status"; -} - -CMStringA WAJid::toString() const -{ - CMStringA ret(user); - if (agent > 0) - ret.AppendFormat("_%d", agent); - if (device > 0) - ret.AppendFormat(":%d", device); - ret.AppendFormat("@%s", server.c_str()); - return ret; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static uint8_t sttLtHashInfo[] = "WhatsApp Patch Integrity"; - -void LT_HASH::add(const void *pData, size_t len) -{ - uint16_t tmp[_countof(hash)]; - HKDF(EVP_sha256(), (BYTE *)"", 0, (BYTE*)pData, len, sttLtHashInfo, sizeof(sttLtHashInfo) - 1, (BYTE *)tmp, sizeof(tmp)); - - for (int i = 0; i < _countof(hash); i++) - hash[i] += tmp[i]; -} - -void LT_HASH::sub(const void *pData, size_t len) -{ - uint16_t tmp[_countof(hash)]; - HKDF(EVP_sha256(), (BYTE *)"", 0, (BYTE *)pData, len, sttLtHashInfo, sizeof(sttLtHashInfo) - 1, (BYTE *)tmp, sizeof(tmp)); - - for (int i = 0; i < _countof(hash); i++) - hash[i] -= tmp[i]; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -WAUser* WhatsAppProto::FindUser(const char *szId) -{ - if (szId == nullptr) - return nullptr; - - mir_cslock lck(m_csUsers); - auto *tmp = (WAUser *)_alloca(sizeof(WAUser)); - tmp->szId = (char*)szId; - return m_arUsers.find(tmp); -} - -WAUser* WhatsAppProto::AddUser(const char *szId, bool bTemporary) -{ - auto *pUser = FindUser(szId); - if (pUser != nullptr) - return pUser; - - MCONTACT hContact = db_add_contact(); - Proto_AddToContact(hContact, m_szModuleName); - - pUser = new WAUser(hContact, mir_strdup(szId)); - pUser->bIsGroupChat = WAJid(szId).isGroup(); - - if (pUser->bIsGroupChat) { - setByte(hContact, "ChatRoom", 1); - setString(hContact, "ChatRoomID", szId); - } - else { - setString(hContact, DBKEY_ID, szId); - if (m_wszDefaultGroup) - Clist_SetGroup(hContact, m_wszDefaultGroup); - } - - if (bTemporary) - Contact::RemoveFromList(hContact); - - mir_cslock lck(m_csUsers); - m_arUsers.insert(pUser); - return pUser; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -WA_PKT_HANDLER WhatsAppProto::FindPersistentHandler(const WANode &pNode) -{ - auto *pChild = pNode.getFirstChild(); - CMStringA szChild = (pChild) ? pChild->title : ""; - CMStringA szTitle = pNode.title; - CMStringA szType = pNode.getAttr("type"); - CMStringA szXmlns = pNode.getAttr("xmlns"); - - for (auto &it : m_arPersistent) { - if (it->pszTitle && szTitle != it->pszTitle) - continue; - if (it->pszType && szType != it->pszType) - continue; - if (it->pszXmlns && szXmlns != it->pszXmlns) - continue; - if (it->pszChild && szChild != it->pszChild) - continue; - return it->pHandler; - } - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CMStringA WhatsAppProto::GenerateMessageId() -{ - return CMStringA(FORMAT, "%d.%d-%d", m_wMsgPrefix[0], m_wMsgPrefix[1], m_iPacketId++); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// sends a piece of JSON to a server via a websocket, masked - -int WhatsAppProto::WSSend(const ProtobufCMessage &msg) -{ - if (m_hServerConn == nullptr) - return -1; - - MBinBuffer buf(proto::Serialize(&msg)); - Netlib_Dump(m_hServerConn, buf.data(), buf.length(), true, 0); - - MBinBuffer payload = m_noise->encodeFrame(buf.data(), buf.length()); - WebSocket_SendBinary(m_hServerConn, payload.data(), payload.length()); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int WhatsAppProto::WSSendNode(WANode &node) -{ - if (m_hServerConn == nullptr) - return 0; - - CMStringA szText; - node.print(szText); - debugLogA("Sending binary node:\n%s", szText.c_str()); - - WAWriter writer; - writer.writeNode(&node); - - MBinBuffer encData = m_noise->encrypt(writer.body.data(), writer.body.length()); - MBinBuffer payload = m_noise->encodeFrame(encData.data(), encData.length()); - WebSocket_SendBinary(m_hServerConn, payload.data(), payload.length()); - return 1; -} - -int WhatsAppProto::WSSendNode(WANode &node, WA_PKT_HANDLER pHandler) -{ - if (m_hServerConn == nullptr) - return 0; - - CMStringA id(GenerateMessageId()); - node.addAttr("id", id); - { - mir_cslock lck(m_csPacketQueue); - m_arPacketQueue.insert(new WARequestSimple(id, pHandler)); - } - - return WSSendNode(node); -} - -int WhatsAppProto::WSSendNode(WANode &node, WA_PKT_HANDLER_FULL pHandler, void *pUserInfo) -{ - if (m_hServerConn == nullptr) - return 0; - - CMStringA id(GenerateMessageId()); - node.addAttr("id", id); - { - mir_cslock lck(m_csPacketQueue); - m_arPacketQueue.insert(new WARequestParam(id, pHandler, pUserInfo)); - } - - return WSSendNode(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -std::string decodeBinStr(const std::string &buf) -{ - size_t cbLen; - void *pData = mir_base64_decode(buf.c_str(), &cbLen); - if (pData == nullptr) - return ""; - - std::string res((char *)pData, cbLen); - mir_free(pData); - return res; -} - -MBinBuffer decodeBufStr(const std::string &buf) -{ - MBinBuffer res; - size_t cbLen; - void *pData = mir_base64_decode(buf.c_str(), &cbLen); - if (pData == nullptr) - return res; - - res.assign(pData, cbLen); - mir_free(pData); - return res; -} - -uint32_t decodeBigEndian(const uint8_t *buf, size_t len) -{ - uint32_t ret = 0; - for (int i = 0; i < len; i++) { - ret <<= 8; - ret += buf[i]; - } - - return ret; -} - -std::string encodeBigEndian(uint32_t num, size_t len) -{ - std::string res; - for (int i = 0; i < len; i++) { - char c = num & 0xFF; - res = c + res; - num >>= 8; - } - return res; -} - -void generateIV(uint8_t *iv, uint32_t &pVar) -{ - auto counter = encodeBigEndian(pVar); - memset(iv, 0, 8); - memcpy(iv + 8, counter.c_str(), sizeof(uint32_t)); - - pVar++; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Padding - -void padBuffer16(MBinBuffer &buf) -{ - uint8_t c = 16 - buf.length() % 16; - - for (uint8_t i = 0; i < c; i++) - buf.append(&c, 1); -} - -MBinBuffer unpadBuffer16(const MBinBuffer &buf) -{ - size_t len = buf.length(); - auto p = buf.data() + len - 1; - if (*p <= 0x10) { - MBinBuffer res; - res.assign(buf.data(), len - *p); - return res; - } - - return buf; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popups - -void WhatsAppProto::InitPopups(void) -{ - g_plugin.addPopupOption(CMStringW(FORMAT, TranslateT("%s error notifications"), m_tszUserName), m_bUsePopups); - - char name[256]; - mir_snprintf(name, "%s_%s", m_szModuleName, "Error"); - - wchar_t desc[256]; - mir_snwprintf(desc, L"%s/%s", m_tszUserName, TranslateT("Errors")); - - POPUPCLASS ppc = {}; - ppc.flags = PCF_UNICODE; - ppc.pszName = name; - ppc.pszDescription.w = desc; - ppc.hIcon = IcoLib_GetIconByHandle(m_hProtoIcon); - ppc.colorBack = RGB(191, 0, 0); //Red - ppc.colorText = RGB(255, 245, 225); //Yellow - ppc.iSeconds = 60; - m_hPopupClass = Popup_RegisterClass(&ppc); - - IcoLib_ReleaseIcon(ppc.hIcon); -} - -void WhatsAppProto::Popup(MCONTACT hContact, const wchar_t *szMsg, const wchar_t *szTitle) -{ - if (!m_bUsePopups) - return; - - char name[256]; - mir_snprintf(name, "%s_%s", m_szModuleName, "Error"); - - CMStringW wszTitle(szTitle); - if (hContact == 0) { - wszTitle.Insert(0, L": "); - wszTitle.Insert(0, m_tszUserName); - } - - POPUPDATACLASS ppd = {}; - ppd.szTitle.w = wszTitle; - ppd.szText.w = szMsg; - ppd.pszClassName = name; - ppd.hContact = hContact; - Popup_AddClass(&ppd); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MBinBuffer WhatsAppProto::unzip(const MBinBuffer &src) -{ - z_stream strm = {}; - inflateInit(&strm); - - strm.avail_in = (uInt)src.length(); - strm.next_in = (Bytef *)src.data(); - - MBinBuffer res; - Bytef buf[2048]; - - while (strm.avail_in > 0) { - strm.avail_out = sizeof(buf); - strm.next_out = buf; - - int ret = inflate(&strm, Z_NO_FLUSH); - switch (ret) { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; - __fallthrough; - - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&strm); - return res; - } - - res.append(buf, sizeof(buf) - strm.avail_out); - if (ret == Z_STREAM_END) - break; - } - - inflateEnd(&strm); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void bin2file(const MBinBuffer &buf, const wchar_t *pwszFileName) -{ - int fileId = _wopen(pwszFileName, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE); - if (fileId != -1) { - write(fileId, buf.data(), (unsigned)buf.length()); - close(fileId); - } -} - -void string2file(const std::string &str, const wchar_t *pwszFileName) -{ - int fileId = _wopen(pwszFileName, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE); - if (fileId != -1) { - write(fileId, str.c_str(), (unsigned)str.size()); - close(fileId); - } -} - -CMStringA file2string(const wchar_t *pwszFileName) -{ - CMStringA res; - - int fileId = _wopen(pwszFileName, _O_RDONLY | _O_BINARY, _S_IREAD | _S_IWRITE); - if (fileId != -1) { - res.Truncate(filelength(fileId)); - read(fileId, res.GetBuffer(), res.GetLength()); - close(fileId); - } - return res; -} - -void WhatsAppProto::GetMessageContent( - CMStringA &txt, - const char *szType, - const char *szMimeType, - const char *szUrl, - const char *szDirectPath, - const ProtobufCBinaryData &pMediaKey, - const char *szCaption) -{ - if (szCaption) { - if (m_bUseBbcodes) - txt.Append("[b]"); - txt.Append(szCaption); - if (m_bUseBbcodes) - txt.Append("[/b]"); - txt.Append("\n"); - } - - CMStringA url = szUrl; - int idx = url.ReverseFind('/'); - if (idx != -1) - url.Delete(0, idx+1); - idx = url.ReverseFind('.'); - if (idx != -1) - url.Truncate(idx); - if (szMimeType) - url.Append(_T2A(ProtoGetAvatarExtension(ProtoGetAvatarFormatByMimeType(szMimeType)))); - - char *szMediaType = NEWSTR_ALLOCA(szType); - szMediaType[0] = toupper(szMediaType[0]); - - MBinBuffer buf = DownloadEncryptedFile(directPath2url(szDirectPath), pMediaKey, szMediaType); - if (!buf.isEmpty()) { - CMStringW pwszFileName(GetTmpFileName(szType, url)); - bin2file(buf, pwszFileName); - - pwszFileName.Replace(L"\\", L"/"); - txt.AppendFormat("file://%s", T2Utf(pwszFileName).get()); - } -} - -CMStringA WhatsAppProto::GetMessageText(const Wa__Message *pMessage) -{ - CMStringA szMessageText; - - if (pMessage) { - if (auto *pExt = pMessage->extendedtextmessage) { - if (pExt->title) { - if (m_bUseBbcodes) - szMessageText.Append("[b]"); - szMessageText.Append(pExt->title); - if (m_bUseBbcodes) - szMessageText.Append("[/b]"); - szMessageText.Append("\n"); - } - - if (pExt->contextinfo && pExt->contextinfo->quotedmessage) - szMessageText.AppendFormat("> %s\n\n", pExt->contextinfo->quotedmessage->conversation); - - if (pExt->text) - szMessageText.Append(pExt->text); - } - else if (auto *pAudio = pMessage->audiomessage) { - GetMessageContent(szMessageText, "audio", pAudio->url, pAudio->directpath, pAudio->mimetype, pAudio->mediakey); - } - else if (auto *pVideo = pMessage->videomessage) { - GetMessageContent(szMessageText, "video", pVideo->url, pVideo->directpath, pVideo->mimetype, pVideo->mediakey, pVideo->caption); - } - else if (auto *pImage = pMessage->imagemessage) { - GetMessageContent(szMessageText, "image", pImage->url, pImage->directpath, pImage->mimetype, pImage->mediakey, pImage->caption); - } - else if (mir_strlen(pMessage->conversation)) - szMessageText = pMessage->conversation; - } - - return szMessageText; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void proto::CleanBinary(ProtobufCBinaryData &field) -{ - if (field.data) { - free(field.data); - field.data = nullptr; - } - field.len = 0; -} - -ProtobufCBinaryData proto::SetBinary(const void *pData, size_t len) -{ - ProtobufCBinaryData res; - if (pData == nullptr) { - res.data = nullptr; - res.len = 0; - } - else { - res.data = (uint8_t *)malloc(res.len = len); - memcpy(res.data, pData, len); - } - return res; -} - -MBinBuffer proto::Serialize(const ProtobufCMessage *msg) -{ - MBinBuffer res(protobuf_c_message_get_packed_size(msg)); - protobuf_c_message_pack(msg, res.data()); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CMStringA directPath2url(const char *pszDirectPath) -{ - return CMStringA("https://mmg.whatsapp.net") + pszDirectPath; -} - -WAMediaKeys::WAMediaKeys(const uint8_t *pKey, size_t keyLen, const char *pszMediaType) -{ - CMStringA pszHkdfString(FORMAT, "WhatsApp %s Keys", pszMediaType); - - HKDF(EVP_sha256(), (BYTE *)"", 0, pKey, (int)keyLen, (BYTE *)pszHkdfString.c_str(), pszHkdfString.GetLength(), (BYTE *)this, sizeof(*this)); -} - -MBinBuffer WhatsAppProto::DownloadEncryptedFile(const char *url, const ProtobufCBinaryData &mediaKeys, const char *pszMediaType) -{ - NETLIBHTTPHEADER headers[1] = {{"Origin", "https://web.whatsapp.com"}}; - - NETLIBHTTPREQUEST req = {}; - req.cbSize = sizeof(req); - req.requestType = REQUEST_GET; - req.szUrl = (char*)url; - req.headersCount = _countof(headers); - req.headers = headers; - - MBinBuffer ret; - auto *pResp = Netlib_HttpTransaction(m_hNetlibUser, &req); - if (pResp) { - if (pResp->resultCode == 200) { - WAMediaKeys out(mediaKeys.data, mediaKeys.len, pszMediaType); - ret = aesDecrypt(EVP_aes_256_cbc(), out.cipherKey, out.iv, pResp->pData, pResp->dataLength); - } - } - - return ret; -} - -CMStringW WhatsAppProto::GetTmpFileName(const char *pszClass, const char *pszAddition) -{ - CMStringW ret(VARSW(L"%miranda_userdata%")); - ret.AppendFormat(L"/%S/%S_%S", m_szModuleName, pszClass, pszAddition); - return ret; -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+WAJid::WAJid(const char *pszUser, const char *pszServer, int iDevice, int iAgent) :
+ user(pszUser ? pszUser : ""),
+ server(pszServer ? pszServer : ""),
+ device(iDevice),
+ agent(iAgent)
+{}
+
+WAJid::WAJid(const char *pszJid, int _id)
+{
+ if (pszJid == nullptr)
+ pszJid = "";
+
+ auto *tmp = NEWSTR_ALLOCA(pszJid);
+ auto *p = strrchr(tmp, '@');
+ if (p) {
+ *p = 0;
+ server = p + 1;
+ }
+
+ if (p = strrchr(tmp, ':')) {
+ *p = 0;
+ device = atoi(p + 1);
+ }
+ else device = _id;
+
+ if (p = strrchr(tmp, '_')) {
+ *p = 0;
+ agent = atoi(p + 1);
+ }
+ else agent = 0;
+
+ user = tmp;
+}
+
+bool WAJid::isUser() const
+{ return server == "s.whatsapp.net";
+}
+
+bool WAJid::isGroup() const
+{ return server == "g.us";
+}
+
+bool WAJid::isBroadcast() const
+{
+ return server == "broadcast";
+}
+
+bool WAJid::isStatusBroadcast() const
+{
+ return isBroadcast() && user == "status";
+}
+
+CMStringA WAJid::toString() const
+{
+ CMStringA ret(user);
+ if (agent > 0)
+ ret.AppendFormat("_%d", agent);
+ if (device > 0)
+ ret.AppendFormat(":%d", device);
+ ret.AppendFormat("@%s", server.c_str());
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static uint8_t sttLtHashInfo[] = "WhatsApp Patch Integrity";
+
+void LT_HASH::add(const void *pData, size_t len)
+{
+ uint16_t tmp[_countof(hash)];
+ HKDF(EVP_sha256(), (BYTE *)"", 0, (BYTE*)pData, len, sttLtHashInfo, sizeof(sttLtHashInfo) - 1, (BYTE *)tmp, sizeof(tmp));
+
+ for (int i = 0; i < _countof(hash); i++)
+ hash[i] += tmp[i];
+}
+
+void LT_HASH::sub(const void *pData, size_t len)
+{
+ uint16_t tmp[_countof(hash)];
+ HKDF(EVP_sha256(), (BYTE *)"", 0, (BYTE *)pData, len, sttLtHashInfo, sizeof(sttLtHashInfo) - 1, (BYTE *)tmp, sizeof(tmp));
+
+ for (int i = 0; i < _countof(hash); i++)
+ hash[i] -= tmp[i];
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+WAUser* WhatsAppProto::FindUser(const char *szId)
+{
+ if (szId == nullptr)
+ return nullptr;
+
+ mir_cslock lck(m_csUsers);
+ auto *tmp = (WAUser *)_alloca(sizeof(WAUser));
+ tmp->szId = (char*)szId;
+ return m_arUsers.find(tmp);
+}
+
+WAUser* WhatsAppProto::AddUser(const char *szId, bool bTemporary)
+{
+ auto *pUser = FindUser(szId);
+ if (pUser != nullptr)
+ return pUser;
+
+ MCONTACT hContact = db_add_contact();
+ Proto_AddToContact(hContact, m_szModuleName);
+
+ pUser = new WAUser(hContact, mir_strdup(szId));
+ pUser->bIsGroupChat = WAJid(szId).isGroup();
+
+ if (pUser->bIsGroupChat) {
+ setByte(hContact, "ChatRoom", 1);
+ setString(hContact, "ChatRoomID", szId);
+ }
+ else {
+ setString(hContact, DBKEY_ID, szId);
+ if (m_wszDefaultGroup)
+ Clist_SetGroup(hContact, m_wszDefaultGroup);
+ }
+
+ if (bTemporary)
+ Contact::RemoveFromList(hContact);
+
+ mir_cslock lck(m_csUsers);
+ m_arUsers.insert(pUser);
+ return pUser;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+WA_PKT_HANDLER WhatsAppProto::FindPersistentHandler(const WANode &pNode)
+{
+ auto *pChild = pNode.getFirstChild();
+ CMStringA szChild = (pChild) ? pChild->title : "";
+ CMStringA szTitle = pNode.title;
+ CMStringA szType = pNode.getAttr("type");
+ CMStringA szXmlns = pNode.getAttr("xmlns");
+
+ for (auto &it : m_arPersistent) {
+ if (it->pszTitle && szTitle != it->pszTitle)
+ continue;
+ if (it->pszType && szType != it->pszType)
+ continue;
+ if (it->pszXmlns && szXmlns != it->pszXmlns)
+ continue;
+ if (it->pszChild && szChild != it->pszChild)
+ continue;
+ return it->pHandler;
+ }
+
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CMStringA WhatsAppProto::GenerateMessageId()
+{
+ return CMStringA(FORMAT, "%d.%d-%d", m_wMsgPrefix[0], m_wMsgPrefix[1], m_iPacketId++);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sends a piece of JSON to a server via a websocket, masked
+
+int WhatsAppProto::WSSend(const ProtobufCMessage &msg)
+{
+ if (m_hServerConn == nullptr)
+ return -1;
+
+ MBinBuffer buf(proto::Serialize(&msg));
+ Netlib_Dump(m_hServerConn, buf.data(), buf.length(), true, 0);
+
+ MBinBuffer payload = m_noise->encodeFrame(buf.data(), buf.length());
+ WebSocket_SendBinary(m_hServerConn, payload.data(), payload.length());
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int WhatsAppProto::WSSendNode(WANode &node)
+{
+ if (m_hServerConn == nullptr)
+ return 0;
+
+ CMStringA szText;
+ node.print(szText);
+ debugLogA("Sending binary node:\n%s", szText.c_str());
+
+ WAWriter writer;
+ writer.writeNode(&node);
+
+ MBinBuffer encData = m_noise->encrypt(writer.body.data(), writer.body.length());
+ MBinBuffer payload = m_noise->encodeFrame(encData.data(), encData.length());
+ WebSocket_SendBinary(m_hServerConn, payload.data(), payload.length());
+ return 1;
+}
+
+int WhatsAppProto::WSSendNode(WANode &node, WA_PKT_HANDLER pHandler)
+{
+ if (m_hServerConn == nullptr)
+ return 0;
+
+ CMStringA id(GenerateMessageId());
+ node.addAttr("id", id);
+ {
+ mir_cslock lck(m_csPacketQueue);
+ m_arPacketQueue.insert(new WARequestSimple(id, pHandler));
+ }
+
+ return WSSendNode(node);
+}
+
+int WhatsAppProto::WSSendNode(WANode &node, WA_PKT_HANDLER_FULL pHandler, void *pUserInfo)
+{
+ if (m_hServerConn == nullptr)
+ return 0;
+
+ CMStringA id(GenerateMessageId());
+ node.addAttr("id", id);
+ {
+ mir_cslock lck(m_csPacketQueue);
+ m_arPacketQueue.insert(new WARequestParam(id, pHandler, pUserInfo));
+ }
+
+ return WSSendNode(node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+std::string decodeBinStr(const std::string &buf)
+{
+ size_t cbLen;
+ void *pData = mir_base64_decode(buf.c_str(), &cbLen);
+ if (pData == nullptr)
+ return "";
+
+ std::string res((char *)pData, cbLen);
+ mir_free(pData);
+ return res;
+}
+
+MBinBuffer decodeBufStr(const std::string &buf)
+{
+ MBinBuffer res;
+ size_t cbLen;
+ void *pData = mir_base64_decode(buf.c_str(), &cbLen);
+ if (pData == nullptr)
+ return res;
+
+ res.assign(pData, cbLen);
+ mir_free(pData);
+ return res;
+}
+
+uint32_t decodeBigEndian(const uint8_t *buf, size_t len)
+{
+ uint32_t ret = 0;
+ for (int i = 0; i < len; i++) {
+ ret <<= 8;
+ ret += buf[i];
+ }
+
+ return ret;
+}
+
+std::string encodeBigEndian(uint32_t num, size_t len)
+{
+ std::string res;
+ for (int i = 0; i < len; i++) {
+ char c = num & 0xFF;
+ res = c + res;
+ num >>= 8;
+ }
+ return res;
+}
+
+void generateIV(uint8_t *iv, uint32_t &pVar)
+{
+ auto counter = encodeBigEndian(pVar);
+ memset(iv, 0, 8);
+ memcpy(iv + 8, counter.c_str(), sizeof(uint32_t));
+
+ pVar++;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Padding
+
+void padBuffer16(MBinBuffer &buf)
+{
+ uint8_t c = 16 - buf.length() % 16;
+
+ for (uint8_t i = 0; i < c; i++)
+ buf.append(&c, 1);
+}
+
+MBinBuffer unpadBuffer16(const MBinBuffer &buf)
+{
+ size_t len = buf.length();
+ auto p = buf.data() + len - 1;
+ if (*p <= 0x10) {
+ MBinBuffer res;
+ res.assign(buf.data(), len - *p);
+ return res;
+ }
+
+ return buf;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popups
+
+void WhatsAppProto::InitPopups(void)
+{
+ g_plugin.addPopupOption(CMStringW(FORMAT, TranslateT("%s error notifications"), m_tszUserName), m_bUsePopups);
+
+ char name[256];
+ mir_snprintf(name, "%s_%s", m_szModuleName, "Error");
+
+ wchar_t desc[256];
+ mir_snwprintf(desc, L"%s/%s", m_tszUserName, TranslateT("Errors"));
+
+ POPUPCLASS ppc = {};
+ ppc.flags = PCF_UNICODE;
+ ppc.pszName = name;
+ ppc.pszDescription.w = desc;
+ ppc.hIcon = IcoLib_GetIconByHandle(m_hProtoIcon);
+ ppc.colorBack = RGB(191, 0, 0); //Red
+ ppc.colorText = RGB(255, 245, 225); //Yellow
+ ppc.iSeconds = 60;
+ m_hPopupClass = Popup_RegisterClass(&ppc);
+
+ IcoLib_ReleaseIcon(ppc.hIcon);
+}
+
+void WhatsAppProto::Popup(MCONTACT hContact, const wchar_t *szMsg, const wchar_t *szTitle)
+{
+ if (!m_bUsePopups)
+ return;
+
+ char name[256];
+ mir_snprintf(name, "%s_%s", m_szModuleName, "Error");
+
+ CMStringW wszTitle(szTitle);
+ if (hContact == 0) {
+ wszTitle.Insert(0, L": ");
+ wszTitle.Insert(0, m_tszUserName);
+ }
+
+ POPUPDATACLASS ppd = {};
+ ppd.szTitle.w = wszTitle;
+ ppd.szText.w = szMsg;
+ ppd.pszClassName = name;
+ ppd.hContact = hContact;
+ Popup_AddClass(&ppd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MBinBuffer WhatsAppProto::unzip(const MBinBuffer &src)
+{
+ z_stream strm = {};
+ inflateInit(&strm);
+
+ strm.avail_in = (uInt)src.length();
+ strm.next_in = (Bytef *)src.data();
+
+ MBinBuffer res;
+ Bytef buf[2048];
+
+ while (strm.avail_in > 0) {
+ strm.avail_out = sizeof(buf);
+ strm.next_out = buf;
+
+ int ret = inflate(&strm, Z_NO_FLUSH);
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ __fallthrough;
+
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&strm);
+ return res;
+ }
+
+ res.append(buf, sizeof(buf) - strm.avail_out);
+ if (ret == Z_STREAM_END)
+ break;
+ }
+
+ inflateEnd(&strm);
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void bin2file(const MBinBuffer &buf, const wchar_t *pwszFileName)
+{
+ int fileId = _wopen(pwszFileName, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE);
+ if (fileId != -1) {
+ write(fileId, buf.data(), (unsigned)buf.length());
+ close(fileId);
+ }
+}
+
+void string2file(const std::string &str, const wchar_t *pwszFileName)
+{
+ int fileId = _wopen(pwszFileName, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE);
+ if (fileId != -1) {
+ write(fileId, str.c_str(), (unsigned)str.size());
+ close(fileId);
+ }
+}
+
+CMStringA file2string(const wchar_t *pwszFileName)
+{
+ CMStringA res;
+
+ int fileId = _wopen(pwszFileName, _O_RDONLY | _O_BINARY, _S_IREAD | _S_IWRITE);
+ if (fileId != -1) {
+ res.Truncate(filelength(fileId));
+ read(fileId, res.GetBuffer(), res.GetLength());
+ close(fileId);
+ }
+ return res;
+}
+
+void WhatsAppProto::GetMessageContent(
+ CMStringA &txt,
+ const char *szType,
+ const char *szMimeType,
+ const char *szUrl,
+ const char *szDirectPath,
+ const ProtobufCBinaryData &pMediaKey,
+ const char *szCaption)
+{
+ if (szCaption) {
+ if (m_bUseBbcodes)
+ txt.Append("[b]");
+ txt.Append(szCaption);
+ if (m_bUseBbcodes)
+ txt.Append("[/b]");
+ txt.Append("\n");
+ }
+
+ CMStringA url = szUrl;
+ int idx = url.ReverseFind('/');
+ if (idx != -1)
+ url.Delete(0, idx+1);
+ idx = url.ReverseFind('.');
+ if (idx != -1)
+ url.Truncate(idx);
+ if (szMimeType)
+ url.Append(_T2A(ProtoGetAvatarExtension(ProtoGetAvatarFormatByMimeType(szMimeType))));
+
+ char *szMediaType = NEWSTR_ALLOCA(szType);
+ szMediaType[0] = toupper(szMediaType[0]);
+
+ MBinBuffer buf = DownloadEncryptedFile(directPath2url(szDirectPath), pMediaKey, szMediaType);
+ if (!buf.isEmpty()) {
+ CMStringW pwszFileName(GetTmpFileName(szType, url));
+ bin2file(buf, pwszFileName);
+
+ pwszFileName.Replace(L"\\", L"/");
+ txt.AppendFormat("file://%s", T2Utf(pwszFileName).get());
+ }
+}
+
+CMStringA WhatsAppProto::GetMessageText(const Wa__Message *pMessage)
+{
+ CMStringA szMessageText;
+
+ if (pMessage) {
+ if (auto *pExt = pMessage->extendedtextmessage) {
+ if (pExt->title) {
+ if (m_bUseBbcodes)
+ szMessageText.Append("[b]");
+ szMessageText.Append(pExt->title);
+ if (m_bUseBbcodes)
+ szMessageText.Append("[/b]");
+ szMessageText.Append("\n");
+ }
+
+ if (pExt->contextinfo && pExt->contextinfo->quotedmessage)
+ szMessageText.AppendFormat("> %s\n\n", pExt->contextinfo->quotedmessage->conversation);
+
+ if (pExt->text)
+ szMessageText.Append(pExt->text);
+ }
+ else if (auto *pAudio = pMessage->audiomessage) {
+ GetMessageContent(szMessageText, "audio", pAudio->url, pAudio->directpath, pAudio->mimetype, pAudio->mediakey);
+ }
+ else if (auto *pVideo = pMessage->videomessage) {
+ GetMessageContent(szMessageText, "video", pVideo->url, pVideo->directpath, pVideo->mimetype, pVideo->mediakey, pVideo->caption);
+ }
+ else if (auto *pImage = pMessage->imagemessage) {
+ GetMessageContent(szMessageText, "image", pImage->url, pImage->directpath, pImage->mimetype, pImage->mediakey, pImage->caption);
+ }
+ else if (mir_strlen(pMessage->conversation))
+ szMessageText = pMessage->conversation;
+ }
+
+ return szMessageText;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void proto::CleanBinary(ProtobufCBinaryData &field)
+{
+ if (field.data) {
+ free(field.data);
+ field.data = nullptr;
+ }
+ field.len = 0;
+}
+
+ProtobufCBinaryData proto::SetBinary(const void *pData, size_t len)
+{
+ ProtobufCBinaryData res;
+ if (pData == nullptr) {
+ res.data = nullptr;
+ res.len = 0;
+ }
+ else {
+ res.data = (uint8_t *)malloc(res.len = len);
+ memcpy(res.data, pData, len);
+ }
+ return res;
+}
+
+MBinBuffer proto::Serialize(const ProtobufCMessage *msg)
+{
+ MBinBuffer res(protobuf_c_message_get_packed_size(msg));
+ protobuf_c_message_pack(msg, res.data());
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CMStringA directPath2url(const char *pszDirectPath)
+{
+ return CMStringA("https://mmg.whatsapp.net") + pszDirectPath;
+}
+
+WAMediaKeys::WAMediaKeys(const uint8_t *pKey, size_t keyLen, const char *pszMediaType)
+{
+ CMStringA pszHkdfString(FORMAT, "WhatsApp %s Keys", pszMediaType);
+
+ HKDF(EVP_sha256(), (BYTE *)"", 0, pKey, (int)keyLen, (BYTE *)pszHkdfString.c_str(), pszHkdfString.GetLength(), (BYTE *)this, sizeof(*this));
+}
+
+MBinBuffer WhatsAppProto::DownloadEncryptedFile(const char *url, const ProtobufCBinaryData &mediaKeys, const char *pszMediaType)
+{
+ NETLIBHTTPHEADER headers[1] = {{"Origin", "https://web.whatsapp.com"}};
+
+ NETLIBHTTPREQUEST req = {};
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_GET;
+ req.szUrl = (char*)url;
+ req.headersCount = _countof(headers);
+ req.headers = headers;
+
+ MBinBuffer ret;
+ auto *pResp = Netlib_HttpTransaction(m_hNetlibUser, &req);
+ if (pResp) {
+ if (pResp->resultCode == 200) {
+ WAMediaKeys out(mediaKeys.data, mediaKeys.len, pszMediaType);
+ ret = aesDecrypt(EVP_aes_256_cbc(), out.cipherKey, out.iv, pResp->pData, pResp->dataLength);
+ }
+ }
+
+ return ret;
+}
+
+CMStringW WhatsAppProto::GetTmpFileName(const char *pszClass, const char *pszAddition)
+{
+ CMStringW ret(VARSW(L"%miranda_userdata%"));
+ ret.AppendFormat(L"/%S/%S_%S", m_szModuleName, pszClass, pszAddition);
+ return ret;
+}
diff --git a/protocols/WhatsApp/src/utils.h b/protocols/WhatsApp/src/utils.h index 02882f1c17..af02695818 100644 --- a/protocols/WhatsApp/src/utils.h +++ b/protocols/WhatsApp/src/utils.h @@ -1,267 +1,267 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#define DICT_VERSION 2 - -#define LIST_EMPTY 0 -#define STREAM_END 2 -#define DICTIONARY_0 236 -#define DICTIONARY_1 237 -#define DICTIONARY_2 238 -#define DICTIONARY_3 239 -#define AD_JID 247 -#define LIST_8 248 -#define LIST_16 249 -#define JID_PAIR 250 -#define HEX_8 251 -#define BINARY_8 252 -#define BINARY_20 253 -#define BINARY_32 254 -#define NIBBLE_8 255 - -class WANode // kinda XML -{ - friend class WAReader; - friend class WAWriter; - - WANode *pParent = nullptr; - OBJLIST<struct Attr> attrs; - OBJLIST<WANode> children; - -public: - WANode(); - WANode(const char *pszTitle); - ~WANode(); - - void addAttr(const char *pszName, const char *pszValue); - void addAttr(const char *pszName, int iValue); - int getAttrInt(const char *pszName) const; - const char *getAttr(const char *pszName) const; - - CMStringA getBody() const; - - WANode *addChild(const char *pszName); - WANode *getChild(const char *pszName) const; - WANode *getFirstChild(void) const; - const OBJLIST<WANode> &getChildren(void) const - { return children; - } - - void print(CMStringA &dest, int level = 0) const; - - CMStringA title; - MBinBuffer content; -}; - -__forceinline WANode &operator<<(WANode &node, const CHAR_PARAM ¶m) -{ - node.addAttr(param.szName, param.szValue); - return node; -} - -__forceinline WANode &operator<<(WANode &node, const INT_PARAM ¶m) -{ - node.addAttr(param.szName, param.iValue); - return node; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -namespace IQ -{ - enum Type { GET, SET, RESULT }; -}; - -struct WANodeIq : public WANode -{ - WANodeIq(IQ::Type type, const char *pszXmlns = nullptr, const char *pszTo = nullptr); -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -struct XCHILD -{ - const char *name, *value; - - __forceinline XCHILD(const char *_name) : - name(_name) - {} -}; - -__forceinline WANode& operator<<(WANode &node, const XCHILD &child) -{ - node.addChild(child.name); - return node; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// WAReader - -class WAReader -{ - const BYTE *m_buf, *m_limit; - - uint32_t readIntN(int i); - CMStringA readStringFromChars(int size); - - bool readAttributes(WANode *node, int count); - uint32_t readInt20(); - bool readList(WANode *pParent, int tag); - int readListSize(int tag); - CMStringA readPacked(int tag); - CMStringA readString(int tag); - -public: - WAReader(const void *buf, size_t cbLen) : - m_buf((BYTE*)buf), - m_limit((BYTE*)buf + cbLen) - {} - - WANode* readNode(); - - __forceinline uint32_t readInt8() { return readIntN(1); } - __forceinline uint32_t readInt16() { return readIntN(2); } - __forceinline uint32_t readInt32() { return readIntN(4); } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// WAWriter - -class WAWriter -{ - __forceinline void writeInt8(int value) { writeIntN(value, 1); } - __forceinline void writeInt16(int value) { writeIntN(value, 2); } - __forceinline void writeInt32(int value) { writeIntN(value, 4); } - - void writeByte(uint8_t b); - void writeIntN(int value, int i); - void writeInt20(int value); - void writeLength(int value); - void writeListSize(int tag); - void writePacked(const CMStringA &str, int tag); - void writeString(const char *str); - bool writeToken(const char *str); - -public: - void writeNode(const WANode *pNode); - - MBinBuffer body; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// WAJid - -struct WAJid -{ - int device, agent; - CMStringA user, server; - - WAJid(const char *pszJid, int device = 0); - WAJid(const char *pszUser, const char *pszServer, int device = 0, int agent = 0); - - CMStringA toString() const; - - bool isUser() const; - bool isGroup() const; - bool isBroadcast() const; - bool isStatusBroadcast() const; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// WASendTask - -struct WASendTask -{ - WASendTask(const char *jid) : - payLoad("message"), - arDest(1) - { - uint8_t msgId[8]; - Utils_GetRandom(&msgId, sizeof(msgId)); - bin2hex(msgId, sizeof(msgId), szMsgId); - strupr(szMsgId); - - payLoad << CHAR_PARAM("id", szMsgId) << CHAR_PARAM("type", "text") << CHAR_PARAM("to", jid); - } - - char szMsgId[40]; - WANode payLoad; - OBJLIST<WAJid> arDest; - MBinBuffer content; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// LT_HASH - -struct LT_HASH -{ - LT_HASH() - { - init(); - }; - - uint16_t hash[64]; - - void add(const void *pData, size_t len); - void sub(const void *pData, size_t len); - - void init() - { - SecureZeroMemory(hash, sizeof(hash)); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// functions - -void bin2file(const MBinBuffer &buf, const wchar_t *pwszFileName); - -void string2file(const std::string &str, const wchar_t *pwszFileName); -CMStringA file2string(const wchar_t *pwszFileName); - -CMStringA directPath2url(const char *pszDirectPath); - -std::string decodeBinStr(const std::string &buf); -MBinBuffer decodeBufStr(const std::string &buf); - -void padBuffer16(MBinBuffer &buf); -MBinBuffer unpadBuffer16(const MBinBuffer &buf); - -CMStringA protobuf_c_text_to_string(const ProtobufCMessage *m); - -MBinBuffer aesDecrypt( - const EVP_CIPHER *cipher, - const uint8_t *key, - const uint8_t *iv, - const void *data, size_t dataLen, - const void *additionalData = 0, size_t additionalLen = 0); - -MBinBuffer aesEncrypt( - const EVP_CIPHER *cipher, - const uint8_t *key, - const uint8_t *iv, - const void *data, size_t dataLen, - const void *additionalData = 0, size_t additionalLen = 0); - -uint32_t decodeBigEndian(const uint8_t *pData, size_t len); - -__forceinline uint32_t decodeBigEndian(const ProtobufCBinaryData &buf) { - return decodeBigEndian(buf.data, buf.len); -} -__forceinline uint32_t decodeBigEndian(const MBinBuffer &buf) { - return decodeBigEndian(buf.data(), buf.length()); -} - -std::string encodeBigEndian(uint32_t num, size_t len = sizeof(uint32_t)); - -void generateIV(uint8_t *iv, uint32_t &pVar); - -unsigned char* HKDF(const EVP_MD *evp_md, - const unsigned char *salt, size_t salt_len, - const unsigned char *key, size_t key_len, - const unsigned char *info, size_t info_len, - unsigned char *okm, size_t okm_len); +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#define DICT_VERSION 2
+
+#define LIST_EMPTY 0
+#define STREAM_END 2
+#define DICTIONARY_0 236
+#define DICTIONARY_1 237
+#define DICTIONARY_2 238
+#define DICTIONARY_3 239
+#define AD_JID 247
+#define LIST_8 248
+#define LIST_16 249
+#define JID_PAIR 250
+#define HEX_8 251
+#define BINARY_8 252
+#define BINARY_20 253
+#define BINARY_32 254
+#define NIBBLE_8 255
+
+class WANode // kinda XML
+{
+ friend class WAReader;
+ friend class WAWriter;
+
+ WANode *pParent = nullptr;
+ OBJLIST<struct Attr> attrs;
+ OBJLIST<WANode> children;
+
+public:
+ WANode();
+ WANode(const char *pszTitle);
+ ~WANode();
+
+ void addAttr(const char *pszName, const char *pszValue);
+ void addAttr(const char *pszName, int iValue);
+ int getAttrInt(const char *pszName) const;
+ const char *getAttr(const char *pszName) const;
+
+ CMStringA getBody() const;
+
+ WANode *addChild(const char *pszName);
+ WANode *getChild(const char *pszName) const;
+ WANode *getFirstChild(void) const;
+ const OBJLIST<WANode> &getChildren(void) const
+ { return children;
+ }
+
+ void print(CMStringA &dest, int level = 0) const;
+
+ CMStringA title;
+ MBinBuffer content;
+};
+
+__forceinline WANode &operator<<(WANode &node, const CHAR_PARAM ¶m)
+{
+ node.addAttr(param.szName, param.szValue);
+ return node;
+}
+
+__forceinline WANode &operator<<(WANode &node, const INT_PARAM ¶m)
+{
+ node.addAttr(param.szName, param.iValue);
+ return node;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+namespace IQ
+{
+ enum Type { GET, SET, RESULT };
+};
+
+struct WANodeIq : public WANode
+{
+ WANodeIq(IQ::Type type, const char *pszXmlns = nullptr, const char *pszTo = nullptr);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct XCHILD
+{
+ const char *name, *value;
+
+ __forceinline XCHILD(const char *_name) :
+ name(_name)
+ {}
+};
+
+__forceinline WANode& operator<<(WANode &node, const XCHILD &child)
+{
+ node.addChild(child.name);
+ return node;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WAReader
+
+class WAReader
+{
+ const BYTE *m_buf, *m_limit;
+
+ uint32_t readIntN(int i);
+ CMStringA readStringFromChars(int size);
+
+ bool readAttributes(WANode *node, int count);
+ uint32_t readInt20();
+ bool readList(WANode *pParent, int tag);
+ int readListSize(int tag);
+ CMStringA readPacked(int tag);
+ CMStringA readString(int tag);
+
+public:
+ WAReader(const void *buf, size_t cbLen) :
+ m_buf((BYTE*)buf),
+ m_limit((BYTE*)buf + cbLen)
+ {}
+
+ WANode* readNode();
+
+ __forceinline uint32_t readInt8() { return readIntN(1); }
+ __forceinline uint32_t readInt16() { return readIntN(2); }
+ __forceinline uint32_t readInt32() { return readIntN(4); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WAWriter
+
+class WAWriter
+{
+ __forceinline void writeInt8(int value) { writeIntN(value, 1); }
+ __forceinline void writeInt16(int value) { writeIntN(value, 2); }
+ __forceinline void writeInt32(int value) { writeIntN(value, 4); }
+
+ void writeByte(uint8_t b);
+ void writeIntN(int value, int i);
+ void writeInt20(int value);
+ void writeLength(int value);
+ void writeListSize(int tag);
+ void writePacked(const CMStringA &str, int tag);
+ void writeString(const char *str);
+ bool writeToken(const char *str);
+
+public:
+ void writeNode(const WANode *pNode);
+
+ MBinBuffer body;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WAJid
+
+struct WAJid
+{
+ int device, agent;
+ CMStringA user, server;
+
+ WAJid(const char *pszJid, int device = 0);
+ WAJid(const char *pszUser, const char *pszServer, int device = 0, int agent = 0);
+
+ CMStringA toString() const;
+
+ bool isUser() const;
+ bool isGroup() const;
+ bool isBroadcast() const;
+ bool isStatusBroadcast() const;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WASendTask
+
+struct WASendTask
+{
+ WASendTask(const char *jid) :
+ payLoad("message"),
+ arDest(1)
+ {
+ uint8_t msgId[8];
+ Utils_GetRandom(&msgId, sizeof(msgId));
+ bin2hex(msgId, sizeof(msgId), szMsgId);
+ strupr(szMsgId);
+
+ payLoad << CHAR_PARAM("id", szMsgId) << CHAR_PARAM("type", "text") << CHAR_PARAM("to", jid);
+ }
+
+ char szMsgId[40];
+ WANode payLoad;
+ OBJLIST<WAJid> arDest;
+ MBinBuffer content;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// LT_HASH
+
+struct LT_HASH
+{
+ LT_HASH()
+ {
+ init();
+ };
+
+ uint16_t hash[64];
+
+ void add(const void *pData, size_t len);
+ void sub(const void *pData, size_t len);
+
+ void init()
+ {
+ SecureZeroMemory(hash, sizeof(hash));
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// functions
+
+void bin2file(const MBinBuffer &buf, const wchar_t *pwszFileName);
+
+void string2file(const std::string &str, const wchar_t *pwszFileName);
+CMStringA file2string(const wchar_t *pwszFileName);
+
+CMStringA directPath2url(const char *pszDirectPath);
+
+std::string decodeBinStr(const std::string &buf);
+MBinBuffer decodeBufStr(const std::string &buf);
+
+void padBuffer16(MBinBuffer &buf);
+MBinBuffer unpadBuffer16(const MBinBuffer &buf);
+
+CMStringA protobuf_c_text_to_string(const ProtobufCMessage *m);
+
+MBinBuffer aesDecrypt(
+ const EVP_CIPHER *cipher,
+ const uint8_t *key,
+ const uint8_t *iv,
+ const void *data, size_t dataLen,
+ const void *additionalData = 0, size_t additionalLen = 0);
+
+MBinBuffer aesEncrypt(
+ const EVP_CIPHER *cipher,
+ const uint8_t *key,
+ const uint8_t *iv,
+ const void *data, size_t dataLen,
+ const void *additionalData = 0, size_t additionalLen = 0);
+
+uint32_t decodeBigEndian(const uint8_t *pData, size_t len);
+
+__forceinline uint32_t decodeBigEndian(const ProtobufCBinaryData &buf) {
+ return decodeBigEndian(buf.data, buf.len);
+}
+__forceinline uint32_t decodeBigEndian(const MBinBuffer &buf) {
+ return decodeBigEndian(buf.data(), buf.length());
+}
+
+std::string encodeBigEndian(uint32_t num, size_t len = sizeof(uint32_t));
+
+void generateIV(uint8_t *iv, uint32_t &pVar);
+
+unsigned char* HKDF(const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len);
diff --git a/protocols/WhatsApp/src/version.h b/protocols/WhatsApp/src/version.h index 3a160f44ec..caa1a7ea52 100644 --- a/protocols/WhatsApp/src/version.h +++ b/protocols/WhatsApp/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "WhatsApp protocol support for Miranda NG."
#define __AUTHOR "George Hazan"
#define __AUTHORWEB "https://miranda-ng.org/p/WhatsApp"
-#define __COPYRIGHT "© 2019-22 Miranda NG team"
+#define __COPYRIGHT "© 2019-23 Miranda NG team"
diff --git a/protocols/WhatsApp/src/wanode.cpp b/protocols/WhatsApp/src/wanode.cpp index 0c3fe15d03..9c40912f28 100644 --- a/protocols/WhatsApp/src/wanode.cpp +++ b/protocols/WhatsApp/src/wanode.cpp @@ -1,608 +1,608 @@ -/* - -WhatsApp plugin for Miranda NG -Copyright © 2019-22 George Hazan - -*/ - -#include "stdafx.h" -#include "dicts.h" - -struct Attr -{ - Attr(const char *pszName, const char *pszValue) : - name(pszName), - value(pszValue) - {} - - Attr(const char *pszName, int iValue) : - name(pszName), - value(FORMAT, "%d", iValue) - {} - - CMStringA name, value; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// WANodeIq members - -WANodeIq::WANodeIq(IQ::Type type, const char *pszXmlns, const char *pszTo) : - WANode("iq") -{ - switch (type) { - case IQ::GET: addAttr("type", "get"); break; - case IQ::SET: addAttr("type", "set"); break; - case IQ::RESULT: addAttr("type", "result"); break; - } - - if (pszXmlns) - addAttr("xmlns", pszXmlns); - - addAttr("to", pszTo ? pszTo : S_WHATSAPP_NET); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// WANode members - -WANode::WANode() : - attrs(1), - children(1) -{} - -WANode::WANode(const char *pszTitle) : - attrs(1), - children(1), - title(pszTitle) -{} - -WANode::~WANode() -{ -} - -const char *WANode::getAttr(const char *pszName) const -{ - if (this != nullptr) - for (auto &p : attrs) - if (p->name == pszName) - return p->value.c_str(); - - return nullptr; -} - -int WANode::getAttrInt(const char *pszName) const -{ - if (this != nullptr) - for (auto &p : attrs) - if (p->name == pszName) - return atoi(p->value.c_str()); - - return 0; -} - -void WANode::addAttr(const char *pszName, const char *pszValue) -{ - attrs.insert(new Attr(pszName, pszValue)); -} - -void WANode::addAttr(const char *pszName, int iValue) -{ - attrs.insert(new Attr(pszName, iValue)); -} - -CMStringA WANode::getBody() const -{ - return CMStringA((char *)content.data(), (int)content.length()); -} - -WANode *WANode::addChild(const char *pszName) -{ - auto *pNew = new WANode(pszName); - pNew->pParent = this; - children.insert(pNew); - return pNew; -} - -WANode* WANode::getChild(const char *pszName) const -{ - if (this == nullptr) - return nullptr; - - for (auto &it : children) - if (it->title == pszName) - return it; - - return nullptr; -} - -WANode* WANode::getFirstChild(void) const -{ - return (children.getCount()) ? &children[0] : nullptr; -} - -void WANode::print(CMStringA &dest, int level) const -{ - for (int i = 0; i < level; i++) - dest.Append(" "); - - dest.AppendFormat("<%s ", title.c_str()); - for (auto &p : attrs) - dest.AppendFormat("%s=\"%s\" ", p->name.c_str(), p->value.c_str()); - dest.Truncate(dest.GetLength() - 1); - - if (content.isEmpty() && !children.getCount()) { - dest.Append("/>\n"); - return; - } - - dest.Append(">"); - if (!content.isEmpty()) { - ptrA tmp((char *)mir_alloc(content.length() * 2 + 1)); - bin2hex(content.data(), content.length(), tmp); - dest.AppendFormat("%s", tmp.get()); - } - - if (children.getCount()) { - dest.Append("\n"); - - for (auto &p : children) - p->print(dest, level + 1); - - for (int i = 0; i < level; i++) - dest.Append(" "); - } - - dest.AppendFormat("</%s>\n", title.c_str()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// WAReader class members - -bool WAReader::readAttributes(WANode *pNode, int count) -{ - if (count == 0) - return true; - - for (int i = 0; i < count; i++) { - CMStringA name = readString(readInt8()); - if (name.IsEmpty()) - return false; - - CMStringA value = readString(readInt8()); - if (value.IsEmpty()) - return false; - - pNode->addAttr(name, value); - } - return true; -} - -uint32_t WAReader::readInt20() -{ - if (m_limit - m_buf < 3) - return 0; - - int ret = (int(m_buf[0] & 0x0F) << 16) + (int(m_buf[1]) << 8) + int(m_buf[2]); - m_buf += 3; - return ret; -} - -uint32_t WAReader::readIntN(int n) -{ - if (m_limit - m_buf < n) - return 0; - - uint32_t res = 0; - for (int i = 0; i < n; i++, m_buf++) - res = (res <<= 8) + *m_buf; - return res; -} - -bool WAReader::readList(WANode *pParent, int tag) -{ - int size = readListSize(tag); - if (size == -1) - return false; - - for (int i = 0; i < size; i++) { - WANode *pNew = readNode(); - if (pNew == nullptr) - return false; - pParent->children.insert(pNew); - } - - return true; -} - -int WAReader::readListSize(int tag) -{ - switch (tag) { - case LIST_EMPTY: - return 0; - case LIST_8: - return readInt8(); - case LIST_16: - return readInt16(); - } - return -1; -} - -WANode *WAReader::readNode() -{ - int listSize = readListSize(readInt8()); - if (listSize == -1) - return nullptr; - - int descrTag = readInt8(); - if (descrTag == STREAM_END) - return nullptr; - - CMStringA name = readString(descrTag); - if (name.IsEmpty()) - return nullptr; - - std::unique_ptr<WANode> ret(new WANode()); - ret->title = name.c_str(); - - if (!readAttributes(ret.get(), (listSize - 1) >> 1)) - return nullptr; - - if ((listSize % 2) == 1) - return ret.release(); - - int size, tag = readInt8(); - switch (tag) { - case LIST_EMPTY: case LIST_8: case LIST_16: - readList(ret.get(), tag); - break; - - case BINARY_8: - size = readInt8(); - -LBL_Binary: - if (m_limit - m_buf < size) - return false; - - ret->content.assign((void *)m_buf, size); - m_buf += size; - break; - - case BINARY_20: - size = readInt20(); - goto LBL_Binary; - - case BINARY_32: - size = readInt32(); - goto LBL_Binary; - - default: - CMStringA str = readString(tag); - ret->content.assign(str.GetBuffer(), str.GetLength() + 1); - } - - return ret.release(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int unpackHex(int val) -{ - if (val < 0 || val > 15) - return -1; - - return (val < 10) ? val + '0' : val - 10 + 'A'; -} - -static int unpackNibble(int val) -{ - if (val < 0 || val > 15) - return -1; - - switch (val) { - case 10: return '-'; - case 11: return '.'; - case 15: return 0; - default: return '0' + val; - } -} - -CMStringA WAReader::readPacked(int tag) -{ - int startByte = readInt8(); - bool bTrim = false; - if (startByte & 0x80) { - startByte &= ~0x80; - bTrim = true; - } - - CMStringA ret; - for (int i = 0; i < startByte; i++) { - BYTE b = readInt8(); - int lower = (tag == NIBBLE_8) ? unpackNibble(b >> 4) : unpackHex(b >> 4); - if (lower == -1) - return ""; - - int higher = (tag == NIBBLE_8) ? unpackNibble(b & 0x0F) : unpackHex(b & 0x0F); - if (higher == -1) - return ""; - - ret.AppendChar(lower); - ret.AppendChar(higher); - } - - if (bTrim && !ret.IsEmpty()) - ret.Truncate(ret.GetLength() - 1); - return ret; -} - -CMStringA WAReader::readString(int tag) -{ - if (tag >= 1 && tag < _countof(SingleByteTokens)) - return SingleByteTokens[tag]; - - int idx; - switch (tag) { - case DICTIONARY_0: - idx = readInt8(); - return (idx < _countof(dict0)) ? dict0[idx] : ""; - - case DICTIONARY_1: - idx = readInt8(); - return (idx < _countof(dict1)) ? dict1[idx] : ""; - - case DICTIONARY_2: - idx = readInt8(); - return (idx < _countof(dict2)) ? dict2[idx] : ""; - - case DICTIONARY_3: - idx = readInt8(); - return (idx < _countof(dict3)) ? dict3[idx] : ""; - - case LIST_EMPTY: - return ""; - - case BINARY_8: - return readStringFromChars(readInt8()); - - case BINARY_20: - return readStringFromChars(readInt20()); - - case BINARY_32: - return readStringFromChars(readInt32()); - - case NIBBLE_8: - case HEX_8: - return readPacked(tag); - - case AD_JID: - { - int agent = readInt8(); - int device = readInt8(); - WAJid jid(readString(readInt8()), "s.whatsapp.net", device, agent); - return jid.toString(); - } - - case JID_PAIR: - CMStringA s1 = readString(readInt8()); - CMStringA s2 = readString(readInt8()); - if (s1.IsEmpty() && s2.IsEmpty()) - break; - - return CMStringA(FORMAT, "%s@%s", s1.c_str(), s2.c_str()); - } - - // error - return ""; -} - -CMStringA WAReader::readStringFromChars(int size) -{ - if (m_limit - m_buf < size) - return ""; - - CMStringA ret((char *)m_buf, size); - m_buf += size; - return ret; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// WAWriter class members - -void WAWriter::writeByte(uint8_t b) -{ - body.append(&b, 1); -} - -void WAWriter::writeIntN(int value, int n) -{ - for (int i = n - 1; i >= 0; i--) - writeByte((value >> i * 8) & 0xFF); -} - -void WAWriter::writeInt20(int value) -{ - writeByte((value >> 16) & 0xFF); - writeByte((value >> 8) & 0xFF); - writeByte(value & 0xFF); -} - -void WAWriter::writeLength(int value) -{ - if (value >= (1 << 20)) { - writeByte(BINARY_32); - writeInt32(value); - } - else if (value >= 256) { - writeByte(BINARY_20); - writeInt20(value); - } - else { - writeByte(BINARY_8); - writeInt8(value); - } -} - -void WAWriter::writeListSize(int length) -{ - if (length == 0) - writeByte(LIST_EMPTY); - else if (length < 256) { - writeByte(LIST_8); - writeInt8(length); - } - else { - writeByte(LIST_16); - writeInt16(length); - } -} - -void WAWriter::writeNode(const WANode *pNode) -{ - // we never send zipped content - if (pNode->pParent == nullptr) - writeByte(0); - - int numAttrs = (int)pNode->attrs.getCount(); - int hasContent = pNode->content.length() != 0 || pNode->children.getCount() != 0; - writeListSize(2 * numAttrs + 1 + hasContent); - - writeString(pNode->title.c_str()); - - // write attributes - for (auto &it : pNode->attrs) { - if (it->value.IsEmpty()) - continue; - - writeString(it->name.c_str()); - writeString(it->value.c_str()); - } - - // write contents - if (pNode->content.length()) { - writeLength((int)pNode->content.length()); - body.append(pNode->content.data(), pNode->content.length()); - } - // write children - else if (pNode->children.getCount()) { - writeListSize(pNode->children.getCount()); - for (auto &it : pNode->children) - writeNode(it); - } -} - -bool WAWriter::writeToken(const char *str) -{ - for (auto &it : SingleByteTokens) - if (!strcmp(str, it)) { - writeByte(int(&it - SingleByteTokens)); - return true; - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static BYTE packNibble(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - - switch (c) { - case '-': return 10; - case '.': return 11; - case 0: return 15; - } - - return -1; -} - -static BYTE packHex(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'A' && c <= 'F') - return 10 + c - 'A'; - - if (c >= 'a' && c <= 'f') - return 10 + c - 'a'; - - if (c == 0) - return 15; - - return -1; -} - -static BYTE packPair(int type, char c1, char c2) -{ - BYTE b1 = (type == NIBBLE_8) ? packNibble(c1) : packHex(c1); - BYTE b2 = (type == NIBBLE_8) ? packNibble(c2) : packHex(c2); - return (b1 << 4) + b2; -} - -static bool isNibble(const CMStringA &str) -{ - return strspn(str, "0123456789-.") == str.GetLength(); -} - -static bool isHex(const CMStringA &str) -{ - return strspn(str, "0123456789abcdefABCDEF") == str.GetLength(); -} - -void WAWriter::writePacked(const CMStringA &str, int tag) -{ - if (str.GetLength() > 254) - return; - - writeByte(tag); - - int len = str.GetLength() / 2; - BYTE firstByte = (str.GetLength() % 2) == 0 ? 0 : 0x81; - writeByte(firstByte + len); - - const char *p = str; - for (int i = 0; i < len; i++, p += 2) - writeByte(packPair(tag, p[0], p[1])); - - if (firstByte != 0) - writeByte(packPair(tag, p[0], 0)); -} - -void WAWriter::writeString(const char *str) -{ - if (writeToken(str)) - return; - - auto *pszDelimiter = strchr(str, '@'); - if (pszDelimiter) { - WAJid jid(str); - if (jid.device || jid.agent) { - writeByte(AD_JID); - writeByte(jid.agent); - writeByte(jid.device); - writeString(jid.user); - } - else { - writeByte(JID_PAIR); - - if (jid.user.IsEmpty()) // empty user - writeByte(LIST_EMPTY); - else - writeString(jid.user); - - writeString(jid.server); - } - } - else { - CMStringA buf(str); - if (isNibble(buf)) - writePacked(buf, NIBBLE_8); - else if (isHex(buf)) - writePacked(buf, HEX_8); - else { - writeLength(buf.GetLength()); - body.append(buf, buf.GetLength()); - } - } -} +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2019-23 George Hazan
+
+*/
+
+#include "stdafx.h"
+#include "dicts.h"
+
+struct Attr
+{
+ Attr(const char *pszName, const char *pszValue) :
+ name(pszName),
+ value(pszValue)
+ {}
+
+ Attr(const char *pszName, int iValue) :
+ name(pszName),
+ value(FORMAT, "%d", iValue)
+ {}
+
+ CMStringA name, value;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WANodeIq members
+
+WANodeIq::WANodeIq(IQ::Type type, const char *pszXmlns, const char *pszTo) :
+ WANode("iq")
+{
+ switch (type) {
+ case IQ::GET: addAttr("type", "get"); break;
+ case IQ::SET: addAttr("type", "set"); break;
+ case IQ::RESULT: addAttr("type", "result"); break;
+ }
+
+ if (pszXmlns)
+ addAttr("xmlns", pszXmlns);
+
+ addAttr("to", pszTo ? pszTo : S_WHATSAPP_NET);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WANode members
+
+WANode::WANode() :
+ attrs(1),
+ children(1)
+{}
+
+WANode::WANode(const char *pszTitle) :
+ attrs(1),
+ children(1),
+ title(pszTitle)
+{}
+
+WANode::~WANode()
+{
+}
+
+const char *WANode::getAttr(const char *pszName) const
+{
+ if (this != nullptr)
+ for (auto &p : attrs)
+ if (p->name == pszName)
+ return p->value.c_str();
+
+ return nullptr;
+}
+
+int WANode::getAttrInt(const char *pszName) const
+{
+ if (this != nullptr)
+ for (auto &p : attrs)
+ if (p->name == pszName)
+ return atoi(p->value.c_str());
+
+ return 0;
+}
+
+void WANode::addAttr(const char *pszName, const char *pszValue)
+{
+ attrs.insert(new Attr(pszName, pszValue));
+}
+
+void WANode::addAttr(const char *pszName, int iValue)
+{
+ attrs.insert(new Attr(pszName, iValue));
+}
+
+CMStringA WANode::getBody() const
+{
+ return CMStringA((char *)content.data(), (int)content.length());
+}
+
+WANode *WANode::addChild(const char *pszName)
+{
+ auto *pNew = new WANode(pszName);
+ pNew->pParent = this;
+ children.insert(pNew);
+ return pNew;
+}
+
+WANode* WANode::getChild(const char *pszName) const
+{
+ if (this == nullptr)
+ return nullptr;
+
+ for (auto &it : children)
+ if (it->title == pszName)
+ return it;
+
+ return nullptr;
+}
+
+WANode* WANode::getFirstChild(void) const
+{
+ return (children.getCount()) ? &children[0] : nullptr;
+}
+
+void WANode::print(CMStringA &dest, int level) const
+{
+ for (int i = 0; i < level; i++)
+ dest.Append(" ");
+
+ dest.AppendFormat("<%s ", title.c_str());
+ for (auto &p : attrs)
+ dest.AppendFormat("%s=\"%s\" ", p->name.c_str(), p->value.c_str());
+ dest.Truncate(dest.GetLength() - 1);
+
+ if (content.isEmpty() && !children.getCount()) {
+ dest.Append("/>\n");
+ return;
+ }
+
+ dest.Append(">");
+ if (!content.isEmpty()) {
+ ptrA tmp((char *)mir_alloc(content.length() * 2 + 1));
+ bin2hex(content.data(), content.length(), tmp);
+ dest.AppendFormat("%s", tmp.get());
+ }
+
+ if (children.getCount()) {
+ dest.Append("\n");
+
+ for (auto &p : children)
+ p->print(dest, level + 1);
+
+ for (int i = 0; i < level; i++)
+ dest.Append(" ");
+ }
+
+ dest.AppendFormat("</%s>\n", title.c_str());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WAReader class members
+
+bool WAReader::readAttributes(WANode *pNode, int count)
+{
+ if (count == 0)
+ return true;
+
+ for (int i = 0; i < count; i++) {
+ CMStringA name = readString(readInt8());
+ if (name.IsEmpty())
+ return false;
+
+ CMStringA value = readString(readInt8());
+ if (value.IsEmpty())
+ return false;
+
+ pNode->addAttr(name, value);
+ }
+ return true;
+}
+
+uint32_t WAReader::readInt20()
+{
+ if (m_limit - m_buf < 3)
+ return 0;
+
+ int ret = (int(m_buf[0] & 0x0F) << 16) + (int(m_buf[1]) << 8) + int(m_buf[2]);
+ m_buf += 3;
+ return ret;
+}
+
+uint32_t WAReader::readIntN(int n)
+{
+ if (m_limit - m_buf < n)
+ return 0;
+
+ uint32_t res = 0;
+ for (int i = 0; i < n; i++, m_buf++)
+ res = (res <<= 8) + *m_buf;
+ return res;
+}
+
+bool WAReader::readList(WANode *pParent, int tag)
+{
+ int size = readListSize(tag);
+ if (size == -1)
+ return false;
+
+ for (int i = 0; i < size; i++) {
+ WANode *pNew = readNode();
+ if (pNew == nullptr)
+ return false;
+ pParent->children.insert(pNew);
+ }
+
+ return true;
+}
+
+int WAReader::readListSize(int tag)
+{
+ switch (tag) {
+ case LIST_EMPTY:
+ return 0;
+ case LIST_8:
+ return readInt8();
+ case LIST_16:
+ return readInt16();
+ }
+ return -1;
+}
+
+WANode *WAReader::readNode()
+{
+ int listSize = readListSize(readInt8());
+ if (listSize == -1)
+ return nullptr;
+
+ int descrTag = readInt8();
+ if (descrTag == STREAM_END)
+ return nullptr;
+
+ CMStringA name = readString(descrTag);
+ if (name.IsEmpty())
+ return nullptr;
+
+ std::unique_ptr<WANode> ret(new WANode());
+ ret->title = name.c_str();
+
+ if (!readAttributes(ret.get(), (listSize - 1) >> 1))
+ return nullptr;
+
+ if ((listSize % 2) == 1)
+ return ret.release();
+
+ int size, tag = readInt8();
+ switch (tag) {
+ case LIST_EMPTY: case LIST_8: case LIST_16:
+ readList(ret.get(), tag);
+ break;
+
+ case BINARY_8:
+ size = readInt8();
+
+LBL_Binary:
+ if (m_limit - m_buf < size)
+ return false;
+
+ ret->content.assign((void *)m_buf, size);
+ m_buf += size;
+ break;
+
+ case BINARY_20:
+ size = readInt20();
+ goto LBL_Binary;
+
+ case BINARY_32:
+ size = readInt32();
+ goto LBL_Binary;
+
+ default:
+ CMStringA str = readString(tag);
+ ret->content.assign(str.GetBuffer(), str.GetLength() + 1);
+ }
+
+ return ret.release();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int unpackHex(int val)
+{
+ if (val < 0 || val > 15)
+ return -1;
+
+ return (val < 10) ? val + '0' : val - 10 + 'A';
+}
+
+static int unpackNibble(int val)
+{
+ if (val < 0 || val > 15)
+ return -1;
+
+ switch (val) {
+ case 10: return '-';
+ case 11: return '.';
+ case 15: return 0;
+ default: return '0' + val;
+ }
+}
+
+CMStringA WAReader::readPacked(int tag)
+{
+ int startByte = readInt8();
+ bool bTrim = false;
+ if (startByte & 0x80) {
+ startByte &= ~0x80;
+ bTrim = true;
+ }
+
+ CMStringA ret;
+ for (int i = 0; i < startByte; i++) {
+ BYTE b = readInt8();
+ int lower = (tag == NIBBLE_8) ? unpackNibble(b >> 4) : unpackHex(b >> 4);
+ if (lower == -1)
+ return "";
+
+ int higher = (tag == NIBBLE_8) ? unpackNibble(b & 0x0F) : unpackHex(b & 0x0F);
+ if (higher == -1)
+ return "";
+
+ ret.AppendChar(lower);
+ ret.AppendChar(higher);
+ }
+
+ if (bTrim && !ret.IsEmpty())
+ ret.Truncate(ret.GetLength() - 1);
+ return ret;
+}
+
+CMStringA WAReader::readString(int tag)
+{
+ if (tag >= 1 && tag < _countof(SingleByteTokens))
+ return SingleByteTokens[tag];
+
+ int idx;
+ switch (tag) {
+ case DICTIONARY_0:
+ idx = readInt8();
+ return (idx < _countof(dict0)) ? dict0[idx] : "";
+
+ case DICTIONARY_1:
+ idx = readInt8();
+ return (idx < _countof(dict1)) ? dict1[idx] : "";
+
+ case DICTIONARY_2:
+ idx = readInt8();
+ return (idx < _countof(dict2)) ? dict2[idx] : "";
+
+ case DICTIONARY_3:
+ idx = readInt8();
+ return (idx < _countof(dict3)) ? dict3[idx] : "";
+
+ case LIST_EMPTY:
+ return "";
+
+ case BINARY_8:
+ return readStringFromChars(readInt8());
+
+ case BINARY_20:
+ return readStringFromChars(readInt20());
+
+ case BINARY_32:
+ return readStringFromChars(readInt32());
+
+ case NIBBLE_8:
+ case HEX_8:
+ return readPacked(tag);
+
+ case AD_JID:
+ {
+ int agent = readInt8();
+ int device = readInt8();
+ WAJid jid(readString(readInt8()), "s.whatsapp.net", device, agent);
+ return jid.toString();
+ }
+
+ case JID_PAIR:
+ CMStringA s1 = readString(readInt8());
+ CMStringA s2 = readString(readInt8());
+ if (s1.IsEmpty() && s2.IsEmpty())
+ break;
+
+ return CMStringA(FORMAT, "%s@%s", s1.c_str(), s2.c_str());
+ }
+
+ // error
+ return "";
+}
+
+CMStringA WAReader::readStringFromChars(int size)
+{
+ if (m_limit - m_buf < size)
+ return "";
+
+ CMStringA ret((char *)m_buf, size);
+ m_buf += size;
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WAWriter class members
+
+void WAWriter::writeByte(uint8_t b)
+{
+ body.append(&b, 1);
+}
+
+void WAWriter::writeIntN(int value, int n)
+{
+ for (int i = n - 1; i >= 0; i--)
+ writeByte((value >> i * 8) & 0xFF);
+}
+
+void WAWriter::writeInt20(int value)
+{
+ writeByte((value >> 16) & 0xFF);
+ writeByte((value >> 8) & 0xFF);
+ writeByte(value & 0xFF);
+}
+
+void WAWriter::writeLength(int value)
+{
+ if (value >= (1 << 20)) {
+ writeByte(BINARY_32);
+ writeInt32(value);
+ }
+ else if (value >= 256) {
+ writeByte(BINARY_20);
+ writeInt20(value);
+ }
+ else {
+ writeByte(BINARY_8);
+ writeInt8(value);
+ }
+}
+
+void WAWriter::writeListSize(int length)
+{
+ if (length == 0)
+ writeByte(LIST_EMPTY);
+ else if (length < 256) {
+ writeByte(LIST_8);
+ writeInt8(length);
+ }
+ else {
+ writeByte(LIST_16);
+ writeInt16(length);
+ }
+}
+
+void WAWriter::writeNode(const WANode *pNode)
+{
+ // we never send zipped content
+ if (pNode->pParent == nullptr)
+ writeByte(0);
+
+ int numAttrs = (int)pNode->attrs.getCount();
+ int hasContent = pNode->content.length() != 0 || pNode->children.getCount() != 0;
+ writeListSize(2 * numAttrs + 1 + hasContent);
+
+ writeString(pNode->title.c_str());
+
+ // write attributes
+ for (auto &it : pNode->attrs) {
+ if (it->value.IsEmpty())
+ continue;
+
+ writeString(it->name.c_str());
+ writeString(it->value.c_str());
+ }
+
+ // write contents
+ if (pNode->content.length()) {
+ writeLength((int)pNode->content.length());
+ body.append(pNode->content.data(), pNode->content.length());
+ }
+ // write children
+ else if (pNode->children.getCount()) {
+ writeListSize(pNode->children.getCount());
+ for (auto &it : pNode->children)
+ writeNode(it);
+ }
+}
+
+bool WAWriter::writeToken(const char *str)
+{
+ for (auto &it : SingleByteTokens)
+ if (!strcmp(str, it)) {
+ writeByte(int(&it - SingleByteTokens));
+ return true;
+ }
+
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static BYTE packNibble(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ switch (c) {
+ case '-': return 10;
+ case '.': return 11;
+ case 0: return 15;
+ }
+
+ return -1;
+}
+
+static BYTE packHex(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+
+ if (c == 0)
+ return 15;
+
+ return -1;
+}
+
+static BYTE packPair(int type, char c1, char c2)
+{
+ BYTE b1 = (type == NIBBLE_8) ? packNibble(c1) : packHex(c1);
+ BYTE b2 = (type == NIBBLE_8) ? packNibble(c2) : packHex(c2);
+ return (b1 << 4) + b2;
+}
+
+static bool isNibble(const CMStringA &str)
+{
+ return strspn(str, "0123456789-.") == str.GetLength();
+}
+
+static bool isHex(const CMStringA &str)
+{
+ return strspn(str, "0123456789abcdefABCDEF") == str.GetLength();
+}
+
+void WAWriter::writePacked(const CMStringA &str, int tag)
+{
+ if (str.GetLength() > 254)
+ return;
+
+ writeByte(tag);
+
+ int len = str.GetLength() / 2;
+ BYTE firstByte = (str.GetLength() % 2) == 0 ? 0 : 0x81;
+ writeByte(firstByte + len);
+
+ const char *p = str;
+ for (int i = 0; i < len; i++, p += 2)
+ writeByte(packPair(tag, p[0], p[1]));
+
+ if (firstByte != 0)
+ writeByte(packPair(tag, p[0], 0));
+}
+
+void WAWriter::writeString(const char *str)
+{
+ if (writeToken(str))
+ return;
+
+ auto *pszDelimiter = strchr(str, '@');
+ if (pszDelimiter) {
+ WAJid jid(str);
+ if (jid.device || jid.agent) {
+ writeByte(AD_JID);
+ writeByte(jid.agent);
+ writeByte(jid.device);
+ writeString(jid.user);
+ }
+ else {
+ writeByte(JID_PAIR);
+
+ if (jid.user.IsEmpty()) // empty user
+ writeByte(LIST_EMPTY);
+ else
+ writeString(jid.user);
+
+ writeString(jid.server);
+ }
+ }
+ else {
+ CMStringA buf(str);
+ if (isNibble(buf))
+ writePacked(buf, NIBBLE_8);
+ else if (isHex(buf))
+ writePacked(buf, HEX_8);
+ else {
+ writeLength(buf.GetLength());
+ body.append(buf, buf.GetLength());
+ }
+ }
+}
diff --git a/protocols/YAMN/src/stdafx.cxx b/protocols/YAMN/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/protocols/YAMN/src/stdafx.cxx +++ b/protocols/YAMN/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/src/core/stdautoaway/src/autoaway.cpp b/src/core/stdautoaway/src/autoaway.cpp index f9a7fa8379..024487ed5a 100644 --- a/src/core/stdautoaway/src/autoaway.cpp +++ b/src/core/stdautoaway/src/autoaway.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdautoaway/src/idle.cpp b/src/core/stdautoaway/src/idle.cpp index aaabe0ddb2..b4faa31c4b 100644 --- a/src/core/stdautoaway/src/idle.cpp +++ b/src/core/stdautoaway/src/idle.cpp @@ -1,89 +1,89 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static UINT_PTR g_hTimer; - -int IdleOptInit(WPARAM wParam, LPARAM); - -static int IdleObject_IsUserIdle() -{ - if (g_plugin.bIdleMethod) { - uint32_t dwTick = Miranda_GetIdle(); - return GetTickCount() - dwTick > (g_plugin.iIdleTime1st * 60 * 1000); - } - - LASTINPUTINFO ii = { sizeof(ii) }; - if (GetLastInputInfo(&ii)) - return GetTickCount() - ii.dwTime > (g_plugin.iIdleTime1st * 60 * 1000); - - return FALSE; -} - -static void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD) -{ - if (g_hTimer != idEvent) - return; - - if (g_plugin.bIdleCheck && IdleObject_IsUserIdle()) - Idle_Enter(1); - - else if (g_plugin.bIdleOnSaver && IsScreenSaverRunning()) - Idle_Enter(2); - - else if (g_plugin.bIdleOnFullScr && IsFullScreen()) - Idle_Enter(3); - - else if (g_plugin.bIdleOnLock && IsWorkstationLocked()) - Idle_Enter(4); - - else if (g_plugin.bIdleOnTerminal && IsTerminalDisconnected()) - Idle_Enter(5); - - else // not idle - Idle_Enter(-1); -} - -void IdleObject_Create() -{ - g_hTimer = SetTimer(nullptr, 0, 2000, IdleTimer); -} - -void IdleObject_Destroy() -{ - KillTimer(nullptr, g_hTimer); -} - -void LoadIdleModule(void) -{ - IdleObject_Create(); - - HookEvent(ME_OPT_INITIALISE, IdleOptInit); -} - -void UnloadIdleModule() -{ - IdleObject_Destroy(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static UINT_PTR g_hTimer;
+
+int IdleOptInit(WPARAM wParam, LPARAM);
+
+static int IdleObject_IsUserIdle()
+{
+ if (g_plugin.bIdleMethod) {
+ uint32_t dwTick = Miranda_GetIdle();
+ return GetTickCount() - dwTick > (g_plugin.iIdleTime1st * 60 * 1000);
+ }
+
+ LASTINPUTINFO ii = { sizeof(ii) };
+ if (GetLastInputInfo(&ii))
+ return GetTickCount() - ii.dwTime > (g_plugin.iIdleTime1st * 60 * 1000);
+
+ return FALSE;
+}
+
+static void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD)
+{
+ if (g_hTimer != idEvent)
+ return;
+
+ if (g_plugin.bIdleCheck && IdleObject_IsUserIdle())
+ Idle_Enter(1);
+
+ else if (g_plugin.bIdleOnSaver && IsScreenSaverRunning())
+ Idle_Enter(2);
+
+ else if (g_plugin.bIdleOnFullScr && IsFullScreen())
+ Idle_Enter(3);
+
+ else if (g_plugin.bIdleOnLock && IsWorkstationLocked())
+ Idle_Enter(4);
+
+ else if (g_plugin.bIdleOnTerminal && IsTerminalDisconnected())
+ Idle_Enter(5);
+
+ else // not idle
+ Idle_Enter(-1);
+}
+
+void IdleObject_Create()
+{
+ g_hTimer = SetTimer(nullptr, 0, 2000, IdleTimer);
+}
+
+void IdleObject_Destroy()
+{
+ KillTimer(nullptr, g_hTimer);
+}
+
+void LoadIdleModule(void)
+{
+ IdleObject_Create();
+
+ HookEvent(ME_OPT_INITIALISE, IdleOptInit);
+}
+
+void UnloadIdleModule()
+{
+ IdleObject_Destroy();
+}
diff --git a/src/core/stdautoaway/src/main.cpp b/src/core/stdautoaway/src/main.cpp index 971fd166ff..1022f2a4d5 100644 --- a/src/core/stdautoaway/src/main.cpp +++ b/src/core/stdautoaway/src/main.cpp @@ -2,7 +2,7 @@ Standard auto away module for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdautoaway/src/options.cpp b/src/core/stdautoaway/src/options.cpp index 17a8f8381b..fed12a11f8 100644 --- a/src/core/stdautoaway/src/options.cpp +++ b/src/core/stdautoaway/src/options.cpp @@ -1,134 +1,134 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static const uint16_t aa_Status[] = { ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND }; - -int IdleGetStatusIndex(uint16_t status) -{ - for (int j = 0; j < _countof(aa_Status); j++) - if (aa_Status[j] == status) - return j; - - return 0; -} - -class COptionsDlg : public CDlgBase -{ - CCtrlEdit edt1sttime; - CCtrlSpin spinIdle; - CCtrlCombo cmbAAStatus; - CCtrlCheck chkShort, chkOnWindows, chkOnMiranda, chkScreenSaver, chkFullScreen, chkLocked; - CCtrlCheck chkPrivate, chkStatusLock, chkTerminal, chkSoundsOff, chkShortIdle; - - void ShowHide() - { - BOOL bChecked = chkShort.GetState(); - chkOnWindows.Enable(bChecked); - chkOnMiranda.Enable(bChecked); - edt1sttime.Enable(bChecked); - - bChecked = chkShortIdle.GetState(); - cmbAAStatus.Enable(bChecked); - chkStatusLock.Enable(bChecked); - } - -public: - COptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_IDLE), - edt1sttime(this, IDC_IDLE1STTIME), - spinIdle(this, IDC_IDLESPIN, 60, 1), - cmbAAStatus(this, IDC_AASTATUS), - chkShort(this, IDC_IDLESHORT), - chkLocked(this, IDC_LOCKED), - chkPrivate(this, IDC_IDLEPRIVATE), - chkTerminal(this, IDC_IDLETERMINAL), - chkOnWindows(this, IDC_IDLEONWINDOWS), - chkSoundsOff(this, IDC_IDLESOUNDSOFF), - chkOnMiranda(this, IDC_IDLEONMIRANDA), - chkShortIdle(this, IDC_AASHORTIDLE), - chkStatusLock(this, IDC_IDLESTATUSLOCK), - chkFullScreen(this, IDC_FULLSCREEN), - chkScreenSaver(this, IDC_SCREENSAVER) - { - CreateLink(chkShort, g_plugin.bIdleCheck); - CreateLink(chkLocked, g_plugin.bIdleOnLock); - CreateLink(chkPrivate, g_plugin.bIdlePrivate); - CreateLink(chkTerminal, g_plugin.bIdleOnTerminal); - CreateLink(chkShortIdle, g_plugin.bAAEnable); - CreateLink(chkOnMiranda, g_plugin.bIdleMethod); - CreateLink(chkSoundsOff, g_plugin.bIdleSoundsOff); - CreateLink(chkStatusLock, g_plugin.bIdleStatusLock); - CreateLink(chkFullScreen, g_plugin.bIdleOnFullScr); - CreateLink(chkScreenSaver, g_plugin.bIdleOnSaver); - - chkShortIdle.OnChange = chkShort.OnChange = Callback(this, &COptionsDlg::onChange); - } - - bool OnInitDialog() override - { - chkOnWindows.SetState(!g_plugin.bIdleMethod); - - spinIdle.SetPosition(g_plugin.iIdleTime1st); - - for (auto &it : aa_Status) - cmbAAStatus.AddString(Clist_GetStatusModeDescription(it, 0)); - cmbAAStatus.SetCurSel(IdleGetStatusIndex(g_plugin.bAAStatus)); - - ShowHide(); - return true; - } - - bool OnApply() override - { - g_plugin.iIdleTime1st = spinIdle.GetPosition(); - - int curSel = cmbAAStatus.GetCurSel(); - if (curSel != CB_ERR) - g_plugin.bAAStatus = aa_Status[curSel]; - - // destroy any current idle and reset settings. - IdleObject_Destroy(); - IdleObject_Create(); - return true; - } - - void onChange(CCtrlCheck*) - { - ShowHide(); - } -}; - -int IdleOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 100000000; - odp.szGroup.a = LPGEN("Status"); - odp.szTitle.a = LPGEN("Idle"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new COptionsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static const uint16_t aa_Status[] = { ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND };
+
+int IdleGetStatusIndex(uint16_t status)
+{
+ for (int j = 0; j < _countof(aa_Status); j++)
+ if (aa_Status[j] == status)
+ return j;
+
+ return 0;
+}
+
+class COptionsDlg : public CDlgBase
+{
+ CCtrlEdit edt1sttime;
+ CCtrlSpin spinIdle;
+ CCtrlCombo cmbAAStatus;
+ CCtrlCheck chkShort, chkOnWindows, chkOnMiranda, chkScreenSaver, chkFullScreen, chkLocked;
+ CCtrlCheck chkPrivate, chkStatusLock, chkTerminal, chkSoundsOff, chkShortIdle;
+
+ void ShowHide()
+ {
+ BOOL bChecked = chkShort.GetState();
+ chkOnWindows.Enable(bChecked);
+ chkOnMiranda.Enable(bChecked);
+ edt1sttime.Enable(bChecked);
+
+ bChecked = chkShortIdle.GetState();
+ cmbAAStatus.Enable(bChecked);
+ chkStatusLock.Enable(bChecked);
+ }
+
+public:
+ COptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_IDLE),
+ edt1sttime(this, IDC_IDLE1STTIME),
+ spinIdle(this, IDC_IDLESPIN, 60, 1),
+ cmbAAStatus(this, IDC_AASTATUS),
+ chkShort(this, IDC_IDLESHORT),
+ chkLocked(this, IDC_LOCKED),
+ chkPrivate(this, IDC_IDLEPRIVATE),
+ chkTerminal(this, IDC_IDLETERMINAL),
+ chkOnWindows(this, IDC_IDLEONWINDOWS),
+ chkSoundsOff(this, IDC_IDLESOUNDSOFF),
+ chkOnMiranda(this, IDC_IDLEONMIRANDA),
+ chkShortIdle(this, IDC_AASHORTIDLE),
+ chkStatusLock(this, IDC_IDLESTATUSLOCK),
+ chkFullScreen(this, IDC_FULLSCREEN),
+ chkScreenSaver(this, IDC_SCREENSAVER)
+ {
+ CreateLink(chkShort, g_plugin.bIdleCheck);
+ CreateLink(chkLocked, g_plugin.bIdleOnLock);
+ CreateLink(chkPrivate, g_plugin.bIdlePrivate);
+ CreateLink(chkTerminal, g_plugin.bIdleOnTerminal);
+ CreateLink(chkShortIdle, g_plugin.bAAEnable);
+ CreateLink(chkOnMiranda, g_plugin.bIdleMethod);
+ CreateLink(chkSoundsOff, g_plugin.bIdleSoundsOff);
+ CreateLink(chkStatusLock, g_plugin.bIdleStatusLock);
+ CreateLink(chkFullScreen, g_plugin.bIdleOnFullScr);
+ CreateLink(chkScreenSaver, g_plugin.bIdleOnSaver);
+
+ chkShortIdle.OnChange = chkShort.OnChange = Callback(this, &COptionsDlg::onChange);
+ }
+
+ bool OnInitDialog() override
+ {
+ chkOnWindows.SetState(!g_plugin.bIdleMethod);
+
+ spinIdle.SetPosition(g_plugin.iIdleTime1st);
+
+ for (auto &it : aa_Status)
+ cmbAAStatus.AddString(Clist_GetStatusModeDescription(it, 0));
+ cmbAAStatus.SetCurSel(IdleGetStatusIndex(g_plugin.bAAStatus));
+
+ ShowHide();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ g_plugin.iIdleTime1st = spinIdle.GetPosition();
+
+ int curSel = cmbAAStatus.GetCurSel();
+ if (curSel != CB_ERR)
+ g_plugin.bAAStatus = aa_Status[curSel];
+
+ // destroy any current idle and reset settings.
+ IdleObject_Destroy();
+ IdleObject_Create();
+ return true;
+ }
+
+ void onChange(CCtrlCheck*)
+ {
+ ShowHide();
+ }
+};
+
+int IdleOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 100000000;
+ odp.szGroup.a = LPGEN("Status");
+ odp.szTitle.a = LPGEN("Idle");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new COptionsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/core/stdautoaway/src/stdafx.h b/src/core/stdautoaway/src/stdafx.h index be6446162a..aed3748da5 100644 --- a/src/core/stdautoaway/src/stdafx.h +++ b/src/core/stdautoaway/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdautoaway/src/version.h b/src/core/stdautoaway/src/version.h index 50fcd17758..e85f99715c 100644 --- a/src/core/stdautoaway/src/version.h +++ b/src/core/stdautoaway/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for away state processing."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdAutoAway"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdaway/src/awaymsg.cpp b/src/core/stdaway/src/awaymsg.cpp index 94f097fbe1..391a61383e 100644 --- a/src/core/stdaway/src/awaymsg.cpp +++ b/src/core/stdaway/src/awaymsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdaway/src/main.cpp b/src/core/stdaway/src/main.cpp index 1dfab72a34..bf275c259a 100644 --- a/src/core/stdaway/src/main.cpp +++ b/src/core/stdaway/src/main.cpp @@ -2,7 +2,7 @@ Standard away message processing module for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdaway/src/options.cpp b/src/core/stdaway/src/options.cpp index 64943461d9..1287a623d3 100644 --- a/src/core/stdaway/src/options.cpp +++ b/src/core/stdaway/src/options.cpp @@ -1,140 +1,140 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static const int statusModes[] = -{ - ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND, - ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_IDLE -}; - -class CAwayMsgOptsDlg : public CDlgBase -{ - struct - { - int ignore; - int noDialog; - int usePrevious; - wchar_t msg[1024]; - } - m_info[_countof(statusModes)]; - - int oldPage = -1; - - CCtrlEdit etdMsg; - CCtrlCombo cmbStatus; - CCtrlCheck chkDontReply, chkUsePrev, chkUseSpecific, chkNoDialog; - -public: - CAwayMsgOptsDlg() : - CDlgBase(g_plugin, IDD_OPT_AWAYMSG), - etdMsg(this, IDC_MSG), - cmbStatus(this, IDC_STATUS), - chkUsePrev(this, IDC_USEPREVIOUS), - chkNoDialog(this, IDC_NODIALOG), - chkDontReply(this, IDC_DONTREPLY), - chkUseSpecific(this, IDC_USESPECIFIC) - { - chkUsePrev.OnChange = chkDontReply.OnChange = chkUseSpecific.OnChange = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); - cmbStatus.OnSelChanged = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); - } - - bool OnInitDialog() override - { - for (auto &it : statusModes) { - if (!(protoModeMsgFlags & Proto_Status2Flag(it))) - continue; - - int j = cmbStatus.AddString(Clist_GetStatusModeDescription(it, 0), it); - m_info[j].ignore = g_plugin.GetStatusModeByte(it, "Ignore"); - m_info[j].noDialog = g_plugin.GetStatusModeByte(it, "NoDlg", true); - m_info[j].usePrevious = g_plugin.GetStatusModeByte(it, "UsePrev"); - - DBVARIANT dbv; - if (g_plugin.getWString(StatusModeToDbSetting(it, "Default"), &dbv)) - if (g_plugin.getWString(StatusModeToDbSetting(it, "Msg"), &dbv)) - dbv.pwszVal = mir_wstrdup(GetDefaultMessage(it)); - mir_wstrcpy(m_info[j].msg, dbv.pwszVal); - mir_free(dbv.pwszVal); - } - - cmbStatus.SetCurSel(0); - onSelChange_Status(0); - return true; - } - - bool OnApply() override - { - onSelChange_Status(0); - - for (int i = cmbStatus.GetCount() - 1; i >= 0; i--) { - int status = cmbStatus.GetItemData(i); - g_plugin.SetStatusModeByte(status, "Ignore", (uint8_t)m_info[i].ignore); - g_plugin.SetStatusModeByte(status, "NoDlg", (uint8_t)m_info[i].noDialog); - g_plugin.SetStatusModeByte(status, "UsePrev", (uint8_t)m_info[i].usePrevious); - g_plugin.setWString(StatusModeToDbSetting(status, "Default"), m_info[i].msg); - } - return true; - } - - void onSelChange_Status(CCtrlCombo*) - { - if (oldPage != -1) { - m_info[oldPage].ignore = chkDontReply.GetState(); - m_info[oldPage].noDialog = chkNoDialog.GetState(); - m_info[oldPage].usePrevious = chkUsePrev.GetState(); - etdMsg.GetText(m_info[oldPage].msg, _countof(m_info[oldPage].msg)); - } - - int i = cmbStatus.GetCurSel(); - chkDontReply.SetState(i < 0 ? 0 : m_info[i].ignore); - chkNoDialog.SetState(i < 0 ? 0 : m_info[i].noDialog); - chkUsePrev.SetState(i < 0 ? 0 : m_info[i].usePrevious); - chkUseSpecific.SetState(i < 0 ? 0 : !m_info[i].usePrevious); - - etdMsg.SetText(i < 0 ? L"" : m_info[i].msg); - - chkNoDialog.Enable(i < 0 ? 0 : !m_info[i].ignore); - chkUsePrev.Enable(i < 0 ? 0 : !m_info[i].ignore); - chkUseSpecific.Enable(i < 0 ? 0 : !m_info[i].ignore); - etdMsg.Enable(i < 0 ? 0 : !(m_info[i].ignore || m_info[i].usePrevious)); - oldPage = i; - } -}; - -int AwayMsgOptInitialise(WPARAM wParam, LPARAM) -{ - if (protoModeMsgFlags == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = 870000000; - odp.szTitle.a = LPGEN("Status messages"); - odp.szGroup.a = LPGEN("Status"); - odp.pDialog = new CAwayMsgOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static const int statusModes[] =
+{
+ ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND,
+ ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_IDLE
+};
+
+class CAwayMsgOptsDlg : public CDlgBase
+{
+ struct
+ {
+ int ignore;
+ int noDialog;
+ int usePrevious;
+ wchar_t msg[1024];
+ }
+ m_info[_countof(statusModes)];
+
+ int oldPage = -1;
+
+ CCtrlEdit etdMsg;
+ CCtrlCombo cmbStatus;
+ CCtrlCheck chkDontReply, chkUsePrev, chkUseSpecific, chkNoDialog;
+
+public:
+ CAwayMsgOptsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_AWAYMSG),
+ etdMsg(this, IDC_MSG),
+ cmbStatus(this, IDC_STATUS),
+ chkUsePrev(this, IDC_USEPREVIOUS),
+ chkNoDialog(this, IDC_NODIALOG),
+ chkDontReply(this, IDC_DONTREPLY),
+ chkUseSpecific(this, IDC_USESPECIFIC)
+ {
+ chkUsePrev.OnChange = chkDontReply.OnChange = chkUseSpecific.OnChange = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status);
+ cmbStatus.OnSelChanged = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status);
+ }
+
+ bool OnInitDialog() override
+ {
+ for (auto &it : statusModes) {
+ if (!(protoModeMsgFlags & Proto_Status2Flag(it)))
+ continue;
+
+ int j = cmbStatus.AddString(Clist_GetStatusModeDescription(it, 0), it);
+ m_info[j].ignore = g_plugin.GetStatusModeByte(it, "Ignore");
+ m_info[j].noDialog = g_plugin.GetStatusModeByte(it, "NoDlg", true);
+ m_info[j].usePrevious = g_plugin.GetStatusModeByte(it, "UsePrev");
+
+ DBVARIANT dbv;
+ if (g_plugin.getWString(StatusModeToDbSetting(it, "Default"), &dbv))
+ if (g_plugin.getWString(StatusModeToDbSetting(it, "Msg"), &dbv))
+ dbv.pwszVal = mir_wstrdup(GetDefaultMessage(it));
+ mir_wstrcpy(m_info[j].msg, dbv.pwszVal);
+ mir_free(dbv.pwszVal);
+ }
+
+ cmbStatus.SetCurSel(0);
+ onSelChange_Status(0);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ onSelChange_Status(0);
+
+ for (int i = cmbStatus.GetCount() - 1; i >= 0; i--) {
+ int status = cmbStatus.GetItemData(i);
+ g_plugin.SetStatusModeByte(status, "Ignore", (uint8_t)m_info[i].ignore);
+ g_plugin.SetStatusModeByte(status, "NoDlg", (uint8_t)m_info[i].noDialog);
+ g_plugin.SetStatusModeByte(status, "UsePrev", (uint8_t)m_info[i].usePrevious);
+ g_plugin.setWString(StatusModeToDbSetting(status, "Default"), m_info[i].msg);
+ }
+ return true;
+ }
+
+ void onSelChange_Status(CCtrlCombo*)
+ {
+ if (oldPage != -1) {
+ m_info[oldPage].ignore = chkDontReply.GetState();
+ m_info[oldPage].noDialog = chkNoDialog.GetState();
+ m_info[oldPage].usePrevious = chkUsePrev.GetState();
+ etdMsg.GetText(m_info[oldPage].msg, _countof(m_info[oldPage].msg));
+ }
+
+ int i = cmbStatus.GetCurSel();
+ chkDontReply.SetState(i < 0 ? 0 : m_info[i].ignore);
+ chkNoDialog.SetState(i < 0 ? 0 : m_info[i].noDialog);
+ chkUsePrev.SetState(i < 0 ? 0 : m_info[i].usePrevious);
+ chkUseSpecific.SetState(i < 0 ? 0 : !m_info[i].usePrevious);
+
+ etdMsg.SetText(i < 0 ? L"" : m_info[i].msg);
+
+ chkNoDialog.Enable(i < 0 ? 0 : !m_info[i].ignore);
+ chkUsePrev.Enable(i < 0 ? 0 : !m_info[i].ignore);
+ chkUseSpecific.Enable(i < 0 ? 0 : !m_info[i].ignore);
+ etdMsg.Enable(i < 0 ? 0 : !(m_info[i].ignore || m_info[i].usePrevious));
+ oldPage = i;
+ }
+};
+
+int AwayMsgOptInitialise(WPARAM wParam, LPARAM)
+{
+ if (protoModeMsgFlags == 0)
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = 870000000;
+ odp.szTitle.a = LPGEN("Status messages");
+ odp.szGroup.a = LPGEN("Status");
+ odp.pDialog = new CAwayMsgOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/core/stdaway/src/sendmsg.cpp b/src/core/stdaway/src/sendmsg.cpp index 1eaaac5cc4..dff644a928 100644 --- a/src/core/stdaway/src/sendmsg.cpp +++ b/src/core/stdaway/src/sendmsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdaway/src/stdafx.h b/src/core/stdaway/src/stdafx.h index 4cd3dd5b24..83d5a859c9 100644 --- a/src/core/stdaway/src/stdafx.h +++ b/src/core/stdaway/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdaway/src/version.h b/src/core/stdaway/src/version.h index 1cbc94e44c..03131a5179 100644 --- a/src/core/stdaway/src/version.h +++ b/src/core/stdaway/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for the away messages processing."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdAway"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdclist/src/clc.h b/src/core/stdclist/src/clc.h index 9ecb9767af..c5a6581b38 100644 --- a/src/core/stdclist/src/clc.h +++ b/src/core/stdclist/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clcfonts.cpp b/src/core/stdclist/src/clcfonts.cpp index c5c4af1a9a..e435026332 100644 --- a/src/core/stdclist/src/clcfonts.cpp +++ b/src/core/stdclist/src/clcfonts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clcopts.cpp b/src/core/stdclist/src/clcopts.cpp index 688fba0c86..077cd80ca0 100644 --- a/src/core/stdclist/src/clcopts.cpp +++ b/src/core/stdclist/src/clcopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clcpaint.cpp b/src/core/stdclist/src/clcpaint.cpp index 9bf6af7581..d2e59b1878 100644 --- a/src/core/stdclist/src/clcpaint.cpp +++ b/src/core/stdclist/src/clcpaint.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clistmenus.cpp b/src/core/stdclist/src/clistmenus.cpp index 7d10154fde..57da5b4f22 100644 --- a/src/core/stdclist/src/clistmenus.cpp +++ b/src/core/stdclist/src/clistmenus.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clistopts.cpp b/src/core/stdclist/src/clistopts.cpp index 29683b6506..3a6f435e24 100644 --- a/src/core/stdclist/src/clistopts.cpp +++ b/src/core/stdclist/src/clistopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/cluiopts.cpp b/src/core/stdclist/src/cluiopts.cpp index 785c0605b1..4816b59b7f 100644 --- a/src/core/stdclist/src/cluiopts.cpp +++ b/src/core/stdclist/src/cluiopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/contact.cpp b/src/core/stdclist/src/contact.cpp index daba1cdc61..cf9ba02cb7 100644 --- a/src/core/stdclist/src/contact.cpp +++ b/src/core/stdclist/src/contact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/init.cpp b/src/core/stdclist/src/init.cpp index 3ca412c5f7..f379863284 100644 --- a/src/core/stdclist/src/init.cpp +++ b/src/core/stdclist/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/stdafx.cxx b/src/core/stdclist/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/src/core/stdclist/src/stdafx.cxx +++ b/src/core/stdclist/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/core/stdclist/src/stdafx.h b/src/core/stdclist/src/stdafx.h index d50af5e471..62b233fe55 100644 --- a/src/core/stdclist/src/stdafx.h +++ b/src/core/stdclist/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/version.h b/src/core/stdclist/src/version.h index 1b178cd258..a652e79523 100644 --- a/src/core/stdclist/src/version.h +++ b/src/core/stdclist/src/version.h @@ -9,4 +9,4 @@ #define __DESCRIPTION "Core module for displaying contacts."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdClist"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdcrypt/src/encrypt.cpp b/src/core/stdcrypt/src/encrypt.cpp index e9689aa374..9d605848a2 100644 --- a/src/core/stdcrypt/src/encrypt.cpp +++ b/src/core/stdcrypt/src/encrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdcrypt/src/main.cpp b/src/core/stdcrypt/src/main.cpp index bc53c00f54..e1dc9218a6 100644 --- a/src/core/stdcrypt/src/main.cpp +++ b/src/core/stdcrypt/src/main.cpp @@ -1,7 +1,7 @@ /*
Standard encryption plugin for Miranda NG
-Copyright (C) 2012-22 George Hazan
+Copyright (C) 2012-23 George Hazan
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
diff --git a/src/core/stdcrypt/src/stdafx.h b/src/core/stdcrypt/src/stdafx.h index 1ea23e6c16..4b6c1e7319 100644 --- a/src/core/stdcrypt/src/stdafx.h +++ b/src/core/stdcrypt/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdcrypt/src/stdcrypt.h b/src/core/stdcrypt/src/stdcrypt.h index 791e827d1a..3966a81631 100644 --- a/src/core/stdcrypt/src/stdcrypt.h +++ b/src/core/stdcrypt/src/stdcrypt.h @@ -1,7 +1,7 @@ /*
Standard encryption plugin for Miranda NG
-Copyright (C) 2012-22 George Hazan
+Copyright (C) 2012-23 George Hazan
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
diff --git a/src/core/stdcrypt/src/utils.cpp b/src/core/stdcrypt/src/utils.cpp index fd5b3cff22..a3279e805e 100644 --- a/src/core/stdcrypt/src/utils.cpp +++ b/src/core/stdcrypt/src/utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdcrypt/src/version.h b/src/core/stdcrypt/src/version.h index 6f6d4f9c96..30de347877 100644 --- a/src/core/stdcrypt/src/version.h +++ b/src/core/stdcrypt/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for encryption."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdCrypt"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdemail/src/email.cpp b/src/core/stdemail/src/email.cpp index b6c68d672f..0f40657fff 100644 --- a/src/core/stdemail/src/email.cpp +++ b/src/core/stdemail/src/email.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdemail/src/main.cpp b/src/core/stdemail/src/main.cpp index 344e9501a9..19aaf95380 100644 --- a/src/core/stdemail/src/main.cpp +++ b/src/core/stdemail/src/main.cpp @@ -2,7 +2,7 @@ Standard e-mail urls launcher for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdemail/src/stdafx.h b/src/core/stdemail/src/stdafx.h index 8214c32bfe..4520b2f62f 100644 --- a/src/core/stdemail/src/stdafx.h +++ b/src/core/stdemail/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdemail/src/version.h b/src/core/stdemail/src/version.h index a158424f02..dd12d45a66 100644 --- a/src/core/stdemail/src/version.h +++ b/src/core/stdemail/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for e-mail urls handling."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdEmail"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdfile/src/file.cpp b/src/core/stdfile/src/file.cpp index 8dcb922619..234f84404f 100644 --- a/src/core/stdfile/src/file.cpp +++ b/src/core/stdfile/src/file.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/file.h b/src/core/stdfile/src/file.h index b89671ab8a..2bdddb04e5 100644 --- a/src/core/stdfile/src/file.h +++ b/src/core/stdfile/src/file.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/fileexistsdlg.cpp b/src/core/stdfile/src/fileexistsdlg.cpp index bd715cfa24..c2e4f6dfa8 100644 --- a/src/core/stdfile/src/fileexistsdlg.cpp +++ b/src/core/stdfile/src/fileexistsdlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/fileopts.cpp b/src/core/stdfile/src/fileopts.cpp index f5385174e1..f0d9e1bda2 100644 --- a/src/core/stdfile/src/fileopts.cpp +++ b/src/core/stdfile/src/fileopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/filerecvdlg.cpp b/src/core/stdfile/src/filerecvdlg.cpp index 54aefe44c5..7a364e210e 100644 --- a/src/core/stdfile/src/filerecvdlg.cpp +++ b/src/core/stdfile/src/filerecvdlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/filesenddlg.cpp b/src/core/stdfile/src/filesenddlg.cpp index 017725e89d..024cf68129 100644 --- a/src/core/stdfile/src/filesenddlg.cpp +++ b/src/core/stdfile/src/filesenddlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/filexferdlg.cpp b/src/core/stdfile/src/filexferdlg.cpp index bcd6c16c54..32ef89ecf3 100644 --- a/src/core/stdfile/src/filexferdlg.cpp +++ b/src/core/stdfile/src/filexferdlg.cpp @@ -1,765 +1,765 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include <io.h> -#include "file.h" - -static int CheckVirusScanned(HWND hwnd, FileDlgData *dat, int i) -{ - if (dat->send) return 1; - if (dat->fileVirusScanned == nullptr) return 0; - if (dat->fileVirusScanned[i]) return 1; - if (g_plugin.getByte("WarnBeforeOpening", 1) == 0) return 1; - - return IDYES == MessageBox(hwnd, - TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"), - TranslateT("File received"), - MB_YESNO | MB_DEFBUTTON2); -} - -#define M_VIRUSSCANDONE (WM_USER+100) -struct virusscanthreadstartinfo -{ - wchar_t *szFile; - int returnCode; - HWND hwndReply; -}; - -wchar_t* PFTS_StringToTchar(int flags, const wchar_t *s) -{ - if (flags & PFTS_UTF) - return mir_utf8decodeW((char*)s); - if (flags & PFTS_UNICODE) - return mir_wstrdup(s); - return mir_a2u((char*)s); -} - -int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS *ft, const wchar_t *s, wchar_t *r) -{ - if (ft->flags & PFTS_UTF) { - wchar_t *ts = mir_utf8decodeW((char*)s); - int res = mir_wstrcmp(ts, r); - mir_free(ts); - return res; - } - if (ft->flags & PFTS_UNICODE) - return mir_wstrcmp(s, r); - - wchar_t *ts = mir_a2u((char*)s); - int res = mir_wstrcmp(ts, r); - mir_free(ts); - return res; -} - -static void SetOpenFileButtonStyle(HWND hwndButton, int enabled) -{ - EnableWindow(hwndButton, enabled); -} - -void FillSendData(FileDlgData *dat, DBEVENTINFO &dbei) -{ - dbei.szModule = Proto_GetBaseAccountName(dat->hContact); - dbei.eventType = EVENTTYPE_FILE; - dbei.flags = DBEF_SENT; - dbei.timestamp = time(0); - char *szFileNames = mir_utf8encodeW(dat->szFilenames), *szMsg = mir_utf8encodeW(dat->szMsg); - dbei.flags |= DBEF_UTF; - - dbei.cbBlob = int(sizeof(uint32_t) + mir_strlen(szFileNames) + mir_strlen(szMsg) + 2); - dbei.pBlob = (uint8_t*)mir_alloc(dbei.cbBlob); - *(PDWORD)dbei.pBlob = 0; - mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t), szFileNames); - mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t) + mir_strlen(szFileNames) + 1, szMsg); - - mir_free(szFileNames), mir_free(szMsg); -} - -static void __cdecl RunVirusScannerThread(virusscanthreadstartinfo *info) -{ - DBVARIANT dbv; - if (!g_plugin.getWString("ScanCmdLine", &dbv)) { - if (dbv.pwszVal[0]) { - STARTUPINFO si = { 0 }; - si.cb = sizeof(si); - wchar_t *pszReplace = wcsstr(dbv.pwszVal, L"%f"); - wchar_t szCmdLine[768]; - if (pszReplace) { - if (info->szFile[mir_wstrlen(info->szFile) - 1] == '\\') - info->szFile[mir_wstrlen(info->szFile) - 1] = '\0'; - *pszReplace = 0; - mir_snwprintf(szCmdLine, L"%s\"%s\"%s", dbv.pwszVal, info->szFile, pszReplace + 2); - } - else - wcsncpy_s(szCmdLine, dbv.pwszVal, _TRUNCATE); - - PROCESS_INFORMATION pi; - if (CreateProcess(nullptr, szCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { - if (WaitForSingleObject(pi.hProcess, 3600 * 1000) == WAIT_OBJECT_0) - PostMessage(info->hwndReply, M_VIRUSSCANDONE, info->returnCode, 0); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - } - db_free(&dbv); - } - mir_free(info->szFile); - mir_free(info); -} - -static void SetFilenameControls(HWND hwndDlg, FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts) -{ - wchar_t msg[MAX_PATH]; - wchar_t *fnbuf = nullptr, *fn = nullptr; - SHFILEINFO shfi = {}; - - if (fts->szCurrentFile.w) { - fnbuf = mir_wstrdup(fts->szCurrentFile.w); - if ((fn = wcsrchr(fnbuf, '\\')) == nullptr) - fn = fnbuf; - else fn++; - } - - if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = nullptr; - - if (fn && (fts->totalFiles > 1)) { - mir_snwprintf(msg, L"%s: %s (%d %s %d)", Clist_GetContactDisplayName(fts->hContact), fn, fts->currentFileNumber + 1, TranslateT("of"), fts->totalFiles); - - SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIcon = shfi.hIcon; - } - else if (fn) { - mir_snwprintf(msg, L"%s: %s", Clist_GetContactDisplayName(fts->hContact), fn); - - SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIcon = shfi.hIcon; - } - else { - mir_wstrncpy(msg, Clist_GetContactDisplayName(fts->hContact), _countof(msg)); - HICON hIcon = Skin_LoadIcon(SKINICON_OTHER_DOWNARROW); - dat->hIcon = CopyIcon(hIcon); - IcoLib_ReleaseIcon(hIcon, NULL); - } - - mir_free(fnbuf); - - SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon); - SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg); -} - -enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN }; -static void SetFtStatus(HWND hwndDlg, wchar_t *text, int mode) -{ - SetDlgItemText(hwndDlg, IDC_STATUS, TranslateW(text)); - SetDlgItemText(hwndDlg, IDC_TRANSFERCOMPLETED, TranslateW(text)); - - ShowWindow(GetDlgItem(hwndDlg, IDC_STATUS), (mode == FTS_TEXT) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_FILEICON), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); -} - -static void HideProgressControls(HWND hwndDlg) -{ - RECT rc; - char buf[64]; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc); - MapWindowPoints(nullptr, hwndDlg, (LPPOINT)&rc, 2); - SetWindowPos(hwndDlg, nullptr, 0, 0, 100, rc.bottom + 3, SWP_NOMOVE | SWP_NOZORDER); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE); - - _strtime(buf); - SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf); - - PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg); -} - -static int FileTransferDlgResizer(HWND, LPARAM param, UTILRESIZECONTROL *urc) -{ - auto *dat = (FileDlgData *)param; - - switch (urc->wId) { - case IDC_CONTACTNAME: - case IDC_STATUS: - case IDC_ALLFILESPROGRESS: - case IDC_TRANSFERCOMPLETED: - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - case IDC_FRAME: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - case IDC_ALLPRECENTS: - case IDCANCEL: - case IDC_OPENFILE: - case IDC_OPENFOLDER: - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - case IDC_ALLTRANSFERRED: - if (dat->waitingForAcceptance) - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3; - return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; - - case IDC_ALLSPEED: - if (dat->waitingForAcceptance) - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx; - urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3; - return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - FileDlgData *dat = (FileDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - dat = (FileDlgData *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - dat->hNotifyEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_RECVEVENT); - dat->transferStatus.currentFileNumber = -1; - if (dat->send) { - if (db_mc_isMeta(dat->hContact)) - dat->hContact = db_mc_getMostOnline(dat->hContact); - dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILE, (WPARAM)dat->szMsg, (LPARAM)dat->files); - SetFtStatus(hwndDlg, LPGENW("Request sent, waiting for acceptance..."), FTS_TEXT); - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - dat->waitingForAcceptance = 1; - // hide "open" button since it may cause potential access violations... - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE); - } - else { //recv - CreateDirectoryTreeW(dat->szSavePath); - dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILEALLOW, (WPARAM)dat->fs, (LPARAM)dat->szSavePath); - dat->transferStatus.szWorkingDir.w = mir_wstrdup(dat->szSavePath); - if (!Contact::OnList(dat->hContact)) - dat->resumeBehaviour = FILERESUME_ASK; - else - dat->resumeBehaviour = g_plugin.getByte("IfExists", FILERESUME_ASK); - SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); - } - - /* check we actually got an fs handle back from the protocol */ - if (!dat->fs) { - SetFtStatus(hwndDlg, LPGENW("Unable to initiate transfer."), FTS_TEXT); - dat->waitingForAcceptance = 0; - } - { - LOGFONT lf; - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); - GetObject(hFont, sizeof(lf), &lf); - lf.lfWeight = FW_BOLD; - hFont = CreateFontIndirect(&lf); - SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_SETFONT, (WPARAM)hFont, 0); - - SHFILEINFO shfi = {}; - SHGetFileInfo(L"", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIconFolder = shfi.hIcon; - } - dat->hIcon = nullptr; - { - char *szProto = Proto_GetBaseAccountName(dat->hContact); - uint16_t status = db_get_w(dat->hContact, szProto, "Status", ID_STATUS_ONLINE); - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadProtoIcon(szProto, status)); - } - - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0); - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, TRUE, 0); - - Button_SetSkin_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open...")); - SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, TRUE, 0); - - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder); - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0); - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, TRUE, 0); - - Button_SetSkin_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel")); - - SetDlgItemText(hwndDlg, IDC_CONTACTNAME, Clist_GetContactDisplayName(dat->hContact)); - - if (!dat->waitingForAcceptance) - SetTimer(hwndDlg, 1, 1000, nullptr); - return TRUE; - - case WM_TIMER: - memmove(dat->bytesRecvedHistory + 1, dat->bytesRecvedHistory, sizeof(dat->bytesRecvedHistory) - sizeof(dat->bytesRecvedHistory[0])); - dat->bytesRecvedHistory[0] = dat->transferStatus.totalProgress; - if (dat->bytesRecvedHistorySize < _countof(dat->bytesRecvedHistory)) - dat->bytesRecvedHistorySize++; - - wchar_t szSpeed[32], szTime[32], szDisplay[96]; - SYSTEMTIME st; - ULARGE_INTEGER li; - FILETIME ft; - - GetSensiblyFormattedSize((dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) / dat->bytesRecvedHistorySize, szSpeed, _countof(szSpeed), 0, 1, NULL); - if (dat->bytesRecvedHistory[0] == dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) - mir_wstrcpy(szTime, L"??:??:??"); - else { - li.QuadPart = 10000000ll * (dat->transferStatus.currentFileSize - dat->transferStatus.currentFileProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); - ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, &st); - GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); - } - if (dat->bytesRecvedHistory[0] != dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) { - li.QuadPart = 10000000ll * (dat->transferStatus.totalBytes - dat->transferStatus.totalProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); - ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, &st); - GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); - } - - mir_snwprintf(szDisplay, L"%s/%s (%s %s)", szSpeed, TranslateT("sec"), szTime, TranslateT("remaining")); - SetDlgItemText(hwndDlg, IDC_ALLSPEED, szDisplay); - break; - - case WM_MEASUREITEM: - return Menu_MeasureItem(lParam); - - case WM_DRAWITEM: - return Menu_DrawItem(lParam); - - case WM_FT_CLEANUP: - if (!dat->fs) { - PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); - DestroyWindow(hwndDlg); - } - break; - - case WM_COMMAND: - if (!dat) - break; - - if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, dat->hContact)) - break; - - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); - DestroyWindow(hwndDlg); - break; - - case IDC_CONTACT: - { - RECT rc; - HMENU hMenu = Menu_BuildContactMenu(dat->hContact); - GetWindowRect((HWND)lParam, &rc); - TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL); - DestroyMenu(hMenu); - } - break; - - case IDC_TRANSFERCOMPLETED: - if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0)) { - ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW); - break; - } - - case IDC_OPENFOLDER: - { - wchar_t *path = dat->transferStatus.szWorkingDir.w; - if (!path || !path[0]) { - path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); - wchar_t *p = wcsrchr(path, '\\'); if (p) *p = 0; - } - - if (path) ShellExecute(NULL, L"open", path, NULL, NULL, SW_SHOW); - } - break; - - case IDC_OPENFILE: - wchar_t **files; - if (dat->send) { - if (dat->files == nullptr) - files = dat->transferStatus.pszFiles.w; - else - files = dat->files; - } - else files = dat->files; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - - if (files && *files) { - int limit; - wchar_t *pszFilename, *pszNewFileName; - - if (dat->send) - limit = dat->transferStatus.totalFiles; - else - limit = dat->transferStatus.currentFileNumber; - - // Loop over all transfered files and add them to the menu - for (int i = 0; i < limit; i++) { - pszFilename = wcsrchr(files[i], '\\'); - if (pszFilename == nullptr) - pszFilename = files[i]; - else - pszFilename++; - - if (pszFilename) { - size_t cbFileNameLen = mir_wstrlen(pszFilename); - - pszNewFileName = (wchar_t*)mir_alloc(cbFileNameLen * 2 * sizeof(wchar_t)); - wchar_t *p = pszNewFileName; - for (size_t pszlen = 0; pszlen < cbFileNameLen; pszlen++) { - *p++ = pszFilename[pszlen]; - if (pszFilename[pszlen] == '&') - *p++ = '&'; - } - *p = '\0'; - AppendMenu(hMenu, MF_STRING, i + 10, pszNewFileName); - mir_free(pszNewFileName); - } - } - } - - RECT rc; - GetWindowRect((HWND)lParam, &rc); - CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED); - int ret = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, nullptr); - CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED); - DestroyMenu(hMenu); - - if (ret == 1) { - wchar_t *path = dat->transferStatus.szWorkingDir.w; - if (!path || !path[0]) { - path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); - wchar_t *p = wcsrchr(path, '\\'); - if (p) - *p = 0; - } - - if (path) ShellExecute(nullptr, L"open", path, nullptr, nullptr, SW_SHOW); - } - else if (ret && CheckVirusScanned(hwndDlg, dat, ret)) - ShellExecute(nullptr, nullptr, files[ret - 10], nullptr, nullptr, SW_SHOW); - } - break; - - case M_FILEEXISTSDLGREPLY: - EnableWindow(hwndDlg, TRUE); - { - PROTOFILERESUME *pfr = (PROTOFILERESUME *)lParam; - wchar_t *szOriginalFilename = (wchar_t *)wParam; - char *szProto = Proto_GetBaseAccountName(dat->hContact); - - switch (pfr->action) { - case FILERESUME_CANCEL: - if (dat->fs) ProtoChainSend(dat->hContact, PSS_FILECANCEL, (WPARAM)dat->fs, 0); - dat->fs = nullptr; - mir_free(szOriginalFilename); - if (pfr->szFilename) mir_free((char *)pfr->szFilename); - mir_free(pfr); - return 0; - case FILERESUME_RESUMEALL: - case FILERESUME_OVERWRITEALL: - dat->resumeBehaviour = pfr->action; - pfr->action &= ~FILERESUMEF_ALL; - break; - case FILERESUME_RENAMEALL: - pfr->action = FILERESUME_RENAME; - { - wchar_t *pszExtension, *pszFilename; - if ((pszFilename = wcsrchr(szOriginalFilename, '\\')) == nullptr) pszFilename = szOriginalFilename; - if ((pszExtension = wcsrchr(pszFilename + 1, '.')) == nullptr) pszExtension = pszFilename + mir_wstrlen(pszFilename); - if (pfr->szFilename) mir_free((wchar_t *)pfr->szFilename); - size_t size = (pszExtension - szOriginalFilename) + 21 + mir_wstrlen(pszExtension); - pfr->szFilename = (wchar_t *)mir_alloc(sizeof(wchar_t) * size); - for (int i = 1;; i++) { - mir_snwprintf((wchar_t *)pfr->szFilename, size, L"%.*s (%u)%s", pszExtension - szOriginalFilename, szOriginalFilename, i, pszExtension); - if (_waccess(pfr->szFilename, 0) != 0) - break; - } - } - break; - } - mir_free(szOriginalFilename); - CallProtoService(szProto, PS_FILERESUME, (WPARAM)dat->fs, (LPARAM)pfr); - delete pfr; - } - break; - - case HM_RECVEVENT: - { - ACKDATA *ack = (ACKDATA *)lParam; - if (ack->hProcess != dat->fs) break; - if (ack->type != ACKTYPE_FILE) break; - if (ack->hContact != dat->hContact) break; - - if (dat->waitingForAcceptance) { - SetTimer(hwndDlg, 1, 1000, nullptr); - dat->waitingForAcceptance = 0; - } - - switch (ack->result) { - case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break; - case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break; - case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break; - case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break; - case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break; - case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENW("Initializing..."), FTS_TEXT); break; - case ACKRESULT_NEXTFILE: - SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT); - SetDlgItemTextA(hwndDlg, IDC_FILENAME, ""); - if (dat->transferStatus.currentFileNumber == 1 && dat->transferStatus.totalFiles > 1 && !dat->send) - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - if (dat->transferStatus.currentFileNumber != -1 && dat->files && !dat->send && g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE) == VIRUSSCAN_DURINGDL) { - if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) - PostMessage(hwndDlg, M_VIRUSSCANDONE, dat->transferStatus.currentFileNumber, 0); - else { - virusscanthreadstartinfo *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); - vstsi->hwndReply = hwndDlg; - vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); - vstsi->returnCode = dat->transferStatus.currentFileNumber; - mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi); - } - } - break; - - case ACKRESULT_FILERESUME: - UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS *)ack->lParam); - { - PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus; - SetFilenameControls(hwndDlg, dat, fts); - if (_waccess(fts->szCurrentFile.w, 0)) - break; - - SetFtStatus(hwndDlg, LPGENW("File already exists"), FTS_TEXT); - if (dat->resumeBehaviour == FILERESUME_ASK) { - TDlgProcFileExistsParam param = { hwndDlg, fts }; - ShowWindow(hwndDlg, SW_SHOWNORMAL); - CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILEEXISTS), hwndDlg, DlgProcFileExists, (LPARAM)¶m); - EnableWindow(hwndDlg, FALSE); - } - else { - PROTOFILERESUME *pfr = new PROTOFILERESUME(); - pfr->action = dat->resumeBehaviour; - pfr->szFilename = nullptr; - PostMessage(hwndDlg, M_FILEEXISTSDLGREPLY, (WPARAM)mir_wstrdup(fts->szCurrentFile.w), (LPARAM)pfr); - } - } - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); - return TRUE; - - case ACKRESULT_DATA: - { - PROTOFILETRANSFERSTATUS *fts = (PROTOFILETRANSFERSTATUS *)ack->lParam; - wchar_t str[64], str2[64], szSizeDone[32], szSizeTotal[32];//, *contactName; - - if (dat->fileVirusScanned == nullptr) - dat->fileVirusScanned = (int *)mir_calloc(sizeof(int) * fts->totalFiles); - - // This needs to be here - otherwise we get holes in the files array - if (!dat->send) { - if (dat->files == nullptr) - dat->files = (wchar_t **)mir_calloc((fts->totalFiles + 1) * sizeof(wchar_t *)); - if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == nullptr) - dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->szCurrentFile.w); - } - - /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */ - if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650)) - break; // the last update was less than a second ago! - dat->dwTicks = GetTickCount(); - - // Update local transfer status with data from protocol - UpdateProtoFileTransferStatus(&dat->transferStatus, fts); - fts = &dat->transferStatus; - - bool firstTime = false; - if ((GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0) { - SetFtStatus(hwndDlg, (fts->flags & PFTS_SENDING) ? LPGENW("Sending...") : LPGENW("Receiving..."), FTS_PROGRESS); - SetFilenameControls(hwndDlg, dat, fts); - firstTime = true; - } - - const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0); - const unsigned long nextPos = fts->totalBytes ? (100ll * fts->totalProgress / fts->totalBytes) : 0; - if (lastPos != nextPos || firstTime) { - SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0); - mir_snwprintf(str, L"%u%%", nextPos); - SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str); - } - - int units; - GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, _countof(szSizeTotal), 0, 1, &units); - GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, _countof(szSizeDone), units, 0, NULL); - mir_snwprintf(str, L"%s/%s", szSizeDone, szSizeTotal); - str2[0] = 0; - GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, _countof(str2)); - if (mir_wstrcmp(str, str2)) - SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str); - } - break; - - case ACKRESULT_SUCCESS: - case ACKRESULT_FAILED: - case ACKRESULT_DENIED: - HideProgressControls(hwndDlg); - KillTimer(hwndDlg, 1); - if (!dat->send) - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - SetDlgItemText(hwndDlg, IDCANCEL, TranslateT("Close")); - if (dat->hNotifyEvent) - UnhookEvent(dat->hNotifyEvent); - dat->hNotifyEvent = nullptr; - - if (ack->result == ACKRESULT_DENIED) { - dat->fs = nullptr; /* protocol will free structure */ - Skin_PlaySound("FileDenied"); - SetFtStatus(hwndDlg, LPGENW("File transfer denied"), FTS_TEXT); - } - else if (ack->result == ACKRESULT_FAILED) { - dat->fs = nullptr; /* protocol will free structure */ - Skin_PlaySound("FileFailed"); - SetFtStatus(hwndDlg, LPGENW("File transfer failed"), FTS_TEXT); - } - else { - Skin_PlaySound("FileDone"); - if (dat->send) { - dat->fs = nullptr; /* protocol will free structure */ - SetFtStatus(hwndDlg, LPGENW("Transfer completed."), FTS_TEXT); - - DBEVENTINFO dbei = {}; - FillSendData(dat, dbei); - db_event_add(dat->hContact, &dbei); - if (dbei.pBlob) - mir_free(dbei.pBlob); - dat->files = nullptr; //protocol library frees this - } - else { - SetFtStatus(hwndDlg, - (dat->transferStatus.totalFiles == 1) ? - LPGENW("Transfer completed, open file.") : - LPGENW("Transfer completed, open folder."), - FTS_OPEN); - - int useScanner = g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE); - if (useScanner != VIRUSSCAN_DISABLE) { - auto *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); - vstsi->hwndReply = hwndDlg; - if (useScanner == VIRUSSCAN_DURINGDL) { - vstsi->returnCode = dat->transferStatus.currentFileNumber; - if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) { - PostMessage(hwndDlg, M_VIRUSSCANDONE, vstsi->returnCode, 0); - mir_free(vstsi); - vstsi = nullptr; - } - else vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); - } - else { - vstsi->szFile = mir_wstrdup(dat->transferStatus.szWorkingDir.w); - vstsi->returnCode = -1; - } - SetFtStatus(hwndDlg, LPGENW("Scanning for viruses..."), FTS_TEXT); - if (vstsi) - mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi); - } - else dat->fs = nullptr; /* protocol will free structure */ - - dat->transferStatus.currentFileNumber = dat->transferStatus.totalFiles; - } - } - - PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg); - break; - } - } - break; - - case M_VIRUSSCANDONE: - { - int done = 1; - if ((int)wParam == -1) { - for (int i = 0; i < dat->transferStatus.totalFiles; i++) - dat->fileVirusScanned[i] = 1; - } - else { - dat->fileVirusScanned[wParam] = 1; - for (int i = 0; i < dat->transferStatus.totalFiles; i++) - if (!dat->fileVirusScanned[i]) { - done = 0; - break; - } - } - if (done) { - dat->fs = nullptr; /* protocol will free structure */ - SetFtStatus(hwndDlg, LPGENW("Transfer and virus scan complete"), FTS_TEXT); - } - } - break; - - case WM_SIZE: - Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FILETRANSFERINFO), FileTransferDlgResizer, LPARAM(dat)); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - break; - - case WM_DESTROY: - KillTimer(hwndDlg, 1); - - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); - DeleteObject(hFont); - - Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT); - Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE); - Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL); - - delete dat; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} - -FileDlgData::~FileDlgData() -{ - if (fs) - ProtoChainSend(hContact, PSS_FILECANCEL, (WPARAM)fs, 0); - if (hPreshutdownEvent) - UnhookEvent(hPreshutdownEvent); - if (hNotifyEvent) - UnhookEvent(hNotifyEvent); - - FreeProtoFileTransferStatus(&transferStatus); - FreeFilesMatrix(&files); - - mir_free(fileVirusScanned); - if (hIcon) - DestroyIcon(hIcon); - if (hIconFolder) - DestroyIcon(hIconFolder); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include <io.h>
+#include "file.h"
+
+static int CheckVirusScanned(HWND hwnd, FileDlgData *dat, int i)
+{
+ if (dat->send) return 1;
+ if (dat->fileVirusScanned == nullptr) return 0;
+ if (dat->fileVirusScanned[i]) return 1;
+ if (g_plugin.getByte("WarnBeforeOpening", 1) == 0) return 1;
+
+ return IDYES == MessageBox(hwnd,
+ TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"),
+ TranslateT("File received"),
+ MB_YESNO | MB_DEFBUTTON2);
+}
+
+#define M_VIRUSSCANDONE (WM_USER+100)
+struct virusscanthreadstartinfo
+{
+ wchar_t *szFile;
+ int returnCode;
+ HWND hwndReply;
+};
+
+wchar_t* PFTS_StringToTchar(int flags, const wchar_t *s)
+{
+ if (flags & PFTS_UTF)
+ return mir_utf8decodeW((char*)s);
+ if (flags & PFTS_UNICODE)
+ return mir_wstrdup(s);
+ return mir_a2u((char*)s);
+}
+
+int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS *ft, const wchar_t *s, wchar_t *r)
+{
+ if (ft->flags & PFTS_UTF) {
+ wchar_t *ts = mir_utf8decodeW((char*)s);
+ int res = mir_wstrcmp(ts, r);
+ mir_free(ts);
+ return res;
+ }
+ if (ft->flags & PFTS_UNICODE)
+ return mir_wstrcmp(s, r);
+
+ wchar_t *ts = mir_a2u((char*)s);
+ int res = mir_wstrcmp(ts, r);
+ mir_free(ts);
+ return res;
+}
+
+static void SetOpenFileButtonStyle(HWND hwndButton, int enabled)
+{
+ EnableWindow(hwndButton, enabled);
+}
+
+void FillSendData(FileDlgData *dat, DBEVENTINFO &dbei)
+{
+ dbei.szModule = Proto_GetBaseAccountName(dat->hContact);
+ dbei.eventType = EVENTTYPE_FILE;
+ dbei.flags = DBEF_SENT;
+ dbei.timestamp = time(0);
+ char *szFileNames = mir_utf8encodeW(dat->szFilenames), *szMsg = mir_utf8encodeW(dat->szMsg);
+ dbei.flags |= DBEF_UTF;
+
+ dbei.cbBlob = int(sizeof(uint32_t) + mir_strlen(szFileNames) + mir_strlen(szMsg) + 2);
+ dbei.pBlob = (uint8_t*)mir_alloc(dbei.cbBlob);
+ *(PDWORD)dbei.pBlob = 0;
+ mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t), szFileNames);
+ mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t) + mir_strlen(szFileNames) + 1, szMsg);
+
+ mir_free(szFileNames), mir_free(szMsg);
+}
+
+static void __cdecl RunVirusScannerThread(virusscanthreadstartinfo *info)
+{
+ DBVARIANT dbv;
+ if (!g_plugin.getWString("ScanCmdLine", &dbv)) {
+ if (dbv.pwszVal[0]) {
+ STARTUPINFO si = { 0 };
+ si.cb = sizeof(si);
+ wchar_t *pszReplace = wcsstr(dbv.pwszVal, L"%f");
+ wchar_t szCmdLine[768];
+ if (pszReplace) {
+ if (info->szFile[mir_wstrlen(info->szFile) - 1] == '\\')
+ info->szFile[mir_wstrlen(info->szFile) - 1] = '\0';
+ *pszReplace = 0;
+ mir_snwprintf(szCmdLine, L"%s\"%s\"%s", dbv.pwszVal, info->szFile, pszReplace + 2);
+ }
+ else
+ wcsncpy_s(szCmdLine, dbv.pwszVal, _TRUNCATE);
+
+ PROCESS_INFORMATION pi;
+ if (CreateProcess(nullptr, szCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
+ if (WaitForSingleObject(pi.hProcess, 3600 * 1000) == WAIT_OBJECT_0)
+ PostMessage(info->hwndReply, M_VIRUSSCANDONE, info->returnCode, 0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ }
+ db_free(&dbv);
+ }
+ mir_free(info->szFile);
+ mir_free(info);
+}
+
+static void SetFilenameControls(HWND hwndDlg, FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts)
+{
+ wchar_t msg[MAX_PATH];
+ wchar_t *fnbuf = nullptr, *fn = nullptr;
+ SHFILEINFO shfi = {};
+
+ if (fts->szCurrentFile.w) {
+ fnbuf = mir_wstrdup(fts->szCurrentFile.w);
+ if ((fn = wcsrchr(fnbuf, '\\')) == nullptr)
+ fn = fnbuf;
+ else fn++;
+ }
+
+ if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = nullptr;
+
+ if (fn && (fts->totalFiles > 1)) {
+ mir_snwprintf(msg, L"%s: %s (%d %s %d)", Clist_GetContactDisplayName(fts->hContact), fn, fts->currentFileNumber + 1, TranslateT("of"), fts->totalFiles);
+
+ SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
+ dat->hIcon = shfi.hIcon;
+ }
+ else if (fn) {
+ mir_snwprintf(msg, L"%s: %s", Clist_GetContactDisplayName(fts->hContact), fn);
+
+ SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
+ dat->hIcon = shfi.hIcon;
+ }
+ else {
+ mir_wstrncpy(msg, Clist_GetContactDisplayName(fts->hContact), _countof(msg));
+ HICON hIcon = Skin_LoadIcon(SKINICON_OTHER_DOWNARROW);
+ dat->hIcon = CopyIcon(hIcon);
+ IcoLib_ReleaseIcon(hIcon, NULL);
+ }
+
+ mir_free(fnbuf);
+
+ SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon);
+ SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg);
+}
+
+enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN };
+static void SetFtStatus(HWND hwndDlg, wchar_t *text, int mode)
+{
+ SetDlgItemText(hwndDlg, IDC_STATUS, TranslateW(text));
+ SetDlgItemText(hwndDlg, IDC_TRANSFERCOMPLETED, TranslateW(text));
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_STATUS), (mode == FTS_TEXT) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FILEICON), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE);
+}
+
+static void HideProgressControls(HWND hwndDlg)
+{
+ RECT rc;
+ char buf[64];
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc);
+ MapWindowPoints(nullptr, hwndDlg, (LPPOINT)&rc, 2);
+ SetWindowPos(hwndDlg, nullptr, 0, 0, 100, rc.bottom + 3, SWP_NOMOVE | SWP_NOZORDER);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE);
+
+ _strtime(buf);
+ SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf);
+
+ PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg);
+}
+
+static int FileTransferDlgResizer(HWND, LPARAM param, UTILRESIZECONTROL *urc)
+{
+ auto *dat = (FileDlgData *)param;
+
+ switch (urc->wId) {
+ case IDC_CONTACTNAME:
+ case IDC_STATUS:
+ case IDC_ALLFILESPROGRESS:
+ case IDC_TRANSFERCOMPLETED:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ case IDC_FRAME:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+ case IDC_ALLPRECENTS:
+ case IDCANCEL:
+ case IDC_OPENFILE:
+ case IDC_OPENFOLDER:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ case IDC_ALLTRANSFERRED:
+ if (dat->waitingForAcceptance)
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3;
+ return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP;
+
+ case IDC_ALLSPEED:
+ if (dat->waitingForAcceptance)
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx;
+ urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3;
+ return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ FileDlgData *dat = (FileDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ dat = (FileDlgData *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->hNotifyEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_RECVEVENT);
+ dat->transferStatus.currentFileNumber = -1;
+ if (dat->send) {
+ if (db_mc_isMeta(dat->hContact))
+ dat->hContact = db_mc_getMostOnline(dat->hContact);
+ dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILE, (WPARAM)dat->szMsg, (LPARAM)dat->files);
+ SetFtStatus(hwndDlg, LPGENW("Request sent, waiting for acceptance..."), FTS_TEXT);
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1);
+ dat->waitingForAcceptance = 1;
+ // hide "open" button since it may cause potential access violations...
+ ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE);
+ }
+ else { //recv
+ CreateDirectoryTreeW(dat->szSavePath);
+ dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILEALLOW, (WPARAM)dat->fs, (LPARAM)dat->szSavePath);
+ dat->transferStatus.szWorkingDir.w = mir_wstrdup(dat->szSavePath);
+ if (!Contact::OnList(dat->hContact))
+ dat->resumeBehaviour = FILERESUME_ASK;
+ else
+ dat->resumeBehaviour = g_plugin.getByte("IfExists", FILERESUME_ASK);
+ SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT);
+ }
+
+ /* check we actually got an fs handle back from the protocol */
+ if (!dat->fs) {
+ SetFtStatus(hwndDlg, LPGENW("Unable to initiate transfer."), FTS_TEXT);
+ dat->waitingForAcceptance = 0;
+ }
+ {
+ LOGFONT lf;
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0);
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_SETFONT, (WPARAM)hFont, 0);
+
+ SHFILEINFO shfi = {};
+ SHGetFileInfo(L"", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
+ dat->hIconFolder = shfi.hIcon;
+ }
+ dat->hIcon = nullptr;
+ {
+ char *szProto = Proto_GetBaseAccountName(dat->hContact);
+ uint16_t status = db_get_w(dat->hContact, szProto, "Status", ID_STATUS_ONLINE);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadProtoIcon(szProto, status));
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, TRUE, 0);
+
+ Button_SetSkin_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open..."));
+ SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, TRUE, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder);
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0);
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, TRUE, 0);
+
+ Button_SetSkin_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel"));
+
+ SetDlgItemText(hwndDlg, IDC_CONTACTNAME, Clist_GetContactDisplayName(dat->hContact));
+
+ if (!dat->waitingForAcceptance)
+ SetTimer(hwndDlg, 1, 1000, nullptr);
+ return TRUE;
+
+ case WM_TIMER:
+ memmove(dat->bytesRecvedHistory + 1, dat->bytesRecvedHistory, sizeof(dat->bytesRecvedHistory) - sizeof(dat->bytesRecvedHistory[0]));
+ dat->bytesRecvedHistory[0] = dat->transferStatus.totalProgress;
+ if (dat->bytesRecvedHistorySize < _countof(dat->bytesRecvedHistory))
+ dat->bytesRecvedHistorySize++;
+
+ wchar_t szSpeed[32], szTime[32], szDisplay[96];
+ SYSTEMTIME st;
+ ULARGE_INTEGER li;
+ FILETIME ft;
+
+ GetSensiblyFormattedSize((dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) / dat->bytesRecvedHistorySize, szSpeed, _countof(szSpeed), 0, 1, NULL);
+ if (dat->bytesRecvedHistory[0] == dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1])
+ mir_wstrcpy(szTime, L"??:??:??");
+ else {
+ li.QuadPart = 10000000ll * (dat->transferStatus.currentFileSize - dat->transferStatus.currentFileProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]);
+ ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart;
+ FileTimeToSystemTime(&ft, &st);
+ GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime));
+ }
+ if (dat->bytesRecvedHistory[0] != dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) {
+ li.QuadPart = 10000000ll * (dat->transferStatus.totalBytes - dat->transferStatus.totalProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]);
+ ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart;
+ FileTimeToSystemTime(&ft, &st);
+ GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime));
+ }
+
+ mir_snwprintf(szDisplay, L"%s/%s (%s %s)", szSpeed, TranslateT("sec"), szTime, TranslateT("remaining"));
+ SetDlgItemText(hwndDlg, IDC_ALLSPEED, szDisplay);
+ break;
+
+ case WM_MEASUREITEM:
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM:
+ return Menu_DrawItem(lParam);
+
+ case WM_FT_CLEANUP:
+ if (!dat->fs) {
+ PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+
+ case WM_COMMAND:
+ if (!dat)
+ break;
+
+ if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, dat->hContact))
+ break;
+
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg);
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDC_CONTACT:
+ {
+ RECT rc;
+ HMENU hMenu = Menu_BuildContactMenu(dat->hContact);
+ GetWindowRect((HWND)lParam, &rc);
+ TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ DestroyMenu(hMenu);
+ }
+ break;
+
+ case IDC_TRANSFERCOMPLETED:
+ if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0)) {
+ ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW);
+ break;
+ }
+
+ case IDC_OPENFOLDER:
+ {
+ wchar_t *path = dat->transferStatus.szWorkingDir.w;
+ if (!path || !path[0]) {
+ path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w);
+ wchar_t *p = wcsrchr(path, '\\'); if (p) *p = 0;
+ }
+
+ if (path) ShellExecute(NULL, L"open", path, NULL, NULL, SW_SHOW);
+ }
+ break;
+
+ case IDC_OPENFILE:
+ wchar_t **files;
+ if (dat->send) {
+ if (dat->files == nullptr)
+ files = dat->transferStatus.pszFiles.w;
+ else
+ files = dat->files;
+ }
+ else files = dat->files;
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+
+ if (files && *files) {
+ int limit;
+ wchar_t *pszFilename, *pszNewFileName;
+
+ if (dat->send)
+ limit = dat->transferStatus.totalFiles;
+ else
+ limit = dat->transferStatus.currentFileNumber;
+
+ // Loop over all transfered files and add them to the menu
+ for (int i = 0; i < limit; i++) {
+ pszFilename = wcsrchr(files[i], '\\');
+ if (pszFilename == nullptr)
+ pszFilename = files[i];
+ else
+ pszFilename++;
+
+ if (pszFilename) {
+ size_t cbFileNameLen = mir_wstrlen(pszFilename);
+
+ pszNewFileName = (wchar_t*)mir_alloc(cbFileNameLen * 2 * sizeof(wchar_t));
+ wchar_t *p = pszNewFileName;
+ for (size_t pszlen = 0; pszlen < cbFileNameLen; pszlen++) {
+ *p++ = pszFilename[pszlen];
+ if (pszFilename[pszlen] == '&')
+ *p++ = '&';
+ }
+ *p = '\0';
+ AppendMenu(hMenu, MF_STRING, i + 10, pszNewFileName);
+ mir_free(pszNewFileName);
+ }
+ }
+ }
+
+ RECT rc;
+ GetWindowRect((HWND)lParam, &rc);
+ CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED);
+ int ret = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, nullptr);
+ CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED);
+ DestroyMenu(hMenu);
+
+ if (ret == 1) {
+ wchar_t *path = dat->transferStatus.szWorkingDir.w;
+ if (!path || !path[0]) {
+ path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w);
+ wchar_t *p = wcsrchr(path, '\\');
+ if (p)
+ *p = 0;
+ }
+
+ if (path) ShellExecute(nullptr, L"open", path, nullptr, nullptr, SW_SHOW);
+ }
+ else if (ret && CheckVirusScanned(hwndDlg, dat, ret))
+ ShellExecute(nullptr, nullptr, files[ret - 10], nullptr, nullptr, SW_SHOW);
+ }
+ break;
+
+ case M_FILEEXISTSDLGREPLY:
+ EnableWindow(hwndDlg, TRUE);
+ {
+ PROTOFILERESUME *pfr = (PROTOFILERESUME *)lParam;
+ wchar_t *szOriginalFilename = (wchar_t *)wParam;
+ char *szProto = Proto_GetBaseAccountName(dat->hContact);
+
+ switch (pfr->action) {
+ case FILERESUME_CANCEL:
+ if (dat->fs) ProtoChainSend(dat->hContact, PSS_FILECANCEL, (WPARAM)dat->fs, 0);
+ dat->fs = nullptr;
+ mir_free(szOriginalFilename);
+ if (pfr->szFilename) mir_free((char *)pfr->szFilename);
+ mir_free(pfr);
+ return 0;
+ case FILERESUME_RESUMEALL:
+ case FILERESUME_OVERWRITEALL:
+ dat->resumeBehaviour = pfr->action;
+ pfr->action &= ~FILERESUMEF_ALL;
+ break;
+ case FILERESUME_RENAMEALL:
+ pfr->action = FILERESUME_RENAME;
+ {
+ wchar_t *pszExtension, *pszFilename;
+ if ((pszFilename = wcsrchr(szOriginalFilename, '\\')) == nullptr) pszFilename = szOriginalFilename;
+ if ((pszExtension = wcsrchr(pszFilename + 1, '.')) == nullptr) pszExtension = pszFilename + mir_wstrlen(pszFilename);
+ if (pfr->szFilename) mir_free((wchar_t *)pfr->szFilename);
+ size_t size = (pszExtension - szOriginalFilename) + 21 + mir_wstrlen(pszExtension);
+ pfr->szFilename = (wchar_t *)mir_alloc(sizeof(wchar_t) * size);
+ for (int i = 1;; i++) {
+ mir_snwprintf((wchar_t *)pfr->szFilename, size, L"%.*s (%u)%s", pszExtension - szOriginalFilename, szOriginalFilename, i, pszExtension);
+ if (_waccess(pfr->szFilename, 0) != 0)
+ break;
+ }
+ }
+ break;
+ }
+ mir_free(szOriginalFilename);
+ CallProtoService(szProto, PS_FILERESUME, (WPARAM)dat->fs, (LPARAM)pfr);
+ delete pfr;
+ }
+ break;
+
+ case HM_RECVEVENT:
+ {
+ ACKDATA *ack = (ACKDATA *)lParam;
+ if (ack->hProcess != dat->fs) break;
+ if (ack->type != ACKTYPE_FILE) break;
+ if (ack->hContact != dat->hContact) break;
+
+ if (dat->waitingForAcceptance) {
+ SetTimer(hwndDlg, 1, 1000, nullptr);
+ dat->waitingForAcceptance = 0;
+ }
+
+ switch (ack->result) {
+ case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break;
+ case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break;
+ case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break;
+ case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENW("Initializing..."), FTS_TEXT); break;
+ case ACKRESULT_NEXTFILE:
+ SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT);
+ SetDlgItemTextA(hwndDlg, IDC_FILENAME, "");
+ if (dat->transferStatus.currentFileNumber == 1 && dat->transferStatus.totalFiles > 1 && !dat->send)
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1);
+ if (dat->transferStatus.currentFileNumber != -1 && dat->files && !dat->send && g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE) == VIRUSSCAN_DURINGDL) {
+ if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY)
+ PostMessage(hwndDlg, M_VIRUSSCANDONE, dat->transferStatus.currentFileNumber, 0);
+ else {
+ virusscanthreadstartinfo *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo));
+ vstsi->hwndReply = hwndDlg;
+ vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]);
+ vstsi->returnCode = dat->transferStatus.currentFileNumber;
+ mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi);
+ }
+ }
+ break;
+
+ case ACKRESULT_FILERESUME:
+ UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS *)ack->lParam);
+ {
+ PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus;
+ SetFilenameControls(hwndDlg, dat, fts);
+ if (_waccess(fts->szCurrentFile.w, 0))
+ break;
+
+ SetFtStatus(hwndDlg, LPGENW("File already exists"), FTS_TEXT);
+ if (dat->resumeBehaviour == FILERESUME_ASK) {
+ TDlgProcFileExistsParam param = { hwndDlg, fts };
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+ CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILEEXISTS), hwndDlg, DlgProcFileExists, (LPARAM)¶m);
+ EnableWindow(hwndDlg, FALSE);
+ }
+ else {
+ PROTOFILERESUME *pfr = new PROTOFILERESUME();
+ pfr->action = dat->resumeBehaviour;
+ pfr->szFilename = nullptr;
+ PostMessage(hwndDlg, M_FILEEXISTSDLGREPLY, (WPARAM)mir_wstrdup(fts->szCurrentFile.w), (LPARAM)pfr);
+ }
+ }
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1);
+ return TRUE;
+
+ case ACKRESULT_DATA:
+ {
+ PROTOFILETRANSFERSTATUS *fts = (PROTOFILETRANSFERSTATUS *)ack->lParam;
+ wchar_t str[64], str2[64], szSizeDone[32], szSizeTotal[32];//, *contactName;
+
+ if (dat->fileVirusScanned == nullptr)
+ dat->fileVirusScanned = (int *)mir_calloc(sizeof(int) * fts->totalFiles);
+
+ // This needs to be here - otherwise we get holes in the files array
+ if (!dat->send) {
+ if (dat->files == nullptr)
+ dat->files = (wchar_t **)mir_calloc((fts->totalFiles + 1) * sizeof(wchar_t *));
+ if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == nullptr)
+ dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->szCurrentFile.w);
+ }
+
+ /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */
+ if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650))
+ break; // the last update was less than a second ago!
+ dat->dwTicks = GetTickCount();
+
+ // Update local transfer status with data from protocol
+ UpdateProtoFileTransferStatus(&dat->transferStatus, fts);
+ fts = &dat->transferStatus;
+
+ bool firstTime = false;
+ if ((GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0) {
+ SetFtStatus(hwndDlg, (fts->flags & PFTS_SENDING) ? LPGENW("Sending...") : LPGENW("Receiving..."), FTS_PROGRESS);
+ SetFilenameControls(hwndDlg, dat, fts);
+ firstTime = true;
+ }
+
+ const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0);
+ const unsigned long nextPos = fts->totalBytes ? (100ll * fts->totalProgress / fts->totalBytes) : 0;
+ if (lastPos != nextPos || firstTime) {
+ SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0);
+ mir_snwprintf(str, L"%u%%", nextPos);
+ SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str);
+ }
+
+ int units;
+ GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, _countof(szSizeTotal), 0, 1, &units);
+ GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, _countof(szSizeDone), units, 0, NULL);
+ mir_snwprintf(str, L"%s/%s", szSizeDone, szSizeTotal);
+ str2[0] = 0;
+ GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, _countof(str2));
+ if (mir_wstrcmp(str, str2))
+ SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str);
+ }
+ break;
+
+ case ACKRESULT_SUCCESS:
+ case ACKRESULT_FAILED:
+ case ACKRESULT_DENIED:
+ HideProgressControls(hwndDlg);
+ KillTimer(hwndDlg, 1);
+ if (!dat->send)
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1);
+ SetDlgItemText(hwndDlg, IDCANCEL, TranslateT("Close"));
+ if (dat->hNotifyEvent)
+ UnhookEvent(dat->hNotifyEvent);
+ dat->hNotifyEvent = nullptr;
+
+ if (ack->result == ACKRESULT_DENIED) {
+ dat->fs = nullptr; /* protocol will free structure */
+ Skin_PlaySound("FileDenied");
+ SetFtStatus(hwndDlg, LPGENW("File transfer denied"), FTS_TEXT);
+ }
+ else if (ack->result == ACKRESULT_FAILED) {
+ dat->fs = nullptr; /* protocol will free structure */
+ Skin_PlaySound("FileFailed");
+ SetFtStatus(hwndDlg, LPGENW("File transfer failed"), FTS_TEXT);
+ }
+ else {
+ Skin_PlaySound("FileDone");
+ if (dat->send) {
+ dat->fs = nullptr; /* protocol will free structure */
+ SetFtStatus(hwndDlg, LPGENW("Transfer completed."), FTS_TEXT);
+
+ DBEVENTINFO dbei = {};
+ FillSendData(dat, dbei);
+ db_event_add(dat->hContact, &dbei);
+ if (dbei.pBlob)
+ mir_free(dbei.pBlob);
+ dat->files = nullptr; //protocol library frees this
+ }
+ else {
+ SetFtStatus(hwndDlg,
+ (dat->transferStatus.totalFiles == 1) ?
+ LPGENW("Transfer completed, open file.") :
+ LPGENW("Transfer completed, open folder."),
+ FTS_OPEN);
+
+ int useScanner = g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE);
+ if (useScanner != VIRUSSCAN_DISABLE) {
+ auto *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo));
+ vstsi->hwndReply = hwndDlg;
+ if (useScanner == VIRUSSCAN_DURINGDL) {
+ vstsi->returnCode = dat->transferStatus.currentFileNumber;
+ if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) {
+ PostMessage(hwndDlg, M_VIRUSSCANDONE, vstsi->returnCode, 0);
+ mir_free(vstsi);
+ vstsi = nullptr;
+ }
+ else vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]);
+ }
+ else {
+ vstsi->szFile = mir_wstrdup(dat->transferStatus.szWorkingDir.w);
+ vstsi->returnCode = -1;
+ }
+ SetFtStatus(hwndDlg, LPGENW("Scanning for viruses..."), FTS_TEXT);
+ if (vstsi)
+ mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi);
+ }
+ else dat->fs = nullptr; /* protocol will free structure */
+
+ dat->transferStatus.currentFileNumber = dat->transferStatus.totalFiles;
+ }
+ }
+
+ PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg);
+ break;
+ }
+ }
+ break;
+
+ case M_VIRUSSCANDONE:
+ {
+ int done = 1;
+ if ((int)wParam == -1) {
+ for (int i = 0; i < dat->transferStatus.totalFiles; i++)
+ dat->fileVirusScanned[i] = 1;
+ }
+ else {
+ dat->fileVirusScanned[wParam] = 1;
+ for (int i = 0; i < dat->transferStatus.totalFiles; i++)
+ if (!dat->fileVirusScanned[i]) {
+ done = 0;
+ break;
+ }
+ }
+ if (done) {
+ dat->fs = nullptr; /* protocol will free structure */
+ SetFtStatus(hwndDlg, LPGENW("Transfer and virus scan complete"), FTS_TEXT);
+ }
+ }
+ break;
+
+ case WM_SIZE:
+ Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FILETRANSFERINFO), FileTransferDlgResizer, LPARAM(dat));
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ break;
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 1);
+
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0);
+ DeleteObject(hFont);
+
+ Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT);
+ Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE);
+ Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL);
+
+ delete dat;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+FileDlgData::~FileDlgData()
+{
+ if (fs)
+ ProtoChainSend(hContact, PSS_FILECANCEL, (WPARAM)fs, 0);
+ if (hPreshutdownEvent)
+ UnhookEvent(hPreshutdownEvent);
+ if (hNotifyEvent)
+ UnhookEvent(hNotifyEvent);
+
+ FreeProtoFileTransferStatus(&transferStatus);
+ FreeFilesMatrix(&files);
+
+ mir_free(fileVirusScanned);
+ if (hIcon)
+ DestroyIcon(hIcon);
+ if (hIconFolder)
+ DestroyIcon(hIconFolder);
+}
diff --git a/src/core/stdfile/src/ftmanager.cpp b/src/core/stdfile/src/ftmanager.cpp index 20206908be..56b0a168a7 100644 --- a/src/core/stdfile/src/ftmanager.cpp +++ b/src/core/stdfile/src/ftmanager.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/main.cpp b/src/core/stdfile/src/main.cpp index 275c58cf44..3797ea1e49 100644 --- a/src/core/stdfile/src/main.cpp +++ b/src/core/stdfile/src/main.cpp @@ -2,7 +2,7 @@ Standard file transfers' plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdfile/src/stdafx.h b/src/core/stdfile/src/stdafx.h index b732c1278c..775d957d38 100644 --- a/src/core/stdfile/src/stdafx.h +++ b/src/core/stdfile/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/version.h b/src/core/stdfile/src/version.h index 777678748d..2099819477 100644 --- a/src/core/stdfile/src/version.h +++ b/src/core/stdfile/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for sending/receiving files."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdFile"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdmsg/src/chat_manager.cpp b/src/core/stdmsg/src/chat_manager.cpp index b72f53acd7..b461b91703 100644 --- a/src/core/stdmsg/src/chat_manager.cpp +++ b/src/core/stdmsg/src/chat_manager.cpp @@ -1,243 +1,243 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -pfnDoTrayIcon oldDoTrayIcon; -pfnDoPopup oldDoPopup; - -SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si) -{ - int i = g_chatApi.arSessions.indexOf(si); - if (i == -1) - return nullptr; - - for (i--; i >= 0; i--) { - SESSION_INFO *p = g_chatApi.arSessions[i]; - if (p->pDlg) - return p; - } - - return nullptr; -} - -SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si) -{ - int i = g_chatApi.arSessions.indexOf(si); - if (i == -1) - return nullptr; - - for (i++; i < g_chatApi.arSessions.getCount(); i++) { - SESSION_INFO *p = g_chatApi.arSessions[i]; - if (p->pDlg) - return p; - } - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HMENU g_hMenu = nullptr; - -GlobalLogSettings g_Settings; - -static MODULEINFO* MM_CreateModule() -{ - return new MODULEINFO(); -} - -static void OnReplaceSession(SESSION_INFO *si) -{ - if (si->pDlg) - RedrawWindow(GetDlgItem(si->pDlg->GetHwnd(), IDC_SRMM_NICKLIST), nullptr, nullptr, RDW_INVALIDATE); -} - -static void OnFlashHighlight(SESSION_INFO *si, int bInactive) -{ - if (!bInactive || si->pDlg == nullptr) - return; - - if (g_Settings.bTabsEnable) - g_pTabDialog->SetMessageHighlight(si->pDlg); - else if (Chat::bFlashWindowHighlight) - si->pDlg->StartFlash(); -} - -static void OnFlashWindow(SESSION_INFO *si, int bInactive) -{ - if (!bInactive || si->pDlg == nullptr) - return; - - if (g_Settings.bTabsEnable) - g_pTabDialog->SetTabHighlight(si->pDlg); - else if (Chat::bFlashWindow) - si->pDlg->StartFlash(); -} - -static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce) -{ - if (gce->iType & g_Settings.dwTrayIconFlags) - return oldDoTrayIcon(si, gce); - return TRUE; -} - -static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce) -{ - if (gce->iType & g_Settings.dwPopupFlags) - return oldDoPopup(si, gce); - return TRUE; -} - -static void OnLoadSettings() -{ - g_Settings.iX = db_get_dw(0, CHAT_MODULE, "roomx", -1); - g_Settings.iY = db_get_dw(0, CHAT_MODULE, "roomy", -1); - - g_Settings.bTabsEnable = db_get_b(0, CHAT_MODULE, "Tabs", 1) != 0; - g_Settings.bTabsAtBottom = db_get_b(0, CHAT_MODULE, "TabBottom", 0) != 0; - g_Settings.bTabCloseOnDblClick = db_get_b(0, CHAT_MODULE, "TabCloseOnDblClick", 0) != 0; - g_Settings.bAddColonToAutoComplete = db_get_b(0, CHAT_MODULE, "AddColonToAutoComplete", 1) != 0; - - g_Settings.iSplitterX = db_get_w(0, CHAT_MODULE, "SplitterX", 105); - if (g_Settings.iSplitterX <= 50) - g_Settings.iSplitterX = 105; - g_Settings.iSplitterY = db_get_w(0, CHAT_MODULE, "SplitterY", 90); - if (g_Settings.iSplitterY <= 65) - g_Settings.iSplitterY = 90; -} - -static void RegisterFonts() -{ - ColourIDW colourid = {}; - strncpy(colourid.dbSettingsGroup, CHAT_MODULE, sizeof(colourid.dbSettingsGroup)); - wcsncpy(colourid.group, LPGENW("Message sessions") L"/" LPGENW("Chat module"), _countof(colourid.group)); - - strncpy(colourid.setting, "ColorLogBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Group chat log background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorMessageBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Message background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistLines", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list lines"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistSelectedBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list background (selected)"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT); - g_plugin.addColor(&colourid); -} - -static void ShowRoom(SESSION_INFO *si) -{ - if (!si) - return; - - // Do we need to create a window? - if (si->pDlg == nullptr) { - CTabbedWindow *pContainer = GetContainer(); - if (g_Settings.bTabsEnable) { - pContainer->AddPage(si); - PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); - } - else { - CMsgDialog *pDlg = pContainer->m_pEmbed = new CMsgDialog(pContainer, si); - pContainer->Create(); - pDlg->SetParent(pContainer->GetHwnd()); - pDlg->Create(); - pContainer->Show(); - pContainer->FixTabIcons(pDlg); - PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); - } - - if (si->iType != GCW_SERVER) - si->pDlg->UpdateNickList(); - else - si->pDlg->UpdateTitle(); - si->pDlg->UpdateStatusBar(); - } - else if (g_Settings.bTabsEnable && g_pTabDialog) - g_pTabDialog->m_tab.ActivatePage(g_pTabDialog->m_tab.GetDlgIndex(si->pDlg)); - - SetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE) | WS_EX_APPWINDOW); - - if (IsIconic(si->pDlg->GetHwnd())) - si->pDlg->Show(SW_NORMAL); - si->pDlg->Show(SW_SHOW); - SetForegroundWindow(si->pDlg->GetHwnd()); -} - -int OnCheckPlugins(WPARAM, LPARAM) -{ - g_plugin.bSmileyInstalled = ServiceExists(MS_SMILEYADD_REPLACESMILEYS); - return 0; -} - -void Load_ChatModule() -{ - AddIcons(); - RegisterFonts(); - - CHAT_MANAGER_INITDATA data = { &g_Settings, sizeof(MODULEINFO), sizeof(SESSION_INFO), LPGENW("Message sessions") L"/" LPGENW("Chat module"), FONTMODE_USE, &g_plugin }; - Chat_CustomizeApi(&data); - - g_chatApi.MM_CreateModule = MM_CreateModule; - g_chatApi.OnReplaceSession = OnReplaceSession; - - g_chatApi.OnLoadSettings = OnLoadSettings; - g_chatApi.OnFlashWindow = OnFlashWindow; - g_chatApi.OnFlashHighlight = OnFlashHighlight; - g_chatApi.ShowRoom = ShowRoom; - - Srmm_CreateHotkey(LPGEN("Messaging"), LPGEN("Send message")); - - oldDoPopup = g_chatApi.DoPopup; g_chatApi.DoPopup = DoPopup; - oldDoTrayIcon = g_chatApi.DoTrayIcon; g_chatApi.DoTrayIcon = DoTrayIcon; - g_chatApi.ReloadSettings(); - - g_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENU)); - - HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins); -} - -void Unload_ChatModule() -{ - db_set_w(0, CHAT_MODULE, "SplitterX", (uint16_t)g_Settings.iSplitterX); - db_set_w(0, CHAT_MODULE, "SplitterY", (uint16_t)g_Settings.iSplitterY); - db_set_dw(0, CHAT_MODULE, "roomx", g_Settings.iX); - db_set_dw(0, CHAT_MODULE, "roomy", g_Settings.iY); - db_set_dw(0, CHAT_MODULE, "roomwidth", g_Settings.iWidth); - db_set_dw(0, CHAT_MODULE, "roomheight", g_Settings.iHeight); - - DestroyMenu(g_hMenu); -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+pfnDoTrayIcon oldDoTrayIcon;
+pfnDoPopup oldDoPopup;
+
+SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si)
+{
+ int i = g_chatApi.arSessions.indexOf(si);
+ if (i == -1)
+ return nullptr;
+
+ for (i--; i >= 0; i--) {
+ SESSION_INFO *p = g_chatApi.arSessions[i];
+ if (p->pDlg)
+ return p;
+ }
+
+ return nullptr;
+}
+
+SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si)
+{
+ int i = g_chatApi.arSessions.indexOf(si);
+ if (i == -1)
+ return nullptr;
+
+ for (i++; i < g_chatApi.arSessions.getCount(); i++) {
+ SESSION_INFO *p = g_chatApi.arSessions[i];
+ if (p->pDlg)
+ return p;
+ }
+
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HMENU g_hMenu = nullptr;
+
+GlobalLogSettings g_Settings;
+
+static MODULEINFO* MM_CreateModule()
+{
+ return new MODULEINFO();
+}
+
+static void OnReplaceSession(SESSION_INFO *si)
+{
+ if (si->pDlg)
+ RedrawWindow(GetDlgItem(si->pDlg->GetHwnd(), IDC_SRMM_NICKLIST), nullptr, nullptr, RDW_INVALIDATE);
+}
+
+static void OnFlashHighlight(SESSION_INFO *si, int bInactive)
+{
+ if (!bInactive || si->pDlg == nullptr)
+ return;
+
+ if (g_Settings.bTabsEnable)
+ g_pTabDialog->SetMessageHighlight(si->pDlg);
+ else if (Chat::bFlashWindowHighlight)
+ si->pDlg->StartFlash();
+}
+
+static void OnFlashWindow(SESSION_INFO *si, int bInactive)
+{
+ if (!bInactive || si->pDlg == nullptr)
+ return;
+
+ if (g_Settings.bTabsEnable)
+ g_pTabDialog->SetTabHighlight(si->pDlg);
+ else if (Chat::bFlashWindow)
+ si->pDlg->StartFlash();
+}
+
+static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce)
+{
+ if (gce->iType & g_Settings.dwTrayIconFlags)
+ return oldDoTrayIcon(si, gce);
+ return TRUE;
+}
+
+static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce)
+{
+ if (gce->iType & g_Settings.dwPopupFlags)
+ return oldDoPopup(si, gce);
+ return TRUE;
+}
+
+static void OnLoadSettings()
+{
+ g_Settings.iX = db_get_dw(0, CHAT_MODULE, "roomx", -1);
+ g_Settings.iY = db_get_dw(0, CHAT_MODULE, "roomy", -1);
+
+ g_Settings.bTabsEnable = db_get_b(0, CHAT_MODULE, "Tabs", 1) != 0;
+ g_Settings.bTabsAtBottom = db_get_b(0, CHAT_MODULE, "TabBottom", 0) != 0;
+ g_Settings.bTabCloseOnDblClick = db_get_b(0, CHAT_MODULE, "TabCloseOnDblClick", 0) != 0;
+ g_Settings.bAddColonToAutoComplete = db_get_b(0, CHAT_MODULE, "AddColonToAutoComplete", 1) != 0;
+
+ g_Settings.iSplitterX = db_get_w(0, CHAT_MODULE, "SplitterX", 105);
+ if (g_Settings.iSplitterX <= 50)
+ g_Settings.iSplitterX = 105;
+ g_Settings.iSplitterY = db_get_w(0, CHAT_MODULE, "SplitterY", 90);
+ if (g_Settings.iSplitterY <= 65)
+ g_Settings.iSplitterY = 90;
+}
+
+static void RegisterFonts()
+{
+ ColourIDW colourid = {};
+ strncpy(colourid.dbSettingsGroup, CHAT_MODULE, sizeof(colourid.dbSettingsGroup));
+ wcsncpy(colourid.group, LPGENW("Message sessions") L"/" LPGENW("Chat module"), _countof(colourid.group));
+
+ strncpy(colourid.setting, "ColorLogBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Group chat log background"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorMessageBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Message background"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorNicklistBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Nick list background"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorNicklistLines", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Nick list lines"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorNicklistSelectedBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Nick list background (selected)"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT);
+ g_plugin.addColor(&colourid);
+}
+
+static void ShowRoom(SESSION_INFO *si)
+{
+ if (!si)
+ return;
+
+ // Do we need to create a window?
+ if (si->pDlg == nullptr) {
+ CTabbedWindow *pContainer = GetContainer();
+ if (g_Settings.bTabsEnable) {
+ pContainer->AddPage(si);
+ PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0);
+ }
+ else {
+ CMsgDialog *pDlg = pContainer->m_pEmbed = new CMsgDialog(pContainer, si);
+ pContainer->Create();
+ pDlg->SetParent(pContainer->GetHwnd());
+ pDlg->Create();
+ pContainer->Show();
+ pContainer->FixTabIcons(pDlg);
+ PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0);
+ }
+
+ if (si->iType != GCW_SERVER)
+ si->pDlg->UpdateNickList();
+ else
+ si->pDlg->UpdateTitle();
+ si->pDlg->UpdateStatusBar();
+ }
+ else if (g_Settings.bTabsEnable && g_pTabDialog)
+ g_pTabDialog->m_tab.ActivatePage(g_pTabDialog->m_tab.GetDlgIndex(si->pDlg));
+
+ SetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE) | WS_EX_APPWINDOW);
+
+ if (IsIconic(si->pDlg->GetHwnd()))
+ si->pDlg->Show(SW_NORMAL);
+ si->pDlg->Show(SW_SHOW);
+ SetForegroundWindow(si->pDlg->GetHwnd());
+}
+
+int OnCheckPlugins(WPARAM, LPARAM)
+{
+ g_plugin.bSmileyInstalled = ServiceExists(MS_SMILEYADD_REPLACESMILEYS);
+ return 0;
+}
+
+void Load_ChatModule()
+{
+ AddIcons();
+ RegisterFonts();
+
+ CHAT_MANAGER_INITDATA data = { &g_Settings, sizeof(MODULEINFO), sizeof(SESSION_INFO), LPGENW("Message sessions") L"/" LPGENW("Chat module"), FONTMODE_USE, &g_plugin };
+ Chat_CustomizeApi(&data);
+
+ g_chatApi.MM_CreateModule = MM_CreateModule;
+ g_chatApi.OnReplaceSession = OnReplaceSession;
+
+ g_chatApi.OnLoadSettings = OnLoadSettings;
+ g_chatApi.OnFlashWindow = OnFlashWindow;
+ g_chatApi.OnFlashHighlight = OnFlashHighlight;
+ g_chatApi.ShowRoom = ShowRoom;
+
+ Srmm_CreateHotkey(LPGEN("Messaging"), LPGEN("Send message"));
+
+ oldDoPopup = g_chatApi.DoPopup; g_chatApi.DoPopup = DoPopup;
+ oldDoTrayIcon = g_chatApi.DoTrayIcon; g_chatApi.DoTrayIcon = DoTrayIcon;
+ g_chatApi.ReloadSettings();
+
+ g_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENU));
+
+ HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
+}
+
+void Unload_ChatModule()
+{
+ db_set_w(0, CHAT_MODULE, "SplitterX", (uint16_t)g_Settings.iSplitterX);
+ db_set_w(0, CHAT_MODULE, "SplitterY", (uint16_t)g_Settings.iSplitterY);
+ db_set_dw(0, CHAT_MODULE, "roomx", g_Settings.iX);
+ db_set_dw(0, CHAT_MODULE, "roomy", g_Settings.iY);
+ db_set_dw(0, CHAT_MODULE, "roomwidth", g_Settings.iWidth);
+ db_set_dw(0, CHAT_MODULE, "roomheight", g_Settings.iHeight);
+
+ DestroyMenu(g_hMenu);
+}
diff --git a/src/core/stdmsg/src/chat_window.cpp b/src/core/stdmsg/src/chat_window.cpp index 52106ae755..8e1871dd4c 100644 --- a/src/core/stdmsg/src/chat_window.cpp +++ b/src/core/stdmsg/src/chat_window.cpp @@ -1,275 +1,275 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadSettings() -{ - m_clrInputBG = db_get_dw(0, CHAT_MODULE, "ColorMessageBG", GetSysColor(COLOR_WINDOW)); - LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::ShowFilterMenu() -{ - HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_hwnd, FilterWndProc, (LPARAM)this); - TranslateDialogDefault(hwnd); - - RECT rc; - GetWindowRect(m_btnFilter.GetHwnd(), &rc); - SetWindowPos(hwnd, HWND_TOP, rc.left - 85, (IsWindowVisible(m_btnFilter.GetHwnd()) || IsWindowVisible(m_btnBold.GetHwnd())) ? rc.top - 206 : rc.top - 186, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); -} - -void CMsgDialog::UpdateNickList() -{ - int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0); - m_nickList.SendMsg(LB_SETTOPINDEX, i, 0); - - UpdateTitle(); -} - -void CMsgDialog::UpdateOptions() -{ - m_btnNickList.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bNicklistEnabled ? IDI_NICKLIST2 : IDI_NICKLIST, FALSE)); - m_btnFilter.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bFilterEnabled ? IDI_FILTER2 : IDI_FILTER, FALSE)); - - HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); - SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); - DestroyIcon(hIcon); - - Window_SetIcon_IcoLib(m_pOwner->GetHwnd(), g_plugin.getIconHandle(IDI_CHANMGR)); - - m_pLog->UpdateOptions(); - - // nicklist - int ih = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListFont, FALSE); - int ih2 = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListHeadingsFont, FALSE); - int height = db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12); - int font = ih > ih2 ? ih : ih2; - - // make sure we have space for icon! - if (g_Settings.bShowContactStatus) - font = font > 16 ? font : 16; - - m_nickList.SendMsg(LB_SETITEMHEIGHT, 0, height > font ? height : font); - InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE); - - CSuper::UpdateOptions(); -} - -void CMsgDialog::UpdateStatusBar() -{ - wchar_t *ptszDispName = m_si->pMI->ptszModDispName; - int x = 12; - x += Chat_GetTextPixelSize(ptszDispName, (HFONT)SendMessage(m_pOwner->m_hwndStatus, WM_GETFONT, 0, 0), TRUE); - x += GetSystemMetrics(SM_CXSMICON); - int iStatusbarParts[2] = { x, -1 }; - SendMessage(m_pOwner->m_hwndStatus, SB_SETPARTS, 2, (LPARAM)&iStatusbarParts); - - HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); - SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); - DestroyIcon(hIcon); - - SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)ptszDispName); - SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); - SendMessage(m_pOwner->m_hwndStatus, SB_SETTIPTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) -{ - auto *si = m_pDlg.m_si; - if (lin == nullptr || si == nullptr) - return; - - if (!bRedraw && si->iType == GCW_CHATROOM && m_pDlg.m_bFilterEnabled && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0) - return; - - LOGSTREAMDATA streamData; - memset(&streamData, 0, sizeof(streamData)); - streamData.hwnd = m_rtf.GetHwnd(); - streamData.si = si; - streamData.lin = lin; - streamData.bStripFormat = FALSE; - - bool bFlag = false; - - EDITSTREAM stream = {}; - stream.pfnCallback = Srmm_LogStreamCallback; - stream.dwCookie = (DWORD_PTR)& streamData; - - SCROLLINFO scroll; - scroll.cbSize = sizeof(SCROLLINFO); - scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll); - - POINT point = {}; - m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point); - - // do not scroll to bottom if there is a selection - CHARRANGE oldsel, sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel); - if (oldsel.cpMax != oldsel.cpMin) - m_rtf.SetDraw(false); - - //set the insertion point at the bottom - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - - // fix for the indent... must be a M$ bug - if (sel.cpMax == 0) - bRedraw = TRUE; - - // should the event(s) be appended to the current log - WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF; - - //get the number of pixels per logical inch - if (bRedraw) { - HDC hdc = GetDC(nullptr); - g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); - g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); - ReleaseDC(nullptr, hdc); - m_rtf.SetDraw(false); - bFlag = true; - } - - // stream in the event(s) - streamData.lin = lin; - streamData.bRedraw = bRedraw; - m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream); - - // do smileys - if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) { - CHARRANGE newsel; - newsel.cpMax = -1; - newsel.cpMin = sel.cpMin; - if (newsel.cpMin < 0) - newsel.cpMin = 0; - - SMADD_RICHEDIT3 sm = {}; - sm.cbSize = sizeof(sm); - sm.hwndRichEditControl = m_rtf.GetHwnd(); - sm.Protocolname = si->pszModule; - sm.rangeToReplace = bRedraw ? nullptr : &newsel; - sm.disableRedraw = TRUE; - sm.hContact = si->hContact; - CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm); - } - - // scroll log to bottom if the log was previously scrolled to bottom, else restore old position - if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50) - ScrollToBottom(); - else - m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point); - - // do we need to restore the selection - if (oldsel.cpMax != oldsel.cpMin) { - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } - - // need to invalidate the window - if (bFlag) { - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - static CMsgDialog *pDlg = nullptr; - switch (uMsg) { - case WM_INITDIALOG: - pDlg = (CMsgDialog*)lParam; - CheckDlgButton(hwndDlg, IDC_1, pDlg->m_iLogFilterFlags & GC_EVENT_ACTION ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_2, pDlg->m_iLogFilterFlags & GC_EVENT_MESSAGE ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_3, pDlg->m_iLogFilterFlags & GC_EVENT_NICK ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_4, pDlg->m_iLogFilterFlags & GC_EVENT_JOIN ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_5, pDlg->m_iLogFilterFlags & GC_EVENT_PART ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_6, pDlg->m_iLogFilterFlags & GC_EVENT_TOPIC ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_7, pDlg->m_iLogFilterFlags & GC_EVENT_ADDSTATUS ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_8, pDlg->m_iLogFilterFlags & GC_EVENT_INFORMATION ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_9, pDlg->m_iLogFilterFlags & GC_EVENT_QUIT ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_10, pDlg->m_iLogFilterFlags & GC_EVENT_KICK ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_11, pDlg->m_iLogFilterFlags & GC_EVENT_NOTICE ? BST_CHECKED : BST_UNCHECKED); - break; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - SetTextColor((HDC)wParam, RGB(60, 60, 150)); - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) { - int iFlags = 0; - - if (IsDlgButtonChecked(hwndDlg, IDC_1) == BST_CHECKED) - iFlags |= GC_EVENT_ACTION; - if (IsDlgButtonChecked(hwndDlg, IDC_2) == BST_CHECKED) - iFlags |= GC_EVENT_MESSAGE; - if (IsDlgButtonChecked(hwndDlg, IDC_3) == BST_CHECKED) - iFlags |= GC_EVENT_NICK; - if (IsDlgButtonChecked(hwndDlg, IDC_4) == BST_CHECKED) - iFlags |= GC_EVENT_JOIN; - if (IsDlgButtonChecked(hwndDlg, IDC_5) == BST_CHECKED) - iFlags |= GC_EVENT_PART; - if (IsDlgButtonChecked(hwndDlg, IDC_6) == BST_CHECKED) - iFlags |= GC_EVENT_TOPIC; - if (IsDlgButtonChecked(hwndDlg, IDC_7) == BST_CHECKED) - iFlags |= GC_EVENT_ADDSTATUS; - if (IsDlgButtonChecked(hwndDlg, IDC_8) == BST_CHECKED) - iFlags |= GC_EVENT_INFORMATION; - if (IsDlgButtonChecked(hwndDlg, IDC_9) == BST_CHECKED) - iFlags |= GC_EVENT_QUIT; - if (IsDlgButtonChecked(hwndDlg, IDC_10) == BST_CHECKED) - iFlags |= GC_EVENT_KICK; - if (IsDlgButtonChecked(hwndDlg, IDC_11) == BST_CHECKED) - iFlags |= GC_EVENT_NOTICE; - - if (iFlags & GC_EVENT_ADDSTATUS) - iFlags |= GC_EVENT_REMOVESTATUS; - - pDlg->m_iLogFilterFlags = iFlags; - if (pDlg->m_bFilterEnabled) - pDlg->RedrawLog(); - PostMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - } - - return FALSE; -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSettings()
+{
+ m_clrInputBG = db_get_dw(0, CHAT_MODULE, "ColorMessageBG", GetSysColor(COLOR_WINDOW));
+ LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::ShowFilterMenu()
+{
+ HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_hwnd, FilterWndProc, (LPARAM)this);
+ TranslateDialogDefault(hwnd);
+
+ RECT rc;
+ GetWindowRect(m_btnFilter.GetHwnd(), &rc);
+ SetWindowPos(hwnd, HWND_TOP, rc.left - 85, (IsWindowVisible(m_btnFilter.GetHwnd()) || IsWindowVisible(m_btnBold.GetHwnd())) ? rc.top - 206 : rc.top - 186, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+void CMsgDialog::UpdateNickList()
+{
+ int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0);
+ m_nickList.SendMsg(LB_SETTOPINDEX, i, 0);
+
+ UpdateTitle();
+}
+
+void CMsgDialog::UpdateOptions()
+{
+ m_btnNickList.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bNicklistEnabled ? IDI_NICKLIST2 : IDI_NICKLIST, FALSE));
+ m_btnFilter.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bFilterEnabled ? IDI_FILTER2 : IDI_FILTER, FALSE));
+
+ HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT);
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon);
+ DestroyIcon(hIcon);
+
+ Window_SetIcon_IcoLib(m_pOwner->GetHwnd(), g_plugin.getIconHandle(IDI_CHANMGR));
+
+ m_pLog->UpdateOptions();
+
+ // nicklist
+ int ih = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListFont, FALSE);
+ int ih2 = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListHeadingsFont, FALSE);
+ int height = db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12);
+ int font = ih > ih2 ? ih : ih2;
+
+ // make sure we have space for icon!
+ if (g_Settings.bShowContactStatus)
+ font = font > 16 ? font : 16;
+
+ m_nickList.SendMsg(LB_SETITEMHEIGHT, 0, height > font ? height : font);
+ InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
+
+ CSuper::UpdateOptions();
+}
+
+void CMsgDialog::UpdateStatusBar()
+{
+ wchar_t *ptszDispName = m_si->pMI->ptszModDispName;
+ int x = 12;
+ x += Chat_GetTextPixelSize(ptszDispName, (HFONT)SendMessage(m_pOwner->m_hwndStatus, WM_GETFONT, 0, 0), TRUE);
+ x += GetSystemMetrics(SM_CXSMICON);
+ int iStatusbarParts[2] = { x, -1 };
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETPARTS, 2, (LPARAM)&iStatusbarParts);
+
+ HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT);
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon);
+ DestroyIcon(hIcon);
+
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)ptszDispName);
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L""));
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETTIPTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L""));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw)
+{
+ auto *si = m_pDlg.m_si;
+ if (lin == nullptr || si == nullptr)
+ return;
+
+ if (!bRedraw && si->iType == GCW_CHATROOM && m_pDlg.m_bFilterEnabled && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0)
+ return;
+
+ LOGSTREAMDATA streamData;
+ memset(&streamData, 0, sizeof(streamData));
+ streamData.hwnd = m_rtf.GetHwnd();
+ streamData.si = si;
+ streamData.lin = lin;
+ streamData.bStripFormat = FALSE;
+
+ bool bFlag = false;
+
+ EDITSTREAM stream = {};
+ stream.pfnCallback = Srmm_LogStreamCallback;
+ stream.dwCookie = (DWORD_PTR)& streamData;
+
+ SCROLLINFO scroll;
+ scroll.cbSize = sizeof(SCROLLINFO);
+ scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
+ GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll);
+
+ POINT point = {};
+ m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point);
+
+ // do not scroll to bottom if there is a selection
+ CHARRANGE oldsel, sel;
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel);
+ if (oldsel.cpMax != oldsel.cpMin)
+ m_rtf.SetDraw(false);
+
+ //set the insertion point at the bottom
+ sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength();
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+
+ // fix for the indent... must be a M$ bug
+ if (sel.cpMax == 0)
+ bRedraw = TRUE;
+
+ // should the event(s) be appended to the current log
+ WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF;
+
+ //get the number of pixels per logical inch
+ if (bRedraw) {
+ HDC hdc = GetDC(nullptr);
+ g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY);
+ g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX);
+ ReleaseDC(nullptr, hdc);
+ m_rtf.SetDraw(false);
+ bFlag = true;
+ }
+
+ // stream in the event(s)
+ streamData.lin = lin;
+ streamData.bRedraw = bRedraw;
+ m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream);
+
+ // do smileys
+ if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) {
+ CHARRANGE newsel;
+ newsel.cpMax = -1;
+ newsel.cpMin = sel.cpMin;
+ if (newsel.cpMin < 0)
+ newsel.cpMin = 0;
+
+ SMADD_RICHEDIT3 sm = {};
+ sm.cbSize = sizeof(sm);
+ sm.hwndRichEditControl = m_rtf.GetHwnd();
+ sm.Protocolname = si->pszModule;
+ sm.rangeToReplace = bRedraw ? nullptr : &newsel;
+ sm.disableRedraw = TRUE;
+ sm.hContact = si->hContact;
+ CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm);
+ }
+
+ // scroll log to bottom if the log was previously scrolled to bottom, else restore old position
+ if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50)
+ ScrollToBottom();
+ else
+ m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point);
+
+ // do we need to restore the selection
+ if (oldsel.cpMax != oldsel.cpMin) {
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel);
+ m_rtf.SetDraw(true);
+ InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE);
+ }
+
+ // need to invalidate the window
+ if (bFlag) {
+ sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength();
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+ m_rtf.SetDraw(true);
+ InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static CMsgDialog *pDlg = nullptr;
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ pDlg = (CMsgDialog*)lParam;
+ CheckDlgButton(hwndDlg, IDC_1, pDlg->m_iLogFilterFlags & GC_EVENT_ACTION ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_2, pDlg->m_iLogFilterFlags & GC_EVENT_MESSAGE ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_3, pDlg->m_iLogFilterFlags & GC_EVENT_NICK ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_4, pDlg->m_iLogFilterFlags & GC_EVENT_JOIN ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_5, pDlg->m_iLogFilterFlags & GC_EVENT_PART ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_6, pDlg->m_iLogFilterFlags & GC_EVENT_TOPIC ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_7, pDlg->m_iLogFilterFlags & GC_EVENT_ADDSTATUS ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_8, pDlg->m_iLogFilterFlags & GC_EVENT_INFORMATION ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_9, pDlg->m_iLogFilterFlags & GC_EVENT_QUIT ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_10, pDlg->m_iLogFilterFlags & GC_EVENT_KICK ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_11, pDlg->m_iLogFilterFlags & GC_EVENT_NOTICE ? BST_CHECKED : BST_UNCHECKED);
+ break;
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, RGB(60, 60, 150));
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ int iFlags = 0;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_1) == BST_CHECKED)
+ iFlags |= GC_EVENT_ACTION;
+ if (IsDlgButtonChecked(hwndDlg, IDC_2) == BST_CHECKED)
+ iFlags |= GC_EVENT_MESSAGE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_3) == BST_CHECKED)
+ iFlags |= GC_EVENT_NICK;
+ if (IsDlgButtonChecked(hwndDlg, IDC_4) == BST_CHECKED)
+ iFlags |= GC_EVENT_JOIN;
+ if (IsDlgButtonChecked(hwndDlg, IDC_5) == BST_CHECKED)
+ iFlags |= GC_EVENT_PART;
+ if (IsDlgButtonChecked(hwndDlg, IDC_6) == BST_CHECKED)
+ iFlags |= GC_EVENT_TOPIC;
+ if (IsDlgButtonChecked(hwndDlg, IDC_7) == BST_CHECKED)
+ iFlags |= GC_EVENT_ADDSTATUS;
+ if (IsDlgButtonChecked(hwndDlg, IDC_8) == BST_CHECKED)
+ iFlags |= GC_EVENT_INFORMATION;
+ if (IsDlgButtonChecked(hwndDlg, IDC_9) == BST_CHECKED)
+ iFlags |= GC_EVENT_QUIT;
+ if (IsDlgButtonChecked(hwndDlg, IDC_10) == BST_CHECKED)
+ iFlags |= GC_EVENT_KICK;
+ if (IsDlgButtonChecked(hwndDlg, IDC_11) == BST_CHECKED)
+ iFlags |= GC_EVENT_NOTICE;
+
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ pDlg->m_iLogFilterFlags = iFlags;
+ if (pDlg->m_bFilterEnabled)
+ pDlg->RedrawLog();
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/src/core/stdmsg/src/cmdlist.cpp b/src/core/stdmsg/src/cmdlist.cpp index beb669dcf5..1ace5b0545 100644 --- a/src/core/stdmsg/src/cmdlist.cpp +++ b/src/core/stdmsg/src/cmdlist.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/cmdlist.h b/src/core/stdmsg/src/cmdlist.h index bbe8c9b11b..fda5562689 100644 --- a/src/core/stdmsg/src/cmdlist.h +++ b/src/core/stdmsg/src/cmdlist.h @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/globals.cpp b/src/core/stdmsg/src/globals.cpp index de2ca11c6d..5d2b95cc46 100644 --- a/src/core/stdmsg/src/globals.cpp +++ b/src/core/stdmsg/src/globals.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgdialog.cpp b/src/core/stdmsg/src/msgdialog.cpp index 8e0c4ff3ed..359b308022 100644 --- a/src/core/stdmsg/src/msgdialog.cpp +++ b/src/core/stdmsg/src/msgdialog.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msglog.cpp b/src/core/stdmsg/src/msglog.cpp index 8fb1f5edbd..791cd1b71e 100644 --- a/src/core/stdmsg/src/msglog.cpp +++ b/src/core/stdmsg/src/msglog.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgoptions.cpp b/src/core/stdmsg/src/msgoptions.cpp index 590afa2478..34dc8e044f 100644 --- a/src/core/stdmsg/src/msgoptions.cpp +++ b/src/core/stdmsg/src/msgoptions.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgs.cpp b/src/core/stdmsg/src/msgs.cpp index 95b209fd02..9f5be7be28 100644 --- a/src/core/stdmsg/src/msgs.cpp +++ b/src/core/stdmsg/src/msgs.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgs.h b/src/core/stdmsg/src/msgs.h index f21110e641..0e9f38dead 100644 --- a/src/core/stdmsg/src/msgs.h +++ b/src/core/stdmsg/src/msgs.h @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgtimedout.cpp b/src/core/stdmsg/src/msgtimedout.cpp index 19744fe695..828924580e 100644 --- a/src/core/stdmsg/src/msgtimedout.cpp +++ b/src/core/stdmsg/src/msgtimedout.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/srmm.cpp b/src/core/stdmsg/src/srmm.cpp index 69c350aad7..4485d40bb3 100644 --- a/src/core/stdmsg/src/srmm.cpp +++ b/src/core/stdmsg/src/srmm.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/statusicon.cpp b/src/core/stdmsg/src/statusicon.cpp index 4244f9037c..6255dc3e1e 100644 --- a/src/core/stdmsg/src/statusicon.cpp +++ b/src/core/stdmsg/src/statusicon.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/stdafx.cxx b/src/core/stdmsg/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/core/stdmsg/src/stdafx.cxx +++ b/src/core/stdmsg/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/core/stdmsg/src/stdafx.h b/src/core/stdmsg/src/stdafx.h index a42feafac9..33d557a6a9 100644 --- a/src/core/stdmsg/src/stdafx.h +++ b/src/core/stdmsg/src/stdafx.h @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/tabs.cpp b/src/core/stdmsg/src/tabs.cpp index d60bec7634..402d1f2f0f 100644 --- a/src/core/stdmsg/src/tabs.cpp +++ b/src/core/stdmsg/src/tabs.cpp @@ -1,668 +1,668 @@ -/* - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "statusicon.h" - -CTabbedWindow *g_pTabDialog = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow* GetContainer() -{ - if (g_Settings.bTabsEnable) { - if (g_pTabDialog == nullptr) { - g_pTabDialog = new CTabbedWindow(); - g_pTabDialog->Show(); - } - return g_pTabDialog; - } - - return new CTabbedWindow(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK TabSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CTabbedWindow *pOwner = (CTabbedWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - TCHITTESTINFO tci = {}; - - static bool bDragging = false; - static int iBeginIndex = 0; - switch (msg) { - case WM_LBUTTONDOWN: - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) { - tci.flags = TCHT_ONITEM; - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) { - bDragging = true; - iBeginIndex = idx; - ImageList_BeginDrag(Clist_GetImageList(), pDlg->GetImageId(), 8, 8); - ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y); - SetCapture(hwnd); - } - return TRUE; - } - } - else pOwner->TabClicked(); - break; - - case WM_CAPTURECHANGED: - bDragging = false; - ImageList_DragLeave(hwnd); - ImageList_EndDrag(); - break; - - case WM_MOUSEMOVE: - if (bDragging) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(hwnd, &tci.pt); - ImageList_DragMove(tci.pt.x, tci.pt.y); - } - break; - - case WM_LBUTTONUP: - if (bDragging && ReleaseCapture()) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - bDragging = false; - ImageList_DragLeave(hwnd); - ImageList_EndDrag(); - - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1 && idx != iBeginIndex) - pOwner->DropTab(idx, iBeginIndex); - } - break; - - case WM_LBUTTONDBLCLK: - if (g_Settings.bTabCloseOnDblClick) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(hwnd, &tci.pt); - - tci.flags = TCHT_ONITEM; - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) - pDlg->CloseTab(); - } - } - break; - - case WM_MBUTTONUP: - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) - pDlg->CloseTab(); - } - break; - } - - return mir_callNextSubclass(hwnd, TabSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow::CTabbedWindow() : - CDlgBase(g_plugin, IDD_CONTAINER), - m_tab(this, IDC_TAB) -{ - SetMinSize(450, 350); -} - -bool CTabbedWindow::OnInitDialog() -{ - SetWindowLongPtr(m_tab.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_tab.GetHwnd(), ::TabSubclassProc); - - m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr); - SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); - - RECT rc; - GetWindowRect(m_hwndStatus, &rc); - m_statusHeight = rc.bottom - rc.top; - - SetWindowPosition(); - - if (!g_Settings.bTabsEnable) { - m_tab.Hide(); - return false; - } - - LONG_PTR mask = GetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE); - if (g_Settings.bTabsAtBottom) - mask |= TCS_BOTTOM; - else - mask &= ~TCS_BOTTOM; - SetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE, mask); - - TabCtrl_SetMinTabWidth(m_tab.GetHwnd(), 80); - TabCtrl_SetImageList(m_tab.GetHwnd(), Clist_GetImageList()); - return true; -} - -void CTabbedWindow::OnDestroy() -{ - DestroyWindow(m_hwndStatus); m_hwndStatus = nullptr; - - SaveWindowPosition(true); - - Utils_SaveWindowPosition(m_hwnd, g_plugin.bSavePerContact ? ((m_pEmbed == nullptr) ? 0 : m_pEmbed->m_hContact) : 0, CHAT_MODULE, "room"); - - if (m_pEmbed == nullptr) - g_pTabDialog = nullptr; -} - -void CTabbedWindow::OnResize() -{ - CDlgBase::OnResize(); - - SendMessage(m_hwndStatus, WM_SIZE, 0, 0); - - if (m_pEmbed) { - RECT rc; - GetClientRect(m_tab.GetHwnd(), &rc); - MoveWindow(m_pEmbed->GetHwnd(), 0, 0, rc.right - rc.left, rc.bottom - rc.top, FALSE); - } - - SaveWindowPosition(false); -} - -int CTabbedWindow::Resizer(UTILRESIZECONTROL *urc) -{ - if (urc->wId == IDC_TAB) { - urc->rcItem.top = 1; - urc->rcItem.bottom = urc->dlgNewSize.cy - m_statusHeight - 1; - return RD_ANCHORX_WIDTH | RD_ANCHORY_CUSTOM; - } - - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; // status bar -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow* CTabbedWindow::AddPage(MCONTACT hContact, wchar_t *pwszText, int iNoActivate) -{ - CMsgDialog *pDlg = new CMsgDialog(this, hContact); - pDlg->m_wszInitialText = pwszText; - if (iNoActivate != -1) - pDlg->m_bNoActivate = iNoActivate != 0; - - if (g_Settings.bTabsEnable) { - m_tab.AddPage(Clist_GetContactDisplayName(hContact), nullptr, pDlg); - FixTabIcons(pDlg); - - m_tab.ActivatePage(m_tab.GetCount() - 1); - } - else { - m_pEmbed = pDlg; - Create(); - pDlg->SetParent(m_hwnd); - pDlg->Create(); - Show(); - } - - PostMessage(m_hwnd, WM_SIZE, 0, 0); - return this; -} - -void CTabbedWindow::AddPage(SESSION_INFO *si, int insertAt) -{ - // does the tab already exist? - int indexfound = (si->pDlg) ? m_tab.GetDlgIndex(si->pDlg) : -1; - if (indexfound == -1) { // create a new tab - wchar_t szTemp[30]; - mir_wstrncpy(szTemp, si->ptszName, 21); - if (mir_wstrlen(si->ptszName) > 20) - mir_wstrncpy(szTemp + 20, L"...", 4); - - if (!IsWindowVisible(m_hwnd)) - Show(SW_SHOW); - - CMsgDialog *pDlg = new CMsgDialog(this, si); - pDlg->SetParent(m_hwnd); - m_tab.AddPage(szTemp, nullptr, pDlg); - m_tab.ActivatePage(m_tab.GetCount() - 1); - FixTabIcons(pDlg); - } - else if (insertAt == -1) - m_tab.ActivatePage(indexfound); -} - -CMsgDialog* CTabbedWindow::CurrPage() const -{ - return (m_pEmbed != nullptr) ? m_pEmbed : (CMsgDialog*)m_tab.GetActivePage(); -} - -void CTabbedWindow::DropTab(int begin, int end) -{ - if (begin == end) - return; - - m_tab.SwapPages(begin, end); - - CMsgDialog *pDlg = (CMsgDialog *)m_tab.GetNthPage(end); - if (pDlg) { - FixTabIcons(pDlg); - m_tab.ActivatePage(end); - } - - // fix the "fixed" positions - int tabCount = m_tab.GetCount(); - for (int i = 0; i < tabCount; i++) { - pDlg = (CMsgDialog *)m_tab.GetNthPage(i); - if (pDlg == nullptr) - continue; - - SESSION_INFO *si = pDlg->m_si; - if (si && si->hContact && db_get_w(si->hContact, si->pszModule, "TabPosition", 0) != 0) - db_set_w(si->hContact, si->pszModule, "TabPosition", i + 1); - } -} - -void CTabbedWindow::FixTabIcons(CMsgDialog *pDlg) -{ - if (pDlg == nullptr) - return; - - int image = pDlg->GetImageId(); - - // if tabs are turned off, simply change the window's icon, otherwise set the tab's icon first - if (m_pEmbed == nullptr) { - int idx = m_tab.GetDlgIndex(pDlg); - if (idx == -1) - return; - - TCITEM tci = {}; - tci.mask = TCIF_IMAGE; - TabCtrl_GetItem(m_tab.GetHwnd(), idx, &tci); - if (tci.iImage != image) { - tci.iImage = image; - TabCtrl_SetItem(m_tab.GetHwnd(), idx, &tci); - } - } - - // set the container's icon only if we're processing the current page - if (pDlg == CurrPage()) { - Window_FreeIcon_IcoLib(m_hwnd); - if (g_plugin.bUseStatusWinIcon) - Window_SetProtoIcon_IcoLib(m_hwnd, pDlg->m_szProto, pDlg->m_wStatus); - else if (pDlg->isChat()) - Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_CHANMGR)); - else - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_EVENT_MESSAGE); - } -} - -void CTabbedWindow::RemoveTab(CMsgDialog *pDlg) -{ - int idx = m_tab.GetDlgIndex(pDlg); - if (idx == -1) - return; - - m_tab.RemovePage(idx); - if (m_tab.GetCount() == 0) - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - else { - if (m_tab.GetNthPage(idx) == nullptr) - idx--; - m_tab.ActivatePage(idx); - } -} - -void CTabbedWindow::SaveWindowPosition(bool bUpdateSession) -{ - WINDOWPLACEMENT wp = {}; - wp.length = sizeof(wp); - GetWindowPlacement(m_hwnd, &wp); - - g_Settings.iX = wp.rcNormalPosition.left; - g_Settings.iY = wp.rcNormalPosition.top; - g_Settings.iWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - g_Settings.iHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; - - if (bUpdateSession) { - iX = g_Settings.iX; - iY = g_Settings.iY; - iWidth = g_Settings.iWidth; - iHeight = g_Settings.iHeight; - } -} - -void CTabbedWindow::SetMessageHighlight(CMsgDialog *pDlg) -{ - if (m_tab.GetDlgIndex(pDlg) == -1) - return; - - pDlg->m_si->wState |= GC_EVENT_HIGHLIGHT; - FixTabIcons(pDlg); - if (Chat::bFlashWindowHighlight && pDlg != m_tab.GetActivePage()) - pDlg->StartFlash(); -} - -void CTabbedWindow::SetTabHighlight(CMsgDialog *pDlg) -{ - if (m_tab.GetDlgIndex(pDlg) == -1) - return; - - FixTabIcons(pDlg); - if (Chat::bFlashWindow && pDlg != m_tab.GetActivePage()) - pDlg->StartFlash(); -} - -void CTabbedWindow::SetWindowPosition() -{ - if (m_pEmbed == nullptr) { - Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room"); - return; - } - - if (iX) { - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(m_hwnd, &wp); - - wp.rcNormalPosition.left = iX; - wp.rcNormalPosition.top = iY; - wp.rcNormalPosition.right = wp.rcNormalPosition.left + iWidth; - wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + iHeight; - wp.showCmd = SW_HIDE; - SetWindowPlacement(m_hwnd, &wp); - return; - } - - int flag = m_pEmbed->m_bNoActivate ? RWPF_HIDDEN : 0; - if (Utils_RestoreWindowPosition(m_hwnd, g_plugin.bSavePerContact ? m_pEmbed->m_hContact : 0, CHAT_MODULE, "room", flag)) { - if (g_plugin.bSavePerContact) { - if (Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room", flag | RWPF_NOMOVE)) - SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); - } - else SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); - } - - if (!g_plugin.bSavePerContact && g_plugin.bCascade) { - RECT rc, rcMax = {}; - CTabbedWindow *pMaxTab = nullptr; - - for (auto &it : g_arDialogs) { - auto *pTab = it->m_pOwner; - if (pTab == this) - continue; - - GetWindowRect(pTab->GetHwnd(), &rc); - if (rc.left > rcMax.left && rc.top > rcMax.top) { - pMaxTab = pTab; - rcMax = rc; - } - } - - if (pMaxTab) { - m_windowWasCascaded = 1; - int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME); - SetWindowPos(m_hwnd, nullptr, rcMax.left + offset, rcMax.top + offset, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); - } - } -} - -void CTabbedWindow::SwitchNextTab() -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total > 1) { - if (i < total - 1) - i++; - else - i = 0; - m_tab.ActivatePage(i); - TabClicked(); - } -} - -void CTabbedWindow::SwitchPrevTab() -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total >= 1) { - if (i > 0) - i--; - else - i = total - 1; - m_tab.ActivatePage(i); - TabClicked(); - } -} - -void CTabbedWindow::SwitchTab(int iNewTab) -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total != -1 && total != 1 && i != iNewTab && total > iNewTab) { - m_tab.ActivatePage(iNewTab); - TabClicked(); - } -} - -void CTabbedWindow::TabClicked() -{ - CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetActivePage(); - if (pDlg == nullptr) - return; - - SetFocus(pDlg->m_message.GetHwnd()); - - SESSION_INFO *si = pDlg->m_si; - if (si) { - if (si->wState & STATE_TALK) { - si->wState &= ~STATE_TALK; - db_set_w(si->hContact, si->pszModule, "ApparentMode", 0); - } - - if (si->wState & GC_EVENT_HIGHLIGHT) { - si->wState &= ~GC_EVENT_HIGHLIGHT; - - if (g_clistApi.pfnGetEvent(si->hContact, 0)) - g_clistApi.pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); - } - - if (!si->pDlg) { - g_chatApi.ShowRoom(si); - SendMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - } - } - - FixTabIcons(pDlg); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CTabbedWindow::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - int idx; - - switch (msg) { - case WM_MOVE: - SaveWindowPosition(false); - break; - - case WM_ENTERSIZEMOVE: - GetClientRect(m_hwnd, &rc); - oldSizeX = rc.right - rc.left; - oldSizeY = rc.bottom - rc.top; - break; - - case WM_EXITSIZEMOVE: - GetClientRect(m_hwnd, &rc); - if (!((rc.right - rc.left) == oldSizeX && (rc.bottom - rc.top) == oldSizeY)) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) { - pDlg->m_pLog->ScrollToBottom(); - pDlg->Resize(); - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; - if (dis->hwndItem == m_hwndStatus) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) - DrawStatusIcons(pDlg->m_hContact, dis->hDC, dis->rcItem, 2); - return TRUE; - } - } - break; - - case WM_COMMAND: - if (LOWORD(wParam) == IDOK) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) { - pDlg->m_btnOk.Click(); - return TRUE; - } - } - break; - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) - break; - - if (!m_pEmbed) { - idx = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (idx != -1) - m_tab.ActivatePage(idx); - } - else m_pEmbed->OnActivate(); - break; - - case WM_NOTIFY: - if (((LPNMHDR)lParam)->hwndFrom == m_hwndStatus) { - if (((LPNMHDR)lParam)->code == NM_CLICK || ((LPNMHDR)lParam)->code == NM_RCLICK) { - NMMOUSE *nm = (NMMOUSE *)lParam; - SendMessage(m_hwndStatus, SB_GETRECT, SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0) - 1, (LPARAM)&rc); - if (nm->pt.x >= rc.left) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) - CheckStatusIconClick(pDlg->m_hContact, m_hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0); - } - return TRUE; - } - } - - if (((LPNMHDR)lParam)->idFrom == IDC_TAB) { - switch (((LPNMHDR)lParam)->code) { - case TCN_SELCHANGE: - m_tab.ActivatePage(TabCtrl_GetCurSel(m_tab.GetHwnd())); - break; - - case NM_RCLICK: - int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); - if (i == -1) - break; - - TCHITTESTINFO tci = {}; - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - ScreenToClient(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); - if ((i = TabCtrl_HitTest(((LPNMHDR)lParam)->hwndFrom, &tci)) == -1) - break; - - CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetNthPage(i); - SESSION_INFO *si = pDlg->m_si; - - ClientToScreen(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); - HMENU hSubMenu = GetSubMenu(g_hMenu, 1); - TranslateMenu(hSubMenu); - - if (si != nullptr) { - uint16_t w = db_get_w(si->hContact, si->pszModule, "TabPosition", 0); - if (w == 0) - CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); - else - CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_CHECKED); - } - else CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, tci.pt.x, tci.pt.y, 0, m_hwnd, nullptr)) { - case ID_CLOSE: - pDlg->CloseTab(); - break; - - case ID_LOCKPOSITION: - if (si != nullptr) { - if (!(GetMenuState(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND)&MF_CHECKED)) { - if (si->hContact) - db_set_w(si->hContact, si->pszModule, "TabPosition", (uint16_t)(i + 1)); - } - else db_unset(si->hContact, si->pszModule, "TabPosition"); - } - break; - - case ID_CLOSEOTHER: - int tabCount = m_tab.GetCount(); - if (tabCount > 1) { - while (tabCount--) { - if (tabCount == i) - continue; - - if (pDlg = (CMsgDialog*)m_tab.GetNthPage(tabCount)) - pDlg->CloseTab(); - } - m_tab.ActivatePage(0); - } - break; - } - } - } - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void UninitTabs() -{ - if (g_pTabDialog != nullptr) { - g_pTabDialog->Close(); - g_pTabDialog = nullptr; - } -} +/*
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "statusicon.h"
+
+CTabbedWindow *g_pTabDialog = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CTabbedWindow* GetContainer()
+{
+ if (g_Settings.bTabsEnable) {
+ if (g_pTabDialog == nullptr) {
+ g_pTabDialog = new CTabbedWindow();
+ g_pTabDialog->Show();
+ }
+ return g_pTabDialog;
+ }
+
+ return new CTabbedWindow();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK TabSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CTabbedWindow *pOwner = (CTabbedWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ TCHITTESTINFO tci = {};
+
+ static bool bDragging = false;
+ static int iBeginIndex = 0;
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) {
+ tci.flags = TCHT_ONITEM;
+ ScreenToClient(hwnd, &tci.pt);
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1) {
+ CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx);
+ if (pDlg) {
+ bDragging = true;
+ iBeginIndex = idx;
+ ImageList_BeginDrag(Clist_GetImageList(), pDlg->GetImageId(), 8, 8);
+ ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y);
+ SetCapture(hwnd);
+ }
+ return TRUE;
+ }
+ }
+ else pOwner->TabClicked();
+ break;
+
+ case WM_CAPTURECHANGED:
+ bDragging = false;
+ ImageList_DragLeave(hwnd);
+ ImageList_EndDrag();
+ break;
+
+ case WM_MOUSEMOVE:
+ if (bDragging) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(hwnd, &tci.pt);
+ ImageList_DragMove(tci.pt.x, tci.pt.y);
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (bDragging && ReleaseCapture()) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+ bDragging = false;
+ ImageList_DragLeave(hwnd);
+ ImageList_EndDrag();
+
+ ScreenToClient(hwnd, &tci.pt);
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1 && idx != iBeginIndex)
+ pOwner->DropTab(idx, iBeginIndex);
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ if (g_Settings.bTabCloseOnDblClick) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(hwnd, &tci.pt);
+
+ tci.flags = TCHT_ONITEM;
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1) {
+ CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx);
+ if (pDlg)
+ pDlg->CloseTab();
+ }
+ }
+ break;
+
+ case WM_MBUTTONUP:
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+
+ ScreenToClient(hwnd, &tci.pt);
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1) {
+ CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx);
+ if (pDlg)
+ pDlg->CloseTab();
+ }
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, TabSubclassProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CTabbedWindow::CTabbedWindow() :
+ CDlgBase(g_plugin, IDD_CONTAINER),
+ m_tab(this, IDC_TAB)
+{
+ SetMinSize(450, 350);
+}
+
+bool CTabbedWindow::OnInitDialog()
+{
+ SetWindowLongPtr(m_tab.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_tab.GetHwnd(), ::TabSubclassProc);
+
+ m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr);
+ SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0);
+
+ RECT rc;
+ GetWindowRect(m_hwndStatus, &rc);
+ m_statusHeight = rc.bottom - rc.top;
+
+ SetWindowPosition();
+
+ if (!g_Settings.bTabsEnable) {
+ m_tab.Hide();
+ return false;
+ }
+
+ LONG_PTR mask = GetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE);
+ if (g_Settings.bTabsAtBottom)
+ mask |= TCS_BOTTOM;
+ else
+ mask &= ~TCS_BOTTOM;
+ SetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE, mask);
+
+ TabCtrl_SetMinTabWidth(m_tab.GetHwnd(), 80);
+ TabCtrl_SetImageList(m_tab.GetHwnd(), Clist_GetImageList());
+ return true;
+}
+
+void CTabbedWindow::OnDestroy()
+{
+ DestroyWindow(m_hwndStatus); m_hwndStatus = nullptr;
+
+ SaveWindowPosition(true);
+
+ Utils_SaveWindowPosition(m_hwnd, g_plugin.bSavePerContact ? ((m_pEmbed == nullptr) ? 0 : m_pEmbed->m_hContact) : 0, CHAT_MODULE, "room");
+
+ if (m_pEmbed == nullptr)
+ g_pTabDialog = nullptr;
+}
+
+void CTabbedWindow::OnResize()
+{
+ CDlgBase::OnResize();
+
+ SendMessage(m_hwndStatus, WM_SIZE, 0, 0);
+
+ if (m_pEmbed) {
+ RECT rc;
+ GetClientRect(m_tab.GetHwnd(), &rc);
+ MoveWindow(m_pEmbed->GetHwnd(), 0, 0, rc.right - rc.left, rc.bottom - rc.top, FALSE);
+ }
+
+ SaveWindowPosition(false);
+}
+
+int CTabbedWindow::Resizer(UTILRESIZECONTROL *urc)
+{
+ if (urc->wId == IDC_TAB) {
+ urc->rcItem.top = 1;
+ urc->rcItem.bottom = urc->dlgNewSize.cy - m_statusHeight - 1;
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_CUSTOM;
+ }
+
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; // status bar
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CTabbedWindow* CTabbedWindow::AddPage(MCONTACT hContact, wchar_t *pwszText, int iNoActivate)
+{
+ CMsgDialog *pDlg = new CMsgDialog(this, hContact);
+ pDlg->m_wszInitialText = pwszText;
+ if (iNoActivate != -1)
+ pDlg->m_bNoActivate = iNoActivate != 0;
+
+ if (g_Settings.bTabsEnable) {
+ m_tab.AddPage(Clist_GetContactDisplayName(hContact), nullptr, pDlg);
+ FixTabIcons(pDlg);
+
+ m_tab.ActivatePage(m_tab.GetCount() - 1);
+ }
+ else {
+ m_pEmbed = pDlg;
+ Create();
+ pDlg->SetParent(m_hwnd);
+ pDlg->Create();
+ Show();
+ }
+
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ return this;
+}
+
+void CTabbedWindow::AddPage(SESSION_INFO *si, int insertAt)
+{
+ // does the tab already exist?
+ int indexfound = (si->pDlg) ? m_tab.GetDlgIndex(si->pDlg) : -1;
+ if (indexfound == -1) { // create a new tab
+ wchar_t szTemp[30];
+ mir_wstrncpy(szTemp, si->ptszName, 21);
+ if (mir_wstrlen(si->ptszName) > 20)
+ mir_wstrncpy(szTemp + 20, L"...", 4);
+
+ if (!IsWindowVisible(m_hwnd))
+ Show(SW_SHOW);
+
+ CMsgDialog *pDlg = new CMsgDialog(this, si);
+ pDlg->SetParent(m_hwnd);
+ m_tab.AddPage(szTemp, nullptr, pDlg);
+ m_tab.ActivatePage(m_tab.GetCount() - 1);
+ FixTabIcons(pDlg);
+ }
+ else if (insertAt == -1)
+ m_tab.ActivatePage(indexfound);
+}
+
+CMsgDialog* CTabbedWindow::CurrPage() const
+{
+ return (m_pEmbed != nullptr) ? m_pEmbed : (CMsgDialog*)m_tab.GetActivePage();
+}
+
+void CTabbedWindow::DropTab(int begin, int end)
+{
+ if (begin == end)
+ return;
+
+ m_tab.SwapPages(begin, end);
+
+ CMsgDialog *pDlg = (CMsgDialog *)m_tab.GetNthPage(end);
+ if (pDlg) {
+ FixTabIcons(pDlg);
+ m_tab.ActivatePage(end);
+ }
+
+ // fix the "fixed" positions
+ int tabCount = m_tab.GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ pDlg = (CMsgDialog *)m_tab.GetNthPage(i);
+ if (pDlg == nullptr)
+ continue;
+
+ SESSION_INFO *si = pDlg->m_si;
+ if (si && si->hContact && db_get_w(si->hContact, si->pszModule, "TabPosition", 0) != 0)
+ db_set_w(si->hContact, si->pszModule, "TabPosition", i + 1);
+ }
+}
+
+void CTabbedWindow::FixTabIcons(CMsgDialog *pDlg)
+{
+ if (pDlg == nullptr)
+ return;
+
+ int image = pDlg->GetImageId();
+
+ // if tabs are turned off, simply change the window's icon, otherwise set the tab's icon first
+ if (m_pEmbed == nullptr) {
+ int idx = m_tab.GetDlgIndex(pDlg);
+ if (idx == -1)
+ return;
+
+ TCITEM tci = {};
+ tci.mask = TCIF_IMAGE;
+ TabCtrl_GetItem(m_tab.GetHwnd(), idx, &tci);
+ if (tci.iImage != image) {
+ tci.iImage = image;
+ TabCtrl_SetItem(m_tab.GetHwnd(), idx, &tci);
+ }
+ }
+
+ // set the container's icon only if we're processing the current page
+ if (pDlg == CurrPage()) {
+ Window_FreeIcon_IcoLib(m_hwnd);
+ if (g_plugin.bUseStatusWinIcon)
+ Window_SetProtoIcon_IcoLib(m_hwnd, pDlg->m_szProto, pDlg->m_wStatus);
+ else if (pDlg->isChat())
+ Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_CHANMGR));
+ else
+ Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_EVENT_MESSAGE);
+ }
+}
+
+void CTabbedWindow::RemoveTab(CMsgDialog *pDlg)
+{
+ int idx = m_tab.GetDlgIndex(pDlg);
+ if (idx == -1)
+ return;
+
+ m_tab.RemovePage(idx);
+ if (m_tab.GetCount() == 0)
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ else {
+ if (m_tab.GetNthPage(idx) == nullptr)
+ idx--;
+ m_tab.ActivatePage(idx);
+ }
+}
+
+void CTabbedWindow::SaveWindowPosition(bool bUpdateSession)
+{
+ WINDOWPLACEMENT wp = {};
+ wp.length = sizeof(wp);
+ GetWindowPlacement(m_hwnd, &wp);
+
+ g_Settings.iX = wp.rcNormalPosition.left;
+ g_Settings.iY = wp.rcNormalPosition.top;
+ g_Settings.iWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+ g_Settings.iHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+
+ if (bUpdateSession) {
+ iX = g_Settings.iX;
+ iY = g_Settings.iY;
+ iWidth = g_Settings.iWidth;
+ iHeight = g_Settings.iHeight;
+ }
+}
+
+void CTabbedWindow::SetMessageHighlight(CMsgDialog *pDlg)
+{
+ if (m_tab.GetDlgIndex(pDlg) == -1)
+ return;
+
+ pDlg->m_si->wState |= GC_EVENT_HIGHLIGHT;
+ FixTabIcons(pDlg);
+ if (Chat::bFlashWindowHighlight && pDlg != m_tab.GetActivePage())
+ pDlg->StartFlash();
+}
+
+void CTabbedWindow::SetTabHighlight(CMsgDialog *pDlg)
+{
+ if (m_tab.GetDlgIndex(pDlg) == -1)
+ return;
+
+ FixTabIcons(pDlg);
+ if (Chat::bFlashWindow && pDlg != m_tab.GetActivePage())
+ pDlg->StartFlash();
+}
+
+void CTabbedWindow::SetWindowPosition()
+{
+ if (m_pEmbed == nullptr) {
+ Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room");
+ return;
+ }
+
+ if (iX) {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(m_hwnd, &wp);
+
+ wp.rcNormalPosition.left = iX;
+ wp.rcNormalPosition.top = iY;
+ wp.rcNormalPosition.right = wp.rcNormalPosition.left + iWidth;
+ wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + iHeight;
+ wp.showCmd = SW_HIDE;
+ SetWindowPlacement(m_hwnd, &wp);
+ return;
+ }
+
+ int flag = m_pEmbed->m_bNoActivate ? RWPF_HIDDEN : 0;
+ if (Utils_RestoreWindowPosition(m_hwnd, g_plugin.bSavePerContact ? m_pEmbed->m_hContact : 0, CHAT_MODULE, "room", flag)) {
+ if (g_plugin.bSavePerContact) {
+ if (Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room", flag | RWPF_NOMOVE))
+ SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW);
+ }
+ else SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW);
+ }
+
+ if (!g_plugin.bSavePerContact && g_plugin.bCascade) {
+ RECT rc, rcMax = {};
+ CTabbedWindow *pMaxTab = nullptr;
+
+ for (auto &it : g_arDialogs) {
+ auto *pTab = it->m_pOwner;
+ if (pTab == this)
+ continue;
+
+ GetWindowRect(pTab->GetHwnd(), &rc);
+ if (rc.left > rcMax.left && rc.top > rcMax.top) {
+ pMaxTab = pTab;
+ rcMax = rc;
+ }
+ }
+
+ if (pMaxTab) {
+ m_windowWasCascaded = 1;
+ int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
+ SetWindowPos(m_hwnd, nullptr, rcMax.left + offset, rcMax.top + offset, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+ }
+ }
+}
+
+void CTabbedWindow::SwitchNextTab()
+{
+ int total = m_tab.GetCount();
+ int i = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (i != -1 && total > 1) {
+ if (i < total - 1)
+ i++;
+ else
+ i = 0;
+ m_tab.ActivatePage(i);
+ TabClicked();
+ }
+}
+
+void CTabbedWindow::SwitchPrevTab()
+{
+ int total = m_tab.GetCount();
+ int i = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (i != -1 && total >= 1) {
+ if (i > 0)
+ i--;
+ else
+ i = total - 1;
+ m_tab.ActivatePage(i);
+ TabClicked();
+ }
+}
+
+void CTabbedWindow::SwitchTab(int iNewTab)
+{
+ int total = m_tab.GetCount();
+ int i = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (i != -1 && total != -1 && total != 1 && i != iNewTab && total > iNewTab) {
+ m_tab.ActivatePage(iNewTab);
+ TabClicked();
+ }
+}
+
+void CTabbedWindow::TabClicked()
+{
+ CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetActivePage();
+ if (pDlg == nullptr)
+ return;
+
+ SetFocus(pDlg->m_message.GetHwnd());
+
+ SESSION_INFO *si = pDlg->m_si;
+ if (si) {
+ if (si->wState & STATE_TALK) {
+ si->wState &= ~STATE_TALK;
+ db_set_w(si->hContact, si->pszModule, "ApparentMode", 0);
+ }
+
+ if (si->wState & GC_EVENT_HIGHLIGHT) {
+ si->wState &= ~GC_EVENT_HIGHLIGHT;
+
+ if (g_clistApi.pfnGetEvent(si->hContact, 0))
+ g_clistApi.pfnRemoveEvent(si->hContact, GC_FAKE_EVENT);
+ }
+
+ if (!si->pDlg) {
+ g_chatApi.ShowRoom(si);
+ SendMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ }
+ }
+
+ FixTabIcons(pDlg);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CTabbedWindow::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+ int idx;
+
+ switch (msg) {
+ case WM_MOVE:
+ SaveWindowPosition(false);
+ break;
+
+ case WM_ENTERSIZEMOVE:
+ GetClientRect(m_hwnd, &rc);
+ oldSizeX = rc.right - rc.left;
+ oldSizeY = rc.bottom - rc.top;
+ break;
+
+ case WM_EXITSIZEMOVE:
+ GetClientRect(m_hwnd, &rc);
+ if (!((rc.right - rc.left) == oldSizeX && (rc.bottom - rc.top) == oldSizeY)) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr) {
+ pDlg->m_pLog->ScrollToBottom();
+ pDlg->Resize();
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->hwndItem == m_hwndStatus) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr)
+ DrawStatusIcons(pDlg->m_hContact, dis->hDC, dis->rcItem, 2);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr) {
+ pDlg->m_btnOk.Click();
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE)
+ break;
+
+ if (!m_pEmbed) {
+ idx = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (idx != -1)
+ m_tab.ActivatePage(idx);
+ }
+ else m_pEmbed->OnActivate();
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->hwndFrom == m_hwndStatus) {
+ if (((LPNMHDR)lParam)->code == NM_CLICK || ((LPNMHDR)lParam)->code == NM_RCLICK) {
+ NMMOUSE *nm = (NMMOUSE *)lParam;
+ SendMessage(m_hwndStatus, SB_GETRECT, SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0) - 1, (LPARAM)&rc);
+ if (nm->pt.x >= rc.left) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr)
+ CheckStatusIconClick(pDlg->m_hContact, m_hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
+ }
+ return TRUE;
+ }
+ }
+
+ if (((LPNMHDR)lParam)->idFrom == IDC_TAB) {
+ switch (((LPNMHDR)lParam)->code) {
+ case TCN_SELCHANGE:
+ m_tab.ActivatePage(TabCtrl_GetCurSel(m_tab.GetHwnd()));
+ break;
+
+ case NM_RCLICK:
+ int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom);
+ if (i == -1)
+ break;
+
+ TCHITTESTINFO tci = {};
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+ ScreenToClient(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt);
+ if ((i = TabCtrl_HitTest(((LPNMHDR)lParam)->hwndFrom, &tci)) == -1)
+ break;
+
+ CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetNthPage(i);
+ SESSION_INFO *si = pDlg->m_si;
+
+ ClientToScreen(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt);
+ HMENU hSubMenu = GetSubMenu(g_hMenu, 1);
+ TranslateMenu(hSubMenu);
+
+ if (si != nullptr) {
+ uint16_t w = db_get_w(si->hContact, si->pszModule, "TabPosition", 0);
+ if (w == 0)
+ CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED);
+ else
+ CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_CHECKED);
+ }
+ else CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED);
+
+ switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, tci.pt.x, tci.pt.y, 0, m_hwnd, nullptr)) {
+ case ID_CLOSE:
+ pDlg->CloseTab();
+ break;
+
+ case ID_LOCKPOSITION:
+ if (si != nullptr) {
+ if (!(GetMenuState(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND)&MF_CHECKED)) {
+ if (si->hContact)
+ db_set_w(si->hContact, si->pszModule, "TabPosition", (uint16_t)(i + 1));
+ }
+ else db_unset(si->hContact, si->pszModule, "TabPosition");
+ }
+ break;
+
+ case ID_CLOSEOTHER:
+ int tabCount = m_tab.GetCount();
+ if (tabCount > 1) {
+ while (tabCount--) {
+ if (tabCount == i)
+ continue;
+
+ if (pDlg = (CMsgDialog*)m_tab.GetNthPage(tabCount))
+ pDlg->CloseTab();
+ }
+ m_tab.ActivatePage(0);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void UninitTabs()
+{
+ if (g_pTabDialog != nullptr) {
+ g_pTabDialog->Close();
+ g_pTabDialog = nullptr;
+ }
+}
diff --git a/src/core/stdmsg/src/version.h b/src/core/stdmsg/src/version.h index 80b240e521..c2573ed98b 100644 --- a/src/core/stdmsg/src/version.h +++ b/src/core/stdmsg/src/version.h @@ -9,4 +9,4 @@ #define __DESCRIPTION "Core module for send/receive instant messages."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdMsg"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdpopup/src/stdafx.cxx b/src/core/stdpopup/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/src/core/stdpopup/src/stdafx.cxx +++ b/src/core/stdpopup/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
\ No newline at end of file diff --git a/src/core/stdpopup/src/version.h b/src/core/stdpopup/src/version.h index 2ffc660f71..16d5b005cd 100644 --- a/src/core/stdpopup/src/version.h +++ b/src/core/stdpopup/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for popups."
#define __AUTHOR "Scott Ellis, Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdPopup"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stduihist/src/history.cpp b/src/core/stduihist/src/history.cpp index 56c3aba8a5..ae92875fab 100644 --- a/src/core/stduihist/src/history.cpp +++ b/src/core/stduihist/src/history.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduihist/src/main.cpp b/src/core/stduihist/src/main.cpp index 1911bd9792..1be11d7ac3 100644 --- a/src/core/stduihist/src/main.cpp +++ b/src/core/stduihist/src/main.cpp @@ -2,7 +2,7 @@ Standard ugly history viewer for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stduihist/src/stdafx.h b/src/core/stduihist/src/stdafx.h index 9817742ad3..aae29d451d 100644 --- a/src/core/stduihist/src/stdafx.h +++ b/src/core/stduihist/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduihist/src/version.h b/src/core/stduihist/src/version.h index 62eda51681..ee7cceff2a 100644 --- a/src/core/stduihist/src/version.h +++ b/src/core/stduihist/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for built-in history viewer."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdUIHist"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stduserinfo/src/contactinfo.cpp b/src/core/stduserinfo/src/contactinfo.cpp index a19783e4df..7adec4c09b 100644 --- a/src/core/stduserinfo/src/contactinfo.cpp +++ b/src/core/stduserinfo/src/contactinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/main.cpp b/src/core/stduserinfo/src/main.cpp index 5334c9727f..9bd9b6f7be 100644 --- a/src/core/stduserinfo/src/main.cpp +++ b/src/core/stduserinfo/src/main.cpp @@ -2,7 +2,7 @@ Standard User Info plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stduserinfo/src/stdafx.h b/src/core/stduserinfo/src/stdafx.h index 76b456e716..ca5412f200 100644 --- a/src/core/stduserinfo/src/stdafx.h +++ b/src/core/stduserinfo/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/stdinfo.cpp b/src/core/stduserinfo/src/stdinfo.cpp index 5acec2d245..ca3b2edd20 100644 --- a/src/core/stduserinfo/src/stdinfo.cpp +++ b/src/core/stduserinfo/src/stdinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/userinfo.cpp b/src/core/stduserinfo/src/userinfo.cpp index 9448d5c96f..30792d9244 100644 --- a/src/core/stduserinfo/src/userinfo.cpp +++ b/src/core/stduserinfo/src/userinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/utils.h b/src/core/stduserinfo/src/utils.h index cb766c47ae..9ceb33b61d 100644 --- a/src/core/stduserinfo/src/utils.h +++ b/src/core/stduserinfo/src/utils.h @@ -1,214 +1,214 @@ -/* - -Standard User Info plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#define SVS_NORMAL 0 -#define SVS_GENDER 1 -#define SVS_ZEROISUNSPEC 2 -#define SVS_IP 3 -#define SVS_COUNTRY 4 -#define SVS_MONTH 5 -#define SVS_SIGNED 6 -#define SVS_TIMEZONE 7 -#define SVS_MARITAL 8 - -///////////////////////////////////////////////////////////////////////////////////////// - -struct DataItem -{ - const char *szSetting; - int idCtrl, special; -}; - -template <size_t _Size> -bool IsEmptyValue(MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) -{ - if (szModule == nullptr) - szModule = Proto_GetBaseAccountName(hContact); - - DBVARIANT dbv; - for (auto &it : buffer) { - if (db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0) - continue; - - db_free(&dbv); - return false; - } - - return true; -} - -template <size_t _Size> -void SetValue(HWND hwndDlg, MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) -{ - if (szModule == nullptr) - szModule = Proto_GetBaseAccountName(hContact); - - for (auto &it : buffer) { - char *pstr = nullptr; - wchar_t *pwstr = nullptr, wstr[80]; - - DBVARIANT dbv = { DBVT_DELETED }; - - bool unspecified; - if (szModule == nullptr) - unspecified = true; - else - unspecified = db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0; - - if (!unspecified) { - switch (dbv.type) { - case DBVT_BYTE: - if (it.special == SVS_GENDER) { - if (dbv.cVal == 'M') pwstr = TranslateT("Male"); - else if (dbv.cVal == 'F') pwstr = TranslateT("Female"); - else unspecified = 1; - } - else if (it.special == SVS_MONTH) { - if (dbv.bVal > 0 && dbv.bVal <= 12) { - pwstr = wstr; - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, wstr, _countof(wstr)); - } - else unspecified = 1; - } - else if (it.special == SVS_TIMEZONE) { - if (dbv.cVal == -100) unspecified = 1; - else { - pwstr = wstr; - mir_snwprintf(wstr, dbv.cVal ? L"UTC%+d:%02d" : L"UTC", -dbv.cVal / 2, (dbv.cVal & 1) * 30); - } - } - else if (it.special == SVS_MARITAL) { - switch (dbv.cVal) { - case 0: - pwstr = TranslateT("<not specified>"); - break; - case 10: - pwstr = TranslateT("Single"); - break; - case 11: - pwstr = TranslateT("Close relationships"); - break; - case 12: - pwstr = TranslateT("Engaged"); - break; - case 20: - pwstr = TranslateT("Married"); - break; - case 30: - pwstr = TranslateT("Divorced"); - break; - case 31: - pwstr = TranslateT("Separated"); - break; - case 40: - pwstr = TranslateT("Widowed"); - break; - case 50: - pwstr = TranslateT("Actively searching"); - break; - case 60: - pwstr = TranslateT("In love"); - break; - case 70: - pwstr = TranslateT("It's complicated"); - break; - case 80: - pwstr = TranslateT("In a civil union"); - break; - default: - unspecified = 1; - } - } - else { - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.bVal == 0); - pwstr = _itow(it.special == SVS_SIGNED ? dbv.cVal : dbv.bVal, wstr, 10); - } - break; - - case DBVT_WORD: - if (it.special == SVS_COUNTRY) { - uint16_t wSave = dbv.wVal; - if (wSave == (uint16_t)-1) { - char szSettingName[100]; - mir_snprintf(szSettingName, "%sName", it.szSetting); - if (!db_get_ws(hContact, szModule, szSettingName, &dbv)) { - pwstr = dbv.pwszVal; - unspecified = false; - break; - } - } - - pwstr = TranslateW(_A2T((char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, wSave, 0))); - unspecified = pwstr == nullptr; - } - else { - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.wVal == 0); - pwstr = _itow(it.special == SVS_SIGNED ? dbv.sVal : dbv.wVal, wstr, 10); - } - break; - - case DBVT_DWORD: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.dVal == 0); - if (it.special == SVS_IP) { - struct in_addr ia; - ia.S_un.S_addr = htonl(dbv.dVal); - mir_wstrncpy(wstr, _A2T(inet_ntoa(ia)), _countof(wstr)); - pwstr = wstr; - if (dbv.dVal == 0) - unspecified = 1; - } - else pwstr = _itow(it.special == SVS_SIGNED ? dbv.lVal : dbv.dVal, wstr, 10); - break; - - case DBVT_ASCIIZ: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - pstr = dbv.pszVal; - break; - - case DBVT_UTF8: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - if (!unspecified) { - SetDlgItemTextW(hwndDlg, it.idCtrl, TranslateW(ptrW(mir_utf8decodeW(dbv.pszVal)))); - goto LBL_Exit; - } - - mir_utf8decode(dbv.pszVal, &pwstr); - break; - - default: - pwstr = wstr; - mir_wstrcpy(wstr, L"???"); - break; - } - } - - if (unspecified) - SetDlgItemText(hwndDlg, it.idCtrl, TranslateT("<not specified>")); - else if (pwstr != nullptr) - SetDlgItemText(hwndDlg, it.idCtrl, pwstr); - else - SetDlgItemTextA(hwndDlg, it.idCtrl, pstr); - -LBL_Exit: - EnableWindow(GetDlgItem(hwndDlg, it.idCtrl), !unspecified); - db_free(&dbv); - } -} +/*
+
+Standard User Info plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#define SVS_NORMAL 0
+#define SVS_GENDER 1
+#define SVS_ZEROISUNSPEC 2
+#define SVS_IP 3
+#define SVS_COUNTRY 4
+#define SVS_MONTH 5
+#define SVS_SIGNED 6
+#define SVS_TIMEZONE 7
+#define SVS_MARITAL 8
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct DataItem
+{
+ const char *szSetting;
+ int idCtrl, special;
+};
+
+template <size_t _Size>
+bool IsEmptyValue(MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr)
+{
+ if (szModule == nullptr)
+ szModule = Proto_GetBaseAccountName(hContact);
+
+ DBVARIANT dbv;
+ for (auto &it : buffer) {
+ if (db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0)
+ continue;
+
+ db_free(&dbv);
+ return false;
+ }
+
+ return true;
+}
+
+template <size_t _Size>
+void SetValue(HWND hwndDlg, MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr)
+{
+ if (szModule == nullptr)
+ szModule = Proto_GetBaseAccountName(hContact);
+
+ for (auto &it : buffer) {
+ char *pstr = nullptr;
+ wchar_t *pwstr = nullptr, wstr[80];
+
+ DBVARIANT dbv = { DBVT_DELETED };
+
+ bool unspecified;
+ if (szModule == nullptr)
+ unspecified = true;
+ else
+ unspecified = db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0;
+
+ if (!unspecified) {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ if (it.special == SVS_GENDER) {
+ if (dbv.cVal == 'M') pwstr = TranslateT("Male");
+ else if (dbv.cVal == 'F') pwstr = TranslateT("Female");
+ else unspecified = 1;
+ }
+ else if (it.special == SVS_MONTH) {
+ if (dbv.bVal > 0 && dbv.bVal <= 12) {
+ pwstr = wstr;
+ GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, wstr, _countof(wstr));
+ }
+ else unspecified = 1;
+ }
+ else if (it.special == SVS_TIMEZONE) {
+ if (dbv.cVal == -100) unspecified = 1;
+ else {
+ pwstr = wstr;
+ mir_snwprintf(wstr, dbv.cVal ? L"UTC%+d:%02d" : L"UTC", -dbv.cVal / 2, (dbv.cVal & 1) * 30);
+ }
+ }
+ else if (it.special == SVS_MARITAL) {
+ switch (dbv.cVal) {
+ case 0:
+ pwstr = TranslateT("<not specified>");
+ break;
+ case 10:
+ pwstr = TranslateT("Single");
+ break;
+ case 11:
+ pwstr = TranslateT("Close relationships");
+ break;
+ case 12:
+ pwstr = TranslateT("Engaged");
+ break;
+ case 20:
+ pwstr = TranslateT("Married");
+ break;
+ case 30:
+ pwstr = TranslateT("Divorced");
+ break;
+ case 31:
+ pwstr = TranslateT("Separated");
+ break;
+ case 40:
+ pwstr = TranslateT("Widowed");
+ break;
+ case 50:
+ pwstr = TranslateT("Actively searching");
+ break;
+ case 60:
+ pwstr = TranslateT("In love");
+ break;
+ case 70:
+ pwstr = TranslateT("It's complicated");
+ break;
+ case 80:
+ pwstr = TranslateT("In a civil union");
+ break;
+ default:
+ unspecified = 1;
+ }
+ }
+ else {
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.bVal == 0);
+ pwstr = _itow(it.special == SVS_SIGNED ? dbv.cVal : dbv.bVal, wstr, 10);
+ }
+ break;
+
+ case DBVT_WORD:
+ if (it.special == SVS_COUNTRY) {
+ uint16_t wSave = dbv.wVal;
+ if (wSave == (uint16_t)-1) {
+ char szSettingName[100];
+ mir_snprintf(szSettingName, "%sName", it.szSetting);
+ if (!db_get_ws(hContact, szModule, szSettingName, &dbv)) {
+ pwstr = dbv.pwszVal;
+ unspecified = false;
+ break;
+ }
+ }
+
+ pwstr = TranslateW(_A2T((char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, wSave, 0)));
+ unspecified = pwstr == nullptr;
+ }
+ else {
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.wVal == 0);
+ pwstr = _itow(it.special == SVS_SIGNED ? dbv.sVal : dbv.wVal, wstr, 10);
+ }
+ break;
+
+ case DBVT_DWORD:
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.dVal == 0);
+ if (it.special == SVS_IP) {
+ struct in_addr ia;
+ ia.S_un.S_addr = htonl(dbv.dVal);
+ mir_wstrncpy(wstr, _A2T(inet_ntoa(ia)), _countof(wstr));
+ pwstr = wstr;
+ if (dbv.dVal == 0)
+ unspecified = 1;
+ }
+ else pwstr = _itow(it.special == SVS_SIGNED ? dbv.lVal : dbv.dVal, wstr, 10);
+ break;
+
+ case DBVT_ASCIIZ:
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ pstr = dbv.pszVal;
+ break;
+
+ case DBVT_UTF8:
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ if (!unspecified) {
+ SetDlgItemTextW(hwndDlg, it.idCtrl, TranslateW(ptrW(mir_utf8decodeW(dbv.pszVal))));
+ goto LBL_Exit;
+ }
+
+ mir_utf8decode(dbv.pszVal, &pwstr);
+ break;
+
+ default:
+ pwstr = wstr;
+ mir_wstrcpy(wstr, L"???");
+ break;
+ }
+ }
+
+ if (unspecified)
+ SetDlgItemText(hwndDlg, it.idCtrl, TranslateT("<not specified>"));
+ else if (pwstr != nullptr)
+ SetDlgItemText(hwndDlg, it.idCtrl, pwstr);
+ else
+ SetDlgItemTextA(hwndDlg, it.idCtrl, pstr);
+
+LBL_Exit:
+ EnableWindow(GetDlgItem(hwndDlg, it.idCtrl), !unspecified);
+ db_free(&dbv);
+ }
+}
diff --git a/src/core/stduserinfo/src/version.h b/src/core/stduserinfo/src/version.h index 1a68b614db..441fd0cdef 100644 --- a/src/core/stduserinfo/src/version.h +++ b/src/core/stduserinfo/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for providing user information."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdUserInfo"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stduseronline/src/main.cpp b/src/core/stduseronline/src/main.cpp index 255a954e35..da3e0c5cd8 100644 --- a/src/core/stduseronline/src/main.cpp +++ b/src/core/stduseronline/src/main.cpp @@ -2,7 +2,7 @@ Standard user online monitor for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stduseronline/src/stdafx.h b/src/core/stduseronline/src/stdafx.h index a34a26fd31..1f069e243c 100644 --- a/src/core/stduseronline/src/stdafx.h +++ b/src/core/stduseronline/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduseronline/src/useronline.cpp b/src/core/stduseronline/src/useronline.cpp index 3b1520a0a6..be50bf3165 100644 --- a/src/core/stduseronline/src/useronline.cpp +++ b/src/core/stduseronline/src/useronline.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduseronline/src/version.h b/src/core/stduseronline/src/version.h index 3edec197fb..ec2b47a6f8 100644 --- a/src/core/stduseronline/src/version.h +++ b/src/core/stduseronline/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for user-is-online event processing."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdUserOnline"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/mir_app/src/CMPluginBase.cpp b/src/mir_app/src/CMPluginBase.cpp index bc768335a2..92c444e46c 100644 --- a/src/mir_app/src/CMPluginBase.cpp +++ b/src/mir_app/src/CMPluginBase.cpp @@ -1,343 +1,343 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" -#include "IcoLib.h" - -static int sttFakeID = -100; - -static int Compare(const CMPluginBase *p1, const CMPluginBase *p2) -{ - return INT_PTR(p1->getInst()) - INT_PTR(p2->getInst()); -} - -static LIST<CMPluginBase> g_arPlugins(10, Compare); - -void RegisterModule(CMPluginBase *pPlugin) -{ - g_arPlugins.insert(pPlugin); -} - -MIR_APP_DLL(HINSTANCE) GetInstByAddress(void *codePtr) -{ - if (g_arPlugins.getCount() == 0) - return nullptr; - - int idx; - void *boo[2] = { 0, codePtr }; - List_GetIndex((SortedList*)&g_arPlugins, (CMPluginBase*)&boo, &idx); - if (idx > 0) - idx--; - - HINSTANCE result = g_arPlugins[idx]->getInst(); - if (result < g_plugin.getInst() && codePtr > g_plugin.getInst()) - return g_plugin.getInst(); - - if (idx == 0 && codePtr < (void*)result) - return nullptr; - - return result; -} - -MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int _hLang) -{ - if (uuid == miid_last) - return --sttFakeID; - - for (auto &it : g_arPlugins) - if (it->getInfo().uuid == uuid) - return (_hLang) ? _hLang : --sttFakeID; - - return 0; -} - -MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid) -{ - for (auto &it : g_arPlugins) - if (it->getInfo().uuid == uuid) - return it->getInst() != nullptr; - - return false; -} - -const char* GetPluginNameByInstance(HINSTANCE hInst) -{ - HINSTANCE boo[2] = { 0, hInst }; - CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); - return (pPlugin == nullptr) ? nullptr : pPlugin->getInfo().shortName; -} - -MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst) -{ - HINSTANCE boo[2] = { 0, hInst }; - CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); - return (pPlugin == nullptr) ? g_plugin : *pPlugin; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// stubs for pascal plugins - -static void wipePluginData(CMPluginBase *pPlugin) -{ - if (Miranda_IsTerminated()) - return; - - KillModuleMenus(pPlugin); - KillModuleFonts(pPlugin); - KillModuleIcons(pPlugin); - KillModuleHotkeys(pPlugin); - KillModulePopups(pPlugin); - KillModuleSounds(pPlugin); - KillModuleExtraIcons(pPlugin); - KillModuleSrmmIcons(pPlugin); - KillModuleToolbarIcons(pPlugin); - KillModuleOptions(pPlugin); -} - -// emulates the call of CMPluginBase::CMPluginBase for Pascal plugins -EXTERN_C MIR_APP_DLL(void) RegisterPlugin(CMPluginBase *pPlugin) -{ - if (pPlugin->getInst() != nullptr) - g_arPlugins.insert(pPlugin); -} - -// emulates the call of CMPluginBase::~CMPluginBase for Pascal plugins -EXTERN_C MIR_APP_DLL(void) UnregisterPlugin(CMPluginBase *pPlugin) -{ - wipePluginData(pPlugin); - g_arPlugins.remove(pPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CompareIcons(const IcolibItem *p1, const IcolibItem *p2) -{ - return p1->default_indx - p2->default_indx; -} - -CMPluginBase::CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo) : - m_szModuleName(moduleName), - m_pInfo(pInfo), - m_arIcons(10, CompareIcons) -{ - if (m_hInst != nullptr) - g_arPlugins.insert(this); -} - -CMPluginBase::~CMPluginBase() -{ - wipePluginData(this); - - if (m_hLogger) { - mir_closeLog(m_hLogger); - m_hLogger = nullptr; - } - - g_arPlugins.remove(this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::Load() -{ - return 0; -} - -int CMPluginBase::Unload() -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::tryOpenLog() -{ - wchar_t path[MAX_PATH]; - mir_snwprintf(path, L"%s\\%S.txt", VARSW(L"%miranda_logpath%").get(), m_szModuleName); - m_hLogger = mir_createLog(m_szModuleName, nullptr, path, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addOptions(WPARAM wParam, OPTIONSDIALOGPAGE *odp) -{ - return ::Options_AddPage(wParam, odp, this); -} - -void CMPluginBase::openOptions(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) -{ - ::Options_Open(pszGroup, pszPage, pszTab, this); -} - -void CMPluginBase::openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) -{ - ::Options_OpenPage(pszGroup, pszPage, pszTab, this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addFont(FontID *pFont) -{ - return Font_Register(pFont, this); -} - -int CMPluginBase::addFont(FontIDW *pFont) -{ - return Font_RegisterW(pFont, this); -} - -int CMPluginBase::addColor(ColourID *pColor) -{ - return Colour_Register(pColor, this); -} - -int CMPluginBase::addColor(ColourIDW *pColor) -{ - return Colour_RegisterW(pColor, this); -} - -int CMPluginBase::addEffect(EffectID *pEffect) -{ - return Effect_Register(pEffect, this); -} - -int CMPluginBase::addEffect(EffectIDW *pEffect) -{ - return Effect_RegisterW(pEffect, this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addFrame(const CLISTFrame *F) -{ - return (int)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)F, (LPARAM)this); -} - -int CMPluginBase::addHotkey(const HOTKEYDESC *hk) -{ - return Hotkey_Register(hk, this); -} - -HANDLE CMPluginBase::addIcon(const SKINICONDESC *sid) -{ - return IcoLib_AddIcon(sid, this); -} - -HGENMENU CMPluginBase::addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib) -{ - return Menu_CreateRoot(hMenuObject, ptszName, position, hIcoLib, this); -} - -HANDLE CMPluginBase::addTTB(const struct TTBButton *pButton) -{ - return (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)pButton, (LPARAM)this); -} - -int CMPluginBase::addUserInfo(WPARAM wParam, USERINFOPAGE *uip) -{ - uip->pPlugin = this; - return CallService("UserInfo/AddPage", wParam, (LPARAM)uip); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::debugLogA(LPCSTR szFormat, ...) -{ - if (m_hLogger == nullptr) - tryOpenLog(); - - va_list args; - va_start(args, szFormat); - mir_writeLogVA(m_hLogger, szFormat, args); - va_end(args); -} - -void CMPluginBase::debugLogW(LPCWSTR wszFormat, ...) -{ - if (m_hLogger == nullptr) - tryOpenLog(); - - va_list args; - va_start(args, wszFormat); - mir_writeLogVW(m_hLogger, wszFormat, args); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#ifdef _WINDOWS -int CMPluginBase::addImgListIcon(HIMAGELIST himl, int iconId) -{ - HICON hIcon = getIcon(iconId); - int ret = ::ImageList_AddIcon(himl, hIcon); - IcoLib_ReleaseIcon(hIcon); - return ret; -} -#endif - -HICON CMPluginBase::getIcon(int iconId, bool big) -{ - return IcoLib_GetIconByHandle(getIconHandle(iconId), big); -} - -HANDLE CMPluginBase::getIconHandle(int iconId) -{ - IcolibItem *p = (IcolibItem*)alloca(sizeof(IcolibItem)); - p->default_indx = -iconId; - return m_arIcons.find(p); -} - -void CMPluginBase::releaseIcon(int iconId, bool big) -{ - IcoLib_ReleaseIcon(getIcon(iconId), big); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::RegisterProtocol(int type, pfnInitProto fnInit, pfnUninitProto fnUninit) -{ - if (isPluginBanned(m_pInfo.uuid)) - return; - - if (type == PROTOTYPE_PROTOCOL && fnInit != nullptr) - type = PROTOTYPE_PROTOWITHACCS; - - MBaseProto *pd = (MBaseProto*)Proto_RegisterModule(type, m_szModuleName); - if (pd) { - pd->fnInit = fnInit; - pd->fnUninit = fnUninit; - pd->hInst = m_hInst; - } -} - -void CMPluginBase::SetUniqueId(const char *pszUniqueId) -{ - if (pszUniqueId == nullptr) - return; - - MBaseProto *pd = g_arProtos.find((MBaseProto*)&m_szModuleName); - if (pd != nullptr) - pd->szUniqueId = mir_strdup(pszUniqueId); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "plugins.h"
+#include "IcoLib.h"
+
+static int sttFakeID = -100;
+
+static int Compare(const CMPluginBase *p1, const CMPluginBase *p2)
+{
+ return INT_PTR(p1->getInst()) - INT_PTR(p2->getInst());
+}
+
+static LIST<CMPluginBase> g_arPlugins(10, Compare);
+
+void RegisterModule(CMPluginBase *pPlugin)
+{
+ g_arPlugins.insert(pPlugin);
+}
+
+MIR_APP_DLL(HINSTANCE) GetInstByAddress(void *codePtr)
+{
+ if (g_arPlugins.getCount() == 0)
+ return nullptr;
+
+ int idx;
+ void *boo[2] = { 0, codePtr };
+ List_GetIndex((SortedList*)&g_arPlugins, (CMPluginBase*)&boo, &idx);
+ if (idx > 0)
+ idx--;
+
+ HINSTANCE result = g_arPlugins[idx]->getInst();
+ if (result < g_plugin.getInst() && codePtr > g_plugin.getInst())
+ return g_plugin.getInst();
+
+ if (idx == 0 && codePtr < (void*)result)
+ return nullptr;
+
+ return result;
+}
+
+MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int _hLang)
+{
+ if (uuid == miid_last)
+ return --sttFakeID;
+
+ for (auto &it : g_arPlugins)
+ if (it->getInfo().uuid == uuid)
+ return (_hLang) ? _hLang : --sttFakeID;
+
+ return 0;
+}
+
+MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid)
+{
+ for (auto &it : g_arPlugins)
+ if (it->getInfo().uuid == uuid)
+ return it->getInst() != nullptr;
+
+ return false;
+}
+
+const char* GetPluginNameByInstance(HINSTANCE hInst)
+{
+ HINSTANCE boo[2] = { 0, hInst };
+ CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo);
+ return (pPlugin == nullptr) ? nullptr : pPlugin->getInfo().shortName;
+}
+
+MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst)
+{
+ HINSTANCE boo[2] = { 0, hInst };
+ CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo);
+ return (pPlugin == nullptr) ? g_plugin : *pPlugin;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// stubs for pascal plugins
+
+static void wipePluginData(CMPluginBase *pPlugin)
+{
+ if (Miranda_IsTerminated())
+ return;
+
+ KillModuleMenus(pPlugin);
+ KillModuleFonts(pPlugin);
+ KillModuleIcons(pPlugin);
+ KillModuleHotkeys(pPlugin);
+ KillModulePopups(pPlugin);
+ KillModuleSounds(pPlugin);
+ KillModuleExtraIcons(pPlugin);
+ KillModuleSrmmIcons(pPlugin);
+ KillModuleToolbarIcons(pPlugin);
+ KillModuleOptions(pPlugin);
+}
+
+// emulates the call of CMPluginBase::CMPluginBase for Pascal plugins
+EXTERN_C MIR_APP_DLL(void) RegisterPlugin(CMPluginBase *pPlugin)
+{
+ if (pPlugin->getInst() != nullptr)
+ g_arPlugins.insert(pPlugin);
+}
+
+// emulates the call of CMPluginBase::~CMPluginBase for Pascal plugins
+EXTERN_C MIR_APP_DLL(void) UnregisterPlugin(CMPluginBase *pPlugin)
+{
+ wipePluginData(pPlugin);
+ g_arPlugins.remove(pPlugin);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CompareIcons(const IcolibItem *p1, const IcolibItem *p2)
+{
+ return p1->default_indx - p2->default_indx;
+}
+
+CMPluginBase::CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo) :
+ m_szModuleName(moduleName),
+ m_pInfo(pInfo),
+ m_arIcons(10, CompareIcons)
+{
+ if (m_hInst != nullptr)
+ g_arPlugins.insert(this);
+}
+
+CMPluginBase::~CMPluginBase()
+{
+ wipePluginData(this);
+
+ if (m_hLogger) {
+ mir_closeLog(m_hLogger);
+ m_hLogger = nullptr;
+ }
+
+ g_arPlugins.remove(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::Load()
+{
+ return 0;
+}
+
+int CMPluginBase::Unload()
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMPluginBase::tryOpenLog()
+{
+ wchar_t path[MAX_PATH];
+ mir_snwprintf(path, L"%s\\%S.txt", VARSW(L"%miranda_logpath%").get(), m_szModuleName);
+ m_hLogger = mir_createLog(m_szModuleName, nullptr, path, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addOptions(WPARAM wParam, OPTIONSDIALOGPAGE *odp)
+{
+ return ::Options_AddPage(wParam, odp, this);
+}
+
+void CMPluginBase::openOptions(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab)
+{
+ ::Options_Open(pszGroup, pszPage, pszTab, this);
+}
+
+void CMPluginBase::openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab)
+{
+ ::Options_OpenPage(pszGroup, pszPage, pszTab, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addFont(FontID *pFont)
+{
+ return Font_Register(pFont, this);
+}
+
+int CMPluginBase::addFont(FontIDW *pFont)
+{
+ return Font_RegisterW(pFont, this);
+}
+
+int CMPluginBase::addColor(ColourID *pColor)
+{
+ return Colour_Register(pColor, this);
+}
+
+int CMPluginBase::addColor(ColourIDW *pColor)
+{
+ return Colour_RegisterW(pColor, this);
+}
+
+int CMPluginBase::addEffect(EffectID *pEffect)
+{
+ return Effect_Register(pEffect, this);
+}
+
+int CMPluginBase::addEffect(EffectIDW *pEffect)
+{
+ return Effect_RegisterW(pEffect, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addFrame(const CLISTFrame *F)
+{
+ return (int)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)F, (LPARAM)this);
+}
+
+int CMPluginBase::addHotkey(const HOTKEYDESC *hk)
+{
+ return Hotkey_Register(hk, this);
+}
+
+HANDLE CMPluginBase::addIcon(const SKINICONDESC *sid)
+{
+ return IcoLib_AddIcon(sid, this);
+}
+
+HGENMENU CMPluginBase::addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib)
+{
+ return Menu_CreateRoot(hMenuObject, ptszName, position, hIcoLib, this);
+}
+
+HANDLE CMPluginBase::addTTB(const struct TTBButton *pButton)
+{
+ return (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)pButton, (LPARAM)this);
+}
+
+int CMPluginBase::addUserInfo(WPARAM wParam, USERINFOPAGE *uip)
+{
+ uip->pPlugin = this;
+ return CallService("UserInfo/AddPage", wParam, (LPARAM)uip);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMPluginBase::debugLogA(LPCSTR szFormat, ...)
+{
+ if (m_hLogger == nullptr)
+ tryOpenLog();
+
+ va_list args;
+ va_start(args, szFormat);
+ mir_writeLogVA(m_hLogger, szFormat, args);
+ va_end(args);
+}
+
+void CMPluginBase::debugLogW(LPCWSTR wszFormat, ...)
+{
+ if (m_hLogger == nullptr)
+ tryOpenLog();
+
+ va_list args;
+ va_start(args, wszFormat);
+ mir_writeLogVW(m_hLogger, wszFormat, args);
+ va_end(args);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _WINDOWS
+int CMPluginBase::addImgListIcon(HIMAGELIST himl, int iconId)
+{
+ HICON hIcon = getIcon(iconId);
+ int ret = ::ImageList_AddIcon(himl, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ return ret;
+}
+#endif
+
+HICON CMPluginBase::getIcon(int iconId, bool big)
+{
+ return IcoLib_GetIconByHandle(getIconHandle(iconId), big);
+}
+
+HANDLE CMPluginBase::getIconHandle(int iconId)
+{
+ IcolibItem *p = (IcolibItem*)alloca(sizeof(IcolibItem));
+ p->default_indx = -iconId;
+ return m_arIcons.find(p);
+}
+
+void CMPluginBase::releaseIcon(int iconId, bool big)
+{
+ IcoLib_ReleaseIcon(getIcon(iconId), big);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMPluginBase::RegisterProtocol(int type, pfnInitProto fnInit, pfnUninitProto fnUninit)
+{
+ if (isPluginBanned(m_pInfo.uuid))
+ return;
+
+ if (type == PROTOTYPE_PROTOCOL && fnInit != nullptr)
+ type = PROTOTYPE_PROTOWITHACCS;
+
+ MBaseProto *pd = (MBaseProto*)Proto_RegisterModule(type, m_szModuleName);
+ if (pd) {
+ pd->fnInit = fnInit;
+ pd->fnUninit = fnUninit;
+ pd->hInst = m_hInst;
+ }
+}
+
+void CMPluginBase::SetUniqueId(const char *pszUniqueId)
+{
+ if (pszUniqueId == nullptr)
+ return;
+
+ MBaseProto *pd = g_arProtos.find((MBaseProto*)&m_szModuleName);
+ if (pd != nullptr)
+ pd->szUniqueId = mir_strdup(pszUniqueId);
+}
diff --git a/src/mir_app/src/Docking.cpp b/src/mir_app/src/Docking.cpp index 9ea5cd9f58..c8dad68b95 100644 --- a/src/mir_app/src/Docking.cpp +++ b/src/mir_app/src/Docking.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/FontOptions.cpp b/src/mir_app/src/FontOptions.cpp index 071da22914..5a8fd4aff8 100644 --- a/src/mir_app/src/FontOptions.cpp +++ b/src/mir_app/src/FontOptions.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/FontService.cpp b/src/mir_app/src/FontService.cpp index 2f4f300ff2..27f16e2ce2 100644 --- a/src/mir_app/src/FontService.cpp +++ b/src/mir_app/src/FontService.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/FontService.h b/src/mir_app/src/FontService.h index e86799ab35..2e562bd185 100644 --- a/src/mir_app/src/FontService.h +++ b/src/mir_app/src/FontService.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/IcoLib.h b/src/mir_app/src/IcoLib.h index 385d1acfc4..78dffe4cf8 100644 --- a/src/mir_app/src/IcoLib.h +++ b/src/mir_app/src/IcoLib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/MDatabaseCommon.cpp b/src/mir_app/src/MDatabaseCommon.cpp index 033cbea2b9..323954a516 100644 --- a/src/mir_app/src/MDatabaseCommon.cpp +++ b/src/mir_app/src/MDatabaseCommon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/MDatabaseCommonCrypt.cpp b/src/mir_app/src/MDatabaseCommonCrypt.cpp index bbfd18f399..88ec00a1cd 100644 --- a/src/mir_app/src/MDatabaseCommonCrypt.cpp +++ b/src/mir_app/src/MDatabaseCommonCrypt.cpp @@ -1,449 +1,449 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "database.h" -#include "encrypt.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Provider selection dialog - -class CSelectCryptoDialog : public CDlgBase -{ - CCtrlCombo m_combo; - CCtrlData m_descr; - CRYPTO_PROVIDER *m_selected = nullptr; - - CRYPTO_PROVIDER *getCurrent() - { - return (CRYPTO_PROVIDER*)m_combo.GetCurData(); - } - - bool OnInitDialog() override - { - for (auto &p : arCryptoProviders) - m_combo.AddStringA(p->pszName, LPARAM(p)); - - m_combo.SetCurSel(0); - m_descr.SetText(arCryptoProviders[0].szDescr.w); - return true; - } - - bool OnApply() override - { - m_selected = getCurrent(); - return true; - } - - void OnComboChanged(CCtrlCombo*) - { - m_descr.SetText(getCurrent()->szDescr.w); - } - -public: - CSelectCryptoDialog() : - CDlgBase(g_plugin, IDD_SELECT_CRYPTOPROVIDER), - m_combo(this, IDC_SELECTCRYPT_COMBO), - m_descr(this, IDC_CRYPTOPROVIDER_DESCR) - { - m_combo.OnChange = Callback(this, &CSelectCryptoDialog::OnComboChanged); - } - - inline CRYPTO_PROVIDER* GetSelected() - { return m_selected; - } -}; - -CRYPTO_PROVIDER* MDatabaseCommon::SelectProvider() -{ - if (arCryptoProviders.getCount() == 0) - return nullptr; - - CRYPTO_PROVIDER *pProv; - if (arCryptoProviders.getCount() > 1) { - CSelectCryptoDialog dlg; - dlg.DoModal(); - pProv = dlg.GetSelected(); - } - else pProv = &arCryptoProviders[0]; - - return (StoreProvider(pProv)) ? pProv : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseCommon::IsSettingEncrypted(LPCSTR szModule, LPCSTR szSetting) -{ - if (!_strnicmp(szSetting, "password", 8)) return true; - if (!mir_strcmp(szSetting, "NLProxyAuthPassword")) return true; - if (!mir_strcmp(szSetting, "LNPassword")) return true; - if (!mir_strcmp(szSetting, "FileProxyPassword")) return true; - if (!mir_strcmp(szSetting, "TokenSecret")) return true; - - if (!mir_strcmp(szModule, "SecureIM")) { - if (!mir_strcmp(szSetting, "pgp")) return true; - if (!mir_strcmp(szSetting, "pgpPrivKey")) return true; - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HGENMENU hSetPwdMenu; - -__forceinline wchar_t *GetMenuTitle(bool bUsesPassword) -{ - return bUsesPassword ? LPGENW("Change/remove password") : LPGENW("Set password"); -} - -void MDatabaseCommon::SetPassword(const wchar_t *ptszPassword) -{ - if (mir_wstrlen(ptszPassword) == 0) { - m_bUsesPassword = false; - m_crypto->setPassword(nullptr); - } - else { - m_bUsesPassword = true; - m_crypto->setPassword(pass_ptrA(mir_utf8encodeW(ptszPassword))); - } - - Menu_ModifyItem(hSetPwdMenu, GetMenuTitle(m_bUsesPassword), Skin_GetIconHandle(SKINICON_OTHER_KEYS)); -} - -static UINT oldLangID; -void LanguageChanged(HWND hwndDlg) -{ - UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); - char Lang[3] = { 0 }; - if (LangID != oldLangID) { - oldLangID = LangID; - GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); - Lang[0] = toupper(Lang[0]); - Lang[1] = tolower(Lang[1]); - SetDlgItemTextA(hwndDlg, IDC_LANG, Lang); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool CheckOldPassword(HWND hwndDlg, MDatabaseCommon *db) -{ - if (db->usesPassword()) { - wchar_t buf[100]; - GetDlgItemText(hwndDlg, IDC_OLDPASS, buf, _countof(buf)); - pass_ptrA oldPass(mir_utf8encodeW(buf)); - if (!db->getCrypt()->checkPassword(oldPass)) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Wrong old password entered!")); - return false; - } - } - return true; -} - -static INT_PTR CALLBACK sttChangePassword(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MDatabaseCommon *db = (MDatabaseCommon *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (uMsg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); - - oldLangID = 0; - SetTimer(hwndDlg, 1, 200, nullptr); - LanguageChanged(hwndDlg); - return TRUE; - - case WM_CTLCOLORSTATIC: - if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_LANG)) { - SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); - } - return FALSE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - - case IDREMOVE: - if (!CheckOldPassword(hwndDlg, db)) { -LBL_Error: - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_NCPAINT, 0, 0); - SetDlgItemTextA(hwndDlg, IDC_USERPASS1, ""); - SetDlgItemTextA(hwndDlg, IDC_USERPASS2, ""); - } - else { - db->SetPassword(nullptr); - db->StoreCryptoKey(); - EndDialog(hwndDlg, IDREMOVE); - } - break; - - case IDOK: - wchar_t buf2[100]; - GetDlgItemText(hwndDlg, IDC_USERPASS1, buf2, _countof(buf2)); - if (wcslen(buf2) < 3) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Password is too short!")); - goto LBL_Error; - } - - wchar_t buf[100]; - GetDlgItemText(hwndDlg, IDC_USERPASS2, buf, _countof(buf)); - if (wcscmp(buf2, buf)) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Passwords do not match!")); - goto LBL_Error; - } - - if (!CheckOldPassword(hwndDlg, db)) - goto LBL_Error; - - db->SetPassword(buf2); - db->StoreCryptoKey(); - SecureZeroMemory(buf, sizeof(buf)); - SecureZeroMemory(buf2, sizeof(buf2)); - EndDialog(hwndDlg, IDOK); - } - break; - - case WM_TIMER: - LanguageChanged(hwndDlg); - return FALSE; - - case WM_DESTROY: - KillTimer(hwndDlg, 1); - Window_FreeIcon_IcoLib(GetDlgItem(hwndDlg, IDC_HEADERBAR)); - } - - return FALSE; -} - -static INT_PTR ChangePassword(void* obj, WPARAM, LPARAM) -{ - MDatabaseCommon *db = (MDatabaseCommon*)obj; - DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(db->usesPassword() ? IDD_CHANGEPASS : IDD_NEWPASS), nullptr, sttChangePassword, (LPARAM)db); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Options - -class CDatabaseOptionsDialog : public CDlgBase -{ - CCtrlCheck m_chkStandart, m_chkTotal; - CCtrlButton m_btnChangePass; - MDatabaseCommon *m_db; - -public: - CDatabaseOptionsDialog(MDatabaseCommon *db) : - CDlgBase(g_plugin, IDD_OPT_DATABASE), - m_db(db), - m_chkTotal(this, IDC_TOTAL), - m_chkStandart(this, IDC_STANDARD), - m_btnChangePass(this, IDC_USERPASS1) - { - m_btnChangePass.OnClick = Callback(this, &CDatabaseOptionsDialog::onClick_ChangePass); - } - - bool OnInitDialog() override - { - m_chkStandart.SetState(!m_db->isEncrypted()); - m_chkTotal.SetState(m_db->isEncrypted()); - return true; - } - - bool OnApply() override - { - SetCursor(LoadCursor(nullptr, IDC_WAIT)); - m_db->EnableEncryption(m_chkTotal.GetState() != 0); - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - m_chkStandart.SetState(!m_db->isEncrypted()); - m_chkTotal.SetState(m_db->isEncrypted()); - return true; - } - - void onClick_ChangePass(CCtrlButton *) - { - ChangePassword(m_db, 0, 0); - } -}; - -static int OnOptionsInit(PVOID obj, WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = { sizeof(odp) }; - odp.position = -790000000; - odp.flags = ODPF_BOLDGROUPS; - odp.szTitle.a = LPGEN("Database"); - odp.pDialog = new CDatabaseOptionsDialog((MDatabaseCommon*)obj); - g_plugin.addOptions(wParam, &odp); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void InitCryptMenuItem(CMenuItem &mi) -{ - auto *pDb = db_get_current(); - - HookEventObj(ME_OPT_INITIALISE, OnOptionsInit, pDb); - - SET_UID(mi, 0x50321866, 0xba1, 0x46dd, 0xb3, 0xa6, 0xc3, 0xcc, 0x55, 0xf2, 0x42, 0x9e); - mi.flags = CMIF_UNICODE; - mi.position = 1000000001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_KEYS); - mi.name.w = GetMenuTitle(pDb->usesPassword()); - mi.pszService = "DB/UI/ChangePassword"; - hSetPwdMenu = Menu_AddMainMenuItem(&mi); - CreateServiceFunctionObj(mi.pszService, ChangePassword, pDb); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CEnterPasswordDialog : public CDlgBase -{ - friend class MDatabaseCommon; - - CTimer m_timer; - CCtrlData m_header; - CCtrlData m_language; - CCtrlEdit m_passwordEdit; - - int m_wrongPass = 0; - wchar_t m_newPass[100]; - MDatabaseCommon *m_db; - - void OnTimer(CTimer*) - { - UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); - char Lang[3] = { 0 }; - GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); - Lang[0] = toupper(Lang[0]); - Lang[1] = tolower(Lang[1]); - m_language.SetTextA(Lang); - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - if (msg == WM_CTLCOLORSTATIC) { - if ((HWND)lParam == m_language.GetHwnd()) { - SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); - } - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - -public: - CEnterPasswordDialog(MDatabaseCommon *db) : - CDlgBase(g_plugin, IDD_LOGIN), - m_timer(this, 1), - m_header(this, IDC_HEADERBAR), - m_language(this, IDC_LANG), - m_passwordEdit(this, IDC_USERPASS), - m_db(db) - { - m_newPass[0] = 0; - m_timer.OnEvent = Callback(this, &CEnterPasswordDialog::OnTimer); - } - - ~CEnterPasswordDialog() - { - SecureZeroMemory(m_newPass, sizeof(m_newPass)); - } - - bool OnInitDialog() override - { - m_header.SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); - - if (m_wrongPass) { - if (m_wrongPass > 2) { - m_passwordEdit.Disable(); - EnableWindow(GetDlgItem(m_hwnd, IDOK), false); - m_header.SetText(TranslateT("Too many errors!")); - } - else m_header.SetText(TranslateT("Password is not correct!")); - } - else m_header.SetText(TranslateT("Please type in your password")); - - m_timer.Start(200); - return true; - } - - bool OnApply() override - { - m_passwordEdit.GetText(m_newPass, _countof(m_newPass)); - return true; - } - - void OnDestroy() override - { - Window_FreeIcon_IcoLib(m_header.GetHwnd()); - } -}; - -int MDatabaseCommon::InitCrypt() -{ - if (m_crypto != nullptr) - return 0; - - CRYPTO_PROVIDER *pProvider = ReadProvider(); - if (pProvider == nullptr) - pProvider = SelectProvider(); - if (pProvider == nullptr) - return 1; - - if ((m_crypto = pProvider->pFactory()) == nullptr) - return 3; - - MBinBuffer key; - BOOL bSuccess = ReadCryptoKey(key); - if (bSuccess && (key.length() == m_crypto->getKeyLength())) { - // first try to set a key without password - if (!m_crypto->setKey(nullptr, (const uint8_t*)key.data(), key.length())) { - CEnterPasswordDialog dlg(this); - while (true) { - if (!dlg.DoModal()) - return 4; - - pass_ptrA szPassword(mir_utf8encodeW(dlg.m_newPass)); - if (m_crypto->setKey(szPassword, (const uint8_t*)key.data(), key.length())) { - m_bUsesPassword = true; - break; - } - dlg.m_wrongPass++; - } - } - } - else { - if (!m_crypto->generateKey()) - return 6; - StoreCryptoKey(); - } - - m_bEncrypted = ReadEncryption(); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "database.h"
+#include "encrypt.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Provider selection dialog
+
+class CSelectCryptoDialog : public CDlgBase
+{
+ CCtrlCombo m_combo;
+ CCtrlData m_descr;
+ CRYPTO_PROVIDER *m_selected = nullptr;
+
+ CRYPTO_PROVIDER *getCurrent()
+ {
+ return (CRYPTO_PROVIDER*)m_combo.GetCurData();
+ }
+
+ bool OnInitDialog() override
+ {
+ for (auto &p : arCryptoProviders)
+ m_combo.AddStringA(p->pszName, LPARAM(p));
+
+ m_combo.SetCurSel(0);
+ m_descr.SetText(arCryptoProviders[0].szDescr.w);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ m_selected = getCurrent();
+ return true;
+ }
+
+ void OnComboChanged(CCtrlCombo*)
+ {
+ m_descr.SetText(getCurrent()->szDescr.w);
+ }
+
+public:
+ CSelectCryptoDialog() :
+ CDlgBase(g_plugin, IDD_SELECT_CRYPTOPROVIDER),
+ m_combo(this, IDC_SELECTCRYPT_COMBO),
+ m_descr(this, IDC_CRYPTOPROVIDER_DESCR)
+ {
+ m_combo.OnChange = Callback(this, &CSelectCryptoDialog::OnComboChanged);
+ }
+
+ inline CRYPTO_PROVIDER* GetSelected()
+ { return m_selected;
+ }
+};
+
+CRYPTO_PROVIDER* MDatabaseCommon::SelectProvider()
+{
+ if (arCryptoProviders.getCount() == 0)
+ return nullptr;
+
+ CRYPTO_PROVIDER *pProv;
+ if (arCryptoProviders.getCount() > 1) {
+ CSelectCryptoDialog dlg;
+ dlg.DoModal();
+ pProv = dlg.GetSelected();
+ }
+ else pProv = &arCryptoProviders[0];
+
+ return (StoreProvider(pProv)) ? pProv : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL MDatabaseCommon::IsSettingEncrypted(LPCSTR szModule, LPCSTR szSetting)
+{
+ if (!_strnicmp(szSetting, "password", 8)) return true;
+ if (!mir_strcmp(szSetting, "NLProxyAuthPassword")) return true;
+ if (!mir_strcmp(szSetting, "LNPassword")) return true;
+ if (!mir_strcmp(szSetting, "FileProxyPassword")) return true;
+ if (!mir_strcmp(szSetting, "TokenSecret")) return true;
+
+ if (!mir_strcmp(szModule, "SecureIM")) {
+ if (!mir_strcmp(szSetting, "pgp")) return true;
+ if (!mir_strcmp(szSetting, "pgpPrivKey")) return true;
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HGENMENU hSetPwdMenu;
+
+__forceinline wchar_t *GetMenuTitle(bool bUsesPassword)
+{
+ return bUsesPassword ? LPGENW("Change/remove password") : LPGENW("Set password");
+}
+
+void MDatabaseCommon::SetPassword(const wchar_t *ptszPassword)
+{
+ if (mir_wstrlen(ptszPassword) == 0) {
+ m_bUsesPassword = false;
+ m_crypto->setPassword(nullptr);
+ }
+ else {
+ m_bUsesPassword = true;
+ m_crypto->setPassword(pass_ptrA(mir_utf8encodeW(ptszPassword)));
+ }
+
+ Menu_ModifyItem(hSetPwdMenu, GetMenuTitle(m_bUsesPassword), Skin_GetIconHandle(SKINICON_OTHER_KEYS));
+}
+
+static UINT oldLangID;
+void LanguageChanged(HWND hwndDlg)
+{
+ UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0);
+ char Lang[3] = { 0 };
+ if (LangID != oldLangID) {
+ oldLangID = LangID;
+ GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2);
+ Lang[0] = toupper(Lang[0]);
+ Lang[1] = tolower(Lang[1]);
+ SetDlgItemTextA(hwndDlg, IDC_LANG, Lang);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static bool CheckOldPassword(HWND hwndDlg, MDatabaseCommon *db)
+{
+ if (db->usesPassword()) {
+ wchar_t buf[100];
+ GetDlgItemText(hwndDlg, IDC_OLDPASS, buf, _countof(buf));
+ pass_ptrA oldPass(mir_utf8encodeW(buf));
+ if (!db->getCrypt()->checkPassword(oldPass)) {
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Wrong old password entered!"));
+ return false;
+ }
+ }
+ return true;
+}
+
+static INT_PTR CALLBACK sttChangePassword(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MDatabaseCommon *db = (MDatabaseCommon *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+
+ oldLangID = 0;
+ SetTimer(hwndDlg, 1, 200, nullptr);
+ LanguageChanged(hwndDlg);
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_LANG)) {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT);
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ EndDialog(hwndDlg, IDCANCEL);
+ break;
+
+ case IDREMOVE:
+ if (!CheckOldPassword(hwndDlg, db)) {
+LBL_Error:
+ SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_NCPAINT, 0, 0);
+ SetDlgItemTextA(hwndDlg, IDC_USERPASS1, "");
+ SetDlgItemTextA(hwndDlg, IDC_USERPASS2, "");
+ }
+ else {
+ db->SetPassword(nullptr);
+ db->StoreCryptoKey();
+ EndDialog(hwndDlg, IDREMOVE);
+ }
+ break;
+
+ case IDOK:
+ wchar_t buf2[100];
+ GetDlgItemText(hwndDlg, IDC_USERPASS1, buf2, _countof(buf2));
+ if (wcslen(buf2) < 3) {
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Password is too short!"));
+ goto LBL_Error;
+ }
+
+ wchar_t buf[100];
+ GetDlgItemText(hwndDlg, IDC_USERPASS2, buf, _countof(buf));
+ if (wcscmp(buf2, buf)) {
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Passwords do not match!"));
+ goto LBL_Error;
+ }
+
+ if (!CheckOldPassword(hwndDlg, db))
+ goto LBL_Error;
+
+ db->SetPassword(buf2);
+ db->StoreCryptoKey();
+ SecureZeroMemory(buf, sizeof(buf));
+ SecureZeroMemory(buf2, sizeof(buf2));
+ EndDialog(hwndDlg, IDOK);
+ }
+ break;
+
+ case WM_TIMER:
+ LanguageChanged(hwndDlg);
+ return FALSE;
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 1);
+ Window_FreeIcon_IcoLib(GetDlgItem(hwndDlg, IDC_HEADERBAR));
+ }
+
+ return FALSE;
+}
+
+static INT_PTR ChangePassword(void* obj, WPARAM, LPARAM)
+{
+ MDatabaseCommon *db = (MDatabaseCommon*)obj;
+ DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(db->usesPassword() ? IDD_CHANGEPASS : IDD_NEWPASS), nullptr, sttChangePassword, (LPARAM)db);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Options
+
+class CDatabaseOptionsDialog : public CDlgBase
+{
+ CCtrlCheck m_chkStandart, m_chkTotal;
+ CCtrlButton m_btnChangePass;
+ MDatabaseCommon *m_db;
+
+public:
+ CDatabaseOptionsDialog(MDatabaseCommon *db) :
+ CDlgBase(g_plugin, IDD_OPT_DATABASE),
+ m_db(db),
+ m_chkTotal(this, IDC_TOTAL),
+ m_chkStandart(this, IDC_STANDARD),
+ m_btnChangePass(this, IDC_USERPASS1)
+ {
+ m_btnChangePass.OnClick = Callback(this, &CDatabaseOptionsDialog::onClick_ChangePass);
+ }
+
+ bool OnInitDialog() override
+ {
+ m_chkStandart.SetState(!m_db->isEncrypted());
+ m_chkTotal.SetState(m_db->isEncrypted());
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ SetCursor(LoadCursor(nullptr, IDC_WAIT));
+ m_db->EnableEncryption(m_chkTotal.GetState() != 0);
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ m_chkStandart.SetState(!m_db->isEncrypted());
+ m_chkTotal.SetState(m_db->isEncrypted());
+ return true;
+ }
+
+ void onClick_ChangePass(CCtrlButton *)
+ {
+ ChangePassword(m_db, 0, 0);
+ }
+};
+
+static int OnOptionsInit(PVOID obj, WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { sizeof(odp) };
+ odp.position = -790000000;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.szTitle.a = LPGEN("Database");
+ odp.pDialog = new CDatabaseOptionsDialog((MDatabaseCommon*)obj);
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void InitCryptMenuItem(CMenuItem &mi)
+{
+ auto *pDb = db_get_current();
+
+ HookEventObj(ME_OPT_INITIALISE, OnOptionsInit, pDb);
+
+ SET_UID(mi, 0x50321866, 0xba1, 0x46dd, 0xb3, 0xa6, 0xc3, 0xcc, 0x55, 0xf2, 0x42, 0x9e);
+ mi.flags = CMIF_UNICODE;
+ mi.position = 1000000001;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_KEYS);
+ mi.name.w = GetMenuTitle(pDb->usesPassword());
+ mi.pszService = "DB/UI/ChangePassword";
+ hSetPwdMenu = Menu_AddMainMenuItem(&mi);
+ CreateServiceFunctionObj(mi.pszService, ChangePassword, pDb);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CEnterPasswordDialog : public CDlgBase
+{
+ friend class MDatabaseCommon;
+
+ CTimer m_timer;
+ CCtrlData m_header;
+ CCtrlData m_language;
+ CCtrlEdit m_passwordEdit;
+
+ int m_wrongPass = 0;
+ wchar_t m_newPass[100];
+ MDatabaseCommon *m_db;
+
+ void OnTimer(CTimer*)
+ {
+ UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0);
+ char Lang[3] = { 0 };
+ GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2);
+ Lang[0] = toupper(Lang[0]);
+ Lang[1] = tolower(Lang[1]);
+ m_language.SetTextA(Lang);
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ if (msg == WM_CTLCOLORSTATIC) {
+ if ((HWND)lParam == m_language.GetHwnd()) {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT);
+ }
+ }
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+public:
+ CEnterPasswordDialog(MDatabaseCommon *db) :
+ CDlgBase(g_plugin, IDD_LOGIN),
+ m_timer(this, 1),
+ m_header(this, IDC_HEADERBAR),
+ m_language(this, IDC_LANG),
+ m_passwordEdit(this, IDC_USERPASS),
+ m_db(db)
+ {
+ m_newPass[0] = 0;
+ m_timer.OnEvent = Callback(this, &CEnterPasswordDialog::OnTimer);
+ }
+
+ ~CEnterPasswordDialog()
+ {
+ SecureZeroMemory(m_newPass, sizeof(m_newPass));
+ }
+
+ bool OnInitDialog() override
+ {
+ m_header.SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true));
+
+ if (m_wrongPass) {
+ if (m_wrongPass > 2) {
+ m_passwordEdit.Disable();
+ EnableWindow(GetDlgItem(m_hwnd, IDOK), false);
+ m_header.SetText(TranslateT("Too many errors!"));
+ }
+ else m_header.SetText(TranslateT("Password is not correct!"));
+ }
+ else m_header.SetText(TranslateT("Please type in your password"));
+
+ m_timer.Start(200);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ m_passwordEdit.GetText(m_newPass, _countof(m_newPass));
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Window_FreeIcon_IcoLib(m_header.GetHwnd());
+ }
+};
+
+int MDatabaseCommon::InitCrypt()
+{
+ if (m_crypto != nullptr)
+ return 0;
+
+ CRYPTO_PROVIDER *pProvider = ReadProvider();
+ if (pProvider == nullptr)
+ pProvider = SelectProvider();
+ if (pProvider == nullptr)
+ return 1;
+
+ if ((m_crypto = pProvider->pFactory()) == nullptr)
+ return 3;
+
+ MBinBuffer key;
+ BOOL bSuccess = ReadCryptoKey(key);
+ if (bSuccess && (key.length() == m_crypto->getKeyLength())) {
+ // first try to set a key without password
+ if (!m_crypto->setKey(nullptr, (const uint8_t*)key.data(), key.length())) {
+ CEnterPasswordDialog dlg(this);
+ while (true) {
+ if (!dlg.DoModal())
+ return 4;
+
+ pass_ptrA szPassword(mir_utf8encodeW(dlg.m_newPass));
+ if (m_crypto->setKey(szPassword, (const uint8_t*)key.data(), key.length())) {
+ m_bUsesPassword = true;
+ break;
+ }
+ dlg.m_wrongPass++;
+ }
+ }
+ }
+ else {
+ if (!m_crypto->generateKey())
+ return 6;
+ StoreCryptoKey();
+ }
+
+ m_bEncrypted = ReadEncryption();
+ return 0;
+}
diff --git a/src/mir_app/src/MDatabaseReadonly.cpp b/src/mir_app/src/MDatabaseReadonly.cpp index f3ed3da7ef..f022d5e5fb 100644 --- a/src/mir_app/src/MDatabaseReadonly.cpp +++ b/src/mir_app/src/MDatabaseReadonly.cpp @@ -1,180 +1,180 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "database.h" - -MDatabaseReadonly::MDatabaseReadonly() -{ -} - -BOOL MDatabaseReadonly::IsRelational(void) -{ - return FALSE; -} - -void MDatabaseReadonly::SetCacheSafetyMode(BOOL) -{ -} - -BOOL MDatabaseReadonly::EnumModuleNames(DBMODULEENUMPROC, void*) -{ - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadCryptoKey(MBinBuffer&) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreCryptoKey() -{ - return FALSE; -} - -STDMETHODIMP_(CRYPTO_PROVIDER*) MDatabaseReadonly::ReadProvider() -{ - return nullptr; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreProvider(CRYPTO_PROVIDER *) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::EnableEncryption(BOOL) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadEncryption() -{ - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MCONTACT MDatabaseReadonly::AddContact(void) -{ - return 0; -} - -int MDatabaseReadonly::DeleteContact(MCONTACT) -{ - return 1; -} - -BOOL MDatabaseReadonly::IsDbContact(MCONTACT contactID) -{ - return contactID == 1; -} - -int MDatabaseReadonly::GetContactSize(void) -{ - return sizeof(DBCachedContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MEVENT MDatabaseReadonly::AddEvent(MCONTACT, const DBEVENTINFO*) -{ - return 0; -} - -BOOL MDatabaseReadonly::DeleteEvent(MEVENT) -{ - return 1; -} - -BOOL MDatabaseReadonly::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*) -{ - return 1; -} - -int MDatabaseReadonly::GetBlobSize(MEVENT) -{ - return 0; -} - -MEVENT MDatabaseReadonly::FindFirstUnreadEvent(MCONTACT) -{ - return 0; -} - -BOOL MDatabaseReadonly::MarkEventRead(MCONTACT, MEVENT) -{ - return 1; -} - -MCONTACT MDatabaseReadonly::GetEventContact(MEVENT) -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseReadonly::GetContactSettingWorker(MCONTACT, LPCSTR, LPCSTR, DBVARIANT*, int) -{ - return 1; -} - -BOOL MDatabaseReadonly::WriteContactSettingWorker(MCONTACT, DBCONTACTWRITESETTING&) -{ - return 1; -} - -BOOL MDatabaseReadonly::DeleteContactSetting(MCONTACT, LPCSTR, LPCSTR) -{ - return 1; -} - -BOOL MDatabaseReadonly::EnumContactSettings(MCONTACT, DBSETTINGENUMPROC, const char*, void*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseReadonly::MetaMergeHistory(DBCachedContact*, DBCachedContact*) -{ - return 1; -} - -BOOL MDatabaseReadonly::MetaSplitHistory(DBCachedContact*, DBCachedContact*) -{ - return 1; -} - -BOOL MDatabaseReadonly::MetaRemoveSubHistory(DBCachedContact*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MEVENT MDatabaseReadonly::GetEventById(LPCSTR, LPCSTR) -{ - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "database.h"
+
+MDatabaseReadonly::MDatabaseReadonly()
+{
+}
+
+BOOL MDatabaseReadonly::IsRelational(void)
+{
+ return FALSE;
+}
+
+void MDatabaseReadonly::SetCacheSafetyMode(BOOL)
+{
+}
+
+BOOL MDatabaseReadonly::EnumModuleNames(DBMODULEENUMPROC, void*)
+{
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadCryptoKey(MBinBuffer&)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreCryptoKey()
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(CRYPTO_PROVIDER*) MDatabaseReadonly::ReadProvider()
+{
+ return nullptr;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreProvider(CRYPTO_PROVIDER *)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::EnableEncryption(BOOL)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadEncryption()
+{
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MCONTACT MDatabaseReadonly::AddContact(void)
+{
+ return 0;
+}
+
+int MDatabaseReadonly::DeleteContact(MCONTACT)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::IsDbContact(MCONTACT contactID)
+{
+ return contactID == 1;
+}
+
+int MDatabaseReadonly::GetContactSize(void)
+{
+ return sizeof(DBCachedContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MEVENT MDatabaseReadonly::AddEvent(MCONTACT, const DBEVENTINFO*)
+{
+ return 0;
+}
+
+BOOL MDatabaseReadonly::DeleteEvent(MEVENT)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*)
+{
+ return 1;
+}
+
+int MDatabaseReadonly::GetBlobSize(MEVENT)
+{
+ return 0;
+}
+
+MEVENT MDatabaseReadonly::FindFirstUnreadEvent(MCONTACT)
+{
+ return 0;
+}
+
+BOOL MDatabaseReadonly::MarkEventRead(MCONTACT, MEVENT)
+{
+ return 1;
+}
+
+MCONTACT MDatabaseReadonly::GetEventContact(MEVENT)
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL MDatabaseReadonly::GetContactSettingWorker(MCONTACT, LPCSTR, LPCSTR, DBVARIANT*, int)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::WriteContactSettingWorker(MCONTACT, DBCONTACTWRITESETTING&)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::DeleteContactSetting(MCONTACT, LPCSTR, LPCSTR)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::EnumContactSettings(MCONTACT, DBSETTINGENUMPROC, const char*, void*)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL MDatabaseReadonly::MetaMergeHistory(DBCachedContact*, DBCachedContact*)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::MetaSplitHistory(DBCachedContact*, DBCachedContact*)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::MetaRemoveSubHistory(DBCachedContact*)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MEVENT MDatabaseReadonly::GetEventById(LPCSTR, LPCSTR)
+{
+ return 0;
+}
diff --git a/src/mir_app/src/MHttpRequest.cpp b/src/mir_app/src/MHttpRequest.cpp index cefe4d551c..7935974463 100644 --- a/src/mir_app/src/MHttpRequest.cpp +++ b/src/mir_app/src/MHttpRequest.cpp @@ -1,93 +1,93 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -MHttpRequest::MHttpRequest() -{ - cbSize = sizeof(NETLIBHTTPREQUEST); - requestType = REQUEST_GET; -} - -MHttpRequest::~MHttpRequest() -{ - for (int i = 0; i < headersCount; i++) { - mir_free(headers[i].szName); - mir_free(headers[i].szValue); - } - mir_free(headers); - mir_free(pData); -} - -void MHttpRequest::AddHeader(LPCSTR szName, LPCSTR szValue) -{ - for (int i = 0; i < headersCount; i++) - if (!mir_strcmp(headers[i].szName, szName)) { - replaceStr(headers[i].szValue, szValue); - return; - } - - headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1)); - headers[headersCount].szName = mir_strdup(szName); - headers[headersCount].szValue = mir_strdup(szValue); - headersCount++; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%ld", param.szName, param.iValue); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%lld", param.szName, param.iValue); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%s", param.szName, mir_urlEncode(param.szValue).c_str()); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const WCHAR_PARAM ¶m) -{ - T2Utf szValue(param.wszValue); - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%s", param.szName, mir_urlEncode(szValue).c_str()); - return pReq; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+MHttpRequest::MHttpRequest()
+{
+ cbSize = sizeof(NETLIBHTTPREQUEST);
+ requestType = REQUEST_GET;
+}
+
+MHttpRequest::~MHttpRequest()
+{
+ for (int i = 0; i < headersCount; i++) {
+ mir_free(headers[i].szName);
+ mir_free(headers[i].szValue);
+ }
+ mir_free(headers);
+ mir_free(pData);
+}
+
+void MHttpRequest::AddHeader(LPCSTR szName, LPCSTR szValue)
+{
+ for (int i = 0; i < headersCount; i++)
+ if (!mir_strcmp(headers[i].szName, szName)) {
+ replaceStr(headers[i].szValue, szValue);
+ return;
+ }
+
+ headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1));
+ headers[headersCount].szName = mir_strdup(szName);
+ headers[headersCount].szValue = mir_strdup(szValue);
+ headersCount++;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT_PARAM ¶m)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%ld", param.szName, param.iValue);
+ return pReq;
+}
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM ¶m)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%lld", param.szName, param.iValue);
+ return pReq;
+}
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM ¶m)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%s", param.szName, mir_urlEncode(param.szValue).c_str());
+ return pReq;
+}
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const WCHAR_PARAM ¶m)
+{
+ T2Utf szValue(param.wszValue);
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%s", param.szName, mir_urlEncode(szValue).c_str());
+ return pReq;
+}
diff --git a/src/mir_app/src/addcontact.cpp b/src/mir_app/src/addcontact.cpp index e065bf94df..2eda1400ca 100644 --- a/src/mir_app/src/addcontact.cpp +++ b/src/mir_app/src/addcontact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/auth.cpp b/src/mir_app/src/auth.cpp index c40053819f..0520c20a11 100644 --- a/src/mir_app/src/auth.cpp +++ b/src/mir_app/src/auth.cpp @@ -1,367 +1,367 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Auth Request dialog - -class CAuthReqDlg : public CDlgBase -{ - MEVENT m_hDbEvent; - MCONTACT m_hContact; - const char *m_szProto; - - CCtrlBase fldHeader, fldReason; - CCtrlEdit edtReason; - CCtrlCheck chkAdd; - CCtrlButton btnAdd, btnDetails, btnLater; - -public: - CAuthReqDlg(MEVENT hEvent) : - CDlgBase(g_plugin, IDD_AUTHREQ), - m_hDbEvent(hEvent), - fldHeader(this, IDC_HEADERBAR), - fldReason(this, IDC_REASON), - edtReason(this, IDC_DENYREASON), - btnAdd(this, IDC_ADD), - btnLater(this, IDC_DECIDELATER), - btnDetails(this, IDC_DETAILS), - chkAdd(this, IDC_ADDCHECK) - { - btnLater.OnClick = Callback(this, &CAuthReqDlg::onClick_Later); - btnDetails.OnClick = Callback(this, &CAuthReqDlg::onClick_Details); - } - - bool OnInitDialog() override - { - Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); - Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); - - // blob is: uin(uint32_t), hcontact(uint32_t), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ) - DBEVENTINFO dbei = {}; - dbei.cbBlob = -1; - if (db_event_get(m_hDbEvent, &dbei)) - return false; - - m_szProto = dbei.szModule; - PROTOACCOUNT *acc = Proto_GetAccount(dbei.szModule); - - uint32_t uin = *(uint32_t*)dbei.pBlob; - m_hContact = DbGetAuthEventContact(&dbei); - char *nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; - char *first = nick + mir_strlen(nick) + 1; - char *last = first + mir_strlen(first) + 1; - char *email = last + mir_strlen(last) + 1; - char *reason = email + mir_strlen(email) + 1; - - #ifdef _WINDOWS - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); - #endif - - ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); - ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); - ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); - ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); - ptrW reasonT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(reason) : mir_a2u(reason)); - - CMStringW wszHeader; - if (firstT[0] && lastT[0]) - wszHeader.Format(L"%s %s", (wchar_t*)firstT, (wchar_t*)lastT); - else if (firstT[0]) - wszHeader = firstT.get(); - else if (lastT[0]) - wszHeader = lastT.get(); - - if (mir_wstrlen(nickT)) { - if (wszHeader.IsEmpty()) - wszHeader = nickT.get(); - else - wszHeader.AppendFormat(L" %s", nickT.get()); - } - if (wszHeader.IsEmpty()) - wszHeader = TranslateT("<Unknown>"); - - if (uin && emailT[0]) - wszHeader.AppendFormat(TranslateT(" requested authorization\n%u (%s) on %s"), uin, emailT.get(), acc->tszAccountName); - else if (uin) - wszHeader.AppendFormat(TranslateT(" requested authorization\n%u on %s"), uin, acc->tszAccountName); - else - wszHeader.AppendFormat(TranslateT(" requested authorization\n%s on %s"), emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); - fldHeader.SetText(wszHeader); - - fldReason.SetText(reasonT); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - - edtReason.SetMaxLength(255); - if (CallProtoService(dbei.szModule, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON) { - edtReason.Disable(); - edtReason.SetText(TranslateT("Feature is not supported by protocol")); - } - - if (Contact::OnList(m_hContact)) { - chkAdd.Disable(); - chkAdd.SetState(false); - } - else chkAdd.SetState(true); - return true; - } - - bool OnApply() override - { - CallProtoService(m_szProto, PS_AUTHALLOW, m_hDbEvent, 0); - - if (chkAdd.GetState()) - Contact::AddByEvent(m_hDbEvent, m_hwnd); - return true; - } - - void OnDestroy() override - { - if (!m_bSucceeded) { - if (edtReason.Enabled()) - CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, (LPARAM)ptrW(edtReason.GetText())); - else - CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, 0); - } - - Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); - Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); - - #ifdef _WINDOWS - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - #endif - } - - void onClick_Later(CCtrlButton*) - { - m_bSucceeded = true; - Close(); - } - - void onClick_Details(CCtrlButton*) - { - CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); - } -}; - -static INT_PTR ShowReqWindow(WPARAM, LPARAM lParam) -{ - (new CAuthReqDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CAddedDlg : public CDlgBase -{ - MEVENT m_hDbEvent; - MCONTACT m_hContact; - - CCtrlBase fldHeader; - CCtrlButton btnDetails, btnAdd; - -public: - CAddedDlg(MEVENT hEvent) : - CDlgBase(g_plugin, IDD_ADDED), - m_hDbEvent(hEvent), - btnAdd(this, IDC_ADD), - btnDetails(this, IDC_DETAILS), - fldHeader(this, IDC_HEADERBAR) - { - btnAdd.OnClick = Callback(this, &CAddedDlg::onClick_Add); - btnDetails.OnClick = Callback(this, &CAddedDlg::onClick_Details); - } - - bool OnInitDialog() override - { - Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); - Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); - - // blob is: uin(uint32_t), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) - DB::EventInfo dbei; - dbei.cbBlob = -1; - db_event_get(m_hDbEvent, &dbei); - - m_hContact = DbGetAuthEventContact(&dbei); - - uint32_t uin = *(uint32_t*)dbei.pBlob; - char* nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; - char* first = nick + mir_strlen(nick) + 1; - char* last = first + mir_strlen(first) + 1; - char* email = last + mir_strlen(last) + 1; - - #ifdef _WINDOWS - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); - #endif - - PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule); - - ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); - ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); - ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); - ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); - - wchar_t name[128] = L""; - int off = 0; - if (firstT[0] && lastT[0]) - off = mir_snwprintf(name, L"%s %s", firstT.get(), lastT.get()); - else if (firstT[0]) - off = mir_snwprintf(name, L"%s", firstT.get()); - else if (lastT[0]) - off = mir_snwprintf(name, L"%s", lastT.get()); - if (nickT[0]) { - if (off) - mir_snwprintf(name + off, _countof(name) - off, L" (%s)", nickT.get()); - else - wcsncpy_s(name, nickT, _TRUNCATE); - } - if (!name[0]) - wcsncpy_s(name, TranslateT("<Unknown>"), _TRUNCATE); - - wchar_t hdr[256]; - if (uin && emailT[0]) - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT.get(), acc->tszAccountName); - else if (uin) - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName); - else - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); - fldHeader.SetText(hdr); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - return true; - } - - bool OnApply() - { - Contact::AddByEvent(m_hDbEvent, m_hwnd); - return true; - } - - void OnDestroy() override - { - Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); - Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); - - #ifdef _WINDOWS - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - #endif - } - - void onClick_Add(CCtrlButton*) - { - Contact::AddByEvent(m_hDbEvent, m_hwnd); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - } - - void onClick_Details(CCtrlButton*) - { - CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); - } -}; - -static INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam) -{ - (new CAddedDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define MS_AUTH_SHOWADDED "Auth/ShowAdded" -#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest" - -static int AuthEventAdded(WPARAM, LPARAM lParam) -{ - wchar_t szTooltip[256]; - MEVENT hDbEvent = (MEVENT)lParam; - - DB::EventInfo dbei; - db_event_get(lParam, &dbei); - if (dbei.flags & (DBEF_SENT | DBEF_READ) || (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED)) - return 0; - - dbei.cbBlob = -1; - db_event_get(hDbEvent, &dbei); - - MCONTACT hContact = DbGetAuthEventContact(&dbei); - - CLISTEVENT cle = {}; - cle.hContact = hContact; - cle.szTooltip.w = szTooltip; - cle.flags = CLEF_UNICODE; - cle.lParam = lParam; - cle.hDbEvent = hDbEvent; - - ptrW szUid(Contact::GetInfo(CNF_UNIQUEID, hContact)); - - if (dbei.eventType == EVENTTYPE_AUTHREQUEST) { - Skin_PlaySound("AuthRequest"); - if (szUid) - mir_snwprintf(szTooltip, TranslateT("%s requests authorization"), szUid.get()); - else - mir_snwprintf(szTooltip, TranslateT("%u requests authorization"), *(uint32_t*)dbei.pBlob); - - cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_REQUEST); - cle.pszService = MS_AUTH_SHOWREQUEST; - g_clistApi.pfnAddEvent(&cle); - } - else if (dbei.eventType == EVENTTYPE_ADDED) { - Skin_PlaySound("AddedEvent"); - if (szUid) - mir_snwprintf(szTooltip, TranslateT("%s added you to their contact list"), szUid.get()); - else - mir_snwprintf(szTooltip, TranslateT("%u added you to their contact list"), *(uint32_t*)dbei.pBlob); - - cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_ADD); - cle.pszService = MS_AUTH_SHOWADDED; - g_clistApi.pfnAddEvent(&cle); - } - return 0; -} - -static void CALLBACK LaunchAuth() -{ - HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded); -} - -int LoadSendRecvAuthModule(void) -{ - CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow); - CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow); - Miranda_WaitOnHandle(LaunchAuth); - - g_plugin.addSound("AuthRequest", LPGENW("Alerts"), LPGENW("Authorization request")); - g_plugin.addSound("AddedEvent", LPGENW("Alerts"), LPGENW("Added event")); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Auth Request dialog
+
+class CAuthReqDlg : public CDlgBase
+{
+ MEVENT m_hDbEvent;
+ MCONTACT m_hContact;
+ const char *m_szProto;
+
+ CCtrlBase fldHeader, fldReason;
+ CCtrlEdit edtReason;
+ CCtrlCheck chkAdd;
+ CCtrlButton btnAdd, btnDetails, btnLater;
+
+public:
+ CAuthReqDlg(MEVENT hEvent) :
+ CDlgBase(g_plugin, IDD_AUTHREQ),
+ m_hDbEvent(hEvent),
+ fldHeader(this, IDC_HEADERBAR),
+ fldReason(this, IDC_REASON),
+ edtReason(this, IDC_DENYREASON),
+ btnAdd(this, IDC_ADD),
+ btnLater(this, IDC_DECIDELATER),
+ btnDetails(this, IDC_DETAILS),
+ chkAdd(this, IDC_ADDCHECK)
+ {
+ btnLater.OnClick = Callback(this, &CAuthReqDlg::onClick_Later);
+ btnDetails.OnClick = Callback(this, &CAuthReqDlg::onClick_Details);
+ }
+
+ bool OnInitDialog() override
+ {
+ Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details"));
+ Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list"));
+
+ // blob is: uin(uint32_t), hcontact(uint32_t), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)
+ DBEVENTINFO dbei = {};
+ dbei.cbBlob = -1;
+ if (db_event_get(m_hDbEvent, &dbei))
+ return false;
+
+ m_szProto = dbei.szModule;
+ PROTOACCOUNT *acc = Proto_GetAccount(dbei.szModule);
+
+ uint32_t uin = *(uint32_t*)dbei.pBlob;
+ m_hContact = DbGetAuthEventContact(&dbei);
+ char *nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2;
+ char *first = nick + mir_strlen(nick) + 1;
+ char *last = first + mir_strlen(first) + 1;
+ char *email = last + mir_strlen(last) + 1;
+ char *reason = email + mir_strlen(email) + 1;
+
+ #ifdef _WINDOWS
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0));
+ SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0));
+ #endif
+
+ ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last));
+ ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first));
+ ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick));
+ ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email));
+ ptrW reasonT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(reason) : mir_a2u(reason));
+
+ CMStringW wszHeader;
+ if (firstT[0] && lastT[0])
+ wszHeader.Format(L"%s %s", (wchar_t*)firstT, (wchar_t*)lastT);
+ else if (firstT[0])
+ wszHeader = firstT.get();
+ else if (lastT[0])
+ wszHeader = lastT.get();
+
+ if (mir_wstrlen(nickT)) {
+ if (wszHeader.IsEmpty())
+ wszHeader = nickT.get();
+ else
+ wszHeader.AppendFormat(L" %s", nickT.get());
+ }
+ if (wszHeader.IsEmpty())
+ wszHeader = TranslateT("<Unknown>");
+
+ if (uin && emailT[0])
+ wszHeader.AppendFormat(TranslateT(" requested authorization\n%u (%s) on %s"), uin, emailT.get(), acc->tszAccountName);
+ else if (uin)
+ wszHeader.AppendFormat(TranslateT(" requested authorization\n%u on %s"), uin, acc->tszAccountName);
+ else
+ wszHeader.AppendFormat(TranslateT(" requested authorization\n%s on %s"), emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName);
+ fldHeader.SetText(wszHeader);
+
+ fldReason.SetText(reasonT);
+
+ if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact))
+ btnAdd.Hide();
+
+ edtReason.SetMaxLength(255);
+ if (CallProtoService(dbei.szModule, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON) {
+ edtReason.Disable();
+ edtReason.SetText(TranslateT("Feature is not supported by protocol"));
+ }
+
+ if (Contact::OnList(m_hContact)) {
+ chkAdd.Disable();
+ chkAdd.SetState(false);
+ }
+ else chkAdd.SetState(true);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ CallProtoService(m_szProto, PS_AUTHALLOW, m_hDbEvent, 0);
+
+ if (chkAdd.GetState())
+ Contact::AddByEvent(m_hDbEvent, m_hwnd);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ if (!m_bSucceeded) {
+ if (edtReason.Enabled())
+ CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, (LPARAM)ptrW(edtReason.GetText()));
+ else
+ CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, 0);
+ }
+
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD);
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS);
+
+ #ifdef _WINDOWS
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0));
+ #endif
+ }
+
+ void onClick_Later(CCtrlButton*)
+ {
+ m_bSucceeded = true;
+ Close();
+ }
+
+ void onClick_Details(CCtrlButton*)
+ {
+ CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0);
+ }
+};
+
+static INT_PTR ShowReqWindow(WPARAM, LPARAM lParam)
+{
+ (new CAuthReqDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CAddedDlg : public CDlgBase
+{
+ MEVENT m_hDbEvent;
+ MCONTACT m_hContact;
+
+ CCtrlBase fldHeader;
+ CCtrlButton btnDetails, btnAdd;
+
+public:
+ CAddedDlg(MEVENT hEvent) :
+ CDlgBase(g_plugin, IDD_ADDED),
+ m_hDbEvent(hEvent),
+ btnAdd(this, IDC_ADD),
+ btnDetails(this, IDC_DETAILS),
+ fldHeader(this, IDC_HEADERBAR)
+ {
+ btnAdd.OnClick = Callback(this, &CAddedDlg::onClick_Add);
+ btnDetails.OnClick = Callback(this, &CAddedDlg::onClick_Details);
+ }
+
+ bool OnInitDialog() override
+ {
+ Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details"));
+ Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list"));
+
+ // blob is: uin(uint32_t), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ)
+ DB::EventInfo dbei;
+ dbei.cbBlob = -1;
+ db_event_get(m_hDbEvent, &dbei);
+
+ m_hContact = DbGetAuthEventContact(&dbei);
+
+ uint32_t uin = *(uint32_t*)dbei.pBlob;
+ char* nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2;
+ char* first = nick + mir_strlen(nick) + 1;
+ char* last = first + mir_strlen(first) + 1;
+ char* email = last + mir_strlen(last) + 1;
+
+ #ifdef _WINDOWS
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0));
+ SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0));
+ #endif
+
+ PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule);
+
+ ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last));
+ ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first));
+ ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick));
+ ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email));
+
+ wchar_t name[128] = L"";
+ int off = 0;
+ if (firstT[0] && lastT[0])
+ off = mir_snwprintf(name, L"%s %s", firstT.get(), lastT.get());
+ else if (firstT[0])
+ off = mir_snwprintf(name, L"%s", firstT.get());
+ else if (lastT[0])
+ off = mir_snwprintf(name, L"%s", lastT.get());
+ if (nickT[0]) {
+ if (off)
+ mir_snwprintf(name + off, _countof(name) - off, L" (%s)", nickT.get());
+ else
+ wcsncpy_s(name, nickT, _TRUNCATE);
+ }
+ if (!name[0])
+ wcsncpy_s(name, TranslateT("<Unknown>"), _TRUNCATE);
+
+ wchar_t hdr[256];
+ if (uin && emailT[0])
+ mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT.get(), acc->tszAccountName);
+ else if (uin)
+ mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName);
+ else
+ mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName);
+ fldHeader.SetText(hdr);
+
+ if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact))
+ btnAdd.Hide();
+ return true;
+ }
+
+ bool OnApply()
+ {
+ Contact::AddByEvent(m_hDbEvent, m_hwnd);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD);
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS);
+
+ #ifdef _WINDOWS
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0));
+ #endif
+ }
+
+ void onClick_Add(CCtrlButton*)
+ {
+ Contact::AddByEvent(m_hDbEvent, m_hwnd);
+
+ if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact))
+ btnAdd.Hide();
+ }
+
+ void onClick_Details(CCtrlButton*)
+ {
+ CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0);
+ }
+};
+
+static INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam)
+{
+ (new CAddedDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define MS_AUTH_SHOWADDED "Auth/ShowAdded"
+#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest"
+
+static int AuthEventAdded(WPARAM, LPARAM lParam)
+{
+ wchar_t szTooltip[256];
+ MEVENT hDbEvent = (MEVENT)lParam;
+
+ DB::EventInfo dbei;
+ db_event_get(lParam, &dbei);
+ if (dbei.flags & (DBEF_SENT | DBEF_READ) || (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED))
+ return 0;
+
+ dbei.cbBlob = -1;
+ db_event_get(hDbEvent, &dbei);
+
+ MCONTACT hContact = DbGetAuthEventContact(&dbei);
+
+ CLISTEVENT cle = {};
+ cle.hContact = hContact;
+ cle.szTooltip.w = szTooltip;
+ cle.flags = CLEF_UNICODE;
+ cle.lParam = lParam;
+ cle.hDbEvent = hDbEvent;
+
+ ptrW szUid(Contact::GetInfo(CNF_UNIQUEID, hContact));
+
+ if (dbei.eventType == EVENTTYPE_AUTHREQUEST) {
+ Skin_PlaySound("AuthRequest");
+ if (szUid)
+ mir_snwprintf(szTooltip, TranslateT("%s requests authorization"), szUid.get());
+ else
+ mir_snwprintf(szTooltip, TranslateT("%u requests authorization"), *(uint32_t*)dbei.pBlob);
+
+ cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_REQUEST);
+ cle.pszService = MS_AUTH_SHOWREQUEST;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ else if (dbei.eventType == EVENTTYPE_ADDED) {
+ Skin_PlaySound("AddedEvent");
+ if (szUid)
+ mir_snwprintf(szTooltip, TranslateT("%s added you to their contact list"), szUid.get());
+ else
+ mir_snwprintf(szTooltip, TranslateT("%u added you to their contact list"), *(uint32_t*)dbei.pBlob);
+
+ cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_ADD);
+ cle.pszService = MS_AUTH_SHOWADDED;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ return 0;
+}
+
+static void CALLBACK LaunchAuth()
+{
+ HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded);
+}
+
+int LoadSendRecvAuthModule(void)
+{
+ CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow);
+ CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow);
+ Miranda_WaitOnHandle(LaunchAuth);
+
+ g_plugin.addSound("AuthRequest", LPGENW("Alerts"), LPGENW("Authorization request"));
+ g_plugin.addSound("AddedEvent", LPGENW("Alerts"), LPGENW("Added event"));
+ return 0;
+}
diff --git a/src/mir_app/src/button.cpp b/src/mir_app/src/button.cpp index 4a9b548c83..b97685716e 100644 --- a/src/mir_app/src/button.cpp +++ b/src/mir_app/src/button.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_manager.cpp b/src/mir_app/src/chat_manager.cpp index 3e0f2b8b5d..7019e3a2a7 100644 --- a/src/mir_app/src/chat_manager.cpp +++ b/src/mir_app/src/chat_manager.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_rtf.cpp b/src/mir_app/src/chat_rtf.cpp index 82545dc363..508fc1a86d 100644 --- a/src/mir_app/src/chat_rtf.cpp +++ b/src/mir_app/src/chat_rtf.cpp @@ -1,204 +1,204 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting -// tags and return plain text - -static wchar_t tszRtfBreaks[] = L" \\\n\r"; - -static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst) -{ - const wchar_t *pszText = Text; - int iIndex = 1; - - static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];"; - wchar_t szRed[10], szGreen[10], szBlue[10]; - - const wchar_t *p1 = wcsstr(pszText, L"\\colortbl"); - if (!p1) - return; - - const wchar_t *pEnd = wcschr(p1, '}'); - - const wchar_t *p2 = wcsstr(p1, L"\\red"); - - for (int i = 0; i < iCount; i++) - pDst[i] = -1; - - while (p2 && p2 < pEnd) { - if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) { - for (int i = 0; i < iCount; i++) { - if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue))) - pDst[i] = iIndex; - } - } - iIndex++; - p1 = p2; - p1++; - - p2 = wcsstr(p1, L"\\red"); - } -} - -static int GetRtfIndex(int iCol, int iCount, int *pIndex) -{ - for (int i = 0; i < iCount; i++) - if (pIndex[i] == iCol) - return i; - - return -1; -} - -int DoRtfToTags(CMStringW &pszText, int iNumColors, COLORREF *pColors) -{ - if (pszText.IsEmpty()) - return FALSE; - - // create an index of colors in the module and map them to - // corresponding colors in the RTF color table - int *pIndex = (int*)_alloca(iNumColors * sizeof(int)); - CreateColorMap(pszText, iNumColors, pColors, pIndex); - - // scan the file for rtf commands and remove or parse them - int idx = pszText.Find(L"\\pard"); - if (idx == -1) { - if ((idx = pszText.Find(L"\\ltrpar")) == -1) - return FALSE; - idx += 7; - } - else idx += 5; - - bool bInsideColor = false, bInsideUl = false; - CMStringW res; - - // iterate through all characters, if rtf control character found then take action - for (const wchar_t *p = pszText.GetString() + idx; *p;) { - switch (*p) { - case '\\': - if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters - res.AppendChar(p[1]); - p += 2; break; - } - if (p[1] == '~') { // non-breaking space - res.AppendChar(0xA0); - p += 2; break; - } - - if (!wcsncmp(p, L"\\cf", 3)) { // foreground color - int iCol = _wtoi(p + 3); - int iInd = GetRtfIndex(iCol, iNumColors, pIndex); - bInsideColor = iInd > 0; - } - else if (!wcsncmp(p, L"\\highlight", 10)) { //background color - wchar_t szTemp[20]; - int iCol = _wtoi(p + 10); - mir_snwprintf(szTemp, L"%d", iCol); - } - else if (!wcsncmp(p, L"\\line", 5)) { // soft line break; - res.AppendChar('\n'); - } - else if (!wcsncmp(p, L"\\endash", 7)) { - res.AppendChar(0x2013); - } - else if (!wcsncmp(p, L"\\emdash", 7)) { - res.AppendChar(0x2014); - } - else if (!wcsncmp(p, L"\\bullet", 7)) { - res.AppendChar(0x2022); - } - else if (!wcsncmp(p, L"\\ldblquote", 10)) { - res.AppendChar(0x201C); - } - else if (!wcsncmp(p, L"\\rdblquote", 10)) { - res.AppendChar(0x201D); - } - else if (!wcsncmp(p, L"\\lquote", 7)) { - res.AppendChar(0x2018); - } - else if (!wcsncmp(p, L"\\rquote", 7)) { - res.AppendChar(0x2019); - } - else if (!wcsncmp(p, L"\\b", 2)) { //bold - res.Append((p[2] != '0') ? L"[b]" : L"[/b]"); - } - else if (!wcsncmp(p, L"\\i", 2)) { // italics - res.Append((p[2] != '0') ? L"[i]" : L"[/i]"); - } - else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out - res.Append((p[7] != '0') ? L"[s]" : L"[/s]"); - } - else if (!wcsncmp(p, L"\\ul", 3)) { // underlined - if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) { - res.Append(L"[u]"); - bInsideUl = true; - } - else if (!wcsncmp(p + 3, L"none", 4)) { - if (bInsideUl) - res.Append(L"[/u]"); - bInsideUl = false; - } - } - else if (!wcsncmp(p, L"\\tab", 4)) { // tab - res.AppendChar('\t'); - } - else if (p[1] == '\'') { // special character - if (p[2] != ' ' && p[2] != '\\') { - wchar_t tmp[10], *t = tmp; - *t++ = p[2]; - if (p[3] != ' ' && p[3] != '\\') - *t++ = p[3]; - *t = 0; - - // convert string containing char in hex format to int. - wchar_t *stoppedHere; - res.AppendChar(wcstol(tmp, &stoppedHere, 16)); - } - } - - p++; // skip initial slash - p += wcscspn(p, tszRtfBreaks); - if (*p == ' ') - p++; - break; - - case '{': // other RTF control characters - case '}': - p++; - break; - - default: // other text that should not be touched - res.AppendChar(*p++); - break; - } - } - - if (bInsideUl) - res.Append(L"[/u]"); - - pszText = res; - return TRUE; -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "chat.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting
+// tags and return plain text
+
+static wchar_t tszRtfBreaks[] = L" \\\n\r";
+
+static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst)
+{
+ const wchar_t *pszText = Text;
+ int iIndex = 1;
+
+ static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];";
+ wchar_t szRed[10], szGreen[10], szBlue[10];
+
+ const wchar_t *p1 = wcsstr(pszText, L"\\colortbl");
+ if (!p1)
+ return;
+
+ const wchar_t *pEnd = wcschr(p1, '}');
+
+ const wchar_t *p2 = wcsstr(p1, L"\\red");
+
+ for (int i = 0; i < iCount; i++)
+ pDst[i] = -1;
+
+ while (p2 && p2 < pEnd) {
+ if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) {
+ for (int i = 0; i < iCount; i++) {
+ if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue)))
+ pDst[i] = iIndex;
+ }
+ }
+ iIndex++;
+ p1 = p2;
+ p1++;
+
+ p2 = wcsstr(p1, L"\\red");
+ }
+}
+
+static int GetRtfIndex(int iCol, int iCount, int *pIndex)
+{
+ for (int i = 0; i < iCount; i++)
+ if (pIndex[i] == iCol)
+ return i;
+
+ return -1;
+}
+
+int DoRtfToTags(CMStringW &pszText, int iNumColors, COLORREF *pColors)
+{
+ if (pszText.IsEmpty())
+ return FALSE;
+
+ // create an index of colors in the module and map them to
+ // corresponding colors in the RTF color table
+ int *pIndex = (int*)_alloca(iNumColors * sizeof(int));
+ CreateColorMap(pszText, iNumColors, pColors, pIndex);
+
+ // scan the file for rtf commands and remove or parse them
+ int idx = pszText.Find(L"\\pard");
+ if (idx == -1) {
+ if ((idx = pszText.Find(L"\\ltrpar")) == -1)
+ return FALSE;
+ idx += 7;
+ }
+ else idx += 5;
+
+ bool bInsideColor = false, bInsideUl = false;
+ CMStringW res;
+
+ // iterate through all characters, if rtf control character found then take action
+ for (const wchar_t *p = pszText.GetString() + idx; *p;) {
+ switch (*p) {
+ case '\\':
+ if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters
+ res.AppendChar(p[1]);
+ p += 2; break;
+ }
+ if (p[1] == '~') { // non-breaking space
+ res.AppendChar(0xA0);
+ p += 2; break;
+ }
+
+ if (!wcsncmp(p, L"\\cf", 3)) { // foreground color
+ int iCol = _wtoi(p + 3);
+ int iInd = GetRtfIndex(iCol, iNumColors, pIndex);
+ bInsideColor = iInd > 0;
+ }
+ else if (!wcsncmp(p, L"\\highlight", 10)) { //background color
+ wchar_t szTemp[20];
+ int iCol = _wtoi(p + 10);
+ mir_snwprintf(szTemp, L"%d", iCol);
+ }
+ else if (!wcsncmp(p, L"\\line", 5)) { // soft line break;
+ res.AppendChar('\n');
+ }
+ else if (!wcsncmp(p, L"\\endash", 7)) {
+ res.AppendChar(0x2013);
+ }
+ else if (!wcsncmp(p, L"\\emdash", 7)) {
+ res.AppendChar(0x2014);
+ }
+ else if (!wcsncmp(p, L"\\bullet", 7)) {
+ res.AppendChar(0x2022);
+ }
+ else if (!wcsncmp(p, L"\\ldblquote", 10)) {
+ res.AppendChar(0x201C);
+ }
+ else if (!wcsncmp(p, L"\\rdblquote", 10)) {
+ res.AppendChar(0x201D);
+ }
+ else if (!wcsncmp(p, L"\\lquote", 7)) {
+ res.AppendChar(0x2018);
+ }
+ else if (!wcsncmp(p, L"\\rquote", 7)) {
+ res.AppendChar(0x2019);
+ }
+ else if (!wcsncmp(p, L"\\b", 2)) { //bold
+ res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
+ }
+ else if (!wcsncmp(p, L"\\i", 2)) { // italics
+ res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
+ }
+ else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
+ res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
+ }
+ else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
+ if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) {
+ res.Append(L"[u]");
+ bInsideUl = true;
+ }
+ else if (!wcsncmp(p + 3, L"none", 4)) {
+ if (bInsideUl)
+ res.Append(L"[/u]");
+ bInsideUl = false;
+ }
+ }
+ else if (!wcsncmp(p, L"\\tab", 4)) { // tab
+ res.AppendChar('\t');
+ }
+ else if (p[1] == '\'') { // special character
+ if (p[2] != ' ' && p[2] != '\\') {
+ wchar_t tmp[10], *t = tmp;
+ *t++ = p[2];
+ if (p[3] != ' ' && p[3] != '\\')
+ *t++ = p[3];
+ *t = 0;
+
+ // convert string containing char in hex format to int.
+ wchar_t *stoppedHere;
+ res.AppendChar(wcstol(tmp, &stoppedHere, 16));
+ }
+ }
+
+ p++; // skip initial slash
+ p += wcscspn(p, tszRtfBreaks);
+ if (*p == ' ')
+ p++;
+ break;
+
+ case '{': // other RTF control characters
+ case '}':
+ p++;
+ break;
+
+ default: // other text that should not be touched
+ res.AppendChar(*p++);
+ break;
+ }
+ }
+
+ if (bInsideUl)
+ res.Append(L"[/u]");
+
+ pszText = res;
+ return TRUE;
+}
diff --git a/src/mir_app/src/chat_svc.cpp b/src/mir_app/src/chat_svc.cpp index 0043df7ae6..021079ba46 100644 --- a/src/mir_app/src/chat_svc.cpp +++ b/src/mir_app/src/chat_svc.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_tools.cpp b/src/mir_app/src/chat_tools.cpp index 4c2bcc7698..cb962183de 100644 --- a/src/mir_app/src/chat_tools.cpp +++ b/src/mir_app/src/chat_tools.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_ui.cpp b/src/mir_app/src/chat_ui.cpp index 789f6fd075..4c5c21b6cd 100644 --- a/src/mir_app/src/chat_ui.cpp +++ b/src/mir_app/src/chat_ui.cpp @@ -1,220 +1,220 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" - -CMOption<bool> g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true); -CMOption<bool> g_bChatTrayInactive(CHAT_MODULE, "TrayIconInactiveOnly", true); - -///////////////////////////////////////////////////////////////////////////////////////// -// Group chat - Events - -#define NR_GC_EVENTS 12 - -static UINT _eventorder[] = -{ - GC_EVENT_ACTION, - GC_EVENT_MESSAGE, - GC_EVENT_NICK, - GC_EVENT_JOIN, - GC_EVENT_PART, - GC_EVENT_TOPIC, - GC_EVENT_ADDSTATUS, - GC_EVENT_INFORMATION, - GC_EVENT_QUIT, - GC_EVENT_KICK, - GC_EVENT_NOTICE, - GC_EVENT_HIGHLIGHT -}; - -class CChatEventOptionDlg : public CDlgBase -{ - CCtrlCheck chkTray, chkPopup, chkRightClick; - CCtrlMButton btn1, btn2, btn3, btn4; - - void InvertColumn(int ctrlId) - { - int enabled = !IsDlgButtonChecked(m_hwnd, ctrlId); - for (int i = 0; i < _countof(_eventorder); i++) - CheckDlgButton(m_hwnd, ctrlId + i, enabled); - NotifyChange(); - } - -public: - CChatEventOptionDlg() : - CDlgBase(g_plugin, IDD_OPT_CHAT_EVENTS), - chkTray(this, IDC_TRAYONLYFORINACTIVE), - chkPopup(this, IDC_POPUPONLYFORINACTIVE), - chkRightClick(this, IDC_RIGHTCLICK), - btn1(this, IDC_ICON1, SKINICON_OTHER_POPUP, LPGEN("Popup")), - btn2(this, IDC_ICON2, SKINICON_OTHER_MIRANDA, LPGEN("Tray")), - btn3(this, IDC_ICON3, SKINICON_OTHER_SOUND, LPGEN("Sound")), - btn4(this, IDC_ICON4, SKINICON_EVENT_FILE, LPGEN("Log to file")) - { - CreateLink(chkTray, g_bChatTrayInactive); - CreateLink(chkPopup, g_bChatPopupInactive); - CreateLink(chkRightClick, g_chatApi.bRightClickFilter); - - btn1.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Popup); - btn2.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Tray); - btn3.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Sound); - btn4.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Log); - } - - bool OnInitDialog() override - { - btn1.MakeFlat(); btn2.MakeFlat(); btn3.MakeFlat(); btn4.MakeFlat(); - - uint32_t dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL); - uint32_t dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwSoundFlags = db_get_dw(0, CHAT_MODULE, "SoundFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL); - - for (int i = 0; i < _countof(_eventorder); i++) { - if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { - CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - } - CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_S1 + i, dwSoundFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - } - return true; - } - - bool OnApply() override - { - uint32_t dwFilterFlags = 0, dwTrayFlags = 0, dwPopupFlags = 0, dwSoundFlags = 0, dwLogFlags = 0; - - for (int i = 0; i < _countof(_eventorder); i++) { - if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { - dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0); - dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0); - } - dwSoundFlags |= (IsDlgButtonChecked(m_hwnd, IDC_S1 + i) ? _eventorder[i] : 0); - dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0); - dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0); - } - db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags); - db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags); - db_set_dw(0, CHAT_MODULE, "SoundFlags", dwSoundFlags); - db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags); - db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags); - - LoadGlobalSettings(); - return true; - } - - void onClick_Popup(CCtrlButton *) { InvertColumn(IDC_P1); } - void onClick_Sound(CCtrlButton *) { InvertColumn(IDC_S1); } - void onClick_Tray(CCtrlButton *) { InvertColumn(IDC_T1); } - void onClick_Log(CCtrlButton *) { InvertColumn(IDC_L1); } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup options - -class COptPopupDlg : public CDlgBase -{ - CCtrlSpin spinTimeout; - CCtrlCheck chkRadio1, chkRadio2, chkRadio3; - CCtrlColor clrBack, clrText; - -public: - COptPopupDlg() : - CDlgBase(g_plugin, IDD_OPTIONSPOPUP), - clrBack(this, IDC_BKG), - clrText(this, IDC_TEXT), - chkRadio1(this, IDC_RADIO1), - chkRadio2(this, IDC_RADIO2), - chkRadio3(this, IDC_RADIO3), - spinTimeout(this, IDC_SPIN1, 100, -1) - { - chkRadio1.OnChange = chkRadio2.OnChange = chkRadio3.OnChange = Callback(this, &COptPopupDlg::onChange_Radio); - } - - bool OnInitDialog() override - { - clrBack.SetColor(g_Settings->crPUBkgColour); - clrText.SetColor(g_Settings->crPUTextColour); - - if (g_Settings->iPopupStyle == 2) - CheckDlgButton(m_hwnd, IDC_RADIO2, BST_CHECKED); - else if (g_Settings->iPopupStyle == 3) - CheckDlgButton(m_hwnd, IDC_RADIO3, BST_CHECKED); - else - CheckDlgButton(m_hwnd, IDC_RADIO1, BST_CHECKED); - onChange_Radio(0); - - spinTimeout.SetPosition(g_Settings->iPopupTimeout); - return true; - } - - bool OnApply() override - { - if (IsDlgButtonChecked(m_hwnd, IDC_RADIO2) == BST_CHECKED) - g_Settings->iPopupStyle = 2; - else if (IsDlgButtonChecked(m_hwnd, IDC_RADIO3) == BST_CHECKED) - g_Settings->iPopupStyle = 3; - else - g_Settings->iPopupStyle = 1; - db_set_b(0, CHAT_MODULE, "PopupStyle", g_Settings->iPopupStyle); - - db_set_w(0, CHAT_MODULE, "PopupTimeout", g_Settings->iPopupTimeout = spinTimeout.GetPosition()); - db_set_dw(0, CHAT_MODULE, "PopupColorBG", g_Settings->crPUBkgColour = clrBack.GetColor()); - db_set_dw(0, CHAT_MODULE, "PopupColorText", g_Settings->crPUTextColour = clrText.GetColor()); - return true; - } - - void onChange_Radio(CCtrlCheck *) - { - bool bStatus = chkRadio3.GetState(); - clrBack.Enable(bStatus); - clrText.Enable(bStatus); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -void ChatOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Group chats"); - odp.szTab.a = LPGEN("Events and filters"); - odp.pDialog = new CChatEventOptionDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.position = 910000002; - odp.szTitle.a = LPGEN("Group chats"); - odp.szGroup.a = LPGEN("Popups"); - odp.szTab.a = nullptr; - odp.pDialog = new COptPopupDlg(); - g_plugin.addOptions(wParam, &odp); -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "chat.h"
+
+CMOption<bool> g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true);
+CMOption<bool> g_bChatTrayInactive(CHAT_MODULE, "TrayIconInactiveOnly", true);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Group chat - Events
+
+#define NR_GC_EVENTS 12
+
+static UINT _eventorder[] =
+{
+ GC_EVENT_ACTION,
+ GC_EVENT_MESSAGE,
+ GC_EVENT_NICK,
+ GC_EVENT_JOIN,
+ GC_EVENT_PART,
+ GC_EVENT_TOPIC,
+ GC_EVENT_ADDSTATUS,
+ GC_EVENT_INFORMATION,
+ GC_EVENT_QUIT,
+ GC_EVENT_KICK,
+ GC_EVENT_NOTICE,
+ GC_EVENT_HIGHLIGHT
+};
+
+class CChatEventOptionDlg : public CDlgBase
+{
+ CCtrlCheck chkTray, chkPopup, chkRightClick;
+ CCtrlMButton btn1, btn2, btn3, btn4;
+
+ void InvertColumn(int ctrlId)
+ {
+ int enabled = !IsDlgButtonChecked(m_hwnd, ctrlId);
+ for (int i = 0; i < _countof(_eventorder); i++)
+ CheckDlgButton(m_hwnd, ctrlId + i, enabled);
+ NotifyChange();
+ }
+
+public:
+ CChatEventOptionDlg() :
+ CDlgBase(g_plugin, IDD_OPT_CHAT_EVENTS),
+ chkTray(this, IDC_TRAYONLYFORINACTIVE),
+ chkPopup(this, IDC_POPUPONLYFORINACTIVE),
+ chkRightClick(this, IDC_RIGHTCLICK),
+ btn1(this, IDC_ICON1, SKINICON_OTHER_POPUP, LPGEN("Popup")),
+ btn2(this, IDC_ICON2, SKINICON_OTHER_MIRANDA, LPGEN("Tray")),
+ btn3(this, IDC_ICON3, SKINICON_OTHER_SOUND, LPGEN("Sound")),
+ btn4(this, IDC_ICON4, SKINICON_EVENT_FILE, LPGEN("Log to file"))
+ {
+ CreateLink(chkTray, g_bChatTrayInactive);
+ CreateLink(chkPopup, g_bChatPopupInactive);
+ CreateLink(chkRightClick, g_chatApi.bRightClickFilter);
+
+ btn1.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Popup);
+ btn2.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Tray);
+ btn3.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Sound);
+ btn4.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Log);
+ }
+
+ bool OnInitDialog() override
+ {
+ btn1.MakeFlat(); btn2.MakeFlat(); btn3.MakeFlat(); btn4.MakeFlat();
+
+ uint32_t dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL);
+ uint32_t dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT);
+ uint32_t dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT);
+ uint32_t dwSoundFlags = db_get_dw(0, CHAT_MODULE, "SoundFlags", GC_EVENT_HIGHLIGHT);
+ uint32_t dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL);
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ if (_eventorder[i] != GC_EVENT_HIGHLIGHT) {
+ CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ }
+ CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_S1 + i, dwSoundFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ uint32_t dwFilterFlags = 0, dwTrayFlags = 0, dwPopupFlags = 0, dwSoundFlags = 0, dwLogFlags = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ if (_eventorder[i] != GC_EVENT_HIGHLIGHT) {
+ dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0);
+ dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0);
+ }
+ dwSoundFlags |= (IsDlgButtonChecked(m_hwnd, IDC_S1 + i) ? _eventorder[i] : 0);
+ dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0);
+ dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0);
+ }
+ db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags);
+ db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags);
+ db_set_dw(0, CHAT_MODULE, "SoundFlags", dwSoundFlags);
+ db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags);
+ db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags);
+
+ LoadGlobalSettings();
+ return true;
+ }
+
+ void onClick_Popup(CCtrlButton *) { InvertColumn(IDC_P1); }
+ void onClick_Sound(CCtrlButton *) { InvertColumn(IDC_S1); }
+ void onClick_Tray(CCtrlButton *) { InvertColumn(IDC_T1); }
+ void onClick_Log(CCtrlButton *) { InvertColumn(IDC_L1); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup options
+
+class COptPopupDlg : public CDlgBase
+{
+ CCtrlSpin spinTimeout;
+ CCtrlCheck chkRadio1, chkRadio2, chkRadio3;
+ CCtrlColor clrBack, clrText;
+
+public:
+ COptPopupDlg() :
+ CDlgBase(g_plugin, IDD_OPTIONSPOPUP),
+ clrBack(this, IDC_BKG),
+ clrText(this, IDC_TEXT),
+ chkRadio1(this, IDC_RADIO1),
+ chkRadio2(this, IDC_RADIO2),
+ chkRadio3(this, IDC_RADIO3),
+ spinTimeout(this, IDC_SPIN1, 100, -1)
+ {
+ chkRadio1.OnChange = chkRadio2.OnChange = chkRadio3.OnChange = Callback(this, &COptPopupDlg::onChange_Radio);
+ }
+
+ bool OnInitDialog() override
+ {
+ clrBack.SetColor(g_Settings->crPUBkgColour);
+ clrText.SetColor(g_Settings->crPUTextColour);
+
+ if (g_Settings->iPopupStyle == 2)
+ CheckDlgButton(m_hwnd, IDC_RADIO2, BST_CHECKED);
+ else if (g_Settings->iPopupStyle == 3)
+ CheckDlgButton(m_hwnd, IDC_RADIO3, BST_CHECKED);
+ else
+ CheckDlgButton(m_hwnd, IDC_RADIO1, BST_CHECKED);
+ onChange_Radio(0);
+
+ spinTimeout.SetPosition(g_Settings->iPopupTimeout);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (IsDlgButtonChecked(m_hwnd, IDC_RADIO2) == BST_CHECKED)
+ g_Settings->iPopupStyle = 2;
+ else if (IsDlgButtonChecked(m_hwnd, IDC_RADIO3) == BST_CHECKED)
+ g_Settings->iPopupStyle = 3;
+ else
+ g_Settings->iPopupStyle = 1;
+ db_set_b(0, CHAT_MODULE, "PopupStyle", g_Settings->iPopupStyle);
+
+ db_set_w(0, CHAT_MODULE, "PopupTimeout", g_Settings->iPopupTimeout = spinTimeout.GetPosition());
+ db_set_dw(0, CHAT_MODULE, "PopupColorBG", g_Settings->crPUBkgColour = clrBack.GetColor());
+ db_set_dw(0, CHAT_MODULE, "PopupColorText", g_Settings->crPUTextColour = clrText.GetColor());
+ return true;
+ }
+
+ void onChange_Radio(CCtrlCheck *)
+ {
+ bool bStatus = chkRadio3.GetState();
+ clrBack.Enable(bStatus);
+ clrText.Enable(bStatus);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ChatOptionsInit(WPARAM wParam)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = 910000000;
+ odp.szGroup.a = LPGEN("Message sessions");
+ odp.szTitle.a = LPGEN("Group chats");
+ odp.szTab.a = LPGEN("Events and filters");
+ odp.pDialog = new CChatEventOptionDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.position = 910000002;
+ odp.szTitle.a = LPGEN("Group chats");
+ odp.szGroup.a = LPGEN("Popups");
+ odp.szTab.a = nullptr;
+ odp.pDialog = new COptPopupDlg();
+ g_plugin.addOptions(wParam, &odp);
+}
diff --git a/src/mir_app/src/clc.cpp b/src/mir_app/src/clc.cpp index d44673425e..0266b7bcf3 100644 --- a/src/mir_app/src/clc.cpp +++ b/src/mir_app/src/clc.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clc.h b/src/mir_app/src/clc.h index d57ec3b1ab..15eae7d75c 100644 --- a/src/mir_app/src/clc.h +++ b/src/mir_app/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clccontact.cpp b/src/mir_app/src/clccontact.cpp index 3dbf578cd6..06639ba8d0 100644 --- a/src/mir_app/src/clccontact.cpp +++ b/src/mir_app/src/clccontact.cpp @@ -1,72 +1,72 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -extern HANDLE hGroupChangeEvent; - -MIR_APP_DLL(void) Clist_LoadContactTree(void) -{ - bool hideOffline = Clist::HideOffline; - for (auto &hContact : Contacts()) { - int status = Contact::GetStatus(hContact); - if ((!hideOffline || status != ID_STATUS_OFFLINE) && !Contact::IsHidden(hContact)) - Clist_ChangeContactIcon(hContact, g_clistApi.pfnIconFromStatusMode(Proto_GetBaseAccountName(hContact), status, hContact)); - } - Clist_EndRebuild(); -} - -MIR_APP_DLL(int) Clist_ContactChangeGroup(MCONTACT hContact, MGROUP hGroup) -{ - CLISTGROUPCHANGE grpChg = {}; - - if (hGroup == 0) - db_unset(hContact, "CList", "Group"); - else { - grpChg.pszNewName = Clist_GroupGetName(hGroup, nullptr); - db_set_ws(hContact, "CList", "Group", grpChg.pszNewName); - } - - NotifyEventHooks(hGroupChangeEvent, hContact, (LPARAM)&grpChg); - return 0; -} - -int fnSetHideOffline(int iValue) -{ - if (iValue == -1) // invert the current value - iValue = !Clist::HideOffline; - - switch (iValue) { - case 0: - case 1: - Clist::HideOffline = iValue; - break; - - default: - return -1; - } - Clist_LoadContactTree(); - return iValue; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "clc.h"
+
+extern HANDLE hGroupChangeEvent;
+
+MIR_APP_DLL(void) Clist_LoadContactTree(void)
+{
+ bool hideOffline = Clist::HideOffline;
+ for (auto &hContact : Contacts()) {
+ int status = Contact::GetStatus(hContact);
+ if ((!hideOffline || status != ID_STATUS_OFFLINE) && !Contact::IsHidden(hContact))
+ Clist_ChangeContactIcon(hContact, g_clistApi.pfnIconFromStatusMode(Proto_GetBaseAccountName(hContact), status, hContact));
+ }
+ Clist_EndRebuild();
+}
+
+MIR_APP_DLL(int) Clist_ContactChangeGroup(MCONTACT hContact, MGROUP hGroup)
+{
+ CLISTGROUPCHANGE grpChg = {};
+
+ if (hGroup == 0)
+ db_unset(hContact, "CList", "Group");
+ else {
+ grpChg.pszNewName = Clist_GroupGetName(hGroup, nullptr);
+ db_set_ws(hContact, "CList", "Group", grpChg.pszNewName);
+ }
+
+ NotifyEventHooks(hGroupChangeEvent, hContact, (LPARAM)&grpChg);
+ return 0;
+}
+
+int fnSetHideOffline(int iValue)
+{
+ if (iValue == -1) // invert the current value
+ iValue = !Clist::HideOffline;
+
+ switch (iValue) {
+ case 0:
+ case 1:
+ Clist::HideOffline = iValue;
+ break;
+
+ default:
+ return -1;
+ }
+ Clist_LoadContactTree();
+ return iValue;
+}
diff --git a/src/mir_app/src/clcfiledrop.cpp b/src/mir_app/src/clcfiledrop.cpp index 25501d7dac..840a046eae 100644 --- a/src/mir_app/src/clcfiledrop.cpp +++ b/src/mir_app/src/clcfiledrop.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clcidents.cpp b/src/mir_app/src/clcidents.cpp index 98ca64a451..83497cf50d 100644 --- a/src/mir_app/src/clcidents.cpp +++ b/src/mir_app/src/clcidents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clcitems.cpp b/src/mir_app/src/clcitems.cpp index 42c3f37d2f..eef6b57d56 100644 --- a/src/mir_app/src/clcitems.cpp +++ b/src/mir_app/src/clcitems.cpp @@ -1,730 +1,730 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -static ClcCacheEntry nullpce = {}; - -// routines for managing adding/removal of items in the list, including sorting - -ClcContact* fnAddItemToGroup(ClcGroup *group, int iAboveItem) -{ - ClcContact* newItem = g_clistApi.pfnCreateClcContact(); - newItem->type = CLCIT_DIVIDER; - newItem->flags = 0; - newItem->szText[0] = '\0'; - memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage)); - group->cl.insert(newItem, iAboveItem); - return newItem; -} - -ClcGroup* fnAddGroup(HWND hwnd, ClcData *dat, const wchar_t *szName, uint32_t flags, int groupId, int calcTotalMembers) -{ - dat->bNeedsResort = true; - if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS)) - return &dat->list; - - ClcGroup *group = &dat->list; - wchar_t *pNextField = NEWWSTR_ALLOCA(szName); - do { - wchar_t *pBackslash = wcschr(pNextField, '\\'), *pThisField = pNextField; - if (pBackslash == nullptr) { - pNextField = nullptr; - } - else { - *pBackslash = 0; - pNextField = pBackslash + 1; - } - - int i, compareResult = 1; - for (i = 0; i < group->cl.getCount(); i++) { - ClcContact *cc = group->cl[i]; - if (cc->type == CLCIT_CONTACT) - break; - if (cc->type != CLCIT_GROUP) - continue; - compareResult = mir_wstrcmp(pThisField, cc->szText); - if (compareResult == 0) { - if (pNextField == nullptr && flags != (uint32_t)-1) { - cc->groupId = (uint16_t)groupId; - group = cc->group; - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - group->groupId = groupId; - } - else group = cc->group; - break; - } - if (pNextField == nullptr && cc->groupId == 0) - break; - if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && cc->groupId > groupId) - break; - } - - if (compareResult) { // not found - if (groupId == 0) - return nullptr; - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - cc->type = CLCIT_GROUP; - mir_wstrncpy(cc->szText, pThisField, _countof(cc->szText)); - cc->groupId = (uint16_t)(pNextField ? 0 : groupId); - cc->group = new ClcGroup(10); - cc->group->parent = group; - group = cc->group; - - if (flags == (uint32_t)-1 || pNextField != nullptr) { - group->expanded = 0; - group->hideOffline = 0; - } - else { - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - } - group->groupId = pNextField ? 0 : groupId; - group->totalMembers = 0; - if (flags != (uint32_t)-1 && pNextField == nullptr && calcTotalMembers) { - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - for (auto &hContact : Contacts()) { - ClcCacheEntry *cache = Clist_GetCacheEntry(hContact); - if (!mir_wstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden)) - group->totalMembers++; - } - } - } - } while (pNextField); - return group; -} - -void fnFreeContact(ClcContact* p) -{ - if (p->type == CLCIT_GROUP) { - FreeGroup(p->group); - delete p->group; p->group = nullptr; - } - - mir_free(p); -} - -void FreeGroup(ClcGroup *group) -{ - if (!group) - return; - - for (auto &it : group->cl) - g_clistApi.pfnFreeContact(it); - - group->cl.destroy(); -} - -static int iInfoItemUniqueHandle = 0; -ClcContact* fnAddInfoItemToGroup(ClcGroup *group, int flags, const wchar_t *pszText) -{ - int i = 0; - - if (flags & CLCIIF_BELOWCONTACTS) - i = group->cl.getCount(); - else if (flags & CLCIIF_BELOWGROUPS) { - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type == CLCIT_CONTACT) - break; - } - else - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type != CLCIT_INFO) - break; - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle + 1); - if (iInfoItemUniqueHandle == 0) - ++iInfoItemUniqueHandle; - cc->type = CLCIT_INFO; - cc->flags = (uint8_t)flags; - cc->hContact = (MCONTACT)++iInfoItemUniqueHandle; - mir_wstrncpy(cc->szText, pszText, _countof(cc->szText)); - return cc; -} - -ClcContact* fnAddContactToGroup(ClcData *dat, ClcGroup *group, MCONTACT hContact) -{ - int i, index = -1; - - dat->bNeedsResort = true; - for (i = group->cl.getCount() - 1; i >= 0; i--) { - ClcContact *cc = group->cl[i]; - if (cc->hContact == hContact) - return cc; - - if (index == -1) - if (cc->type != CLCIT_INFO || !(cc->flags & CLCIIF_BELOWCONTACTS)) - index = i; - } - - char *szProto = Proto_GetBaseAccountName(hContact); - - ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); - replaceStrW(pce->tszGroup, nullptr); - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, index + 1); - cc->type = CLCIT_CONTACT; - cc->iImage = Clist_GetContactIcon(hContact); - cc->hContact = hContact; - cc->pce = pce; - if (szProto != nullptr && !Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) - cc->flags |= CONTACTF_ONLINE; - uint16_t apparentMode = szProto != nullptr ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0; - if (apparentMode == ID_STATUS_OFFLINE) - cc->flags |= CONTACTF_INVISTO; - else if (apparentMode == ID_STATUS_ONLINE) - cc->flags |= CONTACTF_VISTO; - else if (apparentMode) - cc->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; - if (!Contact::OnList(hContact)) - cc->flags |= CONTACTF_NOTONLIST; - uint32_t idleMode = szProto != nullptr ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0; - if (idleMode) - cc->flags |= CONTACTF_IDLE; - mir_wstrncpy(cc->szText, Clist_GetContactDisplayName(hContact), _countof(cc->szText)); - return cc; -} - -void fnAddContactToTree(HWND hwnd, ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline) -{ - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - uint16_t status = ID_STATUS_OFFLINE; - char *szProto = Proto_GetBaseAccountName(hContact); - - dat->bNeedsResort = true; - if (style & CLS_NOHIDEOFFLINE) - checkHideOffline = 0; - if (checkHideOffline) - if (szProto != nullptr) - status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); - - int i; - uint32_t groupFlags; - ClcGroup *group; - ptrW tszGroup(Clist_GetGroup(hContact)); - if (tszGroup == nullptr) - group = &dat->list; - else { - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, (uint32_t)-1, 0, 0); - if (group == nullptr) { - if (!(style & CLS_HIDEEMPTYGROUPS)) - return; - - if (checkHideOffline && Clist_IsHiddenMode(dat, status)) { - for (i = 1;; i++) { - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - return; - - if (!mir_wstrcmp(szGroupName, tszGroup)) - break; - } - if (groupFlags & GROUPF_HIDEOFFLINE) - return; - } - for (i = 1;; i++) { - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - return; - - if (!mir_wstrcmp(szGroupName, tszGroup)) - break; - - size_t len = mir_wstrlen(szGroupName); - if (!wcsncmp(szGroupName, tszGroup, len) && tszGroup[len] == '\\') - g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1); - } - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, groupFlags, i, 1); - } - } - - if (checkHideOffline) { - if (Clist_IsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - if (updateTotalCount) - group->totalMembers++; - return; - } - } - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - if (updateTotalCount) - group->totalMembers++; -} - -MIR_APP_DLL(ClcGroup*) Clist_RemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) -{ - int iContact = group->cl.indexOf(contact); - if (iContact == -1) - return group; - - if (contact->type == CLCIT_CONTACT) { - if (updateTotalCount) - group->totalMembers--; - - g_clistApi.pfnInvalidateDisplayNameCacheEntry(contact->hContact); - } - - g_clistApi.pfnFreeContact(group->cl[iContact]); - group->cl.remove(iContact); - - if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.getCount() == 0 && group->parent != nullptr) - for (auto &cc : group->parent->cl) - if (cc->type == CLCIT_GROUP && cc->groupId == group->groupId) - return Clist_RemoveItemFromGroup(hwnd, group->parent, cc, 0); - - return group; -} - -MIR_APP_DLL(void) Clist_DeleteItemFromTree(HWND hwnd, MCONTACT hItem) -{ - ClcData *dat = (ClcData*)GetWindowLongPtr(hwnd, 0); - dat->bNeedsResort = true; - - // if a contact is found in our contact list, remove it from its group and detach from cache - ClcGroup *group; - ClcContact *contact; - if (Clist_FindItem(hwnd, dat, hItem, &contact, &group)) { - Clist_RemoveItemFromGroup(hwnd, group, contact, 1); - contact->pce = &nullpce; - return; - } - - // if we don't have this contact, simply try to update the number of contacts in a group - if (!IsHContactContact(hItem)) - return; - - ptrW wszGroup(Clist_GetGroup(hItem)); - if (wszGroup == nullptr) - return; - - // decrease member counts of all parent groups too - group = &dat->list; - int nameOffset = 0; - for (int i = 0;; i++) { - if (group->scanIndex == group->cl.getCount()) - break; - - ClcContact *cc = group->cl[i]; - if (cc->type == CLCIT_GROUP) { - size_t len = mir_wstrlen(cc->szText); - if (!wcsncmp(cc->szText, wszGroup.get() + nameOffset, len) && (wszGroup[nameOffset + len] == '\\' || wszGroup[nameOffset + len] == '\0')) { - group->totalMembers--; - if (wszGroup[nameOffset + len] == '\0') - break; - } - } - } -} - -int fnGetContactHiddenStatus(MCONTACT hContact, char*, ClcData*) -{ - return Contact::IsHidden(hContact); -} - -void fnRebuildEntireList(HWND hwnd, ClcData *dat) -{ - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - - dat->list.expanded = 1; - dat->list.hideOffline = db_get_b(0, "CLC", "HideOfflineRoot", 0) && (style & CLS_USEGROUPS); - dat->list.cl.destroy(); - dat->list.totalMembers = 0; - dat->selection = -1; - - for (int i = 1;; i++) { - uint32_t groupFlags; - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - break; - g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0); - } - - for (auto &hContact : Contacts()) { - int nHiddenStatus = g_clistApi.pfnGetContactHiddenStatus(hContact, nullptr, dat); - if (((style & CLS_SHOWHIDDEN) && nHiddenStatus != -1) || !nHiddenStatus) { - ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); - if (pce->szProto == nullptr) - continue; - - ClcGroup *group; - ptrW tszGroupName(Clist_GetGroup(hContact)); - if (tszGroupName == nullptr) - group = &dat->list; - else { - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroupName, (uint32_t)-1, 0, 0); - if (group == nullptr && style & CLS_SHOWHIDDEN) - group = &dat->list; - } - - if (group != nullptr) { - group->totalMembers++; - - if (dat->bFilterSearch && dat->szQuickSearch[0] != '\0') { - wchar_t *name = Clist_GetContactDisplayName(hContact); - wchar_t *lowered_name = CharLowerW(NEWWSTR_ALLOCA(name)); - wchar_t *lowered_search = CharLowerW(NEWWSTR_ALLOCA(dat->szQuickSearch)); - - if (wcsstr(lowered_name, lowered_search)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto == nullptr) { - if (!Clist_IsHiddenMode(dat, ID_STATUS_OFFLINE) || g_clistApi.pfnIsVisibleContact(pce, group)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else if (!Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)) || g_clistApi.pfnIsVisibleContact(pce, group)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - } - } - - if (style & CLS_HIDEEMPTYGROUPS) { - ClcGroup *group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - if (cc->group->cl.getCount() == 0) { - group = Clist_RemoveItemFromGroup(hwnd, group, cc, 0); - } - else { - group = cc->group; - group->scanIndex = 0; - } - continue; - } - group->scanIndex++; - } - } - - g_clistApi.pfnSortCLC(hwnd, dat, 0); - ExtraIcon_SetAll(); -} - -int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly) -{ - int count = group->cl.getCount(); - ClcGroup *topgroup = group; - - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if (group == topgroup) - break; - group = group->parent; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP && (!visibleOnly || cc->group->expanded)) { - group = cc->group; - group->scanIndex = 0; - count += group->cl.getCount(); - continue; - } - group->scanIndex++; - } - return count; -} - -static int __cdecl GroupSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - return mir_wstrcmpi(contact1[0]->szText, contact2[0]->szText); -} - -static int __cdecl ContactSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - int result = g_clistApi.pfnCompareContacts(contact1[0], contact2[0]); - if (result) - return result; - //nothing to distinguish them, so make sure they stay in the same order - return (int)((INT_PTR)contact2[0]->hContact - (INT_PTR)contact1[0]->hContact); -} - -static void InsertionSort(ClcContact **pContactArray, int nArray, int(*CompareProc) (const void *, const void *)) -{ - int i, j; - ClcContact* testElement; - - for (i = 1; i < nArray; i++) { - if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) { - testElement = pContactArray[i]; - for (j = i - 2; j >= 0; j--) - if (CompareProc(&pContactArray[j], &testElement) <= 0) - break; - j++; - memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j)); - pContactArray[j] = testElement; - } - } -} - -static void SortGroup(ClcData *dat, ClcGroup *group, int useInsertionSort) -{ - int i, sortCount; - - for (i = group->cl.getCount() - 1; i >= 0; i--) { - if (group->cl[i]->type == CLCIT_DIVIDER) { - mir_free(group->cl[i]); - group->cl.remove(i); - } - } - - for (i = 0; i < group->cl.getCount(); i++) - if (group->cl[i]->type != CLCIT_INFO) - break; - if (i > group->cl.getCount() - 2) - return; - if (group->cl[i]->type == CLCIT_GROUP) { - if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) { - for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) - if (group->cl[i + sortCount]->type != CLCIT_GROUP) - break; - qsort(group->cl.getArray() + i, sortCount, sizeof(void*), GroupSortProc); - i = i + sortCount; - } - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type == CLCIT_CONTACT) - break; - if (group->cl.getCount() - i < 2) - return; - } - for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) - if (group->cl[i + sortCount]->type != CLCIT_CONTACT) - break; - if (useInsertionSort) - InsertionSort(group->cl.getArray() + i, sortCount, ContactSortProc); - else - qsort(group->cl.getArray() + i, sortCount, sizeof(void*), ContactSortProc); - if (dat->exStyle & CLS_EX_DIVIDERONOFF) { - int prevContactOnline = 0; - for (i = 0; i < group->cl.getCount(); i++) { - if (group->cl[i]->type != CLCIT_CONTACT) - continue; - if (group->cl[i]->flags & CONTACTF_ONLINE) - prevContactOnline = 1; - else { - if (prevContactOnline) { - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - cc->type = CLCIT_DIVIDER; - mir_wstrcpy(cc->szText, TranslateT("Offline")); - } - break; - } - } - } -} - -void fnSortCLC(HWND hwnd, ClcData *dat, int useInsertionSort) -{ - ClcGroup *group = &dat->list; - - if (dat->bNeedsResort) { - MCONTACT hSelItem; - ClcContact *selcontact; - if (g_clistApi.pfnGetRowByIndex(dat, dat->selection, &selcontact, nullptr) == -1) - hSelItem = 0; - else - hSelItem = Clist_ContactToHItem(selcontact); - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - continue; - } - group->scanIndex++; - } - - if (hSelItem) { - ClcGroup *selgroup; - if (Clist_FindItem(hwnd, dat, hSelItem, &selcontact, &selgroup)) - dat->selection = g_clistApi.pfnGetRowsPriorTo(&dat->list, selgroup, selgroup->cl.indexOf(selcontact)); - } - - g_clistApi.pfnRecalcScrollBar(hwnd, dat); - } - dat->bNeedsResort = false; - g_clistApi.pfnInvalidateRect(hwnd, nullptr, FALSE); -} - -struct SavedContactState_t -{ - MCONTACT hContact; - uint16_t iExtraImage[EXTRA_ICON_COUNT]; - int checked; -}; - -struct SavedGroupState_t -{ - int groupId, expanded; -}; - -struct SavedInfoState_t -{ - int parentId; - ClcContact contact; -}; - -MIR_APP_DLL(void) Clist_SaveStateAndRebuildList(HWND hwnd, ClcData *dat) -{ - Clist_HideInfoTip(dat); - KillTimer(hwnd, TIMERID_INFOTIP); - KillTimer(hwnd, TIMERID_RENAME); - Clist_EndRename(dat, 1); - - dat->bLockScrollbar = true; - - OBJLIST<SavedContactState_t> saveContact(10, NumericKeySortT); - OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT); - OBJLIST<SavedInfoState_t> saveInfo(10, NumericKeySortT); - - dat->bNeedsResort = true; - ClcGroup *group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - - SavedGroupState_t *p = new SavedGroupState_t; - p->groupId = group->groupId; - p->expanded = group->expanded; - saveGroup.insert(p); - continue; - } - else if (cc->type == CLCIT_CONTACT) { - SavedContactState_t *p = new SavedContactState_t; - p->hContact = cc->hContact; - memcpy(p->iExtraImage, cc->iExtraImage, sizeof(p->iExtraImage)); - p->checked = cc->flags & CONTACTF_CHECKED; - saveContact.insert(p); - } - else if (cc->type == CLCIT_INFO) { - SavedInfoState_t *p = new SavedInfoState_t; - p->parentId = (group->parent == nullptr) ? -1 : group->groupId; - p->contact = *cc; - saveInfo.insert(p); - } - group->scanIndex++; - } - - FreeGroup(&dat->list); - g_clistApi.pfnRebuildEntireList(hwnd, dat); - - group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - - SavedGroupState_t tmp, *p; - tmp.groupId = group->groupId; - if ((p = saveGroup.find(&tmp)) != nullptr) - group->expanded = p->expanded; - continue; - } - else if (cc->type == CLCIT_CONTACT) { - SavedContactState_t tmp, *p; - tmp.hContact = cc->hContact; - if ((p = saveContact.find(&tmp)) != nullptr) { - memcpy(cc->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage)); - if (p->checked) - cc->flags |= CONTACTF_CHECKED; - } - } - - group->scanIndex++; - } - - for (auto &it : saveInfo) { - if (it->parentId == -1) - group = &dat->list; - else { - ClcContact *contact; - if (!Clist_FindItem(hwnd, dat, it->parentId | HCONTACT_ISGROUP, &contact)) - continue; - group = contact->group; - } - - ClcContact *cc = g_clistApi.pfnAddInfoItemToGroup(group, it->contact.flags, L""); - *cc = it->contact; - } - - dat->bLockScrollbar = false; - Clist_RecalculateGroupCheckboxes(dat); - - g_clistApi.pfnRecalcScrollBar(hwnd, dat); - - NMCLISTCONTROL nm; - nm.hdr.code = CLN_LISTREBUILT; - nm.hdr.hwndFrom = hwnd; - nm.hdr.idFrom = GetDlgCtrlID(hwnd); - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "clc.h"
+
+static ClcCacheEntry nullpce = {};
+
+// routines for managing adding/removal of items in the list, including sorting
+
+ClcContact* fnAddItemToGroup(ClcGroup *group, int iAboveItem)
+{
+ ClcContact* newItem = g_clistApi.pfnCreateClcContact();
+ newItem->type = CLCIT_DIVIDER;
+ newItem->flags = 0;
+ newItem->szText[0] = '\0';
+ memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage));
+ group->cl.insert(newItem, iAboveItem);
+ return newItem;
+}
+
+ClcGroup* fnAddGroup(HWND hwnd, ClcData *dat, const wchar_t *szName, uint32_t flags, int groupId, int calcTotalMembers)
+{
+ dat->bNeedsResort = true;
+ if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS))
+ return &dat->list;
+
+ ClcGroup *group = &dat->list;
+ wchar_t *pNextField = NEWWSTR_ALLOCA(szName);
+ do {
+ wchar_t *pBackslash = wcschr(pNextField, '\\'), *pThisField = pNextField;
+ if (pBackslash == nullptr) {
+ pNextField = nullptr;
+ }
+ else {
+ *pBackslash = 0;
+ pNextField = pBackslash + 1;
+ }
+
+ int i, compareResult = 1;
+ for (i = 0; i < group->cl.getCount(); i++) {
+ ClcContact *cc = group->cl[i];
+ if (cc->type == CLCIT_CONTACT)
+ break;
+ if (cc->type != CLCIT_GROUP)
+ continue;
+ compareResult = mir_wstrcmp(pThisField, cc->szText);
+ if (compareResult == 0) {
+ if (pNextField == nullptr && flags != (uint32_t)-1) {
+ cc->groupId = (uint16_t)groupId;
+ group = cc->group;
+ group->expanded = (flags & GROUPF_EXPANDED) != 0;
+ group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0;
+ group->groupId = groupId;
+ }
+ else group = cc->group;
+ break;
+ }
+ if (pNextField == nullptr && cc->groupId == 0)
+ break;
+ if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && cc->groupId > groupId)
+ break;
+ }
+
+ if (compareResult) { // not found
+ if (groupId == 0)
+ return nullptr;
+
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i);
+ cc->type = CLCIT_GROUP;
+ mir_wstrncpy(cc->szText, pThisField, _countof(cc->szText));
+ cc->groupId = (uint16_t)(pNextField ? 0 : groupId);
+ cc->group = new ClcGroup(10);
+ cc->group->parent = group;
+ group = cc->group;
+
+ if (flags == (uint32_t)-1 || pNextField != nullptr) {
+ group->expanded = 0;
+ group->hideOffline = 0;
+ }
+ else {
+ group->expanded = (flags & GROUPF_EXPANDED) != 0;
+ group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0;
+ }
+ group->groupId = pNextField ? 0 : groupId;
+ group->totalMembers = 0;
+ if (flags != (uint32_t)-1 && pNextField == nullptr && calcTotalMembers) {
+ uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ for (auto &hContact : Contacts()) {
+ ClcCacheEntry *cache = Clist_GetCacheEntry(hContact);
+ if (!mir_wstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden))
+ group->totalMembers++;
+ }
+ }
+ }
+ } while (pNextField);
+ return group;
+}
+
+void fnFreeContact(ClcContact* p)
+{
+ if (p->type == CLCIT_GROUP) {
+ FreeGroup(p->group);
+ delete p->group; p->group = nullptr;
+ }
+
+ mir_free(p);
+}
+
+void FreeGroup(ClcGroup *group)
+{
+ if (!group)
+ return;
+
+ for (auto &it : group->cl)
+ g_clistApi.pfnFreeContact(it);
+
+ group->cl.destroy();
+}
+
+static int iInfoItemUniqueHandle = 0;
+ClcContact* fnAddInfoItemToGroup(ClcGroup *group, int flags, const wchar_t *pszText)
+{
+ int i = 0;
+
+ if (flags & CLCIIF_BELOWCONTACTS)
+ i = group->cl.getCount();
+ else if (flags & CLCIIF_BELOWGROUPS) {
+ for (; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type == CLCIT_CONTACT)
+ break;
+ }
+ else
+ for (; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type != CLCIT_INFO)
+ break;
+
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i);
+ iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle + 1);
+ if (iInfoItemUniqueHandle == 0)
+ ++iInfoItemUniqueHandle;
+ cc->type = CLCIT_INFO;
+ cc->flags = (uint8_t)flags;
+ cc->hContact = (MCONTACT)++iInfoItemUniqueHandle;
+ mir_wstrncpy(cc->szText, pszText, _countof(cc->szText));
+ return cc;
+}
+
+ClcContact* fnAddContactToGroup(ClcData *dat, ClcGroup *group, MCONTACT hContact)
+{
+ int i, index = -1;
+
+ dat->bNeedsResort = true;
+ for (i = group->cl.getCount() - 1; i >= 0; i--) {
+ ClcContact *cc = group->cl[i];
+ if (cc->hContact == hContact)
+ return cc;
+
+ if (index == -1)
+ if (cc->type != CLCIT_INFO || !(cc->flags & CLCIIF_BELOWCONTACTS))
+ index = i;
+ }
+
+ char *szProto = Proto_GetBaseAccountName(hContact);
+
+ ClcCacheEntry *pce = Clist_GetCacheEntry(hContact);
+ replaceStrW(pce->tszGroup, nullptr);
+
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, index + 1);
+ cc->type = CLCIT_CONTACT;
+ cc->iImage = Clist_GetContactIcon(hContact);
+ cc->hContact = hContact;
+ cc->pce = pce;
+ if (szProto != nullptr && !Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)))
+ cc->flags |= CONTACTF_ONLINE;
+ uint16_t apparentMode = szProto != nullptr ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ cc->flags |= CONTACTF_INVISTO;
+ else if (apparentMode == ID_STATUS_ONLINE)
+ cc->flags |= CONTACTF_VISTO;
+ else if (apparentMode)
+ cc->flags |= CONTACTF_VISTO | CONTACTF_INVISTO;
+ if (!Contact::OnList(hContact))
+ cc->flags |= CONTACTF_NOTONLIST;
+ uint32_t idleMode = szProto != nullptr ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0;
+ if (idleMode)
+ cc->flags |= CONTACTF_IDLE;
+ mir_wstrncpy(cc->szText, Clist_GetContactDisplayName(hContact), _countof(cc->szText));
+ return cc;
+}
+
+void fnAddContactToTree(HWND hwnd, ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline)
+{
+ uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ uint16_t status = ID_STATUS_OFFLINE;
+ char *szProto = Proto_GetBaseAccountName(hContact);
+
+ dat->bNeedsResort = true;
+ if (style & CLS_NOHIDEOFFLINE)
+ checkHideOffline = 0;
+ if (checkHideOffline)
+ if (szProto != nullptr)
+ status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE);
+
+ int i;
+ uint32_t groupFlags;
+ ClcGroup *group;
+ ptrW tszGroup(Clist_GetGroup(hContact));
+ if (tszGroup == nullptr)
+ group = &dat->list;
+ else {
+ group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, (uint32_t)-1, 0, 0);
+ if (group == nullptr) {
+ if (!(style & CLS_HIDEEMPTYGROUPS))
+ return;
+
+ if (checkHideOffline && Clist_IsHiddenMode(dat, status)) {
+ for (i = 1;; i++) {
+ wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags);
+ if (szGroupName == nullptr)
+ return;
+
+ if (!mir_wstrcmp(szGroupName, tszGroup))
+ break;
+ }
+ if (groupFlags & GROUPF_HIDEOFFLINE)
+ return;
+ }
+ for (i = 1;; i++) {
+ wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags);
+ if (szGroupName == nullptr)
+ return;
+
+ if (!mir_wstrcmp(szGroupName, tszGroup))
+ break;
+
+ size_t len = mir_wstrlen(szGroupName);
+ if (!wcsncmp(szGroupName, tszGroup, len) && tszGroup[len] == '\\')
+ g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1);
+ }
+ group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, groupFlags, i, 1);
+ }
+ }
+
+ if (checkHideOffline) {
+ if (Clist_IsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ if (updateTotalCount)
+ group->totalMembers++;
+ return;
+ }
+ }
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ if (updateTotalCount)
+ group->totalMembers++;
+}
+
+MIR_APP_DLL(ClcGroup*) Clist_RemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount)
+{
+ int iContact = group->cl.indexOf(contact);
+ if (iContact == -1)
+ return group;
+
+ if (contact->type == CLCIT_CONTACT) {
+ if (updateTotalCount)
+ group->totalMembers--;
+
+ g_clistApi.pfnInvalidateDisplayNameCacheEntry(contact->hContact);
+ }
+
+ g_clistApi.pfnFreeContact(group->cl[iContact]);
+ group->cl.remove(iContact);
+
+ if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.getCount() == 0 && group->parent != nullptr)
+ for (auto &cc : group->parent->cl)
+ if (cc->type == CLCIT_GROUP && cc->groupId == group->groupId)
+ return Clist_RemoveItemFromGroup(hwnd, group->parent, cc, 0);
+
+ return group;
+}
+
+MIR_APP_DLL(void) Clist_DeleteItemFromTree(HWND hwnd, MCONTACT hItem)
+{
+ ClcData *dat = (ClcData*)GetWindowLongPtr(hwnd, 0);
+ dat->bNeedsResort = true;
+
+ // if a contact is found in our contact list, remove it from its group and detach from cache
+ ClcGroup *group;
+ ClcContact *contact;
+ if (Clist_FindItem(hwnd, dat, hItem, &contact, &group)) {
+ Clist_RemoveItemFromGroup(hwnd, group, contact, 1);
+ contact->pce = &nullpce;
+ return;
+ }
+
+ // if we don't have this contact, simply try to update the number of contacts in a group
+ if (!IsHContactContact(hItem))
+ return;
+
+ ptrW wszGroup(Clist_GetGroup(hItem));
+ if (wszGroup == nullptr)
+ return;
+
+ // decrease member counts of all parent groups too
+ group = &dat->list;
+ int nameOffset = 0;
+ for (int i = 0;; i++) {
+ if (group->scanIndex == group->cl.getCount())
+ break;
+
+ ClcContact *cc = group->cl[i];
+ if (cc->type == CLCIT_GROUP) {
+ size_t len = mir_wstrlen(cc->szText);
+ if (!wcsncmp(cc->szText, wszGroup.get() + nameOffset, len) && (wszGroup[nameOffset + len] == '\\' || wszGroup[nameOffset + len] == '\0')) {
+ group->totalMembers--;
+ if (wszGroup[nameOffset + len] == '\0')
+ break;
+ }
+ }
+ }
+}
+
+int fnGetContactHiddenStatus(MCONTACT hContact, char*, ClcData*)
+{
+ return Contact::IsHidden(hContact);
+}
+
+void fnRebuildEntireList(HWND hwnd, ClcData *dat)
+{
+ uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE);
+
+ dat->list.expanded = 1;
+ dat->list.hideOffline = db_get_b(0, "CLC", "HideOfflineRoot", 0) && (style & CLS_USEGROUPS);
+ dat->list.cl.destroy();
+ dat->list.totalMembers = 0;
+ dat->selection = -1;
+
+ for (int i = 1;; i++) {
+ uint32_t groupFlags;
+ wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags);
+ if (szGroupName == nullptr)
+ break;
+ g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0);
+ }
+
+ for (auto &hContact : Contacts()) {
+ int nHiddenStatus = g_clistApi.pfnGetContactHiddenStatus(hContact, nullptr, dat);
+ if (((style & CLS_SHOWHIDDEN) && nHiddenStatus != -1) || !nHiddenStatus) {
+ ClcCacheEntry *pce = Clist_GetCacheEntry(hContact);
+ if (pce->szProto == nullptr)
+ continue;
+
+ ClcGroup *group;
+ ptrW tszGroupName(Clist_GetGroup(hContact));
+ if (tszGroupName == nullptr)
+ group = &dat->list;
+ else {
+ group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroupName, (uint32_t)-1, 0, 0);
+ if (group == nullptr && style & CLS_SHOWHIDDEN)
+ group = &dat->list;
+ }
+
+ if (group != nullptr) {
+ group->totalMembers++;
+
+ if (dat->bFilterSearch && dat->szQuickSearch[0] != '\0') {
+ wchar_t *name = Clist_GetContactDisplayName(hContact);
+ wchar_t *lowered_name = CharLowerW(NEWWSTR_ALLOCA(name));
+ wchar_t *lowered_search = CharLowerW(NEWWSTR_ALLOCA(dat->szQuickSearch));
+
+ if (wcsstr(lowered_name, lowered_search))
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr) {
+ if (!Clist_IsHiddenMode(dat, ID_STATUS_OFFLINE) || g_clistApi.pfnIsVisibleContact(pce, group))
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else if (!Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)) || g_clistApi.pfnIsVisibleContact(pce, group))
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ }
+ }
+
+ if (style & CLS_HIDEEMPTYGROUPS) {
+ ClcGroup *group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ if (cc->group->cl.getCount() == 0) {
+ group = Clist_RemoveItemFromGroup(hwnd, group, cc, 0);
+ }
+ else {
+ group = cc->group;
+ group->scanIndex = 0;
+ }
+ continue;
+ }
+ group->scanIndex++;
+ }
+ }
+
+ g_clistApi.pfnSortCLC(hwnd, dat, 0);
+ ExtraIcon_SetAll();
+}
+
+int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly)
+{
+ int count = group->cl.getCount();
+ ClcGroup *topgroup = group;
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if (group == topgroup)
+ break;
+ group = group->parent;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP && (!visibleOnly || cc->group->expanded)) {
+ group = cc->group;
+ group->scanIndex = 0;
+ count += group->cl.getCount();
+ continue;
+ }
+ group->scanIndex++;
+ }
+ return count;
+}
+
+static int __cdecl GroupSortProc(const void* p1, const void* p2)
+{
+ ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2;
+
+ return mir_wstrcmpi(contact1[0]->szText, contact2[0]->szText);
+}
+
+static int __cdecl ContactSortProc(const void* p1, const void* p2)
+{
+ ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2;
+
+ int result = g_clistApi.pfnCompareContacts(contact1[0], contact2[0]);
+ if (result)
+ return result;
+ //nothing to distinguish them, so make sure they stay in the same order
+ return (int)((INT_PTR)contact2[0]->hContact - (INT_PTR)contact1[0]->hContact);
+}
+
+static void InsertionSort(ClcContact **pContactArray, int nArray, int(*CompareProc) (const void *, const void *))
+{
+ int i, j;
+ ClcContact* testElement;
+
+ for (i = 1; i < nArray; i++) {
+ if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) {
+ testElement = pContactArray[i];
+ for (j = i - 2; j >= 0; j--)
+ if (CompareProc(&pContactArray[j], &testElement) <= 0)
+ break;
+ j++;
+ memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j));
+ pContactArray[j] = testElement;
+ }
+ }
+}
+
+static void SortGroup(ClcData *dat, ClcGroup *group, int useInsertionSort)
+{
+ int i, sortCount;
+
+ for (i = group->cl.getCount() - 1; i >= 0; i--) {
+ if (group->cl[i]->type == CLCIT_DIVIDER) {
+ mir_free(group->cl[i]);
+ group->cl.remove(i);
+ }
+ }
+
+ for (i = 0; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type != CLCIT_INFO)
+ break;
+ if (i > group->cl.getCount() - 2)
+ return;
+ if (group->cl[i]->type == CLCIT_GROUP) {
+ if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) {
+ for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++)
+ if (group->cl[i + sortCount]->type != CLCIT_GROUP)
+ break;
+ qsort(group->cl.getArray() + i, sortCount, sizeof(void*), GroupSortProc);
+ i = i + sortCount;
+ }
+ for (; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type == CLCIT_CONTACT)
+ break;
+ if (group->cl.getCount() - i < 2)
+ return;
+ }
+ for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++)
+ if (group->cl[i + sortCount]->type != CLCIT_CONTACT)
+ break;
+ if (useInsertionSort)
+ InsertionSort(group->cl.getArray() + i, sortCount, ContactSortProc);
+ else
+ qsort(group->cl.getArray() + i, sortCount, sizeof(void*), ContactSortProc);
+ if (dat->exStyle & CLS_EX_DIVIDERONOFF) {
+ int prevContactOnline = 0;
+ for (i = 0; i < group->cl.getCount(); i++) {
+ if (group->cl[i]->type != CLCIT_CONTACT)
+ continue;
+ if (group->cl[i]->flags & CONTACTF_ONLINE)
+ prevContactOnline = 1;
+ else {
+ if (prevContactOnline) {
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i);
+ cc->type = CLCIT_DIVIDER;
+ mir_wstrcpy(cc->szText, TranslateT("Offline"));
+ }
+ break;
+ }
+ }
+ }
+}
+
+void fnSortCLC(HWND hwnd, ClcData *dat, int useInsertionSort)
+{
+ ClcGroup *group = &dat->list;
+
+ if (dat->bNeedsResort) {
+ MCONTACT hSelItem;
+ ClcContact *selcontact;
+ if (g_clistApi.pfnGetRowByIndex(dat, dat->selection, &selcontact, nullptr) == -1)
+ hSelItem = 0;
+ else
+ hSelItem = Clist_ContactToHItem(selcontact);
+ group->scanIndex = 0;
+ SortGroup(dat, group, useInsertionSort);
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ group = cc->group;
+ group->scanIndex = 0;
+ SortGroup(dat, group, useInsertionSort);
+ continue;
+ }
+ group->scanIndex++;
+ }
+
+ if (hSelItem) {
+ ClcGroup *selgroup;
+ if (Clist_FindItem(hwnd, dat, hSelItem, &selcontact, &selgroup))
+ dat->selection = g_clistApi.pfnGetRowsPriorTo(&dat->list, selgroup, selgroup->cl.indexOf(selcontact));
+ }
+
+ g_clistApi.pfnRecalcScrollBar(hwnd, dat);
+ }
+ dat->bNeedsResort = false;
+ g_clistApi.pfnInvalidateRect(hwnd, nullptr, FALSE);
+}
+
+struct SavedContactState_t
+{
+ MCONTACT hContact;
+ uint16_t iExtraImage[EXTRA_ICON_COUNT];
+ int checked;
+};
+
+struct SavedGroupState_t
+{
+ int groupId, expanded;
+};
+
+struct SavedInfoState_t
+{
+ int parentId;
+ ClcContact contact;
+};
+
+MIR_APP_DLL(void) Clist_SaveStateAndRebuildList(HWND hwnd, ClcData *dat)
+{
+ Clist_HideInfoTip(dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ Clist_EndRename(dat, 1);
+
+ dat->bLockScrollbar = true;
+
+ OBJLIST<SavedContactState_t> saveContact(10, NumericKeySortT);
+ OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT);
+ OBJLIST<SavedInfoState_t> saveInfo(10, NumericKeySortT);
+
+ dat->bNeedsResort = true;
+ ClcGroup *group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ group = cc->group;
+ group->scanIndex = 0;
+
+ SavedGroupState_t *p = new SavedGroupState_t;
+ p->groupId = group->groupId;
+ p->expanded = group->expanded;
+ saveGroup.insert(p);
+ continue;
+ }
+ else if (cc->type == CLCIT_CONTACT) {
+ SavedContactState_t *p = new SavedContactState_t;
+ p->hContact = cc->hContact;
+ memcpy(p->iExtraImage, cc->iExtraImage, sizeof(p->iExtraImage));
+ p->checked = cc->flags & CONTACTF_CHECKED;
+ saveContact.insert(p);
+ }
+ else if (cc->type == CLCIT_INFO) {
+ SavedInfoState_t *p = new SavedInfoState_t;
+ p->parentId = (group->parent == nullptr) ? -1 : group->groupId;
+ p->contact = *cc;
+ saveInfo.insert(p);
+ }
+ group->scanIndex++;
+ }
+
+ FreeGroup(&dat->list);
+ g_clistApi.pfnRebuildEntireList(hwnd, dat);
+
+ group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ group = cc->group;
+ group->scanIndex = 0;
+
+ SavedGroupState_t tmp, *p;
+ tmp.groupId = group->groupId;
+ if ((p = saveGroup.find(&tmp)) != nullptr)
+ group->expanded = p->expanded;
+ continue;
+ }
+ else if (cc->type == CLCIT_CONTACT) {
+ SavedContactState_t tmp, *p;
+ tmp.hContact = cc->hContact;
+ if ((p = saveContact.find(&tmp)) != nullptr) {
+ memcpy(cc->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage));
+ if (p->checked)
+ cc->flags |= CONTACTF_CHECKED;
+ }
+ }
+
+ group->scanIndex++;
+ }
+
+ for (auto &it : saveInfo) {
+ if (it->parentId == -1)
+ group = &dat->list;
+ else {
+ ClcContact *contact;
+ if (!Clist_FindItem(hwnd, dat, it->parentId | HCONTACT_ISGROUP, &contact))
+ continue;
+ group = contact->group;
+ }
+
+ ClcContact *cc = g_clistApi.pfnAddInfoItemToGroup(group, it->contact.flags, L"");
+ *cc = it->contact;
+ }
+
+ dat->bLockScrollbar = false;
+ Clist_RecalculateGroupCheckboxes(dat);
+
+ g_clistApi.pfnRecalcScrollBar(hwnd, dat);
+
+ NMCLISTCONTROL nm;
+ nm.hdr.code = CLN_LISTREBUILT;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm);
+}
diff --git a/src/mir_app/src/clcmsgs.cpp b/src/mir_app/src/clcmsgs.cpp index 49267f5cdb..5b92d1a0f0 100644 --- a/src/mir_app/src/clcmsgs.cpp +++ b/src/mir_app/src/clcmsgs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clcutils.cpp b/src/mir_app/src/clcutils.cpp index c3849572cb..1cd238ffc6 100644 --- a/src/mir_app/src/clcutils.cpp +++ b/src/mir_app/src/clcutils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistcore.cpp b/src/mir_app/src/clistcore.cpp index 27372c0101..ebfa53dda6 100644 --- a/src/mir_app/src/clistcore.cpp +++ b/src/mir_app/src/clistcore.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistevents.cpp b/src/mir_app/src/clistevents.cpp index 0533b5b1c7..4e5455ebc2 100644 --- a/src/mir_app/src/clistevents.cpp +++ b/src/mir_app/src/clistevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistgroups.cpp b/src/mir_app/src/clistgroups.cpp index e91b5686d1..79d320515b 100644 --- a/src/mir_app/src/clistgroups.cpp +++ b/src/mir_app/src/clistgroups.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistmod.cpp b/src/mir_app/src/clistmod.cpp index 6a205db148..19b9a8a860 100644 --- a/src/mir_app/src/clistmod.cpp +++ b/src/mir_app/src/clistmod.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistopts.cpp b/src/mir_app/src/clistopts.cpp index 169ee85b6b..8df408c45e 100644 --- a/src/mir_app/src/clistopts.cpp +++ b/src/mir_app/src/clistopts.cpp @@ -1,162 +1,162 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -CMOption<bool> Clist::UseGroups(MODULENAME, "UseGroups", true); -CMOption<bool> Clist::HideOffline(MODULENAME, "HideOffline", false); -CMOption<bool> Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true); -CMOption<bool> Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true); -CMOption<bool> Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true); -CMOption<bool> Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false); -CMOption<bool> Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true); - -CMOption<bool> Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus()); -CMOption<bool> Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false); - -CMOption<bool> Clist::FilterSearch("CLC", "FilterSearch", false); -CMOption<uint32_t> Clist::OfflineModes("CLC", "OfflineModes", MODEF_OFFLINE); - -struct -{ - uint32_t style; - wchar_t *szDescr; -} -static const offlineValues[] = -{ - { MODEF_OFFLINE, LPGENW("Offline") }, - { PF2_ONLINE, LPGENW("Online") }, - { PF2_SHORTAWAY, LPGENW("Away") }, - { PF2_LONGAWAY, LPGENW("Not available") }, - { PF2_LIGHTDND, LPGENW("Occupied") }, - { PF2_HEAVYDND, LPGENW("Do not disturb") }, - { PF2_FREECHAT, LPGENW("Free for chat") }, - { PF2_INVISIBLE, LPGENW("Invisible") } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -class ClistCommonOptsDlg : public CDlgBase -{ - CCtrlSpin blink; - CCtrlCheck chkUseGroups, chkHideOffline, chkConfirmDelete, chkHideEmptyGroups, chkRemoveTempContacts, chkEnableIconBlink, chkFilterSearch; - CCtrlCheck chkAlwaysStatus, chkOneClick, chkEnableTrayBlink; - CCtrlTreeView hideStatuses; - -public: - ClistCommonOptsDlg() : - CDlgBase(g_plugin, IDD_OPT_CLIST), - blink(this, IDC_BLINKSPIN, 0x3FFF, 250), - hideStatuses(this, IDC_HIDEOFFLINEOPTS), - chkOneClick(this, IDC_ONECLK), - chkUseGroups(this, IDC_USEGROUPS), - chkHideOffline(this, IDC_HIDEOFFLINE), - chkFilterSearch(this, IDC_FILTER_SEARCH), - chkAlwaysStatus(this, IDC_ALWAYSSTATUS), - chkConfirmDelete(this, IDC_CONFIRMDELETE), - chkHideEmptyGroups(this, IDC_HIDEEMPTYGROUPS), - chkEnableIconBlink(this, IDC_ENABLE_ICON_BLINK), - chkEnableTrayBlink(this, IDC_ENABLE_TRAY_BLINK), - chkRemoveTempContacts(this, IDC_REMOVETEMP) - { - chkEnableTrayBlink.OnChange = Callback(this, &ClistCommonOptsDlg::onChange_TrayBlink); - - CreateLink(chkOneClick, Clist::Tray1Click); - CreateLink(chkUseGroups, Clist::UseGroups); - CreateLink(chkHideOffline, Clist::HideOffline); - CreateLink(chkFilterSearch, Clist::FilterSearch); - CreateLink(chkAlwaysStatus, Clist::TrayAlwaysStatus); - CreateLink(chkConfirmDelete, Clist::ConfirmDelete); - CreateLink(chkHideEmptyGroups, Clist::HideEmptyGroups); - CreateLink(chkEnableIconBlink, Clist::EnableIconBlink); - CreateLink(chkEnableTrayBlink, Clist::EnableTrayFlash); - CreateLink(chkRemoveTempContacts, Clist::RemoveTempContacts); - } - - bool OnInitDialog() override - { - blink.SetPosition(db_get_w(0, MODULENAME, "IconFlashTime", 550)); - - SetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE, - GetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); - - int style = Clist::OfflineModes; - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - for (auto &it : offlineValues) { - tvis.item.lParam = it.style; - tvis.item.pszText = TranslateW(it.szDescr); - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.state = INDEXTOSTATEIMAGEMASK((style & it.style) != 0 ? 2 : 1); - hideStatuses.InsertItem(&tvis); - } - return true; - } - - bool OnApply() override - { - db_set_w(0, MODULENAME, "IconFlashTime", blink.GetPosition()); - - uint32_t flags = 0; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.hItem = hideStatuses.GetRoot(); - while (tvi.hItem) { - hideStatuses.GetItem(&tvi); - if ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2) - flags |= tvi.lParam; - tvi.hItem = hideStatuses.GetNextSibling(tvi.hItem); - } - Clist::OfflineModes = flags; - - Clist_ClcOptionsChanged(); - Clist_LoadContactTree(); - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - return true; - } - - void onChange_TrayBlink(CCtrlCheck*) - { - bool bEnabled = chkEnableTrayBlink.IsChecked(); - EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKTIME), bEnabled); - EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKSPIN), bEnabled); - } -}; - -int ClcOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -200000000; - odp.szGroup.a = LPGEN("Contact list"); - odp.szTitle.a = LPGEN("Common"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new ClistCommonOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "clc.h"
+
+CMOption<bool> Clist::UseGroups(MODULENAME, "UseGroups", true);
+CMOption<bool> Clist::HideOffline(MODULENAME, "HideOffline", false);
+CMOption<bool> Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true);
+CMOption<bool> Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true);
+CMOption<bool> Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true);
+CMOption<bool> Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false);
+CMOption<bool> Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true);
+
+CMOption<bool> Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus());
+CMOption<bool> Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false);
+
+CMOption<bool> Clist::FilterSearch("CLC", "FilterSearch", false);
+CMOption<uint32_t> Clist::OfflineModes("CLC", "OfflineModes", MODEF_OFFLINE);
+
+struct
+{
+ uint32_t style;
+ wchar_t *szDescr;
+}
+static const offlineValues[] =
+{
+ { MODEF_OFFLINE, LPGENW("Offline") },
+ { PF2_ONLINE, LPGENW("Online") },
+ { PF2_SHORTAWAY, LPGENW("Away") },
+ { PF2_LONGAWAY, LPGENW("Not available") },
+ { PF2_LIGHTDND, LPGENW("Occupied") },
+ { PF2_HEAVYDND, LPGENW("Do not disturb") },
+ { PF2_FREECHAT, LPGENW("Free for chat") },
+ { PF2_INVISIBLE, LPGENW("Invisible") }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class ClistCommonOptsDlg : public CDlgBase
+{
+ CCtrlSpin blink;
+ CCtrlCheck chkUseGroups, chkHideOffline, chkConfirmDelete, chkHideEmptyGroups, chkRemoveTempContacts, chkEnableIconBlink, chkFilterSearch;
+ CCtrlCheck chkAlwaysStatus, chkOneClick, chkEnableTrayBlink;
+ CCtrlTreeView hideStatuses;
+
+public:
+ ClistCommonOptsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_CLIST),
+ blink(this, IDC_BLINKSPIN, 0x3FFF, 250),
+ hideStatuses(this, IDC_HIDEOFFLINEOPTS),
+ chkOneClick(this, IDC_ONECLK),
+ chkUseGroups(this, IDC_USEGROUPS),
+ chkHideOffline(this, IDC_HIDEOFFLINE),
+ chkFilterSearch(this, IDC_FILTER_SEARCH),
+ chkAlwaysStatus(this, IDC_ALWAYSSTATUS),
+ chkConfirmDelete(this, IDC_CONFIRMDELETE),
+ chkHideEmptyGroups(this, IDC_HIDEEMPTYGROUPS),
+ chkEnableIconBlink(this, IDC_ENABLE_ICON_BLINK),
+ chkEnableTrayBlink(this, IDC_ENABLE_TRAY_BLINK),
+ chkRemoveTempContacts(this, IDC_REMOVETEMP)
+ {
+ chkEnableTrayBlink.OnChange = Callback(this, &ClistCommonOptsDlg::onChange_TrayBlink);
+
+ CreateLink(chkOneClick, Clist::Tray1Click);
+ CreateLink(chkUseGroups, Clist::UseGroups);
+ CreateLink(chkHideOffline, Clist::HideOffline);
+ CreateLink(chkFilterSearch, Clist::FilterSearch);
+ CreateLink(chkAlwaysStatus, Clist::TrayAlwaysStatus);
+ CreateLink(chkConfirmDelete, Clist::ConfirmDelete);
+ CreateLink(chkHideEmptyGroups, Clist::HideEmptyGroups);
+ CreateLink(chkEnableIconBlink, Clist::EnableIconBlink);
+ CreateLink(chkEnableTrayBlink, Clist::EnableTrayFlash);
+ CreateLink(chkRemoveTempContacts, Clist::RemoveTempContacts);
+ }
+
+ bool OnInitDialog() override
+ {
+ blink.SetPosition(db_get_w(0, MODULENAME, "IconFlashTime", 550));
+
+ SetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+
+ int style = Clist::OfflineModes;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ for (auto &it : offlineValues) {
+ tvis.item.lParam = it.style;
+ tvis.item.pszText = TranslateW(it.szDescr);
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((style & it.style) != 0 ? 2 : 1);
+ hideStatuses.InsertItem(&tvis);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ db_set_w(0, MODULENAME, "IconFlashTime", blink.GetPosition());
+
+ uint32_t flags = 0;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.hItem = hideStatuses.GetRoot();
+ while (tvi.hItem) {
+ hideStatuses.GetItem(&tvi);
+ if ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2)
+ flags |= tvi.lParam;
+ tvi.hItem = hideStatuses.GetNextSibling(tvi.hItem);
+ }
+ Clist::OfflineModes = flags;
+
+ Clist_ClcOptionsChanged();
+ Clist_LoadContactTree();
+ Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
+ return true;
+ }
+
+ void onChange_TrayBlink(CCtrlCheck*)
+ {
+ bool bEnabled = chkEnableTrayBlink.IsChecked();
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKTIME), bEnabled);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKSPIN), bEnabled);
+ }
+};
+
+int ClcOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -200000000;
+ odp.szGroup.a = LPGEN("Contact list");
+ odp.szTitle.a = LPGEN("Common");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new ClistCommonOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/clistsettings.cpp b/src/mir_app/src/clistsettings.cpp index 6b160e0046..4d3e60b9d4 100644 --- a/src/mir_app/src/clistsettings.cpp +++ b/src/mir_app/src/clistsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clisttray.cpp b/src/mir_app/src/clisttray.cpp index 9d4137748b..c10100a1b3 100644 --- a/src/mir_app/src/clisttray.cpp +++ b/src/mir_app/src/clisttray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clui.cpp b/src/mir_app/src/clui.cpp index e5201fb244..39015a1819 100644 --- a/src/mir_app/src/clui.cpp +++ b/src/mir_app/src/clui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/cluiservices.cpp b/src/mir_app/src/cluiservices.cpp index dc17de4dfb..ae6d2c66de 100644 --- a/src/mir_app/src/cluiservices.cpp +++ b/src/mir_app/src/cluiservices.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/colorchooser.cpp b/src/mir_app/src/colorchooser.cpp index 2931d9f3cf..6a93e61f54 100644 --- a/src/mir_app/src/colorchooser.cpp +++ b/src/mir_app/src/colorchooser.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/contacts.cpp b/src/mir_app/src/contacts.cpp index 3a4c32b18b..8cec2a273a 100644 --- a/src/mir_app/src/contacts.cpp +++ b/src/mir_app/src/contacts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/copyright.h b/src/mir_app/src/copyright.h index 29b0f97206..641ed66f54 100644 --- a/src/mir_app/src/copyright.h +++ b/src/mir_app/src/copyright.h @@ -1,2 +1,2 @@ - -#define LEGAL_COPYRIGHT "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" +
+#define LEGAL_COPYRIGHT "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
diff --git a/src/mir_app/src/database.cpp b/src/mir_app/src/database.cpp index d20361c3b4..149377bc27 100644 --- a/src/mir_app/src/database.cpp +++ b/src/mir_app/src/database.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/database.h b/src/mir_app/src/database.h index 11d5b1b58b..07f32d6bad 100644 --- a/src/mir_app/src/database.h +++ b/src/mir_app/src/database.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright 2012-22 Miranda NG team,
+Copyright 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_events.cpp b/src/mir_app/src/db_events.cpp index 68049037f1..b6bce698d4 100644 --- a/src/mir_app/src/db_events.cpp +++ b/src/mir_app/src/db_events.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_ini.cpp b/src/mir_app/src/db_ini.cpp index 8ce8f88385..6953345787 100644 --- a/src/mir_app/src/db_ini.cpp +++ b/src/mir_app/src/db_ini.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_intf.cpp b/src/mir_app/src/db_intf.cpp index fc9467d8fd..a422ab7b25 100644 --- a/src/mir_app/src/db_intf.cpp +++ b/src/mir_app/src/db_intf.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_upgrade.cpp b/src/mir_app/src/db_upgrade.cpp index c96f638125..2b6f6d7755 100644 --- a/src/mir_app/src/db_upgrade.cpp +++ b/src/mir_app/src/db_upgrade.cpp @@ -1,71 +1,71 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define CONVERT_MSG LPGEN("This database is in the old format that isn't supported anymore. Press Yes to convert it to the new format or No to return") -#define MISSING_DB_MSG LPGEN("To open this database you need to install the Dbx_sqlite plugin. Click Yes to download it from Miranda NG's site or No to return") -#define MISSING_PLUG_MSG LPGEN("To open this database you need to install the Import plugin. Click Yes to download it from Miranda NG's site or No to return") - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(MDatabaseCommon*) DB::Upgrade(const wchar_t *profile) -{ - DATABASELINK *pLink = GetDatabasePlugin("dbx_sqlite"); - if (pLink == nullptr) { - if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_DB_MSG), L"Miranda NG", MB_YESNO)) - Utils_OpenUrl("https://miranda-ng.org/p/Dbx_sqlite"); - return nullptr; - } - - if (!Profile_GetSettingInt(L"Database/SilentUpgrade")) - if (IDYES != MessageBoxW(nullptr, TranslateT(CONVERT_MSG), L"Miranda NG", MB_YESNO)) - return nullptr; - - int errorCode; - CMStringW wszBackupName(profile); - wszBackupName.Append(L".bak"); - DeleteFileW(wszBackupName); - if (!MoveFileW(profile, wszBackupName)) { - uint32_t dwError = GetLastError(); - CMStringW wszError(FORMAT, TranslateT("Cannot move old profile '%s' to '%s': error %d"), profile, wszBackupName.c_str(), dwError); - MessageBoxW(nullptr, wszError, L"Miranda NG", MB_ICONERROR | MB_OK); - return nullptr; - } - - if ((errorCode = pLink->makeDatabase(profile)) != 0) { - MessageBoxW(nullptr, CMStringW(FORMAT, TranslateT("Attempt to create database '%s' failed with error code %d"), profile, errorCode), L"Miranda NG", MB_ICONERROR | MB_OK); -LBL_Error: - DeleteFileW(profile); - MoveFileW(wszBackupName, profile); - return nullptr; - } - - if (SetServiceModePlugin("import", 1) != ERROR_SUCCESS) { - if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_PLUG_MSG), L"Miranda NG", MB_YESNO)) - Utils_OpenUrl("https://miranda-ng.org/p/Import"); - goto LBL_Error; - } - - return pLink->Load(profile, false); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define CONVERT_MSG LPGEN("This database is in the old format that isn't supported anymore. Press Yes to convert it to the new format or No to return")
+#define MISSING_DB_MSG LPGEN("To open this database you need to install the Dbx_sqlite plugin. Click Yes to download it from Miranda NG's site or No to return")
+#define MISSING_PLUG_MSG LPGEN("To open this database you need to install the Import plugin. Click Yes to download it from Miranda NG's site or No to return")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(MDatabaseCommon*) DB::Upgrade(const wchar_t *profile)
+{
+ DATABASELINK *pLink = GetDatabasePlugin("dbx_sqlite");
+ if (pLink == nullptr) {
+ if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_DB_MSG), L"Miranda NG", MB_YESNO))
+ Utils_OpenUrl("https://miranda-ng.org/p/Dbx_sqlite");
+ return nullptr;
+ }
+
+ if (!Profile_GetSettingInt(L"Database/SilentUpgrade"))
+ if (IDYES != MessageBoxW(nullptr, TranslateT(CONVERT_MSG), L"Miranda NG", MB_YESNO))
+ return nullptr;
+
+ int errorCode;
+ CMStringW wszBackupName(profile);
+ wszBackupName.Append(L".bak");
+ DeleteFileW(wszBackupName);
+ if (!MoveFileW(profile, wszBackupName)) {
+ uint32_t dwError = GetLastError();
+ CMStringW wszError(FORMAT, TranslateT("Cannot move old profile '%s' to '%s': error %d"), profile, wszBackupName.c_str(), dwError);
+ MessageBoxW(nullptr, wszError, L"Miranda NG", MB_ICONERROR | MB_OK);
+ return nullptr;
+ }
+
+ if ((errorCode = pLink->makeDatabase(profile)) != 0) {
+ MessageBoxW(nullptr, CMStringW(FORMAT, TranslateT("Attempt to create database '%s' failed with error code %d"), profile, errorCode), L"Miranda NG", MB_ICONERROR | MB_OK);
+LBL_Error:
+ DeleteFileW(profile);
+ MoveFileW(wszBackupName, profile);
+ return nullptr;
+ }
+
+ if (SetServiceModePlugin("import", 1) != ERROR_SUCCESS) {
+ if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_PLUG_MSG), L"Miranda NG", MB_YESNO))
+ Utils_OpenUrl("https://miranda-ng.org/p/Import");
+ goto LBL_Error;
+ }
+
+ return pLink->Load(profile, false);
+}
diff --git a/src/mir_app/src/db_util.cpp b/src/mir_app/src/db_util.cpp index fc23a10050..6fd23cb4f5 100644 --- a/src/mir_app/src/db_util.cpp +++ b/src/mir_app/src/db_util.cpp @@ -1,129 +1,129 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "profilemanager.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Profile_GetPathA(size_t cbLen, char *pszDest) -{ - if (!pszDest || !cbLen) - return 1; - - strncpy_s(pszDest, cbLen, _T2A(g_profileDir), _TRUNCATE); - return 0; -} - -MIR_APP_DLL(int) Profile_GetPathW(size_t cbLen, wchar_t *pwszDest) -{ - if (!pwszDest || !cbLen) - return 1; - - wcsncpy_s(pwszDest, cbLen, g_profileDir, _TRUNCATE); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Profile_GetNameA(size_t cbLen, char *pszDest) -{ - if (!cbLen || !pszDest) - return 1; - - strncpy_s(pszDest, cbLen, ptrA(makeFileName(g_profileName)), _TRUNCATE); - return 0; -} - -MIR_APP_DLL(int) Profile_GetNameW(size_t cbLen, wchar_t *pwszDest) -{ - if (!cbLen || !pwszDest) - return 1; - - wcsncpy_s(pwszDest, cbLen, g_profileName, _TRUNCATE); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Profile_GetSetting(const wchar_t *pwszSetting, wchar_t *pwszBuf, size_t cbLen, const wchar_t *pwszDefault) -{ - if (pwszSetting == nullptr) { - *pwszBuf = 0; - return false; - } - - if (pwszDefault == nullptr) - pwszDefault = L""; - - wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); - - wchar_t *p = wcschr(pBuf, '/'); - if (p) { - *p = 0; p++; - GetPrivateProfileStringW(pBuf, p, pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); - } - else GetPrivateProfileStringW(pBuf, L"", pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); - - return pwszBuf[0] != 0; -} - -MIR_APP_DLL(int) Profile_GetSettingInt(const wchar_t *pwszSetting, int iDefault) -{ - if (pwszSetting == nullptr) - return iDefault; - - wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); - - wchar_t *p = wcschr(pBuf, '/'); - if (p) { - *p = 0; p++; - return GetPrivateProfileIntW(pBuf, p, iDefault, mirandabootini); - } - - return GetPrivateProfileIntW(pBuf, L"", iDefault, mirandabootini); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Profile_SetDefault(const wchar_t *pwszPath) -{ - extern wchar_t* g_defaultProfile; - replaceStrW(g_defaultProfile, pwszPath); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Profile_CheckOpened(const wchar_t *pwszProfileName) -{ - CMStringW wszPhysName(pwszProfileName); - wszPhysName.Replace(L"\\", L"_"); - wszPhysName.Insert(0, L"Global\\"); - - HANDLE hMutex = ::OpenMutexW(SYNCHRONIZE, false, wszPhysName); - if (hMutex == nullptr) - return false; - - ::CloseHandle(hMutex); - return true; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "profilemanager.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Profile_GetPathA(size_t cbLen, char *pszDest)
+{
+ if (!pszDest || !cbLen)
+ return 1;
+
+ strncpy_s(pszDest, cbLen, _T2A(g_profileDir), _TRUNCATE);
+ return 0;
+}
+
+MIR_APP_DLL(int) Profile_GetPathW(size_t cbLen, wchar_t *pwszDest)
+{
+ if (!pwszDest || !cbLen)
+ return 1;
+
+ wcsncpy_s(pwszDest, cbLen, g_profileDir, _TRUNCATE);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Profile_GetNameA(size_t cbLen, char *pszDest)
+{
+ if (!cbLen || !pszDest)
+ return 1;
+
+ strncpy_s(pszDest, cbLen, ptrA(makeFileName(g_profileName)), _TRUNCATE);
+ return 0;
+}
+
+MIR_APP_DLL(int) Profile_GetNameW(size_t cbLen, wchar_t *pwszDest)
+{
+ if (!cbLen || !pwszDest)
+ return 1;
+
+ wcsncpy_s(pwszDest, cbLen, g_profileName, _TRUNCATE);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Profile_GetSetting(const wchar_t *pwszSetting, wchar_t *pwszBuf, size_t cbLen, const wchar_t *pwszDefault)
+{
+ if (pwszSetting == nullptr) {
+ *pwszBuf = 0;
+ return false;
+ }
+
+ if (pwszDefault == nullptr)
+ pwszDefault = L"";
+
+ wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting);
+
+ wchar_t *p = wcschr(pBuf, '/');
+ if (p) {
+ *p = 0; p++;
+ GetPrivateProfileStringW(pBuf, p, pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini);
+ }
+ else GetPrivateProfileStringW(pBuf, L"", pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini);
+
+ return pwszBuf[0] != 0;
+}
+
+MIR_APP_DLL(int) Profile_GetSettingInt(const wchar_t *pwszSetting, int iDefault)
+{
+ if (pwszSetting == nullptr)
+ return iDefault;
+
+ wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting);
+
+ wchar_t *p = wcschr(pBuf, '/');
+ if (p) {
+ *p = 0; p++;
+ return GetPrivateProfileIntW(pBuf, p, iDefault, mirandabootini);
+ }
+
+ return GetPrivateProfileIntW(pBuf, L"", iDefault, mirandabootini);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Profile_SetDefault(const wchar_t *pwszPath)
+{
+ extern wchar_t* g_defaultProfile;
+ replaceStrW(g_defaultProfile, pwszPath);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Profile_CheckOpened(const wchar_t *pwszProfileName)
+{
+ CMStringW wszPhysName(pwszProfileName);
+ wszPhysName.Replace(L"\\", L"_");
+ wszPhysName.Insert(0, L"Global\\");
+
+ HANDLE hMutex = ::OpenMutexW(SYNCHRONIZE, false, wszPhysName);
+ if (hMutex == nullptr)
+ return false;
+
+ ::CloseHandle(hMutex);
+ return true;
+}
diff --git a/src/mir_app/src/descbutton.cpp b/src/mir_app/src/descbutton.cpp index ad198bdaf1..4fdd74f856 100644 --- a/src/mir_app/src/descbutton.cpp +++ b/src/mir_app/src/descbutton.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
Copyright (c) 2007 Artem Shpynov
all portions of this codebase are copyrighted to the people
diff --git a/src/mir_app/src/dll_sniffer.cpp b/src/mir_app/src/dll_sniffer.cpp index 037ca328e5..7f2aca1955 100644 --- a/src/mir_app/src/dll_sniffer.cpp +++ b/src/mir_app/src/dll_sniffer.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/ei_baseIcon.cpp b/src/mir_app/src/ei_baseIcon.cpp index 5ddce65bab..7244d20edb 100644 --- a/src/mir_app/src/ei_baseIcon.cpp +++ b/src/mir_app/src/ei_baseIcon.cpp @@ -1,70 +1,70 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -BaseExtraIcon::BaseExtraIcon(const char *name, const wchar_t *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM param) : - ExtraIcon(name), - m_OnClick(OnClick), - m_onClickParam(param), - m_tszDescription(mir_wstrdup(description)), - m_hDescIcon(descIcon) -{ - if (!IcoLib_IsManaged((HICON)descIcon)) - m_hDescIcon = IcoLib_GetIconHandle((const char *)descIcon); - - m_id = registeredExtraIcons.getCount() + 1; -} - -BaseExtraIcon::~BaseExtraIcon() -{ -} - -void BaseExtraIcon::setOnClick(MIRANDAHOOKPARAM pFunc, LPARAM pParam) -{ - m_OnClick = pFunc; - m_onClickParam = pParam; -} - -const wchar_t* BaseExtraIcon::getDescription() const -{ - return TranslateW_LP(m_tszDescription, m_pPlugin); -} - -HANDLE BaseExtraIcon::getDescIcon() const -{ - return m_hDescIcon; -} - -void BaseExtraIcon::onClick(MCONTACT hContact) -{ - if (m_OnClick != nullptr) - m_OnClick(hContact, (LPARAM)ConvertToClistSlot(m_slot), m_onClickParam); -} - -int BaseExtraIcon::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) -{ - if (m_pParent) - return m_pParent->ClistSetExtraIcon(hContact, hImage); - return Clist_SetExtraIcon(hContact, m_slot, hImage); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+BaseExtraIcon::BaseExtraIcon(const char *name, const wchar_t *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM param) :
+ ExtraIcon(name),
+ m_OnClick(OnClick),
+ m_onClickParam(param),
+ m_tszDescription(mir_wstrdup(description)),
+ m_hDescIcon(descIcon)
+{
+ if (!IcoLib_IsManaged((HICON)descIcon))
+ m_hDescIcon = IcoLib_GetIconHandle((const char *)descIcon);
+
+ m_id = registeredExtraIcons.getCount() + 1;
+}
+
+BaseExtraIcon::~BaseExtraIcon()
+{
+}
+
+void BaseExtraIcon::setOnClick(MIRANDAHOOKPARAM pFunc, LPARAM pParam)
+{
+ m_OnClick = pFunc;
+ m_onClickParam = pParam;
+}
+
+const wchar_t* BaseExtraIcon::getDescription() const
+{
+ return TranslateW_LP(m_tszDescription, m_pPlugin);
+}
+
+HANDLE BaseExtraIcon::getDescIcon() const
+{
+ return m_hDescIcon;
+}
+
+void BaseExtraIcon::onClick(MCONTACT hContact)
+{
+ if (m_OnClick != nullptr)
+ m_OnClick(hContact, (LPARAM)ConvertToClistSlot(m_slot), m_onClickParam);
+}
+
+int BaseExtraIcon::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage)
+{
+ if (m_pParent)
+ return m_pParent->ClistSetExtraIcon(hContact, hImage);
+ return Clist_SetExtraIcon(hContact, m_slot, hImage);
+}
diff --git a/src/mir_app/src/ei_callbackIcon.cpp b/src/mir_app/src/ei_callbackIcon.cpp index a28bd24ed0..205003d054 100644 --- a/src/mir_app/src/ei_callbackIcon.cpp +++ b/src/mir_app/src/ei_callbackIcon.cpp @@ -1,75 +1,75 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -CallbackExtraIcon::CallbackExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, - MIRANDAHOOK _RebuildIcons, MIRANDAHOOK _ApplyIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : - BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param), - m_pfnRebuildIcons(_RebuildIcons), m_pfnApplyIcon(_ApplyIcon), m_needToRebuild(true) -{ -} - -CallbackExtraIcon::~CallbackExtraIcon() -{ -} - -int CallbackExtraIcon::getType() const -{ - return EXTRAICON_TYPE_CALLBACK; -} - -void CallbackExtraIcon::rebuildIcons() -{ - if (!isEnabled()) { - m_needToRebuild = true; - return; - } - - m_needToRebuild = false; - m_pfnRebuildIcons(0, 0); -} - -void CallbackExtraIcon::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - if (m_needToRebuild) - rebuildIcons(); - - m_pfnApplyIcon(hContact, 0); -} - -int CallbackExtraIcon::setIcon(MCONTACT hContact, HANDLE icon) -{ - if (!isEnabled() || hContact == 0) - return -1; - - return ClistSetExtraIcon(hContact, icon); -} - -int CallbackExtraIcon::setIconByName(MCONTACT, const char*) -{ - return -1; -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+CallbackExtraIcon::CallbackExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon,
+ MIRANDAHOOK _RebuildIcons, MIRANDAHOOK _ApplyIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) :
+ BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param),
+ m_pfnRebuildIcons(_RebuildIcons), m_pfnApplyIcon(_ApplyIcon), m_needToRebuild(true)
+{
+}
+
+CallbackExtraIcon::~CallbackExtraIcon()
+{
+}
+
+int CallbackExtraIcon::getType() const
+{
+ return EXTRAICON_TYPE_CALLBACK;
+}
+
+void CallbackExtraIcon::rebuildIcons()
+{
+ if (!isEnabled()) {
+ m_needToRebuild = true;
+ return;
+ }
+
+ m_needToRebuild = false;
+ m_pfnRebuildIcons(0, 0);
+}
+
+void CallbackExtraIcon::applyIcon(MCONTACT hContact)
+{
+ if (!isEnabled() || hContact == 0)
+ return;
+
+ if (m_needToRebuild)
+ rebuildIcons();
+
+ m_pfnApplyIcon(hContact, 0);
+}
+
+int CallbackExtraIcon::setIcon(MCONTACT hContact, HANDLE icon)
+{
+ if (!isEnabled() || hContact == 0)
+ return -1;
+
+ return ClistSetExtraIcon(hContact, icon);
+}
+
+int CallbackExtraIcon::setIconByName(MCONTACT, const char*)
+{
+ return -1;
+}
diff --git a/src/mir_app/src/ei_defaulticons.cpp b/src/mir_app/src/ei_defaulticons.cpp index 14c1b2b359..e2eb20cbf0 100644 --- a/src/mir_app/src/ei_defaulticons.cpp +++ b/src/mir_app/src/ei_defaulticons.cpp @@ -1,339 +1,339 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "m_cluiframes.h" - -#include "extraicons.h" -#include "chat.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// DB extra icons - -HANDLE hExtraVisibility, hExtraChat, hExtraChatMute, hExtraGender, hExtraProto; - -static void SetVisibility(MCONTACT hContact, int apparentMode, bool clear) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return; - - if (apparentMode <= 0) - apparentMode = db_get_w(hContact, proto, "ApparentMode", 0); - - HANDLE hExtraIcon, hIcolib = nullptr; - - // Is chat - if (Contact::IsGroupChat(hContact, proto)) { - hExtraIcon = hExtraChat; - if (apparentMode == ID_STATUS_OFFLINE) - hIcolib = IcoLib_GetIconHandle("ChatActivity"); - } - else { // Not chat - hExtraIcon = hExtraVisibility; - if (apparentMode == ID_STATUS_OFFLINE) - hIcolib = Skin_GetIconHandle(SKINICON_OTHER_INVISIBLE_ALL); - else if (apparentMode == ID_STATUS_ONLINE) - hIcolib = Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL); - } - - if (hIcolib != nullptr || clear) - ExtraIcon_SetIcon(hExtraIcon, hContact, hIcolib); -} - -static void SetGender(MCONTACT hContact, int gender, bool clear) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return; - - if (gender <= 0) - gender = db_get_b(hContact, proto, "Gender", 0); - if (gender <= 0) - gender = db_get_b(hContact, "UserInfo", "Gender", 0); - - const char *ico; - if (gender == 'M') - ico = "gender_male"; - else if (gender == 'F') - ico = "gender_female"; - else - ico = nullptr; - - if (ico != nullptr || clear) - ExtraIcon_SetIconByName(hExtraGender, hContact, ico); -} - -static void SetChatMute(MCONTACT hContact, int mode) -{ - if (hContact == 0) - return; - - if (mode == -1) - mode = db_get_b(hContact, "SRMM", "MuteMode", CHATMODE_NORMAL); - - HANDLE hIcon; - switch (mode) { - case CHATMODE_MUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_OFF); break; - case CHATMODE_UNMUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_ON); break; - default: - hIcon = nullptr; break; - } - - ExtraIcon_SetIcon(hExtraChatMute, hContact, hIcon); -} - -struct Info -{ - const char *name; - const char *desc; - int iSkinIcon; - const char *db[8]; - void(*OnClick)(Info *info, const char *text); - int flags; - - HANDLE hIcolib, hExtraIcon; -}; - -static void EmailOnClick(Info*, const char *text) -{ - char cmd[1024]; - mir_snprintf(cmd, "mailto:%s", text); - ShellExecuteA(nullptr, "open", cmd, nullptr, nullptr, SW_SHOW); -} - -static void HomepageOnClick(Info*, const char *text) -{ - ShellExecuteA(nullptr, "open", text, nullptr, nullptr, SW_SHOW); -} - -static Info infos[] = -{ - { "homepage", LPGEN("Homepage"), SKINICON_OTHER_MIRANDAWEB, - { nullptr, "Homepage", "UserInfo", "Homepage" }, - &HomepageOnClick, EIF_DISABLED_BY_DEFAULT }, - { "sms", LPGEN("Phone/SMS"), SKINICON_OTHER_SMS, - { nullptr, "Cellular", "UserInfo", "Cellular", "UserInfo", "Phone", "UserInfo", "MyPhone0" }, - nullptr, EIF_DISABLED_BY_DEFAULT }, - { "email", LPGEN("E-mail"), SKINICON_OTHER_SENDEMAIL, - { nullptr, "e-mail", "UserInfo", "e-mail", "UserInfo", "Mye-mail0" }, - &EmailOnClick, EIF_DISABLED_BY_DEFAULT }, -}; - -static void SetExtraIcons(MCONTACT hContact) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if ( IsEmpty(proto)) - return; - - for (auto &p : infos) { - for (unsigned int j = 0; j < _countof(p.db); j += 2) { - if (p.db[j + 1] == nullptr) - break; - - ptrA szValue(db_get_sa(hContact, p.db[j] == nullptr ? proto : p.db[j], p.db[j + 1])); - if (!IsEmpty(szValue)) { - ExtraIcon_SetIcon(p.hExtraIcon, hContact, p.hIcolib); - break; - } - } - } -} - -static int SettingChanged(WPARAM hContact, LPARAM lParam) -{ - if (hContact == 0) - return 0; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam; - bool isProto = (strcmp(cws->szModule, proto) == 0); - if (isProto && !strcmp(cws->szSetting, "ApparentMode")) { - SetVisibility(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.wVal, true); - return 0; - } - - if (!strcmp(cws->szModule, "SRMM") && !strcmp(cws->szSetting, "MuteMode")) { - SetChatMute(hContact, cws->value.type == DBVT_DELETED ? CHATMODE_NORMAL : cws->value.bVal); - return 0; - } - - if (!strcmp(cws->szSetting, "Gender") && (isProto || !strcmp(cws->szModule, "UserInfo"))) { - SetGender(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.bVal, true); - return 0; - } - - for (auto &p : infos) { - for (int j = 0; j < _countof(p.db); j += 2) { - if (p.db[j + 1] == nullptr) - break; - if (p.db[j] == nullptr && !isProto) - continue; - if (p.db[j] != nullptr && strcmp(cws->szModule, p.db[j])) - continue; - if (strcmp(cws->szSetting, p.db[j + 1])) - continue; - - bool show = (cws->value.type != DBVT_DELETED && !IsEmpty(cws->value.pszVal)); - ExtraIcon_SetIcon(p.hExtraIcon, hContact, show ? p.hIcolib : nullptr); - break; - } - } - - return 0; -} - -static int DefaultOnClick(WPARAM hContact, LPARAM, LPARAM param) -{ - Info *p = (Info*)param; - if (p == nullptr) - return 0; - - if (hContact == 0) - return 0; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - bool found = false; - for (int j = 0; !found && j < _countof(p->db); j += 2) { - if (p->db[j + 1] == nullptr) - break; - - ptrA szValue(db_get_sa(hContact, p->db[j] == nullptr ? proto : p->db[j], p->db[j + 1])); - if (!IsEmpty(szValue)) { - p->OnClick(p, szValue); - found = true; - } - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// Protocol icon - -struct ProtoInfo -{ - ProtoInfo(LPCSTR _proto, HANDLE _image) : - proto(mir_strdup(_proto)), - hImage(_image) - {} - - ptrA proto; - HANDLE hImage; -}; - -static int CompareProtos(const ProtoInfo *p1, const ProtoInfo *p2) -{ return mir_strcmp(p1->proto, p2->proto); -} - -OBJLIST<ProtoInfo> arProtos(10, CompareProtos); - -static int ProtocolRebuildIcons(WPARAM, LPARAM) -{ - arProtos.destroy(); - return 0; -} - -static ProtoInfo* FindProto(const char *proto) -{ - ProtoInfo *p = arProtos.find((ProtoInfo*)&proto); - if (p) - return p; - - HICON hIcon = Skin_LoadProtoIcon(proto, ID_STATUS_ONLINE); - if (hIcon == nullptr) - return nullptr; - - HANDLE hImage = ExtraIcon_AddIcon(hIcon); - if (hImage == INVALID_HANDLE_VALUE) - return nullptr; - - p = new ProtoInfo(proto, hImage); - arProtos.insert(p); - return p; -} - -static int ProtocolApplyIcon(WPARAM hContact, LPARAM) -{ - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - HANDLE hImage = INVALID_HANDLE_VALUE; - ProtoInfo *pi = FindProto(proto); - if (pi != nullptr) - hImage = pi->hImage; - - ExtraIcon_SetIcon(hExtraProto, hContact, hImage); - return 0; -} - -static int ProtocolOnClick(WPARAM wParam, LPARAM, LPARAM) -{ - if (wParam) - CallService(MS_USERINFO_SHOWDIALOG, wParam, 0); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void DefaultExtraIcons_Load() -{ - hExtraChat = ExtraIcon_RegisterIcolib("chat_activity", LPGEN("Chat activity"), "ChatActivity"); - hExtraChatMute = ExtraIcon_RegisterIcolib("chat_mute", LPGEN("Chat mute mode"), "ChatMute"); - hExtraVisibility = ExtraIcon_RegisterIcolib("visibility", "Visibility", Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL)); - hExtraGender = ExtraIcon_RegisterIcolib("gender", "Gender", "gender_male", nullptr, 0, EIF_DISABLED_BY_DEFAULT); - hExtraProto = ExtraIcon_RegisterCallback("protocol", "Account", Skin_GetIconHandle(SKINICON_OTHER_ACCMGR), - &ProtocolRebuildIcons, &ProtocolApplyIcon, &ProtocolOnClick, 0, EIF_DISABLED_BY_DEFAULT); - - for (auto &p : infos) { - p.hIcolib = Skin_GetIconHandle(p.iSkinIcon); - if (p.OnClick) - p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), DefaultOnClick, (LPARAM)&p, p.flags); - else - p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), nullptr, 0, p.flags); - } - - for (auto &hContact : Contacts()) { - SetChatMute(hContact, -1); - SetExtraIcons(hContact); - SetVisibility(hContact, -1, false); - SetGender(hContact, -1, false); - } - - HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "m_cluiframes.h"
+
+#include "extraicons.h"
+#include "chat.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// DB extra icons
+
+HANDLE hExtraVisibility, hExtraChat, hExtraChatMute, hExtraGender, hExtraProto;
+
+static void SetVisibility(MCONTACT hContact, int apparentMode, bool clear)
+{
+ if (hContact == 0)
+ return;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return;
+
+ if (apparentMode <= 0)
+ apparentMode = db_get_w(hContact, proto, "ApparentMode", 0);
+
+ HANDLE hExtraIcon, hIcolib = nullptr;
+
+ // Is chat
+ if (Contact::IsGroupChat(hContact, proto)) {
+ hExtraIcon = hExtraChat;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ hIcolib = IcoLib_GetIconHandle("ChatActivity");
+ }
+ else { // Not chat
+ hExtraIcon = hExtraVisibility;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ hIcolib = Skin_GetIconHandle(SKINICON_OTHER_INVISIBLE_ALL);
+ else if (apparentMode == ID_STATUS_ONLINE)
+ hIcolib = Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL);
+ }
+
+ if (hIcolib != nullptr || clear)
+ ExtraIcon_SetIcon(hExtraIcon, hContact, hIcolib);
+}
+
+static void SetGender(MCONTACT hContact, int gender, bool clear)
+{
+ if (hContact == 0)
+ return;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return;
+
+ if (gender <= 0)
+ gender = db_get_b(hContact, proto, "Gender", 0);
+ if (gender <= 0)
+ gender = db_get_b(hContact, "UserInfo", "Gender", 0);
+
+ const char *ico;
+ if (gender == 'M')
+ ico = "gender_male";
+ else if (gender == 'F')
+ ico = "gender_female";
+ else
+ ico = nullptr;
+
+ if (ico != nullptr || clear)
+ ExtraIcon_SetIconByName(hExtraGender, hContact, ico);
+}
+
+static void SetChatMute(MCONTACT hContact, int mode)
+{
+ if (hContact == 0)
+ return;
+
+ if (mode == -1)
+ mode = db_get_b(hContact, "SRMM", "MuteMode", CHATMODE_NORMAL);
+
+ HANDLE hIcon;
+ switch (mode) {
+ case CHATMODE_MUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_OFF); break;
+ case CHATMODE_UNMUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_ON); break;
+ default:
+ hIcon = nullptr; break;
+ }
+
+ ExtraIcon_SetIcon(hExtraChatMute, hContact, hIcon);
+}
+
+struct Info
+{
+ const char *name;
+ const char *desc;
+ int iSkinIcon;
+ const char *db[8];
+ void(*OnClick)(Info *info, const char *text);
+ int flags;
+
+ HANDLE hIcolib, hExtraIcon;
+};
+
+static void EmailOnClick(Info*, const char *text)
+{
+ char cmd[1024];
+ mir_snprintf(cmd, "mailto:%s", text);
+ ShellExecuteA(nullptr, "open", cmd, nullptr, nullptr, SW_SHOW);
+}
+
+static void HomepageOnClick(Info*, const char *text)
+{
+ ShellExecuteA(nullptr, "open", text, nullptr, nullptr, SW_SHOW);
+}
+
+static Info infos[] =
+{
+ { "homepage", LPGEN("Homepage"), SKINICON_OTHER_MIRANDAWEB,
+ { nullptr, "Homepage", "UserInfo", "Homepage" },
+ &HomepageOnClick, EIF_DISABLED_BY_DEFAULT },
+ { "sms", LPGEN("Phone/SMS"), SKINICON_OTHER_SMS,
+ { nullptr, "Cellular", "UserInfo", "Cellular", "UserInfo", "Phone", "UserInfo", "MyPhone0" },
+ nullptr, EIF_DISABLED_BY_DEFAULT },
+ { "email", LPGEN("E-mail"), SKINICON_OTHER_SENDEMAIL,
+ { nullptr, "e-mail", "UserInfo", "e-mail", "UserInfo", "Mye-mail0" },
+ &EmailOnClick, EIF_DISABLED_BY_DEFAULT },
+};
+
+static void SetExtraIcons(MCONTACT hContact)
+{
+ if (hContact == 0)
+ return;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if ( IsEmpty(proto))
+ return;
+
+ for (auto &p : infos) {
+ for (unsigned int j = 0; j < _countof(p.db); j += 2) {
+ if (p.db[j + 1] == nullptr)
+ break;
+
+ ptrA szValue(db_get_sa(hContact, p.db[j] == nullptr ? proto : p.db[j], p.db[j + 1]));
+ if (!IsEmpty(szValue)) {
+ ExtraIcon_SetIcon(p.hExtraIcon, hContact, p.hIcolib);
+ break;
+ }
+ }
+ }
+}
+
+static int SettingChanged(WPARAM hContact, LPARAM lParam)
+{
+ if (hContact == 0)
+ return 0;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ bool isProto = (strcmp(cws->szModule, proto) == 0);
+ if (isProto && !strcmp(cws->szSetting, "ApparentMode")) {
+ SetVisibility(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.wVal, true);
+ return 0;
+ }
+
+ if (!strcmp(cws->szModule, "SRMM") && !strcmp(cws->szSetting, "MuteMode")) {
+ SetChatMute(hContact, cws->value.type == DBVT_DELETED ? CHATMODE_NORMAL : cws->value.bVal);
+ return 0;
+ }
+
+ if (!strcmp(cws->szSetting, "Gender") && (isProto || !strcmp(cws->szModule, "UserInfo"))) {
+ SetGender(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.bVal, true);
+ return 0;
+ }
+
+ for (auto &p : infos) {
+ for (int j = 0; j < _countof(p.db); j += 2) {
+ if (p.db[j + 1] == nullptr)
+ break;
+ if (p.db[j] == nullptr && !isProto)
+ continue;
+ if (p.db[j] != nullptr && strcmp(cws->szModule, p.db[j]))
+ continue;
+ if (strcmp(cws->szSetting, p.db[j + 1]))
+ continue;
+
+ bool show = (cws->value.type != DBVT_DELETED && !IsEmpty(cws->value.pszVal));
+ ExtraIcon_SetIcon(p.hExtraIcon, hContact, show ? p.hIcolib : nullptr);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int DefaultOnClick(WPARAM hContact, LPARAM, LPARAM param)
+{
+ Info *p = (Info*)param;
+ if (p == nullptr)
+ return 0;
+
+ if (hContact == 0)
+ return 0;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return 0;
+
+ bool found = false;
+ for (int j = 0; !found && j < _countof(p->db); j += 2) {
+ if (p->db[j + 1] == nullptr)
+ break;
+
+ ptrA szValue(db_get_sa(hContact, p->db[j] == nullptr ? proto : p->db[j], p->db[j + 1]));
+ if (!IsEmpty(szValue)) {
+ p->OnClick(p, szValue);
+ found = true;
+ }
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Protocol icon
+
+struct ProtoInfo
+{
+ ProtoInfo(LPCSTR _proto, HANDLE _image) :
+ proto(mir_strdup(_proto)),
+ hImage(_image)
+ {}
+
+ ptrA proto;
+ HANDLE hImage;
+};
+
+static int CompareProtos(const ProtoInfo *p1, const ProtoInfo *p2)
+{ return mir_strcmp(p1->proto, p2->proto);
+}
+
+OBJLIST<ProtoInfo> arProtos(10, CompareProtos);
+
+static int ProtocolRebuildIcons(WPARAM, LPARAM)
+{
+ arProtos.destroy();
+ return 0;
+}
+
+static ProtoInfo* FindProto(const char *proto)
+{
+ ProtoInfo *p = arProtos.find((ProtoInfo*)&proto);
+ if (p)
+ return p;
+
+ HICON hIcon = Skin_LoadProtoIcon(proto, ID_STATUS_ONLINE);
+ if (hIcon == nullptr)
+ return nullptr;
+
+ HANDLE hImage = ExtraIcon_AddIcon(hIcon);
+ if (hImage == INVALID_HANDLE_VALUE)
+ return nullptr;
+
+ p = new ProtoInfo(proto, hImage);
+ arProtos.insert(p);
+ return p;
+}
+
+static int ProtocolApplyIcon(WPARAM hContact, LPARAM)
+{
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return 0;
+
+ HANDLE hImage = INVALID_HANDLE_VALUE;
+ ProtoInfo *pi = FindProto(proto);
+ if (pi != nullptr)
+ hImage = pi->hImage;
+
+ ExtraIcon_SetIcon(hExtraProto, hContact, hImage);
+ return 0;
+}
+
+static int ProtocolOnClick(WPARAM wParam, LPARAM, LPARAM)
+{
+ if (wParam)
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, 0);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void DefaultExtraIcons_Load()
+{
+ hExtraChat = ExtraIcon_RegisterIcolib("chat_activity", LPGEN("Chat activity"), "ChatActivity");
+ hExtraChatMute = ExtraIcon_RegisterIcolib("chat_mute", LPGEN("Chat mute mode"), "ChatMute");
+ hExtraVisibility = ExtraIcon_RegisterIcolib("visibility", "Visibility", Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL));
+ hExtraGender = ExtraIcon_RegisterIcolib("gender", "Gender", "gender_male", nullptr, 0, EIF_DISABLED_BY_DEFAULT);
+ hExtraProto = ExtraIcon_RegisterCallback("protocol", "Account", Skin_GetIconHandle(SKINICON_OTHER_ACCMGR),
+ &ProtocolRebuildIcons, &ProtocolApplyIcon, &ProtocolOnClick, 0, EIF_DISABLED_BY_DEFAULT);
+
+ for (auto &p : infos) {
+ p.hIcolib = Skin_GetIconHandle(p.iSkinIcon);
+ if (p.OnClick)
+ p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), DefaultOnClick, (LPARAM)&p, p.flags);
+ else
+ p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), nullptr, 0, p.flags);
+ }
+
+ for (auto &hContact : Contacts()) {
+ SetChatMute(hContact, -1);
+ SetExtraIcons(hContact);
+ SetVisibility(hContact, -1, false);
+ SetGender(hContact, -1, false);
+ }
+
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged);
+}
diff --git a/src/mir_app/src/ei_extraIcon.cpp b/src/mir_app/src/ei_extraIcon.cpp index ecec626b7e..fbdfc901ec 100644 --- a/src/mir_app/src/ei_extraIcon.cpp +++ b/src/mir_app/src/ei_extraIcon.cpp @@ -1,83 +1,83 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -ExtraIcon::ExtraIcon(const char *name) : - m_szName(mir_strdup(name)) -{ -} - -ExtraIcon::~ExtraIcon() -{ -} - -const char *ExtraIcon::getName() const -{ - return m_szName; -} - -int ExtraIcon::getSlot() const -{ - return m_slot; -} - -void ExtraIcon::setSlot(int slot) -{ - m_slot = slot; -} - -int ExtraIcon::getPosition() const -{ - return m_position; -} - -void ExtraIcon::setPosition(int position) -{ - m_position = position; -} - -bool ExtraIcon::isEnabled() const -{ - return m_slot >= 0; -} - -void ExtraIcon::doApply(MCONTACT hContact) -{ - if (m_pParent) - m_pParent->applyIcon(hContact); - else - applyIcon(hContact); -} - -void ExtraIcon::applyIcons() -{ - if (!isEnabled()) - return; - - for (auto &hContact : Contacts()) { - // Clear to assert that it will be cleared - Clist_SetExtraIcon(hContact, m_slot, INVALID_HANDLE_VALUE); - doApply(hContact); - } -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+ExtraIcon::ExtraIcon(const char *name) :
+ m_szName(mir_strdup(name))
+{
+}
+
+ExtraIcon::~ExtraIcon()
+{
+}
+
+const char *ExtraIcon::getName() const
+{
+ return m_szName;
+}
+
+int ExtraIcon::getSlot() const
+{
+ return m_slot;
+}
+
+void ExtraIcon::setSlot(int slot)
+{
+ m_slot = slot;
+}
+
+int ExtraIcon::getPosition() const
+{
+ return m_position;
+}
+
+void ExtraIcon::setPosition(int position)
+{
+ m_position = position;
+}
+
+bool ExtraIcon::isEnabled() const
+{
+ return m_slot >= 0;
+}
+
+void ExtraIcon::doApply(MCONTACT hContact)
+{
+ if (m_pParent)
+ m_pParent->applyIcon(hContact);
+ else
+ applyIcon(hContact);
+}
+
+void ExtraIcon::applyIcons()
+{
+ if (!isEnabled())
+ return;
+
+ for (auto &hContact : Contacts()) {
+ // Clear to assert that it will be cleared
+ Clist_SetExtraIcon(hContact, m_slot, INVALID_HANDLE_VALUE);
+ doApply(hContact);
+ }
+}
diff --git a/src/mir_app/src/ei_groupIcon.cpp b/src/mir_app/src/ei_groupIcon.cpp index 50bea072e0..44dcbbd686 100644 --- a/src/mir_app/src/ei_groupIcon.cpp +++ b/src/mir_app/src/ei_groupIcon.cpp @@ -1,200 +1,200 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -ExtraIconGroup::ExtraIconGroup(const char *_name) : - ExtraIcon(_name), - m_items(1) -{ - db_set_resident(EI_MODULE_NAME, _name); -} - -ExtraIconGroup::~ExtraIconGroup() -{ -} - -void ExtraIconGroup::addExtraIcon(BaseExtraIcon *extra) -{ - m_items.insert(extra); - - CMStringW description; - for (auto &p : m_items) { - if (!description.IsEmpty()) - description.Append(L" / "); - description += p->getDescription(); - } - - m_tszDescription = mir_wstrdup(description); -} - -void ExtraIconGroup::rebuildIcons() -{ - for (auto &p : m_items) - p->rebuildIcons(); -} - -void ExtraIconGroup::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - m_setValidExtraIcon = false; - m_insideApply = true; - - for (auto &p : m_items) { - p->applyIcon(hContact); - if (m_setValidExtraIcon) { - m_pCurrentItem = p; - break; - } - } - - m_insideApply = false; -} - -int ExtraIconGroup::getPosition() const -{ - int pos = INT_MAX; - for (auto &p : m_items) - pos = min(pos, p->getPosition()); - return pos; -} - -void ExtraIconGroup::setSlot(int slot) -{ - ExtraIcon::setSlot(slot); - - for (auto &p : m_items) - p->setSlot(slot); -} - -void ExtraIconGroup::onClick(MCONTACT hContact) -{ - if (m_pCurrentItem != nullptr) - m_pCurrentItem->onClick(hContact); -} - -int ExtraIconGroup::setIcon(MCONTACT, HANDLE) -{ - return -1; - // return internalSetIcon(hContact, (void*)value, false); -} - -int ExtraIconGroup::setIconByName(MCONTACT, const char*) -{ - return -1; - // return internalSetIcon(hContact, (void*)value, true); -} - -int ExtraIconGroup::internalSetIcon(ExtraIcon *pChild, MCONTACT hContact, HANDLE value, bool bByName) -{ - if (m_insideApply) { - for (auto &p : m_items) - if (p == pChild) { - if (bByName) - return p->setIconByName(hContact, (const char*)value); - return p->setIcon(hContact, value); - } - - return -1; - } - - int currentPos = m_items.getCount(); - int storePos = m_items.getCount(); - for (int i = 0; i < m_items.getCount(); i++) { - if (m_items[i] == pChild) - storePos = i; - - if (m_items[i] == m_pCurrentItem) - currentPos = i; - } - - if (storePos == m_items.getCount()) - return -1; - - if (storePos > currentPos) { - m_items[storePos]->storeIcon(hContact, value); - return 0; - } - - // Ok, we have to set the icon, but we have to assert it is a valid icon - m_setValidExtraIcon = false; - - int ret; - if (bByName) - ret = m_items[storePos]->setIconByName(hContact, (const char*)value); - else - ret = m_items[storePos]->setIcon(hContact, (HANDLE)value); - - if (storePos < currentPos) { - if (m_setValidExtraIcon) - m_pCurrentItem = m_items[storePos]; - } - else if (storePos == currentPos) { - if (!m_setValidExtraIcon) { - m_pCurrentItem = nullptr; - - m_insideApply = true; - - for (++storePos; storePos < m_items.getCount(); ++storePos) { - m_items[storePos]->applyIcon(hContact); - if (m_setValidExtraIcon) { - m_pCurrentItem = m_items[storePos]; - break; - } - } - - m_insideApply = false; - } - } - - return ret; -} - -const wchar_t* ExtraIconGroup::getDescription() const -{ - return m_tszDescription; -} - -HANDLE ExtraIconGroup::getDescIcon() const -{ - for (auto &p : m_items) - if (HANDLE ret = p->getDescIcon()) - return ret; - - return nullptr; -} - -int ExtraIconGroup::getType() const -{ - return EXTRAICON_TYPE_GROUP; -} - -int ExtraIconGroup::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) -{ - if (hImage != INVALID_HANDLE_VALUE) - m_setValidExtraIcon = true; - - return Clist_SetExtraIcon(hContact, m_slot, hImage); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+ExtraIconGroup::ExtraIconGroup(const char *_name) :
+ ExtraIcon(_name),
+ m_items(1)
+{
+ db_set_resident(EI_MODULE_NAME, _name);
+}
+
+ExtraIconGroup::~ExtraIconGroup()
+{
+}
+
+void ExtraIconGroup::addExtraIcon(BaseExtraIcon *extra)
+{
+ m_items.insert(extra);
+
+ CMStringW description;
+ for (auto &p : m_items) {
+ if (!description.IsEmpty())
+ description.Append(L" / ");
+ description += p->getDescription();
+ }
+
+ m_tszDescription = mir_wstrdup(description);
+}
+
+void ExtraIconGroup::rebuildIcons()
+{
+ for (auto &p : m_items)
+ p->rebuildIcons();
+}
+
+void ExtraIconGroup::applyIcon(MCONTACT hContact)
+{
+ if (!isEnabled() || hContact == 0)
+ return;
+
+ m_setValidExtraIcon = false;
+ m_insideApply = true;
+
+ for (auto &p : m_items) {
+ p->applyIcon(hContact);
+ if (m_setValidExtraIcon) {
+ m_pCurrentItem = p;
+ break;
+ }
+ }
+
+ m_insideApply = false;
+}
+
+int ExtraIconGroup::getPosition() const
+{
+ int pos = INT_MAX;
+ for (auto &p : m_items)
+ pos = min(pos, p->getPosition());
+ return pos;
+}
+
+void ExtraIconGroup::setSlot(int slot)
+{
+ ExtraIcon::setSlot(slot);
+
+ for (auto &p : m_items)
+ p->setSlot(slot);
+}
+
+void ExtraIconGroup::onClick(MCONTACT hContact)
+{
+ if (m_pCurrentItem != nullptr)
+ m_pCurrentItem->onClick(hContact);
+}
+
+int ExtraIconGroup::setIcon(MCONTACT, HANDLE)
+{
+ return -1;
+ // return internalSetIcon(hContact, (void*)value, false);
+}
+
+int ExtraIconGroup::setIconByName(MCONTACT, const char*)
+{
+ return -1;
+ // return internalSetIcon(hContact, (void*)value, true);
+}
+
+int ExtraIconGroup::internalSetIcon(ExtraIcon *pChild, MCONTACT hContact, HANDLE value, bool bByName)
+{
+ if (m_insideApply) {
+ for (auto &p : m_items)
+ if (p == pChild) {
+ if (bByName)
+ return p->setIconByName(hContact, (const char*)value);
+ return p->setIcon(hContact, value);
+ }
+
+ return -1;
+ }
+
+ int currentPos = m_items.getCount();
+ int storePos = m_items.getCount();
+ for (int i = 0; i < m_items.getCount(); i++) {
+ if (m_items[i] == pChild)
+ storePos = i;
+
+ if (m_items[i] == m_pCurrentItem)
+ currentPos = i;
+ }
+
+ if (storePos == m_items.getCount())
+ return -1;
+
+ if (storePos > currentPos) {
+ m_items[storePos]->storeIcon(hContact, value);
+ return 0;
+ }
+
+ // Ok, we have to set the icon, but we have to assert it is a valid icon
+ m_setValidExtraIcon = false;
+
+ int ret;
+ if (bByName)
+ ret = m_items[storePos]->setIconByName(hContact, (const char*)value);
+ else
+ ret = m_items[storePos]->setIcon(hContact, (HANDLE)value);
+
+ if (storePos < currentPos) {
+ if (m_setValidExtraIcon)
+ m_pCurrentItem = m_items[storePos];
+ }
+ else if (storePos == currentPos) {
+ if (!m_setValidExtraIcon) {
+ m_pCurrentItem = nullptr;
+
+ m_insideApply = true;
+
+ for (++storePos; storePos < m_items.getCount(); ++storePos) {
+ m_items[storePos]->applyIcon(hContact);
+ if (m_setValidExtraIcon) {
+ m_pCurrentItem = m_items[storePos];
+ break;
+ }
+ }
+
+ m_insideApply = false;
+ }
+ }
+
+ return ret;
+}
+
+const wchar_t* ExtraIconGroup::getDescription() const
+{
+ return m_tszDescription;
+}
+
+HANDLE ExtraIconGroup::getDescIcon() const
+{
+ for (auto &p : m_items)
+ if (HANDLE ret = p->getDescIcon())
+ return ret;
+
+ return nullptr;
+}
+
+int ExtraIconGroup::getType() const
+{
+ return EXTRAICON_TYPE_GROUP;
+}
+
+int ExtraIconGroup::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage)
+{
+ if (hImage != INVALID_HANDLE_VALUE)
+ m_setValidExtraIcon = true;
+
+ return Clist_SetExtraIcon(hContact, m_slot, hImage);
+}
diff --git a/src/mir_app/src/ei_icolibIcon.cpp b/src/mir_app/src/ei_icolibIcon.cpp index 07f4a2b3df..7095c96902 100644 --- a/src/mir_app/src/ei_icolibIcon.cpp +++ b/src/mir_app/src/ei_icolibIcon.cpp @@ -1,118 +1,118 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" -#include "usedIcons.h" - -#include "IcoLib.h" - -IcolibExtraIcon::IcolibExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : - BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param) -{ - db_set_resident(EI_MODULE_NAME, _name); -} - -IcolibExtraIcon::~IcolibExtraIcon() -{ -} - -int IcolibExtraIcon::getType() const -{ - return EXTRAICON_TYPE_ICOLIB; -} - -void IcolibExtraIcon::rebuildIcons() -{ -} - -void IcolibExtraIcon::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - HANDLE hImage = INVALID_HANDLE_VALUE; - - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - hImage = GetIcon(szIconName); - - ClistSetExtraIcon(hContact, hImage); -} - -int IcolibExtraIcon::setIcon(MCONTACT hContact, HANDLE hIcoLib) -{ - if (hContact == 0) - return -1; - - if (hIcoLib == INVALID_HANDLE_VALUE) - hIcoLib = nullptr; - - if (isEnabled()) { - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - RemoveIcon(szIconName); - } - - IcolibItem *p = (IcolibItem*)hIcoLib; - char *szName = (p) ? p->name : nullptr; - storeIcon(hContact, szName); - - if (isEnabled()) - return ClistSetExtraIcon(hContact, (hIcoLib == nullptr) ? INVALID_HANDLE_VALUE : AddIcon(szName)); - - return 0; -} - -int IcolibExtraIcon::setIconByName(MCONTACT hContact, const char *icon) -{ - if (hContact == 0) - return -1; - - if (icon == INVALID_HANDLE_VALUE) - icon = nullptr; - - if (isEnabled()) { - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - RemoveIcon(szIconName); - } - - storeIcon(hContact, (char*)icon); - - if (isEnabled()) - return ClistSetExtraIcon(hContact, (IsEmpty(icon)) ? INVALID_HANDLE_VALUE : AddIcon(icon)); - - return 0; -} - -void IcolibExtraIcon::storeIcon(MCONTACT hContact, void *icon) -{ - if (hContact == 0) - return; - - const char *icolibName = (const char *)icon; - if (IsEmpty(icolibName)) - db_unset(hContact, EI_MODULE_NAME, m_szName); - else - db_set_s(hContact, EI_MODULE_NAME, m_szName, icolibName); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+#include "usedIcons.h"
+
+#include "IcoLib.h"
+
+IcolibExtraIcon::IcolibExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) :
+ BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param)
+{
+ db_set_resident(EI_MODULE_NAME, _name);
+}
+
+IcolibExtraIcon::~IcolibExtraIcon()
+{
+}
+
+int IcolibExtraIcon::getType() const
+{
+ return EXTRAICON_TYPE_ICOLIB;
+}
+
+void IcolibExtraIcon::rebuildIcons()
+{
+}
+
+void IcolibExtraIcon::applyIcon(MCONTACT hContact)
+{
+ if (!isEnabled() || hContact == 0)
+ return;
+
+ HANDLE hImage = INVALID_HANDLE_VALUE;
+
+ ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName));
+ if (!IsEmpty(szIconName))
+ hImage = GetIcon(szIconName);
+
+ ClistSetExtraIcon(hContact, hImage);
+}
+
+int IcolibExtraIcon::setIcon(MCONTACT hContact, HANDLE hIcoLib)
+{
+ if (hContact == 0)
+ return -1;
+
+ if (hIcoLib == INVALID_HANDLE_VALUE)
+ hIcoLib = nullptr;
+
+ if (isEnabled()) {
+ ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName));
+ if (!IsEmpty(szIconName))
+ RemoveIcon(szIconName);
+ }
+
+ IcolibItem *p = (IcolibItem*)hIcoLib;
+ char *szName = (p) ? p->name : nullptr;
+ storeIcon(hContact, szName);
+
+ if (isEnabled())
+ return ClistSetExtraIcon(hContact, (hIcoLib == nullptr) ? INVALID_HANDLE_VALUE : AddIcon(szName));
+
+ return 0;
+}
+
+int IcolibExtraIcon::setIconByName(MCONTACT hContact, const char *icon)
+{
+ if (hContact == 0)
+ return -1;
+
+ if (icon == INVALID_HANDLE_VALUE)
+ icon = nullptr;
+
+ if (isEnabled()) {
+ ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName));
+ if (!IsEmpty(szIconName))
+ RemoveIcon(szIconName);
+ }
+
+ storeIcon(hContact, (char*)icon);
+
+ if (isEnabled())
+ return ClistSetExtraIcon(hContact, (IsEmpty(icon)) ? INVALID_HANDLE_VALUE : AddIcon(icon));
+
+ return 0;
+}
+
+void IcolibExtraIcon::storeIcon(MCONTACT hContact, void *icon)
+{
+ if (hContact == 0)
+ return;
+
+ const char *icolibName = (const char *)icon;
+ if (IsEmpty(icolibName))
+ db_unset(hContact, EI_MODULE_NAME, m_szName);
+ else
+ db_set_s(hContact, EI_MODULE_NAME, m_szName, icolibName);
+}
diff --git a/src/mir_app/src/ei_options.cpp b/src/mir_app/src/ei_options.cpp index 442c120fbb..11e98abd18 100644 --- a/src/mir_app/src/ei_options.cpp +++ b/src/mir_app/src/ei_options.cpp @@ -1,481 +1,481 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -static class CExtraIconOptsDlg *pGlgOptions; - -int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2); - -struct intlist -{ - intlist() : count(0), data(nullptr) {} - ~intlist() { mir_free(data); } - - void add(int val) - { - data = (int*)mir_realloc(data, sizeof(int)*(count + 1)); - data[count++] = val; - } - - int count; - int *data; -}; - -static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM) -{ - intlist *a = (intlist*)lParam1; - intlist *b = (intlist*)lParam2; - return SortFunc(registeredExtraIcons[a->data[0] - 1], registeredExtraIcons[b->data[0] - 1]); -} - -// Functions ////////////////////////////////////////////////////////////////////////////////////// - -BOOL ScreenToClient(HWND hWnd, LPRECT lpRect) -{ - POINT pt; - pt.x = lpRect->left; - pt.y = lpRect->top; - - BOOL ret = ScreenToClient(hWnd, &pt); - if (!ret) - return ret; - - lpRect->left = pt.x; - lpRect->top = pt.y; - - pt.x = lpRect->right; - pt.y = lpRect->bottom; - - ret = ScreenToClient(hWnd, &pt); - - lpRect->right = pt.x; - lpRect->bottom = pt.y; - - return ret; -} - -static void RemoveExtraIcons(int slot) -{ - for (auto &hContact : Contacts()) - Clist_SetExtraIcon(hContact, slot, INVALID_HANDLE_VALUE); -} - -class CExtraIconOptsDlg : public CDlgBase -{ - intlist* Tree_GetIDs(HTREEITEM hItem) - { - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - return (intlist*)tvi.lParam; - } - - HTREEITEM Tree_AddExtraIcon(BaseExtraIcon *extra, bool selected, HTREEITEM hAfter = TVI_LAST) - { - intlist *ids = new intlist(); - ids->add(extra->getID()); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = hAfter; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.iSelectedImage = tvis.item.iImage = extra->getID(); - tvis.item.lParam = (LPARAM)ids; - tvis.item.pszText = (LPTSTR)extra->getDescription(); - tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); - return m_tree.InsertItem(&tvis); - } - - HTREEITEM Tree_AddExtraIconGroup(intlist &group, bool selected, HTREEITEM hAfter = TVI_LAST) - { - intlist *ids = new intlist(); - CMStringW desc; - int img = 0; - for (int i = 0; i < group.count; i++) { - BaseExtraIcon *extra = registeredExtraIcons[group.data[i] - 1]; - ids->add(extra->getID()); - - if (img == 0 && extra->getDescIcon() != nullptr) - img = extra->getID(); - - if (i > 0) - desc += L" / "; - desc += extra->getDescription(); - } - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = hAfter; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.iSelectedImage = tvis.item.iImage = img; - tvis.item.lParam = (LPARAM)ids; - tvis.item.pszText = (wchar_t*)desc.c_str(); - tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); - return m_tree.InsertItem(&tvis); - } - - void GroupSelectedItems() - { - LIST<_TREEITEM> toRemove(1); - intlist ids; - bool selected = false; - HTREEITEM hPlace = nullptr; - - // Find items - HTREEITEM hItem = m_tree.GetRoot(); - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - while (hItem) { - if (m_tree.IsSelected(hItem)) { - if (hPlace == nullptr) - hPlace = hItem; - - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - - intlist *iids = (intlist*)tvi.lParam; - for (int i = 0; i < iids->count; i++) - ids.add(iids->data[i]); - - if ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)) - selected = true; - - toRemove.insert(hItem); - } - - hItem = m_tree.GetNextSibling(hItem); - } - - if (hPlace != nullptr) { - // Add new - HTREEITEM hNew = Tree_AddExtraIconGroup(ids, selected, hPlace); - - // Remove old - for (auto &p : toRemove) { - delete Tree_GetIDs(p); - m_tree.DeleteItem(p); - } - - // Select - m_tree.UnselectAll(); - m_tree.SelectItem(hNew); - } - } - - void UngroupSelectedItems() - { - HTREEITEM hItem = m_tree.GetSelection(); - if (hItem == nullptr) - return; - - intlist *ids = Tree_GetIDs(hItem); - if (ids->count < 2) - return; - - bool selected = m_tree.IsSelected(hItem); - - for (int i = ids->count - 1; i >= 0; i--) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; - Tree_AddExtraIcon(extra, selected, hItem); - } - - delete ids; - m_tree.DeleteItem(hItem); - - m_tree.UnselectAll(); - } - - int ShowPopup(int popup) - { - // Fix selection - HTREEITEM hSelected = m_tree.GetDropHilight(); - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - if (hItem != hSelected && m_tree.IsSelected(hItem)) - m_tree.DropHilite(hItem); - - hItem = m_tree.GetNextSibling(hItem); - } - - HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_OPT_POPUP)); - HMENU submenu = GetSubMenu(menu, popup); - TranslateMenu(submenu); - - uint32_t pos = GetMessagePos(); - int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_LEFTALIGN, LOWORD(pos), HIWORD(pos), 0, m_hwnd, nullptr); - - DestroyMenu(menu); - - // Revert selection - hItem = m_tree.GetRoot(); - while (hItem) { - if (hItem != hSelected && m_tree.IsSelected(hItem)) - m_tree.DropUnhilite(hItem); - hItem = m_tree.GetNextSibling(hItem); - } - - return ret; - } - - CCtrlTreeView m_tree; - CTimer m_timer; - -public: - CExtraIconOptsDlg() : - CDlgBase(g_plugin, IDD_EI_OPTIONS), - m_tree(this, IDC_EXTRAORDER), - m_timer(this, 1) - { - m_tree.SetFlags(MTREE_DND | MTREE_MULTISELECT); - m_tree.OnRightClick = Callback(this, &CExtraIconOptsDlg::onRClick); - - m_timer.OnEvent = Callback(this, &CExtraIconOptsDlg::onTimer); - } - - bool OnInitDialog() override - { - pGlgOptions = this; - - int numSlots = EXTRA_ICON_COUNT; - if (numSlots < (int)registeredExtraIcons.getCount()) { - HWND label = GetDlgItem(m_hwnd, IDC_MAX_ICONS_L); - SetWindowText(label, CMStringW(FORMAT, TranslateT("*only the first %d icons will be shown"), numSlots)); - ShowWindow(label, SW_SHOW); - } - - BuildIconList(); - return true; - } - - bool OnApply() override - { - // Store old slots - int *oldSlots = new int[registeredExtraIcons.getCount()]; - int lastUsedSlot = -1; - for (int i = 0; i < registeredExtraIcons.getCount(); i++) { - if (registeredExtraIcons[i]->getType() != EXTRAICON_TYPE_GROUP) - oldSlots[i] = registeredExtraIcons[i]->getSlot(); - else // Remove old slot for groups to re-set images - oldSlots[i] = -1; - lastUsedSlot = max(lastUsedSlot, registeredExtraIcons[i]->getSlot()); - } - lastUsedSlot = min(lastUsedSlot, EXTRA_ICON_COUNT); - - // Get user data and create new groups - LIST<ExtraIconGroup> groups(1); - - uint8_t pos = 0; - int firstEmptySlot = 0; - HTREEITEM ht = m_tree.GetRoot(); - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.stateMask = TVIS_STATEIMAGEMASK; - while (ht) { - tvi.hItem = ht; - m_tree.GetItem(&tvi); - - intlist *ids = (intlist*)tvi.lParam; - if (ids == nullptr || ids->count < 1) - continue; // ??? - - bool enabled = ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)); - int slot = (enabled ? firstEmptySlot++ : -1); - if (slot >= EXTRA_ICON_COUNT) - slot = -1; - - if (ids->count == 1) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[0] - 1]; - extra->setPosition(pos++); - extra->setSlot(slot); - } - else { - char name[128]; - mir_snprintf(name, "__group_%d", groups.getCount()); - - ExtraIconGroup *group = new ExtraIconGroup(name); - - for (int i = 0; i < ids->count; i++) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; - extra->setPosition(pos++); - - group->addExtraIcon(extra); - } - - group->setSlot(slot); - groups.insert(group); - } - - ht = m_tree.GetNextSibling(ht); - } - - // Store data - for (auto &extra : registeredExtraIcons) { - char setting[512]; - mir_snprintf(setting, "Position_%s", extra->getName()); - db_set_w(0, EI_MODULE_NAME, setting, extra->getPosition()); - - mir_snprintf(setting, "Slot_%s", extra->getName()); - db_set_w(0, EI_MODULE_NAME, setting, extra->getSlot()); - } - - db_delete_module(0, EI_MODULE_NAME "Groups"); - db_set_w(0, EI_MODULE_NAME "Groups", "Count", groups.getCount()); - for (int k = 0; k < groups.getCount(); k++) { - ExtraIconGroup *group = groups[k]; - - char setting[512]; - mir_snprintf(setting, "%d_count", k); - db_set_w(0, EI_MODULE_NAME "Groups", setting, (uint16_t)group->m_items.getCount()); - - for (int j = 0; j < group->m_items.getCount(); j++) { - BaseExtraIcon *extra = group->m_items[j]; - - mir_snprintf(setting, "%d_%d", k, j); - db_set_s(0, EI_MODULE_NAME "Groups", setting, extra->getName()); - } - } - - // Clean removed slots - for (int j = firstEmptySlot; j <= lastUsedSlot; j++) - RemoveExtraIcons(j); - - // Apply icons to new slots - RebuildListsBasedOnGroups(groups); - for (auto &extra : extraIconsBySlot) - if (extra->isEnabled()) - extra->applyIcons(); - - delete[] oldSlots; - return true; - } - - void OnDestroy() override - { - pGlgOptions = nullptr; - - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - delete Tree_GetIDs(hItem); - hItem = m_tree.GetNextSibling(hItem); - } - - ImageList_Destroy(m_tree.GetImageList(TVSIL_NORMAL)); - } - - void onRClick(CCtrlTreeView::TEventInfo*) - { - HTREEITEM hSelected = m_tree.GetDropHilight(); - if (hSelected != nullptr && !m_tree.IsSelected(hSelected)) { - m_tree.UnselectAll(); - m_tree.SelectItem(hSelected); - } - - int sels = m_tree.GetNumSelected(); - if (sels > 1) { - if (ShowPopup(0) == ID_GROUP) { - GroupSelectedItems(); - NotifyChange(); - } - } - else if (sels == 1) { - HTREEITEM hItem = m_tree.GetSelection(); - intlist *ids = Tree_GetIDs(hItem); - if (ids->count > 1) { - if (ShowPopup(1) == ID_UNGROUP) { - UngroupSelectedItems(); - NotifyChange(); - } - } - } - } - - void BuildIconList() - { - HIMAGELIST hImageList = ImageList_Create(g_iIconSX, g_iIconSX, ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); - - for (auto &extra : registeredExtraIcons) { - extra->setID(registeredExtraIcons.indexOf(&extra)+1); - - HICON hIcon = IcoLib_GetIconByHandle(extra->getDescIcon()); - if (hIcon == nullptr) - ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); - else { - ImageList_AddIcon(hImageList, hIcon); - IcoLib_ReleaseIcon(hIcon); - } - } - m_tree.SetImageList(hImageList, TVSIL_NORMAL); - - for (auto &extra : extraIconsBySlot) { - if (extra->getType() == EXTRAICON_TYPE_GROUP) { - ExtraIconGroup *group = (ExtraIconGroup *)extra; - intlist ids; - for (auto &p : group->m_items) - ids.add(p->getID()); - Tree_AddExtraIconGroup(ids, extra->isEnabled()); - } - else Tree_AddExtraIcon((BaseExtraIcon*)extra, extra->isEnabled()); - } - - TVSORTCB sort = {}; - sort.hParent = nullptr; - sort.lParam = 0; - sort.lpfnCompare = CompareFunc; - m_tree.SortChildrenCB(&sort, 0); - } - - void onTimer(CTimer*) - { - m_timer.Stop(); - m_tree.DeleteAllItems(); - BuildIconList(); - } - - void ResetIconList() - { - m_timer.Start(100); - } -}; - -void eiOptionsRefresh() -{ - if (pGlgOptions) - pGlgOptions->ResetIconList(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int InitOptionsCallback(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szGroup.a = LPGEN("Contact list"); - odp.szTitle.a = LPGEN("Extra icons"); - odp.szTab.a = LPGEN("General"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CExtraIconOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+static class CExtraIconOptsDlg *pGlgOptions;
+
+int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2);
+
+struct intlist
+{
+ intlist() : count(0), data(nullptr) {}
+ ~intlist() { mir_free(data); }
+
+ void add(int val)
+ {
+ data = (int*)mir_realloc(data, sizeof(int)*(count + 1));
+ data[count++] = val;
+ }
+
+ int count;
+ int *data;
+};
+
+static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM)
+{
+ intlist *a = (intlist*)lParam1;
+ intlist *b = (intlist*)lParam2;
+ return SortFunc(registeredExtraIcons[a->data[0] - 1], registeredExtraIcons[b->data[0] - 1]);
+}
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ POINT pt;
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ BOOL ret = ScreenToClient(hWnd, &pt);
+ if (!ret)
+ return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+static void RemoveExtraIcons(int slot)
+{
+ for (auto &hContact : Contacts())
+ Clist_SetExtraIcon(hContact, slot, INVALID_HANDLE_VALUE);
+}
+
+class CExtraIconOptsDlg : public CDlgBase
+{
+ intlist* Tree_GetIDs(HTREEITEM hItem)
+ {
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_tree.GetItem(&tvi);
+ return (intlist*)tvi.lParam;
+ }
+
+ HTREEITEM Tree_AddExtraIcon(BaseExtraIcon *extra, bool selected, HTREEITEM hAfter = TVI_LAST)
+ {
+ intlist *ids = new intlist();
+ ids->add(extra->getID());
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = hAfter;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.iSelectedImage = tvis.item.iImage = extra->getID();
+ tvis.item.lParam = (LPARAM)ids;
+ tvis.item.pszText = (LPTSTR)extra->getDescription();
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1);
+ return m_tree.InsertItem(&tvis);
+ }
+
+ HTREEITEM Tree_AddExtraIconGroup(intlist &group, bool selected, HTREEITEM hAfter = TVI_LAST)
+ {
+ intlist *ids = new intlist();
+ CMStringW desc;
+ int img = 0;
+ for (int i = 0; i < group.count; i++) {
+ BaseExtraIcon *extra = registeredExtraIcons[group.data[i] - 1];
+ ids->add(extra->getID());
+
+ if (img == 0 && extra->getDescIcon() != nullptr)
+ img = extra->getID();
+
+ if (i > 0)
+ desc += L" / ";
+ desc += extra->getDescription();
+ }
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = hAfter;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.iSelectedImage = tvis.item.iImage = img;
+ tvis.item.lParam = (LPARAM)ids;
+ tvis.item.pszText = (wchar_t*)desc.c_str();
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1);
+ return m_tree.InsertItem(&tvis);
+ }
+
+ void GroupSelectedItems()
+ {
+ LIST<_TREEITEM> toRemove(1);
+ intlist ids;
+ bool selected = false;
+ HTREEITEM hPlace = nullptr;
+
+ // Find items
+ HTREEITEM hItem = m_tree.GetRoot();
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ while (hItem) {
+ if (m_tree.IsSelected(hItem)) {
+ if (hPlace == nullptr)
+ hPlace = hItem;
+
+ tvi.hItem = hItem;
+ m_tree.GetItem(&tvi);
+
+ intlist *iids = (intlist*)tvi.lParam;
+ for (int i = 0; i < iids->count; i++)
+ ids.add(iids->data[i]);
+
+ if ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2))
+ selected = true;
+
+ toRemove.insert(hItem);
+ }
+
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ if (hPlace != nullptr) {
+ // Add new
+ HTREEITEM hNew = Tree_AddExtraIconGroup(ids, selected, hPlace);
+
+ // Remove old
+ for (auto &p : toRemove) {
+ delete Tree_GetIDs(p);
+ m_tree.DeleteItem(p);
+ }
+
+ // Select
+ m_tree.UnselectAll();
+ m_tree.SelectItem(hNew);
+ }
+ }
+
+ void UngroupSelectedItems()
+ {
+ HTREEITEM hItem = m_tree.GetSelection();
+ if (hItem == nullptr)
+ return;
+
+ intlist *ids = Tree_GetIDs(hItem);
+ if (ids->count < 2)
+ return;
+
+ bool selected = m_tree.IsSelected(hItem);
+
+ for (int i = ids->count - 1; i >= 0; i--) {
+ BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1];
+ Tree_AddExtraIcon(extra, selected, hItem);
+ }
+
+ delete ids;
+ m_tree.DeleteItem(hItem);
+
+ m_tree.UnselectAll();
+ }
+
+ int ShowPopup(int popup)
+ {
+ // Fix selection
+ HTREEITEM hSelected = m_tree.GetDropHilight();
+ HTREEITEM hItem = m_tree.GetRoot();
+ while (hItem) {
+ if (hItem != hSelected && m_tree.IsSelected(hItem))
+ m_tree.DropHilite(hItem);
+
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_OPT_POPUP));
+ HMENU submenu = GetSubMenu(menu, popup);
+ TranslateMenu(submenu);
+
+ uint32_t pos = GetMessagePos();
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_LEFTALIGN, LOWORD(pos), HIWORD(pos), 0, m_hwnd, nullptr);
+
+ DestroyMenu(menu);
+
+ // Revert selection
+ hItem = m_tree.GetRoot();
+ while (hItem) {
+ if (hItem != hSelected && m_tree.IsSelected(hItem))
+ m_tree.DropUnhilite(hItem);
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ return ret;
+ }
+
+ CCtrlTreeView m_tree;
+ CTimer m_timer;
+
+public:
+ CExtraIconOptsDlg() :
+ CDlgBase(g_plugin, IDD_EI_OPTIONS),
+ m_tree(this, IDC_EXTRAORDER),
+ m_timer(this, 1)
+ {
+ m_tree.SetFlags(MTREE_DND | MTREE_MULTISELECT);
+ m_tree.OnRightClick = Callback(this, &CExtraIconOptsDlg::onRClick);
+
+ m_timer.OnEvent = Callback(this, &CExtraIconOptsDlg::onTimer);
+ }
+
+ bool OnInitDialog() override
+ {
+ pGlgOptions = this;
+
+ int numSlots = EXTRA_ICON_COUNT;
+ if (numSlots < (int)registeredExtraIcons.getCount()) {
+ HWND label = GetDlgItem(m_hwnd, IDC_MAX_ICONS_L);
+ SetWindowText(label, CMStringW(FORMAT, TranslateT("*only the first %d icons will be shown"), numSlots));
+ ShowWindow(label, SW_SHOW);
+ }
+
+ BuildIconList();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ // Store old slots
+ int *oldSlots = new int[registeredExtraIcons.getCount()];
+ int lastUsedSlot = -1;
+ for (int i = 0; i < registeredExtraIcons.getCount(); i++) {
+ if (registeredExtraIcons[i]->getType() != EXTRAICON_TYPE_GROUP)
+ oldSlots[i] = registeredExtraIcons[i]->getSlot();
+ else // Remove old slot for groups to re-set images
+ oldSlots[i] = -1;
+ lastUsedSlot = max(lastUsedSlot, registeredExtraIcons[i]->getSlot());
+ }
+ lastUsedSlot = min(lastUsedSlot, EXTRA_ICON_COUNT);
+
+ // Get user data and create new groups
+ LIST<ExtraIconGroup> groups(1);
+
+ uint8_t pos = 0;
+ int firstEmptySlot = 0;
+ HTREEITEM ht = m_tree.GetRoot();
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.stateMask = TVIS_STATEIMAGEMASK;
+ while (ht) {
+ tvi.hItem = ht;
+ m_tree.GetItem(&tvi);
+
+ intlist *ids = (intlist*)tvi.lParam;
+ if (ids == nullptr || ids->count < 1)
+ continue; // ???
+
+ bool enabled = ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2));
+ int slot = (enabled ? firstEmptySlot++ : -1);
+ if (slot >= EXTRA_ICON_COUNT)
+ slot = -1;
+
+ if (ids->count == 1) {
+ BaseExtraIcon *extra = registeredExtraIcons[ids->data[0] - 1];
+ extra->setPosition(pos++);
+ extra->setSlot(slot);
+ }
+ else {
+ char name[128];
+ mir_snprintf(name, "__group_%d", groups.getCount());
+
+ ExtraIconGroup *group = new ExtraIconGroup(name);
+
+ for (int i = 0; i < ids->count; i++) {
+ BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1];
+ extra->setPosition(pos++);
+
+ group->addExtraIcon(extra);
+ }
+
+ group->setSlot(slot);
+ groups.insert(group);
+ }
+
+ ht = m_tree.GetNextSibling(ht);
+ }
+
+ // Store data
+ for (auto &extra : registeredExtraIcons) {
+ char setting[512];
+ mir_snprintf(setting, "Position_%s", extra->getName());
+ db_set_w(0, EI_MODULE_NAME, setting, extra->getPosition());
+
+ mir_snprintf(setting, "Slot_%s", extra->getName());
+ db_set_w(0, EI_MODULE_NAME, setting, extra->getSlot());
+ }
+
+ db_delete_module(0, EI_MODULE_NAME "Groups");
+ db_set_w(0, EI_MODULE_NAME "Groups", "Count", groups.getCount());
+ for (int k = 0; k < groups.getCount(); k++) {
+ ExtraIconGroup *group = groups[k];
+
+ char setting[512];
+ mir_snprintf(setting, "%d_count", k);
+ db_set_w(0, EI_MODULE_NAME "Groups", setting, (uint16_t)group->m_items.getCount());
+
+ for (int j = 0; j < group->m_items.getCount(); j++) {
+ BaseExtraIcon *extra = group->m_items[j];
+
+ mir_snprintf(setting, "%d_%d", k, j);
+ db_set_s(0, EI_MODULE_NAME "Groups", setting, extra->getName());
+ }
+ }
+
+ // Clean removed slots
+ for (int j = firstEmptySlot; j <= lastUsedSlot; j++)
+ RemoveExtraIcons(j);
+
+ // Apply icons to new slots
+ RebuildListsBasedOnGroups(groups);
+ for (auto &extra : extraIconsBySlot)
+ if (extra->isEnabled())
+ extra->applyIcons();
+
+ delete[] oldSlots;
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pGlgOptions = nullptr;
+
+ HTREEITEM hItem = m_tree.GetRoot();
+ while (hItem) {
+ delete Tree_GetIDs(hItem);
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ ImageList_Destroy(m_tree.GetImageList(TVSIL_NORMAL));
+ }
+
+ void onRClick(CCtrlTreeView::TEventInfo*)
+ {
+ HTREEITEM hSelected = m_tree.GetDropHilight();
+ if (hSelected != nullptr && !m_tree.IsSelected(hSelected)) {
+ m_tree.UnselectAll();
+ m_tree.SelectItem(hSelected);
+ }
+
+ int sels = m_tree.GetNumSelected();
+ if (sels > 1) {
+ if (ShowPopup(0) == ID_GROUP) {
+ GroupSelectedItems();
+ NotifyChange();
+ }
+ }
+ else if (sels == 1) {
+ HTREEITEM hItem = m_tree.GetSelection();
+ intlist *ids = Tree_GetIDs(hItem);
+ if (ids->count > 1) {
+ if (ShowPopup(1) == ID_UNGROUP) {
+ UngroupSelectedItems();
+ NotifyChange();
+ }
+ }
+ }
+ }
+
+ void BuildIconList()
+ {
+ HIMAGELIST hImageList = ImageList_Create(g_iIconSX, g_iIconSX, ILC_COLOR32 | ILC_MASK, 2, 2);
+ ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK));
+
+ for (auto &extra : registeredExtraIcons) {
+ extra->setID(registeredExtraIcons.indexOf(&extra)+1);
+
+ HICON hIcon = IcoLib_GetIconByHandle(extra->getDescIcon());
+ if (hIcon == nullptr)
+ ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK));
+ else {
+ ImageList_AddIcon(hImageList, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ }
+ }
+ m_tree.SetImageList(hImageList, TVSIL_NORMAL);
+
+ for (auto &extra : extraIconsBySlot) {
+ if (extra->getType() == EXTRAICON_TYPE_GROUP) {
+ ExtraIconGroup *group = (ExtraIconGroup *)extra;
+ intlist ids;
+ for (auto &p : group->m_items)
+ ids.add(p->getID());
+ Tree_AddExtraIconGroup(ids, extra->isEnabled());
+ }
+ else Tree_AddExtraIcon((BaseExtraIcon*)extra, extra->isEnabled());
+ }
+
+ TVSORTCB sort = {};
+ sort.hParent = nullptr;
+ sort.lParam = 0;
+ sort.lpfnCompare = CompareFunc;
+ m_tree.SortChildrenCB(&sort, 0);
+ }
+
+ void onTimer(CTimer*)
+ {
+ m_timer.Stop();
+ m_tree.DeleteAllItems();
+ BuildIconList();
+ }
+
+ void ResetIconList()
+ {
+ m_timer.Start(100);
+ }
+};
+
+void eiOptionsRefresh()
+{
+ if (pGlgOptions)
+ pGlgOptions->ResetIconList();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int InitOptionsCallback(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.szGroup.a = LPGEN("Contact list");
+ odp.szTitle.a = LPGEN("Extra icons");
+ odp.szTab.a = LPGEN("General");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CExtraIconOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/ei_services.cpp b/src/mir_app/src/ei_services.cpp index 9f41eb4679..f55551980d 100644 --- a/src/mir_app/src/ei_services.cpp +++ b/src/mir_app/src/ei_services.cpp @@ -1,504 +1,504 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "m_cluiframes.h" - -#include "extraicons.h" -#include "usedIcons.h" -#include "clc.h" - -// Prototypes /////////////////////////////////////////////////////////////////////////// - -int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2) -{ - int ret = p1->getPosition() - p2->getPosition(); - if (ret != 0) - return ret; - - return p1->getID() - p2->getID(); -} - -LIST<ExtraIcon> extraIconsBySlot(10, SortFunc); -LIST<BaseExtraIcon> registeredExtraIcons(10, PtrKeySortT); - -static bool clistRebuildAlreadyCalled = false, clistApplyAlreadyCalled = false; - -// Functions //////////////////////////////////////////////////////////////////////////// - -int InitOptionsCallback(WPARAM wParam, LPARAM lParam); - -int ConvertToClistSlot(int slot) -{ - if (slot < 0) - return slot; - - return slot + 1; -} - -int ExtraImage_ExtraIDToColumnNum(int extra) -{ - return (extra < 1 || extra > EXTRA_ICON_COUNT) ? -1 : extra - 1; -} - -int Clist_SetExtraIcon(MCONTACT hContact, int slot, HANDLE hImage) -{ - if (g_clistApi.hwndContactTree == nullptr) - return -1; - - int icol = ExtraImage_ExtraIDToColumnNum(ConvertToClistSlot(slot)); - if (icol == -1) - return -1; - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGE, hContact, MAKELPARAM(icol, hImage)); - return 0; -} - -BaseExtraIcon* GetExtraIconByName(const char *name) -{ - for (auto &extra : registeredExtraIcons) - if (mir_strcmp(name, extra->getName()) == 0) - return extra; - - return nullptr; -} - -static void LoadGroups(LIST<ExtraIconGroup> &groups) -{ - int count = db_get_w(0, EI_MODULE_NAME "Groups", "Count", 0); - for (int i = 0; i < count; i++) { - char setting[512]; - mir_snprintf(setting, "%d_count", i); - unsigned int items = db_get_w(0, EI_MODULE_NAME "Groups", setting, 0); - if (items < 1) - continue; - - mir_snprintf(setting, "__group_%d", i); - ExtraIconGroup *group = new ExtraIconGroup(setting); - - for (unsigned int j = 0; j < items; j++) { - mir_snprintf(setting, "%d_%d", i, j); - ptrA szIconName(db_get_sa(0, EI_MODULE_NAME "Groups", setting)); - if (IsEmpty(szIconName)) - continue; - - BaseExtraIcon *extra = GetExtraIconByName(szIconName); - if (extra == nullptr) - continue; - - group->m_items.insert(extra); - if (extra->getSlot() >= 0) - group->setSlot(extra->getSlot()); - } - - if (group->m_items.getCount() < 2) { - delete group; - continue; - } - - groups.insert(group); - } -} - -static ExtraIconGroup* IsInGroup(LIST<ExtraIconGroup> &groups, BaseExtraIcon *extra) -{ - for (auto &group : groups) - for (auto &it : group->m_items) - if (extra == it) - return group; - - return nullptr; -} - -void RebuildListsBasedOnGroups(LIST<ExtraIconGroup> &groups) -{ - for (auto &extra : registeredExtraIcons) - extra->setParent(nullptr); - - for (auto &extra : extraIconsBySlot) - if (extra->getType() == EXTRAICON_TYPE_GROUP) - delete extra; - extraIconsBySlot.destroy(); - - for (auto &group : groups) { - for (auto &it : group->m_items) - it->setParent(group); - - extraIconsBySlot.insert(group); - } - - for (auto &extra : registeredExtraIcons) - if (extra->getParent() == nullptr) - extraIconsBySlot.insert(extra); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void ResetSlots(BaseExtraIcon *extra, ExtraIconGroup *group, int iOldSlot = -1) -{ - int slot = 0, oldMaxSlot = -1; - for (auto &ex : extraIconsBySlot) { - if (ex->getSlot() < 0) - continue; - - int oldSlot = ex->getSlot(); - if (oldSlot > oldMaxSlot) - oldMaxSlot = oldSlot+1; - - ex->setSlot(slot++); - - if (clistApplyAlreadyCalled && (ex == group || ex == extra || oldSlot != slot)) - ex->applyIcons(); - } - - if (iOldSlot > oldMaxSlot) - oldMaxSlot = iOldSlot + 1; - - // slots were freed, we need to clear one or more items - if (extra == nullptr) - for (int i = slot; i < oldMaxSlot; i++) - for (auto &hContact : Contacts()) - Clist_SetExtraIcon(hContact, i, INVALID_HANDLE_VALUE); - - if (!Miranda_IsTerminated()) { - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - eiOptionsRefresh(); - } -} - -void KillModuleExtraIcons(CMPluginBase *pPlugin) -{ - LIST<ExtraIcon> arIcons(1); - - for (auto &it : registeredExtraIcons.rev_iter()) - if (it->m_pPlugin == pPlugin) { - arIcons.insert(it); - registeredExtraIcons.removeItem(&it); - } - - if (arIcons.getCount() == 0) - return; - - int iOldSlot = -1; - for (auto &it : arIcons) - if (it->getSlot() > iOldSlot) - iOldSlot = it->getSlot() + 1; - - LIST<ExtraIconGroup> groups(1); - LoadGroups(groups); - RebuildListsBasedOnGroups(groups); - ResetSlots(0, 0, iOldSlot); - - for (auto &it : arIcons) - delete it; -} - -/////////////////////////////////////////////////////////////////////////////// - -int ClistExtraListRebuild(WPARAM, LPARAM) -{ - clistRebuildAlreadyCalled = true; - - ResetIcons(); - - for (auto &it : extraIconsBySlot) - it->rebuildIcons(); - - return 0; -} - -int ClistExtraImageApply(WPARAM hContact, LPARAM) -{ - if (hContact == 0) - return 0; - - clistApplyAlreadyCalled = true; - - for (auto &it : extraIconsBySlot) - it->doApply(hContact); - - return 0; -} - -int ClistExtraClick(WPARAM hContact, LPARAM lParam) -{ - if (hContact == 0) - return 0; - - int clistSlot = (int)lParam; - - for (auto &extra : extraIconsBySlot) { - if (ConvertToClistSlot(extra->getSlot()) == clistSlot) { - extra->onClick(hContact); - break; - } - } - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// Extra image list functions - -HANDLE hEventExtraImageListRebuilding, hEventExtraImageApplying, hEventExtraClick; - -static bool bImageCreated = false; -static HIMAGELIST hExtraImageList = nullptr; - -MIR_APP_DLL(HANDLE) ExtraIcon_AddIcon(HICON hIcon) -{ - if (hExtraImageList == nullptr || hIcon == nullptr) - return INVALID_HANDLE_VALUE; - - int res = ImageList_AddIcon(hExtraImageList, hIcon); - return (res > 0xFFFE) ? INVALID_HANDLE_VALUE : (HANDLE)res; -} - -MIR_APP_DLL(void) ExtraIcon_Reload() -{ - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRASPACE, db_get_b(0, "CLUI", "ExtraColumnSpace", 18), 0); - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, 0); - - if (hExtraImageList) - ImageList_Destroy(hExtraImageList); - - hExtraImageList = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, 1, 256); - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hExtraImageList); - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); - NotifyEventHooks(hEventExtraImageListRebuilding, 0, 0); - bImageCreated = true; -} - -MIR_APP_DLL(void) ExtraIcon_SetAll(MCONTACT hContact) -{ - if (g_clistApi.hwndContactTree == nullptr) - return; - - if (!bImageCreated) - ExtraIcon_Reload(); - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); - - if (hContact == 0) { - for (auto &it : Contacts()) - NotifyEventHooks(hEventExtraImageApplying, it, 0); - } - else NotifyEventHooks(hEventExtraImageApplying, hContact, 0); - - g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE); -} - -/////////////////////////////////////////////////////////////////////////////// -// external functions - -static void EI_PostCreate(BaseExtraIcon *extra, int flags) -{ - char setting[512]; - mir_snprintf(setting, "Position_%s", extra->getName()); - extra->setPosition(db_get_w(0, EI_MODULE_NAME, setting, 1000)); - - mir_snprintf(setting, "Slot_%s", extra->getName()); - int slot = db_get_w(0, EI_MODULE_NAME, setting, -100); - if (slot == EMPTY_EXTRA_ICON) - slot = -1; - else if (slot == -100) { - if (flags & EIF_DISABLED_BY_DEFAULT) { - db_set_w(0, EI_MODULE_NAME, setting, EMPTY_EXTRA_ICON); - slot = -1; - } - else slot = 1; - } - extra->setSlot(slot); - - registeredExtraIcons.insert(extra); - - LIST<ExtraIconGroup> groups(1); - LoadGroups(groups); - - ExtraIconGroup *group = IsInGroup(groups, extra); - if (group != nullptr) - RebuildListsBasedOnGroups(groups); - else { - for (auto &it : groups) - delete it; - - extraIconsBySlot.insert(extra); - } - - if (slot >= 0 || group != nullptr) { - if (clistRebuildAlreadyCalled) - extra->rebuildIcons(); - - ResetSlots(extra, group); - } -} - -EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterCallback(const char *name, const char *description, HANDLE descIcon, - MIRANDAHOOK RebuildIcons, MIRANDAHOOK ApplyIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) -{ - // EXTRAICON_TYPE_CALLBACK - if (IsEmpty(name) || IsEmpty(description)) - return nullptr; - - if (ApplyIcon == nullptr || RebuildIcons == nullptr) - return nullptr; - - // no way to merge - if (GetExtraIconByName(name) != nullptr) - return nullptr; - - ptrW tszDesc(mir_a2u(description)); - - BaseExtraIcon *extra = new CallbackExtraIcon(name, tszDesc, descIcon, RebuildIcons, ApplyIcon, OnClick, onClickParam); - extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress(RebuildIcons)); - EI_PostCreate(extra, flags); - return extra; -} - -EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterIcolib(const char *name, const char *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) -{ - if (IsEmpty(name) || IsEmpty(description)) - return nullptr; - - ptrW tszDesc(mir_a2u(description)); - - BaseExtraIcon *extra = GetExtraIconByName(name); - if (extra != nullptr) { - if (extra->getType() != EXTRAICON_TYPE_ICOLIB) - return nullptr; - - // Found one, now merge it - if (descIcon) - extra->setDescIcon(descIcon); - - if (OnClick != nullptr) - extra->setOnClick(OnClick, onClickParam); - - if (extra->getSlot() > 0) { - if (clistRebuildAlreadyCalled) - extra->rebuildIcons(); - if (clistApplyAlreadyCalled) - extra->applyIcons(); - } - } - else { - extra = new IcolibExtraIcon(name, tszDesc, descIcon, OnClick, onClickParam); - extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress((void*)name)); - EI_PostCreate(extra, flags); - } - - return extra; -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) ExtraIcon_SetIcon(HANDLE hExtraIcon, MCONTACT hContact, HANDLE hImage) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, hImage, false); - - return extra->setIcon(hContact, hImage); -} - -MIR_APP_DLL(int) ExtraIcon_SetIconByName(HANDLE hExtraIcon, MCONTACT hContact, const char *icoName) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, (HANDLE)icoName, true); - - return extra->setIconByName(hContact, icoName); -} - -MIR_APP_DLL(int) ExtraIcon_Clear(HANDLE hExtraIcon, MCONTACT hContact) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, nullptr, false); - - return extra->setIcon(hContact, nullptr); -} - -/////////////////////////////////////////////////////////////////////////////// - -static IconItem iconList[] = -{ - { LPGEN("Chat activity"), "ChatActivity", IDI_CHAT }, - { LPGEN("Mute chat"), "ChatMute", IDI_OFF }, - { LPGEN("Male"), "gender_male", IDI_MALE }, - { LPGEN("Female"), "gender_female", IDI_FEMALE }, - { LPGEN("Database"), "database", IDI_DATABASE }, -}; - -void LoadExtraIconsModule() -{ - // Events - hEventExtraClick = CreateHookableEvent(ME_CLIST_EXTRA_CLICK); - hEventExtraImageApplying = CreateHookableEvent(ME_CLIST_EXTRA_IMAGE_APPLY); - hEventExtraImageListRebuilding = CreateHookableEvent(ME_CLIST_EXTRA_LIST_REBUILD); - - // Icons - g_plugin.registerIcon(LPGEN("Contact list"), iconList); - - // Hooks - HookEvent(ME_OPT_INITIALISE, InitOptionsCallback); - - HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ClistExtraListRebuild); - HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ClistExtraImageApply); - HookEvent(ME_CLIST_EXTRA_CLICK, ClistExtraClick); - - DefaultExtraIcons_Load(); -} - -void UnloadExtraIconsModule(void) -{ - for (auto &extra : extraIconsBySlot) - if (extra->getType() == EXTRAICON_TYPE_GROUP) - delete extra; - - for (auto &it : registeredExtraIcons) - delete it; - - if (hExtraImageList) { - ImageList_Destroy(hExtraImageList); - hExtraImageList = nullptr; - } -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "m_cluiframes.h"
+
+#include "extraicons.h"
+#include "usedIcons.h"
+#include "clc.h"
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2)
+{
+ int ret = p1->getPosition() - p2->getPosition();
+ if (ret != 0)
+ return ret;
+
+ return p1->getID() - p2->getID();
+}
+
+LIST<ExtraIcon> extraIconsBySlot(10, SortFunc);
+LIST<BaseExtraIcon> registeredExtraIcons(10, PtrKeySortT);
+
+static bool clistRebuildAlreadyCalled = false, clistApplyAlreadyCalled = false;
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+int InitOptionsCallback(WPARAM wParam, LPARAM lParam);
+
+int ConvertToClistSlot(int slot)
+{
+ if (slot < 0)
+ return slot;
+
+ return slot + 1;
+}
+
+int ExtraImage_ExtraIDToColumnNum(int extra)
+{
+ return (extra < 1 || extra > EXTRA_ICON_COUNT) ? -1 : extra - 1;
+}
+
+int Clist_SetExtraIcon(MCONTACT hContact, int slot, HANDLE hImage)
+{
+ if (g_clistApi.hwndContactTree == nullptr)
+ return -1;
+
+ int icol = ExtraImage_ExtraIDToColumnNum(ConvertToClistSlot(slot));
+ if (icol == -1)
+ return -1;
+
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGE, hContact, MAKELPARAM(icol, hImage));
+ return 0;
+}
+
+BaseExtraIcon* GetExtraIconByName(const char *name)
+{
+ for (auto &extra : registeredExtraIcons)
+ if (mir_strcmp(name, extra->getName()) == 0)
+ return extra;
+
+ return nullptr;
+}
+
+static void LoadGroups(LIST<ExtraIconGroup> &groups)
+{
+ int count = db_get_w(0, EI_MODULE_NAME "Groups", "Count", 0);
+ for (int i = 0; i < count; i++) {
+ char setting[512];
+ mir_snprintf(setting, "%d_count", i);
+ unsigned int items = db_get_w(0, EI_MODULE_NAME "Groups", setting, 0);
+ if (items < 1)
+ continue;
+
+ mir_snprintf(setting, "__group_%d", i);
+ ExtraIconGroup *group = new ExtraIconGroup(setting);
+
+ for (unsigned int j = 0; j < items; j++) {
+ mir_snprintf(setting, "%d_%d", i, j);
+ ptrA szIconName(db_get_sa(0, EI_MODULE_NAME "Groups", setting));
+ if (IsEmpty(szIconName))
+ continue;
+
+ BaseExtraIcon *extra = GetExtraIconByName(szIconName);
+ if (extra == nullptr)
+ continue;
+
+ group->m_items.insert(extra);
+ if (extra->getSlot() >= 0)
+ group->setSlot(extra->getSlot());
+ }
+
+ if (group->m_items.getCount() < 2) {
+ delete group;
+ continue;
+ }
+
+ groups.insert(group);
+ }
+}
+
+static ExtraIconGroup* IsInGroup(LIST<ExtraIconGroup> &groups, BaseExtraIcon *extra)
+{
+ for (auto &group : groups)
+ for (auto &it : group->m_items)
+ if (extra == it)
+ return group;
+
+ return nullptr;
+}
+
+void RebuildListsBasedOnGroups(LIST<ExtraIconGroup> &groups)
+{
+ for (auto &extra : registeredExtraIcons)
+ extra->setParent(nullptr);
+
+ for (auto &extra : extraIconsBySlot)
+ if (extra->getType() == EXTRAICON_TYPE_GROUP)
+ delete extra;
+ extraIconsBySlot.destroy();
+
+ for (auto &group : groups) {
+ for (auto &it : group->m_items)
+ it->setParent(group);
+
+ extraIconsBySlot.insert(group);
+ }
+
+ for (auto &extra : registeredExtraIcons)
+ if (extra->getParent() == nullptr)
+ extraIconsBySlot.insert(extra);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void ResetSlots(BaseExtraIcon *extra, ExtraIconGroup *group, int iOldSlot = -1)
+{
+ int slot = 0, oldMaxSlot = -1;
+ for (auto &ex : extraIconsBySlot) {
+ if (ex->getSlot() < 0)
+ continue;
+
+ int oldSlot = ex->getSlot();
+ if (oldSlot > oldMaxSlot)
+ oldMaxSlot = oldSlot+1;
+
+ ex->setSlot(slot++);
+
+ if (clistApplyAlreadyCalled && (ex == group || ex == extra || oldSlot != slot))
+ ex->applyIcons();
+ }
+
+ if (iOldSlot > oldMaxSlot)
+ oldMaxSlot = iOldSlot + 1;
+
+ // slots were freed, we need to clear one or more items
+ if (extra == nullptr)
+ for (int i = slot; i < oldMaxSlot; i++)
+ for (auto &hContact : Contacts())
+ Clist_SetExtraIcon(hContact, i, INVALID_HANDLE_VALUE);
+
+ if (!Miranda_IsTerminated()) {
+ Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
+ eiOptionsRefresh();
+ }
+}
+
+void KillModuleExtraIcons(CMPluginBase *pPlugin)
+{
+ LIST<ExtraIcon> arIcons(1);
+
+ for (auto &it : registeredExtraIcons.rev_iter())
+ if (it->m_pPlugin == pPlugin) {
+ arIcons.insert(it);
+ registeredExtraIcons.removeItem(&it);
+ }
+
+ if (arIcons.getCount() == 0)
+ return;
+
+ int iOldSlot = -1;
+ for (auto &it : arIcons)
+ if (it->getSlot() > iOldSlot)
+ iOldSlot = it->getSlot() + 1;
+
+ LIST<ExtraIconGroup> groups(1);
+ LoadGroups(groups);
+ RebuildListsBasedOnGroups(groups);
+ ResetSlots(0, 0, iOldSlot);
+
+ for (auto &it : arIcons)
+ delete it;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ClistExtraListRebuild(WPARAM, LPARAM)
+{
+ clistRebuildAlreadyCalled = true;
+
+ ResetIcons();
+
+ for (auto &it : extraIconsBySlot)
+ it->rebuildIcons();
+
+ return 0;
+}
+
+int ClistExtraImageApply(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0)
+ return 0;
+
+ clistApplyAlreadyCalled = true;
+
+ for (auto &it : extraIconsBySlot)
+ it->doApply(hContact);
+
+ return 0;
+}
+
+int ClistExtraClick(WPARAM hContact, LPARAM lParam)
+{
+ if (hContact == 0)
+ return 0;
+
+ int clistSlot = (int)lParam;
+
+ for (auto &extra : extraIconsBySlot) {
+ if (ConvertToClistSlot(extra->getSlot()) == clistSlot) {
+ extra->onClick(hContact);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Extra image list functions
+
+HANDLE hEventExtraImageListRebuilding, hEventExtraImageApplying, hEventExtraClick;
+
+static bool bImageCreated = false;
+static HIMAGELIST hExtraImageList = nullptr;
+
+MIR_APP_DLL(HANDLE) ExtraIcon_AddIcon(HICON hIcon)
+{
+ if (hExtraImageList == nullptr || hIcon == nullptr)
+ return INVALID_HANDLE_VALUE;
+
+ int res = ImageList_AddIcon(hExtraImageList, hIcon);
+ return (res > 0xFFFE) ? INVALID_HANDLE_VALUE : (HANDLE)res;
+}
+
+MIR_APP_DLL(void) ExtraIcon_Reload()
+{
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRASPACE, db_get_b(0, "CLUI", "ExtraColumnSpace", 18), 0);
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, 0);
+
+ if (hExtraImageList)
+ ImageList_Destroy(hExtraImageList);
+
+ hExtraImageList = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, 1, 256);
+
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hExtraImageList);
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0);
+ NotifyEventHooks(hEventExtraImageListRebuilding, 0, 0);
+ bImageCreated = true;
+}
+
+MIR_APP_DLL(void) ExtraIcon_SetAll(MCONTACT hContact)
+{
+ if (g_clistApi.hwndContactTree == nullptr)
+ return;
+
+ if (!bImageCreated)
+ ExtraIcon_Reload();
+
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0);
+
+ if (hContact == 0) {
+ for (auto &it : Contacts())
+ NotifyEventHooks(hEventExtraImageApplying, it, 0);
+ }
+ else NotifyEventHooks(hEventExtraImageApplying, hContact, 0);
+
+ g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// external functions
+
+static void EI_PostCreate(BaseExtraIcon *extra, int flags)
+{
+ char setting[512];
+ mir_snprintf(setting, "Position_%s", extra->getName());
+ extra->setPosition(db_get_w(0, EI_MODULE_NAME, setting, 1000));
+
+ mir_snprintf(setting, "Slot_%s", extra->getName());
+ int slot = db_get_w(0, EI_MODULE_NAME, setting, -100);
+ if (slot == EMPTY_EXTRA_ICON)
+ slot = -1;
+ else if (slot == -100) {
+ if (flags & EIF_DISABLED_BY_DEFAULT) {
+ db_set_w(0, EI_MODULE_NAME, setting, EMPTY_EXTRA_ICON);
+ slot = -1;
+ }
+ else slot = 1;
+ }
+ extra->setSlot(slot);
+
+ registeredExtraIcons.insert(extra);
+
+ LIST<ExtraIconGroup> groups(1);
+ LoadGroups(groups);
+
+ ExtraIconGroup *group = IsInGroup(groups, extra);
+ if (group != nullptr)
+ RebuildListsBasedOnGroups(groups);
+ else {
+ for (auto &it : groups)
+ delete it;
+
+ extraIconsBySlot.insert(extra);
+ }
+
+ if (slot >= 0 || group != nullptr) {
+ if (clistRebuildAlreadyCalled)
+ extra->rebuildIcons();
+
+ ResetSlots(extra, group);
+ }
+}
+
+EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterCallback(const char *name, const char *description, HANDLE descIcon,
+ MIRANDAHOOK RebuildIcons, MIRANDAHOOK ApplyIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags)
+{
+ // EXTRAICON_TYPE_CALLBACK
+ if (IsEmpty(name) || IsEmpty(description))
+ return nullptr;
+
+ if (ApplyIcon == nullptr || RebuildIcons == nullptr)
+ return nullptr;
+
+ // no way to merge
+ if (GetExtraIconByName(name) != nullptr)
+ return nullptr;
+
+ ptrW tszDesc(mir_a2u(description));
+
+ BaseExtraIcon *extra = new CallbackExtraIcon(name, tszDesc, descIcon, RebuildIcons, ApplyIcon, OnClick, onClickParam);
+ extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress(RebuildIcons));
+ EI_PostCreate(extra, flags);
+ return extra;
+}
+
+EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterIcolib(const char *name, const char *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags)
+{
+ if (IsEmpty(name) || IsEmpty(description))
+ return nullptr;
+
+ ptrW tszDesc(mir_a2u(description));
+
+ BaseExtraIcon *extra = GetExtraIconByName(name);
+ if (extra != nullptr) {
+ if (extra->getType() != EXTRAICON_TYPE_ICOLIB)
+ return nullptr;
+
+ // Found one, now merge it
+ if (descIcon)
+ extra->setDescIcon(descIcon);
+
+ if (OnClick != nullptr)
+ extra->setOnClick(OnClick, onClickParam);
+
+ if (extra->getSlot() > 0) {
+ if (clistRebuildAlreadyCalled)
+ extra->rebuildIcons();
+ if (clistApplyAlreadyCalled)
+ extra->applyIcons();
+ }
+ }
+ else {
+ extra = new IcolibExtraIcon(name, tszDesc, descIcon, OnClick, onClickParam);
+ extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress((void*)name));
+ EI_PostCreate(extra, flags);
+ }
+
+ return extra;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) ExtraIcon_SetIcon(HANDLE hExtraIcon, MCONTACT hContact, HANDLE hImage)
+{
+ if (hExtraIcon == nullptr || hContact == 0)
+ return -1;
+
+ BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon);
+ if (extra == nullptr)
+ return -1;
+
+ if (extra->getParent())
+ return extra->getParent()->internalSetIcon(extra, hContact, hImage, false);
+
+ return extra->setIcon(hContact, hImage);
+}
+
+MIR_APP_DLL(int) ExtraIcon_SetIconByName(HANDLE hExtraIcon, MCONTACT hContact, const char *icoName)
+{
+ if (hExtraIcon == nullptr || hContact == 0)
+ return -1;
+
+ BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon);
+ if (extra == nullptr)
+ return -1;
+
+ if (extra->getParent())
+ return extra->getParent()->internalSetIcon(extra, hContact, (HANDLE)icoName, true);
+
+ return extra->setIconByName(hContact, icoName);
+}
+
+MIR_APP_DLL(int) ExtraIcon_Clear(HANDLE hExtraIcon, MCONTACT hContact)
+{
+ if (hExtraIcon == nullptr || hContact == 0)
+ return -1;
+
+ BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon);
+ if (extra == nullptr)
+ return -1;
+
+ if (extra->getParent())
+ return extra->getParent()->internalSetIcon(extra, hContact, nullptr, false);
+
+ return extra->setIcon(hContact, nullptr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static IconItem iconList[] =
+{
+ { LPGEN("Chat activity"), "ChatActivity", IDI_CHAT },
+ { LPGEN("Mute chat"), "ChatMute", IDI_OFF },
+ { LPGEN("Male"), "gender_male", IDI_MALE },
+ { LPGEN("Female"), "gender_female", IDI_FEMALE },
+ { LPGEN("Database"), "database", IDI_DATABASE },
+};
+
+void LoadExtraIconsModule()
+{
+ // Events
+ hEventExtraClick = CreateHookableEvent(ME_CLIST_EXTRA_CLICK);
+ hEventExtraImageApplying = CreateHookableEvent(ME_CLIST_EXTRA_IMAGE_APPLY);
+ hEventExtraImageListRebuilding = CreateHookableEvent(ME_CLIST_EXTRA_LIST_REBUILD);
+
+ // Icons
+ g_plugin.registerIcon(LPGEN("Contact list"), iconList);
+
+ // Hooks
+ HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+
+ HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ClistExtraListRebuild);
+ HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ClistExtraImageApply);
+ HookEvent(ME_CLIST_EXTRA_CLICK, ClistExtraClick);
+
+ DefaultExtraIcons_Load();
+}
+
+void UnloadExtraIconsModule(void)
+{
+ for (auto &extra : extraIconsBySlot)
+ if (extra->getType() == EXTRAICON_TYPE_GROUP)
+ delete extra;
+
+ for (auto &it : registeredExtraIcons)
+ delete it;
+
+ if (hExtraImageList) {
+ ImageList_Destroy(hExtraImageList);
+ hExtraImageList = nullptr;
+ }
+}
diff --git a/src/mir_app/src/encrypt.cpp b/src/mir_app/src/encrypt.cpp index 79298a9221..d08d8e24c6 100644 --- a/src/mir_app/src/encrypt.cpp +++ b/src/mir_app/src/encrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/encrypt.h b/src/mir_app/src/encrypt.h index bcb37cd799..034e38804e 100644 --- a/src/mir_app/src/encrypt.h +++ b/src/mir_app/src/encrypt.h @@ -1,47 +1,47 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -struct MCryptoProvider : public CRYPTO_PROVIDER -{ - MCryptoProvider(const CRYPTO_PROVIDER *pProvider) - { - memcpy(this, pProvider, sizeof(*pProvider)); - pszName = mir_strdup(pszName); - if (dwFlags & CPF_UNICODE) - szDescr.w = mir_wstrdup(TranslateW_LP(pProvider->szDescr.w, pProvider->pPlugin)); - else - szDescr.w = mir_a2u(TranslateA_LP(pProvider->szDescr.a, pProvider->pPlugin)); - } - - ~MCryptoProvider() - { - mir_free(pszName); - mir_free(szDescr.w); - } -}; - -extern OBJLIST<MCryptoProvider> arCryptoProviders; - -void InitCryptMenuItem(CMenuItem &mi); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+struct MCryptoProvider : public CRYPTO_PROVIDER
+{
+ MCryptoProvider(const CRYPTO_PROVIDER *pProvider)
+ {
+ memcpy(this, pProvider, sizeof(*pProvider));
+ pszName = mir_strdup(pszName);
+ if (dwFlags & CPF_UNICODE)
+ szDescr.w = mir_wstrdup(TranslateW_LP(pProvider->szDescr.w, pProvider->pPlugin));
+ else
+ szDescr.w = mir_a2u(TranslateA_LP(pProvider->szDescr.a, pProvider->pPlugin));
+ }
+
+ ~MCryptoProvider()
+ {
+ mir_free(pszName);
+ mir_free(szDescr.w);
+ }
+};
+
+extern OBJLIST<MCryptoProvider> arCryptoProviders;
+
+void InitCryptMenuItem(CMenuItem &mi);
diff --git a/src/mir_app/src/enterstring.cpp b/src/mir_app/src/enterstring.cpp index adfed7e805..69897231c1 100644 --- a/src/mir_app/src/enterstring.cpp +++ b/src/mir_app/src/enterstring.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/extracticon.cpp b/src/mir_app/src/extracticon.cpp index 5bd7a1cd76..415eaee2f1 100644 --- a/src/mir_app/src/extracticon.cpp +++ b/src/mir_app/src/extracticon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/extraicons.h b/src/mir_app/src/extraicons.h index d0b4b8a711..b22075a467 100644 --- a/src/mir_app/src/extraicons.h +++ b/src/mir_app/src/extraicons.h @@ -1,7 +1,7 @@ /*
Copyright (C) 2009 Ricardo Pescuma Domenecci
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/src/mir_app/src/filter.cpp b/src/mir_app/src/filter.cpp index 8e6af687b5..27bf9cd3b0 100644 --- a/src/mir_app/src/filter.cpp +++ b/src/mir_app/src/filter.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/filter.h b/src/mir_app/src/filter.h index a583915eba..8c8964bfa1 100644 --- a/src/mir_app/src/filter.h +++ b/src/mir_app/src/filter.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/findadd.cpp b/src/mir_app/src/findadd.cpp index 88850481d8..bd7613b444 100644 --- a/src/mir_app/src/findadd.cpp +++ b/src/mir_app/src/findadd.cpp @@ -1,1035 +1,1035 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "findadd.h" - -#define TIMERID_THROBBER 111 - -#define HM_SEARCHACK (WM_USER+10) -#define M_SETGROUPVISIBILITIES (WM_USER+11) - -static HWND hwndFindAdd = nullptr; -static HANDLE hHookModulesLoaded = nullptr; -static HGENMENU hMainMenuItem = nullptr; -static int OnSystemModulesLoaded(WPARAM wParam, LPARAM lParam); - -static int FindAddDlgResizer(HWND, LPARAM lParam, UTILRESIZECONTROL *urc) -{ - static int y, nextY, oldTop; - FindAddDlgData *dat = (FindAddDlgData*)lParam; - - switch (urc->wId) { - case IDC_RESULTS: - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - - case IDOK: - dat->minDlgHeight = nextY + urc->rcItem.bottom - urc->rcItem.top; - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_ADD: - case IDC_MOREOPTIONS: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - - case IDC_STATUSBAR: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - - case IDC_PROTOIDGROUP: //the resize is always processed in template order - nextY = y = urc->rcItem.top; - if (dat->showProtoId) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - break; - - case IDC_EMAILGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showEmail) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_NAMEGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showName) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_ADVANCEDGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showAdvanced) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_TINYEXTENDEDGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showTiny) { - int height = urc->dlgNewSize.cy - y - (urc->dlgOriginalSize.cy - urc->rcItem.bottom); - nextY = y + 200; //min height for custom dialog - urc->rcItem.top = urc->rcItem.bottom - height; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_BYEMAIL: - case IDC_EMAIL: - case IDC_BYNAME: - case IDC_STNAMENICK: - case IDC_STNAMEFIRST: - case IDC_STNAMELAST: - case IDC_NAMENICK: - case IDC_NAMEFIRST: - case IDC_NAMELAST: - case IDC_BYADVANCED: - case IDC_BYCUSTOM: - case IDC_ADVANCED: - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -static void RenderThrobber(HDC hdc, RECT *rcItem, int *throbbing, int *pivot) -{ - InflateRect(rcItem, -1, 0); - int width = rcItem->right - rcItem->left; - int height = rcItem->bottom - rcItem->top; - int height2 = height / 2; - - if (*throbbing) { - /* create memdc */ - HDC hMemDC = CreateCompatibleDC(nullptr); - HBITMAP hBitmap = (HBITMAP)SelectObject(hMemDC, CreateCompatibleBitmap(hdc, width, height)); - /* flush it */ - RECT rc; - rc.left = rc.top = 0; - rc.right = width; - rc.bottom = height; - HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); - FillRect(hMemDC, &rc, hBr); - DeleteObject(hBr); - /* set up the pen */ - HPEN hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 4, GetSysColor(COLOR_BTNSHADOW))); - /* draw everything before the pivot */ - int x = *pivot; - while (x > (-height)) { - MoveToEx(hMemDC, x + height2, 0, nullptr); - LineTo(hMemDC, x - height2, height); - x -= 12; - } - - /* draw everything after the pivot */ - x = *pivot; - while (x < width + height) { - MoveToEx(hMemDC, x + height2, 0, nullptr); - LineTo(hMemDC, x - height2, height); - x += 12; - } - - /* move the pivot */ - *pivot += 2; - /* reset the pivot point if it gets past the rect */ - if (*pivot > width) *pivot = 0; - /* put back the old pen and delete the new one */ - DeleteObject(SelectObject(hMemDC, hPen)); - /* cap the top and bottom */ - hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE))); - MoveToEx(hMemDC, 0, 0, nullptr); - LineTo(hMemDC, width, 0); - MoveToEx(hMemDC, 0, height - 1, nullptr); - LineTo(hMemDC, width, height - 1); - /* select in the old pen and delete the new pen */ - DeleteObject(SelectObject(hMemDC, hPen)); - /* paint to screen */ - BitBlt(hdc, rcItem->left, rcItem->top, width, height, hMemDC, 0, 0, SRCCOPY); - /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */ - hBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); - DeleteObject(hBitmap); - DeleteDC(hMemDC); - } - else { - /* just flush the DC */ - HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); - FillRect(hdc, rcItem, hBr); - DeleteObject(hBr); - } -} - -static void StartThrobber(HWND hwndDlg, FindAddDlgData *dat) -{ - dat->throbbing = 1; - SetTimer(hwndDlg, TIMERID_THROBBER, 25, nullptr); -} - -static void StopThrobber(HWND hwndDlg, FindAddDlgData *dat) -{ - KillTimer(hwndDlg, TIMERID_THROBBER); - dat->throbbing = 0; - dat->pivot = 0; - InvalidateRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), nullptr, FALSE); -} - -static LRESULT CALLBACK AdvancedSearchDlgSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_COMMAND) { - HWND parentHwnd = GetParent(hwndDlg); - switch (LOWORD(wParam)) { - case IDOK: - SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDOK)); - SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); - break; - - case IDCANCEL: - CheckDlgButton(parentHwnd, IDC_ADVANCED, BST_UNCHECKED); - SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDC_ADVANCED, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDC_ADVANCED)); - SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); - break; - } - } - return mir_callNextSubclass(hwndDlg, AdvancedSearchDlgSubclassProc, msg, wParam, lParam); -} - -static void ShowAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == nullptr) - return; - - if (dat->hwndAdvSearch == nullptr) { - RECT rc; - dat->hwndAdvSearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)hwndDlg); - if (dat->hwndAdvSearch != nullptr) - mir_subclassWindow(dat->hwndAdvSearch, AdvancedSearchDlgSubclassProc); - GetWindowRect(GetDlgItem(hwndDlg, IDC_RESULTS), &rc); - SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - - AnimateWindow(dat->hwndAdvSearch, 150, AW_ACTIVATE | AW_SLIDE | AW_HOR_POSITIVE); - RedrawWindow(dat->hwndAdvSearch, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - - CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_CHECKED); -} - -static void ReposTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - if (dat->hwndTinySearch == nullptr) - return; - - RECT rc; - RECT clientRect; - POINT pt = { 0, 0 }; - GetWindowRect(GetDlgItem(hwndDlg, IDC_TINYEXTENDEDGROUP), &rc); - GetWindowRect(dat->hwndTinySearch, &clientRect); - pt.x = rc.left; - pt.y = rc.top; - ScreenToClient(hwndDlg, &pt); - SetWindowPos(dat->hwndTinySearch, nullptr, pt.x + 5, pt.y + 15, rc.right - rc.left - 10, rc.bottom - rc.top - 30, SWP_NOZORDER); -} - -static void ShowTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == nullptr) - return; - - if (dat->hwndTinySearch == nullptr) { - dat->hwndTinySearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)/*GetDlgItem(*/hwndDlg/*, IDC_TINYEXTENDEDGROUP)*/); - if (dat->hwndTinySearch) - ReposTinySearchDlg(hwndDlg, dat); - else - dat->showTiny = false; - } - ShowWindow(dat->hwndTinySearch, SW_SHOW); -} - -static void HideAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - if (dat->hwndAdvSearch == nullptr) - return; - - AnimateWindow(dat->hwndAdvSearch, 150, AW_HIDE | AW_BLEND); - CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_UNCHECKED); -} - -void EnableResultButtons(HWND hwndDlg, int enable) -{ - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enable); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), enable); -} - -static const int controls[] = { IDC_BYPROTOID, IDC_BYEMAIL, IDC_BYNAME, IDC_BYADVANCED, IDC_BYCUSTOM }; - -static void CheckSearchTypeRadioButton(HWND hwndDlg, int idControl) -{ - for (auto &it : controls) - CheckDlgButton(hwndDlg, it, idControl == it ? BST_CHECKED : BST_UNCHECKED); -} - -#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.") -#define sttErrTitle TranslateT("Search") - -static void SetListItemText(HWND hwndList, int idx, int col, wchar_t *szText) -{ - if (szText == nullptr || *szText == 0) - szText = TranslateT("<not specified>"); - - ListView_SetItemText(hwndList, idx, col, szText); -} - -static wchar_t* sttDecodeString(uint32_t dwFlags, MAllStrings &src) -{ - if (dwFlags & PSR_UNICODE) - return mir_wstrdup(src.w); - - if (dwFlags & PSR_UTF8) - return mir_utf8decodeW(src.a); - - return mir_a2u(src.a); -} - -static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - FindAddDlgData *dat = (FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS); - RECT rc, rc2; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - Window_SetSkinIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER); - dat = (FindAddDlgData*)mir_calloc(sizeof(FindAddDlgData)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - dat->notSearchedYet = 1; - dat->iLastColumnSortIndex = 1; - dat->bSortAscending = 1; - SendDlgItemMessage(hwndDlg, IDC_MOREOPTIONS, BUTTONSETARROW, 1, 0); - SendDlgItemMessage(hwndDlg, IDOK, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Ctrl+Search add contact"), BATF_UNICODE); - - ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); - - GetClientRect(hwndList, &rc); - { - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH; - lvc.pszText = TranslateT("Results"); - lvc.cx = rc.right - 1; - ListView_InsertColumn(hwndList, 0, &lvc); - - LVITEM lvi; - lvi.mask = LVIF_TEXT; - lvi.iItem = 0; - lvi.iSubItem = 0; - lvi.pszText = TranslateT("There are no results to display."); - ListView_InsertItem(hwndList, &lvi); - - // Allocate a reasonable amount of space in the status bar - HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); - SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_GETFONT, 0, 0)); - - SIZE textSize; - GetTextExtentPoint32(hdc, TranslateT("Searching"), (int)mir_wstrlen(TranslateT("Searching")), &textSize); - - int partWidth[3]; - partWidth[0] = textSize.cx; - GetTextExtentPoint32(hdc, L"01234567890123456789", 20, &textSize); - partWidth[0] += textSize.cx; - ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); - partWidth[1] = partWidth[0] + 150; - partWidth[2] = -1; - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETPARTS, _countof(partWidth), (LPARAM)partWidth); - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETTEXT, 1 | SBT_OWNERDRAW, 0); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - - wchar_t *szProto = nullptr; - ptrW tszLast(db_get_wsa(0, "FindAdd", "LastSearched")); - if (tszLast) - szProto = NEWWSTR_ALLOCA(tszLast); - - int index = 0, cbwidth = 0, netProtoCount = 0; - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (caps & PF1_ANYSEARCH) - netProtoCount++; - } - dat->himlComboIcons = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, netProtoCount + 1, netProtoCount + 1); - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_SETIMAGELIST, 0, (LPARAM)dat->himlComboIcons); - - COMBOBOXEXITEM cbei; - cbei.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM; - cbei.iItem = 0; - hdc = GetDC(hwndDlg); - SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, WM_GETFONT, 0, 0)); - if (netProtoCount > 1) { - cbei.pszText = TranslateT("All networks"); - GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); - if (textSize.cx > cbwidth) - cbwidth = textSize.cx; - cbei.iImage = cbei.iSelectedImage = ImageList_AddSkinIcon(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL); - cbei.lParam = 0; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); - cbei.iItem++; - } - - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (!(caps & PF1_ANYSEARCH)) - continue; - - cbei.pszText = pa->tszAccountName; - GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); - if (textSize.cx > cbwidth) - cbwidth = textSize.cx; - - HICON hIcon = (HICON)CallProtoServiceInt(0, pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0); - cbei.iImage = cbei.iSelectedImage = ImageList_AddIcon(dat->himlComboIcons, hIcon); - DestroyIcon(hIcon); - cbei.lParam = (LPARAM)pa->szModuleName; - SendDlgItemMessageA(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); - if (szProto && cbei.pszText && !mir_wstrcmp(szProto, pa->tszAccountName)) - index = cbei.iItem; - cbei.iItem++; - } - cbwidth += 32; - - RECT rect; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rect); - if ((rect.right - rect.left) < cbwidth) - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETDROPPEDWIDTH, cbwidth, 0); - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETCURSEL, index, 0); - - SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); - Utils_RestoreWindowPosition(hwndDlg, 0, "FindAdd", ""); - } - return TRUE; - - case WM_SIZE: - Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FINDADD), FindAddDlgResizer, (LPARAM)dat); - ReposTinySearchDlg(hwndDlg, dat); - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_SIZE, 0, 0); - if (dat->notSearchedYet) { - GetClientRect(hwndList, &rc); - ListView_SetColumnWidth(hwndList, 0, rc.right); - } - __fallthrough; - - case WM_MOVE: - if (dat && dat->hwndAdvSearch) { - GetWindowRect(hwndList, &rc); - SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - break; - - case WM_GETMINMAXINFO: - GetWindowRect(hwndList, &rc); - GetWindowRect(hwndDlg, &rc2); - { - MINMAXINFO *mmi = (MINMAXINFO*)lParam; - mmi->ptMinTrackSize.x = rc.left - rc2.left + 10 + GetSystemMetrics(SM_CXFRAME); - GetClientRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); - mmi->ptMinTrackSize.x += rc.right + 5; - GetClientRect(GetDlgItem(hwndDlg, IDC_ADD), &rc); - mmi->ptMinTrackSize.x += rc.right + 5; - GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); - mmi->ptMinTrackSize.y = dat->minDlgHeight + 20 + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFRAME); - GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); - mmi->ptMinTrackSize.y += rc.bottom; - } - return 0; - - case M_SETGROUPVISIBILITIES: - dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0; - { - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == (char *)CB_ERR) - break; - if (szProto == nullptr) { - for (auto &pa : g_arAccounts) { - if (pa->IsEnabled()) { - uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; - if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; - } - } - } - else { - uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_BASICSEARCH) dat->showProtoId = 1; - if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; - if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; - - if (protoCaps & PF1_EXTSEARCHUI) dat->showAdvanced = 1; - else if (protoCaps & PF1_EXTSEARCH) dat->showTiny = 1; - - if (protoCaps & PF1_USERIDISEMAIL && dat->showProtoId) { dat->showProtoId = 0; dat->showEmail = 1; } - if (dat->showProtoId) { - wchar_t *wszUniqueId = (wchar_t *)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); - if (wszUniqueId) - SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, wszUniqueId); - else - SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, TranslateT("Handle")); - - if (protoCaps & PF1_NUMERICUSERID) - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE) | ES_NUMBER); - else - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE)&~ES_NUMBER); - } - } - - if (dat->showTiny) - ShowTinySearchDlg(hwndDlg, dat); - else if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - -#define en(id, t) ShowWindow( GetDlgItem(hwndDlg, IDC_##id), dat->show##t?SW_SHOW:SW_HIDE) - en(PROTOIDGROUP, ProtoId); en(BYPROTOID, ProtoId); en(PROTOID, ProtoId); - en(EMAILGROUP, Email); en(BYEMAIL, Email); en(EMAIL, Email); - en(NAMEGROUP, Name); en(BYNAME, Name); - en(STNAMENICK, Name); en(NAMENICK, Name); - en(STNAMEFIRST, Name); en(NAMEFIRST, Name); - en(STNAMELAST, Name); en(NAMELAST, Name); - en(ADVANCEDGROUP, Advanced); en(BYADVANCED, Advanced); en(ADVANCED, Advanced); - en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny); -#undef en - int checkmarkVisible = (dat->showAdvanced && IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) || - (dat->showEmail && IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) || - (dat->showTiny && IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) || - (dat->showName && IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) || - (dat->showProtoId && IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)); - if (!checkmarkVisible) { - if (dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); - else if (dat->showEmail) CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); - else if (dat->showName) CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); - else if (dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); - else if (dat->showTiny) CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); - } - - SendMessage(hwndDlg, WM_SIZE, 0, 0); - - MINMAXINFO mmi; - SendMessage(hwndDlg, WM_GETMINMAXINFO, 0, (LPARAM)&mmi); - - GetWindowRect(hwndDlg, &rc); - if (rc.bottom - rc.top < mmi.ptMinTrackSize.y) - SetWindowPos(hwndDlg, nullptr, 0, 0, rc.right - rc.left, mmi.ptMinTrackSize.y, SWP_NOZORDER | SWP_NOMOVE); - } - break; - - case WM_TIMER: - if (wParam == TIMERID_THROBBER) { - int borders[3]; - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETBORDERS, 0, (LPARAM)borders); - - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETRECT, 1, (LPARAM)&rc); - InflateRect(&rc, -borders[2] / 2, -borders[1] / 2); - HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); - RenderThrobber(hdc, &rc, &dat->throbbing, &dat->pivot); - ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); - } - break; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam; - if (dis->CtlID == IDC_STATUSBAR && dis->itemID == 1) { - RenderThrobber(dis->hDC, &dis->rcItem, &dat->throbbing, &dat->pivot); - return TRUE; - } - } - break; - - case WM_NOTIFY: - if (wParam == IDC_RESULTS) { - switch (((LPNMHDR)lParam)->code) { - case LVN_ITEMCHANGED: - { - int count = ListView_GetSelectedCount(hwndList); - if (dat->notSearchedYet) - count = 0; - EnableResultButtons(hwndDlg, count); - } - break; - - case LVN_COLUMNCLICK: - HDITEM hdi; - hdi.mask = HDI_FORMAT; - hdi.fmt = HDF_LEFT | HDF_STRING; - Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); - - LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam; - if (nmlv->iSubItem != dat->iLastColumnSortIndex) { - dat->bSortAscending = TRUE; - dat->iLastColumnSortIndex = nmlv->iSubItem; - } - else dat->bSortAscending = !dat->bSortAscending; - - hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP); - Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); - - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - } - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_PROTOLIST: - if (HIWORD(wParam) == CBN_SELCHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - if (dat->hwndAdvSearch) { - DestroyWindow(dat->hwndAdvSearch); - dat->hwndAdvSearch = nullptr; - } - if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); - } - break; - - case IDC_BYPROTOID: - case IDC_BYEMAIL: - case IDC_BYNAME: - { - int count = ListView_GetSelectedCount(hwndList); - if (dat->notSearchedYet) - count = 0; - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), count); - HideAdvancedSearchDlg(hwndDlg, dat); - } - break; - - case IDC_PROTOID: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); - } - break; - - case IDC_EMAIL: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); - } - break; - - case IDC_NAMENICK: - case IDC_NAMEFIRST: - case IDC_NAMELAST: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); - } - break; - - case IDC_ADVANCED: - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), ListView_GetSelectedCount(hwndList) > 0); - if (IsDlgButtonChecked(hwndDlg, IDC_ADVANCED)) - ShowAdvancedSearchDlg(hwndDlg, dat); - else - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); - break; - - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - - case IDOK: - HideAdvancedSearchDlg(hwndDlg, dat); - if (dat->searchCount) { //cancel search - SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); - if (dat->hResultHook) { UnhookEvent(dat->hResultHook); dat->hResultHook = nullptr; } - if (dat->search) { mir_free(dat->search); dat->search = nullptr; } - dat->searchCount = 0; - StopThrobber(hwndDlg, dat); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - } - else { - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (dat->search) - mir_free(dat->search), dat->search = nullptr; - dat->searchCount = 0; - dat->hResultHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_SEARCHACK); - if (IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndTinySearch); - else if (IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)) { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); - rtrimw(str); - if (str[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_BASICSEARCH, PF1_BASICSEARCH, str); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_EMAIL, str, _countof(str)); - rtrimw(str); - if (str[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYEMAIL, PF1_SEARCHBYEMAIL, str); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) { - wchar_t nick[256], first[256], last[256]; - PROTOSEARCHBYNAME psbn; - GetDlgItemText(hwndDlg, IDC_NAMENICK, nick, _countof(nick)); - GetDlgItemText(hwndDlg, IDC_NAMEFIRST, first, _countof(first)); - GetDlgItemText(hwndDlg, IDC_NAMELAST, last, _countof(last)); - psbn.pszFirstName = first; - psbn.pszLastName = last; - psbn.pszNick = nick; - if (nick[0] == 0 && first[0] == 0 && last[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYNAME, PF1_SEARCHBYNAME, &psbn); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) { - if (dat->hwndAdvSearch == nullptr) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndAdvSearch); - } - - if (dat->searchCount == 0) { - if (dat->hResultHook) { - UnhookEvent(dat->hResultHook); - dat->hResultHook = nullptr; - } - break; - } - - dat->notSearchedYet = 0; - FreeSearchResults(hwndList); - - CreateResultsColumns(hwndList, dat, szProto); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - SetStatusBarResultInfo(hwndDlg); - StartThrobber(hwndDlg, dat); - SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel")); - } - break; - - case IDC_ADD: - if (ListView_GetSelectedCount(hwndList) == 1) { - LVITEM lvi; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED); - ListView_GetItem(hwndList, &lvi); - ListSearchResult *lsr = (ListSearchResult*)lvi.lParam; - Contact::AddBySearch(lsr->szProto, &lsr->psr, hwndDlg); - } - else { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); - if (*rtrimw(str) == 0) - break; - - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - - PROTOSEARCHRESULT psr = { 0 }; - psr.cbSize = sizeof(psr); - psr.flags = PSR_UNICODE; - psr.id.w = str; - Contact::AddBySearch(szProto, &psr, hwndDlg); - } - break; - - case IDC_MOREOPTIONS: - GetWindowRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); - ShowMoreOptionsMenu(hwndDlg, rc.left, rc.bottom); - break; - } - - if (lParam && dat->hwndTinySearch == (HWND)lParam && - HIWORD(wParam) == EN_SETFOCUS && LOWORD(wParam) == 0 && - BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) { - CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); - } - break; - - case WM_CONTEXTMENU: - { - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - - LVHITTESTINFO lvhti; - lvhti.pt = pt; - ScreenToClient(hwndDlg, &pt); - switch (GetDlgCtrlID(ChildWindowFromPoint(hwndDlg, pt))) { - case IDC_RESULTS: - if (dat->notSearchedYet) - return TRUE; - ScreenToClient(hwndList, &lvhti.pt); - if (ListView_HitTest(hwndList, &lvhti) == -1) - break; - ShowMoreOptionsMenu(hwndDlg, (short)LOWORD(lParam), (short)HIWORD(lParam)); - return TRUE; - } - } - break; - - case HM_SEARCHACK: - { - ACKDATA *ack = (ACKDATA*)lParam; - if (ack->type != ACKTYPE_SEARCH) - break; - - int i; - for (i = 0; i < dat->searchCount; i++) - if (dat->search[i].hProcess == ack->hProcess && dat->search[i].hProcess != nullptr && !mir_strcmp(dat->search[i].szProto, ack->szModule)) break; - if (i == dat->searchCount) - break; - - if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) { - dat->searchCount--; - memmove(dat->search + i, dat->search + i + 1, sizeof(struct ProtoSearchInfo)*(dat->searchCount - i)); - if (dat->searchCount == 0) { - mir_free(dat->search); - dat->search = nullptr; - UnhookEvent(dat->hResultHook); - dat->hResultHook = nullptr; - SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); - StopThrobber(hwndDlg, dat); - } - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - } - else if (ack->result == ACKRESULT_SEARCHRESULT && ack->lParam) { - CUSTOMSEARCHRESULTS *csr = (CUSTOMSEARCHRESULTS*)ack->lParam; - dat->bFlexSearchResult = TRUE; - PROTOSEARCHRESULT *psr = &csr->psr; - // check if this is column names data (psr->cbSize == 0) - if (psr->cbSize == 0) { // blob contain info about columns - // first remove all exist items - FreeSearchResults(hwndList); - ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too - - //second remove all columns - while (ListView_DeleteColumn(hwndList, 1)); //will delete fist column till it possible - - // now will add columns and captions; - LVCOLUMN lvc = { 0 }; - lvc.mask = LVCF_TEXT; - for (int iColumn = 0; iColumn < csr->nFieldCount; iColumn++) { - lvc.pszText = TranslateW(csr->pszFields[iColumn]); - ListView_InsertColumn(hwndList, iColumn + 1, &lvc); - } - } - else { // blob contain info about found contacts - ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); - lsr->szProto = ack->szModule; - memcpy(&lsr->psr, psr, psr->cbSize); - - /* Next block is not needed but behavior will be kept */ - lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); - lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); - lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); - lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); - lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); - lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; - - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.lParam = (LPARAM)lsr; - for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { - char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); - if (szComboProto == nullptr) continue; - if (!mir_strcmp(szComboProto, ack->szModule)) { - COMBOBOXEXITEM cbei = { 0 }; - cbei.mask = CBEIF_IMAGE; - cbei.iItem = i; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); - lvi.iImage = cbei.iImage; - } - } - int iItem = ListView_InsertItem(hwndList, &lvi); - for (int col = 0; col < csr->nFieldCount; col++) - SetListItemText(hwndList, iItem, col + 1, csr->pszFields[col]); - - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - iItem = 0; - while (ListView_SetColumnWidth(hwndList, iItem++, LVSCW_AUTOSIZE_USEHEADER)); - SetStatusBarResultInfo(hwndDlg); - } - break; - } - else if (ack->result == ACKRESULT_DATA) { - PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)ack->lParam; - ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); - lsr->szProto = ack->szModule; - - dat->bFlexSearchResult = FALSE; - - memcpy(&lsr->psr, psr, psr->cbSize); - lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); - lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); - lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); - lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); - lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); - lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; - - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.lParam = (LPARAM)lsr; - for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { - char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); - if (szComboProto == nullptr) continue; - if (!mir_strcmp(szComboProto, ack->szModule)) { - COMBOBOXEXITEM cbei = { 0 }; - cbei.mask = CBEIF_IMAGE; - cbei.iItem = i; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); - lvi.iImage = cbei.iImage; - break; - } - } - - int iItem = ListView_InsertItem(hwndList, &lvi); - SetListItemText(hwndList, iItem, 1, lsr->psr.id.w); - SetListItemText(hwndList, iItem, 2, lsr->psr.nick.w); - SetListItemText(hwndList, iItem, 3, lsr->psr.firstName.w); - SetListItemText(hwndList, iItem, 4, lsr->psr.lastName.w); - SetListItemText(hwndList, iItem, 5, lsr->psr.email.w); - SetStatusBarResultInfo(hwndDlg); - } - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - int len = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXTLEN, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - wchar_t *szProto = (wchar_t*)alloca(sizeof(wchar_t)*(len + 1)); - if (szProto != nullptr) { - *szProto = '\0'; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXT, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), (LPARAM)szProto); - db_set_ws(0, "FindAdd", "LastSearched", szProto); - } - - SaveColumnSizes(hwndList); - if (dat->hResultHook != nullptr) - UnhookEvent(dat->hResultHook); - FreeSearchResults(hwndList); - ImageList_Destroy(dat->himlComboIcons); - mir_free(dat->search); - if (dat->hwndAdvSearch) { - DestroyWindow(dat->hwndAdvSearch); - dat->hwndAdvSearch = nullptr; - } - if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - mir_free(dat); - Window_FreeIcon_IcoLib(hwndDlg); - Utils_SaveWindowPosition(hwndDlg, 0, "FindAdd", ""); - break; - } - return FALSE; -} - -static INT_PTR FindAddCommand(WPARAM, LPARAM) -{ - if (IsWindow(hwndFindAdd)) { - ShowWindow(hwndFindAdd, SW_SHOWNORMAL); - SetForegroundWindow(hwndFindAdd); - SetFocus(hwndFindAdd); - } - else { - int netProtoCount = 0; - - // Make sure we have some networks to search on. This is not ideal since - // this check will be repeated every time the dialog is requested, but it - // must be done since this service can be called from other places than the menu. - // One alternative would be to only create the service if we have network - // protocols loaded but that would delay the creation until MODULE_LOADED and - // that is not good either... - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_ANYSEARCH) - netProtoCount++; - } - if (netProtoCount > 0) - hwndFindAdd = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FINDADD), nullptr, DlgProcFindAdd); - } - return 0; -} - -int FindAddPreShutdown(WPARAM, LPARAM) -{ - if (IsWindow(hwndFindAdd)) - DestroyWindow(hwndFindAdd); - hwndFindAdd = nullptr; - return 0; -} - -int LoadFindAddModule(void) -{ - CreateServiceFunction(MS_FINDADD_FINDADD, FindAddCommand); - - HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded); - HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded); - HookEvent(ME_SYSTEM_PRESHUTDOWN, FindAddPreShutdown); - - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x860556b9, 0x1577, 0x4f6f, 0x8c, 0xb0, 0x93, 0x24, 0xa8, 0x2e, 0x20, 0x92); - mi.position = 500020000; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FINDUSER); - mi.name.a = LPGEN("&Find/add contacts..."); - mi.pszService = MS_FINDADD_FINDADD; - hMainMenuItem = Menu_AddMainMenuItem(&mi); - return 0; -} - -static int OnSystemModulesLoaded(WPARAM, LPARAM) -{ - int netProtoCount = 0; - - // Make sure we have some networks to search on. - for (auto &pa : g_arAccounts) { - int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_ANYSEARCH) - netProtoCount++; - } - - Menu_ShowItem(hMainMenuItem, netProtoCount != 0); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "findadd.h"
+
+#define TIMERID_THROBBER 111
+
+#define HM_SEARCHACK (WM_USER+10)
+#define M_SETGROUPVISIBILITIES (WM_USER+11)
+
+static HWND hwndFindAdd = nullptr;
+static HANDLE hHookModulesLoaded = nullptr;
+static HGENMENU hMainMenuItem = nullptr;
+static int OnSystemModulesLoaded(WPARAM wParam, LPARAM lParam);
+
+static int FindAddDlgResizer(HWND, LPARAM lParam, UTILRESIZECONTROL *urc)
+{
+ static int y, nextY, oldTop;
+ FindAddDlgData *dat = (FindAddDlgData*)lParam;
+
+ switch (urc->wId) {
+ case IDC_RESULTS:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDOK:
+ dat->minDlgHeight = nextY + urc->rcItem.bottom - urc->rcItem.top;
+ return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM;
+
+ case IDC_ADD:
+ case IDC_MOREOPTIONS:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM;
+
+ case IDC_STATUSBAR:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+
+ case IDC_PROTOIDGROUP: //the resize is always processed in template order
+ nextY = y = urc->rcItem.top;
+ if (dat->showProtoId) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ break;
+
+ case IDC_EMAILGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showEmail) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_NAMEGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showName) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_ADVANCEDGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showAdvanced) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_TINYEXTENDEDGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showTiny) {
+ int height = urc->dlgNewSize.cy - y - (urc->dlgOriginalSize.cy - urc->rcItem.bottom);
+ nextY = y + 200; //min height for custom dialog
+ urc->rcItem.top = urc->rcItem.bottom - height;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM;
+
+ case IDC_BYEMAIL:
+ case IDC_EMAIL:
+ case IDC_BYNAME:
+ case IDC_STNAMENICK:
+ case IDC_STNAMEFIRST:
+ case IDC_STNAMELAST:
+ case IDC_NAMENICK:
+ case IDC_NAMEFIRST:
+ case IDC_NAMELAST:
+ case IDC_BYADVANCED:
+ case IDC_BYCUSTOM:
+ case IDC_ADVANCED:
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+static void RenderThrobber(HDC hdc, RECT *rcItem, int *throbbing, int *pivot)
+{
+ InflateRect(rcItem, -1, 0);
+ int width = rcItem->right - rcItem->left;
+ int height = rcItem->bottom - rcItem->top;
+ int height2 = height / 2;
+
+ if (*throbbing) {
+ /* create memdc */
+ HDC hMemDC = CreateCompatibleDC(nullptr);
+ HBITMAP hBitmap = (HBITMAP)SelectObject(hMemDC, CreateCompatibleBitmap(hdc, width, height));
+ /* flush it */
+ RECT rc;
+ rc.left = rc.top = 0;
+ rc.right = width;
+ rc.bottom = height;
+ HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE);
+ FillRect(hMemDC, &rc, hBr);
+ DeleteObject(hBr);
+ /* set up the pen */
+ HPEN hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 4, GetSysColor(COLOR_BTNSHADOW)));
+ /* draw everything before the pivot */
+ int x = *pivot;
+ while (x > (-height)) {
+ MoveToEx(hMemDC, x + height2, 0, nullptr);
+ LineTo(hMemDC, x - height2, height);
+ x -= 12;
+ }
+
+ /* draw everything after the pivot */
+ x = *pivot;
+ while (x < width + height) {
+ MoveToEx(hMemDC, x + height2, 0, nullptr);
+ LineTo(hMemDC, x - height2, height);
+ x += 12;
+ }
+
+ /* move the pivot */
+ *pivot += 2;
+ /* reset the pivot point if it gets past the rect */
+ if (*pivot > width) *pivot = 0;
+ /* put back the old pen and delete the new one */
+ DeleteObject(SelectObject(hMemDC, hPen));
+ /* cap the top and bottom */
+ hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE)));
+ MoveToEx(hMemDC, 0, 0, nullptr);
+ LineTo(hMemDC, width, 0);
+ MoveToEx(hMemDC, 0, height - 1, nullptr);
+ LineTo(hMemDC, width, height - 1);
+ /* select in the old pen and delete the new pen */
+ DeleteObject(SelectObject(hMemDC, hPen));
+ /* paint to screen */
+ BitBlt(hdc, rcItem->left, rcItem->top, width, height, hMemDC, 0, 0, SRCCOPY);
+ /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */
+ hBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
+ DeleteObject(hBitmap);
+ DeleteDC(hMemDC);
+ }
+ else {
+ /* just flush the DC */
+ HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE);
+ FillRect(hdc, rcItem, hBr);
+ DeleteObject(hBr);
+ }
+}
+
+static void StartThrobber(HWND hwndDlg, FindAddDlgData *dat)
+{
+ dat->throbbing = 1;
+ SetTimer(hwndDlg, TIMERID_THROBBER, 25, nullptr);
+}
+
+static void StopThrobber(HWND hwndDlg, FindAddDlgData *dat)
+{
+ KillTimer(hwndDlg, TIMERID_THROBBER);
+ dat->throbbing = 0;
+ dat->pivot = 0;
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), nullptr, FALSE);
+}
+
+static LRESULT CALLBACK AdvancedSearchDlgSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_COMMAND) {
+ HWND parentHwnd = GetParent(hwndDlg);
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDOK));
+ SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED));
+ break;
+
+ case IDCANCEL:
+ CheckDlgButton(parentHwnd, IDC_ADVANCED, BST_UNCHECKED);
+ SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDC_ADVANCED, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDC_ADVANCED));
+ SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED));
+ break;
+ }
+ }
+ return mir_callNextSubclass(hwndDlg, AdvancedSearchDlgSubclassProc, msg, wParam, lParam);
+}
+
+static void ShowAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (szProto == nullptr)
+ return;
+
+ if (dat->hwndAdvSearch == nullptr) {
+ RECT rc;
+ dat->hwndAdvSearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)hwndDlg);
+ if (dat->hwndAdvSearch != nullptr)
+ mir_subclassWindow(dat->hwndAdvSearch, AdvancedSearchDlgSubclassProc);
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_RESULTS), &rc);
+ SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+
+ AnimateWindow(dat->hwndAdvSearch, 150, AW_ACTIVATE | AW_SLIDE | AW_HOR_POSITIVE);
+ RedrawWindow(dat->hwndAdvSearch, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+ CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_CHECKED);
+}
+
+static void ReposTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ if (dat->hwndTinySearch == nullptr)
+ return;
+
+ RECT rc;
+ RECT clientRect;
+ POINT pt = { 0, 0 };
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_TINYEXTENDEDGROUP), &rc);
+ GetWindowRect(dat->hwndTinySearch, &clientRect);
+ pt.x = rc.left;
+ pt.y = rc.top;
+ ScreenToClient(hwndDlg, &pt);
+ SetWindowPos(dat->hwndTinySearch, nullptr, pt.x + 5, pt.y + 15, rc.right - rc.left - 10, rc.bottom - rc.top - 30, SWP_NOZORDER);
+}
+
+static void ShowTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (szProto == nullptr)
+ return;
+
+ if (dat->hwndTinySearch == nullptr) {
+ dat->hwndTinySearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)/*GetDlgItem(*/hwndDlg/*, IDC_TINYEXTENDEDGROUP)*/);
+ if (dat->hwndTinySearch)
+ ReposTinySearchDlg(hwndDlg, dat);
+ else
+ dat->showTiny = false;
+ }
+ ShowWindow(dat->hwndTinySearch, SW_SHOW);
+}
+
+static void HideAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ if (dat->hwndAdvSearch == nullptr)
+ return;
+
+ AnimateWindow(dat->hwndAdvSearch, 150, AW_HIDE | AW_BLEND);
+ CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_UNCHECKED);
+}
+
+void EnableResultButtons(HWND hwndDlg, int enable)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enable);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), enable);
+}
+
+static const int controls[] = { IDC_BYPROTOID, IDC_BYEMAIL, IDC_BYNAME, IDC_BYADVANCED, IDC_BYCUSTOM };
+
+static void CheckSearchTypeRadioButton(HWND hwndDlg, int idControl)
+{
+ for (auto &it : controls)
+ CheckDlgButton(hwndDlg, it, idControl == it ? BST_CHECKED : BST_UNCHECKED);
+}
+
+#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.")
+#define sttErrTitle TranslateT("Search")
+
+static void SetListItemText(HWND hwndList, int idx, int col, wchar_t *szText)
+{
+ if (szText == nullptr || *szText == 0)
+ szText = TranslateT("<not specified>");
+
+ ListView_SetItemText(hwndList, idx, col, szText);
+}
+
+static wchar_t* sttDecodeString(uint32_t dwFlags, MAllStrings &src)
+{
+ if (dwFlags & PSR_UNICODE)
+ return mir_wstrdup(src.w);
+
+ if (dwFlags & PSR_UTF8)
+ return mir_utf8decodeW(src.a);
+
+ return mir_a2u(src.a);
+}
+
+static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ FindAddDlgData *dat = (FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS);
+ RECT rc, rc2;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetSkinIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER);
+ dat = (FindAddDlgData*)mir_calloc(sizeof(FindAddDlgData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->notSearchedYet = 1;
+ dat->iLastColumnSortIndex = 1;
+ dat->bSortAscending = 1;
+ SendDlgItemMessage(hwndDlg, IDC_MOREOPTIONS, BUTTONSETARROW, 1, 0);
+ SendDlgItemMessage(hwndDlg, IDOK, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Ctrl+Search add contact"), BATF_UNICODE);
+
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
+
+ GetClientRect(hwndList, &rc);
+ {
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvc.pszText = TranslateT("Results");
+ lvc.cx = rc.right - 1;
+ ListView_InsertColumn(hwndList, 0, &lvc);
+
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = 0;
+ lvi.iSubItem = 0;
+ lvi.pszText = TranslateT("There are no results to display.");
+ ListView_InsertItem(hwndList, &lvi);
+
+ // Allocate a reasonable amount of space in the status bar
+ HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR));
+ SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_GETFONT, 0, 0));
+
+ SIZE textSize;
+ GetTextExtentPoint32(hdc, TranslateT("Searching"), (int)mir_wstrlen(TranslateT("Searching")), &textSize);
+
+ int partWidth[3];
+ partWidth[0] = textSize.cx;
+ GetTextExtentPoint32(hdc, L"01234567890123456789", 20, &textSize);
+ partWidth[0] += textSize.cx;
+ ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc);
+ partWidth[1] = partWidth[0] + 150;
+ partWidth[2] = -1;
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETPARTS, _countof(partWidth), (LPARAM)partWidth);
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETTEXT, 1 | SBT_OWNERDRAW, 0);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+
+ wchar_t *szProto = nullptr;
+ ptrW tszLast(db_get_wsa(0, "FindAdd", "LastSearched"));
+ if (tszLast)
+ szProto = NEWWSTR_ALLOCA(tszLast);
+
+ int index = 0, cbwidth = 0, netProtoCount = 0;
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsEnabled())
+ continue;
+
+ uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (caps & PF1_ANYSEARCH)
+ netProtoCount++;
+ }
+ dat->himlComboIcons = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, netProtoCount + 1, netProtoCount + 1);
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_SETIMAGELIST, 0, (LPARAM)dat->himlComboIcons);
+
+ COMBOBOXEXITEM cbei;
+ cbei.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM;
+ cbei.iItem = 0;
+ hdc = GetDC(hwndDlg);
+ SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, WM_GETFONT, 0, 0));
+ if (netProtoCount > 1) {
+ cbei.pszText = TranslateT("All networks");
+ GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize);
+ if (textSize.cx > cbwidth)
+ cbwidth = textSize.cx;
+ cbei.iImage = cbei.iSelectedImage = ImageList_AddSkinIcon(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL);
+ cbei.lParam = 0;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
+ cbei.iItem++;
+ }
+
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsEnabled())
+ continue;
+
+ uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (!(caps & PF1_ANYSEARCH))
+ continue;
+
+ cbei.pszText = pa->tszAccountName;
+ GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize);
+ if (textSize.cx > cbwidth)
+ cbwidth = textSize.cx;
+
+ HICON hIcon = (HICON)CallProtoServiceInt(0, pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0);
+ cbei.iImage = cbei.iSelectedImage = ImageList_AddIcon(dat->himlComboIcons, hIcon);
+ DestroyIcon(hIcon);
+ cbei.lParam = (LPARAM)pa->szModuleName;
+ SendDlgItemMessageA(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
+ if (szProto && cbei.pszText && !mir_wstrcmp(szProto, pa->tszAccountName))
+ index = cbei.iItem;
+ cbei.iItem++;
+ }
+ cbwidth += 32;
+
+ RECT rect;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rect);
+ if ((rect.right - rect.left) < cbwidth)
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETDROPPEDWIDTH, cbwidth, 0);
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETCURSEL, index, 0);
+
+ SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0);
+ Utils_RestoreWindowPosition(hwndDlg, 0, "FindAdd", "");
+ }
+ return TRUE;
+
+ case WM_SIZE:
+ Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FINDADD), FindAddDlgResizer, (LPARAM)dat);
+ ReposTinySearchDlg(hwndDlg, dat);
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_SIZE, 0, 0);
+ if (dat->notSearchedYet) {
+ GetClientRect(hwndList, &rc);
+ ListView_SetColumnWidth(hwndList, 0, rc.right);
+ }
+ __fallthrough;
+
+ case WM_MOVE:
+ if (dat && dat->hwndAdvSearch) {
+ GetWindowRect(hwndList, &rc);
+ SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ GetWindowRect(hwndList, &rc);
+ GetWindowRect(hwndDlg, &rc2);
+ {
+ MINMAXINFO *mmi = (MINMAXINFO*)lParam;
+ mmi->ptMinTrackSize.x = rc.left - rc2.left + 10 + GetSystemMetrics(SM_CXFRAME);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc);
+ mmi->ptMinTrackSize.x += rc.right + 5;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_ADD), &rc);
+ mmi->ptMinTrackSize.x += rc.right + 5;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc);
+ mmi->ptMinTrackSize.y = dat->minDlgHeight + 20 + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFRAME);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc);
+ mmi->ptMinTrackSize.y += rc.bottom;
+ }
+ return 0;
+
+ case M_SETGROUPVISIBILITIES:
+ dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0;
+ {
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (szProto == (char *)CB_ERR)
+ break;
+ if (szProto == nullptr) {
+ for (auto &pa : g_arAccounts) {
+ if (pa->IsEnabled()) {
+ uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1;
+ if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1;
+ }
+ }
+ }
+ else {
+ uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_BASICSEARCH) dat->showProtoId = 1;
+ if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1;
+ if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1;
+
+ if (protoCaps & PF1_EXTSEARCHUI) dat->showAdvanced = 1;
+ else if (protoCaps & PF1_EXTSEARCH) dat->showTiny = 1;
+
+ if (protoCaps & PF1_USERIDISEMAIL && dat->showProtoId) { dat->showProtoId = 0; dat->showEmail = 1; }
+ if (dat->showProtoId) {
+ wchar_t *wszUniqueId = (wchar_t *)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0);
+ if (wszUniqueId)
+ SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, wszUniqueId);
+ else
+ SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, TranslateT("Handle"));
+
+ if (protoCaps & PF1_NUMERICUSERID)
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE) | ES_NUMBER);
+ else
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE)&~ES_NUMBER);
+ }
+ }
+
+ if (dat->showTiny)
+ ShowTinySearchDlg(hwndDlg, dat);
+ else if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch = nullptr;
+ }
+
+#define en(id, t) ShowWindow( GetDlgItem(hwndDlg, IDC_##id), dat->show##t?SW_SHOW:SW_HIDE)
+ en(PROTOIDGROUP, ProtoId); en(BYPROTOID, ProtoId); en(PROTOID, ProtoId);
+ en(EMAILGROUP, Email); en(BYEMAIL, Email); en(EMAIL, Email);
+ en(NAMEGROUP, Name); en(BYNAME, Name);
+ en(STNAMENICK, Name); en(NAMENICK, Name);
+ en(STNAMEFIRST, Name); en(NAMEFIRST, Name);
+ en(STNAMELAST, Name); en(NAMELAST, Name);
+ en(ADVANCEDGROUP, Advanced); en(BYADVANCED, Advanced); en(ADVANCED, Advanced);
+ en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny);
+#undef en
+ int checkmarkVisible = (dat->showAdvanced && IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) ||
+ (dat->showEmail && IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) ||
+ (dat->showTiny && IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) ||
+ (dat->showName && IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) ||
+ (dat->showProtoId && IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID));
+ if (!checkmarkVisible) {
+ if (dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID);
+ else if (dat->showEmail) CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL);
+ else if (dat->showName) CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME);
+ else if (dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED);
+ else if (dat->showTiny) CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM);
+ }
+
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+
+ MINMAXINFO mmi;
+ SendMessage(hwndDlg, WM_GETMINMAXINFO, 0, (LPARAM)&mmi);
+
+ GetWindowRect(hwndDlg, &rc);
+ if (rc.bottom - rc.top < mmi.ptMinTrackSize.y)
+ SetWindowPos(hwndDlg, nullptr, 0, 0, rc.right - rc.left, mmi.ptMinTrackSize.y, SWP_NOZORDER | SWP_NOMOVE);
+ }
+ break;
+
+ case WM_TIMER:
+ if (wParam == TIMERID_THROBBER) {
+ int borders[3];
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETBORDERS, 0, (LPARAM)borders);
+
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETRECT, 1, (LPARAM)&rc);
+ InflateRect(&rc, -borders[2] / 2, -borders[1] / 2);
+ HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR));
+ RenderThrobber(hdc, &rc, &dat->throbbing, &dat->pivot);
+ ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc);
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam;
+ if (dis->CtlID == IDC_STATUSBAR && dis->itemID == 1) {
+ RenderThrobber(dis->hDC, &dis->rcItem, &dat->throbbing, &dat->pivot);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (wParam == IDC_RESULTS) {
+ switch (((LPNMHDR)lParam)->code) {
+ case LVN_ITEMCHANGED:
+ {
+ int count = ListView_GetSelectedCount(hwndList);
+ if (dat->notSearchedYet)
+ count = 0;
+ EnableResultButtons(hwndDlg, count);
+ }
+ break;
+
+ case LVN_COLUMNCLICK:
+ HDITEM hdi;
+ hdi.mask = HDI_FORMAT;
+ hdi.fmt = HDF_LEFT | HDF_STRING;
+ Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi);
+
+ LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam;
+ if (nmlv->iSubItem != dat->iLastColumnSortIndex) {
+ dat->bSortAscending = TRUE;
+ dat->iLastColumnSortIndex = nmlv->iSubItem;
+ }
+ else dat->bSortAscending = !dat->bSortAscending;
+
+ hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP);
+ Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi);
+
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_PROTOLIST:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ if (dat->hwndAdvSearch) {
+ DestroyWindow(dat->hwndAdvSearch);
+ dat->hwndAdvSearch = nullptr;
+ }
+ if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch = nullptr;
+ }
+ SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0);
+ }
+ break;
+
+ case IDC_BYPROTOID:
+ case IDC_BYEMAIL:
+ case IDC_BYNAME:
+ {
+ int count = ListView_GetSelectedCount(hwndList);
+ if (dat->notSearchedYet)
+ count = 0;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), count);
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ }
+ break;
+
+ case IDC_PROTOID:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID);
+ }
+ break;
+
+ case IDC_EMAIL:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL);
+ }
+ break;
+
+ case IDC_NAMENICK:
+ case IDC_NAMEFIRST:
+ case IDC_NAMELAST:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME);
+ }
+ break;
+
+ case IDC_ADVANCED:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), ListView_GetSelectedCount(hwndList) > 0);
+ if (IsDlgButtonChecked(hwndDlg, IDC_ADVANCED))
+ ShowAdvancedSearchDlg(hwndDlg, dat);
+ else
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED);
+ break;
+
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDOK:
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ if (dat->searchCount) { //cancel search
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search"));
+ if (dat->hResultHook) { UnhookEvent(dat->hResultHook); dat->hResultHook = nullptr; }
+ if (dat->search) { mir_free(dat->search); dat->search = nullptr; }
+ dat->searchCount = 0;
+ StopThrobber(hwndDlg, dat);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+ }
+ else {
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (dat->search)
+ mir_free(dat->search), dat->search = nullptr;
+ dat->searchCount = 0;
+ dat->hResultHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_SEARCHACK);
+ if (IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM))
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndTinySearch);
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)) {
+ wchar_t str[256];
+ GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str));
+ rtrimw(str);
+ if (str[0] == 0)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_BASICSEARCH, PF1_BASICSEARCH, str);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) {
+ wchar_t str[256];
+ GetDlgItemText(hwndDlg, IDC_EMAIL, str, _countof(str));
+ rtrimw(str);
+ if (str[0] == 0)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYEMAIL, PF1_SEARCHBYEMAIL, str);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) {
+ wchar_t nick[256], first[256], last[256];
+ PROTOSEARCHBYNAME psbn;
+ GetDlgItemText(hwndDlg, IDC_NAMENICK, nick, _countof(nick));
+ GetDlgItemText(hwndDlg, IDC_NAMEFIRST, first, _countof(first));
+ GetDlgItemText(hwndDlg, IDC_NAMELAST, last, _countof(last));
+ psbn.pszFirstName = first;
+ psbn.pszLastName = last;
+ psbn.pszNick = nick;
+ if (nick[0] == 0 && first[0] == 0 && last[0] == 0)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYNAME, PF1_SEARCHBYNAME, &psbn);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) {
+ if (dat->hwndAdvSearch == nullptr)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndAdvSearch);
+ }
+
+ if (dat->searchCount == 0) {
+ if (dat->hResultHook) {
+ UnhookEvent(dat->hResultHook);
+ dat->hResultHook = nullptr;
+ }
+ break;
+ }
+
+ dat->notSearchedYet = 0;
+ FreeSearchResults(hwndList);
+
+ CreateResultsColumns(hwndList, dat, szProto);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+ SetStatusBarResultInfo(hwndDlg);
+ StartThrobber(hwndDlg, dat);
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel"));
+ }
+ break;
+
+ case IDC_ADD:
+ if (ListView_GetSelectedCount(hwndList) == 1) {
+ LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED);
+ ListView_GetItem(hwndList, &lvi);
+ ListSearchResult *lsr = (ListSearchResult*)lvi.lParam;
+ Contact::AddBySearch(lsr->szProto, &lsr->psr, hwndDlg);
+ }
+ else {
+ wchar_t str[256];
+ GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str));
+ if (*rtrimw(str) == 0)
+ break;
+
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+
+ PROTOSEARCHRESULT psr = { 0 };
+ psr.cbSize = sizeof(psr);
+ psr.flags = PSR_UNICODE;
+ psr.id.w = str;
+ Contact::AddBySearch(szProto, &psr, hwndDlg);
+ }
+ break;
+
+ case IDC_MOREOPTIONS:
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc);
+ ShowMoreOptionsMenu(hwndDlg, rc.left, rc.bottom);
+ break;
+ }
+
+ if (lParam && dat->hwndTinySearch == (HWND)lParam &&
+ HIWORD(wParam) == EN_SETFOCUS && LOWORD(wParam) == 0 &&
+ BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) {
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM);
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+
+ LVHITTESTINFO lvhti;
+ lvhti.pt = pt;
+ ScreenToClient(hwndDlg, &pt);
+ switch (GetDlgCtrlID(ChildWindowFromPoint(hwndDlg, pt))) {
+ case IDC_RESULTS:
+ if (dat->notSearchedYet)
+ return TRUE;
+ ScreenToClient(hwndList, &lvhti.pt);
+ if (ListView_HitTest(hwndList, &lvhti) == -1)
+ break;
+ ShowMoreOptionsMenu(hwndDlg, (short)LOWORD(lParam), (short)HIWORD(lParam));
+ return TRUE;
+ }
+ }
+ break;
+
+ case HM_SEARCHACK:
+ {
+ ACKDATA *ack = (ACKDATA*)lParam;
+ if (ack->type != ACKTYPE_SEARCH)
+ break;
+
+ int i;
+ for (i = 0; i < dat->searchCount; i++)
+ if (dat->search[i].hProcess == ack->hProcess && dat->search[i].hProcess != nullptr && !mir_strcmp(dat->search[i].szProto, ack->szModule)) break;
+ if (i == dat->searchCount)
+ break;
+
+ if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) {
+ dat->searchCount--;
+ memmove(dat->search + i, dat->search + i + 1, sizeof(struct ProtoSearchInfo)*(dat->searchCount - i));
+ if (dat->searchCount == 0) {
+ mir_free(dat->search);
+ dat->search = nullptr;
+ UnhookEvent(dat->hResultHook);
+ dat->hResultHook = nullptr;
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search"));
+ StopThrobber(hwndDlg, dat);
+ }
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+ }
+ else if (ack->result == ACKRESULT_SEARCHRESULT && ack->lParam) {
+ CUSTOMSEARCHRESULTS *csr = (CUSTOMSEARCHRESULTS*)ack->lParam;
+ dat->bFlexSearchResult = TRUE;
+ PROTOSEARCHRESULT *psr = &csr->psr;
+ // check if this is column names data (psr->cbSize == 0)
+ if (psr->cbSize == 0) { // blob contain info about columns
+ // first remove all exist items
+ FreeSearchResults(hwndList);
+ ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too
+
+ //second remove all columns
+ while (ListView_DeleteColumn(hwndList, 1)); //will delete fist column till it possible
+
+ // now will add columns and captions;
+ LVCOLUMN lvc = { 0 };
+ lvc.mask = LVCF_TEXT;
+ for (int iColumn = 0; iColumn < csr->nFieldCount; iColumn++) {
+ lvc.pszText = TranslateW(csr->pszFields[iColumn]);
+ ListView_InsertColumn(hwndList, iColumn + 1, &lvc);
+ }
+ }
+ else { // blob contain info about found contacts
+ ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize);
+ lsr->szProto = ack->szModule;
+ memcpy(&lsr->psr, psr, psr->cbSize);
+
+ /* Next block is not needed but behavior will be kept */
+ lsr->psr.id.w = sttDecodeString(psr->flags, psr->id);
+ lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick);
+ lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName);
+ lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName);
+ lsr->psr.email.w = sttDecodeString(psr->flags, psr->email);
+ lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE;
+
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.lParam = (LPARAM)lsr;
+ for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) {
+ char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0);
+ if (szComboProto == nullptr) continue;
+ if (!mir_strcmp(szComboProto, ack->szModule)) {
+ COMBOBOXEXITEM cbei = { 0 };
+ cbei.mask = CBEIF_IMAGE;
+ cbei.iItem = i;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei);
+ lvi.iImage = cbei.iImage;
+ }
+ }
+ int iItem = ListView_InsertItem(hwndList, &lvi);
+ for (int col = 0; col < csr->nFieldCount; col++)
+ SetListItemText(hwndList, iItem, col + 1, csr->pszFields[col]);
+
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ iItem = 0;
+ while (ListView_SetColumnWidth(hwndList, iItem++, LVSCW_AUTOSIZE_USEHEADER));
+ SetStatusBarResultInfo(hwndDlg);
+ }
+ break;
+ }
+ else if (ack->result == ACKRESULT_DATA) {
+ PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)ack->lParam;
+ ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize);
+ lsr->szProto = ack->szModule;
+
+ dat->bFlexSearchResult = FALSE;
+
+ memcpy(&lsr->psr, psr, psr->cbSize);
+ lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick);
+ lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName);
+ lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName);
+ lsr->psr.email.w = sttDecodeString(psr->flags, psr->email);
+ lsr->psr.id.w = sttDecodeString(psr->flags, psr->id);
+ lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE;
+
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.lParam = (LPARAM)lsr;
+ for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) {
+ char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0);
+ if (szComboProto == nullptr) continue;
+ if (!mir_strcmp(szComboProto, ack->szModule)) {
+ COMBOBOXEXITEM cbei = { 0 };
+ cbei.mask = CBEIF_IMAGE;
+ cbei.iItem = i;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei);
+ lvi.iImage = cbei.iImage;
+ break;
+ }
+ }
+
+ int iItem = ListView_InsertItem(hwndList, &lvi);
+ SetListItemText(hwndList, iItem, 1, lsr->psr.id.w);
+ SetListItemText(hwndList, iItem, 2, lsr->psr.nick.w);
+ SetListItemText(hwndList, iItem, 3, lsr->psr.firstName.w);
+ SetListItemText(hwndList, iItem, 4, lsr->psr.lastName.w);
+ SetListItemText(hwndList, iItem, 5, lsr->psr.email.w);
+ SetStatusBarResultInfo(hwndDlg);
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ int len = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXTLEN, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ wchar_t *szProto = (wchar_t*)alloca(sizeof(wchar_t)*(len + 1));
+ if (szProto != nullptr) {
+ *szProto = '\0';
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXT, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), (LPARAM)szProto);
+ db_set_ws(0, "FindAdd", "LastSearched", szProto);
+ }
+
+ SaveColumnSizes(hwndList);
+ if (dat->hResultHook != nullptr)
+ UnhookEvent(dat->hResultHook);
+ FreeSearchResults(hwndList);
+ ImageList_Destroy(dat->himlComboIcons);
+ mir_free(dat->search);
+ if (dat->hwndAdvSearch) {
+ DestroyWindow(dat->hwndAdvSearch);
+ dat->hwndAdvSearch = nullptr;
+ }
+ if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch = nullptr;
+ }
+ mir_free(dat);
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Utils_SaveWindowPosition(hwndDlg, 0, "FindAdd", "");
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR FindAddCommand(WPARAM, LPARAM)
+{
+ if (IsWindow(hwndFindAdd)) {
+ ShowWindow(hwndFindAdd, SW_SHOWNORMAL);
+ SetForegroundWindow(hwndFindAdd);
+ SetFocus(hwndFindAdd);
+ }
+ else {
+ int netProtoCount = 0;
+
+ // Make sure we have some networks to search on. This is not ideal since
+ // this check will be repeated every time the dialog is requested, but it
+ // must be done since this service can be called from other places than the menu.
+ // One alternative would be to only create the service if we have network
+ // protocols loaded but that would delay the creation until MODULE_LOADED and
+ // that is not good either...
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsEnabled())
+ continue;
+
+ int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_ANYSEARCH)
+ netProtoCount++;
+ }
+ if (netProtoCount > 0)
+ hwndFindAdd = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FINDADD), nullptr, DlgProcFindAdd);
+ }
+ return 0;
+}
+
+int FindAddPreShutdown(WPARAM, LPARAM)
+{
+ if (IsWindow(hwndFindAdd))
+ DestroyWindow(hwndFindAdd);
+ hwndFindAdd = nullptr;
+ return 0;
+}
+
+int LoadFindAddModule(void)
+{
+ CreateServiceFunction(MS_FINDADD_FINDADD, FindAddCommand);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, FindAddPreShutdown);
+
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x860556b9, 0x1577, 0x4f6f, 0x8c, 0xb0, 0x93, 0x24, 0xa8, 0x2e, 0x20, 0x92);
+ mi.position = 500020000;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FINDUSER);
+ mi.name.a = LPGEN("&Find/add contacts...");
+ mi.pszService = MS_FINDADD_FINDADD;
+ hMainMenuItem = Menu_AddMainMenuItem(&mi);
+ return 0;
+}
+
+static int OnSystemModulesLoaded(WPARAM, LPARAM)
+{
+ int netProtoCount = 0;
+
+ // Make sure we have some networks to search on.
+ for (auto &pa : g_arAccounts) {
+ int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_ANYSEARCH)
+ netProtoCount++;
+ }
+
+ Menu_ShowItem(hMainMenuItem, netProtoCount != 0);
+ return 0;
+}
diff --git a/src/mir_app/src/findadd.h b/src/mir_app/src/findadd.h index 81a546fa1a..e0993d696b 100644 --- a/src/mir_app/src/findadd.h +++ b/src/mir_app/src/findadd.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/genmenu.h b/src/mir_app/src/genmenu.h index 693a5bf9a7..8ed7a83359 100644 --- a/src/mir_app/src/genmenu.h +++ b/src/mir_app/src/genmenu.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/headerbar.cpp b/src/mir_app/src/headerbar.cpp index 319334f7a9..f312b22145 100644 --- a/src/mir_app/src/headerbar.cpp +++ b/src/mir_app/src/headerbar.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
Copyright (c) 2007 Artem Shpynov
all portions of this codebase are copyrighted to the people
diff --git a/src/mir_app/src/help.cpp b/src/mir_app/src/help.cpp index 30bd95bcbf..dca9b784b6 100644 --- a/src/mir_app/src/help.cpp +++ b/src/mir_app/src/help.cpp @@ -1,189 +1,189 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "resource.h" - -static class CAboutDlg *pAboutDialog; - -class CAboutDlg : public CDlgBase -{ - int m_iState = 0; - - CCtrlBase ctrlHeaderBar, ctrlDevelopers, ctrlCredits, ctrlWhiteRect; - CCtrlButton btnLink; - -public: - CAboutDlg() : - CDlgBase(g_plugin, IDD_ABOUT), - btnLink(this, IDC_CONTRIBLINK), - ctrlCredits(this, IDC_CREDITSFILE), - ctrlHeaderBar(this, IDC_HEADERBAR), - ctrlWhiteRect(this, IDC_WHITERECT), - ctrlDevelopers(this, IDC_DEVS) - { - btnLink.OnClick = Callback(this, &CAboutDlg::onClick); - - ctrlCredits.UseSystemColors(); - ctrlWhiteRect.UseSystemColors(); - ctrlDevelopers.UseSystemColors(); - } - - bool OnInitDialog() override - { - ptrW wszCopyright(mir_utf8decodeW(LEGAL_COPYRIGHT)); - if (wszCopyright == nullptr) - wszCopyright = mir_a2u(LEGAL_COPYRIGHT); - ctrlDevelopers.SetText(wszCopyright); - - char productVersion[56]; - Miranda_GetVersionText(productVersion, _countof(productVersion)); - ctrlHeaderBar.SetText(CMStringW(FORMAT, L"Miranda NG\nv%S", productVersion)); - - HRSRC hResInfo = FindResource(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CREDITS), L"TEXT"); - uint32_t ResSize = SizeofResource(g_plugin.getInst(), hResInfo); - HGLOBAL hRes = LoadResource(g_plugin.getInst(), hResInfo); - char *pszMsg = (char*)LockResource(hRes); - if (pszMsg) { - char *pszMsgt = (char*)alloca(ResSize + 1); - memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0; - - ptrW ptszMsg; - if (ResSize >= 3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf') - ptszMsg = mir_utf8decodeW(pszMsgt + 3); - else - ptszMsg = mir_a2u_cp(pszMsgt, 1252); - ctrlCredits.SetText(ptszMsg); - - UnlockResource(pszMsg); - } - FreeResource(hRes); - ctrlCredits.Hide(); - - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_MIRANDA); - return true; - } - - void OnDestroy() override - { - pAboutDialog = nullptr; - Window_FreeIcon_IcoLib(m_hwnd); - } - - void onClick(CCtrlButton*) - { - if (m_iState) { - btnLink.SetText(TranslateT("Credits >")); - ctrlDevelopers.Show(); - ctrlCredits.Hide(); - } - else { - btnLink.SetText(TranslateT("< Copyright")); - ctrlDevelopers.Hide(); - ctrlCredits.Show(); - } - m_iState = !m_iState; - } -}; - -static INT_PTR AboutCommand(WPARAM wParam, LPARAM) -{ - if (pAboutDialog) { - SetForegroundWindow(pAboutDialog->GetHwnd()); - SetFocus(pAboutDialog->GetHwnd()); - } - else { - pAboutDialog = new CAboutDlg(); - pAboutDialog->SetParent((HWND)wParam); - pAboutDialog->Show(); - } - return 0; -} - -static INT_PTR IndexCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://wiki.miranda-ng.org"); - return 0; -} - -static INT_PTR WebsiteCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://miranda-ng.org"); - return 0; -} - -static INT_PTR BugCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://github.com/miranda-ng/miranda-ng/issues/new"); - return 0; -} - -int ShutdownHelpModule(WPARAM, LPARAM) -{ - if (pAboutDialog) - pAboutDialog->Close(); - return 0; -} - -int LoadHelpModule(void) -{ - HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownHelpModule); - - CMenuItem mi(&g_plugin); - mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("&Help"), 2000090000); - Menu_ConfigureItem(mi.root, MCI_OPT_UID, "8824ECA5-6942-46D7-9D07-1BA600E0D02E"); - - SET_UID(mi, 0xf3ebf1fa, 0x587c, 0x494d, 0xbd, 0x33, 0x7f, 0x88, 0xb3, 0x61, 0x1e, 0xd3); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA); - mi.position = 2000090000; - mi.name.a = LPGEN("&About..."); - mi.pszService = "Help/AboutCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, AboutCommand); - - SET_UID(mi, 0x495df66f, 0x844e, 0x479a, 0xaf, 0x21, 0x3e, 0x42, 0xc5, 0x14, 0x7c, 0x7e); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP); - mi.position = -500050000; - mi.name.a = LPGEN("&Support"); - mi.pszService = "Help/IndexCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, IndexCommand); - - SET_UID(mi, 0x15e18b58, 0xec73, 0x45c2, 0xb9, 0xf4, 0x2a, 0xfe, 0xc2, 0xb7, 0xd3, 0x25); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB); - mi.position = 2000050000; - mi.name.a = LPGEN("&Miranda NG homepage"); - mi.pszService = "Help/WebsiteCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, WebsiteCommand); - - SET_UID(mi, 0xe7d0fe8b, 0xfdeb, 0x45b3, 0xba, 0x83, 0x3, 0x1e, 0x15, 0xda, 0x7e, 0x52); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_URL); - mi.position = 2000040000; - mi.name.a = LPGEN("&Report bug"); - mi.pszService = "Help/BugCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, BugCommand); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "resource.h"
+
+static class CAboutDlg *pAboutDialog;
+
+class CAboutDlg : public CDlgBase
+{
+ int m_iState = 0;
+
+ CCtrlBase ctrlHeaderBar, ctrlDevelopers, ctrlCredits, ctrlWhiteRect;
+ CCtrlButton btnLink;
+
+public:
+ CAboutDlg() :
+ CDlgBase(g_plugin, IDD_ABOUT),
+ btnLink(this, IDC_CONTRIBLINK),
+ ctrlCredits(this, IDC_CREDITSFILE),
+ ctrlHeaderBar(this, IDC_HEADERBAR),
+ ctrlWhiteRect(this, IDC_WHITERECT),
+ ctrlDevelopers(this, IDC_DEVS)
+ {
+ btnLink.OnClick = Callback(this, &CAboutDlg::onClick);
+
+ ctrlCredits.UseSystemColors();
+ ctrlWhiteRect.UseSystemColors();
+ ctrlDevelopers.UseSystemColors();
+ }
+
+ bool OnInitDialog() override
+ {
+ ptrW wszCopyright(mir_utf8decodeW(LEGAL_COPYRIGHT));
+ if (wszCopyright == nullptr)
+ wszCopyright = mir_a2u(LEGAL_COPYRIGHT);
+ ctrlDevelopers.SetText(wszCopyright);
+
+ char productVersion[56];
+ Miranda_GetVersionText(productVersion, _countof(productVersion));
+ ctrlHeaderBar.SetText(CMStringW(FORMAT, L"Miranda NG\nv%S", productVersion));
+
+ HRSRC hResInfo = FindResource(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CREDITS), L"TEXT");
+ uint32_t ResSize = SizeofResource(g_plugin.getInst(), hResInfo);
+ HGLOBAL hRes = LoadResource(g_plugin.getInst(), hResInfo);
+ char *pszMsg = (char*)LockResource(hRes);
+ if (pszMsg) {
+ char *pszMsgt = (char*)alloca(ResSize + 1);
+ memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0;
+
+ ptrW ptszMsg;
+ if (ResSize >= 3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf')
+ ptszMsg = mir_utf8decodeW(pszMsgt + 3);
+ else
+ ptszMsg = mir_a2u_cp(pszMsgt, 1252);
+ ctrlCredits.SetText(ptszMsg);
+
+ UnlockResource(pszMsg);
+ }
+ FreeResource(hRes);
+ ctrlCredits.Hide();
+
+ Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_MIRANDA);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pAboutDialog = nullptr;
+ Window_FreeIcon_IcoLib(m_hwnd);
+ }
+
+ void onClick(CCtrlButton*)
+ {
+ if (m_iState) {
+ btnLink.SetText(TranslateT("Credits >"));
+ ctrlDevelopers.Show();
+ ctrlCredits.Hide();
+ }
+ else {
+ btnLink.SetText(TranslateT("< Copyright"));
+ ctrlDevelopers.Hide();
+ ctrlCredits.Show();
+ }
+ m_iState = !m_iState;
+ }
+};
+
+static INT_PTR AboutCommand(WPARAM wParam, LPARAM)
+{
+ if (pAboutDialog) {
+ SetForegroundWindow(pAboutDialog->GetHwnd());
+ SetFocus(pAboutDialog->GetHwnd());
+ }
+ else {
+ pAboutDialog = new CAboutDlg();
+ pAboutDialog->SetParent((HWND)wParam);
+ pAboutDialog->Show();
+ }
+ return 0;
+}
+
+static INT_PTR IndexCommand(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://wiki.miranda-ng.org");
+ return 0;
+}
+
+static INT_PTR WebsiteCommand(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://miranda-ng.org");
+ return 0;
+}
+
+static INT_PTR BugCommand(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://github.com/miranda-ng/miranda-ng/issues/new");
+ return 0;
+}
+
+int ShutdownHelpModule(WPARAM, LPARAM)
+{
+ if (pAboutDialog)
+ pAboutDialog->Close();
+ return 0;
+}
+
+int LoadHelpModule(void)
+{
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownHelpModule);
+
+ CMenuItem mi(&g_plugin);
+ mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("&Help"), 2000090000);
+ Menu_ConfigureItem(mi.root, MCI_OPT_UID, "8824ECA5-6942-46D7-9D07-1BA600E0D02E");
+
+ SET_UID(mi, 0xf3ebf1fa, 0x587c, 0x494d, 0xbd, 0x33, 0x7f, 0x88, 0xb3, 0x61, 0x1e, 0xd3);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.position = 2000090000;
+ mi.name.a = LPGEN("&About...");
+ mi.pszService = "Help/AboutCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, AboutCommand);
+
+ SET_UID(mi, 0x495df66f, 0x844e, 0x479a, 0xaf, 0x21, 0x3e, 0x42, 0xc5, 0x14, 0x7c, 0x7e);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP);
+ mi.position = -500050000;
+ mi.name.a = LPGEN("&Support");
+ mi.pszService = "Help/IndexCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, IndexCommand);
+
+ SET_UID(mi, 0x15e18b58, 0xec73, 0x45c2, 0xb9, 0xf4, 0x2a, 0xfe, 0xc2, 0xb7, 0xd3, 0x25);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB);
+ mi.position = 2000050000;
+ mi.name.a = LPGEN("&Miranda NG homepage");
+ mi.pszService = "Help/WebsiteCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, WebsiteCommand);
+
+ SET_UID(mi, 0xe7d0fe8b, 0xfdeb, 0x45b3, 0xba, 0x83, 0x3, 0x1e, 0x15, 0xda, 0x7e, 0x52);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_URL);
+ mi.position = 2000040000;
+ mi.name.a = LPGEN("&Report bug");
+ mi.pszService = "Help/BugCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, BugCommand);
+ return 0;
+}
diff --git a/src/mir_app/src/hotkey_opts.cpp b/src/mir_app/src/hotkey_opts.cpp index 7221e4df60..04d78d2bf8 100644 --- a/src/mir_app/src/hotkey_opts.cpp +++ b/src/mir_app/src/hotkey_opts.cpp @@ -1,1026 +1,1026 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ -#include "stdafx.h" - -#include <m_hotkeys.h> -#include "skin.h" - -static wchar_t *sttHokeyVkToName(uint16_t vkKey) -{ - static wchar_t buf[256] = {}; - uint32_t code = MapVirtualKey(vkKey, 0) << 16; - - switch (vkKey) { - case 0: - case VK_CONTROL: - case VK_SHIFT: - case VK_MENU: - case VK_LWIN: - case VK_RWIN: - case VK_PAUSE: - case VK_CANCEL: - case VK_NUMLOCK: - case VK_CAPITAL: - case VK_SCROLL: - return L""; - case VK_BROWSER_BACK: - return TranslateT("Browser: Back"); - case VK_BROWSER_FORWARD: - return TranslateT("Browser: Forward"); - case VK_BROWSER_REFRESH: - return TranslateT("Browser: Refresh"); - case VK_BROWSER_STOP: - return TranslateT("Browser: Stop"); - case VK_BROWSER_SEARCH: - return TranslateT("Browser: Search"); - case VK_BROWSER_FAVORITES: - return TranslateT("Browser: Fav"); - case VK_BROWSER_HOME: - return TranslateT("Browser: Home"); - case VK_VOLUME_MUTE: - return TranslateT("Mute"); - case VK_VOLUME_DOWN: - return TranslateT("Vol-"); - case VK_VOLUME_UP: - return TranslateT("Vol+"); - case VK_MEDIA_NEXT_TRACK: - return TranslateT("Media: Next Track"); - case VK_MEDIA_PREV_TRACK: - return TranslateT("Media: Prev. Track"); - case VK_MEDIA_STOP: - return TranslateT("Media: Stop"); - case VK_MEDIA_PLAY_PAUSE: - return TranslateT("Media: Play/Pause"); - case VK_LAUNCH_MAIL: - return TranslateT("Mail"); - case VK_LAUNCH_MEDIA_SELECT: - return TranslateT("Media: Select"); - case VK_LAUNCH_APP1: - return TranslateT("App 1"); - case VK_LAUNCH_APP2: - return TranslateT("App 2"); - - case VK_DIVIDE: - case VK_INSERT: - case VK_HOME: - case VK_PRIOR: - case VK_DELETE: - case VK_END: - case VK_NEXT: - case VK_LEFT: - case VK_RIGHT: - case VK_UP: - case VK_DOWN: - code |= (1UL << 24); - } - - GetKeyNameText(code, buf, 256); - return buf; -} - -void HotkeyToName(wchar_t *buf, int size, uint8_t shift, uint8_t key) -{ - mir_snwprintf(buf, size, L"%s%s%s%s%s", - (shift & HOTKEYF_CONTROL) ? TranslateT("Ctrl + ") : L"", - (shift & HOTKEYF_ALT) ? TranslateT("Alt + ") : L"", - (shift & HOTKEYF_SHIFT) ? TranslateT("Shift + ") : L"", - (shift & HOTKEYF_EXT) ? TranslateT("Win + ") : L"", - sttHokeyVkToName(key)); -} - -/////////////////////////////////////////////////////////////////////////////// -// Hotkey control - -static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (!data) - return 0; - - bool bKeyDown = false; - - switch (msg) { - case HKM_GETHOTKEY: - return data->key ? MAKEWORD(data->key, data->shift) : 0; - - case HKM_SETHOTKEY: - { - wchar_t buf[256] = {}; - data->key = (uint8_t)LOWORD(wParam); - data->shift = (uint8_t)HIWORD(wParam); - HotkeyToName(buf, _countof(buf), data->shift, data->key); - SetWindowText(hwnd, buf); - } - return 0; - - case WM_GETDLGCODE: - return DLGC_WANTALLKEYS; - - case WM_KILLFOCUS: - break; - - case WM_CHAR: - case WM_SYSCHAR: - case WM_PASTE: - case WM_CONTEXTMENU: - return TRUE; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - bKeyDown = true; - - case WM_KEYUP: - case WM_SYSKEYUP: - { - wchar_t buf[256] = {}; - - uint8_t shift = 0; - uint8_t key = wParam; - wchar_t *name = sttHokeyVkToName(key); - if (!*name || !bKeyDown) - key = 0; - - if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL; - if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT; - if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT; - if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT; - - if (bKeyDown || !data->key) { - data->shift = shift; - data->key = key; - } - - HotkeyToName(buf, _countof(buf), data->shift, data->key); - SetWindowText(hwnd, buf); - - if (bKeyDown && data->key) - SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd); - } - return TRUE; - - case WM_DESTROY: - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - mir_free(data); - } - - return mir_callNextSubclass(hwnd, sttHotkeyEditProc, msg, wParam, lParam); -} - -MIR_APP_DLL(void) Hotkey_Subclass(HWND hwnd) -{ - THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData)); - SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data); - mir_subclassWindow(hwnd, sttHotkeyEditProc); -} - -MIR_APP_DLL(void) Hotkey_Unsubclass(HWND hwnd) -{ - THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - mir_free(data); -} - -/////////////////////////////////////////////////////////////////////////////// -// Options - -enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE }; - -static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item) -{ - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.iItem = idx; - - if (!item->rootHotkey) { - lvi.mask = LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = COL_NAME; - lvi.pszText = item->getDescr(); - lvi.iImage = item->OptType; - ListView_SetItem(hwndList, &lvi); - - ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled); - } - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = COL_KEY; - HotkeyToName(buf, _countof(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey)); - lvi.pszText = buf; - ListView_SetItem(hwndList, &lvi); - - if (item->rootHotkey) { - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_TYPE; - lvi.iImage = item->OptType; - ListView_SetItem(hwndList, &lvi); - } - - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_RESET; - lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1; - ListView_SetItem(hwndList, &lvi); - - lvi.mask = LVIF_IMAGE | LVIF_TEXT; - lvi.iSubItem = COL_ADDREMOVE; - if (item->rootHotkey) { - lvi.iImage = 4; - lvi.pszText = TranslateT("Remove shortcut"); - } - else { - lvi.iImage = 3; - lvi.pszText = TranslateT("Add another shortcut"); - } - ListView_SetItem(hwndList, &lvi); -} - -static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item) -{ - item->OptDeleted = true; - ListView_DeleteItem(hwndList, idx); - if (item->rootHotkey) - item->rootHotkey->OptChanged = true; -} - -static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) -{ - wchar_t title1[256] = {}, title2[256] = {}; - THotkeyItem *item1 = nullptr, *item2 = nullptr; - LVITEM lvi = {}; - int res; - - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = lParam1; - lvi.pszText = title1; - lvi.cchTextMax = _countof(title1); - if (ListView_GetItem((HWND)lParamSort, &lvi)) - item1 = (THotkeyItem *)lvi.lParam; - - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = lParam2; - lvi.pszText = title2; - lvi.cchTextMax = _countof(title2); - if (ListView_GetItem((HWND)lParamSort, &lvi)) - item2 = (THotkeyItem *)lvi.lParam; - - if (!item1 && !item2) - return mir_wstrcmp(title1, title2); - - if (!item1 && item2) { - if (res = mir_wstrcmp(title1, item2->getSection())) - return res; - return -1; - } - - if (!item2 && item1) { - if (res = mir_wstrcmp(item1->getSection(), title2)) - return res; - return 1; - } - /* item1 != nullptr && item2 != nullptr */ - - if (res = mir_wstrcmp(item1->getSection(), item2->getSection())) return res; - if (res = mir_wstrcmp(item1->getDescr(), item2->getDescr())) return res; - if (!item1->rootHotkey && item2->rootHotkey) return -1; - if (item1->rootHotkey && !item2->rootHotkey) return 1; - return 0; -} - -static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item) -{ - char buf[256]; - mir_snprintf(buf, "mir_hotkey_%d_%d", g_pid, g_hkid++); - - THotkeyItem *newItem = (THotkeyItem *)mir_calloc(sizeof(THotkeyItem)); - newItem->pPlugin = item->pPlugin; - newItem->pszService = mir_strdup(item->pszService); - newItem->pwszSection = mir_wstrdup(item->pwszSection); - newItem->pwszDescription = mir_wstrdup(item->pwszDescription); - newItem->lParam = item->lParam; - newItem->idHotkey = GlobalAddAtomA(buf); - newItem->rootHotkey = item; - newItem->type = newItem->OptType = item->OptType; - newItem->Enabled = newItem->OptEnabled = newItem->OptNew = true; - hotkeys.insert(newItem); - - SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); - - LVITEM lvi = {}; - lvi.mask |= LVIF_PARAM; - lvi.lParam = (LPARAM)newItem; - sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem); - ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); - - SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); - RedrawWindow(hwndList, nullptr, nullptr, RDW_INVALIDATE); - - item->OptChanged = true; -} - -static void sttOptionsSetChanged(THotkeyItem *item) -{ - item->OptChanged = true; - if (item->rootHotkey) - item->rootHotkey->OptChanged = true; -} - -static void sttOptionsSaveItem(THotkeyItem *item) -{ - char buf[MAXMODULELABELLENGTH]; - - if (item->rootHotkey) return; - if (!item->OptChanged) return; - - item->Hotkey = item->OptHotkey; - item->type = item->OptType; - item->Enabled = item->OptEnabled; - - db_set_w(0, DBMODULENAME, item->pszName, item->Hotkey); - db_set_b(0, DBMODULENAME "Off", item->pszName, (uint8_t)!item->Enabled); - if (item->type != HKT_MANUAL) - db_set_b(0, DBMODULENAME "Types", item->pszName, (uint8_t)item->type); - - item->nSubHotkeys = 0; - for (auto &it : hotkeys) { - if (it->rootHotkey == item) { - it->Hotkey = it->OptHotkey; - it->type = it->OptType; - - mir_snprintf(buf, "%s$%d", item->pszName, item->nSubHotkeys); - db_set_w(0, DBMODULENAME, buf, it->Hotkey); - if (it->type != HKT_MANUAL) - db_set_b(0, DBMODULENAME "Types", buf, (uint8_t)it->type); - - ++item->nSubHotkeys; - } - } - - mir_snprintf(buf, "%s$count", item->pszName); - db_set_dw(0, DBMODULENAME, buf, item->nSubHotkeys); -} - -static void sttBuildHotkeyList(HWND hwndList) -{ - ListView_DeleteAllItems(hwndList); - - int nItems = 0; - THotkeyItem *prevItem = nullptr; - for (auto &item : hotkeys) { - LVITEM lvi = {}; - - if (!item->OptDeleted) { - if (!prevItem || mir_wstrcmp(item->pwszSection, prevItem->pwszSection)) { - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = nItems++; - lvi.iSubItem = 0; - lvi.lParam = 0; - lvi.pszText = item->getSection(); - ListView_InsertItem(hwndList, &lvi); - ListView_SetCheckState(hwndList, lvi.iItem, TRUE); - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - lvi.pszText = item->pwszSection; - ListView_SetItem(hwndList, &lvi); - - lvi.iSubItem = 0; - } - - lvi.mask = LVIF_PARAM | LVIF_INDENT; - lvi.iIndent = 1; - lvi.iItem = nItems++; - lvi.lParam = (LPARAM)item; - ListView_InsertItem(hwndList, &lvi); - sttOptionsSetupItem(hwndList, nItems - 1, item); - } - - prevItem = item; - } - - ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); -} - -static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey) -{ - LVITEM lvi; - THotkeyItem *item; - int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (iItem < 0) - return; - - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - ListView_GetItem(hwndHotkey, &lvi); - - if (item = (THotkeyItem *)lvi.lParam) { - RECT rc; - ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc); - MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2); - SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0); - - SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); - SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW); - RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); - - SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY)); - RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); - } -} - -static void sttOptionsDrawTextChunk(HDC hdc, wchar_t *text, RECT *rc) -{ - DrawText(hdc, text, -1, rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS); - - SIZE sz; - GetTextExtentPoint32(hdc, text, (int)mir_wstrlen(text), &sz); - rc->left += sz.cx; -} - -static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - static bool initialized = false; - static int colWidth = 0; - static uint16_t currentLanguage = 0; - - HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS); - - switch (msg) { - case WM_INITDIALOG: - initialized = false; - - TranslateDialogDefault(hwndDlg); - - Hotkey_Subclass(GetDlgItem(hwndDlg, IDC_HOTKEY)); - { - HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 3, 1); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOWS); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_MIRANDA); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOW); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_ADDCONTACT); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_DELETE); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_UNDO); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPOPEN); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPSHUT); - ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL); - } - ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP); - { - RECT rc; - GetClientRect(hwndHotkey, &rc); - colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3 * g_iIconSX - 5; - - LVCOLUMN lvc; - lvc.mask = LVCF_WIDTH; - lvc.cx = colWidth * 2 / 3; - ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc); - lvc.cx = colWidth / 3; - ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc); - - for (auto &it : hotkeys) { - it->OptChanged = false; - it->OptDeleted = it->OptNew = false; - it->OptEnabled = it->Enabled; - it->OptHotkey = it->Hotkey; - it->OptType = it->type; - } - - currentLanguage = LOWORD(GetKeyboardLayout(0)); - sttBuildHotkeyList(hwndHotkey); - } - SetTimer(hwndDlg, 1024, 1000, nullptr); - initialized = TRUE; - { - /* load group states */ - int count = ListView_GetItemCount(hwndHotkey); - wchar_t buf[128]; - LVITEM lvi = {}; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - lvi.mask = LVIF_PARAM; - lvi.iSubItem = 0; - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) - continue; - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - ListView_GetItem(hwndHotkey, &lvi); - - ListView_SetCheckState(hwndHotkey, lvi.iItem, db_get_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), TRUE)); - } - } - - g_hwndHkOptions = hwndDlg; - break; - - case WM_TIMER: - if (initialized) { - uint16_t newLanguage = LOWORD(GetKeyboardLayout(0)); - if (newLanguage == currentLanguage) - break; - - int count = ListView_GetItemCount(hwndHotkey); - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) - sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam); - } - currentLanguage = newLanguage; - } - break; - - case WM_HOTKEYUNREGISTERED: - { - int count = ListView_GetItemCount(hwndHotkey); - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - ListView_GetItem(hwndHotkey, &lvi); - if (!lvi.lParam) continue; - - if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) { - ListView_DeleteItem(hwndHotkey, lvi.iItem); - --lvi.iItem; - --count; - } - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; - RECT rc = lpdis->rcItem; - int prefix = 65; - int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3; - rc.left += 5; - - HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); - if (lpdis->CtlID == IDC_CANVAS2) { - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc); - - rc.left = prefix; - ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc); - - rc.left = prefix + width; - ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc); - - rc.left = prefix + width * 2; - ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc); - return TRUE; - } - - if (lpdis->CtlID == IDC_CANVAS) { - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc); - - rc.left = prefix; - ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc); - - rc.left = prefix + width * 1; - ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc); - - rc.left = prefix + width * 2; - ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc); - return TRUE; - } - } - break; - - case WM_COMMAND: - if ((LOWORD(wParam) == IDC_HOTKEY) && ((HIWORD(wParam) == EN_KILLFOCUS) || (HIWORD(wParam) == 0))) { - LVITEM lvi; - THotkeyItem *item; - uint16_t wHotkey = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0); - - ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE); - SetFocus(hwndHotkey); - if (!wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0)) - break; - - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (lvi.iItem >= 0) { - ListView_GetItem(hwndHotkey, &lvi); - if (item = (THotkeyItem *)lvi.lParam) { - item->OptHotkey = wHotkey; - - sttOptionsSetupItem(hwndHotkey, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - } - break; - - case WM_CONTEXTMENU: - if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) { - HWND hwndList = (HWND)wParam; - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - LVITEM lvi = {}; - THotkeyItem *item = nullptr; - - lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (lvi.iItem < 0) - return FALSE; - - lvi.mask = LVIF_PARAM; - ListView_GetItem(hwndList, &lvi); - if (!(item = (THotkeyItem *)lvi.lParam)) - return FALSE; - - if (pt.x == -1 && pt.y == -1) { - RECT rc; - ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL); - pt.x = rc.left; - pt.y = rc.bottom; - ClientToScreen(hwndList, &pt); - } - - enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT }; - - MENUITEMINFO mii = {}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE; - mii.fState = MFS_DEFAULT; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, MI_CHANGE, TranslateT("Modify")); - SetMenuItemInfo(hMenu, MI_CHANGE, FALSE, &mii); - if (item->type != HKT_MANUAL) { - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING | - ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0), - (UINT_PTR)MI_SYSTEM, TranslateT("System scope")); - AppendMenu(hMenu, MF_STRING | - ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0), - (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope")); - } - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - if (!item->rootHotkey) - AppendMenu(hMenu, MF_STRING, MI_ADD, TranslateT("Add binding")); - else - AppendMenu(hMenu, MF_STRING, MI_REMOVE, TranslateT("Remove")); - if (item->Hotkey != item->OptHotkey) { - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING, MI_REVERT, TranslateT("Undo")); - } - - switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr)) { - case MI_CHANGE: - sttOptionsStartEdit(hwndDlg, hwndHotkey); - break; - case MI_SYSTEM: - item->OptType = HKT_GLOBAL; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case MI_LOCAL: - item->OptType = HKT_LOCAL; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case MI_ADD: - initialized = false; - sttOptionsAddHotkey(hwndList, item); - initialized = true; - break; - case MI_REMOVE: - sttOptionsDeleteHotkey(hwndList, lvi.iItem, item); - break; - case MI_REVERT: - item->OptHotkey = item->Hotkey; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - break; - } - DestroyMenu(hMenu); - break; - } - break; - - case WM_NOTIFY: - { - LPNMHDR lpnmhdr = (LPNMHDR)lParam; - switch (lpnmhdr->idFrom) { - case 0: - if ((lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET)) - break; - - UnregisterHotkeys(); - - for (auto &p : hotkeys.rev_iter()) - if (p->OptNew && p->OptDeleted || p->rootHotkey && !p->OptHotkey || (lpnmhdr->code == PSN_APPLY) && p->OptDeleted || (lpnmhdr->code == PSN_RESET) && p->OptNew) - FreeHotkey(hotkeys.removeItem(&p)); - - if (lpnmhdr->code == PSN_APPLY) { - LVITEM lvi = {}; - int count = ListView_GetItemCount(hwndHotkey); - - for (auto &it : hotkeys) - sttOptionsSaveItem(it); - - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_RESET; - lvi.iImage = -1; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) - ListView_SetItem(hwndHotkey, &lvi); - } - - RegisterHotkeys(); - - NotifyEventHooks(hEvChanged, 0, 0); - break; - - case IDC_LV_HOTKEYS: - switch (lpnmhdr->code) { - case NM_CLICK: - { - LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam; - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.iItem = lpnmia->iItem; - ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi); - - auto *item = (THotkeyItem *)lvi.lParam; - if (item == nullptr) - break; - - LVHITTESTINFO lvhti = {}; - lvhti.pt = lpnmia->ptAction; - lvhti.iItem = lpnmia->iItem; - lvhti.iSubItem = lpnmia->iSubItem; - ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti); - - if ((!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) || - item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) && - ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) { - item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL; - sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - else if (lpnmia->iSubItem == COL_RESET) { - item->OptHotkey = item->Hotkey; - sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - } - else if (lpnmia->iSubItem == COL_ADDREMOVE) { - if (item->rootHotkey) - sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - else { - initialized = false; - sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item); - initialized = true; - } - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - break; - - case LVN_KEYDOWN: - { - LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam; - if (param->wVKey == VK_SUBTRACT || param->wVKey == VK_LEFT || param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); - if (lvi.iItem < 0) - break; - - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - if (lvi.lParam) - break; - - if (param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE); - } - else { - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE); - } - } - else if (param->wVKey == VK_F2) - sttOptionsStartEdit(hwndDlg, hwndHotkey); - } - break; - - case LVN_ITEMACTIVATE: - { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); - if (lvi.iItem < 0) break; - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - if (lvi.lParam) - sttOptionsStartEdit(hwndDlg, hwndHotkey); - else - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem)); - } - break; - - case LVN_ITEMCHANGED: - if (initialized) { - LPNMLISTVIEW param = (LPNMLISTVIEW)lParam; - THotkeyItem *item = (THotkeyItem *)param->lParam; - if (param->uNewState >> 12 == param->uOldState >> 12) - break; - - if (item && !item->rootHotkey) { - item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0; - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - else if (!item) { - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT; - lvi.iItem = param->iItem; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - if (param->uNewState >> 12 == 1) { - int count = ListView_GetItemCount(lpnmhdr->hwndFrom); - LVITEM lvi2 = {}; - lvi2.mask = LVIF_PARAM; - for (lvi2.iItem = 0; lvi2.iItem < count; ++lvi2.iItem) { - ListView_GetItem(lpnmhdr->hwndFrom, &lvi2); - item = (THotkeyItem *)lvi2.lParam; - if (!item) continue; - if (!mir_wstrcmp(item->getSection(), buf)) { - ListView_DeleteItem(lpnmhdr->hwndFrom, lvi2.iItem); - --lvi2.iItem; - --count; - } - } - } - else if (param->uNewState >> 12 == 2) { - int nItems = ListView_GetItemCount(lpnmhdr->hwndFrom); - initialized = false; - for (auto &it : hotkeys) { - LVITEM lvi2 = {}; - if (it->OptDeleted || mir_wstrcmp(buf, it->getSection())) - continue; - - lvi2.mask = LVIF_PARAM | LVIF_INDENT; - lvi2.iIndent = 1; - lvi2.iItem = nItems++; - lvi2.lParam = (LPARAM)it; - ListView_InsertItem(lpnmhdr->hwndFrom, &lvi2); - sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems - 1, it); - } - ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom); - initialized = TRUE; - } - } - } - break; - - case NM_CUSTOMDRAW: - { - NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *)lParam; - switch (param->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - case CDDS_ITEMPREPAINT: - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW); - return TRUE; - - case CDDS_SUBITEM | CDDS_ITEMPREPAINT: - { - THotkeyItem *item; - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = param->nmcd.dwItemSpec; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - item = (THotkeyItem *)lvi.lParam; - if (!item) { - RECT rc; - HFONT hfnt; - - ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); - FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); - - if (param->iSubItem == 0) { - rc.left += 3; - HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); - ImageList_Draw(hIml, - ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7, - param->nmcd.hdc, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 18; - hfnt = (HFONT)SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0)); - DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); - SelectObject(param->nmcd.hdc, hfnt); - } - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - - if (item->rootHotkey && (param->iSubItem == 0)) { - RECT rc; - ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); - FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - break; - } - } - break; - } - break; - } - } - } - break; - - case WM_DESTROY: - { - int count = ListView_GetItemCount(hwndHotkey); - - g_hwndHkOptions = nullptr; - - KillTimer(hwndDlg, 1024); - - wchar_t buf[128]; - LVITEM lvi = {}; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - lvi.mask = LVIF_PARAM; - lvi.iSubItem = 0; - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) continue; - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - ListView_GetItem(hwndHotkey, &lvi); - - db_set_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), ListView_GetCheckState(hwndHotkey, lvi.iItem)); - } - } - } - - return FALSE; -} - -int HotkeyOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = -180000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS); - odp.szTitle.a = LPGEN("Hotkeys"); - odp.szGroup.a = LPGEN("Customize"); - odp.pfnDlgProc = sttOptionsDlgProc; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+#include <m_hotkeys.h>
+#include "skin.h"
+
+static wchar_t *sttHokeyVkToName(uint16_t vkKey)
+{
+ static wchar_t buf[256] = {};
+ uint32_t code = MapVirtualKey(vkKey, 0) << 16;
+
+ switch (vkKey) {
+ case 0:
+ case VK_CONTROL:
+ case VK_SHIFT:
+ case VK_MENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_PAUSE:
+ case VK_CANCEL:
+ case VK_NUMLOCK:
+ case VK_CAPITAL:
+ case VK_SCROLL:
+ return L"";
+ case VK_BROWSER_BACK:
+ return TranslateT("Browser: Back");
+ case VK_BROWSER_FORWARD:
+ return TranslateT("Browser: Forward");
+ case VK_BROWSER_REFRESH:
+ return TranslateT("Browser: Refresh");
+ case VK_BROWSER_STOP:
+ return TranslateT("Browser: Stop");
+ case VK_BROWSER_SEARCH:
+ return TranslateT("Browser: Search");
+ case VK_BROWSER_FAVORITES:
+ return TranslateT("Browser: Fav");
+ case VK_BROWSER_HOME:
+ return TranslateT("Browser: Home");
+ case VK_VOLUME_MUTE:
+ return TranslateT("Mute");
+ case VK_VOLUME_DOWN:
+ return TranslateT("Vol-");
+ case VK_VOLUME_UP:
+ return TranslateT("Vol+");
+ case VK_MEDIA_NEXT_TRACK:
+ return TranslateT("Media: Next Track");
+ case VK_MEDIA_PREV_TRACK:
+ return TranslateT("Media: Prev. Track");
+ case VK_MEDIA_STOP:
+ return TranslateT("Media: Stop");
+ case VK_MEDIA_PLAY_PAUSE:
+ return TranslateT("Media: Play/Pause");
+ case VK_LAUNCH_MAIL:
+ return TranslateT("Mail");
+ case VK_LAUNCH_MEDIA_SELECT:
+ return TranslateT("Media: Select");
+ case VK_LAUNCH_APP1:
+ return TranslateT("App 1");
+ case VK_LAUNCH_APP2:
+ return TranslateT("App 2");
+
+ case VK_DIVIDE:
+ case VK_INSERT:
+ case VK_HOME:
+ case VK_PRIOR:
+ case VK_DELETE:
+ case VK_END:
+ case VK_NEXT:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ code |= (1UL << 24);
+ }
+
+ GetKeyNameText(code, buf, 256);
+ return buf;
+}
+
+void HotkeyToName(wchar_t *buf, int size, uint8_t shift, uint8_t key)
+{
+ mir_snwprintf(buf, size, L"%s%s%s%s%s",
+ (shift & HOTKEYF_CONTROL) ? TranslateT("Ctrl + ") : L"",
+ (shift & HOTKEYF_ALT) ? TranslateT("Alt + ") : L"",
+ (shift & HOTKEYF_SHIFT) ? TranslateT("Shift + ") : L"",
+ (shift & HOTKEYF_EXT) ? TranslateT("Win + ") : L"",
+ sttHokeyVkToName(key));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Hotkey control
+
+static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!data)
+ return 0;
+
+ bool bKeyDown = false;
+
+ switch (msg) {
+ case HKM_GETHOTKEY:
+ return data->key ? MAKEWORD(data->key, data->shift) : 0;
+
+ case HKM_SETHOTKEY:
+ {
+ wchar_t buf[256] = {};
+ data->key = (uint8_t)LOWORD(wParam);
+ data->shift = (uint8_t)HIWORD(wParam);
+ HotkeyToName(buf, _countof(buf), data->shift, data->key);
+ SetWindowText(hwnd, buf);
+ }
+ return 0;
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ case WM_KILLFOCUS:
+ break;
+
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_PASTE:
+ case WM_CONTEXTMENU:
+ return TRUE;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ bKeyDown = true;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ wchar_t buf[256] = {};
+
+ uint8_t shift = 0;
+ uint8_t key = wParam;
+ wchar_t *name = sttHokeyVkToName(key);
+ if (!*name || !bKeyDown)
+ key = 0;
+
+ if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL;
+ if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT;
+ if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT;
+ if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT;
+
+ if (bKeyDown || !data->key) {
+ data->shift = shift;
+ data->key = key;
+ }
+
+ HotkeyToName(buf, _countof(buf), data->shift, data->key);
+ SetWindowText(hwnd, buf);
+
+ if (bKeyDown && data->key)
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd);
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(data);
+ }
+
+ return mir_callNextSubclass(hwnd, sttHotkeyEditProc, msg, wParam, lParam);
+}
+
+MIR_APP_DLL(void) Hotkey_Subclass(HWND hwnd)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data);
+ mir_subclassWindow(hwnd, sttHotkeyEditProc);
+}
+
+MIR_APP_DLL(void) Hotkey_Unsubclass(HWND hwnd)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Options
+
+enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE };
+
+static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item)
+{
+ wchar_t buf[256];
+ LVITEM lvi = {};
+ lvi.iItem = idx;
+
+ if (!item->rootHotkey) {
+ lvi.mask = LVIF_TEXT | LVIF_IMAGE;
+ lvi.iSubItem = COL_NAME;
+ lvi.pszText = item->getDescr();
+ lvi.iImage = item->OptType;
+ ListView_SetItem(hwndList, &lvi);
+
+ ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled);
+ }
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = COL_KEY;
+ HotkeyToName(buf, _countof(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey));
+ lvi.pszText = buf;
+ ListView_SetItem(hwndList, &lvi);
+
+ if (item->rootHotkey) {
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_TYPE;
+ lvi.iImage = item->OptType;
+ ListView_SetItem(hwndList, &lvi);
+ }
+
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_RESET;
+ lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1;
+ ListView_SetItem(hwndList, &lvi);
+
+ lvi.mask = LVIF_IMAGE | LVIF_TEXT;
+ lvi.iSubItem = COL_ADDREMOVE;
+ if (item->rootHotkey) {
+ lvi.iImage = 4;
+ lvi.pszText = TranslateT("Remove shortcut");
+ }
+ else {
+ lvi.iImage = 3;
+ lvi.pszText = TranslateT("Add another shortcut");
+ }
+ ListView_SetItem(hwndList, &lvi);
+}
+
+static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item)
+{
+ item->OptDeleted = true;
+ ListView_DeleteItem(hwndList, idx);
+ if (item->rootHotkey)
+ item->rootHotkey->OptChanged = true;
+}
+
+static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ wchar_t title1[256] = {}, title2[256] = {};
+ THotkeyItem *item1 = nullptr, *item2 = nullptr;
+ LVITEM lvi = {};
+ int res;
+
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = lParam1;
+ lvi.pszText = title1;
+ lvi.cchTextMax = _countof(title1);
+ if (ListView_GetItem((HWND)lParamSort, &lvi))
+ item1 = (THotkeyItem *)lvi.lParam;
+
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = lParam2;
+ lvi.pszText = title2;
+ lvi.cchTextMax = _countof(title2);
+ if (ListView_GetItem((HWND)lParamSort, &lvi))
+ item2 = (THotkeyItem *)lvi.lParam;
+
+ if (!item1 && !item2)
+ return mir_wstrcmp(title1, title2);
+
+ if (!item1 && item2) {
+ if (res = mir_wstrcmp(title1, item2->getSection()))
+ return res;
+ return -1;
+ }
+
+ if (!item2 && item1) {
+ if (res = mir_wstrcmp(item1->getSection(), title2))
+ return res;
+ return 1;
+ }
+ /* item1 != nullptr && item2 != nullptr */
+
+ if (res = mir_wstrcmp(item1->getSection(), item2->getSection())) return res;
+ if (res = mir_wstrcmp(item1->getDescr(), item2->getDescr())) return res;
+ if (!item1->rootHotkey && item2->rootHotkey) return -1;
+ if (item1->rootHotkey && !item2->rootHotkey) return 1;
+ return 0;
+}
+
+static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item)
+{
+ char buf[256];
+ mir_snprintf(buf, "mir_hotkey_%d_%d", g_pid, g_hkid++);
+
+ THotkeyItem *newItem = (THotkeyItem *)mir_calloc(sizeof(THotkeyItem));
+ newItem->pPlugin = item->pPlugin;
+ newItem->pszService = mir_strdup(item->pszService);
+ newItem->pwszSection = mir_wstrdup(item->pwszSection);
+ newItem->pwszDescription = mir_wstrdup(item->pwszDescription);
+ newItem->lParam = item->lParam;
+ newItem->idHotkey = GlobalAddAtomA(buf);
+ newItem->rootHotkey = item;
+ newItem->type = newItem->OptType = item->OptType;
+ newItem->Enabled = newItem->OptEnabled = newItem->OptNew = true;
+ hotkeys.insert(newItem);
+
+ SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
+
+ LVITEM lvi = {};
+ lvi.mask |= LVIF_PARAM;
+ lvi.lParam = (LPARAM)newItem;
+ sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem);
+ ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList);
+
+ SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(hwndList, nullptr, nullptr, RDW_INVALIDATE);
+
+ item->OptChanged = true;
+}
+
+static void sttOptionsSetChanged(THotkeyItem *item)
+{
+ item->OptChanged = true;
+ if (item->rootHotkey)
+ item->rootHotkey->OptChanged = true;
+}
+
+static void sttOptionsSaveItem(THotkeyItem *item)
+{
+ char buf[MAXMODULELABELLENGTH];
+
+ if (item->rootHotkey) return;
+ if (!item->OptChanged) return;
+
+ item->Hotkey = item->OptHotkey;
+ item->type = item->OptType;
+ item->Enabled = item->OptEnabled;
+
+ db_set_w(0, DBMODULENAME, item->pszName, item->Hotkey);
+ db_set_b(0, DBMODULENAME "Off", item->pszName, (uint8_t)!item->Enabled);
+ if (item->type != HKT_MANUAL)
+ db_set_b(0, DBMODULENAME "Types", item->pszName, (uint8_t)item->type);
+
+ item->nSubHotkeys = 0;
+ for (auto &it : hotkeys) {
+ if (it->rootHotkey == item) {
+ it->Hotkey = it->OptHotkey;
+ it->type = it->OptType;
+
+ mir_snprintf(buf, "%s$%d", item->pszName, item->nSubHotkeys);
+ db_set_w(0, DBMODULENAME, buf, it->Hotkey);
+ if (it->type != HKT_MANUAL)
+ db_set_b(0, DBMODULENAME "Types", buf, (uint8_t)it->type);
+
+ ++item->nSubHotkeys;
+ }
+ }
+
+ mir_snprintf(buf, "%s$count", item->pszName);
+ db_set_dw(0, DBMODULENAME, buf, item->nSubHotkeys);
+}
+
+static void sttBuildHotkeyList(HWND hwndList)
+{
+ ListView_DeleteAllItems(hwndList);
+
+ int nItems = 0;
+ THotkeyItem *prevItem = nullptr;
+ for (auto &item : hotkeys) {
+ LVITEM lvi = {};
+
+ if (!item->OptDeleted) {
+ if (!prevItem || mir_wstrcmp(item->pwszSection, prevItem->pwszSection)) {
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = nItems++;
+ lvi.iSubItem = 0;
+ lvi.lParam = 0;
+ lvi.pszText = item->getSection();
+ ListView_InsertItem(hwndList, &lvi);
+ ListView_SetCheckState(hwndList, lvi.iItem, TRUE);
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ lvi.pszText = item->pwszSection;
+ ListView_SetItem(hwndList, &lvi);
+
+ lvi.iSubItem = 0;
+ }
+
+ lvi.mask = LVIF_PARAM | LVIF_INDENT;
+ lvi.iIndent = 1;
+ lvi.iItem = nItems++;
+ lvi.lParam = (LPARAM)item;
+ ListView_InsertItem(hwndList, &lvi);
+ sttOptionsSetupItem(hwndList, nItems - 1, item);
+ }
+
+ prevItem = item;
+ }
+
+ ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList);
+}
+
+static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey)
+{
+ LVITEM lvi;
+ THotkeyItem *item;
+ int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (iItem < 0)
+ return;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ if (item = (THotkeyItem *)lvi.lParam) {
+ RECT rc;
+ ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc);
+ MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2);
+ SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0);
+
+ SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE);
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY));
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE);
+ }
+}
+
+static void sttOptionsDrawTextChunk(HDC hdc, wchar_t *text, RECT *rc)
+{
+ DrawText(hdc, text, -1, rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS);
+
+ SIZE sz;
+ GetTextExtentPoint32(hdc, text, (int)mir_wstrlen(text), &sz);
+ rc->left += sz.cx;
+}
+
+static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static bool initialized = false;
+ static int colWidth = 0;
+ static uint16_t currentLanguage = 0;
+
+ HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ initialized = false;
+
+ TranslateDialogDefault(hwndDlg);
+
+ Hotkey_Subclass(GetDlgItem(hwndDlg, IDC_HOTKEY));
+ {
+ HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 3, 1);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOWS);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_MIRANDA);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOW);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_ADDCONTACT);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_DELETE);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_UNDO);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPOPEN);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPSHUT);
+ ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL);
+ }
+ ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP);
+ {
+ RECT rc;
+ GetClientRect(hwndHotkey, &rc);
+ colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3 * g_iIconSX - 5;
+
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_WIDTH;
+ lvc.cx = colWidth * 2 / 3;
+ ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc);
+ lvc.cx = g_iIconSX;
+ ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc);
+ lvc.cx = colWidth / 3;
+ ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc);
+ lvc.cx = g_iIconSX;
+ ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc);
+ lvc.cx = g_iIconSX;
+ ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc);
+
+ for (auto &it : hotkeys) {
+ it->OptChanged = false;
+ it->OptDeleted = it->OptNew = false;
+ it->OptEnabled = it->Enabled;
+ it->OptHotkey = it->Hotkey;
+ it->OptType = it->type;
+ }
+
+ currentLanguage = LOWORD(GetKeyboardLayout(0));
+ sttBuildHotkeyList(hwndHotkey);
+ }
+ SetTimer(hwndDlg, 1024, 1000, nullptr);
+ initialized = TRUE;
+ {
+ /* load group states */
+ int count = ListView_GetItemCount(hwndHotkey);
+ wchar_t buf[128];
+ LVITEM lvi = {};
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam)
+ continue;
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ ListView_SetCheckState(hwndHotkey, lvi.iItem, db_get_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), TRUE));
+ }
+ }
+
+ g_hwndHkOptions = hwndDlg;
+ break;
+
+ case WM_TIMER:
+ if (initialized) {
+ uint16_t newLanguage = LOWORD(GetKeyboardLayout(0));
+ if (newLanguage == currentLanguage)
+ break;
+
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam)
+ sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam);
+ }
+ currentLanguage = newLanguage;
+ }
+ break;
+
+ case WM_HOTKEYUNREGISTERED:
+ {
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (!lvi.lParam) continue;
+
+ if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) {
+ ListView_DeleteItem(hwndHotkey, lvi.iItem);
+ --lvi.iItem;
+ --count;
+ }
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ RECT rc = lpdis->rcItem;
+ int prefix = 65;
+ int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3;
+ rc.left += 5;
+
+ HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL);
+ if (lpdis->CtlID == IDC_CANVAS2) {
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc);
+
+ rc.left = prefix;
+ ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc);
+
+ rc.left = prefix + width;
+ ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc);
+
+ rc.left = prefix + width * 2;
+ ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc);
+ return TRUE;
+ }
+
+ if (lpdis->CtlID == IDC_CANVAS) {
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc);
+
+ rc.left = prefix;
+ ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc);
+
+ rc.left = prefix + width * 1;
+ ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc);
+
+ rc.left = prefix + width * 2;
+ ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ if ((LOWORD(wParam) == IDC_HOTKEY) && ((HIWORD(wParam) == EN_KILLFOCUS) || (HIWORD(wParam) == 0))) {
+ LVITEM lvi;
+ THotkeyItem *item;
+ uint16_t wHotkey = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0);
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE);
+ SetFocus(hwndHotkey);
+ if (!wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0))
+ break;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (lvi.iItem >= 0) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (item = (THotkeyItem *)lvi.lParam) {
+ item->OptHotkey = wHotkey;
+
+ sttOptionsSetupItem(hwndHotkey, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) {
+ HWND hwndList = (HWND)wParam;
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ LVITEM lvi = {};
+ THotkeyItem *item = nullptr;
+
+ lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0)
+ return FALSE;
+
+ lvi.mask = LVIF_PARAM;
+ ListView_GetItem(hwndList, &lvi);
+ if (!(item = (THotkeyItem *)lvi.lParam))
+ return FALSE;
+
+ if (pt.x == -1 && pt.y == -1) {
+ RECT rc;
+ ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL);
+ pt.x = rc.left;
+ pt.y = rc.bottom;
+ ClientToScreen(hwndList, &pt);
+ }
+
+ enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT };
+
+ MENUITEMINFO mii = {};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, MI_CHANGE, TranslateT("Modify"));
+ SetMenuItemInfo(hMenu, MI_CHANGE, FALSE, &mii);
+ if (item->type != HKT_MANUAL) {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING |
+ ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0),
+ (UINT_PTR)MI_SYSTEM, TranslateT("System scope"));
+ AppendMenu(hMenu, MF_STRING |
+ ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0),
+ (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope"));
+ }
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ if (!item->rootHotkey)
+ AppendMenu(hMenu, MF_STRING, MI_ADD, TranslateT("Add binding"));
+ else
+ AppendMenu(hMenu, MF_STRING, MI_REMOVE, TranslateT("Remove"));
+ if (item->Hotkey != item->OptHotkey) {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING, MI_REVERT, TranslateT("Undo"));
+ }
+
+ switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr)) {
+ case MI_CHANGE:
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ break;
+ case MI_SYSTEM:
+ item->OptType = HKT_GLOBAL;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case MI_LOCAL:
+ item->OptType = HKT_LOCAL;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case MI_ADD:
+ initialized = false;
+ sttOptionsAddHotkey(hwndList, item);
+ initialized = true;
+ break;
+ case MI_REMOVE:
+ sttOptionsDeleteHotkey(hwndList, lvi.iItem, item);
+ break;
+ case MI_REVERT:
+ item->OptHotkey = item->Hotkey;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ break;
+ }
+ DestroyMenu(hMenu);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+ switch (lpnmhdr->idFrom) {
+ case 0:
+ if ((lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET))
+ break;
+
+ UnregisterHotkeys();
+
+ for (auto &p : hotkeys.rev_iter())
+ if (p->OptNew && p->OptDeleted || p->rootHotkey && !p->OptHotkey || (lpnmhdr->code == PSN_APPLY) && p->OptDeleted || (lpnmhdr->code == PSN_RESET) && p->OptNew)
+ FreeHotkey(hotkeys.removeItem(&p));
+
+ if (lpnmhdr->code == PSN_APPLY) {
+ LVITEM lvi = {};
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ for (auto &it : hotkeys)
+ sttOptionsSaveItem(it);
+
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_RESET;
+ lvi.iImage = -1;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem)
+ ListView_SetItem(hwndHotkey, &lvi);
+ }
+
+ RegisterHotkeys();
+
+ NotifyEventHooks(hEvChanged, 0, 0);
+ break;
+
+ case IDC_LV_HOTKEYS:
+ switch (lpnmhdr->code) {
+ case NM_CLICK:
+ {
+ LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
+
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.iItem = lpnmia->iItem;
+ ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi);
+
+ auto *item = (THotkeyItem *)lvi.lParam;
+ if (item == nullptr)
+ break;
+
+ LVHITTESTINFO lvhti = {};
+ lvhti.pt = lpnmia->ptAction;
+ lvhti.iItem = lpnmia->iItem;
+ lvhti.iSubItem = lpnmia->iSubItem;
+ ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti);
+
+ if ((!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) ||
+ item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) &&
+ ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) {
+ item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL;
+ sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (lpnmia->iSubItem == COL_RESET) {
+ item->OptHotkey = item->Hotkey;
+ sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ }
+ else if (lpnmia->iSubItem == COL_ADDREMOVE) {
+ if (item->rootHotkey)
+ sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ else {
+ initialized = false;
+ sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item);
+ initialized = true;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+
+ case LVN_KEYDOWN:
+ {
+ LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam;
+ if (param->wVKey == VK_SUBTRACT || param->wVKey == VK_LEFT || param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0)
+ break;
+
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+ if (lvi.lParam)
+ break;
+
+ if (param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) {
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE);
+ }
+ else {
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE);
+ }
+ }
+ else if (param->wVKey == VK_F2)
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ }
+ break;
+
+ case LVN_ITEMACTIVATE:
+ {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) break;
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ if (lvi.lParam)
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ else
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem));
+ }
+ break;
+
+ case LVN_ITEMCHANGED:
+ if (initialized) {
+ LPNMLISTVIEW param = (LPNMLISTVIEW)lParam;
+ THotkeyItem *item = (THotkeyItem *)param->lParam;
+ if (param->uNewState >> 12 == param->uOldState >> 12)
+ break;
+
+ if (item && !item->rootHotkey) {
+ item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0;
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (!item) {
+ wchar_t buf[256];
+ LVITEM lvi = {};
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = param->iItem;
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ if (param->uNewState >> 12 == 1) {
+ int count = ListView_GetItemCount(lpnmhdr->hwndFrom);
+ LVITEM lvi2 = {};
+ lvi2.mask = LVIF_PARAM;
+ for (lvi2.iItem = 0; lvi2.iItem < count; ++lvi2.iItem) {
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi2);
+ item = (THotkeyItem *)lvi2.lParam;
+ if (!item) continue;
+ if (!mir_wstrcmp(item->getSection(), buf)) {
+ ListView_DeleteItem(lpnmhdr->hwndFrom, lvi2.iItem);
+ --lvi2.iItem;
+ --count;
+ }
+ }
+ }
+ else if (param->uNewState >> 12 == 2) {
+ int nItems = ListView_GetItemCount(lpnmhdr->hwndFrom);
+ initialized = false;
+ for (auto &it : hotkeys) {
+ LVITEM lvi2 = {};
+ if (it->OptDeleted || mir_wstrcmp(buf, it->getSection()))
+ continue;
+
+ lvi2.mask = LVIF_PARAM | LVIF_INDENT;
+ lvi2.iIndent = 1;
+ lvi2.iItem = nItems++;
+ lvi2.lParam = (LPARAM)it;
+ ListView_InsertItem(lpnmhdr->hwndFrom, &lvi2);
+ sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems - 1, it);
+ }
+ ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom);
+ initialized = TRUE;
+ }
+ }
+ }
+ break;
+
+ case NM_CUSTOMDRAW:
+ {
+ NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *)lParam;
+ switch (param->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW);
+ return TRUE;
+
+ case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
+ {
+ THotkeyItem *item;
+ wchar_t buf[256];
+ LVITEM lvi = {};
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = param->nmcd.dwItemSpec;
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ item = (THotkeyItem *)lvi.lParam;
+ if (!item) {
+ RECT rc;
+ HFONT hfnt;
+
+ ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc);
+ FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+ SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
+
+ if (param->iSubItem == 0) {
+ rc.left += 3;
+ HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL);
+ ImageList_Draw(hIml,
+ ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7,
+ param->nmcd.hdc, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 18;
+ hfnt = (HFONT)SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0));
+ DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+ SelectObject(param->nmcd.hdc, hfnt);
+ }
+
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+
+ if (item->rootHotkey && (param->iSubItem == 0)) {
+ RECT rc;
+ ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc);
+ FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ g_hwndHkOptions = nullptr;
+
+ KillTimer(hwndDlg, 1024);
+
+ wchar_t buf[128];
+ LVITEM lvi = {};
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam) continue;
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ db_set_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), ListView_GetCheckState(hwndHotkey, lvi.iItem));
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+int HotkeyOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = -180000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS);
+ odp.szTitle.a = LPGEN("Hotkeys");
+ odp.szGroup.a = LPGEN("Customize");
+ odp.pfnDlgProc = sttOptionsDlgProc;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/hotkeys.cpp b/src/mir_app/src/hotkeys.cpp index 336bf3602e..a60cf84fe8 100644 --- a/src/mir_app/src/hotkeys.cpp +++ b/src/mir_app/src/hotkeys.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/icolib.cpp b/src/mir_app/src/icolib.cpp index aaccc00fd1..3327151b41 100644 --- a/src/mir_app/src/icolib.cpp +++ b/src/mir_app/src/icolib.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/idle.cpp b/src/mir_app/src/idle.cpp index cb1c771f11..fdf0f7b49b 100644 --- a/src/mir_app/src/idle.cpp +++ b/src/mir_app/src/idle.cpp @@ -1,87 +1,87 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define IDLE_MODULE "Idle" - -static bool bModuleInitialized = false; - -static int g_idleType; -static int g_bIsIdle; - -static HANDLE hIdleEvent; - -MIR_APP_DLL(void) Idle_Enter(int type) -{ - int flags = 0; - - if (db_get_b(0, IDLE_MODULE, "IdlePrivate")) - flags |= IDF_PRIVACY; - - if (!g_bIsIdle && type != -1) { - g_bIsIdle = true; - g_idleType = type; - NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags); - } - - if (g_bIsIdle && type == -1) { - g_bIsIdle = false; - g_idleType = 0; - NotifyEventHooks(hIdleEvent, 0, flags); - } -} - -MIR_APP_DLL(void) Idle_GetInfo(MIRANDA_IDLE_INFO &pInfo) -{ - pInfo.idleTime = db_get_dw(0, IDLE_MODULE, "IdleTime1st"); - pInfo.privacy = db_get_b(0, IDLE_MODULE, "IdlePrivate"); - pInfo.aaStatus = db_get_b(0, IDLE_MODULE, "AAEnable", 1) ? db_get_dw(0, IDLE_MODULE, "AAStatus") : 0; - pInfo.aaLock = db_get_b(0, IDLE_MODULE, "IdleStatusLock"); - pInfo.idlesoundsoff = db_get_b(0, IDLE_MODULE, "IdleSoundsOff"); - pInfo.idleType = g_idleType; -} - -int LoadIdleModule(void) -{ - bModuleInitialized = true; - - hIdleEvent = CreateHookableEvent(ME_IDLE_CHANGED); - - g_idleType = g_bIsIdle = 0; - return 0; -} - -void UnloadIdleModule() -{ - if (!bModuleInitialized) return; - - if (g_bIsIdle) { - NotifyEventHooks(hIdleEvent, 0, 0); - g_bIsIdle = false; - } - - DestroyHookableEvent(hIdleEvent); - hIdleEvent = nullptr; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define IDLE_MODULE "Idle"
+
+static bool bModuleInitialized = false;
+
+static int g_idleType;
+static int g_bIsIdle;
+
+static HANDLE hIdleEvent;
+
+MIR_APP_DLL(void) Idle_Enter(int type)
+{
+ int flags = 0;
+
+ if (db_get_b(0, IDLE_MODULE, "IdlePrivate"))
+ flags |= IDF_PRIVACY;
+
+ if (!g_bIsIdle && type != -1) {
+ g_bIsIdle = true;
+ g_idleType = type;
+ NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags);
+ }
+
+ if (g_bIsIdle && type == -1) {
+ g_bIsIdle = false;
+ g_idleType = 0;
+ NotifyEventHooks(hIdleEvent, 0, flags);
+ }
+}
+
+MIR_APP_DLL(void) Idle_GetInfo(MIRANDA_IDLE_INFO &pInfo)
+{
+ pInfo.idleTime = db_get_dw(0, IDLE_MODULE, "IdleTime1st");
+ pInfo.privacy = db_get_b(0, IDLE_MODULE, "IdlePrivate");
+ pInfo.aaStatus = db_get_b(0, IDLE_MODULE, "AAEnable", 1) ? db_get_dw(0, IDLE_MODULE, "AAStatus") : 0;
+ pInfo.aaLock = db_get_b(0, IDLE_MODULE, "IdleStatusLock");
+ pInfo.idlesoundsoff = db_get_b(0, IDLE_MODULE, "IdleSoundsOff");
+ pInfo.idleType = g_idleType;
+}
+
+int LoadIdleModule(void)
+{
+ bModuleInitialized = true;
+
+ hIdleEvent = CreateHookableEvent(ME_IDLE_CHANGED);
+
+ g_idleType = g_bIsIdle = 0;
+ return 0;
+}
+
+void UnloadIdleModule()
+{
+ if (!bModuleInitialized) return;
+
+ if (g_bIsIdle) {
+ NotifyEventHooks(hIdleEvent, 0, 0);
+ g_bIsIdle = false;
+ }
+
+ DestroyHookableEvent(hIdleEvent);
+ hIdleEvent = nullptr;
+}
diff --git a/src/mir_app/src/ignore.cpp b/src/mir_app/src/ignore.cpp index 4e5eb804cf..df84141dfc 100644 --- a/src/mir_app/src/ignore.cpp +++ b/src/mir_app/src/ignore.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/keyboard.cpp b/src/mir_app/src/keyboard.cpp index dd5643ae7e..87929e756c 100644 --- a/src/mir_app/src/keyboard.cpp +++ b/src/mir_app/src/keyboard.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/langpack.h b/src/mir_app/src/langpack.h index 04a5abd7d1..1683e3e0ac 100644 --- a/src/mir_app/src/langpack.h +++ b/src/mir_app/src/langpack.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/lpopts.cpp b/src/mir_app/src/lpopts.cpp index 3675694ff9..7444c8e1dc 100644 --- a/src/mir_app/src/lpopts.cpp +++ b/src/mir_app/src/lpopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/mdatabasecache.cpp b/src/mir_app/src/mdatabasecache.cpp index 29cc99cfb5..01ef939bd0 100644 --- a/src/mir_app/src/mdatabasecache.cpp +++ b/src/mir_app/src/mdatabasecache.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_clist.cpp b/src/mir_app/src/menu_clist.cpp index 0a545db4a0..bf94b21017 100644 --- a/src/mir_app/src/menu_clist.cpp +++ b/src/mir_app/src/menu_clist.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_frames.cpp b/src/mir_app/src/menu_frames.cpp index 323cd0c5b0..1b64b9bd0b 100644 --- a/src/mir_app/src/menu_frames.cpp +++ b/src/mir_app/src/menu_frames.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_groups.cpp b/src/mir_app/src/menu_groups.cpp index 4b12e7a0f8..3460d68b1d 100644 --- a/src/mir_app/src/menu_groups.cpp +++ b/src/mir_app/src/menu_groups.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_options.cpp b/src/mir_app/src/menu_options.cpp index 648e3bbc4e..2f5456918b 100644 --- a/src/mir_app/src/menu_options.cpp +++ b/src/mir_app/src/menu_options.cpp @@ -1,588 +1,588 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "genmenu.h" -#include "plugins.h" - -#define STR_SEPARATOR L"-----------------------------------" - -extern bool bIconsDisabled; -extern int DefaultImageListColorDepth; -void RebuildProtoMenus(); - -MIR_APP_DLL(void) Menu_SetVisible(TMO_IntMenuItem *pimi, bool bVisible) -{ - if ((pimi = MO_GetIntMenuItem(pimi)) == nullptr) - return; - - char szModule[256], menuItemName[256]; - mir_snprintf(szModule, "%s_Items", pimi->parent->pszName); - bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); - - ptrW wszValue(db_get_wsa(0, szModule, menuItemName, L"1;;;")); - wszValue[0] = bVisible ? '1' : '0'; - db_set_ws(0, szModule, menuItemName, wszValue); - - Menu_ShowItem(pimi, bVisible); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct MenuItemOptData : public MZeroedObject -{ - ~MenuItemOptData() {} - - int pos; - - ptrW name; - ptrW defname; - - bool bShow; - int id; - - TMO_IntMenuItem *pimi; -}; - -static int SortMenuItems(const MenuItemOptData *p1, const MenuItemOptData *p2) -{ - if (p1->pos < p2->pos) return -1; - if (p1->pos > p2->pos) return 1; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CGenMenuOptionsPage : public CDlgBase -{ - int iInitMenuValue; - LIST<TMO_IntMenuItem> m_arDeleted; - - wchar_t idstr[100]; - - void SaveTreeInternal(MenuItemOptData *pParent, HTREEITEM hRootItem, const char *szModule) - { - TVITEMEX tvi; - tvi.hItem = hRootItem; - tvi.cchTextMax = _countof(idstr); - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE | TVIF_IMAGE; - tvi.pszText = idstr; - - int count = 0, customOrder = 0; - int runtimepos = 100; - - char pszParent[33]; - if (pParent == nullptr) - pszParent[0] = 0; - else - bin2hex(&pParent->pimi->mi.uid, sizeof(MUUID), pszParent); - - while (tvi.hItem != nullptr) { - m_menuItems.GetItem(&tvi); - auto *iod = (MenuItemOptData*)tvi.lParam; - if (TMO_IntMenuItem *pimi = iod->pimi) { - if (pimi->mi.uid != miid_last) { - char menuItemName[256]; - bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); - - int visible = tvi.iImage != 0; - wchar_t *ptszCustomName; - if (iod->name != nullptr && iod->defname != nullptr && mir_wstrcmp(iod->name, iod->defname) != 0) - ptszCustomName = iod->name; - else - ptszCustomName = L""; - - CMStringW tszValue(FORMAT, L"%d;%d;%S;%s", visible, runtimepos, pszParent, ptszCustomName); - db_set_ws(0, szModule, menuItemName, tszValue); - - if (pimi->mi.flags & CMIF_CUSTOM) - db_set_s(0, szModule, CMStringA(FORMAT, "Custom%d", customOrder++), menuItemName); - } - - HTREEITEM hChild = m_menuItems.GetChild(tvi.hItem); - if (hChild != nullptr) - SaveTreeInternal(iod, hChild, szModule); - - runtimepos += 100; - } - - if (iod->name && !mir_wstrcmp(iod->name, STR_SEPARATOR) && tvi.iImage) - runtimepos += SEPARATORPOSITIONINTERVAL; - - tvi.hItem = m_menuItems.GetNextSibling(tvi.hItem); - count++; - } - } - - void SaveTree() - { - int MenuObjectId; - if (!GetCurrentMenuObjectID(MenuObjectId)) - return; - - TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); - if (pmo == nullptr) - return; - - char szModule[256]; - mir_snprintf(szModule, "%s_Items", pmo->pszName); - db_delete_module(0, szModule); - SaveTreeInternal(nullptr, m_menuItems.GetRoot(), szModule); - db_set_b(0, szModule, "MenuFormat", 1); - } - - void FreeTreeData() - { - HTREEITEM hItem = m_menuItems.GetRoot(); - while (hItem != nullptr) { - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_menuItems.GetItem(&tvi); - delete (MenuItemOptData *)tvi.lParam; - - tvi.lParam = 0; - m_menuItems.SetItem(&tvi); - - hItem = m_menuItems.GetNextSibling(hItem); - } - } - - void RebuildCurrent() - { - int MenuObjectID; - if (GetCurrentMenuObjectID(MenuObjectID)) - BuildTree(MenuObjectID, true); - } - - void BuildTreeInternal(const char *pszModule, bool bReread, TMO_IntMenuItem *pFirst, HTREEITEM hRoot) - { - LIST<MenuItemOptData> arItems(10, SortMenuItems); - - for (TMO_IntMenuItem *p = pFirst; p != nullptr; p = p->next) { - // filter out items whose presence & position might not be changed - if (p->mi.flags & CMIF_SYSTEM) - continue; - - MenuItemOptData *PD = new MenuItemOptData(); - PD->pimi = p; - PD->defname = mir_wstrdup(GetMenuItemText(p)); - PD->name = mir_wstrdup((bReread && p->ptszCustomName != nullptr) ? p->ptszCustomName : PD->defname); - PD->bShow = (p->mi.flags & CMIF_HIDDEN) == 0; - PD->pos = (bReread) ? p->mi.position : p->originalPosition; - PD->id = p->iCommand; - arItems.insert(PD); - } - - int lastpos = 0; - bool bIsFirst = TRUE; - - TVINSERTSTRUCT tvis; - tvis.hParent = hRoot; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - - for (auto &it : arItems) { - if (it != arItems[0] && it->pos - lastpos >= SEPARATORPOSITIONINTERVAL) { - MenuItemOptData *sep = new MenuItemOptData(); - sep->id = -1; - sep->name = mir_wstrdup(STR_SEPARATOR); - sep->pos = it->pos - 1; - - tvis.item.lParam = (LPARAM)sep; - tvis.item.pszText = sep->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.item.cChildren = 0; - m_menuItems.InsertItem(&tvis); - } - - tvis.item.lParam = (LPARAM)it; - tvis.item.pszText = it->name; - tvis.item.iImage = tvis.item.iSelectedImage = it->bShow; - tvis.item.cChildren = it->pimi->submenu.first != nullptr; - - HTREEITEM hti = m_menuItems.InsertItem(&tvis); - if (bIsFirst) { - if (hRoot == nullptr) - m_menuItems.SelectItem(hti); - bIsFirst = false; - } - - if (it->pimi->submenu.first != nullptr) { - BuildTreeInternal(pszModule, bReread, it->pimi->submenu.first, hti); - m_menuItems.Expand(hti, TVE_EXPAND); - } - - lastpos = it->pos; - } - } - - bool BuildTree(int MenuObjectId, bool bReread) - { - FreeTreeData(); - - TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); - if (pmo == nullptr || pmo->m_items.first == nullptr) - return false; - - char szModule[256]; - mir_snprintf(szModule, "%s_Items", pmo->pszName); - - if (bReread) // no need to reread database on reset - MO_RecursiveWalkMenu(pmo->m_items.first, Menu_LoadFromDatabase, szModule); - - m_menuItems.SetDraw(false); - m_menuItems.DeleteAllItems(); - - BuildTreeInternal(szModule, bReread, pmo->m_items.first, nullptr); - - m_menuItems.SetDraw(true); - - m_warning.Show(!pmo->m_bUseUserDefinedItems); - m_menuItems.Enable(pmo->m_bUseUserDefinedItems); - m_btnInsSeparator.Enable(pmo->m_bUseUserDefinedItems); - m_btnInsMenu.Enable(pmo->m_bUseUserDefinedItems); - return 1; - } - - bool GetCurrentMenuObjectID(int &result) - { - int iItem = m_menuObjects.GetCurSel(); - if (iItem == -1) - return false; - - result = (int)m_menuObjects.GetItemData(iItem); - return true; - } - - CCtrlListBox m_menuObjects; - CCtrlTreeView m_menuItems; - CCtrlCheck m_radio1, m_radio2, m_enableIcons; - CCtrlEdit m_customName, m_service, m_module; - CCtrlButton m_btnInsSeparator, m_btnInsMenu, m_btnReset, m_btnSet, m_btnDefault, m_btnDelete; - CCtrlBase m_warning; - -public: - CGenMenuOptionsPage() : - CDlgBase(g_plugin, IDD_OPT_GENMENU), - m_arDeleted(1), - m_menuItems(this, IDC_MENUITEMS), - m_menuObjects(this, IDC_MENUOBJECTS), - m_radio1(this, IDC_RADIO1), - m_radio2(this, IDC_RADIO2), - m_enableIcons(this, IDC_DISABLEMENUICONS), - m_btnInsSeparator(this, IDC_INSERTSEPARATOR), - m_btnInsMenu(this, IDC_INSERTSUBMENU), - m_btnReset(this, IDC_RESETMENU), - m_btnSet(this, IDC_GENMENU_SET), - m_btnDelete(this, IDC_GENMENU_DELETE), - m_btnDefault(this, IDC_GENMENU_DEFAULT), - m_customName(this, IDC_GENMENU_CUSTOMNAME), - m_service(this, IDC_GENMENU_SERVICE), - m_module(this, IDC_GENMENU_MODULE), - m_warning(this, IDC_NOTSUPPORTWARNING) - { - m_btnSet.OnClick = Callback(this, &CGenMenuOptionsPage::btnSet_Clicked); - m_btnReset.OnClick = Callback(this, &CGenMenuOptionsPage::btnReset_Clicked); - m_btnInsSeparator.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsSep_Clicked); - m_btnInsMenu.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsMenu_Clicked); - m_btnDefault.OnClick = Callback(this, &CGenMenuOptionsPage::btnDefault_Clicked); - m_btnDelete.OnClick = Callback(this, &CGenMenuOptionsPage::btnDelete_Clicked); - - m_menuObjects.OnSelChange = Callback(this, &CGenMenuOptionsPage::onMenuObjectChanged); - - m_menuItems.SetFlags(MTREE_CHECKBOX | MTREE_DND); - m_menuItems.OnSelChanged = Callback(this, &CGenMenuOptionsPage::onMenuItemChanged); - m_menuItems.OnBeginDrag = Callback(this, &CGenMenuOptionsPage::onMenuItemBeginDrag); - - m_customName.SetSilent(); - m_service.SetSilent(); - m_module.SetSilent(); - } - - //---- init dialog ------------------------------------------- - bool OnInitDialog() override - { - iInitMenuValue = db_get_b(0, "CList", "MoveProtoMenus", TRUE); - - if (iInitMenuValue) - m_radio2.SetState(true); - else - m_radio1.SetState(true); - - m_enableIcons.SetState(!bIconsDisabled); - - //---- init menu object list -------------------------------------- - for (auto &p : g_menus) - if (p->id != (int)hStatusMenuObject && p->m_bUseUserDefinedItems) - m_menuObjects.AddString(TranslateW(p->ptszDisplayName), p->id); - - m_menuObjects.SetCurSel(0); - RebuildCurrent(); - return true; - } - - bool OnApply() override - { - bIconsDisabled = m_enableIcons.GetState() == 0; - db_set_b(0, "CList", "DisableMenuIcons", bIconsDisabled); - SaveTree(); - - for (auto &pimi : m_arDeleted) - Menu_RemoveItem(pimi); - - int iNewMenuValue = !m_radio1.GetState(); - if (iNewMenuValue != iInitMenuValue) { - db_set_b(0, "CList", "MoveProtoMenus", iNewMenuValue); - - RebuildProtoMenus(); - iInitMenuValue = iNewMenuValue; - } - RebuildCurrent(); - return true; - } - - void OnDestroy() override - { - FreeTreeData(); - } - - void btnInsSep_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; - tvi.hItem = hti; - if (!m_menuItems.GetItem(&tvi)) - return; - - MenuItemOptData *PD = new MenuItemOptData(); - PD->id = -1; - PD->name = mir_wstrdup(STR_SEPARATOR); - PD->pos = ((MenuItemOptData *)tvi.lParam)->pos - 1; - - TVINSERTSTRUCT tvis = {}; - tvis.item.lParam = (LPARAM)PD; - tvis.item.pszText = PD->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - m_menuItems.InsertItem(&tvis); - - NotifyChange(); - } - - void btnInsMenu_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; - tvi.hItem = hti; - if (!m_menuItems.GetItem(&tvi)) - return; - - MenuItemOptData *curData = (MenuItemOptData*)tvi.lParam; - - TMO_MenuItem mi = {}; - UuidCreate((UUID*)&mi.uid); - mi.flags = CMIF_CUSTOM; - mi.name.a = LPGEN("New submenu"); - mi.position = curData->pos - 1; - TMO_IntMenuItem *pimi = Menu_AddItem(curData->pimi->parent->id, &mi, nullptr); - - MenuItemOptData *PD = new MenuItemOptData(); - PD->id = -1; - PD->name = mir_wstrdup(pimi->mi.name.w); - PD->pos = pimi->mi.position; - PD->pimi = pimi; - - TVINSERTSTRUCT tvis = {}; - tvis.item.lParam = (LPARAM)PD; - tvis.item.pszText = PD->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - m_menuItems.InsertItem(&tvis); - - NotifyChange(); - } - - void btnReset_Clicked(CCtrlButton*) - { - int MenuObjectID; - if (GetCurrentMenuObjectID(MenuObjectID)) { - BuildTree(MenuObjectID, false); - NotifyChange(); - } - } - - void btnDefault_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - iod->name = mir_wstrdup(iod->defname); - m_customName.SetText(iod->defname); - - tvi.mask = TVIF_TEXT; - tvi.pszText = iod->name; - m_menuItems.SetItem(&tvi); - NotifyChange(); - } - - void btnSet_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - iod->name = m_customName.GetText(); - - tvi.mask = TVIF_TEXT; - tvi.pszText = iod->name; - m_menuItems.SetItem(&tvi); - NotifyChange(); - } - - void btnDelete_Clicked(CCtrlButton *) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (!(iod->pimi->mi.flags & CMIF_CUSTOM)) - return; - - if (IDYES == MessageBoxW(m_hwnd, TranslateT("Do you really want to delete this menu item?"), TranslateT("Miranda"), MB_YESNO | MB_ICONQUESTION)) { - m_arDeleted.insert(iod->pimi); - m_menuItems.DeleteItem(hti); - delete iod; - NotifyChange(); - } - } - - void onMenuObjectChanged(void*) - { - m_bInitialized = false; - RebuildCurrent(); - m_bInitialized = true; - } - - void onMenuItemChanged(void*) - { - m_customName.SetTextA(""); - m_service.SetTextA(""); - m_module.SetTextA(""); - - m_btnInsMenu.Disable(); - m_btnDefault.Disable(); - m_btnSet.Disable(); - m_btnDelete.Disable(); - m_customName.Disable(); - - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - if (tvi.lParam == 0) - return; - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - m_customName.SetText(iod->name); - - if (iod->pimi->mi.uid != miid_last) { - char szText[100]; - bin2hex(&iod->pimi->mi.uid, sizeof(iod->pimi->mi.uid), szText); - m_service.SetTextA(szText); - } - - const CMPluginBase *pPlugin = iod->pimi->mi.pPlugin; - m_module.SetTextA(pPlugin == nullptr ? "" : pPlugin->getInfo().shortName); - - m_btnInsMenu.Enable(iod->pimi->mi.root == nullptr); - m_btnDefault.Enable(mir_wstrcmp(iod->name, iod->defname) != 0); - m_btnDelete.Enable(iod->pimi->mi.flags & CMIF_CUSTOM); - m_btnSet.Enable(true); - m_customName.Enable(true); - } - - void onMenuItemBeginDrag(CCtrlTreeView::TEventInfo *evt) - { - MenuItemOptData *p = (MenuItemOptData*)evt->nmtv->itemNew.lParam; - if (p->pimi != nullptr) - if (p->pimi->mi.flags & CMIF_UNMOVABLE) - evt->nmhdr->code = 0; // reject an attempt to change item's position - } -}; - -int GenMenuOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -1000000000; - odp.szTitle.a = LPGEN("Menus"); - odp.szGroup.a = LPGEN("Customize"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CGenMenuOptionsPage(); - g_plugin.addOptions(wParam, &odp); - - return ProtocolOrderOptInit(wParam, 0); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "genmenu.h"
+#include "plugins.h"
+
+#define STR_SEPARATOR L"-----------------------------------"
+
+extern bool bIconsDisabled;
+extern int DefaultImageListColorDepth;
+void RebuildProtoMenus();
+
+MIR_APP_DLL(void) Menu_SetVisible(TMO_IntMenuItem *pimi, bool bVisible)
+{
+ if ((pimi = MO_GetIntMenuItem(pimi)) == nullptr)
+ return;
+
+ char szModule[256], menuItemName[256];
+ mir_snprintf(szModule, "%s_Items", pimi->parent->pszName);
+ bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName);
+
+ ptrW wszValue(db_get_wsa(0, szModule, menuItemName, L"1;;;"));
+ wszValue[0] = bVisible ? '1' : '0';
+ db_set_ws(0, szModule, menuItemName, wszValue);
+
+ Menu_ShowItem(pimi, bVisible);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct MenuItemOptData : public MZeroedObject
+{
+ ~MenuItemOptData() {}
+
+ int pos;
+
+ ptrW name;
+ ptrW defname;
+
+ bool bShow;
+ int id;
+
+ TMO_IntMenuItem *pimi;
+};
+
+static int SortMenuItems(const MenuItemOptData *p1, const MenuItemOptData *p2)
+{
+ if (p1->pos < p2->pos) return -1;
+ if (p1->pos > p2->pos) return 1;
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CGenMenuOptionsPage : public CDlgBase
+{
+ int iInitMenuValue;
+ LIST<TMO_IntMenuItem> m_arDeleted;
+
+ wchar_t idstr[100];
+
+ void SaveTreeInternal(MenuItemOptData *pParent, HTREEITEM hRootItem, const char *szModule)
+ {
+ TVITEMEX tvi;
+ tvi.hItem = hRootItem;
+ tvi.cchTextMax = _countof(idstr);
+ tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE | TVIF_IMAGE;
+ tvi.pszText = idstr;
+
+ int count = 0, customOrder = 0;
+ int runtimepos = 100;
+
+ char pszParent[33];
+ if (pParent == nullptr)
+ pszParent[0] = 0;
+ else
+ bin2hex(&pParent->pimi->mi.uid, sizeof(MUUID), pszParent);
+
+ while (tvi.hItem != nullptr) {
+ m_menuItems.GetItem(&tvi);
+ auto *iod = (MenuItemOptData*)tvi.lParam;
+ if (TMO_IntMenuItem *pimi = iod->pimi) {
+ if (pimi->mi.uid != miid_last) {
+ char menuItemName[256];
+ bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName);
+
+ int visible = tvi.iImage != 0;
+ wchar_t *ptszCustomName;
+ if (iod->name != nullptr && iod->defname != nullptr && mir_wstrcmp(iod->name, iod->defname) != 0)
+ ptszCustomName = iod->name;
+ else
+ ptszCustomName = L"";
+
+ CMStringW tszValue(FORMAT, L"%d;%d;%S;%s", visible, runtimepos, pszParent, ptszCustomName);
+ db_set_ws(0, szModule, menuItemName, tszValue);
+
+ if (pimi->mi.flags & CMIF_CUSTOM)
+ db_set_s(0, szModule, CMStringA(FORMAT, "Custom%d", customOrder++), menuItemName);
+ }
+
+ HTREEITEM hChild = m_menuItems.GetChild(tvi.hItem);
+ if (hChild != nullptr)
+ SaveTreeInternal(iod, hChild, szModule);
+
+ runtimepos += 100;
+ }
+
+ if (iod->name && !mir_wstrcmp(iod->name, STR_SEPARATOR) && tvi.iImage)
+ runtimepos += SEPARATORPOSITIONINTERVAL;
+
+ tvi.hItem = m_menuItems.GetNextSibling(tvi.hItem);
+ count++;
+ }
+ }
+
+ void SaveTree()
+ {
+ int MenuObjectId;
+ if (!GetCurrentMenuObjectID(MenuObjectId))
+ return;
+
+ TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId);
+ if (pmo == nullptr)
+ return;
+
+ char szModule[256];
+ mir_snprintf(szModule, "%s_Items", pmo->pszName);
+ db_delete_module(0, szModule);
+ SaveTreeInternal(nullptr, m_menuItems.GetRoot(), szModule);
+ db_set_b(0, szModule, "MenuFormat", 1);
+ }
+
+ void FreeTreeData()
+ {
+ HTREEITEM hItem = m_menuItems.GetRoot();
+ while (hItem != nullptr) {
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_menuItems.GetItem(&tvi);
+ delete (MenuItemOptData *)tvi.lParam;
+
+ tvi.lParam = 0;
+ m_menuItems.SetItem(&tvi);
+
+ hItem = m_menuItems.GetNextSibling(hItem);
+ }
+ }
+
+ void RebuildCurrent()
+ {
+ int MenuObjectID;
+ if (GetCurrentMenuObjectID(MenuObjectID))
+ BuildTree(MenuObjectID, true);
+ }
+
+ void BuildTreeInternal(const char *pszModule, bool bReread, TMO_IntMenuItem *pFirst, HTREEITEM hRoot)
+ {
+ LIST<MenuItemOptData> arItems(10, SortMenuItems);
+
+ for (TMO_IntMenuItem *p = pFirst; p != nullptr; p = p->next) {
+ // filter out items whose presence & position might not be changed
+ if (p->mi.flags & CMIF_SYSTEM)
+ continue;
+
+ MenuItemOptData *PD = new MenuItemOptData();
+ PD->pimi = p;
+ PD->defname = mir_wstrdup(GetMenuItemText(p));
+ PD->name = mir_wstrdup((bReread && p->ptszCustomName != nullptr) ? p->ptszCustomName : PD->defname);
+ PD->bShow = (p->mi.flags & CMIF_HIDDEN) == 0;
+ PD->pos = (bReread) ? p->mi.position : p->originalPosition;
+ PD->id = p->iCommand;
+ arItems.insert(PD);
+ }
+
+ int lastpos = 0;
+ bool bIsFirst = TRUE;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = hRoot;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+
+ for (auto &it : arItems) {
+ if (it != arItems[0] && it->pos - lastpos >= SEPARATORPOSITIONINTERVAL) {
+ MenuItemOptData *sep = new MenuItemOptData();
+ sep->id = -1;
+ sep->name = mir_wstrdup(STR_SEPARATOR);
+ sep->pos = it->pos - 1;
+
+ tvis.item.lParam = (LPARAM)sep;
+ tvis.item.pszText = sep->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ tvis.item.cChildren = 0;
+ m_menuItems.InsertItem(&tvis);
+ }
+
+ tvis.item.lParam = (LPARAM)it;
+ tvis.item.pszText = it->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = it->bShow;
+ tvis.item.cChildren = it->pimi->submenu.first != nullptr;
+
+ HTREEITEM hti = m_menuItems.InsertItem(&tvis);
+ if (bIsFirst) {
+ if (hRoot == nullptr)
+ m_menuItems.SelectItem(hti);
+ bIsFirst = false;
+ }
+
+ if (it->pimi->submenu.first != nullptr) {
+ BuildTreeInternal(pszModule, bReread, it->pimi->submenu.first, hti);
+ m_menuItems.Expand(hti, TVE_EXPAND);
+ }
+
+ lastpos = it->pos;
+ }
+ }
+
+ bool BuildTree(int MenuObjectId, bool bReread)
+ {
+ FreeTreeData();
+
+ TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId);
+ if (pmo == nullptr || pmo->m_items.first == nullptr)
+ return false;
+
+ char szModule[256];
+ mir_snprintf(szModule, "%s_Items", pmo->pszName);
+
+ if (bReread) // no need to reread database on reset
+ MO_RecursiveWalkMenu(pmo->m_items.first, Menu_LoadFromDatabase, szModule);
+
+ m_menuItems.SetDraw(false);
+ m_menuItems.DeleteAllItems();
+
+ BuildTreeInternal(szModule, bReread, pmo->m_items.first, nullptr);
+
+ m_menuItems.SetDraw(true);
+
+ m_warning.Show(!pmo->m_bUseUserDefinedItems);
+ m_menuItems.Enable(pmo->m_bUseUserDefinedItems);
+ m_btnInsSeparator.Enable(pmo->m_bUseUserDefinedItems);
+ m_btnInsMenu.Enable(pmo->m_bUseUserDefinedItems);
+ return 1;
+ }
+
+ bool GetCurrentMenuObjectID(int &result)
+ {
+ int iItem = m_menuObjects.GetCurSel();
+ if (iItem == -1)
+ return false;
+
+ result = (int)m_menuObjects.GetItemData(iItem);
+ return true;
+ }
+
+ CCtrlListBox m_menuObjects;
+ CCtrlTreeView m_menuItems;
+ CCtrlCheck m_radio1, m_radio2, m_enableIcons;
+ CCtrlEdit m_customName, m_service, m_module;
+ CCtrlButton m_btnInsSeparator, m_btnInsMenu, m_btnReset, m_btnSet, m_btnDefault, m_btnDelete;
+ CCtrlBase m_warning;
+
+public:
+ CGenMenuOptionsPage() :
+ CDlgBase(g_plugin, IDD_OPT_GENMENU),
+ m_arDeleted(1),
+ m_menuItems(this, IDC_MENUITEMS),
+ m_menuObjects(this, IDC_MENUOBJECTS),
+ m_radio1(this, IDC_RADIO1),
+ m_radio2(this, IDC_RADIO2),
+ m_enableIcons(this, IDC_DISABLEMENUICONS),
+ m_btnInsSeparator(this, IDC_INSERTSEPARATOR),
+ m_btnInsMenu(this, IDC_INSERTSUBMENU),
+ m_btnReset(this, IDC_RESETMENU),
+ m_btnSet(this, IDC_GENMENU_SET),
+ m_btnDelete(this, IDC_GENMENU_DELETE),
+ m_btnDefault(this, IDC_GENMENU_DEFAULT),
+ m_customName(this, IDC_GENMENU_CUSTOMNAME),
+ m_service(this, IDC_GENMENU_SERVICE),
+ m_module(this, IDC_GENMENU_MODULE),
+ m_warning(this, IDC_NOTSUPPORTWARNING)
+ {
+ m_btnSet.OnClick = Callback(this, &CGenMenuOptionsPage::btnSet_Clicked);
+ m_btnReset.OnClick = Callback(this, &CGenMenuOptionsPage::btnReset_Clicked);
+ m_btnInsSeparator.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsSep_Clicked);
+ m_btnInsMenu.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsMenu_Clicked);
+ m_btnDefault.OnClick = Callback(this, &CGenMenuOptionsPage::btnDefault_Clicked);
+ m_btnDelete.OnClick = Callback(this, &CGenMenuOptionsPage::btnDelete_Clicked);
+
+ m_menuObjects.OnSelChange = Callback(this, &CGenMenuOptionsPage::onMenuObjectChanged);
+
+ m_menuItems.SetFlags(MTREE_CHECKBOX | MTREE_DND);
+ m_menuItems.OnSelChanged = Callback(this, &CGenMenuOptionsPage::onMenuItemChanged);
+ m_menuItems.OnBeginDrag = Callback(this, &CGenMenuOptionsPage::onMenuItemBeginDrag);
+
+ m_customName.SetSilent();
+ m_service.SetSilent();
+ m_module.SetSilent();
+ }
+
+ //---- init dialog -------------------------------------------
+ bool OnInitDialog() override
+ {
+ iInitMenuValue = db_get_b(0, "CList", "MoveProtoMenus", TRUE);
+
+ if (iInitMenuValue)
+ m_radio2.SetState(true);
+ else
+ m_radio1.SetState(true);
+
+ m_enableIcons.SetState(!bIconsDisabled);
+
+ //---- init menu object list --------------------------------------
+ for (auto &p : g_menus)
+ if (p->id != (int)hStatusMenuObject && p->m_bUseUserDefinedItems)
+ m_menuObjects.AddString(TranslateW(p->ptszDisplayName), p->id);
+
+ m_menuObjects.SetCurSel(0);
+ RebuildCurrent();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ bIconsDisabled = m_enableIcons.GetState() == 0;
+ db_set_b(0, "CList", "DisableMenuIcons", bIconsDisabled);
+ SaveTree();
+
+ for (auto &pimi : m_arDeleted)
+ Menu_RemoveItem(pimi);
+
+ int iNewMenuValue = !m_radio1.GetState();
+ if (iNewMenuValue != iInitMenuValue) {
+ db_set_b(0, "CList", "MoveProtoMenus", iNewMenuValue);
+
+ RebuildProtoMenus();
+ iInitMenuValue = iNewMenuValue;
+ }
+ RebuildCurrent();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ FreeTreeData();
+ }
+
+ void btnInsSep_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
+ tvi.hItem = hti;
+ if (!m_menuItems.GetItem(&tvi))
+ return;
+
+ MenuItemOptData *PD = new MenuItemOptData();
+ PD->id = -1;
+ PD->name = mir_wstrdup(STR_SEPARATOR);
+ PD->pos = ((MenuItemOptData *)tvi.lParam)->pos - 1;
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.item.lParam = (LPARAM)PD;
+ tvis.item.pszText = PD->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ tvis.hInsertAfter = hti;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ m_menuItems.InsertItem(&tvis);
+
+ NotifyChange();
+ }
+
+ void btnInsMenu_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
+ tvi.hItem = hti;
+ if (!m_menuItems.GetItem(&tvi))
+ return;
+
+ MenuItemOptData *curData = (MenuItemOptData*)tvi.lParam;
+
+ TMO_MenuItem mi = {};
+ UuidCreate((UUID*)&mi.uid);
+ mi.flags = CMIF_CUSTOM;
+ mi.name.a = LPGEN("New submenu");
+ mi.position = curData->pos - 1;
+ TMO_IntMenuItem *pimi = Menu_AddItem(curData->pimi->parent->id, &mi, nullptr);
+
+ MenuItemOptData *PD = new MenuItemOptData();
+ PD->id = -1;
+ PD->name = mir_wstrdup(pimi->mi.name.w);
+ PD->pos = pimi->mi.position;
+ PD->pimi = pimi;
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.item.lParam = (LPARAM)PD;
+ tvis.item.pszText = PD->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ tvis.hInsertAfter = hti;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ m_menuItems.InsertItem(&tvis);
+
+ NotifyChange();
+ }
+
+ void btnReset_Clicked(CCtrlButton*)
+ {
+ int MenuObjectID;
+ if (GetCurrentMenuObjectID(MenuObjectID)) {
+ BuildTree(MenuObjectID, false);
+ NotifyChange();
+ }
+ }
+
+ void btnDefault_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (iod->name && wcsstr(iod->name, STR_SEPARATOR))
+ return;
+
+ iod->name = mir_wstrdup(iod->defname);
+ m_customName.SetText(iod->defname);
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = iod->name;
+ m_menuItems.SetItem(&tvi);
+ NotifyChange();
+ }
+
+ void btnSet_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (iod->name && wcsstr(iod->name, STR_SEPARATOR))
+ return;
+
+ iod->name = m_customName.GetText();
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = iod->name;
+ m_menuItems.SetItem(&tvi);
+ NotifyChange();
+ }
+
+ void btnDelete_Clicked(CCtrlButton *)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (!(iod->pimi->mi.flags & CMIF_CUSTOM))
+ return;
+
+ if (IDYES == MessageBoxW(m_hwnd, TranslateT("Do you really want to delete this menu item?"), TranslateT("Miranda"), MB_YESNO | MB_ICONQUESTION)) {
+ m_arDeleted.insert(iod->pimi);
+ m_menuItems.DeleteItem(hti);
+ delete iod;
+ NotifyChange();
+ }
+ }
+
+ void onMenuObjectChanged(void*)
+ {
+ m_bInitialized = false;
+ RebuildCurrent();
+ m_bInitialized = true;
+ }
+
+ void onMenuItemChanged(void*)
+ {
+ m_customName.SetTextA("");
+ m_service.SetTextA("");
+ m_module.SetTextA("");
+
+ m_btnInsMenu.Disable();
+ m_btnDefault.Disable();
+ m_btnSet.Disable();
+ m_btnDelete.Disable();
+ m_customName.Disable();
+
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+ if (tvi.lParam == 0)
+ return;
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (iod->name && wcsstr(iod->name, STR_SEPARATOR))
+ return;
+
+ m_customName.SetText(iod->name);
+
+ if (iod->pimi->mi.uid != miid_last) {
+ char szText[100];
+ bin2hex(&iod->pimi->mi.uid, sizeof(iod->pimi->mi.uid), szText);
+ m_service.SetTextA(szText);
+ }
+
+ const CMPluginBase *pPlugin = iod->pimi->mi.pPlugin;
+ m_module.SetTextA(pPlugin == nullptr ? "" : pPlugin->getInfo().shortName);
+
+ m_btnInsMenu.Enable(iod->pimi->mi.root == nullptr);
+ m_btnDefault.Enable(mir_wstrcmp(iod->name, iod->defname) != 0);
+ m_btnDelete.Enable(iod->pimi->mi.flags & CMIF_CUSTOM);
+ m_btnSet.Enable(true);
+ m_customName.Enable(true);
+ }
+
+ void onMenuItemBeginDrag(CCtrlTreeView::TEventInfo *evt)
+ {
+ MenuItemOptData *p = (MenuItemOptData*)evt->nmtv->itemNew.lParam;
+ if (p->pimi != nullptr)
+ if (p->pimi->mi.flags & CMIF_UNMOVABLE)
+ evt->nmhdr->code = 0; // reject an attempt to change item's position
+ }
+};
+
+int GenMenuOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -1000000000;
+ odp.szTitle.a = LPGEN("Menus");
+ odp.szGroup.a = LPGEN("Customize");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CGenMenuOptionsPage();
+ g_plugin.addOptions(wParam, &odp);
+
+ return ProtocolOrderOptInit(wParam, 0);
+}
diff --git a/src/mir_app/src/menu_tray.cpp b/src/mir_app/src/menu_tray.cpp index 69446e1590..77b4a482b9 100644 --- a/src/mir_app/src/menu_tray.cpp +++ b/src/mir_app/src/menu_tray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_utils.cpp b/src/mir_app/src/menu_utils.cpp index 12df821386..ff048552de 100644 --- a/src/mir_app/src/menu_utils.cpp +++ b/src/mir_app/src/menu_utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/meta_addto.cpp b/src/mir_app/src/meta_addto.cpp index 7cf6b8d7ce..345598d429 100644 --- a/src/mir_app/src/meta_addto.cpp +++ b/src/mir_app/src/meta_addto.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_api.cpp b/src/mir_app/src/meta_api.cpp index 97da5e0acd..886e0be1c7 100644 --- a/src/mir_app/src/meta_api.cpp +++ b/src/mir_app/src/meta_api.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_edit.cpp b/src/mir_app/src/meta_edit.cpp index 9f57a4131f..ac7917ac77 100644 --- a/src/mir_app/src/meta_edit.cpp +++ b/src/mir_app/src/meta_edit.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_main.cpp b/src/mir_app/src/meta_main.cpp index 7ac06606b7..f59794693b 100644 --- a/src/mir_app/src/meta_main.cpp +++ b/src/mir_app/src/meta_main.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_menu.cpp b/src/mir_app/src/meta_menu.cpp index dc4ad1252f..1ae7ec2247 100644 --- a/src/mir_app/src/meta_menu.cpp +++ b/src/mir_app/src/meta_menu.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_options.cpp b/src/mir_app/src/meta_options.cpp index b6e3f81b7f..71f7c6a997 100644 --- a/src/mir_app/src/meta_options.cpp +++ b/src/mir_app/src/meta_options.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_services.cpp b/src/mir_app/src/meta_services.cpp index e8d5b042d6..74f218e3d1 100644 --- a/src/mir_app/src/meta_services.cpp +++ b/src/mir_app/src/meta_services.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_utils.cpp b/src/mir_app/src/meta_utils.cpp index ad75ed4910..3db142ba98 100644 --- a/src/mir_app/src/meta_utils.cpp +++ b/src/mir_app/src/meta_utils.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/metacontacts.h b/src/mir_app/src/metacontacts.h index 49e86ef32c..47f8b5bab2 100644 --- a/src/mir_app/src/metacontacts.h +++ b/src/mir_app/src/metacontacts.h @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/miranda.cpp b/src/mir_app/src/miranda.cpp index 7628da2e66..850e769535 100644 --- a/src/mir_app/src/miranda.cpp +++ b/src/mir_app/src/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/miranda.h b/src/mir_app/src/miranda.h index b3dd931124..b0508c18f3 100644 --- a/src/mir_app/src/miranda.h +++ b/src/mir_app/src/miranda.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/modules.cpp b/src/mir_app/src/modules.cpp index 2274247cc2..ff0b6682f2 100644 --- a/src/mir_app/src/modules.cpp +++ b/src/mir_app/src/modules.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/movetogroup.cpp b/src/mir_app/src/movetogroup.cpp index 4f810c4002..3eb84c9e87 100644 --- a/src/mir_app/src/movetogroup.cpp +++ b/src/mir_app/src/movetogroup.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/netlib.cpp b/src/mir_app/src/netlib.cpp index 68c2d6e3d0..2914620911 100644 --- a/src/mir_app/src/netlib.cpp +++ b/src/mir_app/src/netlib.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/netlib.h b/src/mir_app/src/netlib.h index 4b85f6c365..09480637bf 100644 --- a/src/mir_app/src/netlib.h +++ b/src/mir_app/src/netlib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/netlib_autoproxy.cpp b/src/mir_app/src/netlib_autoproxy.cpp index cc2ef2b9e1..f325f3a721 100644 --- a/src/mir_app/src/netlib_autoproxy.cpp +++ b/src/mir_app/src/netlib_autoproxy.cpp @@ -1,365 +1,365 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include <wininet.h> - -///////////////////////////////////////////////////////////////////////////////////////// -// local module data - -static char *szProxyHost[3]; -static LIST<char> proxyBypass(5); - -static HMODULE hModJS; - -static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll; -static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll; -static pfnInternetGetProxyInfo pInternetGetProxyInfo; - -static bool bEnabled, bOneProxy; - -///////////////////////////////////////////////////////////////////////////////////////// - -static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf) -{ - NetlibUser nlu = {}; - nlu.handleType = NLH_USER; - nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS; - nlu.user.szSettingsModule = "(NULL)"; - nlu.toLog = 1; - - // initialize the netlib request - NETLIBHTTPREQUEST nlhr = {}; - nlhr.cbSize = sizeof(nlhr); - nlhr.requestType = REQUEST_GET; - nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT; - nlhr.szUrl = szUrl; - - // download the page - NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr)); - if (nlhrReply) { - if (nlhrReply->resultCode == 200) { - buf.lpszScriptBuffer = nlhrReply->pData; - buf.dwScriptBufferSize = nlhrReply->dataLength + 1; - - nlhrReply->dataLength = 0; - nlhrReply->pData = nullptr; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps) -{ - bool noHttp = false; - bool usingSsl = false; - char szUrl[1024]; - - if ((nlc->url.flags & NLOCF_HTTP) && (nlc->url.flags & NLOCF_SSL) || nlc->url.port == 443 || forceHttps) { - mir_snprintf(szUrl, "https://%s", nlc->url.szHost.c_str()); - usingSsl = true; - } - else if ((nlc->url.flags & NLOCF_HTTP)) - mir_snprintf(szUrl, "http://%s", nlc->url.szHost.c_str()); - else { - strncpy_s(szUrl, nlc->url.szHost, _TRUNCATE); - noHttp = true; - } - - mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr; - nlc->wProxyPort = 0; - nlc->proxyType = 0; - - char *mt = NetlibGetIeProxy(szUrl); - char *m = NEWSTR_ALLOCA(mt); - mir_free(mt); - - if (m == nullptr) - return false; - - // if multiple servers, use the first one - char *c = strchr(m, ';'); if (c) *c = 0; - - // if 'direct' no proxy - if (_stricmp(lrtrim(m), "direct") == 0) - return false; - - // find proxy address - char *h = strchr(m, ' '); - if (h == nullptr) - return false; - - // find proxy port - *h = 0; ++h; - char *p = strchr(h, ':'); - if (p) { *p = 0; ++p; } - - lrtrim(h); ltrim(p); - if (_stricmp(m, "proxy") == 0 && h[0]) { - nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP; - nlc->wProxyPort = p ? atol(p) : 8080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS4; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks5") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS5; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else return false; - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szAutoUrlStr[MAX_PATH] = ""; -static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 }; -static HANDLE hIeProxyMutex; -static bool bAutoProxyInit; - -static void NetlibInitAutoProxy(void) -{ - if (bAutoProxyInit) return; - - if (!hModJS) { - if (!(hModJS = LoadLibraryA("jsproxy.dll"))) - return; - - pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll"); - pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"); - pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo"); - } - - if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) { - abuf.dwStructSize = sizeof(abuf); - GetFile(szAutoUrlStr, abuf); - } - bAutoProxyInit = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct IeProxyParam -{ - char *szUrl; - char *szHost; - char *szProxy; -}; - -static void __cdecl NetlibIeProxyThread(IeProxyParam *param) -{ - param->szProxy = nullptr; - - if (!bAutoProxyInit) { - WaitForSingleObject(hIeProxyMutex, INFINITE); - NetlibInitAutoProxy(); - ReleaseMutex(hIeProxyMutex); - } - - BOOL res; - char *loc = strstr(szAutoUrlStr, "file://"); - if (loc || strstr(szAutoUrlStr, "://") == nullptr) { - Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc); - loc = loc ? loc + 7 : szAutoUrlStr; - res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr); - } - else { - Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize); - if (abuf.dwScriptBufferSize) - res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf); - else - res = false; - } - - if (res) { - char proxyBuffer[1024]; - char *proxy = proxyBuffer; - DWORD dwProxyLen = sizeof(proxyBuffer); - - if (pInternetGetProxyInfo(param->szUrl, (uint32_t)mir_strlen(param->szUrl), - param->szHost, (uint32_t)mir_strlen(param->szHost), &proxy, &dwProxyLen)) - param->szProxy = mir_strdup(lrtrim(proxy)); - - Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost); - pInternetDeInitializeAutoProxyDll(nullptr, 0); - } - else Netlib_Logf(nullptr, "Autoproxy init failed"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NetlibGetIeProxy(char *szUrl) -{ - char *res = nullptr, *szHost; - { - char* p = strstr(szUrl, "://"); - if (p) p += 3; else p = szUrl; - - szHost = NEWSTR_ALLOCA(p); - p = strchr(szHost, '/'); if (p) *p = 0; - p = strchr(szHost, ':'); if (p) *p = 0; - _strlwr(szHost); - } - - if (bEnabled) { - for (auto &p : proxyBypass) { - if (mir_strcmp(p, "<local>") == 0) { - if (strchr(szHost, '.') == nullptr) - return nullptr; - } - else if (wildcmp(szHost, p)) - return nullptr; - } - - int ind = -1; - if (strstr(szUrl, "http://")) - ind = szProxyHost[0] ? 0 : 2; - else if (strstr(szUrl, "https://")) - ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2); - else - ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2)); - - if (ind < 0 || !szProxyHost[ind]) - return nullptr; - - size_t len = mir_strlen(szHost) + 20; - res = (char*)mir_alloc(len); - mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]); - return res; - } - - if (szAutoUrlStr[0]) { - IeProxyParam param = { szUrl, szHost, nullptr }; - HANDLE hThread = mir_forkThread<IeProxyParam>(NetlibIeProxyThread, ¶m); - WaitForSingleObject(hThread, INFINITE); - res = param.szProxy; - } - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLoadIeProxy(void) -{ - HKEY hSettings; - if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings)) - return; - - DWORD tValueLen, enabled = 0; - char szHostStr[256] = "", szProxyBypassStr[4096] = ""; - - tValueLen = sizeof(enabled); - int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (uint8_t*)&enabled, &tValueLen); - bEnabled = enabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szHostStr); - tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (uint8_t*)szHostStr, &tValueLen); - bEnabled = bEnabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szAutoUrlStr); - RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (uint8_t*)szAutoUrlStr, &tValueLen); - - tValueLen = _countof(szProxyBypassStr); - RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (uint8_t*)szProxyBypassStr, &tValueLen); - - RegCloseKey(hSettings); - - if (bEnabled) { - char *szProxy = ltrim(szHostStr); - if (szProxy[0] == 0) { - enabled = false; - return; - } - - while (true) { - char *szProxyEnd = strchr(szProxy, ';'); - if (szProxyEnd) - *szProxyEnd = 0; - - int ind = -1; - if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; } - else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; } - else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; } - else if (strchr(szProxy, '=')) ind = -2; - - if (ind != -2) { - bOneProxy = ind < 0; if (ind < 0) ind = 0; - - lrtrim(szProxy); - - if (strchr(szProxy, ':')) - szProxyHost[ind] = mir_strdup(szProxy); - else { - size_t len = mir_strlen(szProxy) + 10; - szProxyHost[ind] = (char*)mir_alloc(len); - mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080); - } - if (bOneProxy) - break; - } - if (szProxyEnd == nullptr) - break; - szProxy = szProxyEnd + 1; - } - - char *szProxyBypass = szProxyBypassStr; - while (true) { - char *szProxyBypassEnd = strchr(szProxyBypass, ';'); - if (szProxyBypassEnd) - *szProxyBypassEnd = 0; - - lrtrim(szProxyBypass); - - proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass))); - if (szProxyBypassEnd == nullptr) - break; - - szProxyBypass = szProxyBypassEnd + 1; - } - } - - if (bEnabled || szAutoUrlStr[0]) - hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUnloadIeProxy(void) -{ - for (int i = 0; i < 3; i++) - mir_free(szProxyHost[i]); - - for (auto &p : proxyBypass) - mir_free(p); - - mir_free(abuf.lpszScriptBuffer); - - CloseHandle(hIeProxyMutex); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include <wininet.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// local module data
+
+static char *szProxyHost[3];
+static LIST<char> proxyBypass(5);
+
+static HMODULE hModJS;
+
+static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
+static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
+static pfnInternetGetProxyInfo pInternetGetProxyInfo;
+
+static bool bEnabled, bOneProxy;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf)
+{
+ NetlibUser nlu = {};
+ nlu.handleType = NLH_USER;
+ nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS;
+ nlu.user.szSettingsModule = "(NULL)";
+ nlu.toLog = 1;
+
+ // initialize the netlib request
+ NETLIBHTTPREQUEST nlhr = {};
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT;
+ nlhr.szUrl = szUrl;
+
+ // download the page
+ NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr));
+ if (nlhrReply) {
+ if (nlhrReply->resultCode == 200) {
+ buf.lpszScriptBuffer = nlhrReply->pData;
+ buf.dwScriptBufferSize = nlhrReply->dataLength + 1;
+
+ nlhrReply->dataLength = 0;
+ nlhrReply->pData = nullptr;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps)
+{
+ bool noHttp = false;
+ bool usingSsl = false;
+ char szUrl[1024];
+
+ if ((nlc->url.flags & NLOCF_HTTP) && (nlc->url.flags & NLOCF_SSL) || nlc->url.port == 443 || forceHttps) {
+ mir_snprintf(szUrl, "https://%s", nlc->url.szHost.c_str());
+ usingSsl = true;
+ }
+ else if ((nlc->url.flags & NLOCF_HTTP))
+ mir_snprintf(szUrl, "http://%s", nlc->url.szHost.c_str());
+ else {
+ strncpy_s(szUrl, nlc->url.szHost, _TRUNCATE);
+ noHttp = true;
+ }
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr;
+ nlc->wProxyPort = 0;
+ nlc->proxyType = 0;
+
+ char *mt = NetlibGetIeProxy(szUrl);
+ char *m = NEWSTR_ALLOCA(mt);
+ mir_free(mt);
+
+ if (m == nullptr)
+ return false;
+
+ // if multiple servers, use the first one
+ char *c = strchr(m, ';'); if (c) *c = 0;
+
+ // if 'direct' no proxy
+ if (_stricmp(lrtrim(m), "direct") == 0)
+ return false;
+
+ // find proxy address
+ char *h = strchr(m, ' ');
+ if (h == nullptr)
+ return false;
+
+ // find proxy port
+ *h = 0; ++h;
+ char *p = strchr(h, ':');
+ if (p) { *p = 0; ++p; }
+
+ lrtrim(h); ltrim(p);
+ if (_stricmp(m, "proxy") == 0 && h[0]) {
+ nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP;
+ nlc->wProxyPort = p ? atol(p) : 8080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks") == 0 && h[0]) {
+ nlc->proxyType = PROXYTYPE_SOCKS4;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks5") == 0 && h[0]) {
+ nlc->proxyType = PROXYTYPE_SOCKS5;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else return false;
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static char szAutoUrlStr[MAX_PATH] = "";
+static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 };
+static HANDLE hIeProxyMutex;
+static bool bAutoProxyInit;
+
+static void NetlibInitAutoProxy(void)
+{
+ if (bAutoProxyInit) return;
+
+ if (!hModJS) {
+ if (!(hModJS = LoadLibraryA("jsproxy.dll")))
+ return;
+
+ pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll");
+ pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll");
+ pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo");
+ }
+
+ if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) {
+ abuf.dwStructSize = sizeof(abuf);
+ GetFile(szAutoUrlStr, abuf);
+ }
+ bAutoProxyInit = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct IeProxyParam
+{
+ char *szUrl;
+ char *szHost;
+ char *szProxy;
+};
+
+static void __cdecl NetlibIeProxyThread(IeProxyParam *param)
+{
+ param->szProxy = nullptr;
+
+ if (!bAutoProxyInit) {
+ WaitForSingleObject(hIeProxyMutex, INFINITE);
+ NetlibInitAutoProxy();
+ ReleaseMutex(hIeProxyMutex);
+ }
+
+ BOOL res;
+ char *loc = strstr(szAutoUrlStr, "file://");
+ if (loc || strstr(szAutoUrlStr, "://") == nullptr) {
+ Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc);
+ loc = loc ? loc + 7 : szAutoUrlStr;
+ res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr);
+ }
+ else {
+ Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize);
+ if (abuf.dwScriptBufferSize)
+ res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf);
+ else
+ res = false;
+ }
+
+ if (res) {
+ char proxyBuffer[1024];
+ char *proxy = proxyBuffer;
+ DWORD dwProxyLen = sizeof(proxyBuffer);
+
+ if (pInternetGetProxyInfo(param->szUrl, (uint32_t)mir_strlen(param->szUrl),
+ param->szHost, (uint32_t)mir_strlen(param->szHost), &proxy, &dwProxyLen))
+ param->szProxy = mir_strdup(lrtrim(proxy));
+
+ Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost);
+ pInternetDeInitializeAutoProxyDll(nullptr, 0);
+ }
+ else Netlib_Logf(nullptr, "Autoproxy init failed");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* NetlibGetIeProxy(char *szUrl)
+{
+ char *res = nullptr, *szHost;
+ {
+ char* p = strstr(szUrl, "://");
+ if (p) p += 3; else p = szUrl;
+
+ szHost = NEWSTR_ALLOCA(p);
+ p = strchr(szHost, '/'); if (p) *p = 0;
+ p = strchr(szHost, ':'); if (p) *p = 0;
+ _strlwr(szHost);
+ }
+
+ if (bEnabled) {
+ for (auto &p : proxyBypass) {
+ if (mir_strcmp(p, "<local>") == 0) {
+ if (strchr(szHost, '.') == nullptr)
+ return nullptr;
+ }
+ else if (wildcmp(szHost, p))
+ return nullptr;
+ }
+
+ int ind = -1;
+ if (strstr(szUrl, "http://"))
+ ind = szProxyHost[0] ? 0 : 2;
+ else if (strstr(szUrl, "https://"))
+ ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2);
+ else
+ ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2));
+
+ if (ind < 0 || !szProxyHost[ind])
+ return nullptr;
+
+ size_t len = mir_strlen(szHost) + 20;
+ res = (char*)mir_alloc(len);
+ mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]);
+ return res;
+ }
+
+ if (szAutoUrlStr[0]) {
+ IeProxyParam param = { szUrl, szHost, nullptr };
+ HANDLE hThread = mir_forkThread<IeProxyParam>(NetlibIeProxyThread, ¶m);
+ WaitForSingleObject(hThread, INFINITE);
+ res = param.szProxy;
+ }
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibLoadIeProxy(void)
+{
+ HKEY hSettings;
+ if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings))
+ return;
+
+ DWORD tValueLen, enabled = 0;
+ char szHostStr[256] = "", szProxyBypassStr[4096] = "";
+
+ tValueLen = sizeof(enabled);
+ int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (uint8_t*)&enabled, &tValueLen);
+ bEnabled = enabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = _countof(szHostStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (uint8_t*)szHostStr, &tValueLen);
+ bEnabled = bEnabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = _countof(szAutoUrlStr);
+ RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (uint8_t*)szAutoUrlStr, &tValueLen);
+
+ tValueLen = _countof(szProxyBypassStr);
+ RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (uint8_t*)szProxyBypassStr, &tValueLen);
+
+ RegCloseKey(hSettings);
+
+ if (bEnabled) {
+ char *szProxy = ltrim(szHostStr);
+ if (szProxy[0] == 0) {
+ enabled = false;
+ return;
+ }
+
+ while (true) {
+ char *szProxyEnd = strchr(szProxy, ';');
+ if (szProxyEnd)
+ *szProxyEnd = 0;
+
+ int ind = -1;
+ if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; }
+ else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; }
+ else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; }
+ else if (strchr(szProxy, '=')) ind = -2;
+
+ if (ind != -2) {
+ bOneProxy = ind < 0; if (ind < 0) ind = 0;
+
+ lrtrim(szProxy);
+
+ if (strchr(szProxy, ':'))
+ szProxyHost[ind] = mir_strdup(szProxy);
+ else {
+ size_t len = mir_strlen(szProxy) + 10;
+ szProxyHost[ind] = (char*)mir_alloc(len);
+ mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080);
+ }
+ if (bOneProxy)
+ break;
+ }
+ if (szProxyEnd == nullptr)
+ break;
+ szProxy = szProxyEnd + 1;
+ }
+
+ char *szProxyBypass = szProxyBypassStr;
+ while (true) {
+ char *szProxyBypassEnd = strchr(szProxyBypass, ';');
+ if (szProxyBypassEnd)
+ *szProxyBypassEnd = 0;
+
+ lrtrim(szProxyBypass);
+
+ proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass)));
+ if (szProxyBypassEnd == nullptr)
+ break;
+
+ szProxyBypass = szProxyBypassEnd + 1;
+ }
+ }
+
+ if (bEnabled || szAutoUrlStr[0])
+ hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr);
+}
+
+void NetlibUnloadIeProxy(void)
+{
+ for (int i = 0; i < 3; i++)
+ mir_free(szProxyHost[i]);
+
+ for (auto &p : proxyBypass)
+ mir_free(p);
+
+ mir_free(abuf.lpszScriptBuffer);
+
+ CloseHandle(hIeProxyMutex);
+}
diff --git a/src/mir_app/src/netlib_bind.cpp b/src/mir_app/src/netlib_bind.cpp index 53aad20fa3..cd9c955a75 100644 --- a/src/mir_app/src/netlib_bind.cpp +++ b/src/mir_app/src/netlib_bind.cpp @@ -1,330 +1,330 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) -{ - SOCKADDR_IN sin = {0}; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = {0}; - sin6.sin6_family = AF_INET6; - - mir_cslock lck(csNetlibUser); - - if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { - BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); - if (*portn == 0) - return false; - - uint16_t num; - Utils_GetRandom(&num, sizeof(uint16_t)); - *portn = num % *portn; - } - - bool before = false; - while (true) { - const char *psz; - char *pszEnd; - int portMin, portMax, port, portnum = 0; - - for (psz = szPorts;*psz;) { - while (*psz == ' ' || *psz == ',') psz++; - portMin = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) - break; - while (*pszEnd == ' ') - pszEnd++; - if (*pszEnd == '-') { - psz = pszEnd + 1; - portMax = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) portMax = 65535; - if (portMin > portMax) { - port = portMin; - portMin = portMax; - portMax = port; - } - } - else portMax = portMin; - if (portMax >= 1) { - if (portMin <= 0) portMin = 1; - for (port = portMin; port <= portMax; port++) { - if (port > 65535) - break; - - ++portnum; - - if (s == INVALID_SOCKET) continue; - if (!before && portnum <= *portn) continue; - if (before && portnum >= *portn) - return false; - - sin.sin_port = htons((uint16_t)port); - bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; - - sin6.sin6_port = htons((uint16_t)port); - bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; - - if (bV4Mapped && bV6Mapped) { - *portn = portnum + 1; - return true; - } - } - } - psz = pszEnd; - } - - if (*portn < 0) { - *portn = portnum; - return true; - } - - if (*portn >= portnum) - *portn = 0; - else - before = true; - } -} - -int NetlibFreeBoundPort(NetlibBoundPort *nlbp) -{ - NETLIBCONNECTIONEVENTINFO ncei; - - ZeroMemory(&ncei, sizeof(ncei)); - ncei.connected = 0; - ncei.listening = 1; - ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size); - NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0); - - nlbp->close(); - if (nlbp->hThread) - WaitForSingleObject(nlbp->hThread, INFINITE); - Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); - delete nlbp; - return 1; -} - -static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp) -{ - Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort); - - while (true) { - fd_set r; - FD_ZERO(&r); - if (nlbp->s != INVALID_SOCKET) - FD_SET(nlbp->s, &r); - if (nlbp->s6 != INVALID_SOCKET) - FD_SET(nlbp->s6, &r); - if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - - sockaddr_in sin; - int sinLen = sizeof(sin); - memset(&sin, 0, sizeof(sin)); - - SOCKET s; - if (FD_ISSET(nlbp->s, &r)) { - s = accept(nlbp->s, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else if (FD_ISSET(nlbp->s6, &r)) { - s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else s = 0; - - Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlbp->nlu; - nlc->s = s; - - if (nlbp->pfnNewConnection) - nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); - } - - NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); - nlbp->hThread = nullptr; - - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort); -} - -MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb); - if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError()); -LBL_Error: - delete nlbp; - return nullptr; - } - - SOCKADDR_IN sin = { 0 }; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = { 0 }; - sin6.sin6_family = AF_INET6; - - /* if the netlib user wanted a free port given in the range, then - they better have given wPort == 0, let's hope so */ - int foundPort = 0; - if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { - if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { - Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); - SetLastError(WSAEADDRINUSE); - } - else foundPort = 1; - } - else { - /* if ->wPort == 0 then they'll get any free port, otherwise they'll - be asking for whatever was in nlb->wPort*/ - if (nlb->wPort != 0) { - Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort); - sin.sin_port = htons(nlb->wPort); - sin6.sin6_port = htons(nlb->wPort); - } - - if (nlbp->s != INVALID_SOCKET) - if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) { - SOCKADDR_IN sin2 = { 0 }; - int len = sizeof(sin2); - if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len)) - sin6.sin6_port = sin2.sin_port; - foundPort = 1; - } - - if (nlbp->s6 != INVALID_SOCKET) - if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) - foundPort = 1; - } - if (!foundPort) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - SOCKADDR_INET_M sinm = { 0 }; - int len = sizeof(sinm); - if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) { - nlb->wPort = ntohs(sinm.Ipv4.sin_port); - nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr); - } - else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len)) - nlb->wPort = ntohs(sinm.Ipv6.sin6_port); - else { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError()); - goto LBL_Error; - } - nlbp->wPort = nlb->wPort; - - if (nlb->dwInternalIP == 0) { - char hostname[64] = ""; - gethostname(hostname, _countof(hostname)); - - PHOSTENT he = gethostbyname(hostname); - if (he && he->h_addr) - nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); - } - - uint32_t extIP; - if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) { - Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort); - nlb->wExPort = nlbp->wExPort; - nlb->dwExternalIP = extIP; - } - else { - if (nlu->settings.enableUPnP) - Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort); - else - Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort); - - nlbp->wExPort = 0; - nlb->wExPort = nlb->wPort; - nlb->dwExternalIP = nlb->dwInternalIP; - } - - nlbp->hThread = mir_forkThread<NetlibBoundPort>(NetlibBindAcceptThread, nlbp); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.listening = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - memcpy(&ncei.local, &sin, sizeof(sin)); - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - return nlbp; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb) - : handleType(NLH_BOUNDPORT), - nlu(_nlu) -{ - pfnNewConnection = nlb->pfnNewConnection; - pExtra = nlb->pExtra; - - s = socket(PF_INET, SOCK_STREAM, 0); - s6 = socket(PF_INET6, SOCK_STREAM, 0); -} - -void NetlibBoundPort::close() -{ - if (s != INVALID_SOCKET) { - closesocket(s); - s = INVALID_SOCKET; - } - if (s6 != INVALID_SOCKET) { - closesocket(s6); - s6 = INVALID_SOCKET; - } -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn)
+{
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+
+ SOCKADDR_IN6 sin6 = {0};
+ sin6.sin6_family = AF_INET6;
+
+ mir_cslock lck(csNetlibUser);
+
+ if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) {
+ BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn);
+ if (*portn == 0)
+ return false;
+
+ uint16_t num;
+ Utils_GetRandom(&num, sizeof(uint16_t));
+ *portn = num % *portn;
+ }
+
+ bool before = false;
+ while (true) {
+ const char *psz;
+ char *pszEnd;
+ int portMin, portMax, port, portnum = 0;
+
+ for (psz = szPorts;*psz;) {
+ while (*psz == ' ' || *psz == ',') psz++;
+ portMin = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz)
+ break;
+ while (*pszEnd == ' ')
+ pszEnd++;
+ if (*pszEnd == '-') {
+ psz = pszEnd + 1;
+ portMax = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz) portMax = 65535;
+ if (portMin > portMax) {
+ port = portMin;
+ portMin = portMax;
+ portMax = port;
+ }
+ }
+ else portMax = portMin;
+ if (portMax >= 1) {
+ if (portMin <= 0) portMin = 1;
+ for (port = portMin; port <= portMax; port++) {
+ if (port > 65535)
+ break;
+
+ ++portnum;
+
+ if (s == INVALID_SOCKET) continue;
+ if (!before && portnum <= *portn) continue;
+ if (before && portnum >= *portn)
+ return false;
+
+ sin.sin_port = htons((uint16_t)port);
+ bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0;
+
+ sin6.sin6_port = htons((uint16_t)port);
+ bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0;
+
+ if (bV4Mapped && bV6Mapped) {
+ *portn = portnum + 1;
+ return true;
+ }
+ }
+ }
+ psz = pszEnd;
+ }
+
+ if (*portn < 0) {
+ *portn = portnum;
+ return true;
+ }
+
+ if (*portn >= portnum)
+ *portn = 0;
+ else
+ before = true;
+ }
+}
+
+int NetlibFreeBoundPort(NetlibBoundPort *nlbp)
+{
+ NETLIBCONNECTIONEVENTINFO ncei;
+
+ ZeroMemory(&ncei, sizeof(ncei));
+ ncei.connected = 0;
+ ncei.listening = 1;
+ ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule;
+ int size = sizeof(SOCKADDR_IN);
+ getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size);
+ NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0);
+
+ nlbp->close();
+ if (nlbp->hThread)
+ WaitForSingleObject(nlbp->hThread, INFINITE);
+ Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort);
+ delete nlbp;
+ return 1;
+}
+
+static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp)
+{
+ Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort);
+
+ while (true) {
+ fd_set r;
+ FD_ZERO(&r);
+ if (nlbp->s != INVALID_SOCKET)
+ FD_SET(nlbp->s, &r);
+ if (nlbp->s6 != INVALID_SOCKET)
+ FD_SET(nlbp->s6, &r);
+ if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+
+ sockaddr_in sin;
+ int sinLen = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+
+ SOCKET s;
+ if (FD_ISSET(nlbp->s, &r)) {
+ s = accept(nlbp->s, (sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+ }
+ else if (FD_ISSET(nlbp->s6, &r)) {
+ s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+ }
+ else s = 0;
+
+ Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s);
+
+ NetlibConnection *nlc = new NetlibConnection();
+ nlc->nlu = nlbp->nlu;
+ nlc->s = s;
+
+ if (nlbp->pfnNewConnection)
+ nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra);
+ }
+
+ NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP");
+ nlbp->hThread = nullptr;
+
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort);
+}
+
+MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb);
+ if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError());
+LBL_Error:
+ delete nlbp;
+ return nullptr;
+ }
+
+ SOCKADDR_IN sin = { 0 };
+ sin.sin_family = AF_INET;
+
+ SOCKADDR_IN6 sin6 = { 0 };
+ sin6.sin6_family = AF_INET6;
+
+ /* if the netlib user wanted a free port given in the range, then
+ they better have given wPort == 0, let's hope so */
+ int foundPort = 0;
+ if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) {
+ if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) {
+ Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified");
+ SetLastError(WSAEADDRINUSE);
+ }
+ else foundPort = 1;
+ }
+ else {
+ /* if ->wPort == 0 then they'll get any free port, otherwise they'll
+ be asking for whatever was in nlb->wPort*/
+ if (nlb->wPort != 0) {
+ Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort);
+ sin.sin_port = htons(nlb->wPort);
+ sin6.sin6_port = htons(nlb->wPort);
+ }
+
+ if (nlbp->s != INVALID_SOCKET)
+ if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) {
+ SOCKADDR_IN sin2 = { 0 };
+ int len = sizeof(sin2);
+ if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len))
+ sin6.sin6_port = sin2.sin_port;
+ foundPort = 1;
+ }
+
+ if (nlbp->s6 != INVALID_SOCKET)
+ if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0)
+ foundPort = 1;
+ }
+ if (!foundPort) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ SOCKADDR_INET_M sinm = { 0 };
+ int len = sizeof(sinm);
+ if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) {
+ nlb->wPort = ntohs(sinm.Ipv4.sin_port);
+ nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr);
+ }
+ else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len))
+ nlb->wPort = ntohs(sinm.Ipv6.sin6_port);
+ else {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError());
+ goto LBL_Error;
+ }
+ nlbp->wPort = nlb->wPort;
+
+ if (nlb->dwInternalIP == 0) {
+ char hostname[64] = "";
+ gethostname(hostname, _countof(hostname));
+
+ PHOSTENT he = gethostbyname(hostname);
+ if (he && he->h_addr)
+ nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr);
+ }
+
+ uint32_t extIP;
+ if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) {
+ Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort);
+ nlb->wExPort = nlbp->wExPort;
+ nlb->dwExternalIP = extIP;
+ }
+ else {
+ if (nlu->settings.enableUPnP)
+ Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort);
+ else
+ Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort);
+
+ nlbp->wExPort = 0;
+ nlb->wExPort = nlb->wPort;
+ nlb->dwExternalIP = nlb->dwInternalIP;
+ }
+
+ nlbp->hThread = mir_forkThread<NetlibBoundPort>(NetlibBindAcceptThread, nlbp);
+
+ if (GetSubscribersCount((THook*)hEventConnected)) {
+ NETLIBCONNECTIONEVENTINFO ncei = {};
+ ncei.connected = 1;
+ ncei.listening = 1;
+ ncei.szSettingsModule = nlu->user.szSettingsModule;
+ memcpy(&ncei.local, &sin, sizeof(sin));
+ NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0);
+ }
+
+ return nlbp;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb)
+ : handleType(NLH_BOUNDPORT),
+ nlu(_nlu)
+{
+ pfnNewConnection = nlb->pfnNewConnection;
+ pExtra = nlb->pExtra;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ s6 = socket(PF_INET6, SOCK_STREAM, 0);
+}
+
+void NetlibBoundPort::close()
+{
+ if (s != INVALID_SOCKET) {
+ closesocket(s);
+ s = INVALID_SOCKET;
+ }
+ if (s6 != INVALID_SOCKET) {
+ closesocket(s6);
+ s6 = INVALID_SOCKET;
+ }
+}
diff --git a/src/mir_app/src/netlib_http.cpp b/src/mir_app/src/netlib_http.cpp index 7c94db57ca..260d3a6199 100644 --- a/src/mir_app/src/netlib_http.cpp +++ b/src/mir_app/src/netlib_http.cpp @@ -1,1150 +1,1150 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "../libs/zlib/src/zlib.h" -#include "netlib.h" - -#define HTTPRECVHEADERSTIMEOUT 30000 //in ms -#define HTTPRECVDATATIMEOUT 20000 - -struct ProxyAuth -{ - char *szServer; - char *szMethod; - - ProxyAuth(const char *pszServer, const char *pszMethod) - { - szServer = mir_strdup(pszServer); - szMethod = mir_strdup(pszMethod); - } - ~ProxyAuth() - { - mir_free(szServer); - mir_free(szMethod); - } - static int Compare(const ProxyAuth *p1, const ProxyAuth *p2) - { - return mir_strcmpi(p1->szServer, p2->szServer); - } -}; - -struct ProxyAuthList : OBJLIST<ProxyAuth> -{ - ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {} - - void add(const char *szServer, const char *szMethod) - { - if (szServer == nullptr) return; - int i = getIndex((ProxyAuth*)&szServer); - if (i >= 0) { - ProxyAuth &rec = (*this)[i]; - if (szMethod == nullptr) - remove(i); - else if (_stricmp(rec.szMethod, szMethod)) { - mir_free(rec.szMethod); - rec.szMethod = mir_strdup(szMethod); - } - } - else insert(new ProxyAuth(szServer, szMethod)); - } - - const char* find(const char *szServer) - { - ProxyAuth *rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : nullptr; - return rec ? rec->szMethod : nullptr; - } -}; - -ProxyAuthList proxyAuthList; - -static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags) -{ - int dwTimeNow; - - if (nlc->foreBuf.isEmpty() && !Netlib_SslPending(nlc->hSsl)) { - while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) { - int dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000); - int res = WaitUntilReadable(nlc->s, dwDeltaTime); - - switch (res) { - case SOCKET_ERROR: - return SOCKET_ERROR; - - case 1: - return Netlib_Recv(nlc, buf, len, flags); - } - - if (nlc->termRequested || Miranda_IsTerminated()) - return 0; - } - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - return Netlib_Recv(nlc, buf, len, flags); -} - -MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr) -{ - if (nlhr == nullptr || hdr == nullptr) - return nullptr; - - for (int i=0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (_stricmp(p.szName, hdr) == 0) - return p.szValue; - } - - return nullptr; -} - -static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider) -{ - char *szBasicHdr = nullptr; - char *szNegoHdr = nullptr; - char *szNtlmHdr = nullptr; - - for (int i=0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (_stricmp(p.szName, hdr) == 0) { - if (_strnicmp(p.szValue, "Negotiate", 9) == 0) - szNegoHdr = p.szValue; - else if (_strnicmp(p.szValue, "NTLM", 4) == 0) - szNtlmHdr = p.szValue; - else if (_strnicmp(p.szValue, "Basic", 5) == 0) - szBasicHdr = p.szValue; - } - } - - if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr; - if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr; - if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr; - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url) -{ - secur = secur || _strnicmp(szUrl, "https", 5) == 0; - - const char* phost = strstr(szUrl, "://"); - url.szHost = phost ? phost + 3 : szUrl; - - int idx = url.szHost.Find('/'); - if (idx != -1) - url.szHost.Truncate(idx); - - if ((idx = url.szHost.Find(':')) != -1) { - url.port = strtol(url.szHost.c_str() + idx + 1, nullptr, 10); - url.szHost.Truncate(idx); - } - else url.port = secur ? 443 : 80; - url.flags = (secur ? NLOCF_SSL : 0); -} - -static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr) -{ - NetlibUrl url; - - if (szUrl == nullptr) - NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, url); - else - NetlibConnFromUrl(szUrl, false, url); - - url.flags |= NLOCF_HTTP; - if (url.flags & NLOCF_SSL) - nlhr->flags |= NLHRF_SSL; - else - nlhr->flags &= ~NLHRF_SSL; - - if (nlc != nullptr) { - bool httpProxy = !(url.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP; - bool sameHost = mir_strcmp(nlc->url.szHost, url.szHost) == 0 && nlc->url.port == url.port; - - if (!httpProxy && !sameHost) { - NetlibDoCloseSocket(nlc); - - nlc->url = url; - return NetlibDoConnect(nlc) ? nlc : nullptr; - } - } - else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, url.szHost, url.port, 0, url.flags); - - return nlc; -} - -struct HttpSecurityContext -{ - HANDLE m_hNtlmSecurity; - char *m_szHost; - char *m_szProvider; - - HttpSecurityContext() - { - m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr; - } - - ~HttpSecurityContext() { Destroy(); } - - void Destroy(void) - { - if (!m_hNtlmSecurity) return; - - Netlib_DestroySecurityProvider(m_hNtlmSecurity); - m_hNtlmSecurity = nullptr; - mir_free(m_szHost); m_szHost = nullptr; - mir_free(m_szProvider); m_szProvider = nullptr; - } - - bool TryBasic(void) - { - return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic"); - } - - char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete) - { - char *szAuthHdr = nullptr; - bool justCreated = false; - NetlibUser *nlu = nlc->nlu; - - if (m_hNtlmSecurity) { - bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider); - newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost))); - if (newAuth) - Destroy(); - } - - if (m_hNtlmSecurity == nullptr) { - CMStringA szSpnStr; - if (szHost && _stricmp(szProvider, "Basic")) { - unsigned long ip = inet_addr(szHost); - PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET); - szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost); - _strlwr(szSpnStr.GetBuffer() + 5); - Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str()); - } - m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str())); - if (m_hNtlmSecurity) { - m_szProvider = mir_strdup(szProvider); - m_szHost = mir_strdup(szHost); - justCreated = true; - } - } - - if (m_hNtlmSecurity) { - ptrW szLogin, szPassw; - - if (nlu->settings.useProxyAuth) { - mir_cslock lck(csNetlibUser); - szLogin = mir_a2u(nlu->settings.szProxyAuthUser); - szPassw = mir_a2u(nlu->settings.szProxyAuthPassword); - } - - szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete); - if (!szAuthHdr) - Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)"); - else if (justCreated) - proxyAuthList.add(m_szHost, m_szProvider); - } - else complete = 1; - - return szAuthHdr; - } -}; - -static int HttpPeekFirstResponseLine(NetlibConnection *nlc, uint32_t dwTimeoutTime, uint32_t recvFlags, int *resultCode, char **ppszResultDescr, int *length) -{ - int bytesPeeked; - char buffer[2048], *peol; - - while (true) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags); - if (bytesPeeked == 0) { - SetLastError(ERROR_HANDLE_EOF); - return 0; - } - if (bytesPeeked == SOCKET_ERROR) - return 0; - - buffer[bytesPeeked] = '\0'; - if ((peol = strchr(buffer, '\n')) != nullptr) - break; - - if ((int)mir_strlen(buffer) < bytesPeeked) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - if (bytesPeeked == _countof(buffer) - 1) { - SetLastError(ERROR_BUFFER_OVERFLOW); - return 0; - } - if (Miranda_IsTerminated()) - return 0; - Sleep(10); - } - - if (peol == buffer) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - *peol = '\0'; - - if (_strnicmp(buffer, "HTTP/", 5)) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - size_t off = strcspn(buffer, " \t"); - if (off >= (unsigned)bytesPeeked) - return 0; - - char *pResultCode = buffer + off; - *(pResultCode++) = 0; - - char *pResultDescr; - *resultCode = strtol(pResultCode, &pResultDescr, 10); - - if (ppszResultDescr) - *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr)); - - if (length) - *length = peol - buffer + 1; - return 1; -} - -static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader) -{ - bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH); - - if (sendContentLengthHeader && sendData) - httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength); - else - httpRequest.AppendFormat("\r\n"); - - uint32_t hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags); - if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) { - uint32_t sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags); - - bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR; - } - - return bytesSent; -} - -MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) -{ - NETLIBHTTPREQUEST *nlhrReply = nullptr; - HttpSecurityContext httpSecurity; - - char *szHost = nullptr, *szNewUrl = nullptr; - char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr; - int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader; - int bytesSent = 0; - bool lastFirstLineFail = false; - - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - NetlibUser *nlu = nlc->nlu; - if (GetNetlibHandleType(nlu) != NLH_USER) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT; - - const char *pszRequest; - switch (nlhr->requestType) { - case REQUEST_GET: pszRequest = "GET"; break; - case REQUEST_POST: pszRequest = "POST"; break; - case REQUEST_CONNECT: pszRequest = "CONNECT"; break; - case REQUEST_HEAD: pszRequest = "HEAD"; break; - case REQUEST_PUT: pszRequest = "PUT"; break; - case REQUEST_DELETE: pszRequest = "DELETE"; break; - case REQUEST_PATCH: pszRequest = "PATCH"; break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - const char *pszFullUrl = nlhr->szUrl; - const char *pszUrl = nullptr; - - unsigned complete = false; - int count = 11; - while (--count) { - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - bytesSent = SOCKET_ERROR; - break; - } - - if (!NetlibReconnect(nlc)) { - bytesSent = SOCKET_ERROR; - break; - } - - if (!pszUrl) { - pszUrl = pszFullUrl; - if (!(nlhr->flags & NLHRF_MANUALHOST)) { - bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL); - - const char *ppath, *phost; - phost = strstr(pszUrl, "://"); - if (phost == nullptr) phost = pszUrl; - else phost += 3; - ppath = strchr(phost, '/'); - if (ppath == phost) - phost = nullptr; - - replaceStr(szHost, phost); - if (ppath && phost) - szHost[ppath - phost] = 0; - - if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy) - pszUrl = ppath ? ppath : "/"; - - if (usingProxy && phost && !nlc->dnsThroughProxy) { - char *tszHost = mir_strdup(phost); - if (ppath) - tszHost[ppath - phost] = 0; - char *cln = strchr(tszHost, ':'); if (cln) *cln = 0; - - if (inet_addr(tszHost) == INADDR_NONE) { - in_addr ip; - if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) { - mir_free(szHost); - if (cln) *cln = ':'; - szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach(); - } - } - mir_free(tszHost); - } - } - } - - if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) { - if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) { - const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer); - if (szAuthMethodNlu) { - mir_free(pszProxyAuthHdr); - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete); - } - } - } - nlc->proxyAuthNeeded = false; - - CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); - - // HTTP headers - doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0; - for (i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1; - else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1; - else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Connection")) continue; - if (p.szValue == nullptr) continue; - httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue); - } - if (szHost && !doneHostHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost); - if (pszProxyAuthHdr && !doneProxyAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr); - if (pszAuthHdr && !doneAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr); - httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive"); - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive"); - - // Add Sticky Headers - if (nlu->szStickyHeaders != nullptr) - httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders); - - // send it - bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader); - if (bytesSent == SOCKET_ERROR) - break; - - // ntlm reply - if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD) - break; - - uint32_t fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0); - uint32_t dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout; - if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) { - uint32_t err = GetLastError(); - Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count); - - // connection died while we were waiting - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - break; - } - - if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) { - bytesSent = SOCKET_ERROR; - break; - } - - lastFirstLineFail = true; - continue; - } - - int resultCode = nlhr->resultCode; - lastFirstLineFail = false; - - uint32_t hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - uint32_t dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP; - - if (resultCode == 100) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - - else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect - pszUrl = nullptr; - - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - auto *tmpUrl = Netlib_GetHeader(nlhrReply, "Location"); - if (tmpUrl) { - size_t rlen = 0; - if (tmpUrl[0] == '/') { - const char *ppath, *phost; - phost = strstr(pszFullUrl, "://"); - phost = phost ? phost + 3 : pszFullUrl; - ppath = strchr(phost, '/'); - rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl); - } - - nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1); - - strncpy(nlc->szNewUrl, pszFullUrl, rlen); - mir_strcpy(nlc->szNewUrl + rlen, tmpUrl); - pszFullUrl = nlc->szNewUrl; - pszUrl = nullptr; - - if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) { - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 401 && !doneAuthHeader) { //auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - replaceStr(pszAuthHdr, nullptr); - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete); - } - } - if (pszAuthHdr == nullptr) { - proxyAuthList.add(szHost, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr; - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge + 1)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete); - } - } - if (pszProxyAuthHdr == nullptr) { - proxyAuthList.add(nlc->szProxyServer, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else break; - - if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader) - replaceStr(pszProxyAuthHdr, nullptr); - - if (pszAuthHdr && resultCode != 401 && !doneAuthHeader) - replaceStr(pszAuthHdr, nullptr); - - if (nlhrReply) { - Netlib_FreeHttpRequest(nlhrReply); - nlhrReply = nullptr; - } - } - - if (count == 0) bytesSent = SOCKET_ERROR; - if (nlhrReply) - Netlib_FreeHttpRequest(nlhrReply); - - //clean up - mir_free(pszProxyAuthHdr); - mir_free(pszAuthHdr); - mir_free(szHost); - mir_free(szNewUrl); - - if (nlc) - NetlibLeaveNestedCS(&nlc->ncsSend); - - return bytesSent; -} - -MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) -{ - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - if (nlhr->headers) { - for (int i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - mir_free(p.szName); - mir_free(p.szValue); - } - mir_free(nlhr->headers); - } - mir_free(nlhr->pData); - mir_free(nlhr->szResultDescr); - mir_free(nlhr->szUrl); - mir_free(nlhr); - return true; -} - -#define NHRV_BUF_SIZE 8192 - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return nullptr; - - uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; - NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); - nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); - nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions - nlhr->requestType = REQUEST_RESPONSE; - - int firstLineLength = 0; - if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1); - int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT); - if (bytesPeeked != firstLineLength) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - if (bytesPeeked != SOCKET_ERROR) - SetLastError(ERROR_HANDLE_EOF); - return nullptr; - } - - // Make sure all headers arrived - MBinBuffer buf; - int headersCount = 0; - bytesPeeked = 0; - for (bool headersCompleted = false; !headersCompleted;) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE); - if (bytesPeeked == 0) - break; - - if (bytesPeeked == SOCKET_ERROR) { - bytesPeeked = 0; - break; - } - - buf.append(buffer, bytesPeeked); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr) break; - if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) { - bytesPeeked = peol - (char*)buf.data() + 1; - headersCompleted = true; - break; - } - pbuffer = peol + 1; - } - } - - if (bytesPeeked <= 0) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - // Receive headers - nlhr->headersCount = headersCount; - nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r')) - break; - *peol = 0; - - char *pColon = strchr(pbuffer, ':'); - if (pColon == nullptr) { - Netlib_FreeHttpRequest(nlhr); nlhr = nullptr; - SetLastError(ERROR_INVALID_DATA); - break; - } - - *pColon = 0; - nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer)); - nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1)); - pbuffer = peol + 1; - } - - // remove processed data - buf.remove(bytesPeeked); - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - return nlhr; -} - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || - nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || - nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION) - nlhr->nlc = nullptr; - - NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc); - if (nlc == nullptr) - return nullptr; - - NETLIBHTTPREQUEST nlhrSend = *nlhr; - nlhrSend.flags |= NLHRF_SMARTREMOVEHOST; - - bool doneUserAgentHeader = Netlib_GetHeader(nlhr, "User-Agent") != nullptr; - bool doneAcceptEncoding = Netlib_GetHeader(nlhr, "Accept-Encoding") != nullptr; - if (!doneUserAgentHeader || !doneAcceptEncoding) { - nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2)); - memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount); - } - - char szUserAgent[64]; - if (!doneUserAgentHeader) { - nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent"; - nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent; - ++nlhrSend.headersCount; - - char szMirandaVer[64]; - strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE); - #if defined(_WIN64) - strncat_s(szMirandaVer, " x64", _TRUNCATE); - #endif - - char *pspace = strchr(szMirandaVer, ' '); - if (pspace) { - *pspace++ = '\0'; - mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace); - } - else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer); - } - if (!doneAcceptEncoding) { - nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding"; - nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip"; - ++nlhrSend.headersCount; - } - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) { - if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers); - nlhr->resultCode = nlhrSend.resultCode; - Netlib_CloseHandle(nlc); - return nullptr; - } - if (!doneUserAgentHeader || !doneAcceptEncoding) - mir_free(nlhrSend.headers); - - uint32_t dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - uint32_t hflags = - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - NETLIBHTTPREQUEST *nlhrReply; - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - nlhrReply->szUrl = nlc->szNewUrl; - nlc->szNewUrl = nullptr; - } - - if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) { - Netlib_CloseHandle(nlc); - if (nlhrReply) - nlhrReply->nlc = nullptr; - } - else nlhrReply->nlc = nlc; - - return nlhrReply; -} - -void NetlibHttpSetLastErrorUsingHttpResult(int result) -{ - if (result >= 200 && result < 300) { - SetLastError(ERROR_SUCCESS); - return; - } - switch (result) { - case 400: SetLastError(ERROR_BAD_FORMAT); break; - case 401: - case 402: - case 403: - case 407: SetLastError(ERROR_ACCESS_DENIED); break; - case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; - case 405: - case 406: SetLastError(ERROR_INVALID_FUNCTION); break; - case 408: SetLastError(ERROR_TIMEOUT); break; - default: SetLastError(ERROR_GEN_FAILURE); break; - } -} - -char* gzip_decode(char *gzip_data, int *len_ptr, int window) -{ - if (*len_ptr == 0) return nullptr; - - int gzip_len = *len_ptr * 5; - char* output_data = nullptr; - - int gzip_err; - z_stream zstr; - - do { - output_data = (char*)mir_realloc(output_data, gzip_len+1); - if (output_data == nullptr) - break; - - zstr.next_in = (Bytef*)gzip_data; - zstr.avail_in = *len_ptr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream)); - - zstr.next_out = (Bytef*)output_data; - zstr.avail_out = gzip_len; - - gzip_err = inflate(&zstr, Z_FINISH); - - inflateEnd(&zstr); - gzip_len *= 2; - if (gzip_len > 10000000) - break; - } while (gzip_err == Z_BUF_ERROR); - - gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1; - - if (gzip_len <= 0) { - mir_free(output_data); - output_data = nullptr; - } - else output_data[gzip_len] = 0; - - *len_ptr = gzip_len; - return output_data; -} - -static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, uint32_t flags) -{ - MBinBuffer buf; - - while (true) { - char data[1000]; - int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags); - if (recvResult <= 0 || recvResult >= _countof(data)) - return SOCKET_ERROR; - - buf.append(data, recvResult); // add chunk - - auto *peol1 = (const char*)memchr(buf.data(), '\n', buf.length()); - if (peol1 == nullptr) - continue; - - auto *pStart = (const char *)buf.data(); - int cbRest = int(peol1 - pStart) + 1; - const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest); - if (peol2 == nullptr) - continue; - - int sz = peol2 - pStart + 1; - int r = strtol(first ? pStart : peol1 + 1, nullptr, 16); - if (r == 0) { - const char *peol3 = strchr(peol2 + 1, '\n'); - if (peol3 == nullptr) - continue; - sz = peol3 - pStart + 1; - } - buf.remove(sz); // remove all our data from buffer - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - return r; - } -} - -NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect) -{ - int dataLen = -1, i, chunkhdr = 0; - bool chunked = false; - int cenc = 0, cenctype = 0, close = 0; - -next: - NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - if (nlhrReply == nullptr) - return nullptr; - - if (nlhrReply->resultCode == 100) { - Netlib_FreeHttpRequest(nlhrReply); - goto next; - } - - if (nlhrReply->resultCode == 204) - dataLen = 0; - - for (i = 0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (!mir_strcmpi(p.szName, "Content-Length")) - dataLen = atoi(p.szValue); - - if (!mir_strcmpi(p.szName, "Content-Encoding")) { - cenc = i; - if (strstr(p.szValue, "gzip")) - cenctype = 1; - else if (strstr(p.szValue, "deflate")) - cenctype = 2; - } - - if (!mir_strcmpi(p.szName, "Connection")) - close = !mir_strcmpi(p.szValue, "close"); - - if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) { - chunked = true; - chunkhdr = i; - dataLen = -1; - } - } - - if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) { - int recvResult, chunksz = -1; - int dataBufferAlloced; - - if (chunked) { - chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0)); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen = chunksz; - } - dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - - while (chunksz != 0) { - while (true) { - recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT, - nlhrReply->pData + nlhrReply->dataLength, - dataBufferAlloced - nlhrReply->dataLength - 1, - dflags | (cenctype ? MSG_NODUMP : 0)); - - if (recvResult == 0) break; - if (recvResult == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - nlhrReply->dataLength += recvResult; - - if (dataLen >= 0) { - if (nlhrReply->dataLength >= dataLen) - break; - } - else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) { - dataBufferAlloced += 2048; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - if (nlhrReply->pData == nullptr) { - SetLastError(ERROR_OUTOFMEMORY); - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - } - Sleep(10); - } - - if (!chunked) - break; - - chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen += chunksz; - dataBufferAlloced += chunksz; - - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - } - - nlhrReply->pData[nlhrReply->dataLength] = '\0'; - } - - if (chunked) { - nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16); - mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length"); - - nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16); - mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength); - } - - if (cenctype) { - int bufsz = nlhrReply->dataLength; - char* szData = nullptr; - - switch (cenctype) { - case 1: - szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS); - break; - - case 2: - szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS); - if (bufsz < 0) { - bufsz = nlhrReply->dataLength; - szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS); - } - break; - } - - if (bufsz > 0) { - Netlib_Dump(nlc, (uint8_t*)szData, bufsz, false, dflags | MSG_NOTITLE); - mir_free(nlhrReply->pData); - nlhrReply->pData = szData; - nlhrReply->dataLength = bufsz; - - mir_free(nlhrReply->headers[cenc].szName); - mir_free(nlhrReply->headers[cenc].szValue); - memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0])); - } - else if (bufsz == 0) { - mir_free(nlhrReply->pData); - nlhrReply->pData = nullptr; - nlhrReply->dataLength = 0; - } - } - - if (close && - (nlc->proxyType != PROXYTYPE_HTTP || nlc->url.flags & NLOCF_SSL) && - (!isConnect || nlhrReply->resultCode != 200)) - NetlibDoCloseSocket(nlc); - - return nlhrReply; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "../libs/zlib/src/zlib.h"
+#include "netlib.h"
+
+#define HTTPRECVHEADERSTIMEOUT 30000 //in ms
+#define HTTPRECVDATATIMEOUT 20000
+
+struct ProxyAuth
+{
+ char *szServer;
+ char *szMethod;
+
+ ProxyAuth(const char *pszServer, const char *pszMethod)
+ {
+ szServer = mir_strdup(pszServer);
+ szMethod = mir_strdup(pszMethod);
+ }
+ ~ProxyAuth()
+ {
+ mir_free(szServer);
+ mir_free(szMethod);
+ }
+ static int Compare(const ProxyAuth *p1, const ProxyAuth *p2)
+ {
+ return mir_strcmpi(p1->szServer, p2->szServer);
+ }
+};
+
+struct ProxyAuthList : OBJLIST<ProxyAuth>
+{
+ ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {}
+
+ void add(const char *szServer, const char *szMethod)
+ {
+ if (szServer == nullptr) return;
+ int i = getIndex((ProxyAuth*)&szServer);
+ if (i >= 0) {
+ ProxyAuth &rec = (*this)[i];
+ if (szMethod == nullptr)
+ remove(i);
+ else if (_stricmp(rec.szMethod, szMethod)) {
+ mir_free(rec.szMethod);
+ rec.szMethod = mir_strdup(szMethod);
+ }
+ }
+ else insert(new ProxyAuth(szServer, szMethod));
+ }
+
+ const char* find(const char *szServer)
+ {
+ ProxyAuth *rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : nullptr;
+ return rec ? rec->szMethod : nullptr;
+ }
+};
+
+ProxyAuthList proxyAuthList;
+
+static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags)
+{
+ int dwTimeNow;
+
+ if (nlc->foreBuf.isEmpty() && !Netlib_SslPending(nlc->hSsl)) {
+ while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) {
+ int dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000);
+ int res = WaitUntilReadable(nlc->s, dwDeltaTime);
+
+ switch (res) {
+ case SOCKET_ERROR:
+ return SOCKET_ERROR;
+
+ case 1:
+ return Netlib_Recv(nlc, buf, len, flags);
+ }
+
+ if (nlc->termRequested || Miranda_IsTerminated())
+ return 0;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ return Netlib_Recv(nlc, buf, len, flags);
+}
+
+MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr)
+{
+ if (nlhr == nullptr || hdr == nullptr)
+ return nullptr;
+
+ for (int i=0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ if (_stricmp(p.szName, hdr) == 0)
+ return p.szValue;
+ }
+
+ return nullptr;
+}
+
+static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider)
+{
+ char *szBasicHdr = nullptr;
+ char *szNegoHdr = nullptr;
+ char *szNtlmHdr = nullptr;
+
+ for (int i=0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (_stricmp(p.szName, hdr) == 0) {
+ if (_strnicmp(p.szValue, "Negotiate", 9) == 0)
+ szNegoHdr = p.szValue;
+ else if (_strnicmp(p.szValue, "NTLM", 4) == 0)
+ szNtlmHdr = p.szValue;
+ else if (_strnicmp(p.szValue, "Basic", 5) == 0)
+ szBasicHdr = p.szValue;
+ }
+ }
+
+ if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr;
+ if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr;
+ if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr;
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url)
+{
+ secur = secur || _strnicmp(szUrl, "https", 5) == 0;
+
+ const char* phost = strstr(szUrl, "://");
+ url.szHost = phost ? phost + 3 : szUrl;
+
+ int idx = url.szHost.Find('/');
+ if (idx != -1)
+ url.szHost.Truncate(idx);
+
+ if ((idx = url.szHost.Find(':')) != -1) {
+ url.port = strtol(url.szHost.c_str() + idx + 1, nullptr, 10);
+ url.szHost.Truncate(idx);
+ }
+ else url.port = secur ? 443 : 80;
+ url.flags = (secur ? NLOCF_SSL : 0);
+}
+
+static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr)
+{
+ NetlibUrl url;
+
+ if (szUrl == nullptr)
+ NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, url);
+ else
+ NetlibConnFromUrl(szUrl, false, url);
+
+ url.flags |= NLOCF_HTTP;
+ if (url.flags & NLOCF_SSL)
+ nlhr->flags |= NLHRF_SSL;
+ else
+ nlhr->flags &= ~NLHRF_SSL;
+
+ if (nlc != nullptr) {
+ bool httpProxy = !(url.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP;
+ bool sameHost = mir_strcmp(nlc->url.szHost, url.szHost) == 0 && nlc->url.port == url.port;
+
+ if (!httpProxy && !sameHost) {
+ NetlibDoCloseSocket(nlc);
+
+ nlc->url = url;
+ return NetlibDoConnect(nlc) ? nlc : nullptr;
+ }
+ }
+ else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, url.szHost, url.port, 0, url.flags);
+
+ return nlc;
+}
+
+struct HttpSecurityContext
+{
+ HANDLE m_hNtlmSecurity;
+ char *m_szHost;
+ char *m_szProvider;
+
+ HttpSecurityContext()
+ {
+ m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr;
+ }
+
+ ~HttpSecurityContext() { Destroy(); }
+
+ void Destroy(void)
+ {
+ if (!m_hNtlmSecurity) return;
+
+ Netlib_DestroySecurityProvider(m_hNtlmSecurity);
+ m_hNtlmSecurity = nullptr;
+ mir_free(m_szHost); m_szHost = nullptr;
+ mir_free(m_szProvider); m_szProvider = nullptr;
+ }
+
+ bool TryBasic(void)
+ {
+ return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic");
+ }
+
+ char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete)
+ {
+ char *szAuthHdr = nullptr;
+ bool justCreated = false;
+ NetlibUser *nlu = nlc->nlu;
+
+ if (m_hNtlmSecurity) {
+ bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider);
+ newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost)));
+ if (newAuth)
+ Destroy();
+ }
+
+ if (m_hNtlmSecurity == nullptr) {
+ CMStringA szSpnStr;
+ if (szHost && _stricmp(szProvider, "Basic")) {
+ unsigned long ip = inet_addr(szHost);
+ PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET);
+ szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost);
+ _strlwr(szSpnStr.GetBuffer() + 5);
+ Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str());
+ }
+ m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str()));
+ if (m_hNtlmSecurity) {
+ m_szProvider = mir_strdup(szProvider);
+ m_szHost = mir_strdup(szHost);
+ justCreated = true;
+ }
+ }
+
+ if (m_hNtlmSecurity) {
+ ptrW szLogin, szPassw;
+
+ if (nlu->settings.useProxyAuth) {
+ mir_cslock lck(csNetlibUser);
+ szLogin = mir_a2u(nlu->settings.szProxyAuthUser);
+ szPassw = mir_a2u(nlu->settings.szProxyAuthPassword);
+ }
+
+ szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete);
+ if (!szAuthHdr)
+ Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)");
+ else if (justCreated)
+ proxyAuthList.add(m_szHost, m_szProvider);
+ }
+ else complete = 1;
+
+ return szAuthHdr;
+ }
+};
+
+static int HttpPeekFirstResponseLine(NetlibConnection *nlc, uint32_t dwTimeoutTime, uint32_t recvFlags, int *resultCode, char **ppszResultDescr, int *length)
+{
+ int bytesPeeked;
+ char buffer[2048], *peol;
+
+ while (true) {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags);
+ if (bytesPeeked == 0) {
+ SetLastError(ERROR_HANDLE_EOF);
+ return 0;
+ }
+ if (bytesPeeked == SOCKET_ERROR)
+ return 0;
+
+ buffer[bytesPeeked] = '\0';
+ if ((peol = strchr(buffer, '\n')) != nullptr)
+ break;
+
+ if ((int)mir_strlen(buffer) < bytesPeeked) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+ if (bytesPeeked == _countof(buffer) - 1) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ if (Miranda_IsTerminated())
+ return 0;
+ Sleep(10);
+ }
+
+ if (peol == buffer) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ *peol = '\0';
+
+ if (_strnicmp(buffer, "HTTP/", 5)) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ size_t off = strcspn(buffer, " \t");
+ if (off >= (unsigned)bytesPeeked)
+ return 0;
+
+ char *pResultCode = buffer + off;
+ *(pResultCode++) = 0;
+
+ char *pResultDescr;
+ *resultCode = strtol(pResultCode, &pResultDescr, 10);
+
+ if (ppszResultDescr)
+ *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr));
+
+ if (length)
+ *length = peol - buffer + 1;
+ return 1;
+}
+
+static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader)
+{
+ bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH);
+
+ if (sendContentLengthHeader && sendData)
+ httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength);
+ else
+ httpRequest.AppendFormat("\r\n");
+
+ uint32_t hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags);
+ if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) {
+ uint32_t sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags);
+
+ bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR;
+ }
+
+ return bytesSent;
+}
+
+MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr)
+{
+ NETLIBHTTPREQUEST *nlhrReply = nullptr;
+ HttpSecurityContext httpSecurity;
+
+ char *szHost = nullptr, *szNewUrl = nullptr;
+ char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr;
+ int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader;
+ int bytesSent = 0;
+ bool lastFirstLineFail = false;
+
+ if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ NetlibUser *nlu = nlc->nlu;
+ if (GetNetlibHandleType(nlu) != NLH_USER) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT;
+
+ const char *pszRequest;
+ switch (nlhr->requestType) {
+ case REQUEST_GET: pszRequest = "GET"; break;
+ case REQUEST_POST: pszRequest = "POST"; break;
+ case REQUEST_CONNECT: pszRequest = "CONNECT"; break;
+ case REQUEST_HEAD: pszRequest = "HEAD"; break;
+ case REQUEST_PUT: pszRequest = "PUT"; break;
+ case REQUEST_DELETE: pszRequest = "DELETE"; break;
+ case REQUEST_PATCH: pszRequest = "PATCH"; break;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+
+ const char *pszFullUrl = nlhr->szUrl;
+ const char *pszUrl = nullptr;
+
+ unsigned complete = false;
+ int count = 11;
+ while (--count) {
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
+ nlc = nullptr;
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!NetlibReconnect(nlc)) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!pszUrl) {
+ pszUrl = pszFullUrl;
+ if (!(nlhr->flags & NLHRF_MANUALHOST)) {
+ bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL);
+
+ const char *ppath, *phost;
+ phost = strstr(pszUrl, "://");
+ if (phost == nullptr) phost = pszUrl;
+ else phost += 3;
+ ppath = strchr(phost, '/');
+ if (ppath == phost)
+ phost = nullptr;
+
+ replaceStr(szHost, phost);
+ if (ppath && phost)
+ szHost[ppath - phost] = 0;
+
+ if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy)
+ pszUrl = ppath ? ppath : "/";
+
+ if (usingProxy && phost && !nlc->dnsThroughProxy) {
+ char *tszHost = mir_strdup(phost);
+ if (ppath)
+ tszHost[ppath - phost] = 0;
+ char *cln = strchr(tszHost, ':'); if (cln) *cln = 0;
+
+ if (inet_addr(tszHost) == INADDR_NONE) {
+ in_addr ip;
+ if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) {
+ mir_free(szHost);
+ if (cln) *cln = ':';
+ szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach();
+ }
+ }
+ mir_free(tszHost);
+ }
+ }
+ }
+
+ if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) {
+ if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) {
+ const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer);
+ if (szAuthMethodNlu) {
+ mir_free(pszProxyAuthHdr);
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete);
+ }
+ }
+ }
+ nlc->proxyAuthNeeded = false;
+
+ CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0);
+
+ // HTTP headers
+ doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0;
+ for (i = 0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Connection")) continue;
+ if (p.szValue == nullptr) continue;
+ httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue);
+ }
+ if (szHost && !doneHostHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost);
+ if (pszProxyAuthHdr && !doneProxyAuthHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr);
+ if (pszAuthHdr && !doneAuthHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr);
+ httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive");
+ httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive");
+
+ // Add Sticky Headers
+ if (nlu->szStickyHeaders != nullptr)
+ httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders);
+
+ // send it
+ bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader);
+ if (bytesSent == SOCKET_ERROR)
+ break;
+
+ // ntlm reply
+ if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD)
+ break;
+
+ uint32_t fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0);
+ uint32_t dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout;
+ if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) {
+ uint32_t err = GetLastError();
+ Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count);
+
+ // connection died while we were waiting
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
+ nlc = nullptr;
+ break;
+ }
+
+ if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ lastFirstLineFail = true;
+ continue;
+ }
+
+ int resultCode = nlhr->resultCode;
+ lastFirstLineFail = false;
+
+ uint32_t hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ uint32_t dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP;
+
+ if (resultCode == 100)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+
+ else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect
+ pszUrl = nullptr;
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply) {
+ auto *tmpUrl = Netlib_GetHeader(nlhrReply, "Location");
+ if (tmpUrl) {
+ size_t rlen = 0;
+ if (tmpUrl[0] == '/') {
+ const char *ppath, *phost;
+ phost = strstr(pszFullUrl, "://");
+ phost = phost ? phost + 3 : pszFullUrl;
+ ppath = strchr(phost, '/');
+ rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl);
+ }
+
+ nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1);
+
+ strncpy(nlc->szNewUrl, pszFullUrl, rlen);
+ mir_strcpy(nlc->szNewUrl + rlen, tmpUrl);
+ pszFullUrl = nlc->szNewUrl;
+ pszUrl = nullptr;
+
+ if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 401 && !doneAuthHeader) { //auth required
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ replaceStr(pszAuthHdr, nullptr);
+ if (nlhrReply) {
+ char *szAuthStr = nullptr;
+ if (!complete) {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider);
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge))
+ complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr;
+
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszAuthHdr == nullptr) {
+ proxyAuthList.add(szHost, nullptr);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr;
+ if (nlhrReply) {
+ char *szAuthStr = nullptr;
+ if (!complete) {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider);
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge + 1))
+ complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr;
+
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszProxyAuthHdr == nullptr) {
+ proxyAuthList.add(nlc->szProxyServer, nullptr);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else break;
+
+ if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader)
+ replaceStr(pszProxyAuthHdr, nullptr);
+
+ if (pszAuthHdr && resultCode != 401 && !doneAuthHeader)
+ replaceStr(pszAuthHdr, nullptr);
+
+ if (nlhrReply) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ nlhrReply = nullptr;
+ }
+ }
+
+ if (count == 0) bytesSent = SOCKET_ERROR;
+ if (nlhrReply)
+ Netlib_FreeHttpRequest(nlhrReply);
+
+ //clean up
+ mir_free(pszProxyAuthHdr);
+ mir_free(pszAuthHdr);
+ mir_free(szHost);
+ mir_free(szNewUrl);
+
+ if (nlc)
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ return bytesSent;
+}
+
+MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr)
+{
+ if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ if (nlhr->headers) {
+ for (int i = 0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ mir_free(p.szName);
+ mir_free(p.szValue);
+ }
+ mir_free(nlhr->headers);
+ }
+ mir_free(nlhr->pData);
+ mir_free(nlhr->szResultDescr);
+ mir_free(nlhr->szUrl);
+ mir_free(nlhr);
+ return true;
+}
+
+#define NHRV_BUF_SIZE 8192
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags)
+{
+ NetlibConnection *nlc = (NetlibConnection*)hConnection;
+ if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
+ return nullptr;
+
+ uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT;
+ NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST));
+ nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+ nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions
+ nlhr->requestType = REQUEST_RESPONSE;
+
+ int firstLineLength = 0;
+ if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ return nullptr;
+ }
+
+ char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1);
+ int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT);
+ if (bytesPeeked != firstLineLength) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ if (bytesPeeked != SOCKET_ERROR)
+ SetLastError(ERROR_HANDLE_EOF);
+ return nullptr;
+ }
+
+ // Make sure all headers arrived
+ MBinBuffer buf;
+ int headersCount = 0;
+ bytesPeeked = 0;
+ for (bool headersCompleted = false; !headersCompleted;) {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE);
+ if (bytesPeeked == 0)
+ break;
+
+ if (bytesPeeked == SOCKET_ERROR) {
+ bytesPeeked = 0;
+ break;
+ }
+
+ buf.append(buffer, bytesPeeked);
+
+ headersCount = 0;
+ for (char *pbuffer = (char*)buf.data();; headersCount++) {
+ char *peol = strchr(pbuffer, '\n');
+ if (peol == nullptr) break;
+ if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) {
+ bytesPeeked = peol - (char*)buf.data() + 1;
+ headersCompleted = true;
+ break;
+ }
+ pbuffer = peol + 1;
+ }
+ }
+
+ if (bytesPeeked <= 0) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ return nullptr;
+ }
+
+ // Receive headers
+ nlhr->headersCount = headersCount;
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount);
+
+ headersCount = 0;
+ for (char *pbuffer = (char*)buf.data();; headersCount++) {
+ char *peol = strchr(pbuffer, '\n');
+ if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r'))
+ break;
+ *peol = 0;
+
+ char *pColon = strchr(pbuffer, ':');
+ if (pColon == nullptr) {
+ Netlib_FreeHttpRequest(nlhr); nlhr = nullptr;
+ SetLastError(ERROR_INVALID_DATA);
+ break;
+ }
+
+ *pColon = 0;
+ nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer));
+ nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1));
+ pbuffer = peol + 1;
+ }
+
+ // remove processed data
+ buf.remove(bytesPeeked);
+ nlc->foreBuf.appendBefore(buf.data(), buf.length());
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ return nlhr;
+}
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) ||
+ nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) ||
+ nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION)
+ nlhr->nlc = nullptr;
+
+ NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc);
+ if (nlc == nullptr)
+ return nullptr;
+
+ NETLIBHTTPREQUEST nlhrSend = *nlhr;
+ nlhrSend.flags |= NLHRF_SMARTREMOVEHOST;
+
+ bool doneUserAgentHeader = Netlib_GetHeader(nlhr, "User-Agent") != nullptr;
+ bool doneAcceptEncoding = Netlib_GetHeader(nlhr, "Accept-Encoding") != nullptr;
+ if (!doneUserAgentHeader || !doneAcceptEncoding) {
+ nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2));
+ memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount);
+ }
+
+ char szUserAgent[64];
+ if (!doneUserAgentHeader) {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent;
+ ++nlhrSend.headersCount;
+
+ char szMirandaVer[64];
+ strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE);
+ #if defined(_WIN64)
+ strncat_s(szMirandaVer, " x64", _TRUNCATE);
+ #endif
+
+ char *pspace = strchr(szMirandaVer, ' ');
+ if (pspace) {
+ *pspace++ = '\0';
+ mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace);
+ }
+ else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer);
+ }
+ if (!doneAcceptEncoding) {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip";
+ ++nlhrSend.headersCount;
+ }
+ if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) {
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ nlhr->resultCode = nlhrSend.resultCode;
+ Netlib_CloseHandle(nlc);
+ return nullptr;
+ }
+ if (!doneUserAgentHeader || !doneAcceptEncoding)
+ mir_free(nlhrSend.headers);
+
+ uint32_t dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ uint32_t hflags =
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ NETLIBHTTPREQUEST *nlhrReply;
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = Netlib_RecvHttpHeaders(nlc);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply) {
+ nlhrReply->szUrl = nlc->szNewUrl;
+ nlc->szNewUrl = nullptr;
+ }
+
+ if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) {
+ Netlib_CloseHandle(nlc);
+ if (nlhrReply)
+ nlhrReply->nlc = nullptr;
+ }
+ else nlhrReply->nlc = nlc;
+
+ return nlhrReply;
+}
+
+void NetlibHttpSetLastErrorUsingHttpResult(int result)
+{
+ if (result >= 200 && result < 300) {
+ SetLastError(ERROR_SUCCESS);
+ return;
+ }
+ switch (result) {
+ case 400: SetLastError(ERROR_BAD_FORMAT); break;
+ case 401:
+ case 402:
+ case 403:
+ case 407: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 404: SetLastError(ERROR_FILE_NOT_FOUND); break;
+ case 405:
+ case 406: SetLastError(ERROR_INVALID_FUNCTION); break;
+ case 408: SetLastError(ERROR_TIMEOUT); break;
+ default: SetLastError(ERROR_GEN_FAILURE); break;
+ }
+}
+
+char* gzip_decode(char *gzip_data, int *len_ptr, int window)
+{
+ if (*len_ptr == 0) return nullptr;
+
+ int gzip_len = *len_ptr * 5;
+ char* output_data = nullptr;
+
+ int gzip_err;
+ z_stream zstr;
+
+ do {
+ output_data = (char*)mir_realloc(output_data, gzip_len+1);
+ if (output_data == nullptr)
+ break;
+
+ zstr.next_in = (Bytef*)gzip_data;
+ zstr.avail_in = *len_ptr;
+ zstr.zalloc = Z_NULL;
+ zstr.zfree = Z_NULL;
+ zstr.opaque = Z_NULL;
+ inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream));
+
+ zstr.next_out = (Bytef*)output_data;
+ zstr.avail_out = gzip_len;
+
+ gzip_err = inflate(&zstr, Z_FINISH);
+
+ inflateEnd(&zstr);
+ gzip_len *= 2;
+ if (gzip_len > 10000000)
+ break;
+ } while (gzip_err == Z_BUF_ERROR);
+
+ gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1;
+
+ if (gzip_len <= 0) {
+ mir_free(output_data);
+ output_data = nullptr;
+ }
+ else output_data[gzip_len] = 0;
+
+ *len_ptr = gzip_len;
+ return output_data;
+}
+
+static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, uint32_t flags)
+{
+ MBinBuffer buf;
+
+ while (true) {
+ char data[1000];
+ int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags);
+ if (recvResult <= 0 || recvResult >= _countof(data))
+ return SOCKET_ERROR;
+
+ buf.append(data, recvResult); // add chunk
+
+ auto *peol1 = (const char*)memchr(buf.data(), '\n', buf.length());
+ if (peol1 == nullptr)
+ continue;
+
+ auto *pStart = (const char *)buf.data();
+ int cbRest = int(peol1 - pStart) + 1;
+ const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest);
+ if (peol2 == nullptr)
+ continue;
+
+ int sz = peol2 - pStart + 1;
+ int r = strtol(first ? pStart : peol1 + 1, nullptr, 16);
+ if (r == 0) {
+ const char *peol3 = strchr(peol2 + 1, '\n');
+ if (peol3 == nullptr)
+ continue;
+ sz = peol3 - pStart + 1;
+ }
+ buf.remove(sz); // remove all our data from buffer
+ nlc->foreBuf.appendBefore(buf.data(), buf.length());
+ return r;
+ }
+}
+
+NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect)
+{
+ int dataLen = -1, i, chunkhdr = 0;
+ bool chunked = false;
+ int cenc = 0, cenctype = 0, close = 0;
+
+next:
+ NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
+ if (nlhrReply == nullptr)
+ return nullptr;
+
+ if (nlhrReply->resultCode == 100) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ goto next;
+ }
+
+ if (nlhrReply->resultCode == 204)
+ dataLen = 0;
+
+ for (i = 0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (!mir_strcmpi(p.szName, "Content-Length"))
+ dataLen = atoi(p.szValue);
+
+ if (!mir_strcmpi(p.szName, "Content-Encoding")) {
+ cenc = i;
+ if (strstr(p.szValue, "gzip"))
+ cenctype = 1;
+ else if (strstr(p.szValue, "deflate"))
+ cenctype = 2;
+ }
+
+ if (!mir_strcmpi(p.szName, "Connection"))
+ close = !mir_strcmpi(p.szValue, "close");
+
+ if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) {
+ chunked = true;
+ chunkhdr = i;
+ dataLen = -1;
+ }
+ }
+
+ if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) {
+ int recvResult, chunksz = -1;
+ int dataBufferAlloced;
+
+ if (chunked) {
+ chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0));
+ if (chunksz == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ dataLen = chunksz;
+ }
+ dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+
+ while (chunksz != 0) {
+ while (true) {
+ recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT,
+ nlhrReply->pData + nlhrReply->dataLength,
+ dataBufferAlloced - nlhrReply->dataLength - 1,
+ dflags | (cenctype ? MSG_NODUMP : 0));
+
+ if (recvResult == 0) break;
+ if (recvResult == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ nlhrReply->dataLength += recvResult;
+
+ if (dataLen >= 0) {
+ if (nlhrReply->dataLength >= dataLen)
+ break;
+ }
+ else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) {
+ dataBufferAlloced += 2048;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ if (nlhrReply->pData == nullptr) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ }
+ Sleep(10);
+ }
+
+ if (!chunked)
+ break;
+
+ chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP);
+ if (chunksz == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ dataLen += chunksz;
+ dataBufferAlloced += chunksz;
+
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ }
+
+ nlhrReply->pData[nlhrReply->dataLength] = '\0';
+ }
+
+ if (chunked) {
+ nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16);
+ mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length");
+
+ nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16);
+ mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength);
+ }
+
+ if (cenctype) {
+ int bufsz = nlhrReply->dataLength;
+ char* szData = nullptr;
+
+ switch (cenctype) {
+ case 1:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS);
+ break;
+
+ case 2:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS);
+ if (bufsz < 0) {
+ bufsz = nlhrReply->dataLength;
+ szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS);
+ }
+ break;
+ }
+
+ if (bufsz > 0) {
+ Netlib_Dump(nlc, (uint8_t*)szData, bufsz, false, dflags | MSG_NOTITLE);
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = szData;
+ nlhrReply->dataLength = bufsz;
+
+ mir_free(nlhrReply->headers[cenc].szName);
+ mir_free(nlhrReply->headers[cenc].szValue);
+ memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0]));
+ }
+ else if (bufsz == 0) {
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = nullptr;
+ nlhrReply->dataLength = 0;
+ }
+ }
+
+ if (close &&
+ (nlc->proxyType != PROXYTYPE_HTTP || nlc->url.flags & NLOCF_SSL) &&
+ (!isConnect || nlhrReply->resultCode != 200))
+ NetlibDoCloseSocket(nlc);
+
+ return nlhrReply;
+}
diff --git a/src/mir_app/src/netlib_httpproxy.cpp b/src/mir_app/src/netlib_httpproxy.cpp index 4ea58fcd57..66adc7fe9a 100644 --- a/src/mir_app/src/netlib_httpproxy.cpp +++ b/src/mir_app/src/netlib_httpproxy.cpp @@ -1,85 +1,85 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -typedef enum -{ - reqHelloGet, - reqOldGet, - reqOldPost, - reqNewPost, -} -RequestType; - -///////////////////////////////////////////////////////////////////////////////////////// - -#define NETLIBHTTP_RETRYCOUNT 3 -#define NETLIBHTTP_RETRYTIMEOUT 2000 - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - mir_free(nlc->nlhpi.szHttpGetUrl); - mir_free(nlc->nlhpi.szHttpPostUrl); - - nlc->nlhpi.combinePackets = 1; - memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi)); - if (nlc->nlhpi.combinePackets == 0) - nlc->nlhpi.combinePackets = 1; - - nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl); - nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders) -{ - if (GetNetlibHandleType(nlu) != NLH_USER) - return ERROR_INVALID_PARAMETER; - - replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return -1; - - int oldTimeout = nlc->pollingTimeout; - nlc->pollingTimeout = iTimeout; - return oldTimeout; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+typedef enum
+{
+ reqHelloGet,
+ reqOldGet,
+ reqOldPost,
+ reqNewPost,
+}
+RequestType;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define NETLIBHTTP_RETRYCOUNT 3
+#define NETLIBHTTP_RETRYTIMEOUT 2000
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+
+ nlc->nlhpi.combinePackets = 1;
+ memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi));
+ if (nlc->nlhpi.combinePackets == 0)
+ nlc->nlhpi.combinePackets = 1;
+
+ nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl);
+ nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl);
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER)
+ return ERROR_INVALID_PARAMETER;
+
+ replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION)
+ return -1;
+
+ int oldTimeout = nlc->pollingTimeout;
+ nlc->pollingTimeout = iTimeout;
+ return oldTimeout;
+}
diff --git a/src/mir_app/src/netlib_log.cpp b/src/mir_app/src/netlib_log.cpp index 4a1cea21df..1ddbb63f6f 100644 --- a/src/mir_app/src/netlib_log.cpp +++ b/src/mir_app/src/netlib_log.cpp @@ -1,578 +1,578 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define MS_NETLIB_LOGWIN "Netlib/Log/Win" - -#define TIMEFORMAT_NONE 0 -#define TIMEFORMAT_HHMMSS 1 -#define TIMEFORMAT_MILLISECONDS 2 -#define TIMEFORMAT_MICROSECONDS 3 -struct { - HWND hwndOpts; - bool toOutputDebugString, toFile, toLog; - bool showUser, rotateLogs, bPrintDate; - bool dumpSent, dumpRecv, dumpProxy, dumpSsl; - bool textDumps, autoDetectText; - int timeFormat; - CMStringW tszFile, tszUserFile; -} -static logOptions = {}; - -struct LOGMSG -{ - const char* pszHead; - const char* pszMsg; -}; - -static __int64 mirandaStartTime, perfCounterFreq; -static int bIsActive = TRUE; -static HANDLE hLogEvent = nullptr; -static HANDLE hLogger = nullptr; - -static void InitLog() -{ - logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", true) != 0; - logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", true) != 0; - logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", true) != 0; - logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", false) != 0; - logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", true) != 0; - logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", true) != 0; - logOptions.bPrintDate = db_get_b(0, "Netlib", "PrintDate", false) != 0; - logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS); - logOptions.rotateLogs = db_get_b(0, "Netlib", "RotateLogs", false); - logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", true) != 0; - logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", false) != 0; - logOptions.toFile = db_get_b(0, "Netlib", "ToFile", false) != 0; - logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", true) != 0; - - if (hLogger) { - mir_closeLog(hLogger); - hLogger = nullptr; - } - - ptrW szBuf(db_get_wsa(0, "Netlib", "File")); - if (mir_wstrlen(szBuf)) { - logOptions.tszUserFile = szBuf.get(); - - wchar_t path[MAX_PATH]; - PathToAbsoluteW(VARSW(szBuf), path); - logOptions.tszFile = path; - } - else { - db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt"); - logOptions.tszFile = VARSW(logOptions.tszUserFile); - } - - if (logOptions.toFile) { - CMStringW wszFileName = logOptions.tszFile; - if (logOptions.rotateLogs) { - int iLogNumber = db_get_dw(0, "Netlib", "RotateId"); - wszFileName.AppendFormat(L".%d", iLogNumber); - db_set_dw(0, "Netlib", "RotateId", (iLogNumber + 1) % 10); - } - - hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), wszFileName, 0); - } -} - -static const wchar_t *szTimeFormats[] = -{ - LPGENW("No times"), - LPGENW("Standard hh:mm:ss times"), - LPGENW("Times in milliseconds"), - LPGENW("Times in microseconds") -}; - -class CLogOptionsDlg : public CDlgBase -{ - CCtrlEdit edtFileName; - CCtrlCombo cmbTimeFormat; - CCtrlButton btnRunNow, btnFileName, btnRunAtStart; - CCtrlTreeView treeFilter; - -public: - CLogOptionsDlg() : - CDlgBase(g_plugin, IDD_NETLIBLOGOPTS), - treeFilter(this, IDC_FILTER), - edtFileName(this, IDC_FILENAME), - cmbTimeFormat(this, IDC_TIMEFORMAT), - btnRunNow(this, IDC_RUNNOW), - btnFileName(this, IDC_FILENAMEBROWSE), - btnRunAtStart(this, IDC_RUNATSTARTBROWSE) - { - btnFileName.OnClick = btnRunAtStart.OnClick = Callback(this, &CLogOptionsDlg::onClick_Browse); - - edtFileName.OnChange = Callback(this, &CLogOptionsDlg::onChange_FileName); - } - - bool OnInitDialog() override - { - logOptions.hwndOpts = m_hwnd; - - CheckDlgButton(m_hwnd, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWDATE, logOptions.bPrintDate ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_LOGROTATE, logOptions.rotateLogs ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED); - - for (auto &it : szTimeFormats) - cmbTimeFormat.AddString(TranslateW(it)); - cmbTimeFormat.SetCurSel(logOptions.timeFormat); - - edtFileName.SetText(logOptions.tszUserFile); - SetDlgItemText(m_hwnd, IDC_PATH, logOptions.tszFile); - - ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart")); - if (szRun) - SetDlgItemTextA(m_hwnd, IDC_RUNATSTART, szRun); - - SetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES)); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - - for (auto &it : netlibUser) { - tvis.item.pszText = it->user.szDescriptiveName.w; - tvis.item.lParam = netlibUser.indexOf(&it); - tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1); - treeFilter.InsertItem(&tvis); - } - - tvis.item.lParam = -1; - tvis.item.pszText = TranslateT("(Miranda core logging)"); - tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1); - treeFilter.InsertItem(&tvis); - return true; - } - - bool OnApply() override - { - wchar_t str[MAX_PATH]; - GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); - db_set_ws(0, "Netlib", "RunAtStart", str); - - edtFileName.GetText(str, _countof(str)); - logOptions.tszUserFile = rtrimw(str); - db_set_ws(0, "Netlib", "File", str); - - GetDlgItemText(m_hwnd, IDC_PATH, str, _countof(str)); - logOptions.tszFile = rtrimw(str); - - db_set_b(0, "Netlib", "ToFile", IsDlgButtonChecked(m_hwnd, IDC_TOFILE)); - db_set_b(0, "Netlib", "DumpSsl", IsDlgButtonChecked(m_hwnd, IDC_DUMPSSL)); - db_set_b(0, "Netlib", "DumpRecv", IsDlgButtonChecked(m_hwnd, IDC_DUMPRECV)); - db_set_b(0, "Netlib", "DumpSent", IsDlgButtonChecked(m_hwnd, IDC_DUMPSENT)); - db_set_b(0, "Netlib", "ShowUser", IsDlgButtonChecked(m_hwnd, IDC_SHOWNAMES)); - db_set_b(0, "Netlib", "DumpProxy", IsDlgButtonChecked(m_hwnd, IDC_DUMPPROXY)); - db_set_b(0, "Netlib", "PrintDate", IsDlgButtonChecked(m_hwnd, IDC_SHOWDATE)); - db_set_b(0, "Netlib", "TextDumps", IsDlgButtonChecked(m_hwnd, IDC_TEXTDUMPS)); - db_set_b(0, "Netlib", "RotateLogs", IsDlgButtonChecked(m_hwnd, IDC_LOGROTATE)); - db_set_b(0, "Netlib", "AutoDetectText", IsDlgButtonChecked(m_hwnd, IDC_AUTODETECTTEXT)); - db_set_b(0, "Netlib", "ShowLogOptsAtStart", IsDlgButtonChecked(m_hwnd, IDC_SHOWTHISDLGATSTART)); - db_set_b(0, "Netlib", "ToOutputDebugString", IsDlgButtonChecked(m_hwnd, IDC_TOOUTPUTDEBUGSTRING)); - - db_set_b(0, "Netlib", "TimeFormat", cmbTimeFormat.GetCurSel()); - - TVITEMEX tvi = {}; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; - tvi.hItem = treeFilter.GetRoot(); - - while (tvi.hItem) { - treeFilter.GetItem(&tvi); - bool checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2); - - if (tvi.lParam == -1) { - logOptions.toLog = checked; - db_set_dw(0, "Netlib", "NLlog", checked); - } - else if (tvi.lParam < netlibUser.getCount()) { - netlibUser[tvi.lParam]->toLog = checked; - db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked); - } - - tvi.hItem = treeFilter.GetNextSibling(tvi.hItem); - } - - InitLog(); - return true; - } - - void OnDestroy() override - { - ImageList_Destroy(TreeView_GetImageList(GetDlgItem(m_hwnd, IDC_FILTER), TVSIL_STATE)); - logOptions.hwndOpts = nullptr; - } - - void onChange_FileName(CCtrlEdit *pEdit) - { - if (pEdit->GetHwnd() == GetFocus()) - CheckDlgButton(m_hwnd, IDC_TOFILE, BST_CHECKED); - - wchar_t path[MAX_PATH]; - pEdit->GetText(path, _countof(path)); - PathToAbsoluteW(VARSW(path), path); - SetDlgItemText(m_hwnd, IDC_PATH, path); - } - - void onClick_Browse(CCtrlButton *pButton) - { - wchar_t str[MAX_PATH]; - GetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str, _countof(str)); - - wchar_t filter[200]; - mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT; - if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) - ofn.lpstrTitle = TranslateT("Select where log file will be created"); - else { - ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - ofn.lpstrTitle = TranslateT("Select program to be run"); - } - ofn.lpstrFilter = filter; - ofn.lpstrFile = str; - ofn.nMaxFile = _countof(str) - 2; - ofn.nMaxFileTitle = MAX_PATH; - if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) { - if (!GetSaveFileName(&ofn)) - return; - } - else if (!GetOpenFileName(&ofn)) - return; - - if (pButton->GetCtrlId() == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) { - memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t))); - str[0] = '"'; - mir_wstrcat(str, L"\""); - } - SetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str); - } - - void onClick_RunNow(CCtrlButton *) - { - wchar_t str[MAX_PATH]; - GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); - if (!str[0]) - return; - - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcessW(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } -}; - -void NetlibLogShowOptions(void) -{ - if (logOptions.hwndOpts == nullptr) - (new CLogOptionsDlg())->Create(); - SetForegroundWindow(logOptions.hwndOpts); -} - -static INT_PTR ShowOptions(WPARAM, LPARAM) -{ - NetlibLogShowOptions(); - return 0; -} - -int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags) -{ - if (!bIsActive) - return 0; - - uint32_t dwOriginalLastError = GetLastError(); - - if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - /* if the Netlib user handle is nullptr, just pretend its not */ - if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog)) - return 1; - - LARGE_INTEGER liTimeNow; - char szDate[32], szTime[32], szHead[128]; - switch (logOptions.timeFormat) { - case TIMEFORMAT_HHMMSS: - GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime)); - mir_strcat(szTime, " "); - break; - - case TIMEFORMAT_MILLISECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - case TIMEFORMAT_MICROSECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - default: - szTime[0] = 0; - break; - } - - if (logOptions.bPrintDate) { - GetDateFormatA(LOCALE_USER_DEFAULT, 0, nullptr, "yyyy-MM-dd", szDate, _countof(szDate)); - mir_strcat(szDate, " "); - } - else szDate[0] = 0; - - if (flags & MSG_NOTITLE) - szHead[0] = 0; - else { - char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr; - if (szUser) - mir_snprintf(szHead, "[%s%s%04X] [%s] ", szDate, szTime, GetCurrentThreadId(), szUser); - else - mir_snprintf(szHead, "[%s%s%04X] ", szDate, szTime, GetCurrentThreadId()); - } - - if (logOptions.toOutputDebugString) { - if (szHead[0]) - OutputDebugStringA(szHead); - OutputDebugStringA(pszMsg); - OutputDebugStringA("\n"); - } - - if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) { - size_t len = mir_strlen(pszMsg); - mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n"); - } - - LOGMSG logMsg = { szHead, pszMsg }; - NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg); - - SetLastError(dwOriginalLastError); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void PROTO_INTERFACE::debugLogA(const char *szFormat, ...) -{ - char buf[4096]; - va_list args; - va_start(args, szFormat); - int res = _vsnprintf(buf, _countof(buf), szFormat, args); - NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0); - va_end(args); -} - -void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...) -{ - wchar_t buf[4096]; - va_list args; - va_start(args, wszFormat); - int res = _vsnwprintf(buf, _countof(buf), wszFormat, args); - NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - char szText[8000]; - mir_vsnprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, szText, 0); -} - -MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...) -{ - va_list va; - va_start(va, fmt); - wchar_t szText[8000]; - mir_vsnwprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr) -{ - return NetlibLog_Worker(hUser, pszStr, 0); -} - -MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr) -{ - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *pBuf, size_t len, bool bIsSent, int flags) -{ - // This section checks a number of conditions and aborts - // the dump if the data should not be written to the log - - // Check packet flags - if (flags & (MSG_PEEK | MSG_NODUMP)) - return; - - // Check user's log settings - if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty()))) - return; - if ((bIsSent && !logOptions.dumpSent) || (!bIsSent && !logOptions.dumpRecv)) - return; - if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy) - return; - if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl) - return; - - NetlibUser *nlu; - CMStringA str; - { - mir_cslock lock(csConnectionHeader); - - nlu = nlc ? nlc->nlu : nullptr; - if (!(flags & MSG_NOTITLE)) - str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, bIsSent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : ""); - } - - // check filter settings - if (nlu == nullptr) { - if (!logOptions.toLog) - return; - } - else if (!nlu->toLog) - return; - - const uint8_t *buf = (const uint8_t *)pBuf; - - bool isText = true; - if (!logOptions.textDumps) - isText = false; - else if (!(flags & MSG_DUMPASTEXT)) { - if (logOptions.autoDetectText) { - for (size_t i = 0; i < len; i++) { - if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) { - isText = false; - break; - } - } - } - else isText = false; - } - - // Text data - if (isText) { - str.Append((const char*)buf, (int)len); - } - // Binary data - else { - for (int line = 0;; line += 16) { - auto *p = buf + line; - int colsInLine = min(16, (int)len - line); - if (colsInLine == 16) - str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ", - line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); - else { - str.AppendFormat("%08X: ", line); - - // Dump data as hex - int col; - for (col = 0; col < colsInLine; col++) - str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' '); - - // Fill out last line with blanks - for (; col < 16; col++) - str.Append(" "); - - str.AppendChar(' '); - } - - for (int col = 0; col < colsInLine; col++) - str.AppendChar((p[col] < ' ') ? '.' : p[col]); - - if (len - line <= 16) - break; - - str.AppendChar('\r'); // End each line with a break - str.AppendChar('\n'); // End each line with a break - } - } - - NetlibLog_Worker(nlu, str, flags); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLogInit(void) -{ - LARGE_INTEGER li; - QueryPerformanceFrequency(&li); - perfCounterFreq = li.QuadPart; - QueryPerformanceCounter(&li); - mirandaStartTime = li.QuadPart; - - CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions); - hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP); - - InitLog(); - - if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0)) - NetlibLogShowOptions(); - - ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart")); - if (szBuf) { - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - } -} - -void NetlibLogShutdown(void) -{ - bIsActive = FALSE; - DestroyHookableEvent(hLogEvent); hLogEvent = nullptr; - if (IsWindow(logOptions.hwndOpts)) - DestroyWindow(logOptions.hwndOpts); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#define MS_NETLIB_LOGWIN "Netlib/Log/Win"
+
+#define TIMEFORMAT_NONE 0
+#define TIMEFORMAT_HHMMSS 1
+#define TIMEFORMAT_MILLISECONDS 2
+#define TIMEFORMAT_MICROSECONDS 3
+struct {
+ HWND hwndOpts;
+ bool toOutputDebugString, toFile, toLog;
+ bool showUser, rotateLogs, bPrintDate;
+ bool dumpSent, dumpRecv, dumpProxy, dumpSsl;
+ bool textDumps, autoDetectText;
+ int timeFormat;
+ CMStringW tszFile, tszUserFile;
+}
+static logOptions = {};
+
+struct LOGMSG
+{
+ const char* pszHead;
+ const char* pszMsg;
+};
+
+static __int64 mirandaStartTime, perfCounterFreq;
+static int bIsActive = TRUE;
+static HANDLE hLogEvent = nullptr;
+static HANDLE hLogger = nullptr;
+
+static void InitLog()
+{
+ logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", true) != 0;
+ logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", true) != 0;
+ logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", true) != 0;
+ logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", false) != 0;
+ logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", true) != 0;
+ logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", true) != 0;
+ logOptions.bPrintDate = db_get_b(0, "Netlib", "PrintDate", false) != 0;
+ logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS);
+ logOptions.rotateLogs = db_get_b(0, "Netlib", "RotateLogs", false);
+ logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", true) != 0;
+ logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", false) != 0;
+ logOptions.toFile = db_get_b(0, "Netlib", "ToFile", false) != 0;
+ logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", true) != 0;
+
+ if (hLogger) {
+ mir_closeLog(hLogger);
+ hLogger = nullptr;
+ }
+
+ ptrW szBuf(db_get_wsa(0, "Netlib", "File"));
+ if (mir_wstrlen(szBuf)) {
+ logOptions.tszUserFile = szBuf.get();
+
+ wchar_t path[MAX_PATH];
+ PathToAbsoluteW(VARSW(szBuf), path);
+ logOptions.tszFile = path;
+ }
+ else {
+ db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt");
+ logOptions.tszFile = VARSW(logOptions.tszUserFile);
+ }
+
+ if (logOptions.toFile) {
+ CMStringW wszFileName = logOptions.tszFile;
+ if (logOptions.rotateLogs) {
+ int iLogNumber = db_get_dw(0, "Netlib", "RotateId");
+ wszFileName.AppendFormat(L".%d", iLogNumber);
+ db_set_dw(0, "Netlib", "RotateId", (iLogNumber + 1) % 10);
+ }
+
+ hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), wszFileName, 0);
+ }
+}
+
+static const wchar_t *szTimeFormats[] =
+{
+ LPGENW("No times"),
+ LPGENW("Standard hh:mm:ss times"),
+ LPGENW("Times in milliseconds"),
+ LPGENW("Times in microseconds")
+};
+
+class CLogOptionsDlg : public CDlgBase
+{
+ CCtrlEdit edtFileName;
+ CCtrlCombo cmbTimeFormat;
+ CCtrlButton btnRunNow, btnFileName, btnRunAtStart;
+ CCtrlTreeView treeFilter;
+
+public:
+ CLogOptionsDlg() :
+ CDlgBase(g_plugin, IDD_NETLIBLOGOPTS),
+ treeFilter(this, IDC_FILTER),
+ edtFileName(this, IDC_FILENAME),
+ cmbTimeFormat(this, IDC_TIMEFORMAT),
+ btnRunNow(this, IDC_RUNNOW),
+ btnFileName(this, IDC_FILENAMEBROWSE),
+ btnRunAtStart(this, IDC_RUNATSTARTBROWSE)
+ {
+ btnFileName.OnClick = btnRunAtStart.OnClick = Callback(this, &CLogOptionsDlg::onClick_Browse);
+
+ edtFileName.OnChange = Callback(this, &CLogOptionsDlg::onChange_FileName);
+ }
+
+ bool OnInitDialog() override
+ {
+ logOptions.hwndOpts = m_hwnd;
+
+ CheckDlgButton(m_hwnd, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_SHOWDATE, logOptions.bPrintDate ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_LOGROTATE, logOptions.rotateLogs ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED);
+
+ for (auto &it : szTimeFormats)
+ cmbTimeFormat.AddString(TranslateW(it));
+ cmbTimeFormat.SetCurSel(logOptions.timeFormat);
+
+ edtFileName.SetText(logOptions.tszUserFile);
+ SetDlgItemText(m_hwnd, IDC_PATH, logOptions.tszFile);
+
+ ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart"));
+ if (szRun)
+ SetDlgItemTextA(m_hwnd, IDC_RUNATSTART, szRun);
+
+ SetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+
+ for (auto &it : netlibUser) {
+ tvis.item.pszText = it->user.szDescriptiveName.w;
+ tvis.item.lParam = netlibUser.indexOf(&it);
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1);
+ treeFilter.InsertItem(&tvis);
+ }
+
+ tvis.item.lParam = -1;
+ tvis.item.pszText = TranslateT("(Miranda core logging)");
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1);
+ treeFilter.InsertItem(&tvis);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ wchar_t str[MAX_PATH];
+ GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str));
+ db_set_ws(0, "Netlib", "RunAtStart", str);
+
+ edtFileName.GetText(str, _countof(str));
+ logOptions.tszUserFile = rtrimw(str);
+ db_set_ws(0, "Netlib", "File", str);
+
+ GetDlgItemText(m_hwnd, IDC_PATH, str, _countof(str));
+ logOptions.tszFile = rtrimw(str);
+
+ db_set_b(0, "Netlib", "ToFile", IsDlgButtonChecked(m_hwnd, IDC_TOFILE));
+ db_set_b(0, "Netlib", "DumpSsl", IsDlgButtonChecked(m_hwnd, IDC_DUMPSSL));
+ db_set_b(0, "Netlib", "DumpRecv", IsDlgButtonChecked(m_hwnd, IDC_DUMPRECV));
+ db_set_b(0, "Netlib", "DumpSent", IsDlgButtonChecked(m_hwnd, IDC_DUMPSENT));
+ db_set_b(0, "Netlib", "ShowUser", IsDlgButtonChecked(m_hwnd, IDC_SHOWNAMES));
+ db_set_b(0, "Netlib", "DumpProxy", IsDlgButtonChecked(m_hwnd, IDC_DUMPPROXY));
+ db_set_b(0, "Netlib", "PrintDate", IsDlgButtonChecked(m_hwnd, IDC_SHOWDATE));
+ db_set_b(0, "Netlib", "TextDumps", IsDlgButtonChecked(m_hwnd, IDC_TEXTDUMPS));
+ db_set_b(0, "Netlib", "RotateLogs", IsDlgButtonChecked(m_hwnd, IDC_LOGROTATE));
+ db_set_b(0, "Netlib", "AutoDetectText", IsDlgButtonChecked(m_hwnd, IDC_AUTODETECTTEXT));
+ db_set_b(0, "Netlib", "ShowLogOptsAtStart", IsDlgButtonChecked(m_hwnd, IDC_SHOWTHISDLGATSTART));
+ db_set_b(0, "Netlib", "ToOutputDebugString", IsDlgButtonChecked(m_hwnd, IDC_TOOUTPUTDEBUGSTRING));
+
+ db_set_b(0, "Netlib", "TimeFormat", cmbTimeFormat.GetCurSel());
+
+ TVITEMEX tvi = {};
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
+ tvi.hItem = treeFilter.GetRoot();
+
+ while (tvi.hItem) {
+ treeFilter.GetItem(&tvi);
+ bool checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2);
+
+ if (tvi.lParam == -1) {
+ logOptions.toLog = checked;
+ db_set_dw(0, "Netlib", "NLlog", checked);
+ }
+ else if (tvi.lParam < netlibUser.getCount()) {
+ netlibUser[tvi.lParam]->toLog = checked;
+ db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked);
+ }
+
+ tvi.hItem = treeFilter.GetNextSibling(tvi.hItem);
+ }
+
+ InitLog();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(m_hwnd, IDC_FILTER), TVSIL_STATE));
+ logOptions.hwndOpts = nullptr;
+ }
+
+ void onChange_FileName(CCtrlEdit *pEdit)
+ {
+ if (pEdit->GetHwnd() == GetFocus())
+ CheckDlgButton(m_hwnd, IDC_TOFILE, BST_CHECKED);
+
+ wchar_t path[MAX_PATH];
+ pEdit->GetText(path, _countof(path));
+ PathToAbsoluteW(VARSW(path), path);
+ SetDlgItemText(m_hwnd, IDC_PATH, path);
+ }
+
+ void onClick_Browse(CCtrlButton *pButton)
+ {
+ wchar_t str[MAX_PATH];
+ GetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str, _countof(str));
+
+ wchar_t filter[200];
+ mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = m_hwnd;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE)
+ ofn.lpstrTitle = TranslateT("Select where log file will be created");
+ else {
+ ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
+ ofn.lpstrTitle = TranslateT("Select program to be run");
+ }
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _countof(str) - 2;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) {
+ if (!GetSaveFileName(&ofn))
+ return;
+ }
+ else if (!GetOpenFileName(&ofn))
+ return;
+
+ if (pButton->GetCtrlId() == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) {
+ memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t)));
+ str[0] = '"';
+ mir_wstrcat(str, L"\"");
+ }
+ SetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str);
+ }
+
+ void onClick_RunNow(CCtrlButton *)
+ {
+ wchar_t str[MAX_PATH];
+ GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str));
+ if (!str[0])
+ return;
+
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+ CreateProcessW(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+};
+
+void NetlibLogShowOptions(void)
+{
+ if (logOptions.hwndOpts == nullptr)
+ (new CLogOptionsDlg())->Create();
+ SetForegroundWindow(logOptions.hwndOpts);
+}
+
+static INT_PTR ShowOptions(WPARAM, LPARAM)
+{
+ NetlibLogShowOptions();
+ return 0;
+}
+
+int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags)
+{
+ if (!bIsActive)
+ return 0;
+
+ uint32_t dwOriginalLastError = GetLastError();
+
+ if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ /* if the Netlib user handle is nullptr, just pretend its not */
+ if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog))
+ return 1;
+
+ LARGE_INTEGER liTimeNow;
+ char szDate[32], szTime[32], szHead[128];
+ switch (logOptions.timeFormat) {
+ case TIMEFORMAT_HHMMSS:
+ GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime));
+ mir_strcat(szTime, " ");
+ break;
+
+ case TIMEFORMAT_MILLISECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq,
+ 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ case TIMEFORMAT_MICROSECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq,
+ 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ default:
+ szTime[0] = 0;
+ break;
+ }
+
+ if (logOptions.bPrintDate) {
+ GetDateFormatA(LOCALE_USER_DEFAULT, 0, nullptr, "yyyy-MM-dd", szDate, _countof(szDate));
+ mir_strcat(szDate, " ");
+ }
+ else szDate[0] = 0;
+
+ if (flags & MSG_NOTITLE)
+ szHead[0] = 0;
+ else {
+ char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr;
+ if (szUser)
+ mir_snprintf(szHead, "[%s%s%04X] [%s] ", szDate, szTime, GetCurrentThreadId(), szUser);
+ else
+ mir_snprintf(szHead, "[%s%s%04X] ", szDate, szTime, GetCurrentThreadId());
+ }
+
+ if (logOptions.toOutputDebugString) {
+ if (szHead[0])
+ OutputDebugStringA(szHead);
+ OutputDebugStringA(pszMsg);
+ OutputDebugStringA("\n");
+ }
+
+ if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) {
+ size_t len = mir_strlen(pszMsg);
+ mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
+ }
+
+ LOGMSG logMsg = { szHead, pszMsg };
+ NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
+
+ SetLastError(dwOriginalLastError);
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void PROTO_INTERFACE::debugLogA(const char *szFormat, ...)
+{
+ char buf[4096];
+ va_list args;
+ va_start(args, szFormat);
+ int res = _vsnprintf(buf, _countof(buf), szFormat, args);
+ NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0);
+ va_end(args);
+}
+
+void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...)
+{
+ wchar_t buf[4096];
+ va_list args;
+ va_start(args, wszFormat);
+ int res = _vsnwprintf(buf, _countof(buf), wszFormat, args);
+ NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0);
+ va_end(args);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ char szText[8000];
+ mir_vsnprintf(szText, _countof(szText), fmt, va);
+ va_end(va);
+ return NetlibLog_Worker(hUser, szText, 0);
+}
+
+MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ wchar_t szText[8000];
+ mir_vsnwprintf(szText, _countof(szText), fmt, va);
+ va_end(va);
+ return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr)
+{
+ return NetlibLog_Worker(hUser, pszStr, 0);
+}
+
+MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr)
+{
+ return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *pBuf, size_t len, bool bIsSent, int flags)
+{
+ // This section checks a number of conditions and aborts
+ // the dump if the data should not be written to the log
+
+ // Check packet flags
+ if (flags & (MSG_PEEK | MSG_NODUMP))
+ return;
+
+ // Check user's log settings
+ if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty())))
+ return;
+ if ((bIsSent && !logOptions.dumpSent) || (!bIsSent && !logOptions.dumpRecv))
+ return;
+ if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
+ return;
+ if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
+ return;
+
+ NetlibUser *nlu;
+ CMStringA str;
+ {
+ mir_cslock lock(csConnectionHeader);
+
+ nlu = nlc ? nlc->nlu : nullptr;
+ if (!(flags & MSG_NOTITLE))
+ str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, bIsSent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
+ }
+
+ // check filter settings
+ if (nlu == nullptr) {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ const uint8_t *buf = (const uint8_t *)pBuf;
+
+ bool isText = true;
+ if (!logOptions.textDumps)
+ isText = false;
+ else if (!(flags & MSG_DUMPASTEXT)) {
+ if (logOptions.autoDetectText) {
+ for (size_t i = 0; i < len; i++) {
+ if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) {
+ isText = false;
+ break;
+ }
+ }
+ }
+ else isText = false;
+ }
+
+ // Text data
+ if (isText) {
+ str.Append((const char*)buf, (int)len);
+ }
+ // Binary data
+ else {
+ for (int line = 0;; line += 16) {
+ auto *p = buf + line;
+ int colsInLine = min(16, (int)len - line);
+ if (colsInLine == 16)
+ str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ",
+ line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ else {
+ str.AppendFormat("%08X: ", line);
+
+ // Dump data as hex
+ int col;
+ for (col = 0; col < colsInLine; col++)
+ str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' ');
+
+ // Fill out last line with blanks
+ for (; col < 16; col++)
+ str.Append(" ");
+
+ str.AppendChar(' ');
+ }
+
+ for (int col = 0; col < colsInLine; col++)
+ str.AppendChar((p[col] < ' ') ? '.' : p[col]);
+
+ if (len - line <= 16)
+ break;
+
+ str.AppendChar('\r'); // End each line with a break
+ str.AppendChar('\n'); // End each line with a break
+ }
+ }
+
+ NetlibLog_Worker(nlu, str, flags);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibLogInit(void)
+{
+ LARGE_INTEGER li;
+ QueryPerformanceFrequency(&li);
+ perfCounterFreq = li.QuadPart;
+ QueryPerformanceCounter(&li);
+ mirandaStartTime = li.QuadPart;
+
+ CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions);
+ hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP);
+
+ InitLog();
+
+ if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0))
+ NetlibLogShowOptions();
+
+ ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart"));
+ if (szBuf) {
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+ CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
+ }
+}
+
+void NetlibLogShutdown(void)
+{
+ bIsActive = FALSE;
+ DestroyHookableEvent(hLogEvent); hLogEvent = nullptr;
+ if (IsWindow(logOptions.hwndOpts))
+ DestroyWindow(logOptions.hwndOpts);
+}
diff --git a/src/mir_app/src/netlib_openconn.cpp b/src/mir_app/src/netlib_openconn.cpp index 49e584e4d3..7d9b0b9114 100644 --- a/src/mir_app/src/netlib_openconn.cpp +++ b/src/mir_app/src/netlib_openconn.cpp @@ -1,726 +1,726 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern mir_cs csNetlibUser; -extern uint32_t g_LastConnectionTick; -extern int connectionTimeout; -static int iUPnPCleanup = 0; - -#define RECV_DEFAULT_TIMEOUT 60000 - -//returns in network byte order -uint32_t DnsLookup(NetlibUser *nlu, const char *szHost) -{ - HOSTENT* host; - uint32_t ip = inet_addr(szHost); - if (ip != INADDR_NONE) - return ip; - - __try { - host = gethostbyname(szHost); - if (host) - return *(u_long*)host->h_addr_list[0]; - - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError()); - } - __except (EXCEPTION_EXECUTE_HANDLER) {} - - return 0; -} - -int WaitUntilReadable(SOCKET s, uint32_t dwTimeout, bool check) -{ - fd_set readfd; - TIMEVAL tv; - - if (s == INVALID_SOCKET) return SOCKET_ERROR; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - int result = select(0, &readfd, nullptr, nullptr, &tv); - if (result == 0 && !check) SetLastError(ERROR_TIMEOUT); - return result; -} - -int WaitUntilWritable(SOCKET s, uint32_t dwTimeout) -{ - fd_set writefd; - TIMEVAL tv; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&writefd); - FD_SET(s, &writefd); - - switch (select(0, nullptr, &writefd, nullptr, &tv)) { - case 0: - SetLastError(ERROR_TIMEOUT); - case SOCKET_ERROR: - return 0; - } - return 1; -} - -bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, uint32_t dwTimeout) -{ - int nReceived = 0; - uint32_t dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout; - - while ((dwTimeNow = GetTickCount()) < dwCompleteTime) { - if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false; - nReceived = Netlib_Recv(nlc, buf, len, flags); - if (nReceived <= 0) return false; - - buf += nReceived; - len -= nReceived; - if (len <= 0) return true; - } - SetLastError(ERROR_TIMEOUT); - return false; -} - -static int NetlibInitSocks4Connection(NetlibConnection *nlc) -{ - // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol - NetlibUrl &url = nlc->url; - if (url.szHost.IsEmpty()) - return 0; - - NetlibUser *nlu = nlc->nlu; - size_t nHostLen = mir_strlen(url.szHost) + 1; - size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1; - size_t len = 8 + nUserLen; - - char* pInit = (char*)alloca(len + nHostLen); - pInit[0] = 4; // SOCKS4 - pInit[1] = 1; //connect - *(PWORD)&pInit[2] = htons(url.port); - - if (nUserLen <= 1) pInit[8] = 0; - else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen); - - //if cannot resolve host, try resolving through proxy (requires SOCKS4a) - uint32_t ip = DnsLookup(nlu, url.szHost); - *(PDWORD)&pInit[4] = ip ? ip : 0x01000000; - if (!ip) { - memcpy(&pInit[len], url.szHost, nHostLen); - len += nHostLen; - } - - if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - char reply[8]; - if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - switch ((uint8_t)reply[1]) { - case 90: return 1; - case 91: SetLastError(ERROR_ACCESS_DENIED); break; - case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; - case 93: SetLastError(ERROR_INVALID_ACCESS); break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (uint8_t)reply[1], GetLastError()); - return 0; -} - -static int NetlibInitSocks5Connection(NetlibConnection *nlc) -{ - //rfc1928 - uint8_t buf[258]; - NetlibUser *nlu = nlc->nlu; - - buf[0] = 5; //yep, socks5 - buf[1] = 1; //one auth method - buf[2] = nlu->settings.useProxyAuth ? 2 : 0; - if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - //confirmation of auth method - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if ((buf[1] != 0 && buf[1] != 2)) { - SetLastError(ERROR_INVALID_ID_AUTHORITY); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError()); - return 0; - } - - if (buf[1] == 2) { //rfc1929 - size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser); - size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword); - uint8_t *pAuthBuf = (uint8_t*)mir_alloc(3 + nUserLen + nPassLen); - pAuthBuf[0] = 1; //auth version - pAuthBuf[1] = (uint8_t)nUserLen; - memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen); - pAuthBuf[2 + nUserLen] = (uint8_t)nPassLen; - memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen); - if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pAuthBuf); - return 0; - } - mir_free(pAuthBuf); - - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if (buf[1]) { - SetLastError(ERROR_ACCESS_DENIED); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - } - - size_t nHostLen; - uint32_t hostIP; - - NetlibUrl &url = nlc->url; - if (nlc->dnsThroughProxy) { - hostIP = inet_addr(url.szHost); - nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(url.szHost) + 1 : 4; - } - else { - hostIP = DnsLookup(nlu, url.szHost); - if (hostIP == 0) - return 0; - nHostLen = 4; - } - uint8_t *pInit = (uint8_t*)mir_alloc(6 + nHostLen); - pInit[0] = 5; //SOCKS5 - pInit[1] = url.flags & NLOCF_UDP ? 3 : 1; //connect or UDP - pInit[2] = 0; //reserved - if (hostIP == INADDR_NONE) { //DNS lookup through proxy - pInit[3] = 3; - pInit[4] = uint8_t(nHostLen - 1); - memcpy(pInit + 5, url.szHost, nHostLen - 1); - } - else { - pInit[3] = 1; - *(PDWORD)(pInit + 4) = hostIP; - } - *(PWORD)(pInit + 4 + nHostLen) = htons(url.port); - if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pInit); - return 0; - } - mir_free(pInit); - - if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - if (buf[0] != 5 || buf[1]) { - const char* err = "Unknown response"; - if (buf[0] != 5) - SetLastError(ERROR_BAD_FORMAT); - else { - switch (buf[1]) { - case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break; - case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break; - case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break; - case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break; - case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break; - case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break; - case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break; - case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - } - Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err); - return 0; - } - - int nRecvSize = 0; - switch (buf[3]) { - case 1:// ipv4 addr - nRecvSize = 5; - break; - case 3:// dns name addr - nRecvSize = buf[4] + 2; - break; - case 4:// ipv6 addr - nRecvSize = 17; - break; - default: - Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]); - return 0; - } - if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - //connected - return 1; -} - -static bool NetlibInitHttpsConnection(NetlibConnection *nlc) -{ - // rfc2817 - NetlibUrl &url = nlc->url; - CMStringA szUrl; - if (nlc->dnsThroughProxy) - szUrl.Format("%s:%u", url.szHost.c_str(), url.port); - else { - uint32_t ip = DnsLookup(nlc->nlu, url.szHost); - if (ip == 0) return false; - szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), url.port); - } - - NETLIBHTTPREQUEST nlhrSend = { 0 }; - nlhrSend.cbSize = sizeof(nlhrSend); - nlhrSend.requestType = REQUEST_CONNECT; - nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT; - nlhrSend.szUrl = szUrl.GetBuffer(); - - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) - return false; - - NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true); - if (nlhrReply == nullptr) - return false; - - if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) { - if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) { - Netlib_FreeHttpRequest(nlhrReply); - nlc->dnsThroughProxy = 0; - return NetlibInitHttpsConnection(nlc); - } - - NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); - Netlib_Logf(nlc->nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__, - nlc->nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr); - Netlib_FreeHttpRequest(nlhrReply); - return 0; - } - Netlib_FreeHttpRequest(nlhrReply); - return true; // connected -} - -static void FreePartiallyInitedConnection(NetlibConnection *nlc) -{ - uint32_t dwOriginalLastError = GetLastError(); - - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) - delete nlc; - - SetLastError(dwOriginalLastError); -} - -static bool my_connectIP(NetlibConnection *nlc) -{ - NetlibUser *nlu = nlc->nlu; - int rc = SOCKET_ERROR, retrycnt = 0; - u_long notblocking = 1; - uint32_t lasterr = 0; - static const TIMEVAL tv = { 1, 0 }; - - // if timeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway - int timeout = (nlc->timeout <= 0) ? 30 : nlc->timeout; - - // this is for XP SP2 where there is a default connection attempt limit of 10/second - if (connectionTimeout) { - WaitForSingleObject(hConnectionOpenMutex, 10000); - int waitdiff = GetTickCount() - g_LastConnectionTick; - if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); - g_LastConnectionTick = GetTickCount(); - ReleaseMutex(hConnectionOpenMutex); - - // might have died in between the wait - if (Miranda_IsTerminated()) - return false; - } - - char szPort[6]; - addrinfo *air = nullptr, *ai, hints = { 0 }; - - hints.ai_family = AF_UNSPEC; - - NetlibUrl &url = nlc->url; - if (url.flags & NLOCF_UDP) { - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - } - else { - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - } - - if (nlc->proxyType) { - if (!nlc->szProxyServer) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, url.szHost.c_str(), url.port); - - _itoa(nlc->wProxyPort, szPort, 10); - if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError()); - return false; - } - } - else { - if (url.szHost.IsEmpty()) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, url.szHost.c_str(), url.port); - - _itoa(url.port, szPort, 10); - - if (GetAddrInfoA(url.szHost, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", url.szHost.c_str(), WSAGetLastError()); - return false; - } - } - - for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) { - Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get()); -retry: - nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (nlc->s == INVALID_SOCKET) { - FreeAddrInfoA(air); - return false; - } - - // return the socket to non blocking - if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) { - FreeAddrInfoA(air); - return false; - } - - if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { - SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; - SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; - if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum)) - Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); - } - - // try a connect - if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) { - rc = 0; - break; - } - - // didn't work, was it cos of nonblocking? - if (WSAGetLastError() != WSAEWOULDBLOCK) { - rc = SOCKET_ERROR; - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - continue; - } - - while (true) { // timeout loop - fd_set r, w, e; - FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); - FD_SET(nlc->s, &r); - FD_SET(nlc->s, &w); - FD_SET(nlc->s, &e); - if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) - break; - - if (rc > 0) { - if (FD_ISSET(nlc->s, &w)) { - // connection was successful - rc = 0; - lasterr = 0; - } - if (FD_ISSET(nlc->s, &r)) { - // connection was closed - rc = SOCKET_ERROR; - lasterr = WSAECONNRESET; - } - if (FD_ISSET(nlc->s, &e)) { - // connection failed. - int len = sizeof(lasterr); - rc = SOCKET_ERROR; - getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); - if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - goto retry; - } - } - break; - } - else if (Miranda_IsTerminated()) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - - if (--timeout == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - } - - if (rc == 0) break; - - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - } - - FreeAddrInfoA(air); - - notblocking = 0; - if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); - if (rc && lasterr) SetLastError(lasterr); - return rc == 0; -} - -static int NetlibHttpFallbackToDirect(NetlibConnection *nlc) -{ - NetlibDoCloseSocket(nlc, true); - - Netlib_Logf(nlc->nlu, "Fallback to direct connection"); - - nlc->proxyAuthNeeded = false; - nlc->proxyType = 0; - replaceStr(nlc->szProxyServer, nullptr); - if (!my_connectIP(nlc)) { - Netlib_Logf(nlc->nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - return true; -} - -bool NetlibDoConnect(NetlibConnection *nlc) -{ - NetlibUser *nlu = nlc->nlu; - - replaceStr(nlc->szProxyServer, nullptr); - - bool usingProxy = false, forceHttps = false; - if (nlu->settings.useProxy) { - if (nlu->settings.proxyType == PROXYTYPE_IE) - usingProxy = NetlibGetIeProxyConn(nlc, false); - else { - if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) { - nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer); - nlc->wProxyPort = nlu->settings.wProxyPort; - nlc->proxyType = nlu->settings.proxyType; - usingProxy = true; - } - } - } - - while (!my_connectIP(nlc)) { - // if connection failed, the state of nlc might be unpredictable - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) { - // Fallback to direct only when using HTTP proxy, as this is what used by companies - // If other type of proxy used it's an indication of security nutcase, leave him alone - if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) { - usingProxy = false; - nlc->proxyType = 0; - Netlib_Logf(nlu, "Fallback to direct connection"); - continue; - } - if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) { - forceHttps = true; - usingProxy = NetlibGetIeProxyConn(nlc, true); - if (usingProxy) - continue; - } - } - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - - if (usingProxy && !((nlc->url.flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) { - if (!WaitUntilWritable(nlc->s, 30000)) - return false; - - switch (nlc->proxyType) { - case PROXYTYPE_SOCKS4: - if (!NetlibInitSocks4Connection(nlc)) - return false; - break; - - case PROXYTYPE_SOCKS5: - if (!NetlibInitSocks5Connection(nlc)) - return false; - break; - - case PROXYTYPE_HTTPS: - case PROXYTYPE_HTTP: - nlc->proxyAuthNeeded = true; - if (!NetlibInitHttpsConnection(nlc)) { - usingProxy = false; - if (!NetlibHttpFallbackToDirect(nlc)) - return false; - } - break; - - default: - SetLastError(ERROR_INVALID_PARAMETER); - FreePartiallyInitedConnection(nlc); - return false; - } - } - - Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nlc->url.szHost.c_str(), nlc->url.port); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size); - if (nlu->settings.useProxy) { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size); - ncei.remote.sin_family = AF_INET; - ncei.remote.sin_port = htons(nlc->url.port); - ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nlc->url.szHost); - } - else { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size); - } - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - if (NLOCF_SSL & nlc->url.flags) - return Netlib_StartSsl(nlc, nullptr) != 0; - - return true; -} - -bool NetlibReconnect(NetlibConnection *nlc) -{ - // a connection might be freed already - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return false; - - char buf[4]; - bool opened = nlc->s != INVALID_SOCKET; - if (opened) { - switch (WaitUntilReadable(nlc->s, 0, true)) { - case SOCKET_ERROR: - opened = false; - break; - - case 0: - opened = true; - break; - - case 1: - opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - NetlibDoCloseSocket(nlc, true); - } - - if (!opened) { - if (Miranda_IsTerminated()) - return false; - - return NetlibDoConnect(nlc); - } - return true; -} - -MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const char *szHost, int port, int timeout, int flags) -{ - if (szHost == nullptr || port == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING)) - return nullptr; - - Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", szHost, port, flags); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlu; - nlc->timeout = timeout; - nlc->url.szHost = szHost; - nlc->url.port = port; - nlc->url.flags = flags; - nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0; - - if (!NetlibDoConnect(nlc)) { - FreePartiallyInitedConnection(nlc); - return nullptr; - } - - if (iUPnPCleanup == 0) { - mir_cslock lck(csNetlibUser); - iUPnPCleanup = 1; - mir_forkthread(NetlibUPnPCleanup); - } - - return nlc; -} - -NetlibConnection::NetlibConnection() -{ - handleType = NLH_CONNECTION; - s = s2 = INVALID_SOCKET; - hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); - NetlibInitializeNestedCS(&ncsSend); - NetlibInitializeNestedCS(&ncsRecv); -} - -NetlibConnection::~NetlibConnection() -{ - handleType = 0; - - if (s != INVALID_SOCKET) - closesocket(s); - - mir_free(szNewUrl); - mir_free(szProxyServer); - - mir_free(nlhpi.szHttpPostUrl); - mir_free(nlhpi.szHttpGetUrl); - - NetlibDeleteNestedCS(&ncsSend); - NetlibDeleteNestedCS(&ncsRecv); - - CloseHandle(hOkToCloseEvent); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+extern mir_cs csNetlibUser;
+extern uint32_t g_LastConnectionTick;
+extern int connectionTimeout;
+static int iUPnPCleanup = 0;
+
+#define RECV_DEFAULT_TIMEOUT 60000
+
+//returns in network byte order
+uint32_t DnsLookup(NetlibUser *nlu, const char *szHost)
+{
+ HOSTENT* host;
+ uint32_t ip = inet_addr(szHost);
+ if (ip != INADDR_NONE)
+ return ip;
+
+ __try {
+ host = gethostbyname(szHost);
+ if (host)
+ return *(u_long*)host->h_addr_list[0];
+
+ Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError());
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER) {}
+
+ return 0;
+}
+
+int WaitUntilReadable(SOCKET s, uint32_t dwTimeout, bool check)
+{
+ fd_set readfd;
+ TIMEVAL tv;
+
+ if (s == INVALID_SOCKET) return SOCKET_ERROR;
+
+ tv.tv_sec = dwTimeout / 1000;
+ tv.tv_usec = (dwTimeout % 1000) * 1000;
+
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+
+ int result = select(0, &readfd, nullptr, nullptr, &tv);
+ if (result == 0 && !check) SetLastError(ERROR_TIMEOUT);
+ return result;
+}
+
+int WaitUntilWritable(SOCKET s, uint32_t dwTimeout)
+{
+ fd_set writefd;
+ TIMEVAL tv;
+
+ tv.tv_sec = dwTimeout / 1000;
+ tv.tv_usec = (dwTimeout % 1000) * 1000;
+
+ FD_ZERO(&writefd);
+ FD_SET(s, &writefd);
+
+ switch (select(0, nullptr, &writefd, nullptr, &tv)) {
+ case 0:
+ SetLastError(ERROR_TIMEOUT);
+ case SOCKET_ERROR:
+ return 0;
+ }
+ return 1;
+}
+
+bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, uint32_t dwTimeout)
+{
+ int nReceived = 0;
+ uint32_t dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout;
+
+ while ((dwTimeNow = GetTickCount()) < dwCompleteTime) {
+ if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false;
+ nReceived = Netlib_Recv(nlc, buf, len, flags);
+ if (nReceived <= 0) return false;
+
+ buf += nReceived;
+ len -= nReceived;
+ if (len <= 0) return true;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return false;
+}
+
+static int NetlibInitSocks4Connection(NetlibConnection *nlc)
+{
+ // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol
+ NetlibUrl &url = nlc->url;
+ if (url.szHost.IsEmpty())
+ return 0;
+
+ NetlibUser *nlu = nlc->nlu;
+ size_t nHostLen = mir_strlen(url.szHost) + 1;
+ size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1;
+ size_t len = 8 + nUserLen;
+
+ char* pInit = (char*)alloca(len + nHostLen);
+ pInit[0] = 4; // SOCKS4
+ pInit[1] = 1; //connect
+ *(PWORD)&pInit[2] = htons(url.port);
+
+ if (nUserLen <= 1) pInit[8] = 0;
+ else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen);
+
+ //if cannot resolve host, try resolving through proxy (requires SOCKS4a)
+ uint32_t ip = DnsLookup(nlu, url.szHost);
+ *(PDWORD)&pInit[4] = ip ? ip : 0x01000000;
+ if (!ip) {
+ memcpy(&pInit[len], url.szHost, nHostLen);
+ len += nHostLen;
+ }
+
+ if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ return 0;
+ }
+
+ char reply[8];
+ if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+
+ switch ((uint8_t)reply[1]) {
+ case 90: return 1;
+ case 91: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break;
+ case 93: SetLastError(ERROR_INVALID_ACCESS); break;
+ default: SetLastError(ERROR_INVALID_DATA); break;
+ }
+ Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (uint8_t)reply[1], GetLastError());
+ return 0;
+}
+
+static int NetlibInitSocks5Connection(NetlibConnection *nlc)
+{
+ //rfc1928
+ uint8_t buf[258];
+ NetlibUser *nlu = nlc->nlu;
+
+ buf[0] = 5; //yep, socks5
+ buf[1] = 1; //one auth method
+ buf[2] = nlu->settings.useProxyAuth ? 2 : 0;
+ if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ return 0;
+ }
+
+ //confirmation of auth method
+ if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+ if ((buf[1] != 0 && buf[1] != 2)) {
+ SetLastError(ERROR_INVALID_ID_AUTHORITY);
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError());
+ return 0;
+ }
+
+ if (buf[1] == 2) { //rfc1929
+ size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser);
+ size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword);
+ uint8_t *pAuthBuf = (uint8_t*)mir_alloc(3 + nUserLen + nPassLen);
+ pAuthBuf[0] = 1; //auth version
+ pAuthBuf[1] = (uint8_t)nUserLen;
+ memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen);
+ pAuthBuf[2 + nUserLen] = (uint8_t)nPassLen;
+ memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen);
+ if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ mir_free(pAuthBuf);
+ return 0;
+ }
+ mir_free(pAuthBuf);
+
+ if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+ if (buf[1]) {
+ SetLastError(ERROR_ACCESS_DENIED);
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+ }
+
+ size_t nHostLen;
+ uint32_t hostIP;
+
+ NetlibUrl &url = nlc->url;
+ if (nlc->dnsThroughProxy) {
+ hostIP = inet_addr(url.szHost);
+ nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(url.szHost) + 1 : 4;
+ }
+ else {
+ hostIP = DnsLookup(nlu, url.szHost);
+ if (hostIP == 0)
+ return 0;
+ nHostLen = 4;
+ }
+ uint8_t *pInit = (uint8_t*)mir_alloc(6 + nHostLen);
+ pInit[0] = 5; //SOCKS5
+ pInit[1] = url.flags & NLOCF_UDP ? 3 : 1; //connect or UDP
+ pInit[2] = 0; //reserved
+ if (hostIP == INADDR_NONE) { //DNS lookup through proxy
+ pInit[3] = 3;
+ pInit[4] = uint8_t(nHostLen - 1);
+ memcpy(pInit + 5, url.szHost, nHostLen - 1);
+ }
+ else {
+ pInit[3] = 1;
+ *(PDWORD)(pInit + 4) = hostIP;
+ }
+ *(PWORD)(pInit + 4 + nHostLen) = htons(url.port);
+ if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ mir_free(pInit);
+ return 0;
+ }
+ mir_free(pInit);
+
+ if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+
+ if (buf[0] != 5 || buf[1]) {
+ const char* err = "Unknown response";
+ if (buf[0] != 5)
+ SetLastError(ERROR_BAD_FORMAT);
+ else {
+ switch (buf[1]) {
+ case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break;
+ case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break;
+ case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break;
+ case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break;
+ case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break;
+ case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break;
+ case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break;
+ case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break;
+ default: SetLastError(ERROR_INVALID_DATA); break;
+ }
+ }
+ Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err);
+ return 0;
+ }
+
+ int nRecvSize = 0;
+ switch (buf[3]) {
+ case 1:// ipv4 addr
+ nRecvSize = 5;
+ break;
+ case 3:// dns name addr
+ nRecvSize = buf[4] + 2;
+ break;
+ case 4:// ipv6 addr
+ nRecvSize = 17;
+ break;
+ default:
+ Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]);
+ return 0;
+ }
+ if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+
+ //connected
+ return 1;
+}
+
+static bool NetlibInitHttpsConnection(NetlibConnection *nlc)
+{
+ // rfc2817
+ NetlibUrl &url = nlc->url;
+ CMStringA szUrl;
+ if (nlc->dnsThroughProxy)
+ szUrl.Format("%s:%u", url.szHost.c_str(), url.port);
+ else {
+ uint32_t ip = DnsLookup(nlc->nlu, url.szHost);
+ if (ip == 0) return false;
+ szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), url.port);
+ }
+
+ NETLIBHTTPREQUEST nlhrSend = { 0 };
+ nlhrSend.cbSize = sizeof(nlhrSend);
+ nlhrSend.requestType = REQUEST_CONNECT;
+ nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT;
+ nlhrSend.szUrl = szUrl.GetBuffer();
+
+ if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR)
+ return false;
+
+ NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true);
+ if (nlhrReply == nullptr)
+ return false;
+
+ if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) {
+ if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ nlc->dnsThroughProxy = 0;
+ return NetlibInitHttpsConnection(nlc);
+ }
+
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ Netlib_Logf(nlc->nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__,
+ nlc->nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr);
+ Netlib_FreeHttpRequest(nlhrReply);
+ return 0;
+ }
+ Netlib_FreeHttpRequest(nlhrReply);
+ return true; // connected
+}
+
+static void FreePartiallyInitedConnection(NetlibConnection *nlc)
+{
+ uint32_t dwOriginalLastError = GetLastError();
+
+ if (GetNetlibHandleType(nlc) == NLH_CONNECTION)
+ delete nlc;
+
+ SetLastError(dwOriginalLastError);
+}
+
+static bool my_connectIP(NetlibConnection *nlc)
+{
+ NetlibUser *nlu = nlc->nlu;
+ int rc = SOCKET_ERROR, retrycnt = 0;
+ u_long notblocking = 1;
+ uint32_t lasterr = 0;
+ static const TIMEVAL tv = { 1, 0 };
+
+ // if timeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway
+ int timeout = (nlc->timeout <= 0) ? 30 : nlc->timeout;
+
+ // this is for XP SP2 where there is a default connection attempt limit of 10/second
+ if (connectionTimeout) {
+ WaitForSingleObject(hConnectionOpenMutex, 10000);
+ int waitdiff = GetTickCount() - g_LastConnectionTick;
+ if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE);
+ g_LastConnectionTick = GetTickCount();
+ ReleaseMutex(hConnectionOpenMutex);
+
+ // might have died in between the wait
+ if (Miranda_IsTerminated())
+ return false;
+ }
+
+ char szPort[6];
+ addrinfo *air = nullptr, *ai, hints = { 0 };
+
+ hints.ai_family = AF_UNSPEC;
+
+ NetlibUrl &url = nlc->url;
+ if (url.flags & NLOCF_UDP) {
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ }
+ else {
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ }
+
+ if (nlc->proxyType) {
+ if (!nlc->szProxyServer)
+ return false;
+
+ Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, url.szHost.c_str(), url.port);
+
+ _itoa(nlc->wProxyPort, szPort, 10);
+ if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) {
+ Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError());
+ return false;
+ }
+ }
+ else {
+ if (url.szHost.IsEmpty())
+ return false;
+
+ Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, url.szHost.c_str(), url.port);
+
+ _itoa(url.port, szPort, 10);
+
+ if (GetAddrInfoA(url.szHost, szPort, &hints, &air)) {
+ Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", url.szHost.c_str(), WSAGetLastError());
+ return false;
+ }
+ }
+
+ for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) {
+ Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get());
+retry:
+ nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (nlc->s == INVALID_SOCKET) {
+ FreeAddrInfoA(air);
+ return false;
+ }
+
+ // return the socket to non blocking
+ if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) {
+ FreeAddrInfoA(air);
+ return false;
+ }
+
+ if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) {
+ SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET;
+ SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET;
+ if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum))
+ Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified");
+ }
+
+ // try a connect
+ if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) {
+ rc = 0;
+ break;
+ }
+
+ // didn't work, was it cos of nonblocking?
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+ rc = SOCKET_ERROR;
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ continue;
+ }
+
+ while (true) { // timeout loop
+ fd_set r, w, e;
+ FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
+ FD_SET(nlc->s, &r);
+ FD_SET(nlc->s, &w);
+ FD_SET(nlc->s, &e);
+ if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR)
+ break;
+
+ if (rc > 0) {
+ if (FD_ISSET(nlc->s, &w)) {
+ // connection was successful
+ rc = 0;
+ lasterr = 0;
+ }
+ if (FD_ISSET(nlc->s, &r)) {
+ // connection was closed
+ rc = SOCKET_ERROR;
+ lasterr = WSAECONNRESET;
+ }
+ if (FD_ISSET(nlc->s, &e)) {
+ // connection failed.
+ int len = sizeof(lasterr);
+ rc = SOCKET_ERROR;
+ getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len);
+ if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) {
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ goto retry;
+ }
+ }
+ break;
+ }
+ else if (Miranda_IsTerminated()) {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+
+ if (--timeout == 0) {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ if (rc == 0) break;
+
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ }
+
+ FreeAddrInfoA(air);
+
+ notblocking = 0;
+ if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking);
+ if (rc && lasterr) SetLastError(lasterr);
+ return rc == 0;
+}
+
+static int NetlibHttpFallbackToDirect(NetlibConnection *nlc)
+{
+ NetlibDoCloseSocket(nlc, true);
+
+ Netlib_Logf(nlc->nlu, "Fallback to direct connection");
+
+ nlc->proxyAuthNeeded = false;
+ nlc->proxyType = 0;
+ replaceStr(nlc->szProxyServer, nullptr);
+ if (!my_connectIP(nlc)) {
+ Netlib_Logf(nlc->nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+ return true;
+}
+
+bool NetlibDoConnect(NetlibConnection *nlc)
+{
+ NetlibUser *nlu = nlc->nlu;
+
+ replaceStr(nlc->szProxyServer, nullptr);
+
+ bool usingProxy = false, forceHttps = false;
+ if (nlu->settings.useProxy) {
+ if (nlu->settings.proxyType == PROXYTYPE_IE)
+ usingProxy = NetlibGetIeProxyConn(nlc, false);
+ else {
+ if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) {
+ nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer);
+ nlc->wProxyPort = nlu->settings.wProxyPort;
+ nlc->proxyType = nlu->settings.proxyType;
+ usingProxy = true;
+ }
+ }
+ }
+
+ while (!my_connectIP(nlc)) {
+ // if connection failed, the state of nlc might be unpredictable
+ if (GetNetlibHandleType(nlc) == NLH_CONNECTION) {
+ // Fallback to direct only when using HTTP proxy, as this is what used by companies
+ // If other type of proxy used it's an indication of security nutcase, leave him alone
+ if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) {
+ usingProxy = false;
+ nlc->proxyType = 0;
+ Netlib_Logf(nlu, "Fallback to direct connection");
+ continue;
+ }
+ if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) {
+ forceHttps = true;
+ usingProxy = NetlibGetIeProxyConn(nlc, true);
+ if (usingProxy)
+ continue;
+ }
+ }
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+
+ if (usingProxy && !((nlc->url.flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) {
+ if (!WaitUntilWritable(nlc->s, 30000))
+ return false;
+
+ switch (nlc->proxyType) {
+ case PROXYTYPE_SOCKS4:
+ if (!NetlibInitSocks4Connection(nlc))
+ return false;
+ break;
+
+ case PROXYTYPE_SOCKS5:
+ if (!NetlibInitSocks5Connection(nlc))
+ return false;
+ break;
+
+ case PROXYTYPE_HTTPS:
+ case PROXYTYPE_HTTP:
+ nlc->proxyAuthNeeded = true;
+ if (!NetlibInitHttpsConnection(nlc)) {
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc))
+ return false;
+ }
+ break;
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ FreePartiallyInitedConnection(nlc);
+ return false;
+ }
+ }
+
+ Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nlc->url.szHost.c_str(), nlc->url.port);
+
+ if (GetSubscribersCount((THook*)hEventConnected)) {
+ NETLIBCONNECTIONEVENTINFO ncei = {};
+ ncei.connected = 1;
+ ncei.szSettingsModule = nlu->user.szSettingsModule;
+ int size = sizeof(SOCKADDR_IN);
+ getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size);
+ if (nlu->settings.useProxy) {
+ size = sizeof(SOCKADDR_IN);
+ getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size);
+ ncei.remote.sin_family = AF_INET;
+ ncei.remote.sin_port = htons(nlc->url.port);
+ ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nlc->url.szHost);
+ }
+ else {
+ size = sizeof(SOCKADDR_IN);
+ getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size);
+ }
+ NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0);
+ }
+
+ if (NLOCF_SSL & nlc->url.flags)
+ return Netlib_StartSsl(nlc, nullptr) != 0;
+
+ return true;
+}
+
+bool NetlibReconnect(NetlibConnection *nlc)
+{
+ // a connection might be freed already
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION)
+ return false;
+
+ char buf[4];
+ bool opened = nlc->s != INVALID_SOCKET;
+ if (opened) {
+ switch (WaitUntilReadable(nlc->s, 0, true)) {
+ case SOCKET_ERROR:
+ opened = false;
+ break;
+
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ NetlibDoCloseSocket(nlc, true);
+ }
+
+ if (!opened) {
+ if (Miranda_IsTerminated())
+ return false;
+
+ return NetlibDoConnect(nlc);
+ }
+ return true;
+}
+
+MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const char *szHost, int port, int timeout, int flags)
+{
+ if (szHost == nullptr || port == 0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING))
+ return nullptr;
+
+ Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", szHost, port, flags);
+
+ NetlibConnection *nlc = new NetlibConnection();
+ nlc->nlu = nlu;
+ nlc->timeout = timeout;
+ nlc->url.szHost = szHost;
+ nlc->url.port = port;
+ nlc->url.flags = flags;
+ nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0;
+
+ if (!NetlibDoConnect(nlc)) {
+ FreePartiallyInitedConnection(nlc);
+ return nullptr;
+ }
+
+ if (iUPnPCleanup == 0) {
+ mir_cslock lck(csNetlibUser);
+ iUPnPCleanup = 1;
+ mir_forkthread(NetlibUPnPCleanup);
+ }
+
+ return nlc;
+}
+
+NetlibConnection::NetlibConnection()
+{
+ handleType = NLH_CONNECTION;
+ s = s2 = INVALID_SOCKET;
+ hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr);
+ NetlibInitializeNestedCS(&ncsSend);
+ NetlibInitializeNestedCS(&ncsRecv);
+}
+
+NetlibConnection::~NetlibConnection()
+{
+ handleType = 0;
+
+ if (s != INVALID_SOCKET)
+ closesocket(s);
+
+ mir_free(szNewUrl);
+ mir_free(szProxyServer);
+
+ mir_free(nlhpi.szHttpPostUrl);
+ mir_free(nlhpi.szHttpGetUrl);
+
+ NetlibDeleteNestedCS(&ncsSend);
+ NetlibDeleteNestedCS(&ncsRecv);
+
+ CloseHandle(hOkToCloseEvent);
+}
diff --git a/src/mir_app/src/netlib_opts.cpp b/src/mir_app/src/netlib_opts.cpp index a7e0419287..be75114fac 100644 --- a/src/mir_app/src/netlib_opts.cpp +++ b/src/mir_app/src/netlib_opts.cpp @@ -1,518 +1,518 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -struct NetlibTempSettings -{ - uint32_t flags; - char *szSettingsModule; - NETLIBUSERSETTINGS settings; -}; - -static LIST <NetlibTempSettings> tempSettings(5); - -static const UINT outgoingConnectionsControls[] = -{ - IDC_STATIC12, - IDC_USEPROXY, - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS, - IDC_SPECIFYPORTSO, - IDC_PORTSRANGEO, - IDC_STATIC54, - IDC_VALIDATESSL}; -static const UINT useProxyControls[] = { - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS}; -static const UINT specifyOPortsControls[] = { - IDC_PORTSRANGEO, - IDC_STATIC54 -}; -static const UINT incomingConnectionsControls[] = { - IDC_STATIC43, - IDC_SPECIFYPORTS, - IDC_PORTSRANGE, - IDC_STATIC52, - IDC_ENABLEUPNP}; -static const UINT specifyPortsControls[] = { - IDC_PORTSRANGE, - IDC_STATIC52}; - -static const wchar_t* szProxyTypes[] = {LPGENW("<mixed>"), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"}; -static const uint16_t oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080}; - -#define M_REFRESHALL (WM_USER+100) -#define M_REFRESHENABLING (WM_USER+101) - -static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - EnableWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType) -{ - int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type])); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type); - if (type == selectType) - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0); -} - -static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source) -{ - *dest = *source; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); -} - -static void CombineSettingsStrings(char **dest, char **source) -{ - if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; } -} - -static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, uint32_t *destFlags, NETLIBUSERSETTINGS *source, uint32_t sourceFlags) -{ - if (sourceFlags & NUF_OUTGOING) { - if (*destFlags & NUF_OUTGOING) { - if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2; - if (dest->useProxy != source->useProxy) dest->useProxy = 2; - if (dest->proxyType != source->proxyType) dest->proxyType = 0; - CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer); - if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0; - if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2; - CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser); - CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword); - if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2; - if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2; - CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts); - } - else { - dest->validateSSL = source->validateSSL; - dest->useProxy = source->useProxy; - dest->proxyType = source->proxyType; - dest->szProxyServer = source->szProxyServer; - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); - dest->wProxyPort = source->wProxyPort; - dest->useProxyAuth = source->useProxyAuth; - dest->szProxyAuthUser = source->szProxyAuthUser; - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - dest->szProxyAuthPassword = source->szProxyAuthPassword; - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - dest->dnsThroughProxy = source->dnsThroughProxy; - dest->specifyOutgoingPorts = source->specifyOutgoingPorts; - dest->szOutgoingPorts = source->szOutgoingPorts; - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - } - } - if (sourceFlags & NUF_INCOMING) { - if (*destFlags & NUF_INCOMING) { - if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2; - if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2; - CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts); - } - else { - dest->enableUPnP = source->enableUPnP; - dest->specifyIncomingPorts = source->specifyIncomingPorts; - dest->szIncomingPorts = source->szIncomingPorts; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - } - } - if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION)) - *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION; - else *destFlags |= sourceFlags; -} - -static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED; - CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - *(int*)(((uint8_t*)&p->settings) + memberOffset) = newValue; - } - else *(int*)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset) = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); -} - -static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId)); - char *szNewValue = (char*)mir_alloc(newValueLen+1); - GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1); - if (iUser == -1) { - for (auto &p : tempSettings) { - if (!(p->flags & NUF_NOOPTIONS)) { - char **ppszNew = (char**)(((uint8_t*)&p->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = mir_strdup(szNewValue); - } - } - mir_free(szNewValue); - } - else { - char **ppszNew = (char**)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = szNewValue; - } -} - -static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, uint32_t flags) -{ - if (flags & NUF_OUTGOING) { - db_set_b(0, szSettingsModule, "NLValidateSSL", (uint8_t)settings->validateSSL); - db_set_b(0, szSettingsModule, "NLUseProxy", (uint8_t)settings->useProxy); - db_set_b(0, szSettingsModule, "NLProxyType", (uint8_t)settings->proxyType); - db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : ""); - db_set_w(0, szSettingsModule, "NLProxyPort", (uint16_t)settings->wProxyPort); - db_set_b(0, szSettingsModule, "NLUseProxyAuth", (uint8_t)settings->useProxyAuth); - db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : ""); - db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : ""); - db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (uint8_t)settings->dnsThroughProxy); - db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (uint8_t)settings->specifyOutgoingPorts); - db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : ""); - } - if (flags & NUF_INCOMING) { - db_set_b(0, szSettingsModule, "NLEnableUPnP", (uint8_t)settings->enableUPnP); - db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (uint8_t)settings->specifyIncomingPorts); - db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : ""); - } -} - -void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings) -{ - mir_cslock lck(csNetlibUser); - - NetlibUser tUser; - tUser.user.szSettingsModule = (char*)szSettingsModule; - NetlibUser *thisUser = netlibUser.find(&tUser); - if (thisUser == nullptr) - return; - - NetlibFreeUserSettingsStruct(&thisUser->settings); - CopySettingsStruct(&thisUser->settings, settings); - WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags); - - NETLIBUSERSETTINGS combinedSettings = { 0 }; - combinedSettings.cbSize = sizeof(combinedSettings); - - uint32_t flags = 0; - for (auto &p : netlibUser) { - if (p->user.flags & NUF_NOOPTIONS) - continue; - CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags); - } - if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0; - if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0; - if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5; - if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0; - if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1; - if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1; - if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0; - WriteSettingsStructToDb("Netlib", &combinedSettings, flags); - NetlibFreeUserSettingsStruct(&combinedSettings); -} - -static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int iUser; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("<All connections>")); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0); - - mir_cslock lck(csNetlibUser); - for (auto &it : netlibUser) { - NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings)); - thisSettings->flags = it->user.flags; - thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule); - CopySettingsStruct(&thisSettings->settings, &it->settings); - tempSettings.insert(thisSettings); - - if (it->user.flags & NUF_NOOPTIONS) - continue; - iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it)); - } - } - - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return TRUE; - - case M_REFRESHALL: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - { - NETLIBUSERSETTINGS settings = { 0 }; - uint32_t flags = 0; - - if (iUser == -1) { - settings.cbSize = sizeof(settings); - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - CombineSettingsStructs(&settings, &flags, &p->settings, p->flags); - } - else { - NetlibFreeUserSettingsStruct(&settings); - CopySettingsStruct(&settings, &tempSettings[iUser]->settings); - flags = tempSettings[iUser]->flags; - } - ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0); - if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType); - if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType); - if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType); - if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION)) - AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType); - SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : ""); - if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE); - else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, ""); - CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth); - SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : ""); - SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : ""); - CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy); - CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL); - - ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP); - - NetlibFreeUserSettingsStruct(&settings); - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - break; - - case M_REFRESHENABLING: - wchar_t str[80]; - { - int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]); - SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str); - if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) { - int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1; - EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE); - if (selectedProxyType == 0) { - for (auto &p : tempSettings) { - if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING)) - continue; - - if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - enableAuth = 1; - if (p->settings.useProxyAuth) - enableUser = enablePass = 1; - } - } - } - else { - if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - if (selectedProxyType == PROXYTYPE_IE) enableServer = 0; - enableAuth = 1; - if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED) - enableUser = enablePass = 1; - } - } - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer); - } - else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE); - EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED); - EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED); - } - break; - - case WM_COMMAND: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - switch (LOWORD(wParam)) { - case IDC_NETLIBUSERS: - if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return 0; - - case IDC_LOGOPTIONS: - NetlibLogShowOptions(); - return 0; - - case IDC_PROXYTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - if (iUser == -1) { - if (newValue == 0) - return 0; - - for (auto &p : tempSettings) { - if (p->flags & NUF_NOOPTIONS) - continue; - if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS)) - p->settings.proxyType = PROXYTYPE_HTTPS; - else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION) - p->settings.proxyType = PROXYTYPE_HTTP; - else p->settings.proxyType = newValue; - } - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - } - else { - tempSettings[iUser]->settings.proxyType = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - } - break; - case IDC_USEPROXY: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy)); - break; - case IDC_PROXYAUTH: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth)); - break; - case IDC_PROXYDNS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy)); - break; - case IDC_SPECIFYPORTS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts)); - break; - case IDC_SPECIFYPORTSO: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts)); - break; - case IDC_ENABLEUPNP: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP)); - break; - case IDC_VALIDATESSL: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL)); - break; - case IDC_PROXYHOST: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer)); - break; - case IDC_PROXYPORT: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - { - int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - p->settings.wProxyPort = newValue; - } - else tempSettings[iUser]->settings.wProxyPort = newValue; - } - break; - case IDC_PROXYUSER: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser)); - break; - case IDC_PROXYPASS: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword)); - break; - case IDC_PORTSRANGE: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts)); - break; - case IDC_PORTSRANGEO: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts)); - break; - } - ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - for (auto &p : tempSettings) - NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings); - return TRUE; - } - break; - } - break; - - case WM_DESTROY: - for (auto &p : tempSettings) { - mir_free(p->szSettingsModule); - NetlibFreeUserSettingsStruct(&p->settings); - mir_free(p); - } - tempSettings.destroy(); - break; - } - return FALSE; -} - -int NetlibOptInitialise(WPARAM wParam, LPARAM) -{ - int optionsCount = 0; - { - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (!(p->user.flags & NUF_NOOPTIONS)) - ++optionsCount; - } - - if (optionsCount == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.position = 900000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); - odp.szTitle.a = LPGEN("Network"); - odp.pfnDlgProc = DlgProcNetlibOpts; - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+struct NetlibTempSettings
+{
+ uint32_t flags;
+ char *szSettingsModule;
+ NETLIBUSERSETTINGS settings;
+};
+
+static LIST <NetlibTempSettings> tempSettings(5);
+
+static const UINT outgoingConnectionsControls[] =
+{
+ IDC_STATIC12,
+ IDC_USEPROXY,
+ IDC_STATIC21, IDC_PROXYTYPE,
+ IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
+ IDC_PROXYDNS,
+ IDC_SPECIFYPORTSO,
+ IDC_PORTSRANGEO,
+ IDC_STATIC54,
+ IDC_VALIDATESSL};
+static const UINT useProxyControls[] = {
+ IDC_STATIC21, IDC_PROXYTYPE,
+ IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
+ IDC_PROXYDNS};
+static const UINT specifyOPortsControls[] = {
+ IDC_PORTSRANGEO,
+ IDC_STATIC54
+};
+static const UINT incomingConnectionsControls[] = {
+ IDC_STATIC43,
+ IDC_SPECIFYPORTS,
+ IDC_PORTSRANGE,
+ IDC_STATIC52,
+ IDC_ENABLEUPNP};
+static const UINT specifyPortsControls[] = {
+ IDC_PORTSRANGE,
+ IDC_STATIC52};
+
+static const wchar_t* szProxyTypes[] = {LPGENW("<mixed>"), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"};
+static const uint16_t oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080};
+
+#define M_REFRESHALL (WM_USER+100)
+#define M_REFRESHENABLING (WM_USER+101)
+
+static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
+{
+ for (int i = 0; i < cControls; i++)
+ ShowWindow(GetDlgItem(hwndDlg, controls[i]), state);
+}
+
+static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
+{
+ for (int i = 0; i < cControls; i++)
+ EnableWindow(GetDlgItem(hwndDlg, controls[i]), state);
+}
+
+static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType)
+{
+ int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type]));
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type);
+ if (type == selectType)
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0);
+}
+
+static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source)
+{
+ *dest = *source;
+ if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
+ if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
+ if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
+ if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
+}
+
+static void CombineSettingsStrings(char **dest, char **source)
+{
+ if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; }
+}
+
+static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, uint32_t *destFlags, NETLIBUSERSETTINGS *source, uint32_t sourceFlags)
+{
+ if (sourceFlags & NUF_OUTGOING) {
+ if (*destFlags & NUF_OUTGOING) {
+ if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2;
+ if (dest->useProxy != source->useProxy) dest->useProxy = 2;
+ if (dest->proxyType != source->proxyType) dest->proxyType = 0;
+ CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer);
+ if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0;
+ if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2;
+ CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser);
+ CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword);
+ if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2;
+ if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2;
+ CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts);
+ }
+ else {
+ dest->validateSSL = source->validateSSL;
+ dest->useProxy = source->useProxy;
+ dest->proxyType = source->proxyType;
+ dest->szProxyServer = source->szProxyServer;
+ if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
+ dest->wProxyPort = source->wProxyPort;
+ dest->useProxyAuth = source->useProxyAuth;
+ dest->szProxyAuthUser = source->szProxyAuthUser;
+ if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
+ dest->szProxyAuthPassword = source->szProxyAuthPassword;
+ if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
+ dest->dnsThroughProxy = source->dnsThroughProxy;
+ dest->specifyOutgoingPorts = source->specifyOutgoingPorts;
+ dest->szOutgoingPorts = source->szOutgoingPorts;
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
+ }
+ }
+ if (sourceFlags & NUF_INCOMING) {
+ if (*destFlags & NUF_INCOMING) {
+ if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2;
+ if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2;
+ CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts);
+ }
+ else {
+ dest->enableUPnP = source->enableUPnP;
+ dest->specifyIncomingPorts = source->specifyIncomingPorts;
+ dest->szIncomingPorts = source->szIncomingPorts;
+ if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
+ }
+ }
+ if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION))
+ *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION;
+ else *destFlags |= sourceFlags;
+}
+
+static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
+{
+ int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED;
+ CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED);
+ if (iUser == -1) {
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ *(int*)(((uint8_t*)&p->settings) + memberOffset) = newValue;
+ }
+ else *(int*)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset) = newValue;
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+}
+
+static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
+{
+ int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId));
+ char *szNewValue = (char*)mir_alloc(newValueLen+1);
+ GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1);
+ if (iUser == -1) {
+ for (auto &p : tempSettings) {
+ if (!(p->flags & NUF_NOOPTIONS)) {
+ char **ppszNew = (char**)(((uint8_t*)&p->settings) + memberOffset);
+ mir_free(*ppszNew);
+ *ppszNew = mir_strdup(szNewValue);
+ }
+ }
+ mir_free(szNewValue);
+ }
+ else {
+ char **ppszNew = (char**)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset);
+ mir_free(*ppszNew);
+ *ppszNew = szNewValue;
+ }
+}
+
+static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, uint32_t flags)
+{
+ if (flags & NUF_OUTGOING) {
+ db_set_b(0, szSettingsModule, "NLValidateSSL", (uint8_t)settings->validateSSL);
+ db_set_b(0, szSettingsModule, "NLUseProxy", (uint8_t)settings->useProxy);
+ db_set_b(0, szSettingsModule, "NLProxyType", (uint8_t)settings->proxyType);
+ db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : "");
+ db_set_w(0, szSettingsModule, "NLProxyPort", (uint16_t)settings->wProxyPort);
+ db_set_b(0, szSettingsModule, "NLUseProxyAuth", (uint8_t)settings->useProxyAuth);
+ db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : "");
+ db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : "");
+ db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (uint8_t)settings->dnsThroughProxy);
+ db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (uint8_t)settings->specifyOutgoingPorts);
+ db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : "");
+ }
+ if (flags & NUF_INCOMING) {
+ db_set_b(0, szSettingsModule, "NLEnableUPnP", (uint8_t)settings->enableUPnP);
+ db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (uint8_t)settings->specifyIncomingPorts);
+ db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : "");
+ }
+}
+
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings)
+{
+ mir_cslock lck(csNetlibUser);
+
+ NetlibUser tUser;
+ tUser.user.szSettingsModule = (char*)szSettingsModule;
+ NetlibUser *thisUser = netlibUser.find(&tUser);
+ if (thisUser == nullptr)
+ return;
+
+ NetlibFreeUserSettingsStruct(&thisUser->settings);
+ CopySettingsStruct(&thisUser->settings, settings);
+ WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags);
+
+ NETLIBUSERSETTINGS combinedSettings = { 0 };
+ combinedSettings.cbSize = sizeof(combinedSettings);
+
+ uint32_t flags = 0;
+ for (auto &p : netlibUser) {
+ if (p->user.flags & NUF_NOOPTIONS)
+ continue;
+ CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags);
+ }
+ if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0;
+ if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0;
+ if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5;
+ if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0;
+ if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1;
+ if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1;
+ if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0;
+ WriteSettingsStructToDb("Netlib", &combinedSettings, flags);
+ NetlibFreeUserSettingsStruct(&combinedSettings);
+}
+
+static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int iUser;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("<All connections>"));
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0);
+
+ mir_cslock lck(csNetlibUser);
+ for (auto &it : netlibUser) {
+ NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings));
+ thisSettings->flags = it->user.flags;
+ thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule);
+ CopySettingsStruct(&thisSettings->settings, &it->settings);
+ tempSettings.insert(thisSettings);
+
+ if (it->user.flags & NUF_NOOPTIONS)
+ continue;
+ iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it));
+ }
+ }
+
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ return TRUE;
+
+ case M_REFRESHALL:
+ iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
+ {
+ NETLIBUSERSETTINGS settings = { 0 };
+ uint32_t flags = 0;
+
+ if (iUser == -1) {
+ settings.cbSize = sizeof(settings);
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ CombineSettingsStructs(&settings, &flags, &p->settings, p->flags);
+ }
+ else {
+ NetlibFreeUserSettingsStruct(&settings);
+ CopySettingsStruct(&settings, &tempSettings[iUser]->settings);
+ flags = tempSettings[iUser]->flags;
+ }
+ ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE);
+ CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy);
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0);
+ if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType);
+ if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType);
+ if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType);
+ if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION))
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType);
+ SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : "");
+ if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE);
+ else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, "");
+ CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth);
+ SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : "");
+ SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : "");
+ CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy);
+ CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL);
+
+ ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE);
+ CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts);
+ SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : "");
+
+ CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts);
+ SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : "");
+
+ CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP);
+
+ NetlibFreeUserSettingsStruct(&settings);
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+ }
+ break;
+
+ case M_REFRESHENABLING:
+ wchar_t str[80];
+ {
+ int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
+ mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]);
+ SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str);
+ if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) {
+ int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1;
+ EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE);
+ if (selectedProxyType == 0) {
+ for (auto &p : tempSettings) {
+ if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING))
+ continue;
+
+ if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1;
+ else {
+ enableAuth = 1;
+ if (p->settings.useProxyAuth)
+ enableUser = enablePass = 1;
+ }
+ }
+ }
+ else {
+ if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1;
+ else {
+ if (selectedProxyType == PROXYTYPE_IE) enableServer = 0;
+ enableAuth = 1;
+ if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED)
+ enableUser = enablePass = 1;
+ }
+ }
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer);
+ }
+ else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE);
+ EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED);
+ EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED);
+ }
+ break;
+
+ case WM_COMMAND:
+ iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
+ switch (LOWORD(wParam)) {
+ case IDC_NETLIBUSERS:
+ if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ return 0;
+
+ case IDC_LOGOPTIONS:
+ NetlibLogShowOptions();
+ return 0;
+
+ case IDC_PROXYTYPE:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
+ if (iUser == -1) {
+ if (newValue == 0)
+ return 0;
+
+ for (auto &p : tempSettings) {
+ if (p->flags & NUF_NOOPTIONS)
+ continue;
+ if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS))
+ p->settings.proxyType = PROXYTYPE_HTTPS;
+ else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION)
+ p->settings.proxyType = PROXYTYPE_HTTP;
+ else p->settings.proxyType = newValue;
+ }
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ }
+ else {
+ tempSettings[iUser]->settings.proxyType = newValue;
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+ }
+ }
+ break;
+ case IDC_USEPROXY:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy));
+ break;
+ case IDC_PROXYAUTH:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth));
+ break;
+ case IDC_PROXYDNS:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy));
+ break;
+ case IDC_SPECIFYPORTS:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts));
+ break;
+ case IDC_SPECIFYPORTSO:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts));
+ break;
+ case IDC_ENABLEUPNP:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP));
+ break;
+ case IDC_VALIDATESSL:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL));
+ break;
+ case IDC_PROXYHOST:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer));
+ break;
+ case IDC_PROXYPORT:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ {
+ int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE);
+ if (iUser == -1) {
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ p->settings.wProxyPort = newValue;
+ }
+ else tempSettings[iUser]->settings.wProxyPort = newValue;
+ }
+ break;
+ case IDC_PROXYUSER:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser));
+ break;
+ case IDC_PROXYPASS:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword));
+ break;
+ case IDC_PORTSRANGE:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts));
+ break;
+ case IDC_PORTSRANGEO:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts));
+ break;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ for (auto &p : tempSettings)
+ NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings);
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ for (auto &p : tempSettings) {
+ mir_free(p->szSettingsModule);
+ NetlibFreeUserSettingsStruct(&p->settings);
+ mir_free(p);
+ }
+ tempSettings.destroy();
+ break;
+ }
+ return FALSE;
+}
+
+int NetlibOptInitialise(WPARAM wParam, LPARAM)
+{
+ int optionsCount = 0;
+ {
+ mir_cslock lck(csNetlibUser);
+ for (auto &p : netlibUser)
+ if (!(p->user.flags & NUF_NOOPTIONS))
+ ++optionsCount;
+ }
+
+ if (optionsCount == 0)
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 900000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB);
+ odp.szTitle.a = LPGEN("Network");
+ odp.pfnDlgProc = DlgProcNetlibOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/netlib_pktrecver.cpp b/src/mir_app/src/netlib_pktrecver.cpp index 66621d43a3..3b86fed51d 100644 --- a/src/mir_app/src/netlib_pktrecver.cpp +++ b/src/mir_app/src/netlib_pktrecver.cpp @@ -1,80 +1,80 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); - nlpr->handleType = NLH_PACKETRECVER; - nlpr->nlc = nlc; - nlpr->packetRecver.bufferSize = iMaxSize; - nlpr->packetRecver.buffer = (uint8_t*)mir_alloc(nlpr->packetRecver.bufferSize); - nlpr->packetRecver.bytesUsed = 0; - nlpr->packetRecver.bytesAvailable = 0; - return nlpr; -} - -MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam) -{ - NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver; - if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout; - if (nlprParam->bytesUsed == 0) { - if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) { - nlpr->packetRecver.bytesAvailable = 0; - Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching"); - } - } - else { - memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed); - nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed; - } - - if (nlprParam->dwTimeout != INFINITE) { - if (!Netlib_SslPending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) { - *nlprParam = nlpr->packetRecver; - return SOCKET_ERROR; - } - } - - INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0); - if (recvResult > 0) - nlpr->packetRecver.bytesAvailable += recvResult; - *nlprParam = nlpr->packetRecver; - return recvResult; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver));
+ nlpr->handleType = NLH_PACKETRECVER;
+ nlpr->nlc = nlc;
+ nlpr->packetRecver.bufferSize = iMaxSize;
+ nlpr->packetRecver.buffer = (uint8_t*)mir_alloc(nlpr->packetRecver.bufferSize);
+ nlpr->packetRecver.bytesUsed = 0;
+ nlpr->packetRecver.bytesAvailable = 0;
+ return nlpr;
+}
+
+MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam)
+{
+ NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver;
+ if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+ if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout;
+ if (nlprParam->bytesUsed == 0) {
+ if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) {
+ nlpr->packetRecver.bytesAvailable = 0;
+ Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching");
+ }
+ }
+ else {
+ memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed);
+ nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed;
+ }
+
+ if (nlprParam->dwTimeout != INFINITE) {
+ if (!Netlib_SslPending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) {
+ *nlprParam = nlpr->packetRecver;
+ return SOCKET_ERROR;
+ }
+ }
+
+ INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0);
+ if (recvResult > 0)
+ nlpr->packetRecver.bytesAvailable += recvResult;
+ *nlprParam = nlpr->packetRecver;
+ return recvResult;
+}
diff --git a/src/mir_app/src/netlib_security.cpp b/src/mir_app/src/netlib_security.cpp index 278991b7e4..90aa9c68ff 100644 --- a/src/mir_app/src/netlib_security.cpp +++ b/src/mir_app/src/netlib_security.cpp @@ -1,363 +1,363 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define SECURITY_WIN32 -#include <security.h> -#include <rpcdce.h> - -#pragma comment(lib, "secur32.lib") - -struct NtlmHandleType -{ - CtxtHandle hClientContext; - CredHandle hClientCredential; - wchar_t* szProvider; - wchar_t* szPrincipal; - unsigned cbMaxToken; - bool hasDomain; -}; - -struct NTLM_String -{ - uint16_t len; - uint16_t allocedSpace; - uint32_t offset; -}; - -struct NtlmType2packet -{ - char sign[8]; - uint32_t type; // == 2 - NTLM_String targetName; - uint32_t flags; - uint8_t challenge[8]; - uint8_t context[8]; - NTLM_String targetInfo; -}; - -static unsigned ntlmCnt = 0; -static mir_cs csSec; - -static void ReportSecError(SECURITY_STATUS scRet, int line) -{ - wchar_t szMsgBuf[256]; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - - wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0; - - Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal) -{ - HANDLE hSecurity = nullptr; - - if (mir_wstrcmpi(szProvider, L"Basic") == 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->szProvider = mir_wstrdup(szProvider); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - - return hNtlm; - } - - mir_cslock lck(csSec); - - PSecPkgInfo ntlmSecurityPackageInfo; - SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo); - if (sc == SEC_E_OK) { - NtlmHandleType* hNtlm; - - hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; - FreeContextBuffer(ntlmSecurityPackageInfo); - - hNtlm->szProvider = mir_wstrdup(szProvider); - hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L""); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - } - return hSecurity; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity) -{ - if (hSecurity == nullptr) - return; - - mir_cslock lck(csSec); - - if (ntlmCnt != 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - if (hNtlm != nullptr) { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - mir_free(hNtlm->szProvider); - mir_free(hNtlm->szPrincipal); - mir_free(hNtlm); - } - - --ntlmCnt; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) -{ - if (!szChallenge || !szChallenge[0]) return nullptr; - - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - unsigned char inDataBuffer[1024]; - - SecBuffer inBuffers[2] = - { - { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, - { chlsz, SECBUFFER_STREAM, szChallenge } - }; - - SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; - - unsigned long qop = 0; - SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - // unsigned char LayerMask = inDataBuffer[0]; - // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); - - SecPkgContext_Sizes sizes; - sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); - unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); - - unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; - - SecBuffer outBuffers[3] = - { - { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, - { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, - { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } - }; - SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; - - sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned i, ressz = 0; - for (i = 0; i < outBuffersDesc.cBuffers; i++) - ressz += outBuffersDesc.pBuffers[i].cbBuffer; - - unsigned char *response = (unsigned char*)alloca(ressz), *p = response; - for (i = 0; i < outBuffersDesc.cBuffers; i++) { - memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); - p += outBuffersDesc.pBuffers[i].cbBuffer; - } - - return mir_base64_encode(response, ressz); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete) -{ - if (hSecurity == nullptr || ntlmCnt == 0) - return nullptr; - - SecBufferDesc outputBufferDescriptor, inputBufferDescriptor; - SecBuffer outputSecurityToken, inputSecurityToken; - char *szOutputToken; - - NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity; - - Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete); - - if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) { - bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0; - bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0'; - if (hasChallenge) { - size_t tokenLen; - uint8_t *token = (uint8_t*)mir_base64_decode(szChallenge, &tokenLen); - if (token == nullptr) - return nullptr; - - if (isGSSAPI && complete) - return CompleteGssapi(hSecurity, token, (unsigned)tokenLen); - - inputBufferDescriptor.cBuffers = 1; - inputBufferDescriptor.pBuffers = &inputSecurityToken; - inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - inputSecurityToken.BufferType = SECBUFFER_TOKEN; - inputSecurityToken.cbBuffer = (unsigned)tokenLen; - inputSecurityToken.pvBuffer = token; - - // try to decode the domain name from the NTLM challenge - if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) { - NtlmType2packet* pkt = (NtlmType2packet*)token; - if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { - - wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset]; - int domainLen = pkt->targetName.len; - - // Negotiate ANSI? if yes, convert the ANSI name to unicode - if ((pkt->flags & 1) == 0) { - int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0); - wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t)); - domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; - buf[domainLen] = 0; - domainName = buf; - } - else domainLen /= sizeof(wchar_t); - - if (domainLen) { - CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login); - char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete); - mir_free(szChl); - } - } - } - } - else { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - - SEC_WINNT_AUTH_IDENTITY auth; - - if (login != nullptr && login[0] != '\0') { - memset(&auth, 0, sizeof(auth)); - - Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); - - const wchar_t* loginName = login; - const wchar_t* domainName = wcschr(login, '\\'); - size_t domainLen = 0; - size_t loginLen = mir_wstrlen(loginName); - if (domainName != nullptr) { - loginName = domainName + 1; - loginLen = mir_wstrlen(loginName); - domainLen = domainName - login; - domainName = login; - } - else if ((domainName = wcschr(login, '@')) != nullptr) { - loginName = login; - loginLen = domainName - login; - domainLen = mir_wstrlen(++domainName); - } - - auth.User = (PWORD)loginName; - auth.UserLength = (ULONG)loginLen; - auth.Password = (PWORD)psw; - auth.PasswordLength = (ULONG)mir_wstrlen(psw); - auth.Domain = (PWORD)domainName; - auth.DomainLength = (ULONG)domainLen; - auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - - hNtlm->hasDomain = domainLen != 0; - } - - TimeStamp tokenExpiration; - SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - } - - outputBufferDescriptor.cBuffers = 1; - outputBufferDescriptor.pBuffers = &outputSecurityToken; - outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - outputSecurityToken.BufferType = SECBUFFER_TOKEN; - outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; - outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); - - ULONG contextAttributes; - TimeStamp tokenExpiration; - SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential, - hasChallenge ? &hNtlm->hClientContext : nullptr, - hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, - hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext, - &outputBufferDescriptor, &contextAttributes, &tokenExpiration); - Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc); - - complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); - if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { - sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); - Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc); - } - - if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer); - } - else { - if (!login || !psw) - return nullptr; - - T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw)); - szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth)); - complete = true; - } - - if (szOutputToken == nullptr) - return nullptr; - - if (!http) - return szOutputToken; - - CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken); - mir_free(szOutputToken); - return szResult.Detach(); -} - -MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete) -{ - return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <rpcdce.h>
+
+#pragma comment(lib, "secur32.lib")
+
+struct NtlmHandleType
+{
+ CtxtHandle hClientContext;
+ CredHandle hClientCredential;
+ wchar_t* szProvider;
+ wchar_t* szPrincipal;
+ unsigned cbMaxToken;
+ bool hasDomain;
+};
+
+struct NTLM_String
+{
+ uint16_t len;
+ uint16_t allocedSpace;
+ uint32_t offset;
+};
+
+struct NtlmType2packet
+{
+ char sign[8];
+ uint32_t type; // == 2
+ NTLM_String targetName;
+ uint32_t flags;
+ uint8_t challenge[8];
+ uint8_t context[8];
+ NTLM_String targetInfo;
+};
+
+static unsigned ntlmCnt = 0;
+static mir_cs csSec;
+
+static void ReportSecError(SECURITY_STATUS scRet, int line)
+{
+ wchar_t szMsgBuf[256];
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr);
+
+ wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0;
+
+ Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal)
+{
+ HANDLE hSecurity = nullptr;
+
+ if (mir_wstrcmpi(szProvider, L"Basic") == 0) {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->szProvider = mir_wstrdup(szProvider);
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+
+ return hNtlm;
+ }
+
+ mir_cslock lck(csSec);
+
+ PSecPkgInfo ntlmSecurityPackageInfo;
+ SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo);
+ if (sc == SEC_E_OK) {
+ NtlmHandleType* hNtlm;
+
+ hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken;
+ FreeContextBuffer(ntlmSecurityPackageInfo);
+
+ hNtlm->szProvider = mir_wstrdup(szProvider);
+ hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L"");
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+ }
+ return hSecurity;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity)
+{
+ if (hSecurity == nullptr)
+ return;
+
+ mir_cslock lck(csSec);
+
+ if (ntlmCnt != 0) {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ if (hNtlm != nullptr) {
+ if (SecIsValidHandle(&hNtlm->hClientContext))
+ DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential))
+ FreeCredentialsHandle(&hNtlm->hClientCredential);
+ mir_free(hNtlm->szProvider);
+ mir_free(hNtlm->szPrincipal);
+ mir_free(hNtlm);
+ }
+
+ --ntlmCnt;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz)
+{
+ if (!szChallenge || !szChallenge[0]) return nullptr;
+
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ unsigned char inDataBuffer[1024];
+
+ SecBuffer inBuffers[2] =
+ {
+ { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer },
+ { chlsz, SECBUFFER_STREAM, szChallenge }
+ };
+
+ SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers };
+
+ unsigned long qop = 0;
+ SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ // unsigned char LayerMask = inDataBuffer[0];
+ // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]);
+
+ SecPkgContext_Sizes sizes;
+ sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer);
+ unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize);
+
+ unsigned char outDataBuffer[4] = { 1, 0, 16, 0 };
+
+ SecBuffer outBuffers[3] =
+ {
+ { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer },
+ { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer },
+ { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer }
+ };
+ SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers };
+
+ sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ unsigned i, ressz = 0;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++)
+ ressz += outBuffersDesc.pBuffers[i].cbBuffer;
+
+ unsigned char *response = (unsigned char*)alloca(ressz), *p = response;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++) {
+ memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer);
+ p += outBuffersDesc.pBuffers[i].cbBuffer;
+ }
+
+ return mir_base64_encode(response, ressz);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete)
+{
+ if (hSecurity == nullptr || ntlmCnt == 0)
+ return nullptr;
+
+ SecBufferDesc outputBufferDescriptor, inputBufferDescriptor;
+ SecBuffer outputSecurityToken, inputSecurityToken;
+ char *szOutputToken;
+
+ NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity;
+
+ Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete);
+
+ if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) {
+ bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0;
+ bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0';
+ if (hasChallenge) {
+ size_t tokenLen;
+ uint8_t *token = (uint8_t*)mir_base64_decode(szChallenge, &tokenLen);
+ if (token == nullptr)
+ return nullptr;
+
+ if (isGSSAPI && complete)
+ return CompleteGssapi(hSecurity, token, (unsigned)tokenLen);
+
+ inputBufferDescriptor.cBuffers = 1;
+ inputBufferDescriptor.pBuffers = &inputSecurityToken;
+ inputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ inputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ inputSecurityToken.cbBuffer = (unsigned)tokenLen;
+ inputSecurityToken.pvBuffer = token;
+
+ // try to decode the domain name from the NTLM challenge
+ if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) {
+ NtlmType2packet* pkt = (NtlmType2packet*)token;
+ if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) {
+
+ wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset];
+ int domainLen = pkt->targetName.len;
+
+ // Negotiate ANSI? if yes, convert the ANSI name to unicode
+ if ((pkt->flags & 1) == 0) {
+ int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0);
+ wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t));
+ domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1;
+ buf[domainLen] = 0;
+ domainName = buf;
+ }
+ else domainLen /= sizeof(wchar_t);
+
+ if (domainLen) {
+ CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login);
+ char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete);
+ mir_free(szChl);
+ }
+ }
+ }
+ }
+ else {
+ if (SecIsValidHandle(&hNtlm->hClientContext))
+ DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential))
+ FreeCredentialsHandle(&hNtlm->hClientCredential);
+
+ SEC_WINNT_AUTH_IDENTITY auth;
+
+ if (login != nullptr && login[0] != '\0') {
+ memset(&auth, 0, sizeof(auth));
+
+ Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)");
+
+ const wchar_t* loginName = login;
+ const wchar_t* domainName = wcschr(login, '\\');
+ size_t domainLen = 0;
+ size_t loginLen = mir_wstrlen(loginName);
+ if (domainName != nullptr) {
+ loginName = domainName + 1;
+ loginLen = mir_wstrlen(loginName);
+ domainLen = domainName - login;
+ domainName = login;
+ }
+ else if ((domainName = wcschr(login, '@')) != nullptr) {
+ loginName = login;
+ loginLen = domainName - login;
+ domainLen = mir_wstrlen(++domainName);
+ }
+
+ auth.User = (PWORD)loginName;
+ auth.UserLength = (ULONG)loginLen;
+ auth.Password = (PWORD)psw;
+ auth.PasswordLength = (ULONG)mir_wstrlen(psw);
+ auth.Domain = (PWORD)domainName;
+ auth.DomainLength = (ULONG)domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+
+ hNtlm->hasDomain = domainLen != 0;
+ }
+
+ TimeStamp tokenExpiration;
+ SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+ }
+
+ outputBufferDescriptor.cBuffers = 1;
+ outputBufferDescriptor.pBuffers = &outputSecurityToken;
+ outputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ outputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ outputSecurityToken.cbBuffer = hNtlm->cbMaxToken;
+ outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer);
+
+ ULONG contextAttributes;
+ TimeStamp tokenExpiration;
+ SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential,
+ hasChallenge ? &hNtlm->hClientContext : nullptr,
+ hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP,
+ hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext,
+ &outputBufferDescriptor, &contextAttributes, &tokenExpiration);
+ Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc);
+
+ complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED);
+ if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) {
+ sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor);
+ Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc);
+ }
+
+ if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer);
+ }
+ else {
+ if (!login || !psw)
+ return nullptr;
+
+ T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw));
+ szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth));
+ complete = true;
+ }
+
+ if (szOutputToken == nullptr)
+ return nullptr;
+
+ if (!http)
+ return szOutputToken;
+
+ CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken);
+ mir_free(szOutputToken);
+ return szResult.Detach();
+}
+
+MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete)
+{
+ return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete);
+}
diff --git a/src/mir_app/src/netlib_sock.cpp b/src/mir_app/src/netlib_sock.cpp index ebd7f3a7ea..032bfed993 100644 --- a/src/mir_app/src/netlib_sock.cpp +++ b/src/mir_app/src/netlib_sock.cpp @@ -1,287 +1,287 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern HANDLE hSendEvent, hRecvEvent; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - int result; - Netlib_Dump(nlc, (uint8_t*)buf, len, true, flags); - if (nlc->hSsl) - result = Netlib_SslWrite(nlc->hSsl, buf, len); - else - result = send(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsSend); - - NETLIBNOTIFY nln = { buf, len, flags, result }; - NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return SOCKET_ERROR; - - int recvResult; - if (!nlc->foreBuf.isEmpty()) { - recvResult = min(len, (int)nlc->foreBuf.length()); - memcpy(buf, nlc->foreBuf.data(), recvResult); - nlc->foreBuf.remove(recvResult); - } - else if (nlc->hSsl) - recvResult = Netlib_SslRead(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0); - else - recvResult = recv(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - if (recvResult <= 0) - return recvResult; - - Netlib_Dump(nlc, (uint8_t*)buf, recvResult, false, flags); - - if ((flags & MSG_PEEK) == 0) { - NETLIBNOTIFY nln = { buf, len, flags, recvResult }; - NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - } - return recvResult; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending) -{ - FD_ZERO(fd); - for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) { - NetlibConnection *nlcCheck = hConns[i]; - if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) { - SetLastError(ERROR_INVALID_DATA); - return 0; - } - FD_SET(nlcCheck->s, fd); - if (!nlcCheck->foreBuf.isEmpty() || Netlib_SslPending(nlcCheck->hSsl)) - pending++; - } - return 1; -} - -MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - { - mir_cslock lock(csConnectionHeader); - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { - return SOCKET_ERROR; - } - } - if (pending) - return 1; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); -} - -MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - { - mir_cslock lock(csConnectionHeader); - - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { - return SOCKET_ERROR; - } - } - - int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); - - mir_cslock lock(csConnectionHeader); - /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() - to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) - This happens for read/write/except */ - NetlibConnection *conn = nullptr; - int j; - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hReadConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - - if (Netlib_SslPending(conn->hSsl)) - nls->hReadStatus[j] = TRUE; - nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hWriteConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hExceptConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd); - } - return rc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr) -{ - if (!str) return false; - - int len = sizeof(SOCKADDR_INET_M); - return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len); -} - -MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr) -{ - char saddr[128]; - DWORD len = sizeof(saddr); - if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len)) - return mir_strdup(saddr); - - if (addr->sin_family == AF_INET) { - char *szIp = inet_ntoa(addr->sin_addr); - if (addr->sin_port != 0) { - mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port); - return mir_strdup(saddr); - } - return mir_strdup(szIp); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN nlc, NETLIBCONNINFO *connInfo) -{ - if (!nlc || !connInfo) - return 1; - - sockaddr_in sin = { 0 }; - int len = sizeof(sin); - if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) { - connInfo->wPort = ntohs(sin.sin_port); - connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0; - strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -inline bool IsAddrGlobal(const IN6_ADDR *a) -{ - unsigned char High = a->s6_bytes[0] & 0xf0; - return High != 0 && High != 0xf0; -} - -MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly) -{ - addrinfo *air = nullptr, *ai, hints = { 0 }; - const char *szMyHost = ""; - - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_PASSIVE; - - if (GetAddrInfoA(szMyHost, nullptr, &hints, &air)) - return nullptr; - - unsigned n = 0; - for (ai = air; ai; ai = ai->ai_next) { - SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) - ++n; - } - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - unsigned i = 0; - for (ai = air; ai; ai = ai->ai_next) { - sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) { - char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr); - if (szIp) - strncpy_s(addr->szIp[i++], szIp, _TRUNCATE); - mir_free(szIp); - } - } - FreeAddrInfoA(air); - return addr; -} - -static NETLIBIPLIST* GetMyIpv4(void) -{ - char hostname[256] = ""; - - gethostname(hostname, sizeof(hostname)); - PHOSTENT he = gethostbyname(hostname); - - unsigned n; - for (n = 0; he->h_addr_list[n]; ++n) - ; - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - for (unsigned i = 0; i < n; i++) - strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE); - - return addr; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+extern HANDLE hSendEvent, hRecvEvent;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags)
+{
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+
+ int result;
+ Netlib_Dump(nlc, (uint8_t*)buf, len, true, flags);
+ if (nlc->hSsl)
+ result = Netlib_SslWrite(nlc->hSsl, buf, len);
+ else
+ result = send(nlc->s, buf, len, flags & 0xFFFF);
+
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ NETLIBNOTIFY nln = { buf, len, flags, result };
+ NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags)
+{
+ if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
+ return SOCKET_ERROR;
+
+ int recvResult;
+ if (!nlc->foreBuf.isEmpty()) {
+ recvResult = min(len, (int)nlc->foreBuf.length());
+ memcpy(buf, nlc->foreBuf.data(), recvResult);
+ nlc->foreBuf.remove(recvResult);
+ }
+ else if (nlc->hSsl)
+ recvResult = Netlib_SslRead(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0);
+ else
+ recvResult = recv(nlc->s, buf, len, flags & 0xFFFF);
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ if (recvResult <= 0)
+ return recvResult;
+
+ Netlib_Dump(nlc, (uint8_t*)buf, recvResult, false, flags);
+
+ if ((flags & MSG_PEEK) == 0) {
+ NETLIBNOTIFY nln = { buf, len, flags, recvResult };
+ NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+ }
+ return recvResult;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending)
+{
+ FD_ZERO(fd);
+ for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) {
+ NetlibConnection *nlcCheck = hConns[i];
+ if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ FD_SET(nlcCheck->s, fd);
+ if (!nlcCheck->foreBuf.isEmpty() || Netlib_SslPending(nlcCheck->hSsl))
+ pending++;
+ }
+ return 1;
+}
+
+MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls)
+{
+ if (nls == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+ {
+ mir_cslock lock(csConnectionHeader);
+ if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
+ || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
+ || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) {
+ return SOCKET_ERROR;
+ }
+ }
+ if (pending)
+ return 1;
+
+ TIMEVAL tv;
+ tv.tv_sec = nls->dwTimeout / 1000;
+ tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
+ return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
+}
+
+MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls)
+{
+ if (nls == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+
+ TIMEVAL tv;
+ tv.tv_sec = nls->dwTimeout / 1000;
+ tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
+ {
+ mir_cslock lock(csConnectionHeader);
+
+ if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
+ || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
+ || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) {
+ return SOCKET_ERROR;
+ }
+ }
+
+ int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
+
+ mir_cslock lock(csConnectionHeader);
+ /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET()
+ to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not)
+ This happens for read/write/except */
+ NetlibConnection *conn = nullptr;
+ int j;
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hReadConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+
+ if (Netlib_SslPending(conn->hSsl))
+ nls->hReadStatus[j] = TRUE;
+ nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd);
+ }
+
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hWriteConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+ nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd);
+ }
+
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hExceptConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+ nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd);
+ }
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr)
+{
+ if (!str) return false;
+
+ int len = sizeof(SOCKADDR_INET_M);
+ return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len);
+}
+
+MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr)
+{
+ char saddr[128];
+ DWORD len = sizeof(saddr);
+ if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len))
+ return mir_strdup(saddr);
+
+ if (addr->sin_family == AF_INET) {
+ char *szIp = inet_ntoa(addr->sin_addr);
+ if (addr->sin_port != 0) {
+ mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port);
+ return mir_strdup(saddr);
+ }
+ return mir_strdup(szIp);
+ }
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN nlc, NETLIBCONNINFO *connInfo)
+{
+ if (!nlc || !connInfo)
+ return 1;
+
+ sockaddr_in sin = { 0 };
+ int len = sizeof(sin);
+ if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) {
+ connInfo->wPort = ntohs(sin.sin_port);
+ connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0;
+ strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+inline bool IsAddrGlobal(const IN6_ADDR *a)
+{
+ unsigned char High = a->s6_bytes[0] & 0xf0;
+ return High != 0 && High != 0xf0;
+}
+
+MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly)
+{
+ addrinfo *air = nullptr, *ai, hints = { 0 };
+ const char *szMyHost = "";
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+
+ if (GetAddrInfoA(szMyHost, nullptr, &hints, &air))
+ return nullptr;
+
+ unsigned n = 0;
+ for (ai = air; ai; ai = ai->ai_next) {
+ SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr;
+ if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr))))
+ ++n;
+ }
+
+ NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
+ addr->cbNum = n;
+
+ unsigned i = 0;
+ for (ai = air; ai; ai = ai->ai_next) {
+ sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr;
+ if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) {
+ char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr);
+ if (szIp)
+ strncpy_s(addr->szIp[i++], szIp, _TRUNCATE);
+ mir_free(szIp);
+ }
+ }
+ FreeAddrInfoA(air);
+ return addr;
+}
+
+static NETLIBIPLIST* GetMyIpv4(void)
+{
+ char hostname[256] = "";
+
+ gethostname(hostname, sizeof(hostname));
+ PHOSTENT he = gethostbyname(hostname);
+
+ unsigned n;
+ for (n = 0; he->h_addr_list[n]; ++n)
+ ;
+
+ NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
+ addr->cbNum = n;
+
+ for (unsigned i = 0; i < n; i++)
+ strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE);
+
+ return addr;
+}
diff --git a/src/mir_app/src/netlib_ssl.cpp b/src/mir_app/src/netlib_ssl.cpp index d2ab355ec6..14d2ca5d13 100644 --- a/src/mir_app/src/netlib_ssl.cpp +++ b/src/mir_app/src/netlib_ssl.cpp @@ -1,465 +1,465 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <openssl/rand.h> - -static bool bSslInitDone; - -enum SocketState -{ - sockOpen, - sockClosed, - sockError -}; - -struct SslHandle : public MZeroedObject -{ - ~SslHandle() - { - if (session) - SSL_free(session); - } - - SOCKET s; - SSL *session; - SocketState state; -}; - -static SSL_CTX *g_ctx; -static mir_cs csSsl; - -static void dump_error(SSL *session, int err) -{ - err = SSL_get_error(session, err); - - char buf[100]; - ERR_error_string_n(err, buf, sizeof(buf)); - Netlib_Logf(nullptr, "SSL negotiation failure: %s (%d)", buf, err); -} - -const char* SSL_GetCipherName(SslHandle *ssl) -{ - if (!ssl || !ssl->session) - return nullptr; - - return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl->session)); -} - -static void ReportSslError(SECURITY_STATUS scRet, int line, bool = false) -{ - CMStringW tszMsg(FORMAT, L"SSL connection failure(%x %u) :", scRet, line); - - switch (scRet) { - case 0: - case ERROR_NOT_READY: - return; - - case SEC_E_INVALID_TOKEN: - tszMsg += TranslateW_LP(L"Client cannot decode host message. Possible causes: host does not support SSL or requires not existing security package"); - break; - - case CERT_E_CN_NO_MATCH: - case SEC_E_WRONG_PRINCIPAL: - tszMsg += TranslateW_LP(L"Host we are connecting to is not the one certificate was issued for"); - break; - - default: - wchar_t szMsgBuf[256]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - tszMsg += szMsgBuf; - } - - Netlib_LogfW(nullptr, tszMsg); - - SetLastError(scRet); - PUShowMessageW(tszMsg.GetBuffer(), SM_WARNING); -} - -static PCCERT_CONTEXT SSL_X509ToCryptCert(X509 * x509) -{ - unsigned char *buf = nullptr; - PCCERT_CONTEXT pCertContext = nullptr; - - int len = i2d_X509(x509, &buf); - if ((len >= 0) && buf) { - pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); - - CRYPTO_free(buf, __FILE__, __LINE__); - } - return pCertContext; -} - -static PCCERT_CONTEXT SSL_CertChainToCryptAnchor(SSL* session) -{ - /* convert the active certificate chain provided in the handshake of 'session' into - the format used by CryptAPI. - */ - PCCERT_CONTEXT anchor = nullptr; - // create cert store - HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr); - - if (store) { - X509 *server_cert = SSL_get_peer_certificate(session); - if (server_cert) { - // add the server's cert first, to make sure CryptAPI builds the correct chain - PCCERT_CONTEXT primary_cert; - BOOL ok = CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(server_cert), CERT_STORE_ADD_ALWAYS, &primary_cert); - if (ok && primary_cert) { - // add all remaining certs to store (note: stack needs not be freed, it is not a copy) - STACK_OF(X509) *server_chain = SSL_get_peer_cert_chain(session); - if (server_chain) { - for (int i = 0; i < OPENSSL_sk_num((OPENSSL_STACK *)server_chain); i++) { - X509 *next_cert = (X509 *)OPENSSL_sk_value((OPENSSL_STACK *)server_chain, i); - CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(next_cert), CERT_STORE_ADD_USE_EXISTING, nullptr); - } - } - - // return primary cert; MUST be freed by caller which will free the associated store - anchor = primary_cert; - } - else { - if (primary_cert) - CertFreeCertificateContext(primary_cert); - } - - X509_free(server_cert); - } - - CertCloseStore(store, 0); - } - - return anchor; -} - -static LPSTR rgszUsages[] = -{ - szOID_PKIX_KP_SERVER_AUTH, - szOID_SERVER_GATED_CRYPTO, - szOID_SGC_NETSCAPE -}; - -static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, uint32_t dwCertFlags) -{ - uint32_t scRet; - - ptrW pwszServerName(mir_a2u(pszServerName)); - - HTTPSPolicyCallbackData polHttps = {}; - CERT_CHAIN_POLICY_PARA PolicyPara = {}; - CERT_CHAIN_POLICY_STATUS PolicyStatus = {}; - CERT_CHAIN_PARA ChainPara = {}; - - PCCERT_CHAIN_CONTEXT pChainContext = nullptr; - PCCERT_CONTEXT pServerCert = SSL_CertChainToCryptAnchor(ssl->session); - if (pServerCert == nullptr) { - scRet = SEC_E_WRONG_PRINCIPAL; - goto cleanup; - } - - ChainPara.cbSize = sizeof(ChainPara); - ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; - ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages); - ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; - - if (!CertGetCertificateChain(nullptr, pServerCert, nullptr, pServerCert->hCertStore, &ChainPara, 0, nullptr, &pChainContext)) { - scRet = GetLastError(); - goto cleanup; - } - - polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); - polHttps.dwAuthType = AUTHTYPE_SERVER; - polHttps.fdwChecks = dwCertFlags; - polHttps.pwszServerName = pwszServerName; - - PolicyPara.cbSize = sizeof(PolicyPara); - PolicyPara.pvExtraPolicyPara = &polHttps; - - PolicyStatus.cbSize = sizeof(PolicyStatus); - - if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { - scRet = GetLastError(); - goto cleanup; - } - - if (PolicyStatus.dwError) { - scRet = PolicyStatus.dwError; - goto cleanup; - } - - scRet = SEC_E_OK; - -cleanup: - if (pChainContext) - CertFreeCertificateChain(pChainContext); - if (pServerCert) - CertFreeCertificateContext(pServerCert); - - ReportSslError(scRet, __LINE__, true); - return scRet == SEC_E_OK; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// negotiate SSL session, verify cert, return NULL if failed - -MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify) -{ - std::unique_ptr<SslHandle> ssl(new SslHandle()); - ssl->s = s; - { - mir_cslock lck(csSsl); - ssl->session = SSL_new(g_ctx); - } - - if (!ssl->session) { - Netlib_Logf(nullptr, "SSL setup failure: session"); - return false; - } - SSL_set_fd(ssl->session, ssl->s); - - SSL_set_tlsext_host_name(ssl->session, host); - - int err = SSL_connect(ssl->session); - if (err != 1) { - dump_error(ssl->session, err); - return nullptr; - } - - if (verify) { - uint32_t dwFlags = 0; - if (!host || inet_addr(host) != INADDR_NONE) - dwFlags |= 0x00001000; - if (!VerifyCertificate(ssl.get(), host, dwFlags)) - return nullptr; - } - - return ssl.release(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// return true if there is either unsend or buffered received data (ie. after peek) - -MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl) -{ - return ssl && ssl->session && (SSL_pending(ssl->session) > 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// reads number of bytes, keeps in buffer if peek != 0 - -MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek) -{ - if (!ssl || !ssl->session) return SOCKET_ERROR; - if (num <= 0) return 0; - - int err = 0; - if (peek) - err = SSL_peek(ssl->session, buf, num); - else - err = SSL_read(ssl->session, buf, num); - - if (err <= 0) { - int err2 = SSL_get_error(ssl->session, err); - if (err2 == SSL_ERROR_ZERO_RETURN) { - Netlib_Logf(nullptr, "SSL connection gracefully closed"); - ssl->state = sockClosed; - return 0; - } - - int err3 = ERR_get_error(); - if (err3) { - Netlib_Logf(nullptr, "SSL failure recieving data (%d, %d, %d, %d)", err, err2, err3, WSAGetLastError()); - ssl->state = sockError; - return SOCKET_ERROR; - } - } - - return err; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// writes data to the SSL socket - -MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num) -{ - if (!ssl || !ssl->session) - return SOCKET_ERROR; - if (num <= 0) - return 0; - - int err = SSL_write(ssl->session, buf, num); - if (err > 0) - return err; - - int err2 = SSL_get_error(ssl->session, err); - switch (err2) { - case SSL_ERROR_ZERO_RETURN: - Netlib_Logf(nullptr, "SSL connection gracefully closed"); - ssl->state = sockClosed; - break; - - default: - Netlib_Logf(nullptr, "SSL failure sending data (%d, %d, %d)", err, err2, WSAGetLastError()); - ssl->state = sockError; - return SOCKET_ERROR; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// closes SSL session, but keeps socket open - -MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl) -{ - if (ssl && ssl->session) { - SOCKET s = SSL_get_fd(ssl->session); - if (s != -1) - shutdown(s, SD_BOTH); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// frees all data associated with the SSL socket - -MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl) -{ - delete ssl; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// makes connection SSL -// returns 0 on failure / 1 on success - -MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (nlc == nullptr) - return 0; - - NetlibUser *nlu = nlc->nlu; - if (szHost == nullptr) - szHost = nlc->url.szHost; - szHost = NEWSTR_ALLOCA(szHost); - - Netlib_Logf(nlu, "(%d %s) Starting SSL/TLS negotiation", int(nlc->s), szHost); - - nlc->hSsl = Netlib_SslConnect(nlc->s, szHost, nlu->settings.validateSSL); - if (nlc->hSsl == nullptr) - Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL/TLS connection", int(nlc->s), szHost); - else - Netlib_Logf(nlu, "(%d %s) SSL/TLS negotiation successful", int(nlc->s), szHost); - - return nlc->hSsl != nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// gets TLS channel binging data for a socket - -static char TLS13_Label[] = "EXPORTER-Channel-Binding"; - -MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer) -{ - if (nlc == nullptr || nlc->hSsl == nullptr) - return nullptr; - - char buf[1000]; - auto *pszVersion = SSL_get_version(nlc->hSsl->session); - if (tlsVer && !mir_strcmp(pszVersion, "TLSv1.3")) { - int res = SSL_export_keying_material(nlc->hSsl->session, - (uint8_t *)buf, 32, TLS13_Label, sizeof(TLS13_Label) - 1, 0, 0, 0); - if (res == 1) { - tlsVer = 13; - void *pBuf = mir_alloc(cbLen = 32); - memcpy(pBuf, buf, cbLen); - return pBuf; - } - } - - size_t len = SSL_get_finished(nlc->hSsl->session, buf, sizeof(buf)); - if (len == 0) - return nullptr; - - tlsVer = 12; - cbLen = (int)len; - void *pBuf = mir_alloc(len); - memcpy(pBuf, buf, len); - return pBuf; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// module entry point - -bool OpenSsl_Init(void) -{ - /* Load Library Pointers */ - if (bSslInitDone) - return true; - - if (!bSslInitDone) { // init OpenSSL - SSL_library_init(); - SSL_load_error_strings(); - // FIXME check errors - - const SSL_METHOD *meth = TLS_client_method(); - if (!meth) { - Netlib_Logf(nullptr, "SSL setup failure: client method"); - return false; - } - - g_ctx = SSL_CTX_new(meth); - if (!g_ctx) { - Netlib_Logf(nullptr, "SSL setup failure: context"); - return false; - } - - VARSW wszPemFile(L"%miranda_path%\\libs\\microsoft.pem"); - SSL_CTX_load_verify_locations(g_ctx, _T2A(wszPemFile), NULL); - - // SSL_read/write should transparently handle renegotiations - SSL_CTX_ctrl(g_ctx, SSL_CTRL_MODE, SSL_MODE_AUTO_RETRY, nullptr); - // SSL_CTX_set_quiet_shutdown(g_ctx, TRUE); - - RAND_screen(); - - bSslInitDone = true; - } - - return bSslInitDone; -} - -void OpenSsl_Unload(void) -{ - /* Load Library Pointers */ - if (!bSslInitDone) - return; - - if (g_ctx) - SSL_CTX_free(g_ctx); - - bSslInitDone = false; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+static bool bSslInitDone;
+
+enum SocketState
+{
+ sockOpen,
+ sockClosed,
+ sockError
+};
+
+struct SslHandle : public MZeroedObject
+{
+ ~SslHandle()
+ {
+ if (session)
+ SSL_free(session);
+ }
+
+ SOCKET s;
+ SSL *session;
+ SocketState state;
+};
+
+static SSL_CTX *g_ctx;
+static mir_cs csSsl;
+
+static void dump_error(SSL *session, int err)
+{
+ err = SSL_get_error(session, err);
+
+ char buf[100];
+ ERR_error_string_n(err, buf, sizeof(buf));
+ Netlib_Logf(nullptr, "SSL negotiation failure: %s (%d)", buf, err);
+}
+
+const char* SSL_GetCipherName(SslHandle *ssl)
+{
+ if (!ssl || !ssl->session)
+ return nullptr;
+
+ return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl->session));
+}
+
+static void ReportSslError(SECURITY_STATUS scRet, int line, bool = false)
+{
+ CMStringW tszMsg(FORMAT, L"SSL connection failure(%x %u) :", scRet, line);
+
+ switch (scRet) {
+ case 0:
+ case ERROR_NOT_READY:
+ return;
+
+ case SEC_E_INVALID_TOKEN:
+ tszMsg += TranslateW_LP(L"Client cannot decode host message. Possible causes: host does not support SSL or requires not existing security package");
+ break;
+
+ case CERT_E_CN_NO_MATCH:
+ case SEC_E_WRONG_PRINCIPAL:
+ tszMsg += TranslateW_LP(L"Host we are connecting to is not the one certificate was issued for");
+ break;
+
+ default:
+ wchar_t szMsgBuf[256];
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr);
+ tszMsg += szMsgBuf;
+ }
+
+ Netlib_LogfW(nullptr, tszMsg);
+
+ SetLastError(scRet);
+ PUShowMessageW(tszMsg.GetBuffer(), SM_WARNING);
+}
+
+static PCCERT_CONTEXT SSL_X509ToCryptCert(X509 * x509)
+{
+ unsigned char *buf = nullptr;
+ PCCERT_CONTEXT pCertContext = nullptr;
+
+ int len = i2d_X509(x509, &buf);
+ if ((len >= 0) && buf) {
+ pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len);
+
+ CRYPTO_free(buf, __FILE__, __LINE__);
+ }
+ return pCertContext;
+}
+
+static PCCERT_CONTEXT SSL_CertChainToCryptAnchor(SSL* session)
+{
+ /* convert the active certificate chain provided in the handshake of 'session' into
+ the format used by CryptAPI.
+ */
+ PCCERT_CONTEXT anchor = nullptr;
+ // create cert store
+ HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr);
+
+ if (store) {
+ X509 *server_cert = SSL_get_peer_certificate(session);
+ if (server_cert) {
+ // add the server's cert first, to make sure CryptAPI builds the correct chain
+ PCCERT_CONTEXT primary_cert;
+ BOOL ok = CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(server_cert), CERT_STORE_ADD_ALWAYS, &primary_cert);
+ if (ok && primary_cert) {
+ // add all remaining certs to store (note: stack needs not be freed, it is not a copy)
+ STACK_OF(X509) *server_chain = SSL_get_peer_cert_chain(session);
+ if (server_chain) {
+ for (int i = 0; i < OPENSSL_sk_num((OPENSSL_STACK *)server_chain); i++) {
+ X509 *next_cert = (X509 *)OPENSSL_sk_value((OPENSSL_STACK *)server_chain, i);
+ CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(next_cert), CERT_STORE_ADD_USE_EXISTING, nullptr);
+ }
+ }
+
+ // return primary cert; MUST be freed by caller which will free the associated store
+ anchor = primary_cert;
+ }
+ else {
+ if (primary_cert)
+ CertFreeCertificateContext(primary_cert);
+ }
+
+ X509_free(server_cert);
+ }
+
+ CertCloseStore(store, 0);
+ }
+
+ return anchor;
+}
+
+static LPSTR rgszUsages[] =
+{
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+};
+
+static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, uint32_t dwCertFlags)
+{
+ uint32_t scRet;
+
+ ptrW pwszServerName(mir_a2u(pszServerName));
+
+ HTTPSPolicyCallbackData polHttps = {};
+ CERT_CHAIN_POLICY_PARA PolicyPara = {};
+ CERT_CHAIN_POLICY_STATUS PolicyStatus = {};
+ CERT_CHAIN_PARA ChainPara = {};
+
+ PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
+ PCCERT_CONTEXT pServerCert = SSL_CertChainToCryptAnchor(ssl->session);
+ if (pServerCert == nullptr) {
+ scRet = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ ChainPara.cbSize = sizeof(ChainPara);
+ ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages);
+ ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
+
+ if (!CertGetCertificateChain(nullptr, pServerCert, nullptr, pServerCert->hCertStore, &ChainPara, 0, nullptr, &pChainContext)) {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
+ polHttps.dwAuthType = AUTHTYPE_SERVER;
+ polHttps.fdwChecks = dwCertFlags;
+ polHttps.pwszServerName = pwszServerName;
+
+ PolicyPara.cbSize = sizeof(PolicyPara);
+ PolicyPara.pvExtraPolicyPara = &polHttps;
+
+ PolicyStatus.cbSize = sizeof(PolicyStatus);
+
+ if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ if (PolicyStatus.dwError) {
+ scRet = PolicyStatus.dwError;
+ goto cleanup;
+ }
+
+ scRet = SEC_E_OK;
+
+cleanup:
+ if (pChainContext)
+ CertFreeCertificateChain(pChainContext);
+ if (pServerCert)
+ CertFreeCertificateContext(pServerCert);
+
+ ReportSslError(scRet, __LINE__, true);
+ return scRet == SEC_E_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// negotiate SSL session, verify cert, return NULL if failed
+
+MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify)
+{
+ std::unique_ptr<SslHandle> ssl(new SslHandle());
+ ssl->s = s;
+ {
+ mir_cslock lck(csSsl);
+ ssl->session = SSL_new(g_ctx);
+ }
+
+ if (!ssl->session) {
+ Netlib_Logf(nullptr, "SSL setup failure: session");
+ return false;
+ }
+ SSL_set_fd(ssl->session, ssl->s);
+
+ SSL_set_tlsext_host_name(ssl->session, host);
+
+ int err = SSL_connect(ssl->session);
+ if (err != 1) {
+ dump_error(ssl->session, err);
+ return nullptr;
+ }
+
+ if (verify) {
+ uint32_t dwFlags = 0;
+ if (!host || inet_addr(host) != INADDR_NONE)
+ dwFlags |= 0x00001000;
+ if (!VerifyCertificate(ssl.get(), host, dwFlags))
+ return nullptr;
+ }
+
+ return ssl.release();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// return true if there is either unsend or buffered received data (ie. after peek)
+
+MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl)
+{
+ return ssl && ssl->session && (SSL_pending(ssl->session) > 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reads number of bytes, keeps in buffer if peek != 0
+
+MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek)
+{
+ if (!ssl || !ssl->session) return SOCKET_ERROR;
+ if (num <= 0) return 0;
+
+ int err = 0;
+ if (peek)
+ err = SSL_peek(ssl->session, buf, num);
+ else
+ err = SSL_read(ssl->session, buf, num);
+
+ if (err <= 0) {
+ int err2 = SSL_get_error(ssl->session, err);
+ if (err2 == SSL_ERROR_ZERO_RETURN) {
+ Netlib_Logf(nullptr, "SSL connection gracefully closed");
+ ssl->state = sockClosed;
+ return 0;
+ }
+
+ int err3 = ERR_get_error();
+ if (err3) {
+ Netlib_Logf(nullptr, "SSL failure recieving data (%d, %d, %d, %d)", err, err2, err3, WSAGetLastError());
+ ssl->state = sockError;
+ return SOCKET_ERROR;
+ }
+ }
+
+ return err;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// writes data to the SSL socket
+
+MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num)
+{
+ if (!ssl || !ssl->session)
+ return SOCKET_ERROR;
+ if (num <= 0)
+ return 0;
+
+ int err = SSL_write(ssl->session, buf, num);
+ if (err > 0)
+ return err;
+
+ int err2 = SSL_get_error(ssl->session, err);
+ switch (err2) {
+ case SSL_ERROR_ZERO_RETURN:
+ Netlib_Logf(nullptr, "SSL connection gracefully closed");
+ ssl->state = sockClosed;
+ break;
+
+ default:
+ Netlib_Logf(nullptr, "SSL failure sending data (%d, %d, %d)", err, err2, WSAGetLastError());
+ ssl->state = sockError;
+ return SOCKET_ERROR;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// closes SSL session, but keeps socket open
+
+MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl)
+{
+ if (ssl && ssl->session) {
+ SOCKET s = SSL_get_fd(ssl->session);
+ if (s != -1)
+ shutdown(s, SD_BOTH);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// frees all data associated with the SSL socket
+
+MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl)
+{
+ delete ssl;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// makes connection SSL
+// returns 0 on failure / 1 on success
+
+MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost)
+{
+ NetlibConnection *nlc = (NetlibConnection*)hConnection;
+ if (nlc == nullptr)
+ return 0;
+
+ NetlibUser *nlu = nlc->nlu;
+ if (szHost == nullptr)
+ szHost = nlc->url.szHost;
+ szHost = NEWSTR_ALLOCA(szHost);
+
+ Netlib_Logf(nlu, "(%d %s) Starting SSL/TLS negotiation", int(nlc->s), szHost);
+
+ nlc->hSsl = Netlib_SslConnect(nlc->s, szHost, nlu->settings.validateSSL);
+ if (nlc->hSsl == nullptr)
+ Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL/TLS connection", int(nlc->s), szHost);
+ else
+ Netlib_Logf(nlu, "(%d %s) SSL/TLS negotiation successful", int(nlc->s), szHost);
+
+ return nlc->hSsl != nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// gets TLS channel binging data for a socket
+
+static char TLS13_Label[] = "EXPORTER-Channel-Binding";
+
+MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer)
+{
+ if (nlc == nullptr || nlc->hSsl == nullptr)
+ return nullptr;
+
+ char buf[1000];
+ auto *pszVersion = SSL_get_version(nlc->hSsl->session);
+ if (tlsVer && !mir_strcmp(pszVersion, "TLSv1.3")) {
+ int res = SSL_export_keying_material(nlc->hSsl->session,
+ (uint8_t *)buf, 32, TLS13_Label, sizeof(TLS13_Label) - 1, 0, 0, 0);
+ if (res == 1) {
+ tlsVer = 13;
+ void *pBuf = mir_alloc(cbLen = 32);
+ memcpy(pBuf, buf, cbLen);
+ return pBuf;
+ }
+ }
+
+ size_t len = SSL_get_finished(nlc->hSsl->session, buf, sizeof(buf));
+ if (len == 0)
+ return nullptr;
+
+ tlsVer = 12;
+ cbLen = (int)len;
+ void *pBuf = mir_alloc(len);
+ memcpy(pBuf, buf, len);
+ return pBuf;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// module entry point
+
+bool OpenSsl_Init(void)
+{
+ /* Load Library Pointers */
+ if (bSslInitDone)
+ return true;
+
+ if (!bSslInitDone) { // init OpenSSL
+ SSL_library_init();
+ SSL_load_error_strings();
+ // FIXME check errors
+
+ const SSL_METHOD *meth = TLS_client_method();
+ if (!meth) {
+ Netlib_Logf(nullptr, "SSL setup failure: client method");
+ return false;
+ }
+
+ g_ctx = SSL_CTX_new(meth);
+ if (!g_ctx) {
+ Netlib_Logf(nullptr, "SSL setup failure: context");
+ return false;
+ }
+
+ VARSW wszPemFile(L"%miranda_path%\\libs\\microsoft.pem");
+ SSL_CTX_load_verify_locations(g_ctx, _T2A(wszPemFile), NULL);
+
+ // SSL_read/write should transparently handle renegotiations
+ SSL_CTX_ctrl(g_ctx, SSL_CTRL_MODE, SSL_MODE_AUTO_RETRY, nullptr);
+ // SSL_CTX_set_quiet_shutdown(g_ctx, TRUE);
+
+ RAND_screen();
+
+ bSslInitDone = true;
+ }
+
+ return bSslInitDone;
+}
+
+void OpenSsl_Unload(void)
+{
+ /* Load Library Pointers */
+ if (!bSslInitDone)
+ return;
+
+ if (g_ctx)
+ SSL_CTX_free(g_ctx);
+
+ bSslInitDone = false;
+}
diff --git a/src/mir_app/src/netlib_upnp.cpp b/src/mir_app/src/netlib_upnp.cpp index d9570b23af..17b7f5f463 100644 --- a/src/mir_app/src/netlib_upnp.cpp +++ b/src/mir_app/src/netlib_upnp.cpp @@ -1,813 +1,813 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -static const char search_request_msg[] = - "M-SEARCH * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "MAN: \"ssdp:discover\"\r\n" - "MX: 1\r\n" - "ST: urn:schemas-upnp-org:service:%s\r\n" - "\r\n"; - -static const char xml_get_hdr[] = - "GET %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "ACCEPT-LANGUAGE: *\r\n\r\n"; - -static const char soap_post_hdr[] = - "POST %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char soap_post_hdr_m[] = - "M-POST %s URL HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n" - "01-SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char search_device[] = - "<serviceType>%s</serviceType>"; - -static const char soap_action[] = - "<?xml version = \"1.0\"?>\r\n" - "<s:Envelope\r\n" - " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n" - " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" - " <s:Body>\r\n" - " <u:%s xmlns:u = \"%s\">\r\n" - "%s" - " </u:%s>\r\n" - " </s:Body>\r\n" - "</s:Envelope>\r\n"; - -static const char soap_query[] = - "<s:Envelope\r\n" - " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n" - " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" - " <s:Body>\r\n" - " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n" - " <u:varName>%s</u:varName>\r\n" - " </u:QueryStateVariable>\r\n" - " </s:Body>\r\n" - "</s:Envelope>\r\n"; - -static const char add_port_mapping[] = - " <NewRemoteHost></NewRemoteHost>\r\n" - " <NewExternalPort>%i</NewExternalPort>\r\n" - " <NewProtocol>%s</NewProtocol>\r\n" - " <NewInternalPort>%i</NewInternalPort>\r\n" - " <NewInternalClient>%s</NewInternalClient>\r\n" - " <NewEnabled>1</NewEnabled>\r\n" - " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n" - " <NewLeaseDuration>0</NewLeaseDuration>\r\n"; - -static const char delete_port_mapping[] = - " <NewRemoteHost></NewRemoteHost>\r\n" - " <NewExternalPort>%i</NewExternalPort>\r\n" - " <NewProtocol>%s</NewProtocol>\r\n"; - -static const char get_port_mapping[] = - " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n"; - -static bool gatewayFound; -static SOCKADDR_IN locIP; -static time_t lastDiscTime; -static int expireTime = 120; - -static int retryCount; -static SOCKET sock = INVALID_SOCKET; -static char szConnHost[256]; -static unsigned short sConnPort; - -static uint16_t *portList; -static unsigned numports, numportsAlloc; -static HANDLE portListMutex; - -static char szCtlUrl[256], szDev[256]; - -typedef enum -{ - DeviceGetReq, - ControlAction, - ControlQuery -} ReqType; - -static bool txtParseParam(char* szData, char* presearch, - char* start, char* finish, char* param, size_t size) -{ - char *cp, *cp1; - size_t len; - - *param = 0; - - if (presearch != nullptr) { - cp1 = strstr(szData, presearch); - if (cp1 == nullptr) return false; - } - else - cp1 = szData; - - cp = strstr(cp1, start); - if (cp == nullptr) return false; - cp += mir_strlen(start); - while (*cp == ' ') ++cp; - - cp1 = strstr(cp, finish); - if (cp1 == nullptr) return false; - while (*(cp1-1) == ' ' && cp1 > cp) --cp1; - - len = min((size_t)(cp1 - cp), size-1); - strncpy(param, cp, len); - param[len] = 0; - - return true; -} - -void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath) -{ - char *ppath, *phost, *pport; - int sz; - - phost = strstr(szUrl, "://"); - if (phost == nullptr) phost = szUrl; - else phost += 3; - - ppath = strchr(phost, '/'); - if (ppath == nullptr) ppath = phost + mir_strlen(phost); - - pport = strchr(phost, ':'); - if (pport == nullptr) pport = ppath; - - if (szHost != nullptr) { - sz = pport - phost + 1; - if (sz > 256) sz = 256; - strncpy(szHost, phost, sz); - szHost[sz - 1] = 0; - } - - if (sPort != nullptr) { - if (pport < ppath) { - long prt = atol(pport + 1); - *sPort = prt != 0 ? (unsigned short)prt : 80; - } - else - *sPort = 80; - } - - if (szPath != nullptr) { - strncpy(szPath, ppath, 256); - szPath[255] = 0; - } -} - -static void LongLog(char* szData) -{ - Netlib_Logf(nullptr, szData); -} - -static void closeRouterConnection(void) -{ - if (sock != INVALID_SOCKET) { - closesocket(sock); - sock = INVALID_SOCKET; - } -} - -static void validateSocket(void) -{ - static const TIMEVAL tv = { 0, 0 }; - fd_set rfd; - char buf[4]; - - if (sock == INVALID_SOCKET) - return; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - bool opened = false; - switch (select(1, &rfd, nullptr, nullptr, &tv)) { - case 0: - opened = true; - break; - - case 1: - opened = recv(sock, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - closeRouterConnection(); -} - -static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype) -{ - // Parse URL - char szHost[256], szPath[256], szRes[16]; - int sz = 0, res = 0; - unsigned short sPort; - bool needClose = false; - - const char* szPostHdr = soap_post_hdr; - char* szData = (char*)mir_alloc(4096); - char* szReq = nullptr; - - parseURL(szUrl, szHost, &sPort, szPath); - - if (sPort != sConnPort || _stricmp(szHost, szConnHost)) - closeRouterConnection(); - else - validateSocket(); - - while (true) { - retryCount = 0; - switch (reqtype) { - case DeviceGetReq: - sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort); - break; - - case ControlAction: - { - char szData1[1024]; - - szReq = mir_strdup(szResult); - sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1); - } - break; - - case ControlQuery: - { - char szData1[1024]; - sz = mir_snprintf(szData1, soap_query, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1); - } - break; - } - szResult[0] = 0; - { - static const TIMEVAL tv = { 6, 0 }; - static unsigned ttl = 4; - static u_long mode = 1; - fd_set rfd, wfd, efd; - SOCKADDR_IN enetaddr; - -retrycon: - if (sock == INVALID_SOCKET) { - sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(sPort); - enetaddr.sin_addr.s_addr = inet_addr(szHost); - - // Resolve host name if needed - if (enetaddr.sin_addr.s_addr == INADDR_NONE) { - PHOSTENT he = gethostbyname(szHost); - if (he) - enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; - } - - Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort); - - FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); - FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd); - - // Limit the scope of the connection (does not work for - setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); - - // Put socket into non-blocking mode for timeout on connect - ioctlsocket(sock, FIONBIO, &mode); - - // Connect to the remote host - if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) { - int err = WSAGetLastError(); - - // Socket connection failed - if (err != WSAEWOULDBLOCK) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed %d", err); - break; - } - // Wait for socket to connect - else if (select(1, &rfd, &wfd, &efd, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect timeout"); - break; - } - else if (!FD_ISSET(sock, &wfd)) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed"); - break; - } - } - strncpy_s(szConnHost, szHost, _TRUNCATE); - sConnPort = sPort; - } - - if (send(sock, szData, sz, 0) != SOCKET_ERROR) { - char *hdrend = nullptr; - int acksz = 0, pktsz = 0; - - if (szActionName == nullptr) { - int len = sizeof(locIP); - getsockname(sock, (SOCKADDR*)&locIP, &len); - if (locIP.sin_addr.S_un.S_addr == 0x0100007f) { - struct hostent *he; - - gethostname(szPath, sizeof(szPath)); - he = gethostbyname(szPath); - if (he != nullptr) - locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0]; - } - } - - LongLog(szData); - sz = 0; - while (true) { - int bytesRecv; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - // Wait for the next packet - if (select(1, &rfd, nullptr, nullptr, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP recieve timeout"); - break; - } - - // - bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0); - - // Connection closed or aborted, all data received - if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) { - closeRouterConnection(); - if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) { - ++retryCount; - goto retrycon; - } - break; - } - - sz += bytesRecv; - - // Insert null terminator to use string functions - if (sz >= (resSize - 1)) { - szResult[resSize - 1] = 0; - break; - } - else - szResult[sz] = 0; - - // HTTP header found? - if (hdrend == nullptr) { - // Find HTTP header end - hdrend = strstr(szResult, "\r\n\r\n"); - if (hdrend == nullptr) { - hdrend = strstr(szResult, "\n\n"); - if (hdrend) hdrend += 2; - } - - else - hdrend += 4; - - if (hdrend != nullptr) { - // Get packet size if provided - if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) || - txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) { - // Add size of HTTP header to the packet size to compute full transmission size - pktsz = atol(ltrimp(szRes)) + (hdrend - szResult); - } - // Get encoding type if provided - else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) { - if (_stricmp(lrtrimp(szRes), "Chunked") == 0) - acksz = hdrend - szResult; - } - if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) { - needClose = (_stricmp(lrtrimp(szRes), "close") == 0); - } - } - } - - // Content-Length bytes reached, all data received - if (sz >= pktsz && pktsz != 0) { - szResult[pktsz] = 0; - break; - } - - // Chunked encoding processing - if (sz > acksz && acksz != 0) { -retry: - // Parse out chunk size - char* data = szResult + acksz; - char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n'); - if (peol1 != nullptr) { - char *peol2 = strchr(++peol1, '\n'); - if (peol2 != nullptr) { - // Get chunk size - int chunkBytes = strtol(peol1, nullptr, 16); - acksz += chunkBytes; - peol2++; - - memmove(data, peol2, mir_strlen(peol2) + 1); - sz -= peol2 - data; - - // Last chunk, all data received - if (chunkBytes == 0) break; - if (sz > acksz) goto retry; - } - } - } - } - LongLog(szResult); - } - else { - if (retryCount < 2) { - closeRouterConnection(); - ++retryCount; - goto retrycon; - } - else - Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError()); - } - } - txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); - res = atol(szRes); - if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr) - szPostHdr = soap_post_hdr_m; - else - break; - } - - if (needClose) - closeRouterConnection(); - - mir_free(szData); - mir_free(szReq); - return res; -} - -static unsigned getExtIP(void) -{ - char szExtIP[30]; - char* szData = (char*)mir_alloc(4096); szData[0] = 0; - - unsigned extip = 0; - int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction); - if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP))) - extip = ntohl(inet_addr(szExtIP)); - - mir_free(szData); - return extip; -} - -static bool getUPnPURLs(char* szUrl, size_t sizeUrl) -{ - char* szData = (char*)mir_alloc(8192); - - gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200; - if (gatewayFound) { - char szTemp[256], *rpth; - size_t ctlLen; - - txtParseParam(szData, nullptr, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp)); - strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl)); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - - mir_snprintf(szTemp, search_device, szDev); - txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl); - - // URL combining per RFC 2396 - if (szUrl[0] != 0) { - if (strstr(szUrl, "://") != nullptr) // absolute URI - rpth = szCtlUrl; - else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path - { - rpth = strstr(szCtlUrl, "//"); - if (rpth == nullptr) rpth = szCtlUrl; - } - else if (szUrl[0] == '/') // relative URI abs_path - { - rpth = strstr(szCtlUrl, "//"); - rpth = rpth ? rpth + 2 : szCtlUrl; - - rpth = strchr(rpth, '/'); - if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl); - } - else { // relative URI rel_path - size_t ctlCLen = mir_strlen(szCtlUrl); - rpth = szCtlUrl + ctlCLen; - if (ctlCLen != 0 && *(rpth - 1) != '/') - strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen); - } - - ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl); - strncpy(rpth, szUrl, ctlLen); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - } - else { - szCtlUrl[0] = 0; - gatewayFound = false; - } - } - mir_free(szData); - - return gatewayFound; -} - -static void discoverUPnP(void) -{ - char* buf; - int buflen; - unsigned i, j, nip = 0; - unsigned* ips = nullptr; - - static const unsigned any = INADDR_ANY; - static const TIMEVAL tv = { 1, 600000 }; - - char szUrl[256] = ""; - char hostname[256]; - PHOSTENT he; - fd_set readfd; - - SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - SOCKADDR_IN enetaddr; - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(1900); - enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); - - gethostname(hostname, sizeof(hostname)); - he = gethostbyname(hostname); - - if (he) { - while (he->h_addr_list[nip]) ++nip; - - ips = (unsigned*)mir_alloc(nip * sizeof(unsigned)); - - for (j = 0; j < nip; j++) - ips[j] = *(unsigned*)he->h_addr_list[j]; - } - - buf = (char*)mir_alloc(1500); - - for (i = 3; --i && szUrl[0] == 0;) { - for (j = 0; j < nip; j++) { - if (ips) - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned)); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - } - - if (Miranda_IsTerminated()) break; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) { - buflen = recv(s, buf, 1500, 0); - if (buflen != SOCKET_ERROR) { - buf[buflen] = 0; - LongLog(buf); - - if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) || - txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) { - char age[30]; - char szHostNew[256], szHostExist[256]; - - lrtrim(szUrl); - - parseURL(szUrl, szHostNew, nullptr, nullptr); - parseURL(szCtlUrl, szHostExist, nullptr, nullptr); - if (mir_strcmp(szHostNew, szHostExist) == 0) { - gatewayFound = true; - break; - } - - txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev)); - txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age)); - expireTime = atoi(lrtrimp(age)); - lrtrim(szDev); - - if (getUPnPURLs(szUrl, sizeof(szUrl))) { - gatewayFound = getExtIP() != 0; - if (gatewayFound) break; - } - } - } - FD_ZERO(&readfd); - FD_SET(s, &readfd); - } - } - - mir_free(buf); - mir_free(ips); - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned)); - closesocket(s); -} - -static bool findUPnPGateway(void) -{ - if ((time(0) - lastDiscTime) >= expireTime) { - WaitForSingleObject(portListMutex, INFINITE); - - time_t curTime = time(0); - - if ((curTime - lastDiscTime) >= expireTime) { - gatewayFound = false; - - discoverUPnP(); - lastDiscTime = curTime; - - Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl); - } - - ReleaseMutex(portListMutex); - } - - return gatewayFound; -} - -bool NetlibUPnPAddPortMapping(uint16_t intport, char *proto, uint16_t *extport, uint32_t *extip, bool search) -{ - int res = 0, i = 5; - - if (findUPnPGateway()) { - char* szData = (char*)mir_alloc(4096); - char szExtIP[30]; - - *extport = intport - 1; - *extip = ntohl(locIP.sin_addr.S_un.S_addr); - - WaitForSingleObject(portListMutex, INFINITE); - - do { - ++*extport; - mir_snprintf(szData, 4096, add_port_mapping, - *extport, proto, intport, inet_ntoa(locIP.sin_addr)); - res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction); - txtParseParam(szData, nullptr, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP)); - - } while (search && res == 500 && atol(szExtIP) == 718 && --i); - - mir_free(szData); - - if (res == 200) { - unsigned ip = getExtIP(); - if (ip) *extip = ip; - - if (numports >= numportsAlloc) - mir_realloc(portList, sizeof(uint16_t)*(numportsAlloc += 10)); - portList[numports++] = *extport; - } - - ReleaseMutex(portListMutex); - } - - return res == 200; -} - -void NetlibUPnPDeletePortMapping(uint16_t extport, char* proto) -{ - if (extport == 0) - return; - - // findUPnPGateway(); - - if (gatewayFound) { - unsigned i; - char* szData = (char*)mir_alloc(4096); - - WaitForSingleObject(portListMutex, INFINITE); - mir_snprintf(szData, 4096, delete_port_mapping, extport, proto); - httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction); - - for (i = 0; i < numports; i++) - if (portList[i] == extport && --numports > 0) - memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(uint16_t)); - - mir_free(szData); - ReleaseMutex(portListMutex); - } -} - -void NetlibUPnPCleanup(void*) -{ - // upnp is disabled globally, no need for a cleanup - if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0) - return; - - { - int incoming = 0; - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (p->user.flags & NUF_INCOMING) { - incoming = 1; - break; - } - - if (!incoming) - return; - } - - if (findUPnPGateway()) { - char *szData = (char*)alloca(4096); - char buf[50], lip[50]; - unsigned j = 0, k, num = 100; - - strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE); - - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 && - txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf))) - num = atol(buf); - - uint16_t ports[30]; - for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) { - mir_snprintf(szData, 4096, get_port_mapping, i); - - ReleaseMutex(portListMutex); - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200) - break; - - if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0) - continue; - - if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0) - continue; - - if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf))) { - uint16_t mport = (uint16_t)atol(buf); - - if (j >= _countof(ports)) - break; - - for (k = 0; k < numports; ++k) - if (portList[k] == mport) - break; - - if (k >= numports) - ports[j++] = mport; - } - } - - ReleaseMutex(portListMutex); - - for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++) - NetlibUPnPDeletePortMapping(ports[i], "TCP"); - } -} - -void NetlibUPnPInit(void) -{ - numports = 0; - numportsAlloc = 10; - portList = (uint16_t*)mir_alloc(sizeof(uint16_t)*numportsAlloc); - - portListMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUPnPDestroy(void) -{ - mir_free(portList); - CloseHandle(portListMutex); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+static const char search_request_msg[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 1\r\n"
+ "ST: urn:schemas-upnp-org:service:%s\r\n"
+ "\r\n";
+
+static const char xml_get_hdr[] =
+ "GET %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "ACCEPT-LANGUAGE: *\r\n\r\n";
+
+static const char soap_post_hdr[] =
+ "POST %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
+ "SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char soap_post_hdr_m[] =
+ "M-POST %s URL HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
+ "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n"
+ "01-SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char search_device[] =
+ "<serviceType>%s</serviceType>";
+
+static const char soap_action[] =
+ "<?xml version = \"1.0\"?>\r\n"
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:%s xmlns:u = \"%s\">\r\n"
+ "%s"
+ " </u:%s>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char soap_query[] =
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n"
+ " <u:varName>%s</u:varName>\r\n"
+ " </u:QueryStateVariable>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char add_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n"
+ " <NewInternalPort>%i</NewInternalPort>\r\n"
+ " <NewInternalClient>%s</NewInternalClient>\r\n"
+ " <NewEnabled>1</NewEnabled>\r\n"
+ " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
+ " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
+
+static const char delete_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n";
+
+static const char get_port_mapping[] =
+ " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n";
+
+static bool gatewayFound;
+static SOCKADDR_IN locIP;
+static time_t lastDiscTime;
+static int expireTime = 120;
+
+static int retryCount;
+static SOCKET sock = INVALID_SOCKET;
+static char szConnHost[256];
+static unsigned short sConnPort;
+
+static uint16_t *portList;
+static unsigned numports, numportsAlloc;
+static HANDLE portListMutex;
+
+static char szCtlUrl[256], szDev[256];
+
+typedef enum
+{
+ DeviceGetReq,
+ ControlAction,
+ ControlQuery
+} ReqType;
+
+static bool txtParseParam(char* szData, char* presearch,
+ char* start, char* finish, char* param, size_t size)
+{
+ char *cp, *cp1;
+ size_t len;
+
+ *param = 0;
+
+ if (presearch != nullptr) {
+ cp1 = strstr(szData, presearch);
+ if (cp1 == nullptr) return false;
+ }
+ else
+ cp1 = szData;
+
+ cp = strstr(cp1, start);
+ if (cp == nullptr) return false;
+ cp += mir_strlen(start);
+ while (*cp == ' ') ++cp;
+
+ cp1 = strstr(cp, finish);
+ if (cp1 == nullptr) return false;
+ while (*(cp1-1) == ' ' && cp1 > cp) --cp1;
+
+ len = min((size_t)(cp1 - cp), size-1);
+ strncpy(param, cp, len);
+ param[len] = 0;
+
+ return true;
+}
+
+void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath)
+{
+ char *ppath, *phost, *pport;
+ int sz;
+
+ phost = strstr(szUrl, "://");
+ if (phost == nullptr) phost = szUrl;
+ else phost += 3;
+
+ ppath = strchr(phost, '/');
+ if (ppath == nullptr) ppath = phost + mir_strlen(phost);
+
+ pport = strchr(phost, ':');
+ if (pport == nullptr) pport = ppath;
+
+ if (szHost != nullptr) {
+ sz = pport - phost + 1;
+ if (sz > 256) sz = 256;
+ strncpy(szHost, phost, sz);
+ szHost[sz - 1] = 0;
+ }
+
+ if (sPort != nullptr) {
+ if (pport < ppath) {
+ long prt = atol(pport + 1);
+ *sPort = prt != 0 ? (unsigned short)prt : 80;
+ }
+ else
+ *sPort = 80;
+ }
+
+ if (szPath != nullptr) {
+ strncpy(szPath, ppath, 256);
+ szPath[255] = 0;
+ }
+}
+
+static void LongLog(char* szData)
+{
+ Netlib_Logf(nullptr, szData);
+}
+
+static void closeRouterConnection(void)
+{
+ if (sock != INVALID_SOCKET) {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+}
+
+static void validateSocket(void)
+{
+ static const TIMEVAL tv = { 0, 0 };
+ fd_set rfd;
+ char buf[4];
+
+ if (sock == INVALID_SOCKET)
+ return;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ bool opened = false;
+ switch (select(1, &rfd, nullptr, nullptr, &tv)) {
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(sock, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ closeRouterConnection();
+}
+
+static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype)
+{
+ // Parse URL
+ char szHost[256], szPath[256], szRes[16];
+ int sz = 0, res = 0;
+ unsigned short sPort;
+ bool needClose = false;
+
+ const char* szPostHdr = soap_post_hdr;
+ char* szData = (char*)mir_alloc(4096);
+ char* szReq = nullptr;
+
+ parseURL(szUrl, szHost, &sPort, szPath);
+
+ if (sPort != sConnPort || _stricmp(szHost, szConnHost))
+ closeRouterConnection();
+ else
+ validateSocket();
+
+ while (true) {
+ retryCount = 0;
+ switch (reqtype) {
+ case DeviceGetReq:
+ sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort);
+ break;
+
+ case ControlAction:
+ {
+ char szData1[1024];
+
+ szReq = mir_strdup(szResult);
+ sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName);
+ sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1);
+ }
+ break;
+
+ case ControlQuery:
+ {
+ char szData1[1024];
+ sz = mir_snprintf(szData1, soap_query, szActionName);
+ sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1);
+ }
+ break;
+ }
+ szResult[0] = 0;
+ {
+ static const TIMEVAL tv = { 6, 0 };
+ static unsigned ttl = 4;
+ static u_long mode = 1;
+ fd_set rfd, wfd, efd;
+ SOCKADDR_IN enetaddr;
+
+retrycon:
+ if (sock == INVALID_SOCKET) {
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(sPort);
+ enetaddr.sin_addr.s_addr = inet_addr(szHost);
+
+ // Resolve host name if needed
+ if (enetaddr.sin_addr.s_addr == INADDR_NONE) {
+ PHOSTENT he = gethostbyname(szHost);
+ if (he)
+ enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0];
+ }
+
+ Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort);
+
+ FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
+ FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd);
+
+ // Limit the scope of the connection (does not work for
+ setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned));
+
+ // Put socket into non-blocking mode for timeout on connect
+ ioctlsocket(sock, FIONBIO, &mode);
+
+ // Connect to the remote host
+ if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+
+ // Socket connection failed
+ if (err != WSAEWOULDBLOCK) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect failed %d", err);
+ break;
+ }
+ // Wait for socket to connect
+ else if (select(1, &rfd, &wfd, &efd, &tv) != 1) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect timeout");
+ break;
+ }
+ else if (!FD_ISSET(sock, &wfd)) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect failed");
+ break;
+ }
+ }
+ strncpy_s(szConnHost, szHost, _TRUNCATE);
+ sConnPort = sPort;
+ }
+
+ if (send(sock, szData, sz, 0) != SOCKET_ERROR) {
+ char *hdrend = nullptr;
+ int acksz = 0, pktsz = 0;
+
+ if (szActionName == nullptr) {
+ int len = sizeof(locIP);
+ getsockname(sock, (SOCKADDR*)&locIP, &len);
+ if (locIP.sin_addr.S_un.S_addr == 0x0100007f) {
+ struct hostent *he;
+
+ gethostname(szPath, sizeof(szPath));
+ he = gethostbyname(szPath);
+ if (he != nullptr)
+ locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0];
+ }
+ }
+
+ LongLog(szData);
+ sz = 0;
+ while (true) {
+ int bytesRecv;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ // Wait for the next packet
+ if (select(1, &rfd, nullptr, nullptr, &tv) != 1) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP recieve timeout");
+ break;
+ }
+
+ //
+ bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0);
+
+ // Connection closed or aborted, all data received
+ if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) {
+ closeRouterConnection();
+ if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) {
+ ++retryCount;
+ goto retrycon;
+ }
+ break;
+ }
+
+ sz += bytesRecv;
+
+ // Insert null terminator to use string functions
+ if (sz >= (resSize - 1)) {
+ szResult[resSize - 1] = 0;
+ break;
+ }
+ else
+ szResult[sz] = 0;
+
+ // HTTP header found?
+ if (hdrend == nullptr) {
+ // Find HTTP header end
+ hdrend = strstr(szResult, "\r\n\r\n");
+ if (hdrend == nullptr) {
+ hdrend = strstr(szResult, "\n\n");
+ if (hdrend) hdrend += 2;
+ }
+
+ else
+ hdrend += 4;
+
+ if (hdrend != nullptr) {
+ // Get packet size if provided
+ if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) ||
+ txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) {
+ // Add size of HTTP header to the packet size to compute full transmission size
+ pktsz = atol(ltrimp(szRes)) + (hdrend - szResult);
+ }
+ // Get encoding type if provided
+ else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) {
+ if (_stricmp(lrtrimp(szRes), "Chunked") == 0)
+ acksz = hdrend - szResult;
+ }
+ if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) {
+ needClose = (_stricmp(lrtrimp(szRes), "close") == 0);
+ }
+ }
+ }
+
+ // Content-Length bytes reached, all data received
+ if (sz >= pktsz && pktsz != 0) {
+ szResult[pktsz] = 0;
+ break;
+ }
+
+ // Chunked encoding processing
+ if (sz > acksz && acksz != 0) {
+retry:
+ // Parse out chunk size
+ char* data = szResult + acksz;
+ char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n');
+ if (peol1 != nullptr) {
+ char *peol2 = strchr(++peol1, '\n');
+ if (peol2 != nullptr) {
+ // Get chunk size
+ int chunkBytes = strtol(peol1, nullptr, 16);
+ acksz += chunkBytes;
+ peol2++;
+
+ memmove(data, peol2, mir_strlen(peol2) + 1);
+ sz -= peol2 - data;
+
+ // Last chunk, all data received
+ if (chunkBytes == 0) break;
+ if (sz > acksz) goto retry;
+ }
+ }
+ }
+ }
+ LongLog(szResult);
+ }
+ else {
+ if (retryCount < 2) {
+ closeRouterConnection();
+ ++retryCount;
+ goto retrycon;
+ }
+ else
+ Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError());
+ }
+ }
+ txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes));
+ res = atol(szRes);
+ if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr)
+ szPostHdr = soap_post_hdr_m;
+ else
+ break;
+ }
+
+ if (needClose)
+ closeRouterConnection();
+
+ mir_free(szData);
+ mir_free(szReq);
+ return res;
+}
+
+static unsigned getExtIP(void)
+{
+ char szExtIP[30];
+ char* szData = (char*)mir_alloc(4096); szData[0] = 0;
+
+ unsigned extip = 0;
+ int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction);
+ if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP)))
+ extip = ntohl(inet_addr(szExtIP));
+
+ mir_free(szData);
+ return extip;
+}
+
+static bool getUPnPURLs(char* szUrl, size_t sizeUrl)
+{
+ char* szData = (char*)mir_alloc(8192);
+
+ gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200;
+ if (gatewayFound) {
+ char szTemp[256], *rpth;
+ size_t ctlLen;
+
+ txtParseParam(szData, nullptr, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp));
+ strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl));
+ szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
+
+ mir_snprintf(szTemp, search_device, szDev);
+ txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl);
+
+ // URL combining per RFC 2396
+ if (szUrl[0] != 0) {
+ if (strstr(szUrl, "://") != nullptr) // absolute URI
+ rpth = szCtlUrl;
+ else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ if (rpth == nullptr) rpth = szCtlUrl;
+ }
+ else if (szUrl[0] == '/') // relative URI abs_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ rpth = rpth ? rpth + 2 : szCtlUrl;
+
+ rpth = strchr(rpth, '/');
+ if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl);
+ }
+ else { // relative URI rel_path
+ size_t ctlCLen = mir_strlen(szCtlUrl);
+ rpth = szCtlUrl + ctlCLen;
+ if (ctlCLen != 0 && *(rpth - 1) != '/')
+ strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen);
+ }
+
+ ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl);
+ strncpy(rpth, szUrl, ctlLen);
+ szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
+ }
+ else {
+ szCtlUrl[0] = 0;
+ gatewayFound = false;
+ }
+ }
+ mir_free(szData);
+
+ return gatewayFound;
+}
+
+static void discoverUPnP(void)
+{
+ char* buf;
+ int buflen;
+ unsigned i, j, nip = 0;
+ unsigned* ips = nullptr;
+
+ static const unsigned any = INADDR_ANY;
+ static const TIMEVAL tv = { 1, 600000 };
+
+ char szUrl[256] = "";
+ char hostname[256];
+ PHOSTENT he;
+ fd_set readfd;
+
+ SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ SOCKADDR_IN enetaddr;
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(1900);
+ enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250");
+
+ gethostname(hostname, sizeof(hostname));
+ he = gethostbyname(hostname);
+
+ if (he) {
+ while (he->h_addr_list[nip]) ++nip;
+
+ ips = (unsigned*)mir_alloc(nip * sizeof(unsigned));
+
+ for (j = 0; j < nip; j++)
+ ips[j] = *(unsigned*)he->h_addr_list[j];
+ }
+
+ buf = (char*)mir_alloc(1500);
+
+ for (i = 3; --i && szUrl[0] == 0;) {
+ for (j = 0; j < nip; j++) {
+ if (ips)
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned));
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1");
+ sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1");
+ sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+ }
+
+ if (Miranda_IsTerminated()) break;
+
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+
+ while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) {
+ buflen = recv(s, buf, 1500, 0);
+ if (buflen != SOCKET_ERROR) {
+ buf[buflen] = 0;
+ LongLog(buf);
+
+ if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) ||
+ txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) {
+ char age[30];
+ char szHostNew[256], szHostExist[256];
+
+ lrtrim(szUrl);
+
+ parseURL(szUrl, szHostNew, nullptr, nullptr);
+ parseURL(szCtlUrl, szHostExist, nullptr, nullptr);
+ if (mir_strcmp(szHostNew, szHostExist) == 0) {
+ gatewayFound = true;
+ break;
+ }
+
+ txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev));
+ txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age));
+ expireTime = atoi(lrtrimp(age));
+ lrtrim(szDev);
+
+ if (getUPnPURLs(szUrl, sizeof(szUrl))) {
+ gatewayFound = getExtIP() != 0;
+ if (gatewayFound) break;
+ }
+ }
+ }
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+ }
+ }
+
+ mir_free(buf);
+ mir_free(ips);
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned));
+ closesocket(s);
+}
+
+static bool findUPnPGateway(void)
+{
+ if ((time(0) - lastDiscTime) >= expireTime) {
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ time_t curTime = time(0);
+
+ if ((curTime - lastDiscTime) >= expireTime) {
+ gatewayFound = false;
+
+ discoverUPnP();
+ lastDiscTime = curTime;
+
+ Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl);
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return gatewayFound;
+}
+
+bool NetlibUPnPAddPortMapping(uint16_t intport, char *proto, uint16_t *extport, uint32_t *extip, bool search)
+{
+ int res = 0, i = 5;
+
+ if (findUPnPGateway()) {
+ char* szData = (char*)mir_alloc(4096);
+ char szExtIP[30];
+
+ *extport = intport - 1;
+ *extip = ntohl(locIP.sin_addr.S_un.S_addr);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ do {
+ ++*extport;
+ mir_snprintf(szData, 4096, add_port_mapping,
+ *extport, proto, intport, inet_ntoa(locIP.sin_addr));
+ res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction);
+ txtParseParam(szData, nullptr, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP));
+
+ } while (search && res == 500 && atol(szExtIP) == 718 && --i);
+
+ mir_free(szData);
+
+ if (res == 200) {
+ unsigned ip = getExtIP();
+ if (ip) *extip = ip;
+
+ if (numports >= numportsAlloc)
+ mir_realloc(portList, sizeof(uint16_t)*(numportsAlloc += 10));
+ portList[numports++] = *extport;
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return res == 200;
+}
+
+void NetlibUPnPDeletePortMapping(uint16_t extport, char* proto)
+{
+ if (extport == 0)
+ return;
+
+ // findUPnPGateway();
+
+ if (gatewayFound) {
+ unsigned i;
+ char* szData = (char*)mir_alloc(4096);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+ mir_snprintf(szData, 4096, delete_port_mapping, extport, proto);
+ httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction);
+
+ for (i = 0; i < numports; i++)
+ if (portList[i] == extport && --numports > 0)
+ memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(uint16_t));
+
+ mir_free(szData);
+ ReleaseMutex(portListMutex);
+ }
+}
+
+void NetlibUPnPCleanup(void*)
+{
+ // upnp is disabled globally, no need for a cleanup
+ if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0)
+ return;
+
+ {
+ int incoming = 0;
+ mir_cslock lck(csNetlibUser);
+ for (auto &p : netlibUser)
+ if (p->user.flags & NUF_INCOMING) {
+ incoming = 1;
+ break;
+ }
+
+ if (!incoming)
+ return;
+ }
+
+ if (findUPnPGateway()) {
+ char *szData = (char*)alloca(4096);
+ char buf[50], lip[50];
+ unsigned j = 0, k, num = 100;
+
+ strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 &&
+ txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf)))
+ num = atol(buf);
+
+ uint16_t ports[30];
+ for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) {
+ mir_snprintf(szData, 4096, get_port_mapping, i);
+
+ ReleaseMutex(portListMutex);
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200)
+ break;
+
+ if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0)
+ continue;
+
+ if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0)
+ continue;
+
+ if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf))) {
+ uint16_t mport = (uint16_t)atol(buf);
+
+ if (j >= _countof(ports))
+ break;
+
+ for (k = 0; k < numports; ++k)
+ if (portList[k] == mport)
+ break;
+
+ if (k >= numports)
+ ports[j++] = mport;
+ }
+ }
+
+ ReleaseMutex(portListMutex);
+
+ for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++)
+ NetlibUPnPDeletePortMapping(ports[i], "TCP");
+ }
+}
+
+void NetlibUPnPInit(void)
+{
+ numports = 0;
+ numportsAlloc = 10;
+ portList = (uint16_t*)mir_alloc(sizeof(uint16_t)*numportsAlloc);
+
+ portListMutex = CreateMutex(nullptr, FALSE, nullptr);
+}
+
+void NetlibUPnPDestroy(void)
+{
+ mir_free(portList);
+ CloseHandle(portListMutex);
+}
diff --git a/src/mir_app/src/netlib_websocket.cpp b/src/mir_app/src/netlib_websocket.cpp index 4b860cc0db..cd3aaaef4e 100644 --- a/src/mir_app/src/netlib_websocket.cpp +++ b/src/mir_app/src/netlib_websocket.cpp @@ -1,174 +1,174 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include "../../libs/zlib/src/zlib.h" - -MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost, NETLIBHTTPHEADER *pHeaders) -{ - CMStringA tmpHost(szHost); - - // connect to the gateway server - if (!mir_strncmp(tmpHost, "wss://", 6)) - tmpHost.Delete(0, 6); - - auto *nlr = new MHttpRequest; - nlr->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL; - nlr->szUrl = tmpHost.GetBuffer(); - nlr->AddHeader("Accept", "*/*"); - nlr->AddHeader("Upgrade", "websocket"); - nlr->AddHeader("Pragma", "no-cache"); - nlr->AddHeader("Cache-Control", "no-cache"); - nlr->AddHeader("Connection", "keep-alive, Upgrade"); - - uint8_t binNonce[16]; - Utils_GetRandom(binNonce, sizeof(binNonce)); - nlr->AddHeader("Sec-WebSocket-Key", ptrA(mir_base64_encode(binNonce, sizeof(binNonce)))); - nlr->AddHeader("Sec-WebSocket-Version", "13"); - nlr->AddHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits"); - - if (pHeaders) { - while (pHeaders->szName != nullptr) { - nlr->AddHeader(pHeaders->szName, pHeaders->szValue); - pHeaders++; - } - } - - auto *pReply = Netlib_HttpTransaction(nlu, nlr); - delete nlr; - - if (pReply == nullptr) { - Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, send failed", tmpHost.c_str()); - return nullptr; - } - - if (pReply->resultCode != 101) - Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, status %d", tmpHost.c_str(), pReply->resultCode); - - return pReply; -} - -MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize) -{ - if (bufSize < 2) - return false; - - auto *buf = (const uint8_t *)pData; - hdr.bIsFinal = (buf[0] & 0x80) != 0; - hdr.bIsMasked = (buf[1] & 0x80) != 0; - hdr.opCode = buf[0] & 0x0F; - hdr.firstByte = buf[1] & 0x7F; - hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0); - if (bufSize < hdr.headerSize) - return false; - - uint64_t tmpSize = 0; - switch (hdr.firstByte) { - case 0x7F: - tmpSize += ((uint64_t)buf[2]) << 56; - tmpSize += ((uint64_t)buf[3]) << 48; - tmpSize += ((uint64_t)buf[4]) << 40; - tmpSize += ((uint64_t)buf[5]) << 32; - tmpSize += ((uint64_t)buf[6]) << 24; - tmpSize += ((uint64_t)buf[7]) << 16; - tmpSize += ((uint64_t)buf[8]) << 8; - tmpSize += ((uint64_t)buf[9]); - break; - - case 0x7E: - tmpSize += ((uint64_t)buf[2]) << 8; - tmpSize += ((uint64_t)buf[3]); - break; - - default: - tmpSize = hdr.firstByte; - } - hdr.payloadSize = tmpSize; - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t dataLen, uint8_t opCode) -{ - uint8_t header[20]; - size_t datalen; - - header[0] = 0x80 + (opCode & 0x7F); - if (dataLen < 126) { - header[1] = (dataLen & 0xFF); - datalen = 2; - } - else if (dataLen < 65536) { - header[1] = 0x7E; - header[2] = (dataLen >> 8) & 0xFF; - header[3] = dataLen & 0xFF; - datalen = 4; - } - else { - header[1] = 0x7F; - header[2] = (dataLen >> 56) & 0xff; - header[3] = (dataLen >> 48) & 0xff; - header[4] = (dataLen >> 40) & 0xff; - header[5] = (dataLen >> 32) & 0xff; - header[6] = (dataLen >> 24) & 0xff; - header[7] = (dataLen >> 16) & 0xff; - header[8] = (dataLen >> 8) & 0xff; - header[9] = dataLen & 0xff; - datalen = 10; - } - - union { - uint32_t dwMask; - uint8_t arMask[4]; - }; - - dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)dataLen); - memcpy(header + datalen, arMask, _countof(arMask)); - datalen += _countof(arMask); - header[1] |= 0x80; - - ptrA sendBuf((char*)mir_alloc(dataLen + datalen)); - memcpy(sendBuf, header, datalen); - if (dataLen) { - memcpy(sendBuf.get() + datalen, pData, dataLen); - for (size_t i = 0; i < dataLen; i++) - sendBuf[i + datalen] ^= arMask[i & 3]; - } - Netlib_Send(nlc, sendBuf, int(dataLen + datalen), MSG_NODUMP); -} - -MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData) -{ - if (nlc && pData) - WebSocket_Send(nlc, pData, strlen(pData), 1); -} - -MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t dataLen) -{ - if (nlc && pData) - WebSocket_Send(nlc, pData, dataLen, 2); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include "../../libs/zlib/src/zlib.h"
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost, NETLIBHTTPHEADER *pHeaders)
+{
+ CMStringA tmpHost(szHost);
+
+ // connect to the gateway server
+ if (!mir_strncmp(tmpHost, "wss://", 6))
+ tmpHost.Delete(0, 6);
+
+ auto *nlr = new MHttpRequest;
+ nlr->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL;
+ nlr->szUrl = tmpHost.GetBuffer();
+ nlr->AddHeader("Accept", "*/*");
+ nlr->AddHeader("Upgrade", "websocket");
+ nlr->AddHeader("Pragma", "no-cache");
+ nlr->AddHeader("Cache-Control", "no-cache");
+ nlr->AddHeader("Connection", "keep-alive, Upgrade");
+
+ uint8_t binNonce[16];
+ Utils_GetRandom(binNonce, sizeof(binNonce));
+ nlr->AddHeader("Sec-WebSocket-Key", ptrA(mir_base64_encode(binNonce, sizeof(binNonce))));
+ nlr->AddHeader("Sec-WebSocket-Version", "13");
+ nlr->AddHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits");
+
+ if (pHeaders) {
+ while (pHeaders->szName != nullptr) {
+ nlr->AddHeader(pHeaders->szName, pHeaders->szValue);
+ pHeaders++;
+ }
+ }
+
+ auto *pReply = Netlib_HttpTransaction(nlu, nlr);
+ delete nlr;
+
+ if (pReply == nullptr) {
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, send failed", tmpHost.c_str());
+ return nullptr;
+ }
+
+ if (pReply->resultCode != 101)
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, status %d", tmpHost.c_str(), pReply->resultCode);
+
+ return pReply;
+}
+
+MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize)
+{
+ if (bufSize < 2)
+ return false;
+
+ auto *buf = (const uint8_t *)pData;
+ hdr.bIsFinal = (buf[0] & 0x80) != 0;
+ hdr.bIsMasked = (buf[1] & 0x80) != 0;
+ hdr.opCode = buf[0] & 0x0F;
+ hdr.firstByte = buf[1] & 0x7F;
+ hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0);
+ if (bufSize < hdr.headerSize)
+ return false;
+
+ uint64_t tmpSize = 0;
+ switch (hdr.firstByte) {
+ case 0x7F:
+ tmpSize += ((uint64_t)buf[2]) << 56;
+ tmpSize += ((uint64_t)buf[3]) << 48;
+ tmpSize += ((uint64_t)buf[4]) << 40;
+ tmpSize += ((uint64_t)buf[5]) << 32;
+ tmpSize += ((uint64_t)buf[6]) << 24;
+ tmpSize += ((uint64_t)buf[7]) << 16;
+ tmpSize += ((uint64_t)buf[8]) << 8;
+ tmpSize += ((uint64_t)buf[9]);
+ break;
+
+ case 0x7E:
+ tmpSize += ((uint64_t)buf[2]) << 8;
+ tmpSize += ((uint64_t)buf[3]);
+ break;
+
+ default:
+ tmpSize = hdr.firstByte;
+ }
+ hdr.payloadSize = tmpSize;
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t dataLen, uint8_t opCode)
+{
+ uint8_t header[20];
+ size_t datalen;
+
+ header[0] = 0x80 + (opCode & 0x7F);
+ if (dataLen < 126) {
+ header[1] = (dataLen & 0xFF);
+ datalen = 2;
+ }
+ else if (dataLen < 65536) {
+ header[1] = 0x7E;
+ header[2] = (dataLen >> 8) & 0xFF;
+ header[3] = dataLen & 0xFF;
+ datalen = 4;
+ }
+ else {
+ header[1] = 0x7F;
+ header[2] = (dataLen >> 56) & 0xff;
+ header[3] = (dataLen >> 48) & 0xff;
+ header[4] = (dataLen >> 40) & 0xff;
+ header[5] = (dataLen >> 32) & 0xff;
+ header[6] = (dataLen >> 24) & 0xff;
+ header[7] = (dataLen >> 16) & 0xff;
+ header[8] = (dataLen >> 8) & 0xff;
+ header[9] = dataLen & 0xff;
+ datalen = 10;
+ }
+
+ union {
+ uint32_t dwMask;
+ uint8_t arMask[4];
+ };
+
+ dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)dataLen);
+ memcpy(header + datalen, arMask, _countof(arMask));
+ datalen += _countof(arMask);
+ header[1] |= 0x80;
+
+ ptrA sendBuf((char*)mir_alloc(dataLen + datalen));
+ memcpy(sendBuf, header, datalen);
+ if (dataLen) {
+ memcpy(sendBuf.get() + datalen, pData, dataLen);
+ for (size_t i = 0; i < dataLen; i++)
+ sendBuf[i + datalen] ^= arMask[i & 3];
+ }
+ Netlib_Send(nlc, sendBuf, int(dataLen + datalen), MSG_NODUMP);
+}
+
+MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData)
+{
+ if (nlc && pData)
+ WebSocket_Send(nlc, pData, strlen(pData), 1);
+}
+
+MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t dataLen)
+{
+ if (nlc && pData)
+ WebSocket_Send(nlc, pData, dataLen, 2);
+}
diff --git a/src/mir_app/src/newplugins.cpp b/src/mir_app/src/newplugins.cpp index 6a79d72330..367afde7b0 100644 --- a/src/mir_app/src/newplugins.cpp +++ b/src/mir_app/src/newplugins.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/options.cpp b/src/mir_app/src/options.cpp index ea7f32ac99..81c136a0ab 100644 --- a/src/mir_app/src/options.cpp +++ b/src/mir_app/src/options.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
Copyright (c) 2007 Artem Shpynov
all portions of this codebase are copyrighted to the people
diff --git a/src/mir_app/src/path.cpp b/src/mir_app/src/path.cpp index 616100510f..49e5cd7487 100644 --- a/src/mir_app/src/path.cpp +++ b/src/mir_app/src/path.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/pluginopts.cpp b/src/mir_app/src/pluginopts.cpp index c601a83072..7e58c5fca3 100644 --- a/src/mir_app/src/pluginopts.cpp +++ b/src/mir_app/src/pluginopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/popupOption.cpp b/src/mir_app/src/popupOption.cpp index 6749ba4e9c..4ac5290c6d 100644 --- a/src/mir_app/src/popupOption.cpp +++ b/src/mir_app/src/popupOption.cpp @@ -1,123 +1,123 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" - -struct MPopupOption -{ - MPopupOption(CMPluginBase *pPlugin, const char *pszDescr, CMOption<bool> &pVal) : - m_plugin(pPlugin), - m_val(pVal), - m_descr(pszDescr) - {} - - MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption<bool> &pVal) : - m_plugin(pPlugin), - m_val(pVal), - m_descr(pwszDescr) - {} - - CMPluginBase *m_plugin; - CMOption<bool> &m_val; - CMStringW m_descr; -}; - -static OBJLIST<MPopupOption> g_arOptions(1); - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addPopupOption(const char *pszDescr, CMOption<bool> &pVal) -{ - g_arOptions.insert(new MPopupOption(this, pszDescr, pVal)); - return 0; -} - -int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &pVal) -{ - g_arOptions.insert(new MPopupOption(this, pwszDescr, pVal)); - return 0; -} - -void KillModulePopups(CMPluginBase *pPlugin) -{ - for (auto &it : g_arOptions.rev_iter()) - if (it->m_plugin == pPlugin) - g_arOptions.remove(g_arOptions.indexOf(&it)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CPopupOptionsDlg : public CDlgBase -{ - CCtrlListView m_list; - -public: - CPopupOptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_POPUPOPTION), - m_list(this, IDC_TREE) - {} - - bool OnInitDialog() override - { - m_list.SetExtendedListViewStyleEx(0, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); - - LVITEM lvi; - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iSubItem = 0; - - for (auto &it : g_arOptions) { - lvi.pszText = TranslateW_LP(it->m_descr, it->m_plugin); - lvi.lParam = LPARAM(it); - - int iRow = m_list.InsertItem(&lvi); - m_list.SetItemState(iRow, it->m_val ? 0x2000 : 0x1000, LVIS_STATEIMAGEMASK); - } - - return true; - } - - bool OnApply() override - { - int iRows = m_list.GetItemCount(); - - for (int i = 0; i < iRows; i++) { - auto *p = (MPopupOption *)m_list.GetItemData(i); - p->m_val = m_list.GetItemState(i, LVIS_STATEIMAGEMASK) == 0x2000; - } - return true; - } -}; - -int PopupOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -1000000000; - odp.szGroup.a = LPGEN("Popups"); - odp.szTitle.a = LPGEN("Events"); - odp.pDialog = new CPopupOptionsDlg(); - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "plugins.h"
+
+struct MPopupOption
+{
+ MPopupOption(CMPluginBase *pPlugin, const char *pszDescr, CMOption<bool> &pVal) :
+ m_plugin(pPlugin),
+ m_val(pVal),
+ m_descr(pszDescr)
+ {}
+
+ MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption<bool> &pVal) :
+ m_plugin(pPlugin),
+ m_val(pVal),
+ m_descr(pwszDescr)
+ {}
+
+ CMPluginBase *m_plugin;
+ CMOption<bool> &m_val;
+ CMStringW m_descr;
+};
+
+static OBJLIST<MPopupOption> g_arOptions(1);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addPopupOption(const char *pszDescr, CMOption<bool> &pVal)
+{
+ g_arOptions.insert(new MPopupOption(this, pszDescr, pVal));
+ return 0;
+}
+
+int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &pVal)
+{
+ g_arOptions.insert(new MPopupOption(this, pwszDescr, pVal));
+ return 0;
+}
+
+void KillModulePopups(CMPluginBase *pPlugin)
+{
+ for (auto &it : g_arOptions.rev_iter())
+ if (it->m_plugin == pPlugin)
+ g_arOptions.remove(g_arOptions.indexOf(&it));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CPopupOptionsDlg : public CDlgBase
+{
+ CCtrlListView m_list;
+
+public:
+ CPopupOptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_POPUPOPTION),
+ m_list(this, IDC_TREE)
+ {}
+
+ bool OnInitDialog() override
+ {
+ m_list.SetExtendedListViewStyleEx(0, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
+
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iSubItem = 0;
+
+ for (auto &it : g_arOptions) {
+ lvi.pszText = TranslateW_LP(it->m_descr, it->m_plugin);
+ lvi.lParam = LPARAM(it);
+
+ int iRow = m_list.InsertItem(&lvi);
+ m_list.SetItemState(iRow, it->m_val ? 0x2000 : 0x1000, LVIS_STATEIMAGEMASK);
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int iRows = m_list.GetItemCount();
+
+ for (int i = 0; i < iRows; i++) {
+ auto *p = (MPopupOption *)m_list.GetItemData(i);
+ p->m_val = m_list.GetItemState(i, LVIS_STATEIMAGEMASK) == 0x2000;
+ }
+ return true;
+ }
+};
+
+int PopupOptionsInit(WPARAM wParam)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -1000000000;
+ odp.szGroup.a = LPGEN("Popups");
+ odp.szTitle.a = LPGEN("Events");
+ odp.pDialog = new CPopupOptionsDlg();
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/popups.cpp b/src/mir_app/src/popups.cpp index bc4d5f86d8..aa7af870f3 100644 --- a/src/mir_app/src/popups.cpp +++ b/src/mir_app/src/popups.cpp @@ -1,157 +1,157 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static bool bModuleInit = false, bPopupsEnabled = true; - -MIR_APP_DLL(bool) Popup_Enabled() -{ - if (!bModuleInit) { - bModuleInit = true; - bPopupsEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0; - } - - return bPopupsEnabled; -} - -MIR_APP_DLL(void) Popup_Enable(bool bEnable) -{ - bPopupsEnabled = bEnable; - db_set_b(0, "Popup", "ModuleIsEnabled", bEnable); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup v2.0 - -MIR_APP_DLL(HWND) Popup_Add(const POPUPDATA2 *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUP2, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(void) Popup_Change(HWND hwndPopup, const POPUPDATA2 *pData) -{ - CallService(MS_POPUP_CHANGEPOPUP2, (WPARAM)hwndPopup, (LPARAM)pData); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popups v1.0 - -MIR_APP_DLL(HWND) PUAddPopup(POPUPDATA *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(HWND) PUAddPopupW(POPUPDATAW *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(int) PUChangeW(HWND hWndPopup, POPUPDATAW *newData) -{ - return (int)CallService(MS_POPUP_CHANGEW, (WPARAM)hWndPopup, (LPARAM)newData); -} - -MIR_APP_DLL(int) PUChangeTextW(HWND hWndPopup, const wchar_t *lpwzNewText) -{ - return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopup, (LPARAM)lpwzNewText); -} - -MIR_APP_DLL(int) PUDeletePopup(HWND hWndPopup) -{ - return (int)CallService(MS_POPUP_DESTROYPOPUP, 0, (LPARAM)hWndPopup); -} - -MIR_APP_DLL(MCONTACT) PUGetContact(HWND hPopupWindow) -{ - return (MCONTACT)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopupWindow, 0); -} - -MIR_APP_DLL(void*) PUGetPluginData(HWND hPopupWindow) -{ - return (void*)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hPopupWindow, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Messages - -MIR_APP_DLL(int) PUShowMessage(const char *lpzText, uint32_t kind) -{ - return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText, (LPARAM)kind); -} - -MIR_APP_DLL(int) PUShowMessageW(const wchar_t *lpwzText, uint32_t kind) -{ - return (int)CallService(MS_POPUP_SHOWMESSAGEW, (WPARAM)lpwzText, (LPARAM)kind); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Actions - -MIR_APP_DLL(int) PURegisterActions(POPUPACTION *actions, int count) -{ - return (int)CallService(MS_POPUP_REGISTERACTIONS, (WPARAM)actions, (LPARAM)count); -} - -MIR_APP_DLL(HANDLE) PURegisterNotification(POPUPNOTIFICATION *notification) -{ - return (HANDLE)CallService(MS_POPUP_REGISTERNOTIFICATION, (WPARAM)notification, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup classes - -MIR_APP_DLL(HANDLE) Popup_RegisterClass(POPUPCLASS *pc) -{ - return (HANDLE)CallService(MS_POPUP_REGISTERCLASS, 0, LPARAM(pc)); -} - -MIR_APP_DLL(void) Popup_UnregisterClass(HANDLE ppc) -{ - if (ppc) - CallService(MS_POPUP_UNREGISTERCLASS, 0, LPARAM(ppc)); -} - -MIR_APP_DLL(HWND) Popup_AddClass(POPUPDATACLASS *pData) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)pData); -} - -MIR_APP_DLL(HWND) ShowClassPopup(const char *name, const char *title, const char *text) -{ - POPUPDATACLASS d = {}; - d.pszClassName = name; - d.szTitle.a = title; - d.szText.a = text; - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); -} - -MIR_APP_DLL(HWND) ShowClassPopupW(const char *name, const wchar_t *title, const wchar_t *text) -{ - POPUPDATACLASS d = {}; - d.pszClassName = name; - d.szTitle.w = title; - d.szText.w = text; - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static bool bModuleInit = false, bPopupsEnabled = true;
+
+MIR_APP_DLL(bool) Popup_Enabled()
+{
+ if (!bModuleInit) {
+ bModuleInit = true;
+ bPopupsEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0;
+ }
+
+ return bPopupsEnabled;
+}
+
+MIR_APP_DLL(void) Popup_Enable(bool bEnable)
+{
+ bPopupsEnabled = bEnable;
+ db_set_b(0, "Popup", "ModuleIsEnabled", bEnable);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup v2.0
+
+MIR_APP_DLL(HWND) Popup_Add(const POPUPDATA2 *ppdp, int flags)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUP2, (WPARAM)ppdp, flags);
+}
+
+MIR_APP_DLL(void) Popup_Change(HWND hwndPopup, const POPUPDATA2 *pData)
+{
+ CallService(MS_POPUP_CHANGEPOPUP2, (WPARAM)hwndPopup, (LPARAM)pData);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popups v1.0
+
+MIR_APP_DLL(HWND) PUAddPopup(POPUPDATA *ppdp, int flags)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp, flags);
+}
+
+MIR_APP_DLL(HWND) PUAddPopupW(POPUPDATAW *ppdp, int flags)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp, flags);
+}
+
+MIR_APP_DLL(int) PUChangeW(HWND hWndPopup, POPUPDATAW *newData)
+{
+ return (int)CallService(MS_POPUP_CHANGEW, (WPARAM)hWndPopup, (LPARAM)newData);
+}
+
+MIR_APP_DLL(int) PUChangeTextW(HWND hWndPopup, const wchar_t *lpwzNewText)
+{
+ return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopup, (LPARAM)lpwzNewText);
+}
+
+MIR_APP_DLL(int) PUDeletePopup(HWND hWndPopup)
+{
+ return (int)CallService(MS_POPUP_DESTROYPOPUP, 0, (LPARAM)hWndPopup);
+}
+
+MIR_APP_DLL(MCONTACT) PUGetContact(HWND hPopupWindow)
+{
+ return (MCONTACT)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopupWindow, 0);
+}
+
+MIR_APP_DLL(void*) PUGetPluginData(HWND hPopupWindow)
+{
+ return (void*)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hPopupWindow, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Messages
+
+MIR_APP_DLL(int) PUShowMessage(const char *lpzText, uint32_t kind)
+{
+ return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText, (LPARAM)kind);
+}
+
+MIR_APP_DLL(int) PUShowMessageW(const wchar_t *lpwzText, uint32_t kind)
+{
+ return (int)CallService(MS_POPUP_SHOWMESSAGEW, (WPARAM)lpwzText, (LPARAM)kind);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Actions
+
+MIR_APP_DLL(int) PURegisterActions(POPUPACTION *actions, int count)
+{
+ return (int)CallService(MS_POPUP_REGISTERACTIONS, (WPARAM)actions, (LPARAM)count);
+}
+
+MIR_APP_DLL(HANDLE) PURegisterNotification(POPUPNOTIFICATION *notification)
+{
+ return (HANDLE)CallService(MS_POPUP_REGISTERNOTIFICATION, (WPARAM)notification, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup classes
+
+MIR_APP_DLL(HANDLE) Popup_RegisterClass(POPUPCLASS *pc)
+{
+ return (HANDLE)CallService(MS_POPUP_REGISTERCLASS, 0, LPARAM(pc));
+}
+
+MIR_APP_DLL(void) Popup_UnregisterClass(HANDLE ppc)
+{
+ if (ppc)
+ CallService(MS_POPUP_UNREGISTERCLASS, 0, LPARAM(ppc));
+}
+
+MIR_APP_DLL(HWND) Popup_AddClass(POPUPDATACLASS *pData)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)pData);
+}
+
+MIR_APP_DLL(HWND) ShowClassPopup(const char *name, const char *title, const char *text)
+{
+ POPUPDATACLASS d = {};
+ d.pszClassName = name;
+ d.szTitle.a = title;
+ d.szText.a = text;
+ return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d);
+}
+
+MIR_APP_DLL(HWND) ShowClassPopupW(const char *name, const wchar_t *title, const wchar_t *text)
+{
+ POPUPDATACLASS d = {};
+ d.pszClassName = name;
+ d.szTitle.w = title;
+ d.szText.w = text;
+ return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d);
+}
diff --git a/src/mir_app/src/profilemanager.cpp b/src/mir_app/src/profilemanager.cpp index edbd7101cb..b2ea141b6c 100644 --- a/src/mir_app/src/profilemanager.cpp +++ b/src/mir_app/src/profilemanager.cpp @@ -1,641 +1,641 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" -#include "langpack.h" -#include "profilemanager.h" -#include <sys/stat.h> - -#pragma warning(disable : 4512) - -#define WM_INPUTCHANGED (WM_USER + 0x3000) -#define WM_FOCUSTEXTBOX (WM_USER + 0x3001) - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile creator - -static BOOL EnumProfilesForList(const wchar_t *tszFullPath, wchar_t *profile, CCtrlListView &list, const wchar_t *szProfile) -{ - wchar_t sizeBuf[64]; - bool bFileLocked; - - wchar_t *p = wcsrchr(profile, '.'); - mir_wstrcpy(sizeBuf, L"0 KB"); - if (p != nullptr) *p = 0; - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; - item.pszText = profile; - item.iItem = 0; - - struct _stat statbuf; - if (_wstat(tszFullPath, &statbuf) == 0) { - if (statbuf.st_size > 1000000) { - mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1048576.0); - mir_wstrcpy(sizeBuf + 5, L" MB"); - } - else { - mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1024.0); - mir_wstrcpy(sizeBuf + 5, L" KB"); - } - bFileLocked = Profile_CheckOpened(tszFullPath); - } - else bFileLocked = true; - - DATABASELINK *dblink; - switch (touchDatabase(tszFullPath, &dblink)) { - case ERROR_SUCCESS: - item.iImage = (bFileLocked) ? 1 : 0; - break; - - case EGROKPRF_OBSOLETE: - item.iImage = 2; - break; - - case EGROKPRF_CANTREAD: - item.iImage = (bFileLocked) ? 1 : 3; - break; - - default: - item.iImage = 3; - } - - item.lParam = (LPARAM)dblink; - - int iItem = list.InsertItem(&item); - if (mir_wstrcmpi(szProfile, tszFullPath) == 0) - list.SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - - list.SetItemText(iItem, 2, sizeBuf); - - if (dblink != nullptr) - list.SetItemText(iItem, 1, TranslateW(dblink->szFullName)); - else if (bFileLocked) // file locked - list.SetItemText(iItem, 1, TranslateT("<In use>")); - else - list.SetItemText(iItem, 1, TranslateT("<Unknown format>")); - - return TRUE; -} - -static int findProfiles(CCtrlListView &list, const wchar_t *szProfile) -{ - // find in Miranda NG profile subfolders - MFilePath searchspec; - searchspec.Format(L"%s\\*.*", g_profileDir); - - for (auto &it: searchspec.search()) { - // find all subfolders except "." and ".." - if (!it.isDir() || !wcscmp(it.getPath(), L".") || !wcscmp(it.getPath(), L"..")) - continue; - - MFilePath fullPath; - fullPath.Format(L"%s\\%s\\%s.dat", g_profileDir, it.getPath(), it.getPath()); - if (fullPath.isExist()) { - wchar_t profileName[MAX_PATH]; - mir_snwprintf(profileName, L"%s.dat", it.getPath()); - if (!EnumProfilesForList(fullPath, profileName, list, szProfile)) - break; - } - } - - return 1; -} - -static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_CHAR) { - if (wcschr(L".?/\\#' ", (wchar_t)wParam) != nullptr) - return 0; - PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0); - } - return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam); -} - -class CCreateProfileDlg : public CDlgBase -{ - CCtrlButton &m_btnOk; - PROFILEMANAGERDATA *m_pd; - - int CreateProfile(const wchar_t *profile, DATABASELINK *link) - { - // check if the file already exists - const wchar_t *file = wcsrchr(profile, '\\'); - if (file) - file++; - - int err = 0; - wchar_t buf[256]; - - if (_waccess(profile, 0) == 0) { - // file already exists! - mir_snwprintf(buf, - TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."), - file); - if (MessageBoxW(m_hwnd, buf, TranslateT("The profile already exists"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) - return 0; - - // move the file - if (DeleteDirectoryTreeW(profile, true) != 0) { - mir_snwprintf(buf, TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file); - MessageBoxW(m_hwnd, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION | MB_OK); - return 0; - } - // now the file should be gone! - } - // ask the database to create the profile - CreatePathToFileW(profile); - if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) { - mir_snwprintf(buf, TranslateT("Unable to create the profile '%s', the error was %x"), file, err); - MessageBoxW(m_hwnd, buf, TranslateT("Problem creating profile"), MB_ICONERROR | MB_OK); - return 0; - } - - // the profile has been created! - g_bDbCreated = true; - return 1; - } - - bool m_bFocused; - CCtrlCombo m_driverList; - CCtrlEdit m_profileName; - CCtrlBase m_warning; - -public: - CCreateProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILE_NEW), - m_btnOk(_btn), - m_pd(_pd), - m_bFocused(false), - m_driverList(this, IDC_PROFILEDRIVERS), - m_profileName(this, IDC_PROFILENAME), - m_warning(this, IDC_NODBDRIVERS) - {} - - bool OnInitDialog() override - { - // what, no plugins?! - if (arDbPlugins.getCount() == 0) { - m_driverList.Enable(false); - m_profileName.Enable(false); - ShowWindow(m_warning.GetHwnd(), TRUE); - } - else { - for (auto &p : arDbPlugins) - if (p->capabilities & MDB_CAPS_CREATE) - m_driverList.AddString(TranslateW(p->szFullName), (LPARAM)p); - } - - // default item - m_driverList.SetCurSel(0); - - // subclass the profile name box - mir_subclassWindow(m_profileName.GetHwnd(), ProfileNameValidate); - - // decide if there is a default profile name given in the INI and if it should be used - if (m_pd->noProfiles || (shouldAutoCreate(m_pd->m_profile) && !m_pd->m_profile.isExist())) { - wchar_t *profile = wcsrchr(m_pd->m_profile.GetBuffer(), '\\'); - if (profile) ++profile; - else profile = m_pd->m_profile.GetBuffer(); - - wchar_t *p = wcsrchr(profile, '.'); - wchar_t c = 0; - if (p) { c = *p; *p = 0; } - - m_profileName.SetText(profile); - if (c) *p = c; - } - - // focus on the textbox - PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); - return true; - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - switch (msg) { - case WM_FOCUSTEXTBOX: - SetFocus(m_profileName.GetHwnd()); - break; - - case WM_INPUTCHANGED: // when input in the edit box changes - NotifyChange(); - m_btnOk.Enable(GetWindowTextLength(m_profileName.GetHwnd()) > 0); - break; - - case WM_SHOWWINDOW: - if (wParam) { - m_btnOk.SetText(TranslateT("&Create")); - SendMessage(m_hwnd, WM_INPUTCHANGED, 0, 0); - m_bFocused = true; - } - else m_bFocused = false; - break; - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - - bool OnApply() override - { - LRESULT curSel = m_driverList.GetCurSel(); - if (curSel == -1 || !m_bFocused) - return false; // should never happen - - ptrW szName(m_profileName.GetText()); - if (mir_wstrlen(szName) == 0) - return false; - - // profile placed in "profile_name" subfolder - m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, szName.get(), szName.get()); - m_pd->dblink = (DATABASELINK *)m_driverList.GetItemData(curSel); - - if (CreateProfile(m_pd->m_profile, m_pd->dblink) == 0) - SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); - return true; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile selector - -static int numMessages[5]; - -static void stubAddMessage(int iType, const wchar_t *, ...) -{ - if (iType < 5) - numMessages[iType]++; -} - -class CChooseProfileDlg : public CDlgBase -{ - CCtrlButton &m_btnOk; - PROFILEMANAGERDATA *m_pd; - HANDLE m_hFileNotify; - - void DeleteProfile(const LVITEM &item) - { - CMStringW wszMessage(FORMAT, TranslateT("Are you sure you want to remove profile \"%s\"?"), item.pszText); - if (IDYES != MessageBoxW(nullptr, wszMessage, L"Miranda NG", MB_YESNO | MB_TASKMODAL | MB_ICONWARNING)) - return; - - wszMessage.Format(L"%s\\%s", g_profileDir, item.pszText); - DeleteDirectoryTreeW(wszMessage, true); - - m_profileList.DeleteItem(item.iItem); - } - - void CheckProfile(const wchar_t *profile) - { - CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - - if (TryLoadPlugin(plugin_checker, false)) - CallService(MS_DB_CHECKPROFILE, (WPARAM)wszFullName.c_str(), 0); - else - Plugin_Uninit(plugin_checker); - } - - void CompactProfile(DATABASELINK *dblink, const wchar_t *profile) - { - CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - - if (auto *db = dblink->Load(wszFullName, false)) { - db->Compact(); - delete db; - - MessageBoxW(nullptr, TranslateT("Database was compacted successfully"), L"Miranda NG", MB_OK | MB_ICONINFORMATION); - } - } - - void CheckRun() - { - m_btnOk.Enable(m_profileList.GetSelectedCount() == 1); - - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE; - item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (!m_profileList.GetItem(&item)) - return; - - switch(item.iImage) { - case 3: - m_btnOk.Enable(false); - return; - - case 2: - m_btnOk.SetText(TranslateT("&Convert")); - break; - - default: - m_btnOk.SetText(TranslateT("&Run")); - } - - // profile is placed in "profile_name" subfolder - - wchar_t tmpPath[MAX_PATH]; - mir_snwprintf(tmpPath, L"%s\\%s.dat", g_profileDir, profile); - if (_waccess(tmpPath, 2)) - m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - else - m_pd->m_profile = tmpPath; - } - - void ExecuteMenu(LPARAM lParam) - { - LVHITTESTINFO lvht = { 0 }; - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - ScreenToClient(m_profileList.GetHwnd(), &lvht.pt); - - if (m_profileList.HitTest(&lvht) == -1) - return; - - if (lvht.iItem == -1) - return; - - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT; - item.iItem = lvht.iItem; - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (!m_profileList.GetItem(&item)) - return; - - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - - HMENU hMenu = CreatePopupMenu(); - if (item.iImage < 2) { - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - } - - DATABASELINK *dblink = (DATABASELINK*)item.lParam; - if (dblink != nullptr) { - bool bAdded = false; - if (dblink->capabilities & MDB_CAPS_COMPACT) { - AppendMenu(hMenu, MF_STRING, 3, TranslateT("Compact database")); - bAdded = true; - } - - if (plugin_checker && (dblink->capabilities & MDB_CAPS_CHECK)) { - AppendMenu(hMenu, MF_STRING, 4, TranslateT("Check database")); - bAdded = true; - } - - if (bAdded) - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - } - - AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete")); - int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, m_hwnd, nullptr); - switch (index) { - case 1: - SendMessage(GetParent(m_hwndParent), WM_COMMAND, IDOK, 0); - break; - - case 2: - DeleteProfile(item); - break; - - case 3: - CompactProfile(dblink, profile); - break; - - case 4: - CheckProfile(profile); - break; - } - DestroyMenu(hMenu); - } - - CCtrlListView m_profileList; - -public: - CChooseProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILE_SELECTION), - m_btnOk(_btn), - m_pd(_pd), - m_profileList(this, IDC_PROFILELIST) - { - m_profileList.OnItemChanged = Callback(this, &CChooseProfileDlg::list_OnItemChanged); - m_profileList.OnKeyDown = Callback(this, &CChooseProfileDlg::list_OnKeyDown); - m_profileList.OnGetInfoTip = Callback(this, &CChooseProfileDlg::list_OnGetTip); - m_profileList.OnDoubleClick = Callback(this, &CChooseProfileDlg::list_OnDblClick); - } - - bool OnInitDialog() override - { - // set columns - LVCOLUMN col; - col.mask = LVCF_TEXT | LVCF_WIDTH; - col.pszText = TranslateT("Profile"); - col.cx = 100; - m_profileList.InsertColumn(0, &col); - - col.pszText = TranslateT("Driver"); - col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL); - m_profileList.InsertColumn(1, &col); - - col.pszText = TranslateT("Size"); - col.cx = 60; - m_profileList.InsertColumn(2, &col); - - // icons - HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL)); - - // LV will destroy the image list - m_profileList.SetImageList(hImgList, LVSIL_SMALL); - m_profileList.SetExtendedListViewStyle(m_profileList.GetExtendedListViewStyle() | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT); - - // find all the profiles - findProfiles(m_profileList, m_pd->m_profile); - PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); - - m_hFileNotify = FindFirstChangeNotification(g_profileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE); - if (m_hFileNotify != INVALID_HANDLE_VALUE) - SetTimer(m_hwnd, 0, 1200, nullptr); - return true; - } - - void OnDestroy() - { - KillTimer(m_hwnd, 0); - FindCloseChangeNotification(m_hFileNotify); - } - - void list_OnItemChanged(CCtrlListView::TEventInfo*) - { - CheckRun(); - } - - void list_OnKeyDown(CCtrlListView::TEventInfo *evt) - { - if (evt->nmlvkey->wVKey == VK_DELETE) { - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT; - item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (m_profileList.GetItem(&item)) - DeleteProfile(item); - } - } - - void list_OnGetTip(CCtrlListView::TEventInfo *evt) - { - if (auto pTip = evt->nmlvit) { - wchar_t profilename[MAX_PATH], tszFullPath[MAX_PATH]; - struct _stat statbuf; - m_profileList.GetItemText(pTip->iItem, 0, profilename, _countof(profilename)); - mir_snwprintf(tszFullPath, L"%s\\%s\\%s.dat", g_profileDir, profilename, profilename); - _wstat(tszFullPath, &statbuf); - mir_snwprintf(pTip->pszText, pTip->cchTextMax, L"%s\n%s: %s\n%s: %s", tszFullPath, TranslateT("Created"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_mtime)))); - } - } - - void list_OnDblClick(CCtrlListView::TEventInfo*) - { - CheckRun(); - EndDialog(GetParent(m_hwndParent), 1); - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - switch (msg) { - case WM_TIMER: - if (WaitForSingleObject(m_hFileNotify, 0) == WAIT_OBJECT_0) { - m_profileList.DeleteAllItems(); - findProfiles(m_profileList, m_pd->m_profile); - FindNextChangeNotification(m_hFileNotify); - } - break; - - case WM_FOCUSTEXTBOX: - SetFocus(m_profileList.GetHwnd()); - if (m_pd->m_profile.IsEmpty() || m_profileList.GetSelectedCount() == 0) - m_profileList.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - break; - - case WM_SHOWWINDOW: - if (wParam) - CheckRun(); - break; - - case WM_CONTEXTMENU: - ExecuteMenu(lParam); - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Tab manager + its envelope - -class CProfileManager : public CDlgBase -{ - PROFILEMANAGERDATA *m_pd; - - CCtrlPages m_tab; - CCtrlButton m_btnOk; - CCtrlCheck m_chkSmEnabled; - CCtrlCombo m_servicePlugs; - CCtrlBase m_warning; - -public: - CProfileManager(PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILEMANAGER), - m_btnOk(this, IDOK), - m_pd(_pd), - m_tab(this, IDC_TABS), - m_warning(this, IDC_SM_LABEL), - m_servicePlugs(this, IDC_SM_COMBO), - m_chkSmEnabled(this, IDC_SM_ENABLED) - { - m_chkSmEnabled.OnChange = Callback(this, &CProfileManager::onChanged); - - m_tab.AddPage(LPGENW("My profiles"), nullptr, new CChooseProfileDlg(m_btnOk, m_pd)); - m_tab.AddPage(LPGENW("New profile"), nullptr, new CCreateProfileDlg(m_btnOk, m_pd)); - } - - bool OnInitDialog() override - { - // MUST NOT be replaced with Window_SetIcon_IcoLib!!! - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconSX, g_iIconSY, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconX, g_iIconY, 0)); - - if (m_pd->noProfiles || shouldAutoCreate(m_pd->m_profile)) - m_tab.ActivatePage(1); - - // service mode combobox - if (servicePlugins.getCount() == 0) { - ShowWindow(m_warning.GetHwnd(), FALSE); - ShowWindow(m_chkSmEnabled.GetHwnd(), FALSE); - ShowWindow(m_servicePlugs.GetHwnd(), FALSE); - } - else { - for (int i = 0; i < servicePlugins.getCount(); i++) { - pluginEntry *p = servicePlugins[i]; - m_servicePlugs.AddStringA(p->pluginname, i); - } - - m_servicePlugs.Disable(); - m_servicePlugs.SetCurSel(0); - } - return true; - } - - void OnDestroy() - { - if (m_chkSmEnabled.GetState()) { - int idx = m_servicePlugs.GetCurData(); - if (idx != -1) - plugin_service = servicePlugins[idx]; - } - - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - } - - void onChanged(CCtrlCheck*) - { - m_servicePlugs.Enable(m_chkSmEnabled.GetState()); - } -}; - -int getProfileManager(PROFILEMANAGERDATA *pd) -{ - return CProfileManager(pd).DoModal(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "plugins.h"
+#include "langpack.h"
+#include "profilemanager.h"
+#include <sys/stat.h>
+
+#pragma warning(disable : 4512)
+
+#define WM_INPUTCHANGED (WM_USER + 0x3000)
+#define WM_FOCUSTEXTBOX (WM_USER + 0x3001)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Profile creator
+
+static BOOL EnumProfilesForList(const wchar_t *tszFullPath, wchar_t *profile, CCtrlListView &list, const wchar_t *szProfile)
+{
+ wchar_t sizeBuf[64];
+ bool bFileLocked;
+
+ wchar_t *p = wcsrchr(profile, '.');
+ mir_wstrcpy(sizeBuf, L"0 KB");
+ if (p != nullptr) *p = 0;
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
+ item.pszText = profile;
+ item.iItem = 0;
+
+ struct _stat statbuf;
+ if (_wstat(tszFullPath, &statbuf) == 0) {
+ if (statbuf.st_size > 1000000) {
+ mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1048576.0);
+ mir_wstrcpy(sizeBuf + 5, L" MB");
+ }
+ else {
+ mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1024.0);
+ mir_wstrcpy(sizeBuf + 5, L" KB");
+ }
+ bFileLocked = Profile_CheckOpened(tszFullPath);
+ }
+ else bFileLocked = true;
+
+ DATABASELINK *dblink;
+ switch (touchDatabase(tszFullPath, &dblink)) {
+ case ERROR_SUCCESS:
+ item.iImage = (bFileLocked) ? 1 : 0;
+ break;
+
+ case EGROKPRF_OBSOLETE:
+ item.iImage = 2;
+ break;
+
+ case EGROKPRF_CANTREAD:
+ item.iImage = (bFileLocked) ? 1 : 3;
+ break;
+
+ default:
+ item.iImage = 3;
+ }
+
+ item.lParam = (LPARAM)dblink;
+
+ int iItem = list.InsertItem(&item);
+ if (mir_wstrcmpi(szProfile, tszFullPath) == 0)
+ list.SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+
+ list.SetItemText(iItem, 2, sizeBuf);
+
+ if (dblink != nullptr)
+ list.SetItemText(iItem, 1, TranslateW(dblink->szFullName));
+ else if (bFileLocked) // file locked
+ list.SetItemText(iItem, 1, TranslateT("<In use>"));
+ else
+ list.SetItemText(iItem, 1, TranslateT("<Unknown format>"));
+
+ return TRUE;
+}
+
+static int findProfiles(CCtrlListView &list, const wchar_t *szProfile)
+{
+ // find in Miranda NG profile subfolders
+ MFilePath searchspec;
+ searchspec.Format(L"%s\\*.*", g_profileDir);
+
+ for (auto &it: searchspec.search()) {
+ // find all subfolders except "." and ".."
+ if (!it.isDir() || !wcscmp(it.getPath(), L".") || !wcscmp(it.getPath(), L".."))
+ continue;
+
+ MFilePath fullPath;
+ fullPath.Format(L"%s\\%s\\%s.dat", g_profileDir, it.getPath(), it.getPath());
+ if (fullPath.isExist()) {
+ wchar_t profileName[MAX_PATH];
+ mir_snwprintf(profileName, L"%s.dat", it.getPath());
+ if (!EnumProfilesForList(fullPath, profileName, list, szProfile))
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_CHAR) {
+ if (wcschr(L".?/\\#' ", (wchar_t)wParam) != nullptr)
+ return 0;
+ PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0);
+ }
+ return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam);
+}
+
+class CCreateProfileDlg : public CDlgBase
+{
+ CCtrlButton &m_btnOk;
+ PROFILEMANAGERDATA *m_pd;
+
+ int CreateProfile(const wchar_t *profile, DATABASELINK *link)
+ {
+ // check if the file already exists
+ const wchar_t *file = wcsrchr(profile, '\\');
+ if (file)
+ file++;
+
+ int err = 0;
+ wchar_t buf[256];
+
+ if (_waccess(profile, 0) == 0) {
+ // file already exists!
+ mir_snwprintf(buf,
+ TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."),
+ file);
+ if (MessageBoxW(m_hwnd, buf, TranslateT("The profile already exists"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES)
+ return 0;
+
+ // move the file
+ if (DeleteDirectoryTreeW(profile, true) != 0) {
+ mir_snwprintf(buf, TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file);
+ MessageBoxW(m_hwnd, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION | MB_OK);
+ return 0;
+ }
+ // now the file should be gone!
+ }
+ // ask the database to create the profile
+ CreatePathToFileW(profile);
+ if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) {
+ mir_snwprintf(buf, TranslateT("Unable to create the profile '%s', the error was %x"), file, err);
+ MessageBoxW(m_hwnd, buf, TranslateT("Problem creating profile"), MB_ICONERROR | MB_OK);
+ return 0;
+ }
+
+ // the profile has been created!
+ g_bDbCreated = true;
+ return 1;
+ }
+
+ bool m_bFocused;
+ CCtrlCombo m_driverList;
+ CCtrlEdit m_profileName;
+ CCtrlBase m_warning;
+
+public:
+ CCreateProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) :
+ CDlgBase(g_plugin, IDD_PROFILE_NEW),
+ m_btnOk(_btn),
+ m_pd(_pd),
+ m_bFocused(false),
+ m_driverList(this, IDC_PROFILEDRIVERS),
+ m_profileName(this, IDC_PROFILENAME),
+ m_warning(this, IDC_NODBDRIVERS)
+ {}
+
+ bool OnInitDialog() override
+ {
+ // what, no plugins?!
+ if (arDbPlugins.getCount() == 0) {
+ m_driverList.Enable(false);
+ m_profileName.Enable(false);
+ ShowWindow(m_warning.GetHwnd(), TRUE);
+ }
+ else {
+ for (auto &p : arDbPlugins)
+ if (p->capabilities & MDB_CAPS_CREATE)
+ m_driverList.AddString(TranslateW(p->szFullName), (LPARAM)p);
+ }
+
+ // default item
+ m_driverList.SetCurSel(0);
+
+ // subclass the profile name box
+ mir_subclassWindow(m_profileName.GetHwnd(), ProfileNameValidate);
+
+ // decide if there is a default profile name given in the INI and if it should be used
+ if (m_pd->noProfiles || (shouldAutoCreate(m_pd->m_profile) && !m_pd->m_profile.isExist())) {
+ wchar_t *profile = wcsrchr(m_pd->m_profile.GetBuffer(), '\\');
+ if (profile) ++profile;
+ else profile = m_pd->m_profile.GetBuffer();
+
+ wchar_t *p = wcsrchr(profile, '.');
+ wchar_t c = 0;
+ if (p) { c = *p; *p = 0; }
+
+ m_profileName.SetText(profile);
+ if (c) *p = c;
+ }
+
+ // focus on the textbox
+ PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0);
+ return true;
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ switch (msg) {
+ case WM_FOCUSTEXTBOX:
+ SetFocus(m_profileName.GetHwnd());
+ break;
+
+ case WM_INPUTCHANGED: // when input in the edit box changes
+ NotifyChange();
+ m_btnOk.Enable(GetWindowTextLength(m_profileName.GetHwnd()) > 0);
+ break;
+
+ case WM_SHOWWINDOW:
+ if (wParam) {
+ m_btnOk.SetText(TranslateT("&Create"));
+ SendMessage(m_hwnd, WM_INPUTCHANGED, 0, 0);
+ m_bFocused = true;
+ }
+ else m_bFocused = false;
+ break;
+ }
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+ bool OnApply() override
+ {
+ LRESULT curSel = m_driverList.GetCurSel();
+ if (curSel == -1 || !m_bFocused)
+ return false; // should never happen
+
+ ptrW szName(m_profileName.GetText());
+ if (mir_wstrlen(szName) == 0)
+ return false;
+
+ // profile placed in "profile_name" subfolder
+ m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, szName.get(), szName.get());
+ m_pd->dblink = (DATABASELINK *)m_driverList.GetItemData(curSel);
+
+ if (CreateProfile(m_pd->m_profile, m_pd->dblink) == 0)
+ SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return true;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Profile selector
+
+static int numMessages[5];
+
+static void stubAddMessage(int iType, const wchar_t *, ...)
+{
+ if (iType < 5)
+ numMessages[iType]++;
+}
+
+class CChooseProfileDlg : public CDlgBase
+{
+ CCtrlButton &m_btnOk;
+ PROFILEMANAGERDATA *m_pd;
+ HANDLE m_hFileNotify;
+
+ void DeleteProfile(const LVITEM &item)
+ {
+ CMStringW wszMessage(FORMAT, TranslateT("Are you sure you want to remove profile \"%s\"?"), item.pszText);
+ if (IDYES != MessageBoxW(nullptr, wszMessage, L"Miranda NG", MB_YESNO | MB_TASKMODAL | MB_ICONWARNING))
+ return;
+
+ wszMessage.Format(L"%s\\%s", g_profileDir, item.pszText);
+ DeleteDirectoryTreeW(wszMessage, true);
+
+ m_profileList.DeleteItem(item.iItem);
+ }
+
+ void CheckProfile(const wchar_t *profile)
+ {
+ CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile);
+
+ if (TryLoadPlugin(plugin_checker, false))
+ CallService(MS_DB_CHECKPROFILE, (WPARAM)wszFullName.c_str(), 0);
+ else
+ Plugin_Uninit(plugin_checker);
+ }
+
+ void CompactProfile(DATABASELINK *dblink, const wchar_t *profile)
+ {
+ CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile);
+
+ if (auto *db = dblink->Load(wszFullName, false)) {
+ db->Compact();
+ delete db;
+
+ MessageBoxW(nullptr, TranslateT("Database was compacted successfully"), L"Miranda NG", MB_OK | MB_ICONINFORMATION);
+ }
+ }
+
+ void CheckRun()
+ {
+ m_btnOk.Enable(m_profileList.GetSelectedCount() == 1);
+
+ wchar_t profile[MAX_PATH];
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_IMAGE;
+ item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL);
+ item.pszText = profile;
+ item.cchTextMax = _countof(profile);
+ if (!m_profileList.GetItem(&item))
+ return;
+
+ switch(item.iImage) {
+ case 3:
+ m_btnOk.Enable(false);
+ return;
+
+ case 2:
+ m_btnOk.SetText(TranslateT("&Convert"));
+ break;
+
+ default:
+ m_btnOk.SetText(TranslateT("&Run"));
+ }
+
+ // profile is placed in "profile_name" subfolder
+
+ wchar_t tmpPath[MAX_PATH];
+ mir_snwprintf(tmpPath, L"%s\\%s.dat", g_profileDir, profile);
+ if (_waccess(tmpPath, 2))
+ m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, profile, profile);
+ else
+ m_pd->m_profile = tmpPath;
+ }
+
+ void ExecuteMenu(LPARAM lParam)
+ {
+ LVHITTESTINFO lvht = { 0 };
+ lvht.pt.x = GET_X_LPARAM(lParam);
+ lvht.pt.y = GET_Y_LPARAM(lParam);
+ ScreenToClient(m_profileList.GetHwnd(), &lvht.pt);
+
+ if (m_profileList.HitTest(&lvht) == -1)
+ return;
+
+ if (lvht.iItem == -1)
+ return;
+
+ wchar_t profile[MAX_PATH];
+ LVITEM item = { 0 };
+ item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
+ item.iItem = lvht.iItem;
+ item.pszText = profile;
+ item.cchTextMax = _countof(profile);
+ if (!m_profileList.GetItem(&item))
+ return;
+
+ lvht.pt.x = GET_X_LPARAM(lParam);
+ lvht.pt.y = GET_Y_LPARAM(lParam);
+
+ HMENU hMenu = CreatePopupMenu();
+ if (item.iImage < 2) {
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ }
+
+ DATABASELINK *dblink = (DATABASELINK*)item.lParam;
+ if (dblink != nullptr) {
+ bool bAdded = false;
+ if (dblink->capabilities & MDB_CAPS_COMPACT) {
+ AppendMenu(hMenu, MF_STRING, 3, TranslateT("Compact database"));
+ bAdded = true;
+ }
+
+ if (plugin_checker && (dblink->capabilities & MDB_CAPS_CHECK)) {
+ AppendMenu(hMenu, MF_STRING, 4, TranslateT("Check database"));
+ bAdded = true;
+ }
+
+ if (bAdded)
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ }
+
+ AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete"));
+ int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, m_hwnd, nullptr);
+ switch (index) {
+ case 1:
+ SendMessage(GetParent(m_hwndParent), WM_COMMAND, IDOK, 0);
+ break;
+
+ case 2:
+ DeleteProfile(item);
+ break;
+
+ case 3:
+ CompactProfile(dblink, profile);
+ break;
+
+ case 4:
+ CheckProfile(profile);
+ break;
+ }
+ DestroyMenu(hMenu);
+ }
+
+ CCtrlListView m_profileList;
+
+public:
+ CChooseProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) :
+ CDlgBase(g_plugin, IDD_PROFILE_SELECTION),
+ m_btnOk(_btn),
+ m_pd(_pd),
+ m_profileList(this, IDC_PROFILELIST)
+ {
+ m_profileList.OnItemChanged = Callback(this, &CChooseProfileDlg::list_OnItemChanged);
+ m_profileList.OnKeyDown = Callback(this, &CChooseProfileDlg::list_OnKeyDown);
+ m_profileList.OnGetInfoTip = Callback(this, &CChooseProfileDlg::list_OnGetTip);
+ m_profileList.OnDoubleClick = Callback(this, &CChooseProfileDlg::list_OnDblClick);
+ }
+
+ bool OnInitDialog() override
+ {
+ // set columns
+ LVCOLUMN col;
+ col.mask = LVCF_TEXT | LVCF_WIDTH;
+ col.pszText = TranslateT("Profile");
+ col.cx = 100;
+ m_profileList.InsertColumn(0, &col);
+
+ col.pszText = TranslateT("Driver");
+ col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL);
+ m_profileList.InsertColumn(1, &col);
+
+ col.pszText = TranslateT("Size");
+ col.cx = 60;
+ m_profileList.InsertColumn(2, &col);
+
+ // icons
+ HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1);
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL));
+
+ // LV will destroy the image list
+ m_profileList.SetImageList(hImgList, LVSIL_SMALL);
+ m_profileList.SetExtendedListViewStyle(m_profileList.GetExtendedListViewStyle() | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT);
+
+ // find all the profiles
+ findProfiles(m_profileList, m_pd->m_profile);
+ PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0);
+
+ m_hFileNotify = FindFirstChangeNotification(g_profileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE);
+ if (m_hFileNotify != INVALID_HANDLE_VALUE)
+ SetTimer(m_hwnd, 0, 1200, nullptr);
+ return true;
+ }
+
+ void OnDestroy()
+ {
+ KillTimer(m_hwnd, 0);
+ FindCloseChangeNotification(m_hFileNotify);
+ }
+
+ void list_OnItemChanged(CCtrlListView::TEventInfo*)
+ {
+ CheckRun();
+ }
+
+ void list_OnKeyDown(CCtrlListView::TEventInfo *evt)
+ {
+ if (evt->nmlvkey->wVKey == VK_DELETE) {
+ wchar_t profile[MAX_PATH];
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT;
+ item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL);
+ item.pszText = profile;
+ item.cchTextMax = _countof(profile);
+ if (m_profileList.GetItem(&item))
+ DeleteProfile(item);
+ }
+ }
+
+ void list_OnGetTip(CCtrlListView::TEventInfo *evt)
+ {
+ if (auto pTip = evt->nmlvit) {
+ wchar_t profilename[MAX_PATH], tszFullPath[MAX_PATH];
+ struct _stat statbuf;
+ m_profileList.GetItemText(pTip->iItem, 0, profilename, _countof(profilename));
+ mir_snwprintf(tszFullPath, L"%s\\%s\\%s.dat", g_profileDir, profilename, profilename);
+ _wstat(tszFullPath, &statbuf);
+ mir_snwprintf(pTip->pszText, pTip->cchTextMax, L"%s\n%s: %s\n%s: %s", tszFullPath, TranslateT("Created"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_mtime))));
+ }
+ }
+
+ void list_OnDblClick(CCtrlListView::TEventInfo*)
+ {
+ CheckRun();
+ EndDialog(GetParent(m_hwndParent), 1);
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ switch (msg) {
+ case WM_TIMER:
+ if (WaitForSingleObject(m_hFileNotify, 0) == WAIT_OBJECT_0) {
+ m_profileList.DeleteAllItems();
+ findProfiles(m_profileList, m_pd->m_profile);
+ FindNextChangeNotification(m_hFileNotify);
+ }
+ break;
+
+ case WM_FOCUSTEXTBOX:
+ SetFocus(m_profileList.GetHwnd());
+ if (m_pd->m_profile.IsEmpty() || m_profileList.GetSelectedCount() == 0)
+ m_profileList.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+ break;
+
+ case WM_SHOWWINDOW:
+ if (wParam)
+ CheckRun();
+ break;
+
+ case WM_CONTEXTMENU:
+ ExecuteMenu(lParam);
+ break;
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Tab manager + its envelope
+
+class CProfileManager : public CDlgBase
+{
+ PROFILEMANAGERDATA *m_pd;
+
+ CCtrlPages m_tab;
+ CCtrlButton m_btnOk;
+ CCtrlCheck m_chkSmEnabled;
+ CCtrlCombo m_servicePlugs;
+ CCtrlBase m_warning;
+
+public:
+ CProfileManager(PROFILEMANAGERDATA *_pd) :
+ CDlgBase(g_plugin, IDD_PROFILEMANAGER),
+ m_btnOk(this, IDOK),
+ m_pd(_pd),
+ m_tab(this, IDC_TABS),
+ m_warning(this, IDC_SM_LABEL),
+ m_servicePlugs(this, IDC_SM_COMBO),
+ m_chkSmEnabled(this, IDC_SM_ENABLED)
+ {
+ m_chkSmEnabled.OnChange = Callback(this, &CProfileManager::onChanged);
+
+ m_tab.AddPage(LPGENW("My profiles"), nullptr, new CChooseProfileDlg(m_btnOk, m_pd));
+ m_tab.AddPage(LPGENW("New profile"), nullptr, new CCreateProfileDlg(m_btnOk, m_pd));
+ }
+
+ bool OnInitDialog() override
+ {
+ // MUST NOT be replaced with Window_SetIcon_IcoLib!!!
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconSX, g_iIconSY, 0));
+ SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconX, g_iIconY, 0));
+
+ if (m_pd->noProfiles || shouldAutoCreate(m_pd->m_profile))
+ m_tab.ActivatePage(1);
+
+ // service mode combobox
+ if (servicePlugins.getCount() == 0) {
+ ShowWindow(m_warning.GetHwnd(), FALSE);
+ ShowWindow(m_chkSmEnabled.GetHwnd(), FALSE);
+ ShowWindow(m_servicePlugs.GetHwnd(), FALSE);
+ }
+ else {
+ for (int i = 0; i < servicePlugins.getCount(); i++) {
+ pluginEntry *p = servicePlugins[i];
+ m_servicePlugs.AddStringA(p->pluginname, i);
+ }
+
+ m_servicePlugs.Disable();
+ m_servicePlugs.SetCurSel(0);
+ }
+ return true;
+ }
+
+ void OnDestroy()
+ {
+ if (m_chkSmEnabled.GetState()) {
+ int idx = m_servicePlugs.GetCurData();
+ if (idx != -1)
+ plugin_service = servicePlugins[idx];
+ }
+
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0));
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0));
+ }
+
+ void onChanged(CCtrlCheck*)
+ {
+ m_servicePlugs.Enable(m_chkSmEnabled.GetState());
+ }
+};
+
+int getProfileManager(PROFILEMANAGERDATA *pd)
+{
+ return CProfileManager(pd).DoModal();
+}
diff --git a/src/mir_app/src/profilemanager.h b/src/mir_app/src/profilemanager.h index 39685e82bc..ab7e9e8a14 100644 --- a/src/mir_app/src/profilemanager.h +++ b/src/mir_app/src/profilemanager.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_accs.cpp b/src/mir_app/src/proto_accs.cpp index 8947568395..501497361c 100644 --- a/src/mir_app/src/proto_accs.cpp +++ b/src/mir_app/src/proto_accs.cpp @@ -1,435 +1,435 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "clc.h" - -bool CheckProtocolOrder(void); -void BuildProtoMenus(); - -HICON Proto_GetIcon(PROTO_INTERFACE *ppro, int iconIndex); - -static bool bModuleInitialized = false; -static HANDLE hHooks[4]; - -static int CompareAccounts(const PROTOACCOUNT* p1, const PROTOACCOUNT* p2) -{ - return mir_strcmp(p1->szModuleName, p2->szModuleName); -} - -LIST<PROTOACCOUNT> g_arAccounts(10, CompareAccounts); - -///////////////////////////////////////////////////////////////////////////////////////// - -static int EnumDbModules(const char *szModuleName, void*) -{ - ptrA szProtoName(db_get_sa(0, szModuleName, "AM_BaseProto")); - if (szProtoName) { - if (!Proto_GetAccount(szModuleName)) { - PROTOACCOUNT *pa = new PROTOACCOUNT(szModuleName); - pa->szProtoName = szProtoName.detach(); - pa->tszAccountName = mir_a2u(szModuleName); - pa->bIsVisible = true; - pa->bIsEnabled = false; - pa->iOrder = g_arAccounts.getCount(); - g_arAccounts.insert(pa); - } - } - return 0; -} - -void LoadDbAccounts(void) -{ - int ver = db_get_dw(0, "Protocols", "PrVer", -1); - int count = db_get_dw(0, "Protocols", "ProtoCount", 0); - - for (int i = 0; i < count; i++) { - char buf[10]; - _itoa(i, buf, 10); - ptrA szModuleName(db_get_sa(0, "Protocols", buf)); - if (szModuleName == nullptr) - continue; - - PROTOACCOUNT *pa = Proto_GetAccount(szModuleName); - if (pa == nullptr) { - pa = new PROTOACCOUNT(szModuleName); - g_arAccounts.insert(pa); - } - - _itoa(OFFSET_VISIBLE + i, buf, 10); - pa->bIsVisible = db_get_dw(0, "Protocols", buf, 1) != 0; - - _itoa(OFFSET_PROTOPOS + i, buf, 10); - pa->iOrder = db_get_dw(0, "Protocols", buf, 1); - - if (ver >= 4) { - _itoa(OFFSET_NAME + i, buf, 10); - pa->tszAccountName = db_get_wsa(0, "Protocols", buf); - - _itoa(OFFSET_ENABLED + i, buf, 10); - pa->bIsEnabled = db_get_dw(0, "Protocols", buf, 1) != 0; - if (!pa->bIsEnabled && !mir_strcmp(pa->szModuleName, META_PROTO)) { - pa->bIsEnabled = true; - db_set_dw(0, "Protocols", buf, 1); - } - pa->szProtoName = db_get_sa(0, szModuleName, "AM_BaseProto"); - } - else pa->bIsEnabled = true; - - if (!pa->szProtoName) { - pa->szProtoName = mir_strdup(szModuleName); - db_set_s(0, szModuleName, "AM_BaseProto", pa->szProtoName); - } - - if (!pa->tszAccountName) - pa->tszAccountName = mir_a2u(szModuleName); - } - - if (CheckProtocolOrder()) - WriteDbAccounts(); - - int anum = g_arAccounts.getCount(); - db_enum_modules(EnumDbModules); - if (anum != g_arAccounts.getCount()) - WriteDbAccounts(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WriteDbAccounts() -{ - // enum all old settings to delete - db_delete_module(0, "Protocols"); - - // write new data - for (int i = 0; i < g_arAccounts.getCount(); i++) { - PROTOACCOUNT *pa = g_arAccounts[i]; - - char buf[20]; - _itoa(i, buf, 10); - db_set_s(0, "Protocols", buf, pa->szModuleName); - - _itoa(OFFSET_PROTOPOS + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->iOrder); - - _itoa(OFFSET_VISIBLE + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->bIsVisible); - - _itoa(OFFSET_ENABLED + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->bIsEnabled); - - _itoa(OFFSET_NAME + i, buf, 10); - db_set_ws(0, "Protocols", buf, pa->tszAccountName); - } - - db_set_dw(0, "Protocols", "ProtoCount", g_arAccounts.getCount()); - db_set_dw(0, "Protocols", "PrVer", 4); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int OnContactDeleted(WPARAM hContact, LPARAM) -{ - if (auto *ppro = Proto_GetInstance(hContact)) - ppro->OnContactDeleted(hContact); - return 0; -} - -static int OnEventEdited(WPARAM hContact, LPARAM hDbEvent) -{ - if (auto *ppro = Proto_GetInstance(hContact)) - ppro->OnEventEdited(hContact, hDbEvent); - return 0; -} - -void InitStaticAccounts() -{ - int count = 0; - - for (auto &pa : g_arAccounts) { - if (!pa->ppro || !pa->IsEnabled()) - continue; - - pa->ppro->OnModulesLoaded(); - - if (!pa->bOldProto) - count++; - - if (pa->IsVisible()) - pa->ppro->OnBuildProtoMenu(); - } - - if (count == 0 && !db_get_b(0, "FirstRun", "AccManager", 0)) { - db_set_b(0, "FirstRun", "AccManager", 1); - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - } - // This is for pack creators with a profile with predefined g_arAccounts - else if (db_get_b(0, "FirstRun", "ForceShowAccManager", 0)) { - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - db_unset(0, "FirstRun", "ForceShowAccManager"); - } -} - -static int UninitializeStaticAccounts(WPARAM, LPARAM) -{ - // request permission to exit first - for (auto &pa : g_arAccounts) - if (pa->ppro && pa->IsEnabled()) - if (!pa->ppro->IsReadyToExit()) - return 1; - - // okay, all protocols are ready, exiting - for (auto &pa : g_arAccounts) - if (pa->ppro && pa->IsEnabled()) - pa->ppro->OnShutdown(); - - return 0; -} - -int LoadAccountsModule(void) -{ - bModuleInitialized = true; - - for (auto &pa : g_arAccounts) { - pa->bDynDisabled = !Proto_IsProtocolLoaded(pa->szProtoName); - if (pa->ppro) - continue; - - if (!pa->IsEnabled()) - continue; - - if (!ActivateAccount(pa, false)) - pa->bDynDisabled = true; - } - - hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts); - hHooks[2] = HookEvent(ME_DB_CONTACT_DELETED, OnContactDeleted); - hHooks[3] = HookEvent(ME_DB_EVENT_EDITED, OnEventEdited); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HANDLE CreateProtoServiceEx(const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param) -{ - char tmp[100]; - mir_snprintf(tmp, "%s%s", szModule, szService); - return CreateServiceFunctionObj(tmp, pFunc, param); -} - -bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic) -{ - MBaseProto *ppd = Proto_GetProto(pa->szProtoName); - if (ppd == nullptr) - return false; - - if (ppd->fnInit == nullptr) - return false; - - PROTO_INTERFACE *ppi = pa->ppro; - if (ppi == nullptr) { - ppi = ppd->fnInit(pa->szModuleName, pa->tszAccountName); - if (ppi == nullptr) - return false; - - pa->ppro = ppi; - - if (bIsDynamic) { - if (g_bModulesLoadedFired) - pa->ppro->OnModulesLoaded(); - if (!db_get_b(0, "CList", "MoveProtoMenus", true)) - pa->ppro->OnBuildProtoMenu(); - pa->bDynDisabled = false; - } - } - - if (ppi->m_hProtoIcon == nullptr) - ppi->m_hProtoIcon = IcoLib_IsManaged(Skin_LoadProtoIcon(pa->szModuleName, ID_STATUS_ONLINE)); - ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE; - return true; -} - -MIR_APP_DLL(int) Proto_GetAverageStatus(int *pAccountNumber) -{ - int netProtoCount = 0, averageMode = 0; - - for (auto &pa : g_arAccounts) { - if (!pa->IsVisible() || pa->IsLocked()) - continue; - - netProtoCount++; - if (averageMode == 0) - averageMode = pa->iRealStatus; - else if (averageMode > 0 && averageMode != pa->iRealStatus) { - averageMode = -1; - if (pAccountNumber == nullptr) - break; - } - } - - if (pAccountNumber) - *pAccountNumber = netProtoCount; - return averageMode; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct DeactivationThreadParam -{ - PROTO_INTERFACE *ppro; - pfnUninitProto fnUninit; - int flags; -}; - -pfnUninitProto GetProtocolDestructor(char *szProto); - -static void __cdecl DeactivationThread(DeactivationThreadParam *param) -{ - PROTO_INTERFACE *p = (PROTO_INTERFACE*)param->ppro; - p->SetStatus(ID_STATUS_OFFLINE); - - char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName); - - if (param->flags & DAF_DYNAMIC) { - while (!p->IsReadyToExit()) - SleepEx(100, TRUE); - - p->OnShutdown(); - } - - KillObjectThreads(p); // waits for them before terminating - KillObjectEventHooks(p); // untie an object from the outside world - - if (param->flags & DAF_ERASE) - p->OnErase(); - - if (param->fnUninit) - param->fnUninit(p); - - KillObjectServices(p); - - if (param->flags & DAF_ERASE) - EraseAccount(szModuleName); - - delete param; -} - -void DeactivateAccount(PROTOACCOUNT *pa, int flags) -{ - if (pa->hwndAccMgrUI) { - DestroyWindow(pa->hwndAccMgrUI); - pa->hwndAccMgrUI = nullptr; - pa->bAccMgrUIChanged = FALSE; - } - - if (flags & DAF_DYNAMIC) - NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa); - else - pa->iIconBase = -1; - - if (pa->ppro == nullptr) { - if (flags & DAF_ERASE) - EraseAccount(pa->szModuleName); - return; - } - - DeactivationThreadParam *param = new DeactivationThreadParam; - param->ppro = pa->ppro; - param->fnUninit = GetProtocolDestructor(pa->szProtoName); - param->flags = flags; - pa->ppro = nullptr; - if (flags & DAF_FORK) - mir_forkThread<DeactivationThreadParam>(DeactivationThread, param); - else - DeactivationThread(param); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void KillModuleAccounts(HINSTANCE hInst) -{ - for (auto &pd : g_arProtos.rev_iter()) { - if (pd->hInst != hInst) - continue; - - for (auto &pa : g_arAccounts.rev_iter()) { - if (!mir_strcmp(pa->szProtoName, pd->szName)) { - pa->bDynDisabled = true; - DeactivateAccount(pa, DAF_DYNAMIC); - } - } - - g_arProtos.removeItem(&pd); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void EraseAccount(const char *pszModuleName) -{ - // remove protocol contacts first - for (MCONTACT hContact = db_find_first(pszModuleName); hContact != 0;) { - MCONTACT hNext = db_find_next(hContact, pszModuleName); - db_delete_contact(hContact); - hContact = hNext; - } - - // remove all protocol settings - db_delete_module(0, pszModuleName); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void UnloadAccount(PROTOACCOUNT *pa, int flags) -{ - DeactivateAccount(pa, flags); - - // szModuleName should be freed only on a program's exit. - // otherwise many plugins dependand on static protocol names will crash! - // do NOT fix this 'leak', please - if (!(flags & DAF_DYNAMIC)) - delete pa; - else { - replaceStrW(pa->tszAccountName, 0); - replaceStr(pa->szProtoName, 0); - replaceStr(pa->szUniqueId, 0); - } -} - -void UnloadAccountsModule() -{ - if (!bModuleInitialized) - return; - - auto T = g_arAccounts.rev_iter(); - for (auto &it : T) { - UnloadAccount(it, 0); - g_arAccounts.removeItem(&it); - } - g_arAccounts.destroy(); - - for (auto &it : hHooks) - UnhookEvent(it); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "clc.h"
+
+bool CheckProtocolOrder(void);
+void BuildProtoMenus();
+
+HICON Proto_GetIcon(PROTO_INTERFACE *ppro, int iconIndex);
+
+static bool bModuleInitialized = false;
+static HANDLE hHooks[4];
+
+static int CompareAccounts(const PROTOACCOUNT* p1, const PROTOACCOUNT* p2)
+{
+ return mir_strcmp(p1->szModuleName, p2->szModuleName);
+}
+
+LIST<PROTOACCOUNT> g_arAccounts(10, CompareAccounts);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int EnumDbModules(const char *szModuleName, void*)
+{
+ ptrA szProtoName(db_get_sa(0, szModuleName, "AM_BaseProto"));
+ if (szProtoName) {
+ if (!Proto_GetAccount(szModuleName)) {
+ PROTOACCOUNT *pa = new PROTOACCOUNT(szModuleName);
+ pa->szProtoName = szProtoName.detach();
+ pa->tszAccountName = mir_a2u(szModuleName);
+ pa->bIsVisible = true;
+ pa->bIsEnabled = false;
+ pa->iOrder = g_arAccounts.getCount();
+ g_arAccounts.insert(pa);
+ }
+ }
+ return 0;
+}
+
+void LoadDbAccounts(void)
+{
+ int ver = db_get_dw(0, "Protocols", "PrVer", -1);
+ int count = db_get_dw(0, "Protocols", "ProtoCount", 0);
+
+ for (int i = 0; i < count; i++) {
+ char buf[10];
+ _itoa(i, buf, 10);
+ ptrA szModuleName(db_get_sa(0, "Protocols", buf));
+ if (szModuleName == nullptr)
+ continue;
+
+ PROTOACCOUNT *pa = Proto_GetAccount(szModuleName);
+ if (pa == nullptr) {
+ pa = new PROTOACCOUNT(szModuleName);
+ g_arAccounts.insert(pa);
+ }
+
+ _itoa(OFFSET_VISIBLE + i, buf, 10);
+ pa->bIsVisible = db_get_dw(0, "Protocols", buf, 1) != 0;
+
+ _itoa(OFFSET_PROTOPOS + i, buf, 10);
+ pa->iOrder = db_get_dw(0, "Protocols", buf, 1);
+
+ if (ver >= 4) {
+ _itoa(OFFSET_NAME + i, buf, 10);
+ pa->tszAccountName = db_get_wsa(0, "Protocols", buf);
+
+ _itoa(OFFSET_ENABLED + i, buf, 10);
+ pa->bIsEnabled = db_get_dw(0, "Protocols", buf, 1) != 0;
+ if (!pa->bIsEnabled && !mir_strcmp(pa->szModuleName, META_PROTO)) {
+ pa->bIsEnabled = true;
+ db_set_dw(0, "Protocols", buf, 1);
+ }
+ pa->szProtoName = db_get_sa(0, szModuleName, "AM_BaseProto");
+ }
+ else pa->bIsEnabled = true;
+
+ if (!pa->szProtoName) {
+ pa->szProtoName = mir_strdup(szModuleName);
+ db_set_s(0, szModuleName, "AM_BaseProto", pa->szProtoName);
+ }
+
+ if (!pa->tszAccountName)
+ pa->tszAccountName = mir_a2u(szModuleName);
+ }
+
+ if (CheckProtocolOrder())
+ WriteDbAccounts();
+
+ int anum = g_arAccounts.getCount();
+ db_enum_modules(EnumDbModules);
+ if (anum != g_arAccounts.getCount())
+ WriteDbAccounts();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WriteDbAccounts()
+{
+ // enum all old settings to delete
+ db_delete_module(0, "Protocols");
+
+ // write new data
+ for (int i = 0; i < g_arAccounts.getCount(); i++) {
+ PROTOACCOUNT *pa = g_arAccounts[i];
+
+ char buf[20];
+ _itoa(i, buf, 10);
+ db_set_s(0, "Protocols", buf, pa->szModuleName);
+
+ _itoa(OFFSET_PROTOPOS + i, buf, 10);
+ db_set_dw(0, "Protocols", buf, pa->iOrder);
+
+ _itoa(OFFSET_VISIBLE + i, buf, 10);
+ db_set_dw(0, "Protocols", buf, pa->bIsVisible);
+
+ _itoa(OFFSET_ENABLED + i, buf, 10);
+ db_set_dw(0, "Protocols", buf, pa->bIsEnabled);
+
+ _itoa(OFFSET_NAME + i, buf, 10);
+ db_set_ws(0, "Protocols", buf, pa->tszAccountName);
+ }
+
+ db_set_dw(0, "Protocols", "ProtoCount", g_arAccounts.getCount());
+ db_set_dw(0, "Protocols", "PrVer", 4);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int OnContactDeleted(WPARAM hContact, LPARAM)
+{
+ if (auto *ppro = Proto_GetInstance(hContact))
+ ppro->OnContactDeleted(hContact);
+ return 0;
+}
+
+static int OnEventEdited(WPARAM hContact, LPARAM hDbEvent)
+{
+ if (auto *ppro = Proto_GetInstance(hContact))
+ ppro->OnEventEdited(hContact, hDbEvent);
+ return 0;
+}
+
+void InitStaticAccounts()
+{
+ int count = 0;
+
+ for (auto &pa : g_arAccounts) {
+ if (!pa->ppro || !pa->IsEnabled())
+ continue;
+
+ pa->ppro->OnModulesLoaded();
+
+ if (!pa->bOldProto)
+ count++;
+
+ if (pa->IsVisible())
+ pa->ppro->OnBuildProtoMenu();
+ }
+
+ if (count == 0 && !db_get_b(0, "FirstRun", "AccManager", 0)) {
+ db_set_b(0, "FirstRun", "AccManager", 1);
+ CallService(MS_PROTO_SHOWACCMGR, 0, 0);
+ }
+ // This is for pack creators with a profile with predefined g_arAccounts
+ else if (db_get_b(0, "FirstRun", "ForceShowAccManager", 0)) {
+ CallService(MS_PROTO_SHOWACCMGR, 0, 0);
+ db_unset(0, "FirstRun", "ForceShowAccManager");
+ }
+}
+
+static int UninitializeStaticAccounts(WPARAM, LPARAM)
+{
+ // request permission to exit first
+ for (auto &pa : g_arAccounts)
+ if (pa->ppro && pa->IsEnabled())
+ if (!pa->ppro->IsReadyToExit())
+ return 1;
+
+ // okay, all protocols are ready, exiting
+ for (auto &pa : g_arAccounts)
+ if (pa->ppro && pa->IsEnabled())
+ pa->ppro->OnShutdown();
+
+ return 0;
+}
+
+int LoadAccountsModule(void)
+{
+ bModuleInitialized = true;
+
+ for (auto &pa : g_arAccounts) {
+ pa->bDynDisabled = !Proto_IsProtocolLoaded(pa->szProtoName);
+ if (pa->ppro)
+ continue;
+
+ if (!pa->IsEnabled())
+ continue;
+
+ if (!ActivateAccount(pa, false))
+ pa->bDynDisabled = true;
+ }
+
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts);
+ hHooks[2] = HookEvent(ME_DB_CONTACT_DELETED, OnContactDeleted);
+ hHooks[3] = HookEvent(ME_DB_EVENT_EDITED, OnEventEdited);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HANDLE CreateProtoServiceEx(const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param)
+{
+ char tmp[100];
+ mir_snprintf(tmp, "%s%s", szModule, szService);
+ return CreateServiceFunctionObj(tmp, pFunc, param);
+}
+
+bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic)
+{
+ MBaseProto *ppd = Proto_GetProto(pa->szProtoName);
+ if (ppd == nullptr)
+ return false;
+
+ if (ppd->fnInit == nullptr)
+ return false;
+
+ PROTO_INTERFACE *ppi = pa->ppro;
+ if (ppi == nullptr) {
+ ppi = ppd->fnInit(pa->szModuleName, pa->tszAccountName);
+ if (ppi == nullptr)
+ return false;
+
+ pa->ppro = ppi;
+
+ if (bIsDynamic) {
+ if (g_bModulesLoadedFired)
+ pa->ppro->OnModulesLoaded();
+ if (!db_get_b(0, "CList", "MoveProtoMenus", true))
+ pa->ppro->OnBuildProtoMenu();
+ pa->bDynDisabled = false;
+ }
+ }
+
+ if (ppi->m_hProtoIcon == nullptr)
+ ppi->m_hProtoIcon = IcoLib_IsManaged(Skin_LoadProtoIcon(pa->szModuleName, ID_STATUS_ONLINE));
+ ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE;
+ return true;
+}
+
+MIR_APP_DLL(int) Proto_GetAverageStatus(int *pAccountNumber)
+{
+ int netProtoCount = 0, averageMode = 0;
+
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsVisible() || pa->IsLocked())
+ continue;
+
+ netProtoCount++;
+ if (averageMode == 0)
+ averageMode = pa->iRealStatus;
+ else if (averageMode > 0 && averageMode != pa->iRealStatus) {
+ averageMode = -1;
+ if (pAccountNumber == nullptr)
+ break;
+ }
+ }
+
+ if (pAccountNumber)
+ *pAccountNumber = netProtoCount;
+ return averageMode;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct DeactivationThreadParam
+{
+ PROTO_INTERFACE *ppro;
+ pfnUninitProto fnUninit;
+ int flags;
+};
+
+pfnUninitProto GetProtocolDestructor(char *szProto);
+
+static void __cdecl DeactivationThread(DeactivationThreadParam *param)
+{
+ PROTO_INTERFACE *p = (PROTO_INTERFACE*)param->ppro;
+ p->SetStatus(ID_STATUS_OFFLINE);
+
+ char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName);
+
+ if (param->flags & DAF_DYNAMIC) {
+ while (!p->IsReadyToExit())
+ SleepEx(100, TRUE);
+
+ p->OnShutdown();
+ }
+
+ KillObjectThreads(p); // waits for them before terminating
+ KillObjectEventHooks(p); // untie an object from the outside world
+
+ if (param->flags & DAF_ERASE)
+ p->OnErase();
+
+ if (param->fnUninit)
+ param->fnUninit(p);
+
+ KillObjectServices(p);
+
+ if (param->flags & DAF_ERASE)
+ EraseAccount(szModuleName);
+
+ delete param;
+}
+
+void DeactivateAccount(PROTOACCOUNT *pa, int flags)
+{
+ if (pa->hwndAccMgrUI) {
+ DestroyWindow(pa->hwndAccMgrUI);
+ pa->hwndAccMgrUI = nullptr;
+ pa->bAccMgrUIChanged = FALSE;
+ }
+
+ if (flags & DAF_DYNAMIC)
+ NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa);
+ else
+ pa->iIconBase = -1;
+
+ if (pa->ppro == nullptr) {
+ if (flags & DAF_ERASE)
+ EraseAccount(pa->szModuleName);
+ return;
+ }
+
+ DeactivationThreadParam *param = new DeactivationThreadParam;
+ param->ppro = pa->ppro;
+ param->fnUninit = GetProtocolDestructor(pa->szProtoName);
+ param->flags = flags;
+ pa->ppro = nullptr;
+ if (flags & DAF_FORK)
+ mir_forkThread<DeactivationThreadParam>(DeactivationThread, param);
+ else
+ DeactivationThread(param);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void KillModuleAccounts(HINSTANCE hInst)
+{
+ for (auto &pd : g_arProtos.rev_iter()) {
+ if (pd->hInst != hInst)
+ continue;
+
+ for (auto &pa : g_arAccounts.rev_iter()) {
+ if (!mir_strcmp(pa->szProtoName, pd->szName)) {
+ pa->bDynDisabled = true;
+ DeactivateAccount(pa, DAF_DYNAMIC);
+ }
+ }
+
+ g_arProtos.removeItem(&pd);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void EraseAccount(const char *pszModuleName)
+{
+ // remove protocol contacts first
+ for (MCONTACT hContact = db_find_first(pszModuleName); hContact != 0;) {
+ MCONTACT hNext = db_find_next(hContact, pszModuleName);
+ db_delete_contact(hContact);
+ hContact = hNext;
+ }
+
+ // remove all protocol settings
+ db_delete_module(0, pszModuleName);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void UnloadAccount(PROTOACCOUNT *pa, int flags)
+{
+ DeactivateAccount(pa, flags);
+
+ // szModuleName should be freed only on a program's exit.
+ // otherwise many plugins dependand on static protocol names will crash!
+ // do NOT fix this 'leak', please
+ if (!(flags & DAF_DYNAMIC))
+ delete pa;
+ else {
+ replaceStrW(pa->tszAccountName, 0);
+ replaceStr(pa->szProtoName, 0);
+ replaceStr(pa->szUniqueId, 0);
+ }
+}
+
+void UnloadAccountsModule()
+{
+ if (!bModuleInitialized)
+ return;
+
+ auto T = g_arAccounts.rev_iter();
+ for (auto &it : T) {
+ UnloadAccount(it, 0);
+ g_arAccounts.removeItem(&it);
+ }
+ g_arAccounts.destroy();
+
+ for (auto &it : hHooks)
+ UnhookEvent(it);
+}
diff --git a/src/mir_app/src/proto_chains.cpp b/src/mir_app/src/proto_chains.cpp index 0764a01164..5417c07995 100644 --- a/src/mir_app/src/proto_chains.cpp +++ b/src/mir_app/src/proto_chains.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_interface.cpp b/src/mir_app/src/proto_interface.cpp index ed500cf69e..4ccab724de 100644 --- a/src/mir_app/src/proto_interface.cpp +++ b/src/mir_app/src/proto_interface.cpp @@ -1,351 +1,351 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static HGENMENU hReqAuth = nullptr, hGrantAuth = nullptr, hRevokeAuth = nullptr, hServerHist = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol constructor & destructor - -PROTO_INTERFACE::PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName) -{ - m_iVersion = 2; - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - m_szModuleName = mir_strdup(pszModuleName); - m_tszUserName = mir_wstrdup(ptszUserName); - db_set_resident(m_szModuleName, "Status"); -} - -PROTO_INTERFACE::~PROTO_INTERFACE() -{ - if (m_hNetlibUser) - Netlib_CloseHandle(m_hNetlibUser); - - mir_free(m_szModuleName); - mir_free(m_tszUserName); - - WindowList_Destroy(m_hWindowList); -} - -HGENMENU PROTO_INTERFACE::GetMenuItem(ProtoMenuItemType aType) -{ - switch (aType) { - case PROTO_MENU_REQ_AUTH: return hReqAuth; - case PROTO_MENU_GRANT_AUTH: return hGrantAuth; - case PROTO_MENU_REVOKE_AUTH: return hRevokeAuth; - case PROTO_MENU_LOAD_HISTORY: return hServerHist; - } - - return nullptr; -} - -void PROTO_INTERFACE::OnBuildProtoMenu() -{} - -void PROTO_INTERFACE::OnContactAdded(MCONTACT) -{} - -void PROTO_INTERFACE::OnContactDeleted(MCONTACT) -{} - -void PROTO_INTERFACE::OnEventEdited(MCONTACT, MEVENT) -{} - -void PROTO_INTERFACE::OnErase() -{} - -void PROTO_INTERFACE::OnModulesLoaded() -{} - -bool PROTO_INTERFACE::IsReadyToExit() -{ - return true; -} - -void PROTO_INTERFACE::OnShutdown() -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// default PROTO_INTERFACE method implementations - -MCONTACT PROTO_INTERFACE::AddToList(int, PROTOSEARCHRESULT*) -{ - return 0; // error -} - -MCONTACT PROTO_INTERFACE::AddToListByEvent(int, int, MEVENT) -{ - return 0; // error -} - -int PROTO_INTERFACE::Authorize(MEVENT) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthDeny(MEVENT, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthRecv(MCONTACT, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthRequest(MCONTACT, const wchar_t*) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::FileAllow(MCONTACT, HANDLE, const wchar_t*) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::FileCancel(MCONTACT, HANDLE) -{ - return 1; // error -} - -int PROTO_INTERFACE::FileDeny(MCONTACT, HANDLE, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::FileResume(HANDLE, int, const wchar_t*) -{ - return 1; // error -} - -INT_PTR PROTO_INTERFACE::GetCaps(int, MCONTACT) -{ - return 0; // empty value -} - -int PROTO_INTERFACE::GetInfo(MCONTACT, int) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::SearchBasic(const wchar_t*) -{ - return nullptr; // error -} - -HANDLE PROTO_INTERFACE::SearchByEmail(const wchar_t*) -{ - return nullptr; // error -} - -HANDLE PROTO_INTERFACE::SearchByName(const wchar_t*, const wchar_t*, const wchar_t*) -{ - return nullptr; // error -} - -HWND PROTO_INTERFACE::SearchAdvanced(HWND) -{ - return nullptr; // error -} - -HWND PROTO_INTERFACE::CreateExtendedSearchUI(HWND) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::RecvContacts(MCONTACT, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::RecvFile(MCONTACT hContact, PROTORECVFILE *pcre) -{ - CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)pcre }; - return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs); -} - -MEVENT PROTO_INTERFACE::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre) -{ - if (pre->szMessage == nullptr) - return 0; - - ptrA pszTemp; - mir_ptr<uint8_t> pszBlob; - - DBEVENTINFO dbei = {}; - dbei.flags = DBEF_UTF; - dbei.szModule = Proto_GetBaseAccountName(hContact); - dbei.timestamp = pre->timestamp; - dbei.eventType = EVENTTYPE_MESSAGE; - dbei.cbBlob = (uint32_t)mir_strlen(pre->szMessage) + 1; - dbei.pBlob = (uint8_t*)pre->szMessage; - - if (pre->flags & PREF_CREATEREAD) - dbei.flags |= DBEF_READ; - if (pre->flags & PREF_SENT) - dbei.flags |= DBEF_SENT; - - // if it's possible to find an existing event by its id, do that - if ((GetCaps(PFLAGNUM_4) & PF4_SERVERMSGID) && pre->szMsgId != nullptr) { - MEVENT hDbEvent = db_event_getById(m_szModuleName, pre->szMsgId); - if (hDbEvent == 0 || db_event_edit(hContact, hDbEvent, &dbei)) { - dbei.szId = pre->szMsgId; - hDbEvent = db_event_add(hContact, &dbei); - } - return hDbEvent; - } - - // event is new? add it - return (INT_PTR)db_event_add(hContact, &dbei); -} - -int PROTO_INTERFACE::SendContacts(MCONTACT, int, int, MCONTACT*) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::SendFile(MCONTACT, const wchar_t*, wchar_t**) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::SendMsg(MCONTACT, int, const char*) -{ - return 0; // error -} - -int PROTO_INTERFACE::SetApparentMode(MCONTACT, int) -{ - return 1; // error -} - -int PROTO_INTERFACE::SetStatus(int) -{ - return 1; // you better declare it -} - -HANDLE PROTO_INTERFACE::GetAwayMsg(MCONTACT) -{ - return nullptr; // no away message -} - -int PROTO_INTERFACE::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::SetAwayMsg(int, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::UserIsTyping(MCONTACT, int) -{ - return 1; // error -} - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol menus - -static INT_PTR __cdecl stubRequestAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_REQAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubGrantAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_GRANTAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubRevokeAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_REVOKEAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubLoadHistory(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_LOADHISTORY, hContact, 0); - return 0; -} - -static int __cdecl ProtoPrebuildContactMenu(WPARAM hContact, LPARAM) -{ - Menu_ShowItem(hReqAuth, false); - Menu_ShowItem(hGrantAuth, false); - Menu_ShowItem(hRevokeAuth, false); - - const char *szProto = Proto_GetBaseAccountName(hContact); - Menu_ShowItem(hServerHist, ProtoServiceExists(szProto, PS_MENU_LOADHISTORY)); - return 0; -} - -void InitProtoMenus(void) -{ - // "Request authorization" - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x36375a1f, 0xc142, 0x4d6e, 0xa6, 0x57, 0xe4, 0x76, 0x5d, 0xbc, 0x59, 0x8e); - mi.pszService = "Proto/Menu/ReqAuth"; - mi.name.a = LPGEN("Request authorization"); - mi.position = -2000001002; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST); - hReqAuth = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, stubRequestAuth); - - // "Grant authorization" - SET_UID(mi, 0x4c90452a, 0x869a, 0x4a81, 0xaf, 0xa8, 0x28, 0x34, 0xaf, 0x2b, 0x6b, 0x30); - mi.pszService = "Proto/Menu/GrantAuth"; - mi.name.a = LPGEN("Grant authorization"); - mi.position = -2000001001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_GRANT); - hGrantAuth = Menu_AddContactMenuItem(&mi); - - // "Revoke authorization" - SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77); - mi.pszService = "Proto/Menu/RevokeAuth"; - mi.name.a = LPGEN("Revoke authorization"); - mi.position = -2000001000; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE); - hRevokeAuth = Menu_AddContactMenuItem(&mi); - - SET_UID(mi, 0xd15b841d, 0xb0fc, 0x4ab5, 0x96, 0x94, 0xcf, 0x6c, 0x6e, 0x99, 0x4b, 0x3c); // {D15B841D-B0FC-4AB5-9694-CF6C6E994B3C} - mi.pszService = "Proto/Menu/LoadHistory"; - mi.name.a = LPGEN("Load server history"); - mi.position = -200001004; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HISTORY); - hServerHist = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, stubLoadHistory); - - HookEvent(ME_CLIST_PREBUILDCONTACTMENU, ProtoPrebuildContactMenu); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static HGENMENU hReqAuth = nullptr, hGrantAuth = nullptr, hRevokeAuth = nullptr, hServerHist = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// protocol constructor & destructor
+
+PROTO_INTERFACE::PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName)
+{
+ m_iVersion = 2;
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+ m_szModuleName = mir_strdup(pszModuleName);
+ m_tszUserName = mir_wstrdup(ptszUserName);
+ db_set_resident(m_szModuleName, "Status");
+}
+
+PROTO_INTERFACE::~PROTO_INTERFACE()
+{
+ if (m_hNetlibUser)
+ Netlib_CloseHandle(m_hNetlibUser);
+
+ mir_free(m_szModuleName);
+ mir_free(m_tszUserName);
+
+ WindowList_Destroy(m_hWindowList);
+}
+
+HGENMENU PROTO_INTERFACE::GetMenuItem(ProtoMenuItemType aType)
+{
+ switch (aType) {
+ case PROTO_MENU_REQ_AUTH: return hReqAuth;
+ case PROTO_MENU_GRANT_AUTH: return hGrantAuth;
+ case PROTO_MENU_REVOKE_AUTH: return hRevokeAuth;
+ case PROTO_MENU_LOAD_HISTORY: return hServerHist;
+ }
+
+ return nullptr;
+}
+
+void PROTO_INTERFACE::OnBuildProtoMenu()
+{}
+
+void PROTO_INTERFACE::OnContactAdded(MCONTACT)
+{}
+
+void PROTO_INTERFACE::OnContactDeleted(MCONTACT)
+{}
+
+void PROTO_INTERFACE::OnEventEdited(MCONTACT, MEVENT)
+{}
+
+void PROTO_INTERFACE::OnErase()
+{}
+
+void PROTO_INTERFACE::OnModulesLoaded()
+{}
+
+bool PROTO_INTERFACE::IsReadyToExit()
+{
+ return true;
+}
+
+void PROTO_INTERFACE::OnShutdown()
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// default PROTO_INTERFACE method implementations
+
+MCONTACT PROTO_INTERFACE::AddToList(int, PROTOSEARCHRESULT*)
+{
+ return 0; // error
+}
+
+MCONTACT PROTO_INTERFACE::AddToListByEvent(int, int, MEVENT)
+{
+ return 0; // error
+}
+
+int PROTO_INTERFACE::Authorize(MEVENT)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::AuthDeny(MEVENT, const wchar_t*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::AuthRecv(MCONTACT, PROTORECVEVENT*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::AuthRequest(MCONTACT, const wchar_t*)
+{
+ return 1; // error
+}
+
+HANDLE PROTO_INTERFACE::FileAllow(MCONTACT, HANDLE, const wchar_t*)
+{
+ return nullptr; // error
+}
+
+int PROTO_INTERFACE::FileCancel(MCONTACT, HANDLE)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::FileDeny(MCONTACT, HANDLE, const wchar_t*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::FileResume(HANDLE, int, const wchar_t*)
+{
+ return 1; // error
+}
+
+INT_PTR PROTO_INTERFACE::GetCaps(int, MCONTACT)
+{
+ return 0; // empty value
+}
+
+int PROTO_INTERFACE::GetInfo(MCONTACT, int)
+{
+ return 1; // error
+}
+
+HANDLE PROTO_INTERFACE::SearchBasic(const wchar_t*)
+{
+ return nullptr; // error
+}
+
+HANDLE PROTO_INTERFACE::SearchByEmail(const wchar_t*)
+{
+ return nullptr; // error
+}
+
+HANDLE PROTO_INTERFACE::SearchByName(const wchar_t*, const wchar_t*, const wchar_t*)
+{
+ return nullptr; // error
+}
+
+HWND PROTO_INTERFACE::SearchAdvanced(HWND)
+{
+ return nullptr; // error
+}
+
+HWND PROTO_INTERFACE::CreateExtendedSearchUI(HWND)
+{
+ return nullptr; // error
+}
+
+int PROTO_INTERFACE::RecvContacts(MCONTACT, PROTORECVEVENT*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::RecvFile(MCONTACT hContact, PROTORECVFILE *pcre)
+{
+ CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)pcre };
+ return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs);
+}
+
+MEVENT PROTO_INTERFACE::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre)
+{
+ if (pre->szMessage == nullptr)
+ return 0;
+
+ ptrA pszTemp;
+ mir_ptr<uint8_t> pszBlob;
+
+ DBEVENTINFO dbei = {};
+ dbei.flags = DBEF_UTF;
+ dbei.szModule = Proto_GetBaseAccountName(hContact);
+ dbei.timestamp = pre->timestamp;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = (uint32_t)mir_strlen(pre->szMessage) + 1;
+ dbei.pBlob = (uint8_t*)pre->szMessage;
+
+ if (pre->flags & PREF_CREATEREAD)
+ dbei.flags |= DBEF_READ;
+ if (pre->flags & PREF_SENT)
+ dbei.flags |= DBEF_SENT;
+
+ // if it's possible to find an existing event by its id, do that
+ if ((GetCaps(PFLAGNUM_4) & PF4_SERVERMSGID) && pre->szMsgId != nullptr) {
+ MEVENT hDbEvent = db_event_getById(m_szModuleName, pre->szMsgId);
+ if (hDbEvent == 0 || db_event_edit(hContact, hDbEvent, &dbei)) {
+ dbei.szId = pre->szMsgId;
+ hDbEvent = db_event_add(hContact, &dbei);
+ }
+ return hDbEvent;
+ }
+
+ // event is new? add it
+ return (INT_PTR)db_event_add(hContact, &dbei);
+}
+
+int PROTO_INTERFACE::SendContacts(MCONTACT, int, int, MCONTACT*)
+{
+ return 1; // error
+}
+
+HANDLE PROTO_INTERFACE::SendFile(MCONTACT, const wchar_t*, wchar_t**)
+{
+ return nullptr; // error
+}
+
+int PROTO_INTERFACE::SendMsg(MCONTACT, int, const char*)
+{
+ return 0; // error
+}
+
+int PROTO_INTERFACE::SetApparentMode(MCONTACT, int)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::SetStatus(int)
+{
+ return 1; // you better declare it
+}
+
+HANDLE PROTO_INTERFACE::GetAwayMsg(MCONTACT)
+{
+ return nullptr; // no away message
+}
+
+int PROTO_INTERFACE::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::SetAwayMsg(int, const wchar_t*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::UserIsTyping(MCONTACT, int)
+{
+ return 1; // error
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// protocol menus
+
+static INT_PTR __cdecl stubRequestAuth(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_REQAUTH, hContact, 0);
+ return 0;
+}
+
+static INT_PTR __cdecl stubGrantAuth(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_GRANTAUTH, hContact, 0);
+ return 0;
+}
+
+static INT_PTR __cdecl stubRevokeAuth(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_REVOKEAUTH, hContact, 0);
+ return 0;
+}
+
+static INT_PTR __cdecl stubLoadHistory(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_LOADHISTORY, hContact, 0);
+ return 0;
+}
+
+static int __cdecl ProtoPrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ Menu_ShowItem(hReqAuth, false);
+ Menu_ShowItem(hGrantAuth, false);
+ Menu_ShowItem(hRevokeAuth, false);
+
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ Menu_ShowItem(hServerHist, ProtoServiceExists(szProto, PS_MENU_LOADHISTORY));
+ return 0;
+}
+
+void InitProtoMenus(void)
+{
+ // "Request authorization"
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x36375a1f, 0xc142, 0x4d6e, 0xa6, 0x57, 0xe4, 0x76, 0x5d, 0xbc, 0x59, 0x8e);
+ mi.pszService = "Proto/Menu/ReqAuth";
+ mi.name.a = LPGEN("Request authorization");
+ mi.position = -2000001002;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST);
+ hReqAuth = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, stubRequestAuth);
+
+ // "Grant authorization"
+ SET_UID(mi, 0x4c90452a, 0x869a, 0x4a81, 0xaf, 0xa8, 0x28, 0x34, 0xaf, 0x2b, 0x6b, 0x30);
+ mi.pszService = "Proto/Menu/GrantAuth";
+ mi.name.a = LPGEN("Grant authorization");
+ mi.position = -2000001001;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_GRANT);
+ hGrantAuth = Menu_AddContactMenuItem(&mi);
+
+ // "Revoke authorization"
+ SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77);
+ mi.pszService = "Proto/Menu/RevokeAuth";
+ mi.name.a = LPGEN("Revoke authorization");
+ mi.position = -2000001000;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE);
+ hRevokeAuth = Menu_AddContactMenuItem(&mi);
+
+ SET_UID(mi, 0xd15b841d, 0xb0fc, 0x4ab5, 0x96, 0x94, 0xcf, 0x6c, 0x6e, 0x99, 0x4b, 0x3c); // {D15B841D-B0FC-4AB5-9694-CF6C6E994B3C}
+ mi.pszService = "Proto/Menu/LoadHistory";
+ mi.name.a = LPGEN("Load server history");
+ mi.position = -200001004;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HISTORY);
+ hServerHist = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, stubLoadHistory);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, ProtoPrebuildContactMenu);
+}
diff --git a/src/mir_app/src/proto_internal.cpp b/src/mir_app/src/proto_internal.cpp index b7a74d8d4d..0b40f9cee1 100644 --- a/src/mir_app/src/proto_internal.cpp +++ b/src/mir_app/src/proto_internal.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_opts.cpp b/src/mir_app/src/proto_opts.cpp index c25a6c937b..c2dfe73e72 100644 --- a/src/mir_app/src/proto_opts.cpp +++ b/src/mir_app/src/proto_opts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_order.cpp b/src/mir_app/src/proto_order.cpp index 9096b0cb61..3024e62b0e 100644 --- a/src/mir_app/src/proto_order.cpp +++ b/src/mir_app/src/proto_order.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_ui.cpp b/src/mir_app/src/proto_ui.cpp index 3ef0a00233..13319b83a1 100644 --- a/src/mir_app/src/proto_ui.cpp +++ b/src/mir_app/src/proto_ui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_utils.cpp b/src/mir_app/src/proto_utils.cpp index 79bd7012fa..281d76719e 100644 --- a/src/mir_app/src/proto_utils.cpp +++ b/src/mir_app/src/proto_utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/protocols.cpp b/src/mir_app/src/protocols.cpp index 12fe246fb1..f3a1d60f5c 100644 --- a/src/mir_app/src/protocols.cpp +++ b/src/mir_app/src/protocols.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/pu_utils.cpp b/src/mir_app/src/pu_utils.cpp index 9495031fe7..1ed944ea3a 100644 --- a/src/mir_app/src/pu_utils.cpp +++ b/src/mir_app/src/pu_utils.cpp @@ -1,331 +1,331 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// pu_stub.exe interface - -#include "stdafx.h" - -static HANDLE g_hPipe = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// are we running with admin priviledges? - -static bool IsRunAsAdmin() -{ - BOOL bIsRunAsAdmin = false; - uint32_t dwError = ERROR_SUCCESS; - PSID pAdministratorsGroup = nullptr; - - // Allocate and initialize a SID of the administrators group. - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) { - dwError = GetLastError(); - goto Cleanup; - } - - // Determine whether the SID of administrators group is bEnabled in - // the primary access token of the process. - if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) { - dwError = GetLastError(); - goto Cleanup; - } - -Cleanup: - // Centralized cleanup for all allocated resources. - if (pAdministratorsGroup) { - FreeSid(pAdministratorsGroup); - pAdministratorsGroup = nullptr; - } - - // Throw the error if something failed in the function. - if (ERROR_SUCCESS != dwError) { - throw dwError; - } - - return bIsRunAsAdmin != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if we're working via pu_stub or not - -MIR_APP_DLL(bool) PU::IsDirect() -{ - return g_hPipe == nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if Miranda's folder is writeable - -MIR_APP_DLL(bool) PU::IsMirandaFolderWritable() -{ - if (!IsWinVerVistaPlus()) - return true; - - wchar_t wszPath[MAX_PATH]; - GetModuleFileNameW(nullptr, wszPath, _countof(wszPath)); - wchar_t *ext = wcsrchr(wszPath, '.'); - if (ext != nullptr) - *ext = '\0'; - wcscat(wszPath, L".test"); - HANDLE hFile = CreateFileW(wszPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile == INVALID_HANDLE_VALUE) - return false; - - CloseHandle(hFile); - DeleteFileW(wszPath); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if a process has enough rights to write into Miranda's folder - -MIR_APP_DLL(bool) PU::IsProcessElevated() -{ - bool bIsElevated = false; - HANDLE hToken = nullptr; - - // Open the primary access token of the process with TOKEN_QUERY. - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) - goto Cleanup; - - // Retrieve token elevation information. - TOKEN_ELEVATION elevation; - DWORD dwSize; - if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { - // When the process is run on operating systems prior to Windows - // Vista, GetTokenInformation returns FALSE with the - // ERROR_INVALID_PARAMETER error code because TokenElevation is - // not supported on those operating systems. - goto Cleanup; - } - - bIsElevated = elevation.TokenIsElevated != 0; - -Cleanup: - // Centralized cleanup for all allocated resources. - if (hToken) - CloseHandle(hToken); - - return bIsElevated; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Launches pu_stub.exe with elevated priviledges if needed - -MIR_APP_DLL(bool) PU::PrepareEscalation(const wchar_t *pwszFile) -{ - CMStringW wszFilePath; - // First try to create a file near Miranda32.exe - if (pwszFile == nullptr) { - wchar_t szPath[MAX_PATH]; - GetModuleFileName(nullptr, szPath, _countof(szPath)); - wchar_t *ext = wcsrchr(szPath, '.'); - if (ext != nullptr) - *ext = '\0'; - wszFilePath = szPath; - } - else wszFilePath = pwszFile; - - wszFilePath.Append(L".test"); - - HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile != INVALID_HANDLE_VALUE) { - // we are admins or UAC is disable, cool - CloseHandle(hFile); - DeleteFileW(wszFilePath); - return true; - } - - // Check the current process's "run as administrator" status. - if (IsRunAsAdmin()) - return true; - - // if pipe already opened? - if (g_hPipe != nullptr) - return true; - - // Elevate the process. Create a pipe for a stub first - wchar_t wzPipeName[MAX_PATH]; - mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId()); - g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr); - if (g_hPipe == INVALID_HANDLE_VALUE) { - g_hPipe = nullptr; - } - else { - wchar_t cmdLine[100], *p; - wchar_t szPath[MAX_PATH]; - GetModuleFileName(nullptr, szPath, _countof(szPath)); - if ((p = wcsrchr(szPath, '\\')) != nullptr) - wcscpy(p + 1, L"pu_stub.exe"); - mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId()); - - // Launch a stub - SHELLEXECUTEINFO sei = { sizeof(sei) }; - sei.lpVerb = L"runas"; - sei.lpFile = szPath; - sei.lpParameters = cmdLine; - sei.hwnd = nullptr; - sei.nShow = SW_NORMAL; - if (ShellExecuteEx(&sei)) { - if (g_hPipe != nullptr) - ConnectNamedPipe(g_hPipe, nullptr); - return true; - } - - uint32_t dwError = GetLastError(); - if (dwError == ERROR_CANCELLED) { - // The user refused to allow privileges elevation. - // Do nothing ... - } - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2) -{ - uint8_t buf[1024]; - uint32_t l1 = lstrlen(p1), l2 = lstrlen(p2); - if (l1 > MAX_PATH || l2 > MAX_PATH) - return ERROR_BAD_ARGUMENTS; - - *(uint32_t *)buf = opcode; - wchar_t *dst = (wchar_t *)&buf[sizeof(uint32_t)]; - lstrcpy(dst, p1); - dst += l1 + 1; - if (p2) { - lstrcpy(dst, p2); - dst += l2 + 1; - } - else *dst++ = 0; - - DWORD dwBytes = 0, dwError; - if (!WriteFile(g_hPipe, buf, (uint32_t)((uint8_t *)dst - buf), &dwBytes, nullptr)) - return GetLastError(); - - dwError = 0; - if (!ReadFile(g_hPipe, &dwError, sizeof(uint32_t), &dwBytes, nullptr)) - return GetLastError(); - if (dwBytes != sizeof(uint32_t)) - return ERROR_BAD_ARGUMENTS; - - return dwError; -} - -MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst) -{ - if (g_hPipe == nullptr) - return CopyFileW(pSrc, pDst, FALSE); - - return TransactPipe(1, pSrc, pDst); -} - -MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst) -{ - if (g_hPipe == nullptr) { - if (!DeleteFileW(pDst)) { - uint32_t dwError = GetLastError(); - if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND) - return dwError; - } - - if (!MoveFileW(pSrc, pDst)) { // use copy on error - switch (uint32_t dwError = GetLastError()) { - case ERROR_ALREADY_EXISTS: - case ERROR_FILE_NOT_FOUND: - return 0; // this file was included into many archives, so Miranda tries to move it again & again - - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_LOCK_VIOLATION: - // use copy routine if a move operation isn't available - // for example, when files are on different disks - if (!CopyFileW(pSrc, pDst, FALSE)) - return GetLastError(); - - if (!DeleteFileW(pSrc)) - return GetLastError(); - break; - - default: - return dwError; - } - } - - return ERROR_SUCCESS; - } - - return TransactPipe(2, pSrc, pDst); -} - -MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile) -{ - if (g_hPipe == nullptr) - return DeleteFileW(pwszFile); - - return TransactPipe(3, pwszFile, nullptr); -} - -MIR_APP_DLL(int) PU::SafeRecycleBin(const wchar_t *pwszFile) -{ - if (g_hPipe == nullptr) { - CMStringW tmpPath(pwszFile); - tmpPath.AppendChar(0); - - SHFILEOPSTRUCT shfo = {}; - shfo.wFunc = FO_DELETE; - shfo.pFrom = tmpPath; - shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO; - return SHFileOperation(&shfo); - } - - return TransactPipe(7, pwszFile, nullptr); -} - -MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder) -{ - if (g_hPipe == nullptr) - return CreateDirectoryTreeW(pwszFolder); - - return TransactPipe(4, pwszFolder, nullptr); -} - -MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName) -{ - if (g_hPipe == nullptr) - return DeleteDirectoryTreeW(pwszDirName); - - return TransactPipe(6, pwszDirName, nullptr); -} - -MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder) -{ - if (g_hPipe == nullptr) { - CreatePathToFileW(pwszFolder); - return 0; - } - - return TransactPipe(5, pwszFolder, nullptr); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// pu_stub.exe interface
+
+#include "stdafx.h"
+
+static HANDLE g_hPipe = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// are we running with admin priviledges?
+
+static bool IsRunAsAdmin()
+{
+ BOOL bIsRunAsAdmin = false;
+ uint32_t dwError = ERROR_SUCCESS;
+ PSID pAdministratorsGroup = nullptr;
+
+ // Allocate and initialize a SID of the administrators group.
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) {
+ dwError = GetLastError();
+ goto Cleanup;
+ }
+
+ // Determine whether the SID of administrators group is bEnabled in
+ // the primary access token of the process.
+ if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) {
+ dwError = GetLastError();
+ goto Cleanup;
+ }
+
+Cleanup:
+ // Centralized cleanup for all allocated resources.
+ if (pAdministratorsGroup) {
+ FreeSid(pAdministratorsGroup);
+ pAdministratorsGroup = nullptr;
+ }
+
+ // Throw the error if something failed in the function.
+ if (ERROR_SUCCESS != dwError) {
+ throw dwError;
+ }
+
+ return bIsRunAsAdmin != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Checks if we're working via pu_stub or not
+
+MIR_APP_DLL(bool) PU::IsDirect()
+{
+ return g_hPipe == nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Checks if Miranda's folder is writeable
+
+MIR_APP_DLL(bool) PU::IsMirandaFolderWritable()
+{
+ if (!IsWinVerVistaPlus())
+ return true;
+
+ wchar_t wszPath[MAX_PATH];
+ GetModuleFileNameW(nullptr, wszPath, _countof(wszPath));
+ wchar_t *ext = wcsrchr(wszPath, '.');
+ if (ext != nullptr)
+ *ext = '\0';
+ wcscat(wszPath, L".test");
+ HANDLE hFile = CreateFileW(wszPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return false;
+
+ CloseHandle(hFile);
+ DeleteFileW(wszPath);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Checks if a process has enough rights to write into Miranda's folder
+
+MIR_APP_DLL(bool) PU::IsProcessElevated()
+{
+ bool bIsElevated = false;
+ HANDLE hToken = nullptr;
+
+ // Open the primary access token of the process with TOKEN_QUERY.
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+ goto Cleanup;
+
+ // Retrieve token elevation information.
+ TOKEN_ELEVATION elevation;
+ DWORD dwSize;
+ if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) {
+ // When the process is run on operating systems prior to Windows
+ // Vista, GetTokenInformation returns FALSE with the
+ // ERROR_INVALID_PARAMETER error code because TokenElevation is
+ // not supported on those operating systems.
+ goto Cleanup;
+ }
+
+ bIsElevated = elevation.TokenIsElevated != 0;
+
+Cleanup:
+ // Centralized cleanup for all allocated resources.
+ if (hToken)
+ CloseHandle(hToken);
+
+ return bIsElevated;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Launches pu_stub.exe with elevated priviledges if needed
+
+MIR_APP_DLL(bool) PU::PrepareEscalation(const wchar_t *pwszFile)
+{
+ CMStringW wszFilePath;
+ // First try to create a file near Miranda32.exe
+ if (pwszFile == nullptr) {
+ wchar_t szPath[MAX_PATH];
+ GetModuleFileName(nullptr, szPath, _countof(szPath));
+ wchar_t *ext = wcsrchr(szPath, '.');
+ if (ext != nullptr)
+ *ext = '\0';
+ wszFilePath = szPath;
+ }
+ else wszFilePath = pwszFile;
+
+ wszFilePath.Append(L".test");
+
+ HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ // we are admins or UAC is disable, cool
+ CloseHandle(hFile);
+ DeleteFileW(wszFilePath);
+ return true;
+ }
+
+ // Check the current process's "run as administrator" status.
+ if (IsRunAsAdmin())
+ return true;
+
+ // if pipe already opened?
+ if (g_hPipe != nullptr)
+ return true;
+
+ // Elevate the process. Create a pipe for a stub first
+ wchar_t wzPipeName[MAX_PATH];
+ mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId());
+ g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr);
+ if (g_hPipe == INVALID_HANDLE_VALUE) {
+ g_hPipe = nullptr;
+ }
+ else {
+ wchar_t cmdLine[100], *p;
+ wchar_t szPath[MAX_PATH];
+ GetModuleFileName(nullptr, szPath, _countof(szPath));
+ if ((p = wcsrchr(szPath, '\\')) != nullptr)
+ wcscpy(p + 1, L"pu_stub.exe");
+ mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId());
+
+ // Launch a stub
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.lpVerb = L"runas";
+ sei.lpFile = szPath;
+ sei.lpParameters = cmdLine;
+ sei.hwnd = nullptr;
+ sei.nShow = SW_NORMAL;
+ if (ShellExecuteEx(&sei)) {
+ if (g_hPipe != nullptr)
+ ConnectNamedPipe(g_hPipe, nullptr);
+ return true;
+ }
+
+ uint32_t dwError = GetLastError();
+ if (dwError == ERROR_CANCELLED) {
+ // The user refused to allow privileges elevation.
+ // Do nothing ...
+ }
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2)
+{
+ uint8_t buf[1024];
+ uint32_t l1 = lstrlen(p1), l2 = lstrlen(p2);
+ if (l1 > MAX_PATH || l2 > MAX_PATH)
+ return ERROR_BAD_ARGUMENTS;
+
+ *(uint32_t *)buf = opcode;
+ wchar_t *dst = (wchar_t *)&buf[sizeof(uint32_t)];
+ lstrcpy(dst, p1);
+ dst += l1 + 1;
+ if (p2) {
+ lstrcpy(dst, p2);
+ dst += l2 + 1;
+ }
+ else *dst++ = 0;
+
+ DWORD dwBytes = 0, dwError;
+ if (!WriteFile(g_hPipe, buf, (uint32_t)((uint8_t *)dst - buf), &dwBytes, nullptr))
+ return GetLastError();
+
+ dwError = 0;
+ if (!ReadFile(g_hPipe, &dwError, sizeof(uint32_t), &dwBytes, nullptr))
+ return GetLastError();
+ if (dwBytes != sizeof(uint32_t))
+ return ERROR_BAD_ARGUMENTS;
+
+ return dwError;
+}
+
+MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst)
+{
+ if (g_hPipe == nullptr)
+ return CopyFileW(pSrc, pDst, FALSE);
+
+ return TransactPipe(1, pSrc, pDst);
+}
+
+MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst)
+{
+ if (g_hPipe == nullptr) {
+ if (!DeleteFileW(pDst)) {
+ uint32_t dwError = GetLastError();
+ if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND)
+ return dwError;
+ }
+
+ if (!MoveFileW(pSrc, pDst)) { // use copy on error
+ switch (uint32_t dwError = GetLastError()) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_NOT_FOUND:
+ return 0; // this file was included into many archives, so Miranda tries to move it again & again
+
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ // use copy routine if a move operation isn't available
+ // for example, when files are on different disks
+ if (!CopyFileW(pSrc, pDst, FALSE))
+ return GetLastError();
+
+ if (!DeleteFileW(pSrc))
+ return GetLastError();
+ break;
+
+ default:
+ return dwError;
+ }
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ return TransactPipe(2, pSrc, pDst);
+}
+
+MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile)
+{
+ if (g_hPipe == nullptr)
+ return DeleteFileW(pwszFile);
+
+ return TransactPipe(3, pwszFile, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeRecycleBin(const wchar_t *pwszFile)
+{
+ if (g_hPipe == nullptr) {
+ CMStringW tmpPath(pwszFile);
+ tmpPath.AppendChar(0);
+
+ SHFILEOPSTRUCT shfo = {};
+ shfo.wFunc = FO_DELETE;
+ shfo.pFrom = tmpPath;
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO;
+ return SHFileOperation(&shfo);
+ }
+
+ return TransactPipe(7, pwszFile, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder)
+{
+ if (g_hPipe == nullptr)
+ return CreateDirectoryTreeW(pwszFolder);
+
+ return TransactPipe(4, pwszFolder, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName)
+{
+ if (g_hPipe == nullptr)
+ return DeleteDirectoryTreeW(pwszDirName);
+
+ return TransactPipe(6, pwszDirName, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder)
+{
+ if (g_hPipe == nullptr) {
+ CreatePathToFileW(pwszFolder);
+ return 0;
+ }
+
+ return TransactPipe(5, pwszFolder, nullptr);
+}
diff --git a/src/mir_app/src/searchresults.cpp b/src/mir_app/src/searchresults.cpp index 2a298d6598..892ec2c7dd 100644 --- a/src/mir_app/src/searchresults.cpp +++ b/src/mir_app/src/searchresults.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/skin.h b/src/mir_app/src/skin.h index d0a76adfc0..e6225825a8 100644 --- a/src/mir_app/src/skin.h +++ b/src/mir_app/src/skin.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/skin2opts.cpp b/src/mir_app/src/skin2opts.cpp index 5d1cd2e072..a42db0b245 100644 --- a/src/mir_app/src/skin2opts.cpp +++ b/src/mir_app/src/skin2opts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/skinicons.cpp b/src/mir_app/src/skinicons.cpp index 927f5e798f..f6ce8e8b5d 100644 --- a/src/mir_app/src/skinicons.cpp +++ b/src/mir_app/src/skinicons.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/sounds.cpp b/src/mir_app/src/sounds.cpp index 8da80075df..8762adb26b 100644 --- a/src/mir_app/src/sounds.cpp +++ b/src/mir_app/src/sounds.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/srmm_base.cpp b/src/mir_app/src/srmm_base.cpp index 85a9308301..fd1d564a8a 100644 --- a/src/mir_app/src/srmm_base.cpp +++ b/src/mir_app/src/srmm_base.cpp @@ -1,923 +1,923 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" -#include "resource.h" -#include "skin.h" -#include <m_history.h> - -CSrmmBaseDialog::CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, SESSION_INFO *si) : - CDlgBase(pPlugin, idDialog), - timerFlash(this, 1), - timerType(this, 2), - - m_message(this, IDC_SRMM_MESSAGE), - m_nickList(this, IDC_SRMM_NICKLIST), - - m_btnOk(this, IDOK), - m_btnFilter(this, IDC_SRMM_FILTER), - m_btnHistory(this, IDC_SRMM_HISTORY), - m_btnNickList(this, IDC_SRMM_SHOWNICKLIST), - m_btnChannelMgr(this, IDC_SRMM_CHANMGR), - - m_btnColor(this, IDC_SRMM_COLOR), - m_btnBkColor(this, IDC_SRMM_BKGCOLOR), - m_btnBold(this, IDC_SRMM_BOLD), - - m_btnItalic(this, IDC_SRMM_ITALICS), - m_btnUnderline(this, IDC_SRMM_UNDERLINE), - - m_si(si), - m_hContact(0), - m_clrInputBG(GetSysColor(COLOR_WINDOW)) -{ - m_bFilterEnabled = db_get_b(0, CHAT_MODULE, "FilterEnabled", 0) != 0; - m_bNicklistEnabled = db_get_b(0, CHAT_MODULE, "ShowNicklist", 1) != 0; - m_iLogFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", 0x03E0); - - m_btnColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_Color); - m_btnBkColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BkColor); - m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BIU); - - m_btnHistory.OnClick = Callback(this, &CSrmmBaseDialog::onClick_History); - m_btnChannelMgr.OnClick = Callback(this, &CSrmmBaseDialog::onClick_ChanMgr); - - m_nickList.OnDblClick = Callback(this, &CMsgDialog::onDblClick_List); - - if (si) { - m_hContact = si->hContact; - - if (si->pMI->bColor) { - m_iFG = 4; - m_bFGSet = true; - } - if (si->pMI->bBkgColor) { - m_iBG = 2; - m_bBGSet = true; - } - } -} - -void CSrmmBaseDialog::RunUserMenu(HWND hwndOwner, USERINFO *ui, const POINT &pt) -{ - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_USERMENU)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - - USERINFO uinew; - memcpy(&uinew, ui, sizeof(USERINFO)); - - wchar_t szTemp[50]; - if (uinew.pszNick) - mir_snwprintf(szTemp, TranslateT("&Message %s"), uinew.pszNick); - else - mir_wstrncpy(szTemp, TranslateT("&Message"), _countof(szTemp) - 1); - - if (mir_wstrlen(szTemp) > 40) - mir_wstrncpy(szTemp + 40, L"...", 4); - ModifyMenu(hMenu, 0, MF_STRING | MF_BYPOSITION, IDM_SENDMESSAGE, szTemp); - - UINT uID = Chat_CreateMenu(hwndOwner, hSubMenu, pt, m_si, uinew.pszUID); - switch (uID) { - case 0: - break; - - case IDM_SENDMESSAGE: - Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); - break; - - default: - Chat_DoEventHook(m_si, GC_USER_NICKLISTMENU, ui, nullptr, uID); - break; - } - DestroyMenu(hMenu); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK Srmm_ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_RBUTTONUP: - if (g_chatApi.bRightClickFilter) { - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg == nullptr) - break; - - switch (GetDlgCtrlID(hwnd)) { - case IDC_SRMM_FILTER: - pDlg->ShowFilterMenu(); - break; - - case IDC_SRMM_COLOR: - pDlg->ShowColorChooser(IDC_SRMM_COLOR); - break; - - case IDC_SRMM_BKGCOLOR: - pDlg->ShowColorChooser(IDC_SRMM_BKGCOLOR); - break; - } - } - break; - } - - return mir_callNextSubclass(hwnd, Srmm_ButtonSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg != nullptr) - return pDlg->WndProc_Message(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubMessageProc, msg, wParam, lParam); -} - -LRESULT CSrmmBaseDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_SETCURSOR: - if (m_bInMenu) { - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - return TRUE; - } - break; - - case WM_CHAR: - switch (wParam) { - case 0x02: // ctrl+B - if (m_btnBold.Enabled()) - return 1; - break; - case 0x09: // ctrl+I - if (m_btnItalic.Enabled()) - return 1; - break; - case 0x15: // ctrl+U - if (m_btnUnderline.Enabled()) - return 1; - break; - } - break; - - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - if (wParam == VK_BACK) - if (m_message.GetRichTextLength() == 0) - return 1; - - MSG tmp = { m_hwnd, msg, wParam, lParam }; - if (Hotkey_Check(&tmp, g_pszHotkeySection) == 100) { - if (!(GetWindowLongPtr(m_message.GetHwnd(), GWL_STYLE) & ES_READONLY)) { - PostMessage(m_hwnd, WM_COMMAND, IDOK, 0); - return true; - } - } - } - - LRESULT res = mir_callNextSubclass(m_message.GetHwnd(), stubMessageProc, msg, wParam, lParam); - switch (msg) { - case WM_GETDLGCODE: - return res & ~DLGC_HASSETSEL; - - case WM_KEYUP: - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - RefreshButtonStatus(); - break; - - case WM_KEYDOWN: - if ((GetKeyState(VK_CONTROL) & 0x8000) && wParam == 'V' || (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_INSERT) { - if (IsClipboardFormatAvailable(CF_HDROP)) { - m_message.SendMsg(WM_PASTE, 0, 0); - return 0; - } - } - - __fallthrough; - - case WM_SYSKEYDOWN: - if (!(GetKeyState(VK_RMENU) & 0x8000)) { - MSG message = { m_hwnd, msg, wParam, lParam }; - LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); - if (iButtonFrom) { - Srmm_ProcessToolbarHotkey(m_hContact, iButtonFrom, m_hwnd); - return TRUE; - } - } - break; - } - - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// process mouse - hovering for the nickname list.fires events so the protocol can -// show the userinfo - tooltip. - -static void ProcessNickListHovering(HWND hwnd, int hoveredItem, SESSION_INFO *parentdat) -{ - static int currentHovered = -1; - static HWND hwndToolTip = nullptr; - static HWND oldParent = nullptr; - - if (hoveredItem == currentHovered) - return; - - currentHovered = hoveredItem; - - if (oldParent != hwnd && hwndToolTip) { - SendMessage(hwndToolTip, TTM_DELTOOL, 0, 0); - DestroyWindow(hwndToolTip); - hwndToolTip = nullptr; - } - - if (hoveredItem == -1) { - SendMessage(hwndToolTip, TTM_ACTIVATE, 0, 0); - return; - } - - bool bNewTip = false; - if (!hwndToolTip) { - bNewTip = true; - hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr, - WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - hwnd, nullptr, g_plugin.getInst(), nullptr); - } - - RECT clientRect; - GetClientRect(hwnd, &clientRect); - - TOOLINFO ti = { sizeof(ti) }; - ti.uFlags = TTF_SUBCLASS; - ti.hinst = g_plugin.getInst(); - ti.hwnd = hwnd; - ti.uId = 1; - ti.rect = clientRect; - - CMStringW wszBuf; - - USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(parentdat->ptszID, parentdat->pszModule, currentHovered); - if (ui1) { - if (ProtoServiceExists(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { - wchar_t *p = (wchar_t*)CallProtoService(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)parentdat->ptszID, (LPARAM)ui1->pszUID); - if (p != nullptr) { - wszBuf = p; - mir_free(p); - } - } - - if (wszBuf.IsEmpty()) - wszBuf.Format(L"%s: %s\r\n%s: %s\r\n%s: %s", - TranslateT("Nickname"), ui1->pszNick, - TranslateT("Unique ID"), ui1->pszUID, - TranslateT("Status"), g_chatApi.TM_WordToString(parentdat->pStatuses, ui1->Status)); - ti.lpszText = wszBuf.GetBuffer(); - } - - SendMessage(hwndToolTip, bNewTip ? TTM_ADDTOOL : TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); - SendMessage(hwndToolTip, TTM_ACTIVATE, (ti.lpszText != nullptr), 0); - SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 400); -} - -static void CALLBACK ChatTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD) -{ - SESSION_INFO *si = (SESSION_INFO*)idEvent; - - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hwnd, &pt); - - uint32_t nItemUnderMouse = (uint32_t)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); - if (HIWORD(nItemUnderMouse) == 1) - nItemUnderMouse = (uint32_t)(-1); - else - nItemUnderMouse &= 0xFFFF; - if (((int)nItemUnderMouse != si->currentHovered) || (nItemUnderMouse == -1)) { - KillTimer(hwnd, idEvent); - return; - } - - USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(si->ptszID, si->pszModule, si->currentHovered); - if (ui1) { - CMStringW wszBuf; - if (ProtoServiceExists(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { - wchar_t *p = (wchar_t*)CallProtoService(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)si->ptszID, (LPARAM)ui1->pszUID); - if (p) { - wszBuf = p; - mir_free(p); - } - } - if (wszBuf.IsEmpty()) - wszBuf.Format(L"<b>%s:</b>\t%s\n<b>%s:</b>\t%s\n<b>%s:</b>\t%s", - TranslateT("Nick"), ui1->pszNick, - TranslateT("Unique ID"), ui1->pszUID, - TranslateT("Status"), g_chatApi.TM_WordToString(si->pStatuses, ui1->Status)); - - CLCINFOTIP ti = { sizeof(ti) }; - Tipper_ShowTip(wszBuf, &ti); - si->bHasToolTip = true; - } - KillTimer(hwnd, idEvent); -} - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg != nullptr) - return pDlg->WndProc_Nicklist(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubNicklistProc, msg, wParam, lParam); -} - -LRESULT CSrmmBaseDialog::WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - - switch (msg) { - case WM_MEASUREITEM: - { - MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; - if (mis->CtlType == ODT_MENU) - return Menu_MeasureItem(lParam); - } - return FALSE; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - if (dis->CtlType == ODT_MENU) - return Menu_DrawItem(lParam); - } - return FALSE; - - case WM_MOUSEMOVE: - RECT clientRect; - { - bool bTooltipExists = ServiceExists(MS_TIPPER_HIDETIP); - - POINT pt = { LOWORD(lParam), HIWORD(lParam) }; - GetClientRect(m_nickList.GetHwnd(), &clientRect); - if (PtInRect(&clientRect, pt)) { - // hit test item under mouse - uint32_t nItemUnderMouse = m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, lParam); - if (HIWORD(nItemUnderMouse) == 1) - nItemUnderMouse = (uint32_t)(-1); - else - nItemUnderMouse &= 0xFFFF; - - if (bTooltipExists) { - if ((int)nItemUnderMouse == m_si->currentHovered) - break; - m_si->currentHovered = (int)nItemUnderMouse; - - KillTimer(m_nickList.GetHwnd(), 1); - - if (m_si->bHasToolTip) { - Tipper_Hide(); - m_si->bHasToolTip = false; - } - - if (nItemUnderMouse != -1) - SetTimer(m_nickList.GetHwnd(), (UINT_PTR)m_si, 450, ChatTimerProc); - } - else ProcessNickListHovering(m_nickList.GetHwnd(), (int)nItemUnderMouse, m_si); - } - else { - if (bTooltipExists) { - KillTimer(m_nickList.GetHwnd(), 1); - if (m_si->bHasToolTip) { - Tipper_Hide(); - m_si->bHasToolTip = false; - } - } - else ProcessNickListHovering(m_nickList.GetHwnd(), -1, nullptr); - } - } - break; - - case WM_ERASEBKGND: - { - HDC dc = (HDC)wParam; - if (dc == nullptr) - break; - - int nUsers = m_si->getUserList().getCount(); - - int index = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - if (index == LB_ERR || nUsers <= 0) - break; - - int height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); - if (height == LB_ERR) - break; - - GetClientRect(m_nickList.GetHwnd(), &rc); - - int items = nUsers - index; - if (rc.bottom - rc.top > items * height) { - rc.top = items * height; - FillRect(dc, &rc, g_chatApi.hListBkgBrush); - } - } - return 1; - - case WM_CONTEXTMENU: - POINT pt; - { - int height = 0; - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - if (pt.x == -1 && pt.y == -1) { - int index = m_nickList.GetCurSel(); - int top = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); - pt.x = 4; - pt.y = (index - top)*height + 1; - } - else ScreenToClient(m_nickList.GetHwnd(), &pt); - - int item = LOWORD(m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y))); - USERINFO *ui = g_chatApi.SM_GetUserFromIndex(m_si->ptszID, m_si->pszModule, item); - if (ui != nullptr) { - if (pt.x == -1 && pt.y == -1) - pt.y += height - 4; - ClientToScreen(m_nickList.GetHwnd(), &pt); - - RunUserMenu(m_nickList.GetHwnd(), ui, pt); - return TRUE; - } - } - break; - } - - return mir_callNextSubclass(m_nickList.GetHwnd(), stubNicklistProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::OnInitDialog() -{ - WindowList_Add(g_hWindowList, m_hwnd, m_hContact); - SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this); - - m_pLog = Srmm_GetLogWindow((CMsgDialog*)this); - if (m_pLog->GetType() != 0) { // custom log type - HWND hwndLog = GetDlgItem(m_hwnd, IDC_SRMM_LOG); - EnableWindow(hwndLog, FALSE); - ShowWindow(hwndLog, SW_HIDE); - } - m_pLog->Attach(); - - SetWindowLongPtr(m_message.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_message.GetHwnd(), stubMessageProc); - m_message.SetReadOnly(false); - ::DragAcceptFiles(m_message.GetHwnd(), TRUE); - - if (isChat()) { - SetWindowLongPtr(m_nickList.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_nickList.GetHwnd(), stubNicklistProc); - } - - // three buttons below are initiated inside this call, so button creation must precede subclassing - Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); - - mir_subclassWindow(m_btnFilter.GetHwnd(), Srmm_ButtonSubclassProc); - mir_subclassWindow(m_btnColor.GetHwnd(), Srmm_ButtonSubclassProc); - mir_subclassWindow(m_btnBkColor.GetHwnd(), Srmm_ButtonSubclassProc); - - LoadSettings(); - return true; -} - -void CSrmmBaseDialog::OnDestroy() -{ - m_pLog->Detach(); - delete m_pLog; - - WindowList_Remove(g_hWindowList, m_hwnd); - - SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); - mir_unsubclassWindow(m_message.GetHwnd(), stubMessageProc); - mir_unsubclassWindow(m_nickList.GetHwnd(), stubNicklistProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CSrmmBaseDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_COMMAND: - if (!lParam && Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact)) - return 0; - - if (wParam >= MIN_CBUTTONID && wParam <= MAX_CBUTTONID) { - Srmm_ClickToolbarIcon(m_hContact, wParam, m_hwnd, 0); - return 0; - } - break; - - case WM_ACTIVATE: - if (m_si && LOWORD(wParam) == WA_INACTIVE) { - m_si->wState &= ~GC_EVENT_HIGHLIGHT; - m_si->wState &= ~STATE_TALK; - } - break; - - case WM_CBD_RECREATE: - Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); - break; - - case WM_NOTIFY: - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->hwndFrom == m_pLog->GetHwnd()) - m_pLog->Notify(wParam, lParam); - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); -} - -void CSrmmBaseDialog::AddLog() -{ - if (m_si->pLogEnd) - m_pLog->LogEvents(m_si->pLog, false); - else - m_pLog->Clear(); -} - -bool CSrmmBaseDialog::AllowTyping() const -{ - return isChat() ? m_si->iType != GCW_SERVER : true; -} - -void CSrmmBaseDialog::ClearLog() -{ - m_pLog->Clear(); -} - -void CSrmmBaseDialog::UpdateOptions() -{ - MODULEINFO *mi = m_si->pMI; - EnableWindow(m_btnBold.GetHwnd(), mi->bBold); - EnableWindow(m_btnItalic.GetHwnd(), mi->bItalics); - EnableWindow(m_btnUnderline.GetHwnd(), mi->bUnderline); - EnableWindow(m_btnColor.GetHwnd(), mi->bColor); - EnableWindow(m_btnBkColor.GetHwnd(), mi->bBkgColor); - if (m_si->iType == GCW_CHATROOM) - EnableWindow(m_btnChannelMgr.GetHwnd(), mi->bChanMgr); - - Resize(); - RedrawLog2(m_si); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void RedrawLog2(SESSION_INFO *si) -{ - si->LastTime = 0; - if (si->pLog) - si->pDlg->log()->LogEvents(si->pLogEnd, TRUE); -} - -static void __cdecl phase2(SESSION_INFO *si) -{ - Sleep(30); - if (si && si->pDlg) - RedrawLog2(si); -} - -void CSrmmBaseDialog::RedrawLog() -{ - m_si->LastTime = 0; - if (m_si->pLog) { - LOGINFO *pLog = m_si->pLog; - if (m_si->iEventCount > 60) { - int index = 0; - while (index < 59) { - if (pLog->next == nullptr) - break; - - pLog = pLog->next; - if (m_si->iType != GCW_CHATROOM || !m_bFilterEnabled || (m_iLogFilterFlags & pLog->iType) != 0) - index++; - } - m_pLog->LogEvents(pLog, true); - mir_forkThread<SESSION_INFO>(phase2, m_si); - } - else m_pLog->LogEvents(m_si->pLogEnd, true); - } - else ClearLog(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSrmmBaseDialog::onClick_Color(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwEffects = 0; - cf.dwMask = CFM_COLOR; - - if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { - if (!g_chatApi.bRightClickFilter) { - ShowColorChooser(pButton->GetCtrlId()); - return; - } - if (m_bFGSet) - cf.crTextColor = m_iFG; - } - else cf.crTextColor = m_clrInputFG; - - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_BkColor(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwEffects = 0; - cf.dwMask = CFM_BACKCOLOR; - - if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { - if (!g_chatApi.bRightClickFilter) { - ShowColorChooser(pButton->GetCtrlId()); - return; - } - if (m_bBGSet) - cf.crBackColor = m_iBG; - } - else cf.crBackColor = m_clrInputBG; - - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_BIU(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; - cf.dwEffects = 0; - - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_BOLD)) - cf.dwEffects |= CFE_BOLD; - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_ITALICS)) - cf.dwEffects |= CFE_ITALIC; - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_UNDERLINE)) - cf.dwEffects |= CFE_UNDERLINE; - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_History(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - if (m_si != nullptr) - ShellExecute(m_hwnd, nullptr, g_chatApi.GetChatLogsFilename(m_si, 0), nullptr, nullptr, SW_SHOW); - else - CallService(MS_HISTORY_SHOWCONTACTHISTORY, m_hContact, 0); -} - -void CSrmmBaseDialog::onClick_ChanMgr(CCtrlButton *pButton) -{ - if (pButton->Enabled()) - Chat_DoEventHook(m_si, GC_USER_CHANMGR, nullptr, nullptr, 0); -} - -void CSrmmBaseDialog::onDblClick_List(CCtrlListBox *pList) -{ - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pList->GetHwnd(), &hti.pt); - - int item = LOWORD(pList->SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y))); - USERINFO *ui = g_chatApi.UM_FindUserFromIndex(m_si, item); - if (ui == nullptr) - return; - - bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - if (Chat::bDoubleClick4Privat ? bShift : !bShift) { - int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0)); - CMStringW tszName(ui->pszNick); - if (selStart == 0) - tszName.AppendChar(':'); - tszName.AppendChar(' '); - - m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString()); - PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - SetFocus(m_message.GetHwnd()); - } - else Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern HANDLE hHookSrmmEvent; - -int CSrmmBaseDialog::NotifyEvent(int code) -{ - if (m_hContact == 0 && m_hwnd == nullptr) - return -1; - - MessageWindowEventData mwe = {}; - mwe.hContact = m_hContact; - mwe.hwndWindow = m_hwnd; - mwe.uType = code; - mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH; - mwe.hwndInput = m_message.GetHwnd(); - mwe.hwndLog = m_pLog->GetHwnd(); - return ::NotifyEventHooks(hHookSrmmEvent, 0, (LPARAM)&mwe); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact) -{ - if (PasteFilesAsURL(hDrop)) - return true; - - return ::ProcessFileDrop(hDrop, hContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// If enabled pastes droped files as list of URL of file:/// type -// Can be enabled/disabled by Chat/ShiftDropFilePasteURL database parameter -// @param hDrop - Drop handle -// @return Returns true if processed here, returns false if should be processed elsewhere - -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 - return false; - - int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0); - if (fileCount == 0) - return true; - - CMStringW pasteString(L" "); - for (int i = 0; i < fileCount; i++) { - wchar_t szFilename[MAX_PATH]; - if (DragQueryFileW(hDrop, i, szFilename, _countof(szFilename))) { - CMStringW fileString(L"file:///"); - fileString.Append(szFilename); - fileString.Replace(L"%", L"%25"); - fileString.Replace(L" ", L"%20"); - fileString.Append((i != fileCount - 1) ? L"\r\n" : L" "); - pasteString += fileString; - } - } - - m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pasteString.c_str()); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::ProcessHotkeys(int key, bool isShift, bool isCtrl, bool isAlt) -{ - // Esc (close tab) - if (key == VK_ESCAPE && !isShift && !isCtrl && !isAlt) { - CloseTab(); - return true; - } - - if (isCtrl && !isAlt) { - switch (key) { - case VK_SPACE: // ctrl-space (paste clean text) - m_btnBold.Push(false); m_btnBold.Click(); - m_btnItalic.Push(false); m_btnItalic.Click(); - m_btnUnderline.Push(false); m_btnUnderline.Click(); - - m_btnColor.Push(false); m_btnColor.Click(); - m_btnBkColor.Push(false); m_btnBkColor.Click(); - return true; - - case 0x42: // ctrl-b (bold) - m_btnBold.Push(!m_btnBold.IsPushed()); - m_btnBold.Click(); - return true; - - case 0x48: // ctrl-h (history) - m_btnHistory.Click(); - return true; - - case 0x49: // ctrl-i (italics) - m_btnItalic.Push(!m_btnItalic.IsPushed()); - m_btnItalic.Click(); - return true; - - case 0x4b: // ctrl-k (text color) - m_btnColor.Push(!m_btnColor.IsPushed()); - m_btnColor.Click(); - return true; - - case 0x4c: // ctrl-l (back color) - m_btnBkColor.Push(!m_btnBkColor.IsPushed()); - m_btnBkColor.Click(); - return true; - - case 0x55: // ctrl-u (underlining) - m_btnUnderline.Push(!m_btnUnderline.IsPushed()); - m_btnUnderline.Click(); - return true; - - case VK_F4: // ctrl-F4 - CloseTab(); - return true; - } - } - - return false; -} - -void CSrmmBaseDialog::RefreshButtonStatus() -{ - if (m_si == nullptr) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR; - m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); - - if (m_si->pMI->bColor) { - bool bState = m_btnColor.IsPushed(); - if (!bState && cf.crTextColor != m_clrInputFG) - m_btnColor.Push(true); - else if (bState && cf.crTextColor == m_clrInputFG) - m_btnColor.Push(false); - } - - if (m_si->pMI->bBkgColor) { - bool bState = m_btnBkColor.IsPushed(); - if (!bState && cf.crBackColor != m_clrInputBG) - m_btnBkColor.Push(true); - else if (bState && cf.crBackColor == m_clrInputBG) - m_btnBkColor.Push(false); - } - - if (m_si->pMI->bBold) { - bool bState = m_btnBold.IsPushed(); - UINT u2 = cf.dwEffects & CFE_BOLD; - if (!bState && u2 != 0) - m_btnBold.Push(true); - else if (bState && u2 == 0) - m_btnBold.Push(false); - } - - if (m_si->pMI->bItalics) { - bool bState = m_btnItalic.IsPushed(); - UINT u2 = cf.dwEffects & CFE_ITALIC; - if (!bState && u2 != 0) - m_btnItalic.Push(true); - else if (bState && u2 == 0) - m_btnItalic.Push(false); - } - - if (m_si->pMI->bUnderline) { - bool bState = m_btnUnderline.IsPushed(); - UINT u2 = cf.dwEffects & CFE_UNDERLINE; - if (!bState && u2 != 0) - m_btnUnderline.Push(true); - else if (bState && u2 == 0) - m_btnUnderline.Push(false); - } -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "chat.h"
+#include "resource.h"
+#include "skin.h"
+#include <m_history.h>
+
+CSrmmBaseDialog::CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, SESSION_INFO *si) :
+ CDlgBase(pPlugin, idDialog),
+ timerFlash(this, 1),
+ timerType(this, 2),
+
+ m_message(this, IDC_SRMM_MESSAGE),
+ m_nickList(this, IDC_SRMM_NICKLIST),
+
+ m_btnOk(this, IDOK),
+ m_btnFilter(this, IDC_SRMM_FILTER),
+ m_btnHistory(this, IDC_SRMM_HISTORY),
+ m_btnNickList(this, IDC_SRMM_SHOWNICKLIST),
+ m_btnChannelMgr(this, IDC_SRMM_CHANMGR),
+
+ m_btnColor(this, IDC_SRMM_COLOR),
+ m_btnBkColor(this, IDC_SRMM_BKGCOLOR),
+ m_btnBold(this, IDC_SRMM_BOLD),
+
+ m_btnItalic(this, IDC_SRMM_ITALICS),
+ m_btnUnderline(this, IDC_SRMM_UNDERLINE),
+
+ m_si(si),
+ m_hContact(0),
+ m_clrInputBG(GetSysColor(COLOR_WINDOW))
+{
+ m_bFilterEnabled = db_get_b(0, CHAT_MODULE, "FilterEnabled", 0) != 0;
+ m_bNicklistEnabled = db_get_b(0, CHAT_MODULE, "ShowNicklist", 1) != 0;
+ m_iLogFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", 0x03E0);
+
+ m_btnColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_Color);
+ m_btnBkColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BkColor);
+ m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BIU);
+
+ m_btnHistory.OnClick = Callback(this, &CSrmmBaseDialog::onClick_History);
+ m_btnChannelMgr.OnClick = Callback(this, &CSrmmBaseDialog::onClick_ChanMgr);
+
+ m_nickList.OnDblClick = Callback(this, &CMsgDialog::onDblClick_List);
+
+ if (si) {
+ m_hContact = si->hContact;
+
+ if (si->pMI->bColor) {
+ m_iFG = 4;
+ m_bFGSet = true;
+ }
+ if (si->pMI->bBkgColor) {
+ m_iBG = 2;
+ m_bBGSet = true;
+ }
+ }
+}
+
+void CSrmmBaseDialog::RunUserMenu(HWND hwndOwner, USERINFO *ui, const POINT &pt)
+{
+ HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_USERMENU));
+ HMENU hSubMenu = GetSubMenu(hMenu, 0);
+ TranslateMenu(hSubMenu);
+
+ USERINFO uinew;
+ memcpy(&uinew, ui, sizeof(USERINFO));
+
+ wchar_t szTemp[50];
+ if (uinew.pszNick)
+ mir_snwprintf(szTemp, TranslateT("&Message %s"), uinew.pszNick);
+ else
+ mir_wstrncpy(szTemp, TranslateT("&Message"), _countof(szTemp) - 1);
+
+ if (mir_wstrlen(szTemp) > 40)
+ mir_wstrncpy(szTemp + 40, L"...", 4);
+ ModifyMenu(hMenu, 0, MF_STRING | MF_BYPOSITION, IDM_SENDMESSAGE, szTemp);
+
+ UINT uID = Chat_CreateMenu(hwndOwner, hSubMenu, pt, m_si, uinew.pszUID);
+ switch (uID) {
+ case 0:
+ break;
+
+ case IDM_SENDMESSAGE:
+ Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0);
+ break;
+
+ default:
+ Chat_DoEventHook(m_si, GC_USER_NICKLISTMENU, ui, nullptr, uID);
+ break;
+ }
+ DestroyMenu(hMenu);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK Srmm_ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_RBUTTONUP:
+ if (g_chatApi.bRightClickFilter) {
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ if (pDlg == nullptr)
+ break;
+
+ switch (GetDlgCtrlID(hwnd)) {
+ case IDC_SRMM_FILTER:
+ pDlg->ShowFilterMenu();
+ break;
+
+ case IDC_SRMM_COLOR:
+ pDlg->ShowColorChooser(IDC_SRMM_COLOR);
+ break;
+
+ case IDC_SRMM_BKGCOLOR:
+ pDlg->ShowColorChooser(IDC_SRMM_BKGCOLOR);
+ break;
+ }
+ }
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, Srmm_ButtonSubclassProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ if (pDlg != nullptr)
+ return pDlg->WndProc_Message(msg, wParam, lParam);
+
+ return mir_callNextSubclass(hwnd, stubMessageProc, msg, wParam, lParam);
+}
+
+LRESULT CSrmmBaseDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_SETCURSOR:
+ if (m_bInMenu) {
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ return TRUE;
+ }
+ break;
+
+ case WM_CHAR:
+ switch (wParam) {
+ case 0x02: // ctrl+B
+ if (m_btnBold.Enabled())
+ return 1;
+ break;
+ case 0x09: // ctrl+I
+ if (m_btnItalic.Enabled())
+ return 1;
+ break;
+ case 0x15: // ctrl+U
+ if (m_btnUnderline.Enabled())
+ return 1;
+ break;
+ }
+ break;
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ if (wParam == VK_BACK)
+ if (m_message.GetRichTextLength() == 0)
+ return 1;
+
+ MSG tmp = { m_hwnd, msg, wParam, lParam };
+ if (Hotkey_Check(&tmp, g_pszHotkeySection) == 100) {
+ if (!(GetWindowLongPtr(m_message.GetHwnd(), GWL_STYLE) & ES_READONLY)) {
+ PostMessage(m_hwnd, WM_COMMAND, IDOK, 0);
+ return true;
+ }
+ }
+ }
+
+ LRESULT res = mir_callNextSubclass(m_message.GetHwnd(), stubMessageProc, msg, wParam, lParam);
+ switch (msg) {
+ case WM_GETDLGCODE:
+ return res & ~DLGC_HASSETSEL;
+
+ case WM_KEYUP:
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ RefreshButtonStatus();
+ break;
+
+ case WM_KEYDOWN:
+ if ((GetKeyState(VK_CONTROL) & 0x8000) && wParam == 'V' || (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_INSERT) {
+ if (IsClipboardFormatAvailable(CF_HDROP)) {
+ m_message.SendMsg(WM_PASTE, 0, 0);
+ return 0;
+ }
+ }
+
+ __fallthrough;
+
+ case WM_SYSKEYDOWN:
+ if (!(GetKeyState(VK_RMENU) & 0x8000)) {
+ MSG message = { m_hwnd, msg, wParam, lParam };
+ LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION);
+ if (iButtonFrom) {
+ Srmm_ProcessToolbarHotkey(m_hContact, iButtonFrom, m_hwnd);
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// process mouse - hovering for the nickname list.fires events so the protocol can
+// show the userinfo - tooltip.
+
+static void ProcessNickListHovering(HWND hwnd, int hoveredItem, SESSION_INFO *parentdat)
+{
+ static int currentHovered = -1;
+ static HWND hwndToolTip = nullptr;
+ static HWND oldParent = nullptr;
+
+ if (hoveredItem == currentHovered)
+ return;
+
+ currentHovered = hoveredItem;
+
+ if (oldParent != hwnd && hwndToolTip) {
+ SendMessage(hwndToolTip, TTM_DELTOOL, 0, 0);
+ DestroyWindow(hwndToolTip);
+ hwndToolTip = nullptr;
+ }
+
+ if (hoveredItem == -1) {
+ SendMessage(hwndToolTip, TTM_ACTIVATE, 0, 0);
+ return;
+ }
+
+ bool bNewTip = false;
+ if (!hwndToolTip) {
+ bNewTip = true;
+ hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr,
+ WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, nullptr, g_plugin.getInst(), nullptr);
+ }
+
+ RECT clientRect;
+ GetClientRect(hwnd, &clientRect);
+
+ TOOLINFO ti = { sizeof(ti) };
+ ti.uFlags = TTF_SUBCLASS;
+ ti.hinst = g_plugin.getInst();
+ ti.hwnd = hwnd;
+ ti.uId = 1;
+ ti.rect = clientRect;
+
+ CMStringW wszBuf;
+
+ USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(parentdat->ptszID, parentdat->pszModule, currentHovered);
+ if (ui1) {
+ if (ProtoServiceExists(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) {
+ wchar_t *p = (wchar_t*)CallProtoService(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)parentdat->ptszID, (LPARAM)ui1->pszUID);
+ if (p != nullptr) {
+ wszBuf = p;
+ mir_free(p);
+ }
+ }
+
+ if (wszBuf.IsEmpty())
+ wszBuf.Format(L"%s: %s\r\n%s: %s\r\n%s: %s",
+ TranslateT("Nickname"), ui1->pszNick,
+ TranslateT("Unique ID"), ui1->pszUID,
+ TranslateT("Status"), g_chatApi.TM_WordToString(parentdat->pStatuses, ui1->Status));
+ ti.lpszText = wszBuf.GetBuffer();
+ }
+
+ SendMessage(hwndToolTip, bNewTip ? TTM_ADDTOOL : TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
+ SendMessage(hwndToolTip, TTM_ACTIVATE, (ti.lpszText != nullptr), 0);
+ SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 400);
+}
+
+static void CALLBACK ChatTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD)
+{
+ SESSION_INFO *si = (SESSION_INFO*)idEvent;
+
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hwnd, &pt);
+
+ uint32_t nItemUnderMouse = (uint32_t)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y));
+ if (HIWORD(nItemUnderMouse) == 1)
+ nItemUnderMouse = (uint32_t)(-1);
+ else
+ nItemUnderMouse &= 0xFFFF;
+ if (((int)nItemUnderMouse != si->currentHovered) || (nItemUnderMouse == -1)) {
+ KillTimer(hwnd, idEvent);
+ return;
+ }
+
+ USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(si->ptszID, si->pszModule, si->currentHovered);
+ if (ui1) {
+ CMStringW wszBuf;
+ if (ProtoServiceExists(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) {
+ wchar_t *p = (wchar_t*)CallProtoService(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)si->ptszID, (LPARAM)ui1->pszUID);
+ if (p) {
+ wszBuf = p;
+ mir_free(p);
+ }
+ }
+ if (wszBuf.IsEmpty())
+ wszBuf.Format(L"<b>%s:</b>\t%s\n<b>%s:</b>\t%s\n<b>%s:</b>\t%s",
+ TranslateT("Nick"), ui1->pszNick,
+ TranslateT("Unique ID"), ui1->pszUID,
+ TranslateT("Status"), g_chatApi.TM_WordToString(si->pStatuses, ui1->Status));
+
+ CLCINFOTIP ti = { sizeof(ti) };
+ Tipper_ShowTip(wszBuf, &ti);
+ si->bHasToolTip = true;
+ }
+ KillTimer(hwnd, idEvent);
+}
+
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ if (pDlg != nullptr)
+ return pDlg->WndProc_Nicklist(msg, wParam, lParam);
+
+ return mir_callNextSubclass(hwnd, stubNicklistProc, msg, wParam, lParam);
+}
+
+LRESULT CSrmmBaseDialog::WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+
+ switch (msg) {
+ case WM_MEASUREITEM:
+ {
+ MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam;
+ if (mis->CtlType == ODT_MENU)
+ return Menu_MeasureItem(lParam);
+ }
+ return FALSE;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+ if (dis->CtlType == ODT_MENU)
+ return Menu_DrawItem(lParam);
+ }
+ return FALSE;
+
+ case WM_MOUSEMOVE:
+ RECT clientRect;
+ {
+ bool bTooltipExists = ServiceExists(MS_TIPPER_HIDETIP);
+
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ GetClientRect(m_nickList.GetHwnd(), &clientRect);
+ if (PtInRect(&clientRect, pt)) {
+ // hit test item under mouse
+ uint32_t nItemUnderMouse = m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, lParam);
+ if (HIWORD(nItemUnderMouse) == 1)
+ nItemUnderMouse = (uint32_t)(-1);
+ else
+ nItemUnderMouse &= 0xFFFF;
+
+ if (bTooltipExists) {
+ if ((int)nItemUnderMouse == m_si->currentHovered)
+ break;
+ m_si->currentHovered = (int)nItemUnderMouse;
+
+ KillTimer(m_nickList.GetHwnd(), 1);
+
+ if (m_si->bHasToolTip) {
+ Tipper_Hide();
+ m_si->bHasToolTip = false;
+ }
+
+ if (nItemUnderMouse != -1)
+ SetTimer(m_nickList.GetHwnd(), (UINT_PTR)m_si, 450, ChatTimerProc);
+ }
+ else ProcessNickListHovering(m_nickList.GetHwnd(), (int)nItemUnderMouse, m_si);
+ }
+ else {
+ if (bTooltipExists) {
+ KillTimer(m_nickList.GetHwnd(), 1);
+ if (m_si->bHasToolTip) {
+ Tipper_Hide();
+ m_si->bHasToolTip = false;
+ }
+ }
+ else ProcessNickListHovering(m_nickList.GetHwnd(), -1, nullptr);
+ }
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ {
+ HDC dc = (HDC)wParam;
+ if (dc == nullptr)
+ break;
+
+ int nUsers = m_si->getUserList().getCount();
+
+ int index = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ if (index == LB_ERR || nUsers <= 0)
+ break;
+
+ int height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0);
+ if (height == LB_ERR)
+ break;
+
+ GetClientRect(m_nickList.GetHwnd(), &rc);
+
+ int items = nUsers - index;
+ if (rc.bottom - rc.top > items * height) {
+ rc.top = items * height;
+ FillRect(dc, &rc, g_chatApi.hListBkgBrush);
+ }
+ }
+ return 1;
+
+ case WM_CONTEXTMENU:
+ POINT pt;
+ {
+ int height = 0;
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ if (pt.x == -1 && pt.y == -1) {
+ int index = m_nickList.GetCurSel();
+ int top = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0);
+ pt.x = 4;
+ pt.y = (index - top)*height + 1;
+ }
+ else ScreenToClient(m_nickList.GetHwnd(), &pt);
+
+ int item = LOWORD(m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)));
+ USERINFO *ui = g_chatApi.SM_GetUserFromIndex(m_si->ptszID, m_si->pszModule, item);
+ if (ui != nullptr) {
+ if (pt.x == -1 && pt.y == -1)
+ pt.y += height - 4;
+ ClientToScreen(m_nickList.GetHwnd(), &pt);
+
+ RunUserMenu(m_nickList.GetHwnd(), ui, pt);
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ return mir_callNextSubclass(m_nickList.GetHwnd(), stubNicklistProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CSrmmBaseDialog::OnInitDialog()
+{
+ WindowList_Add(g_hWindowList, m_hwnd, m_hContact);
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
+
+ m_pLog = Srmm_GetLogWindow((CMsgDialog*)this);
+ if (m_pLog->GetType() != 0) { // custom log type
+ HWND hwndLog = GetDlgItem(m_hwnd, IDC_SRMM_LOG);
+ EnableWindow(hwndLog, FALSE);
+ ShowWindow(hwndLog, SW_HIDE);
+ }
+ m_pLog->Attach();
+
+ SetWindowLongPtr(m_message.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_message.GetHwnd(), stubMessageProc);
+ m_message.SetReadOnly(false);
+ ::DragAcceptFiles(m_message.GetHwnd(), TRUE);
+
+ if (isChat()) {
+ SetWindowLongPtr(m_nickList.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_nickList.GetHwnd(), stubNicklistProc);
+ }
+
+ // three buttons below are initiated inside this call, so button creation must precede subclassing
+ Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON);
+
+ mir_subclassWindow(m_btnFilter.GetHwnd(), Srmm_ButtonSubclassProc);
+ mir_subclassWindow(m_btnColor.GetHwnd(), Srmm_ButtonSubclassProc);
+ mir_subclassWindow(m_btnBkColor.GetHwnd(), Srmm_ButtonSubclassProc);
+
+ LoadSettings();
+ return true;
+}
+
+void CSrmmBaseDialog::OnDestroy()
+{
+ m_pLog->Detach();
+ delete m_pLog;
+
+ WindowList_Remove(g_hWindowList, m_hwnd);
+
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
+ mir_unsubclassWindow(m_message.GetHwnd(), stubMessageProc);
+ mir_unsubclassWindow(m_nickList.GetHwnd(), stubNicklistProc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CSrmmBaseDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_COMMAND:
+ if (!lParam && Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact))
+ return 0;
+
+ if (wParam >= MIN_CBUTTONID && wParam <= MAX_CBUTTONID) {
+ Srmm_ClickToolbarIcon(m_hContact, wParam, m_hwnd, 0);
+ return 0;
+ }
+ break;
+
+ case WM_ACTIVATE:
+ if (m_si && LOWORD(wParam) == WA_INACTIVE) {
+ m_si->wState &= ~GC_EVENT_HIGHLIGHT;
+ m_si->wState &= ~STATE_TALK;
+ }
+ break;
+
+ case WM_CBD_RECREATE:
+ Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON);
+ break;
+
+ case WM_NOTIFY:
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if (hdr->hwndFrom == m_pLog->GetHwnd())
+ m_pLog->Notify(wParam, lParam);
+ break;
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+}
+
+void CSrmmBaseDialog::AddLog()
+{
+ if (m_si->pLogEnd)
+ m_pLog->LogEvents(m_si->pLog, false);
+ else
+ m_pLog->Clear();
+}
+
+bool CSrmmBaseDialog::AllowTyping() const
+{
+ return isChat() ? m_si->iType != GCW_SERVER : true;
+}
+
+void CSrmmBaseDialog::ClearLog()
+{
+ m_pLog->Clear();
+}
+
+void CSrmmBaseDialog::UpdateOptions()
+{
+ MODULEINFO *mi = m_si->pMI;
+ EnableWindow(m_btnBold.GetHwnd(), mi->bBold);
+ EnableWindow(m_btnItalic.GetHwnd(), mi->bItalics);
+ EnableWindow(m_btnUnderline.GetHwnd(), mi->bUnderline);
+ EnableWindow(m_btnColor.GetHwnd(), mi->bColor);
+ EnableWindow(m_btnBkColor.GetHwnd(), mi->bBkgColor);
+ if (m_si->iType == GCW_CHATROOM)
+ EnableWindow(m_btnChannelMgr.GetHwnd(), mi->bChanMgr);
+
+ Resize();
+ RedrawLog2(m_si);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void RedrawLog2(SESSION_INFO *si)
+{
+ si->LastTime = 0;
+ if (si->pLog)
+ si->pDlg->log()->LogEvents(si->pLogEnd, TRUE);
+}
+
+static void __cdecl phase2(SESSION_INFO *si)
+{
+ Sleep(30);
+ if (si && si->pDlg)
+ RedrawLog2(si);
+}
+
+void CSrmmBaseDialog::RedrawLog()
+{
+ m_si->LastTime = 0;
+ if (m_si->pLog) {
+ LOGINFO *pLog = m_si->pLog;
+ if (m_si->iEventCount > 60) {
+ int index = 0;
+ while (index < 59) {
+ if (pLog->next == nullptr)
+ break;
+
+ pLog = pLog->next;
+ if (m_si->iType != GCW_CHATROOM || !m_bFilterEnabled || (m_iLogFilterFlags & pLog->iType) != 0)
+ index++;
+ }
+ m_pLog->LogEvents(pLog, true);
+ mir_forkThread<SESSION_INFO>(phase2, m_si);
+ }
+ else m_pLog->LogEvents(m_si->pLogEnd, true);
+ }
+ else ClearLog();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSrmmBaseDialog::onClick_Color(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwEffects = 0;
+ cf.dwMask = CFM_COLOR;
+
+ if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) {
+ if (!g_chatApi.bRightClickFilter) {
+ ShowColorChooser(pButton->GetCtrlId());
+ return;
+ }
+ if (m_bFGSet)
+ cf.crTextColor = m_iFG;
+ }
+ else cf.crTextColor = m_clrInputFG;
+
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+}
+
+void CSrmmBaseDialog::onClick_BkColor(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwEffects = 0;
+ cf.dwMask = CFM_BACKCOLOR;
+
+ if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) {
+ if (!g_chatApi.bRightClickFilter) {
+ ShowColorChooser(pButton->GetCtrlId());
+ return;
+ }
+ if (m_bBGSet)
+ cf.crBackColor = m_iBG;
+ }
+ else cf.crBackColor = m_clrInputBG;
+
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+}
+
+void CSrmmBaseDialog::onClick_BIU(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
+ cf.dwEffects = 0;
+
+ if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_BOLD))
+ cf.dwEffects |= CFE_BOLD;
+ if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_ITALICS))
+ cf.dwEffects |= CFE_ITALIC;
+ if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_UNDERLINE))
+ cf.dwEffects |= CFE_UNDERLINE;
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+}
+
+void CSrmmBaseDialog::onClick_History(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ if (m_si != nullptr)
+ ShellExecute(m_hwnd, nullptr, g_chatApi.GetChatLogsFilename(m_si, 0), nullptr, nullptr, SW_SHOW);
+ else
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, m_hContact, 0);
+}
+
+void CSrmmBaseDialog::onClick_ChanMgr(CCtrlButton *pButton)
+{
+ if (pButton->Enabled())
+ Chat_DoEventHook(m_si, GC_USER_CHANMGR, nullptr, nullptr, 0);
+}
+
+void CSrmmBaseDialog::onDblClick_List(CCtrlListBox *pList)
+{
+ TVHITTESTINFO hti;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(pList->GetHwnd(), &hti.pt);
+
+ int item = LOWORD(pList->SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y)));
+ USERINFO *ui = g_chatApi.UM_FindUserFromIndex(m_si, item);
+ if (ui == nullptr)
+ return;
+
+ bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
+ if (Chat::bDoubleClick4Privat ? bShift : !bShift) {
+ int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0));
+ CMStringW tszName(ui->pszNick);
+ if (selStart == 0)
+ tszName.AppendChar(':');
+ tszName.AppendChar(' ');
+
+ m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString());
+ PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ SetFocus(m_message.GetHwnd());
+ }
+ else Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern HANDLE hHookSrmmEvent;
+
+int CSrmmBaseDialog::NotifyEvent(int code)
+{
+ if (m_hContact == 0 && m_hwnd == nullptr)
+ return -1;
+
+ MessageWindowEventData mwe = {};
+ mwe.hContact = m_hContact;
+ mwe.hwndWindow = m_hwnd;
+ mwe.uType = code;
+ mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwe.hwndInput = m_message.GetHwnd();
+ mwe.hwndLog = m_pLog->GetHwnd();
+ return ::NotifyEventHooks(hHookSrmmEvent, 0, (LPARAM)&mwe);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact)
+{
+ if (PasteFilesAsURL(hDrop))
+ return true;
+
+ return ::ProcessFileDrop(hDrop, hContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// If enabled pastes droped files as list of URL of file:/// type
+// Can be enabled/disabled by Chat/ShiftDropFilePasteURL database parameter
+// @param hDrop - Drop handle
+// @return Returns true if processed here, returns false if should be processed elsewhere
+
+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
+ return false;
+
+ int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0);
+ if (fileCount == 0)
+ return true;
+
+ CMStringW pasteString(L" ");
+ for (int i = 0; i < fileCount; i++) {
+ wchar_t szFilename[MAX_PATH];
+ if (DragQueryFileW(hDrop, i, szFilename, _countof(szFilename))) {
+ CMStringW fileString(L"file:///");
+ fileString.Append(szFilename);
+ fileString.Replace(L"%", L"%25");
+ fileString.Replace(L" ", L"%20");
+ fileString.Append((i != fileCount - 1) ? L"\r\n" : L" ");
+ pasteString += fileString;
+ }
+ }
+
+ m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pasteString.c_str());
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CSrmmBaseDialog::ProcessHotkeys(int key, bool isShift, bool isCtrl, bool isAlt)
+{
+ // Esc (close tab)
+ if (key == VK_ESCAPE && !isShift && !isCtrl && !isAlt) {
+ CloseTab();
+ return true;
+ }
+
+ if (isCtrl && !isAlt) {
+ switch (key) {
+ case VK_SPACE: // ctrl-space (paste clean text)
+ m_btnBold.Push(false); m_btnBold.Click();
+ m_btnItalic.Push(false); m_btnItalic.Click();
+ m_btnUnderline.Push(false); m_btnUnderline.Click();
+
+ m_btnColor.Push(false); m_btnColor.Click();
+ m_btnBkColor.Push(false); m_btnBkColor.Click();
+ return true;
+
+ case 0x42: // ctrl-b (bold)
+ m_btnBold.Push(!m_btnBold.IsPushed());
+ m_btnBold.Click();
+ return true;
+
+ case 0x48: // ctrl-h (history)
+ m_btnHistory.Click();
+ return true;
+
+ case 0x49: // ctrl-i (italics)
+ m_btnItalic.Push(!m_btnItalic.IsPushed());
+ m_btnItalic.Click();
+ return true;
+
+ case 0x4b: // ctrl-k (text color)
+ m_btnColor.Push(!m_btnColor.IsPushed());
+ m_btnColor.Click();
+ return true;
+
+ case 0x4c: // ctrl-l (back color)
+ m_btnBkColor.Push(!m_btnBkColor.IsPushed());
+ m_btnBkColor.Click();
+ return true;
+
+ case 0x55: // ctrl-u (underlining)
+ m_btnUnderline.Push(!m_btnUnderline.IsPushed());
+ m_btnUnderline.Click();
+ return true;
+
+ case VK_F4: // ctrl-F4
+ CloseTab();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CSrmmBaseDialog::RefreshButtonStatus()
+{
+ if (m_si == nullptr)
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR;
+ m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+
+ if (m_si->pMI->bColor) {
+ bool bState = m_btnColor.IsPushed();
+ if (!bState && cf.crTextColor != m_clrInputFG)
+ m_btnColor.Push(true);
+ else if (bState && cf.crTextColor == m_clrInputFG)
+ m_btnColor.Push(false);
+ }
+
+ if (m_si->pMI->bBkgColor) {
+ bool bState = m_btnBkColor.IsPushed();
+ if (!bState && cf.crBackColor != m_clrInputBG)
+ m_btnBkColor.Push(true);
+ else if (bState && cf.crBackColor == m_clrInputBG)
+ m_btnBkColor.Push(false);
+ }
+
+ if (m_si->pMI->bBold) {
+ bool bState = m_btnBold.IsPushed();
+ UINT u2 = cf.dwEffects & CFE_BOLD;
+ if (!bState && u2 != 0)
+ m_btnBold.Push(true);
+ else if (bState && u2 == 0)
+ m_btnBold.Push(false);
+ }
+
+ if (m_si->pMI->bItalics) {
+ bool bState = m_btnItalic.IsPushed();
+ UINT u2 = cf.dwEffects & CFE_ITALIC;
+ if (!bState && u2 != 0)
+ m_btnItalic.Push(true);
+ else if (bState && u2 == 0)
+ m_btnItalic.Push(false);
+ }
+
+ if (m_si->pMI->bUnderline) {
+ bool bState = m_btnUnderline.IsPushed();
+ UINT u2 = cf.dwEffects & CFE_UNDERLINE;
+ if (!bState && u2 != 0)
+ m_btnUnderline.Push(true);
+ else if (bState && u2 == 0)
+ m_btnUnderline.Push(false);
+ }
+}
diff --git a/src/mir_app/src/srmm_log.cpp b/src/mir_app/src/srmm_log.cpp index bc97e37023..d60636339d 100644 --- a/src/mir_app/src/srmm_log.cpp +++ b/src/mir_app/src/srmm_log.cpp @@ -1,196 +1,196 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log container - -#include "stdafx.h" -#include "chat.h" - -struct LoggerClass -{ - LoggerClass(CMPlugin *p1, const char *p2, const wchar_t *p3, pfnSrmmLogCreator p4) : - pPlugin(p1), - szShortName(mir_strdup(p2)), - wszScreenName(mir_wstrdup(p3)), - pfnBuilder(p4) - {} - - CMPlugin *pPlugin; - ptrA szShortName; - ptrW wszScreenName; - pfnSrmmLogCreator pfnBuilder; -}; - -static OBJLIST<LoggerClass> g_arLogClasses(1, PtrKeySortT); - -static CMOption<uint8_t> g_bEnableCustomLogs("SRMM", "EnableCustomLogs", 0); - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool sttEnableCustomLogs(CMsgDialog *pDlg) -{ - // always enable custom log viewers for private chats - if (!pDlg->isChat()) - return true; - - // if custom log viewers are disable, use build-in one - if (!g_bEnableCustomLogs) - return false; - - // check if custom viewers are forbidden for this particular account - auto *szProto = Proto_GetBaseAccountName(pDlg->m_hContact); - if (szProto) { - // hidden setting !!!!!!!! - CMStringA szProtoList(db_get_sm(0, "SRMM", "DisableCustomLogsForProto")); - - int iStart = 0; - while (true) { - auto forbiddenProto = szProtoList.Tokenize(",; ", iStart); - if (forbiddenProto.IsEmpty()) - break; - - if (forbiddenProto == szProto) - return false; - } - } - - // ok-ok, use that custom viewer - return true; -} - -CSrmmLogWindow* Srmm_GetLogWindow(CMsgDialog *pDlg) -{ - if (sttEnableCustomLogs(pDlg)) { - CMStringA szViewerName(db_get_sm(pDlg->m_hContact, SRMM_MODULE, "Logger")); - if (szViewerName.IsEmpty()) - szViewerName = db_get_sm(0, "SRMM", "Logger", "built-in"); - - for (auto &it : g_arLogClasses) - if (szViewerName == it->szShortName) - return it->pfnBuilder(*pDlg); - } - - for (auto &it : g_arLogClasses) - if (!mir_strcmp(it->szShortName, "built-in")) - return it->pfnBuilder(*pDlg); - - return nullptr; // shall never happen -} - -///////////////////////////////////////////////////////////////////////////////////////// -// options dialog - -static class CSrmmLogOptionsDlg *pDialog = nullptr; - -class CSrmmLogOptionsDlg : public CDlgBase -{ - CCtrlListBox m_list; - CCtrlCheck chkCustomLogs; - -public: - CSrmmLogOptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_SRMMLOG), - m_list(this, IDC_LIST), - chkCustomLogs(this, IDC_ENABLE_CUSTOM) - { - CreateLink(chkCustomLogs, g_bEnableCustomLogs); - - m_list.OnSelChange = Callback(this, &CSrmmLogOptionsDlg::onChange_List); - } - - bool OnInitDialog() override - { - pDialog = this; - ptrA szCurr(db_get_sa(0, "SRMM", "Logger", "built-in")); - - for (auto &it : g_arLogClasses) { - int idx = m_list.AddString(TranslateW_LP(it->wszScreenName, it->pPlugin), LPARAM(it)); - if (!mir_strcmp(szCurr, it->szShortName)) - m_list.SetCurSel(idx); - } - - return true; - } - - bool OnApply() override - { - int idx = m_list.GetCurSel(); - if (idx == -1) - return false; - - auto *pLogger = (LoggerClass *)m_list.GetItemData(idx); - db_set_s(0, "SRMM", "Logger", pLogger->szShortName); - return true; - } - - void OnDestroy() override - { - pDialog = nullptr; - } - - void Rebuild() - { - m_list.ResetContent(); - OnInitDialog(); - } - - void onChange_List(CCtrlListBox *) - { - NotifyChange(); - } -}; - -void SrmmLogOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Log viewer"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CSrmmLogOptionsDlg(); - g_plugin.addOptions(wParam, &odp); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder) -{ - if (!pszShortName || !pwszScreenName || !fnBuilder) - return nullptr; - - auto *p = new LoggerClass(pPlugin, pszShortName, pwszScreenName, fnBuilder); - g_arLogClasses.insert(p); - - if (pDialog) - pDialog->Rebuild(); - return p; -} - -MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE pLogger) -{ - g_arLogClasses.remove((LoggerClass *)pLogger); - - if (pDialog) - pDialog->Rebuild(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SRMM log container
+
+#include "stdafx.h"
+#include "chat.h"
+
+struct LoggerClass
+{
+ LoggerClass(CMPlugin *p1, const char *p2, const wchar_t *p3, pfnSrmmLogCreator p4) :
+ pPlugin(p1),
+ szShortName(mir_strdup(p2)),
+ wszScreenName(mir_wstrdup(p3)),
+ pfnBuilder(p4)
+ {}
+
+ CMPlugin *pPlugin;
+ ptrA szShortName;
+ ptrW wszScreenName;
+ pfnSrmmLogCreator pfnBuilder;
+};
+
+static OBJLIST<LoggerClass> g_arLogClasses(1, PtrKeySortT);
+
+static CMOption<uint8_t> g_bEnableCustomLogs("SRMM", "EnableCustomLogs", 0);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static bool sttEnableCustomLogs(CMsgDialog *pDlg)
+{
+ // always enable custom log viewers for private chats
+ if (!pDlg->isChat())
+ return true;
+
+ // if custom log viewers are disable, use build-in one
+ if (!g_bEnableCustomLogs)
+ return false;
+
+ // check if custom viewers are forbidden for this particular account
+ auto *szProto = Proto_GetBaseAccountName(pDlg->m_hContact);
+ if (szProto) {
+ // hidden setting !!!!!!!!
+ CMStringA szProtoList(db_get_sm(0, "SRMM", "DisableCustomLogsForProto"));
+
+ int iStart = 0;
+ while (true) {
+ auto forbiddenProto = szProtoList.Tokenize(",; ", iStart);
+ if (forbiddenProto.IsEmpty())
+ break;
+
+ if (forbiddenProto == szProto)
+ return false;
+ }
+ }
+
+ // ok-ok, use that custom viewer
+ return true;
+}
+
+CSrmmLogWindow* Srmm_GetLogWindow(CMsgDialog *pDlg)
+{
+ if (sttEnableCustomLogs(pDlg)) {
+ CMStringA szViewerName(db_get_sm(pDlg->m_hContact, SRMM_MODULE, "Logger"));
+ if (szViewerName.IsEmpty())
+ szViewerName = db_get_sm(0, "SRMM", "Logger", "built-in");
+
+ for (auto &it : g_arLogClasses)
+ if (szViewerName == it->szShortName)
+ return it->pfnBuilder(*pDlg);
+ }
+
+ for (auto &it : g_arLogClasses)
+ if (!mir_strcmp(it->szShortName, "built-in"))
+ return it->pfnBuilder(*pDlg);
+
+ return nullptr; // shall never happen
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// options dialog
+
+static class CSrmmLogOptionsDlg *pDialog = nullptr;
+
+class CSrmmLogOptionsDlg : public CDlgBase
+{
+ CCtrlListBox m_list;
+ CCtrlCheck chkCustomLogs;
+
+public:
+ CSrmmLogOptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_SRMMLOG),
+ m_list(this, IDC_LIST),
+ chkCustomLogs(this, IDC_ENABLE_CUSTOM)
+ {
+ CreateLink(chkCustomLogs, g_bEnableCustomLogs);
+
+ m_list.OnSelChange = Callback(this, &CSrmmLogOptionsDlg::onChange_List);
+ }
+
+ bool OnInitDialog() override
+ {
+ pDialog = this;
+ ptrA szCurr(db_get_sa(0, "SRMM", "Logger", "built-in"));
+
+ for (auto &it : g_arLogClasses) {
+ int idx = m_list.AddString(TranslateW_LP(it->wszScreenName, it->pPlugin), LPARAM(it));
+ if (!mir_strcmp(szCurr, it->szShortName))
+ m_list.SetCurSel(idx);
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int idx = m_list.GetCurSel();
+ if (idx == -1)
+ return false;
+
+ auto *pLogger = (LoggerClass *)m_list.GetItemData(idx);
+ db_set_s(0, "SRMM", "Logger", pLogger->szShortName);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pDialog = nullptr;
+ }
+
+ void Rebuild()
+ {
+ m_list.ResetContent();
+ OnInitDialog();
+ }
+
+ void onChange_List(CCtrlListBox *)
+ {
+ NotifyChange();
+ }
+};
+
+void SrmmLogOptionsInit(WPARAM wParam)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 910000000;
+ odp.szGroup.a = LPGEN("Message sessions");
+ odp.szTitle.a = LPGEN("Log viewer");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CSrmmLogOptionsDlg();
+ g_plugin.addOptions(wParam, &odp);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder)
+{
+ if (!pszShortName || !pwszScreenName || !fnBuilder)
+ return nullptr;
+
+ auto *p = new LoggerClass(pPlugin, pszShortName, pwszScreenName, fnBuilder);
+ g_arLogClasses.insert(p);
+
+ if (pDialog)
+ pDialog->Rebuild();
+ return p;
+}
+
+MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE pLogger)
+{
+ g_arLogClasses.remove((LoggerClass *)pLogger);
+
+ if (pDialog)
+ pDialog->Rebuild();
+}
diff --git a/src/mir_app/src/srmm_log_hpp.cpp b/src/mir_app/src/srmm_log_hpp.cpp index 09fe9b3f4b..584ed539f9 100644 --- a/src/mir_app/src/srmm_log_hpp.cpp +++ b/src/mir_app/src/srmm_log_hpp.cpp @@ -1,227 +1,227 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// CHppLogWindow class - -#include "stdafx.h" -#include "chat.h" - -#define EVENTTYPE_STATUSCHANGE 25368 -#define EVENTTYPE_ERRMSG 25366 - -class CHppLogWindow : public CSrmmLogWindow -{ - HWND m_hwnd = nullptr; - -public: - CHppLogWindow(CMsgDialog &pDlg) : - CSrmmLogWindow(pDlg) - { - } - - void Attach() override - { - IEVIEWWINDOW ieWindow = {}; - ieWindow.iType = IEW_CREATE; - ieWindow.dwMode = IEWM_TABSRMM; - ieWindow.parent = m_pDlg.GetHwnd(); - ieWindow.cx = 10; - ieWindow.cy = 10; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - m_hwnd = ieWindow.hwnd; - } - - void Detach() override - { - IEVIEWWINDOW ieWindow = {}; - ieWindow.iType = IEW_DESTROY; - ieWindow.hwnd = m_hwnd; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - - ////////////////////////////////////////////////////////////////////////////////////// - - bool AtBottom() override - { - return false; - } - - void Clear() override - { - IEVIEWEVENT event = {}; - event.iType = IEE_CLEAR_LOG; - event.hwnd = m_hwnd; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - HWND GetHwnd() override - { - return m_hwnd; - } - - int GetType() override - { - return 2; - } - - wchar_t* GetSelection() override - { - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.iType = IEE_GET_SELECTION; - event.hContact = m_pDlg.m_hContact; - event.dwFlags = 0; - return (wchar_t *)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override - { - if (!bAppend) - Clear(); - - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.iType = IEE_LOG_DB_EVENTS; - event.hDbEventFirst = hDbEventFirst; - event.hContact = m_pDlg.m_hContact; - event.count = count; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - void CHppLogWindow::LogEvents(LOGINFO *pLog, bool) - { - IEVIEWEVENTDATA ied = {}; - ied.dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT; - - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.hContact = m_pDlg.m_hContact; - event.codepage = CP_ACP; - event.iType = IEE_LOG_MEM_EVENTS; - event.eventData = &ied; - event.count = 1; - - while (pLog) { - if (pLog->ptszText) { - ied.szNick.w = pLog->ptszNick; - ied.szText.w = pLog->ptszText; - ied.time = pLog->time; - ied.bIsMe = pLog->bIsMe; - - switch (pLog->iType) { - case GC_EVENT_MESSAGE: - ied.iType = IEED_GC_EVENT_MESSAGE; - ied.dwData = IEEDD_GC_SHOW_NICK; - break; - case GC_EVENT_ACTION: - ied.iType = IEED_GC_EVENT_ACTION; - break; - case GC_EVENT_JOIN: - ied.iType = IEED_GC_EVENT_JOIN; - break; - case GC_EVENT_PART: - ied.iType = IEED_GC_EVENT_PART; - break; - case GC_EVENT_QUIT: - ied.iType = IEED_GC_EVENT_QUIT; - break; - case GC_EVENT_NICK: - ied.iType = IEED_GC_EVENT_NICK; - break; - case GC_EVENT_KICK: - ied.iType = IEED_GC_EVENT_KICK; - break; - case GC_EVENT_NOTICE: - ied.iType = IEED_GC_EVENT_NOTICE; - break; - case GC_EVENT_TOPIC: - ied.iType = IEED_GC_EVENT_TOPIC; - break; - case GC_EVENT_INFORMATION: - ied.iType = IEED_GC_EVENT_INFORMATION; - break; - case GC_EVENT_ADDSTATUS: - ied.iType = IEED_GC_EVENT_ADDSTATUS; - break; - case GC_EVENT_REMOVESTATUS: - ied.iType = IEED_GC_EVENT_REMOVESTATUS; - break; - } - ied.dwData |= g_Settings->bShowTime ? IEEDD_GC_SHOW_TIME : 0; - ied.dwData |= IEEDD_GC_SHOW_ICON; - ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM) & event); - } - - pLog = pLog->prev; - } - } - - void Resize() override - { - RECT rcRichEdit; - GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rcRichEdit); - - POINT pt = { rcRichEdit.left, rcRichEdit.top }; - ScreenToClient(GetParent(m_hwnd), &pt); - - IEVIEWWINDOW ieWindow = { sizeof(ieWindow) }; - ieWindow.iType = IEW_SETPOS; - ieWindow.parent = m_hwnd; - ieWindow.hwnd = m_hwnd; - ieWindow.x = pt.x; - ieWindow.y = pt.y; - ieWindow.cx = rcRichEdit.right - rcRichEdit.left; - ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top; - if (ieWindow.cx != 0 && ieWindow.cy != 0) - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - - void ScrollToBottom() override - { - IEVIEWWINDOW iew = { sizeof(iew) }; - iew.iType = IEW_SCROLLBOTTOM; - iew.hwnd = m_hwnd; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static HANDLE hLogger; - -static CSrmmLogWindow *logBuilder(CMsgDialog &pDlg) -{ - return new CHppLogWindow(pDlg); -} - -MIR_APP_DLL(void) RegisterHppLogger() -{ - hLogger = RegisterSrmmLog(&g_plugin, "hpp", L"History++", &logBuilder); -} - -MIR_APP_DLL(void) UnregisterHppLogger() -{ - UnregisterSrmmLog(hLogger); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CHppLogWindow class
+
+#include "stdafx.h"
+#include "chat.h"
+
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_ERRMSG 25366
+
+class CHppLogWindow : public CSrmmLogWindow
+{
+ HWND m_hwnd = nullptr;
+
+public:
+ CHppLogWindow(CMsgDialog &pDlg) :
+ CSrmmLogWindow(pDlg)
+ {
+ }
+
+ void Attach() override
+ {
+ IEVIEWWINDOW ieWindow = {};
+ ieWindow.iType = IEW_CREATE;
+ ieWindow.dwMode = IEWM_TABSRMM;
+ ieWindow.parent = m_pDlg.GetHwnd();
+ ieWindow.cx = 10;
+ ieWindow.cy = 10;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ m_hwnd = ieWindow.hwnd;
+ }
+
+ void Detach() override
+ {
+ IEVIEWWINDOW ieWindow = {};
+ ieWindow.iType = IEW_DESTROY;
+ ieWindow.hwnd = m_hwnd;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ bool AtBottom() override
+ {
+ return false;
+ }
+
+ void Clear() override
+ {
+ IEVIEWEVENT event = {};
+ event.iType = IEE_CLEAR_LOG;
+ event.hwnd = m_hwnd;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+
+ HWND GetHwnd() override
+ {
+ return m_hwnd;
+ }
+
+ int GetType() override
+ {
+ return 2;
+ }
+
+ wchar_t* GetSelection() override
+ {
+ IEVIEWEVENT event = {};
+ event.hwnd = m_hwnd;
+ event.iType = IEE_GET_SELECTION;
+ event.hContact = m_pDlg.m_hContact;
+ event.dwFlags = 0;
+ return (wchar_t *)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+
+ void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override
+ {
+ if (!bAppend)
+ Clear();
+
+ IEVIEWEVENT event = {};
+ event.hwnd = m_hwnd;
+ event.iType = IEE_LOG_DB_EVENTS;
+ event.hDbEventFirst = hDbEventFirst;
+ event.hContact = m_pDlg.m_hContact;
+ event.count = count;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+
+ void CHppLogWindow::LogEvents(LOGINFO *pLog, bool)
+ {
+ IEVIEWEVENTDATA ied = {};
+ ied.dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT;
+
+ IEVIEWEVENT event = {};
+ event.hwnd = m_hwnd;
+ event.hContact = m_pDlg.m_hContact;
+ event.codepage = CP_ACP;
+ event.iType = IEE_LOG_MEM_EVENTS;
+ event.eventData = &ied;
+ event.count = 1;
+
+ while (pLog) {
+ if (pLog->ptszText) {
+ ied.szNick.w = pLog->ptszNick;
+ ied.szText.w = pLog->ptszText;
+ ied.time = pLog->time;
+ ied.bIsMe = pLog->bIsMe;
+
+ switch (pLog->iType) {
+ case GC_EVENT_MESSAGE:
+ ied.iType = IEED_GC_EVENT_MESSAGE;
+ ied.dwData = IEEDD_GC_SHOW_NICK;
+ break;
+ case GC_EVENT_ACTION:
+ ied.iType = IEED_GC_EVENT_ACTION;
+ break;
+ case GC_EVENT_JOIN:
+ ied.iType = IEED_GC_EVENT_JOIN;
+ break;
+ case GC_EVENT_PART:
+ ied.iType = IEED_GC_EVENT_PART;
+ break;
+ case GC_EVENT_QUIT:
+ ied.iType = IEED_GC_EVENT_QUIT;
+ break;
+ case GC_EVENT_NICK:
+ ied.iType = IEED_GC_EVENT_NICK;
+ break;
+ case GC_EVENT_KICK:
+ ied.iType = IEED_GC_EVENT_KICK;
+ break;
+ case GC_EVENT_NOTICE:
+ ied.iType = IEED_GC_EVENT_NOTICE;
+ break;
+ case GC_EVENT_TOPIC:
+ ied.iType = IEED_GC_EVENT_TOPIC;
+ break;
+ case GC_EVENT_INFORMATION:
+ ied.iType = IEED_GC_EVENT_INFORMATION;
+ break;
+ case GC_EVENT_ADDSTATUS:
+ ied.iType = IEED_GC_EVENT_ADDSTATUS;
+ break;
+ case GC_EVENT_REMOVESTATUS:
+ ied.iType = IEED_GC_EVENT_REMOVESTATUS;
+ break;
+ }
+ ied.dwData |= g_Settings->bShowTime ? IEEDD_GC_SHOW_TIME : 0;
+ ied.dwData |= IEEDD_GC_SHOW_ICON;
+ ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM) & event);
+ }
+
+ pLog = pLog->prev;
+ }
+ }
+
+ void Resize() override
+ {
+ RECT rcRichEdit;
+ GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rcRichEdit);
+
+ POINT pt = { rcRichEdit.left, rcRichEdit.top };
+ ScreenToClient(GetParent(m_hwnd), &pt);
+
+ IEVIEWWINDOW ieWindow = { sizeof(ieWindow) };
+ ieWindow.iType = IEW_SETPOS;
+ ieWindow.parent = m_hwnd;
+ ieWindow.hwnd = m_hwnd;
+ ieWindow.x = pt.x;
+ ieWindow.y = pt.y;
+ ieWindow.cx = rcRichEdit.right - rcRichEdit.left;
+ ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top;
+ if (ieWindow.cx != 0 && ieWindow.cy != 0)
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+
+ void ScrollToBottom() override
+ {
+ IEVIEWWINDOW iew = { sizeof(iew) };
+ iew.iType = IEW_SCROLLBOTTOM;
+ iew.hwnd = m_hwnd;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HANDLE hLogger;
+
+static CSrmmLogWindow *logBuilder(CMsgDialog &pDlg)
+{
+ return new CHppLogWindow(pDlg);
+}
+
+MIR_APP_DLL(void) RegisterHppLogger()
+{
+ hLogger = RegisterSrmmLog(&g_plugin, "hpp", L"History++", &logBuilder);
+}
+
+MIR_APP_DLL(void) UnregisterHppLogger()
+{
+ UnregisterSrmmLog(hLogger);
+}
diff --git a/src/mir_app/src/srmm_log_rtf.cpp b/src/mir_app/src/srmm_log_rtf.cpp index 01d8b7c5c8..c6bf86af64 100644 --- a/src/mir_app/src/srmm_log_rtf.cpp +++ b/src/mir_app/src/srmm_log_rtf.cpp @@ -1,408 +1,408 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log container - -#include "stdafx.h" -#include "chat.h" - -#define EVENTTYPE_STATUSCHANGE 25368 -#define EVENTTYPE_ERRMSG 25366 - -CRtfLogWindow::CRtfLogWindow(CMsgDialog &pDlg) : - CSrmmLogWindow(pDlg), - m_rtf(*(CCtrlRichEdit*)pDlg.FindControl(IDC_SRMM_LOG)) -{ -} - -CRtfLogWindow::~CRtfLogWindow() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CRtfLogWindow *pLog = (CRtfLogWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (pLog != nullptr) - return pLog->WndProc(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubLogProc, msg, wParam, lParam); -} - -void CRtfLogWindow::Attach() -{ - SetWindowLongPtr(m_rtf.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - m_rtf.SetReadOnly(true); - - mir_subclassWindow(m_rtf.GetHwnd(), stubLogProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CRtfLogWindow::Detach() -{ - mir_unsubclassWindow(m_rtf.GetHwnd(), stubLogProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CRtfLogWindow::AtBottom() -{ - if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) - return false; - - SCROLLINFO si = {}; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); - return (si.nPos + (int)si.nPage + 5) >= si.nMax; -} - -void CRtfLogWindow::Clear() -{ - m_rtf.SetText(L""); -} - -HWND CRtfLogWindow::GetHwnd() -{ - return m_rtf.GetHwnd(); -} - -int CRtfLogWindow::GetType() -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - CMStringW *str = (CMStringW *)dwCookie; - str->Append((wchar_t*)pbBuff, cb / 2); - *pcb = cb; - return 0; -} - -wchar_t* CRtfLogWindow::GetSelection() -{ - CHARRANGE sel; - SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) - return nullptr; - - CMStringW result; - - EDITSTREAM stream; - memset(&stream, 0, sizeof(stream)); - stream.pfnCallback = StreamOutCallback; - stream.dwCookie = (DWORD_PTR)&result; - SendMessage(m_rtf.GetHwnd(), EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream); - return result.Detach(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam) -{ - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->code != EN_LINK) - return FALSE; - - ENLINK *pLink = (ENLINK *)lParam; - switch (pLink->msg) { - case WM_SETCURSOR: - SetCursor(g_hCurHyperlinkHand); - SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); - return TRUE; - - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - CHARRANGE sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) - break; - - CMStringW wszText(' ', pLink->chrg.cpMax - pLink->chrg.cpMin + 1); - - TEXTRANGE tr; - tr.chrg = pLink->chrg; - tr.lpstrText = wszText.GetBuffer(); - m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (wcschr(tr.lpstrText, '@') != nullptr && wcschr(tr.lpstrText, ':') == nullptr && wcschr(tr.lpstrText, '/') == nullptr) - wszText.Insert(0, L"mailto:"); - - if (pLink->msg == WM_RBUTTONDOWN) { - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); - HMENU hSubMenu = GetSubMenu(hMenu, 6); - TranslateMenu(hSubMenu); - - POINT pt = { GET_X_LPARAM(pLink->lParam), GET_Y_LPARAM(pLink->lParam) }; - ClientToScreen(((NMHDR *)lParam)->hwndFrom, &pt); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) { - case IDM_OPENLINK: - Utils_OpenUrlW(wszText); - break; - - case IDM_COPYLINK: - Utils_ClipboardCopy(wszText); - break; - } - - DestroyMenu(hMenu); - SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); - return TRUE; - } - - Utils_OpenUrlW(wszText); - SetFocus(m_pDlg.m_message.GetHwnd()); - } - - return FALSE; -} - -void CRtfLogWindow::Resize() -{ - bool bottomScroll = !m_pDlg.isChat(); - if (AtBottom()) - bottomScroll = true; - - // ::MoveWindow(m_rtf.GetHwnd(), x, y, cx, cy, true); - - if (bottomScroll) - ScrollToBottom(); -} - -void CRtfLogWindow::ScrollToBottom() -{ - if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) - return; - - SCROLLINFO si = {}; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_RANGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); - - si.fMask = SIF_POS; - si.nPos = si.nMax - si.nPage; - SetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si, TRUE); - m_rtf.SendMsg(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n"; - -INT_PTR CRtfLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - CHARRANGE sel; - - switch (msg) { - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) { - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) { - sel.cpMin = sel.cpMax; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - } - } - break; - - case WM_SETCURSOR: - if (m_pDlg.m_bInMenu) { - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - return TRUE; - } - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (!(GetKeyState(VK_RMENU) & 0x8000)) { - MSG message = { m_pDlg.m_hwnd, msg, wParam, lParam }; - LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); - if (iButtonFrom) { - Srmm_ProcessToolbarHotkey(m_pDlg.m_hContact, iButtonFrom, m_pDlg.m_hwnd); - return TRUE; - } - } - break; - - case WM_CHAR: - if (wParam >= ' ') { - SetFocus(m_pDlg.m_message.GetHwnd()); - m_pDlg.m_message.SendMsg(WM_CHAR, wParam, lParam); - } - else if (wParam == '\t') - SetFocus(m_pDlg.m_message.GetHwnd()); - break; - - case WM_CONTEXTMENU: - POINT pt, ptl; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (lParam == 0xFFFFFFFF) { - m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); - ClientToScreen(m_rtf.GetHwnd(), &pt); - } - else { - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - } - ptl = pt; - ScreenToClient(m_rtf.GetHwnd(), &ptl); - { - wchar_t *pszWord = (wchar_t *)_alloca(8192); - pszWord[0] = '\0'; - - // get a word under cursor - if (sel.cpMin == sel.cpMax) { - int iCharIndex = m_rtf.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl); - if (iCharIndex < 0) - break; - - sel.cpMin = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex); - sel.cpMax = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex); - } - - if (sel.cpMax > sel.cpMin) { - TEXTRANGE tr = { 0 }; - tr.chrg = sel; - tr.lpstrText = pszWord; - int iRes = m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (iRes > 0) { - wchar_t *p = wcschr(pszWord, '\r'); - if (p) - *p = 0; - - size_t iLen = mir_wstrlen(pszWord) - 1; - while (wcschr(szTrimString, pszWord[iLen])) { - pszWord[iLen] = '\0'; - iLen--; - } - } - } - - CHARRANGE all = { 0, -1 }; - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_LOGMENU)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - m_pDlg.m_bInMenu = true; - - int flags = MF_BYPOSITION | (GetRichTextLength(m_rtf.GetHwnd()) == 0 ? MF_GRAYED : MF_ENABLED); - EnableMenuItem(hSubMenu, 0, flags); - EnableMenuItem(hSubMenu, 2, flags); - - if (pszWord && pszWord[0]) { - CMStringW wszText(FORMAT, TranslateT("Look up '%s':"), pszWord); - if (wszText.GetLength() > 30) { - wszText.Truncate(30); - wszText.AppendChar('\''); - } - ModifyMenu(hSubMenu, 4, MF_STRING | MF_BYPOSITION, 4, wszText); - } - else ModifyMenu(hSubMenu, 4, MF_STRING | MF_GRAYED | MF_BYPOSITION, 4, TranslateT("No word to look up")); - - UINT uID = Chat_CreateMenu(m_rtf.GetHwnd(), hSubMenu, pt, m_pDlg.m_si, nullptr); - m_pDlg.m_bInMenu = false; - DestroyMenu(hMenu); - - switch (uID) { - case 0: - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_COPYALL: - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); - m_rtf.SendMsg(WM_COPY, 0, 0); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_CLEAR: - m_rtf.SetText(L""); - if (auto *si = m_pDlg.m_si) { - g_chatApi.LM_RemoveAll(&si->pLog, &si->pLogEnd); - si->iEventCount = 0; - si->LastTime = 0; - } - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_SEARCH_GOOGLE: - case IDM_SEARCH_BING: - case IDM_SEARCH_YANDEX: - case IDM_SEARCH_YAHOO: - case IDM_SEARCH_WIKIPEDIA: - case IDM_SEARCH_FOODNETWORK: - case IDM_SEARCH_GOOGLE_MAPS: - case IDM_SEARCH_GOOGLE_TRANSLATE: - { - CMStringW szURL; - switch (uID) { - case IDM_SEARCH_WIKIPEDIA: - szURL.Format(L"http://en.wikipedia.org/wiki/%s", pszWord); - break; - case IDM_SEARCH_YAHOO: - szURL.Format(L"http://search.yahoo.com/search?p=%s&ei=UTF-8", pszWord); - break; - case IDM_SEARCH_FOODNETWORK: - szURL.Format(L"http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", pszWord); - break; - case IDM_SEARCH_BING: - szURL.Format(L"http://www.bing.com/search?q=%s&form=OSDSRC", pszWord); - break; - case IDM_SEARCH_GOOGLE_MAPS: - szURL.Format(L"http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - case IDM_SEARCH_GOOGLE_TRANSLATE: - szURL.Format(L"http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - case IDM_SEARCH_YANDEX: - szURL.Format(L"http://yandex.ru/yandsearch?text=%s", pszWord); - break; - case IDM_SEARCH_GOOGLE: - szURL.Format(L"http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - } - Utils_OpenUrlW(szURL); - } - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - default: - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - Chat_DoEventHook(m_pDlg.m_si, GC_USER_LOGMENU, nullptr, nullptr, uID); - break; - } - } - return 0; - } - - LRESULT res = mir_callNextSubclass(m_rtf.GetHwnd(), stubLogProc, msg, wParam, lParam); - if (msg == WM_GETDLGCODE) - return res & ~DLGC_HASSETSEL; - return res; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SRMM log container
+
+#include "stdafx.h"
+#include "chat.h"
+
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_ERRMSG 25366
+
+CRtfLogWindow::CRtfLogWindow(CMsgDialog &pDlg) :
+ CSrmmLogWindow(pDlg),
+ m_rtf(*(CCtrlRichEdit*)pDlg.FindControl(IDC_SRMM_LOG))
+{
+}
+
+CRtfLogWindow::~CRtfLogWindow()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CRtfLogWindow *pLog = (CRtfLogWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (pLog != nullptr)
+ return pLog->WndProc(msg, wParam, lParam);
+
+ return mir_callNextSubclass(hwnd, stubLogProc, msg, wParam, lParam);
+}
+
+void CRtfLogWindow::Attach()
+{
+ SetWindowLongPtr(m_rtf.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ m_rtf.SetReadOnly(true);
+
+ mir_subclassWindow(m_rtf.GetHwnd(), stubLogProc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CRtfLogWindow::Detach()
+{
+ mir_unsubclassWindow(m_rtf.GetHwnd(), stubLogProc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CRtfLogWindow::AtBottom()
+{
+ if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL))
+ return false;
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
+ GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si);
+ return (si.nPos + (int)si.nPage + 5) >= si.nMax;
+}
+
+void CRtfLogWindow::Clear()
+{
+ m_rtf.SetText(L"");
+}
+
+HWND CRtfLogWindow::GetHwnd()
+{
+ return m_rtf.GetHwnd();
+}
+
+int CRtfLogWindow::GetType()
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ CMStringW *str = (CMStringW *)dwCookie;
+ str->Append((wchar_t*)pbBuff, cb / 2);
+ *pcb = cb;
+ return 0;
+}
+
+wchar_t* CRtfLogWindow::GetSelection()
+{
+ CHARRANGE sel;
+ SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin == sel.cpMax)
+ return nullptr;
+
+ CMStringW result;
+
+ EDITSTREAM stream;
+ memset(&stream, 0, sizeof(stream));
+ stream.pfnCallback = StreamOutCallback;
+ stream.dwCookie = (DWORD_PTR)&result;
+ SendMessage(m_rtf.GetHwnd(), EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream);
+ return result.Detach();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam)
+{
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if (hdr->code != EN_LINK)
+ return FALSE;
+
+ ENLINK *pLink = (ENLINK *)lParam;
+ switch (pLink->msg) {
+ case WM_SETCURSOR:
+ SetCursor(g_hCurHyperlinkHand);
+ SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ CHARRANGE sel;
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin != sel.cpMax)
+ break;
+
+ CMStringW wszText(' ', pLink->chrg.cpMax - pLink->chrg.cpMin + 1);
+
+ TEXTRANGE tr;
+ tr.chrg = pLink->chrg;
+ tr.lpstrText = wszText.GetBuffer();
+ m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ if (wcschr(tr.lpstrText, '@') != nullptr && wcschr(tr.lpstrText, ':') == nullptr && wcschr(tr.lpstrText, '/') == nullptr)
+ wszText.Insert(0, L"mailto:");
+
+ if (pLink->msg == WM_RBUTTONDOWN) {
+ HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
+ HMENU hSubMenu = GetSubMenu(hMenu, 6);
+ TranslateMenu(hSubMenu);
+
+ POINT pt = { GET_X_LPARAM(pLink->lParam), GET_Y_LPARAM(pLink->lParam) };
+ ClientToScreen(((NMHDR *)lParam)->hwndFrom, &pt);
+
+ switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) {
+ case IDM_OPENLINK:
+ Utils_OpenUrlW(wszText);
+ break;
+
+ case IDM_COPYLINK:
+ Utils_ClipboardCopy(wszText);
+ break;
+ }
+
+ DestroyMenu(hMenu);
+ SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+
+ Utils_OpenUrlW(wszText);
+ SetFocus(m_pDlg.m_message.GetHwnd());
+ }
+
+ return FALSE;
+}
+
+void CRtfLogWindow::Resize()
+{
+ bool bottomScroll = !m_pDlg.isChat();
+ if (AtBottom())
+ bottomScroll = true;
+
+ // ::MoveWindow(m_rtf.GetHwnd(), x, y, cx, cy, true);
+
+ if (bottomScroll)
+ ScrollToBottom();
+}
+
+void CRtfLogWindow::ScrollToBottom()
+{
+ if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL))
+ return;
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_RANGE;
+ GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si);
+
+ si.fMask = SIF_POS;
+ si.nPos = si.nMax - si.nPage;
+ SetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si, TRUE);
+ m_rtf.SendMsg(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n";
+
+INT_PTR CRtfLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CHARRANGE sel;
+
+ switch (msg) {
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin != sel.cpMax) {
+ sel.cpMin = sel.cpMax;
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+ }
+ }
+ break;
+
+ case WM_SETCURSOR:
+ if (m_pDlg.m_bInMenu) {
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ return TRUE;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ if (!(GetKeyState(VK_RMENU) & 0x8000)) {
+ MSG message = { m_pDlg.m_hwnd, msg, wParam, lParam };
+ LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION);
+ if (iButtonFrom) {
+ Srmm_ProcessToolbarHotkey(m_pDlg.m_hContact, iButtonFrom, m_pDlg.m_hwnd);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_CHAR:
+ if (wParam >= ' ') {
+ SetFocus(m_pDlg.m_message.GetHwnd());
+ m_pDlg.m_message.SendMsg(WM_CHAR, wParam, lParam);
+ }
+ else if (wParam == '\t')
+ SetFocus(m_pDlg.m_message.GetHwnd());
+ break;
+
+ case WM_CONTEXTMENU:
+ POINT pt, ptl;
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (lParam == 0xFFFFFFFF) {
+ m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax);
+ ClientToScreen(m_rtf.GetHwnd(), &pt);
+ }
+ else {
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ }
+ ptl = pt;
+ ScreenToClient(m_rtf.GetHwnd(), &ptl);
+ {
+ wchar_t *pszWord = (wchar_t *)_alloca(8192);
+ pszWord[0] = '\0';
+
+ // get a word under cursor
+ if (sel.cpMin == sel.cpMax) {
+ int iCharIndex = m_rtf.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl);
+ if (iCharIndex < 0)
+ break;
+
+ sel.cpMin = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex);
+ sel.cpMax = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex);
+ }
+
+ if (sel.cpMax > sel.cpMin) {
+ TEXTRANGE tr = { 0 };
+ tr.chrg = sel;
+ tr.lpstrText = pszWord;
+ int iRes = m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ if (iRes > 0) {
+ wchar_t *p = wcschr(pszWord, '\r');
+ if (p)
+ *p = 0;
+
+ size_t iLen = mir_wstrlen(pszWord) - 1;
+ while (wcschr(szTrimString, pszWord[iLen])) {
+ pszWord[iLen] = '\0';
+ iLen--;
+ }
+ }
+ }
+
+ CHARRANGE all = { 0, -1 };
+ HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_LOGMENU));
+ HMENU hSubMenu = GetSubMenu(hMenu, 0);
+ TranslateMenu(hSubMenu);
+ m_pDlg.m_bInMenu = true;
+
+ int flags = MF_BYPOSITION | (GetRichTextLength(m_rtf.GetHwnd()) == 0 ? MF_GRAYED : MF_ENABLED);
+ EnableMenuItem(hSubMenu, 0, flags);
+ EnableMenuItem(hSubMenu, 2, flags);
+
+ if (pszWord && pszWord[0]) {
+ CMStringW wszText(FORMAT, TranslateT("Look up '%s':"), pszWord);
+ if (wszText.GetLength() > 30) {
+ wszText.Truncate(30);
+ wszText.AppendChar('\'');
+ }
+ ModifyMenu(hSubMenu, 4, MF_STRING | MF_BYPOSITION, 4, wszText);
+ }
+ else ModifyMenu(hSubMenu, 4, MF_STRING | MF_GRAYED | MF_BYPOSITION, 4, TranslateT("No word to look up"));
+
+ UINT uID = Chat_CreateMenu(m_rtf.GetHwnd(), hSubMenu, pt, m_pDlg.m_si, nullptr);
+ m_pDlg.m_bInMenu = false;
+ DestroyMenu(hMenu);
+
+ switch (uID) {
+ case 0:
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ case IDM_COPYALL:
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all);
+ m_rtf.SendMsg(WM_COPY, 0, 0);
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ case IDM_CLEAR:
+ m_rtf.SetText(L"");
+ if (auto *si = m_pDlg.m_si) {
+ g_chatApi.LM_RemoveAll(&si->pLog, &si->pLogEnd);
+ si->iEventCount = 0;
+ si->LastTime = 0;
+ }
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ case IDM_SEARCH_GOOGLE:
+ case IDM_SEARCH_BING:
+ case IDM_SEARCH_YANDEX:
+ case IDM_SEARCH_YAHOO:
+ case IDM_SEARCH_WIKIPEDIA:
+ case IDM_SEARCH_FOODNETWORK:
+ case IDM_SEARCH_GOOGLE_MAPS:
+ case IDM_SEARCH_GOOGLE_TRANSLATE:
+ {
+ CMStringW szURL;
+ switch (uID) {
+ case IDM_SEARCH_WIKIPEDIA:
+ szURL.Format(L"http://en.wikipedia.org/wiki/%s", pszWord);
+ break;
+ case IDM_SEARCH_YAHOO:
+ szURL.Format(L"http://search.yahoo.com/search?p=%s&ei=UTF-8", pszWord);
+ break;
+ case IDM_SEARCH_FOODNETWORK:
+ szURL.Format(L"http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", pszWord);
+ break;
+ case IDM_SEARCH_BING:
+ szURL.Format(L"http://www.bing.com/search?q=%s&form=OSDSRC", pszWord);
+ break;
+ case IDM_SEARCH_GOOGLE_MAPS:
+ szURL.Format(L"http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", pszWord);
+ break;
+ case IDM_SEARCH_GOOGLE_TRANSLATE:
+ szURL.Format(L"http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", pszWord);
+ break;
+ case IDM_SEARCH_YANDEX:
+ szURL.Format(L"http://yandex.ru/yandsearch?text=%s", pszWord);
+ break;
+ case IDM_SEARCH_GOOGLE:
+ szURL.Format(L"http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", pszWord);
+ break;
+ }
+ Utils_OpenUrlW(szURL);
+ }
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ default:
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ Chat_DoEventHook(m_pDlg.m_si, GC_USER_LOGMENU, nullptr, nullptr, uID);
+ break;
+ }
+ }
+ return 0;
+ }
+
+ LRESULT res = mir_callNextSubclass(m_rtf.GetHwnd(), stubLogProc, msg, wParam, lParam);
+ if (msg == WM_GETDLGCODE)
+ return res & ~DLGC_HASSETSEL;
+ return res;
+}
diff --git a/src/mir_app/src/srmm_statusicon.cpp b/src/mir_app/src/srmm_statusicon.cpp index 1324a30eb7..12a613610e 100644 --- a/src/mir_app/src/srmm_statusicon.cpp +++ b/src/mir_app/src/srmm_statusicon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/srmm_toolbar.cpp b/src/mir_app/src/srmm_toolbar.cpp index f833ea16f9..fb593ea1a4 100644 --- a/src/mir_app/src/srmm_toolbar.cpp +++ b/src/mir_app/src/srmm_toolbar.cpp @@ -1,886 +1,886 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "chat.h" -#include "skin.h" - -#define BB_MODULE_NAME "SRMM_Toolbar" - -#define DPISCALEY_S(argY) ((int)((double)(argY) * g_DPIscaleY)) -#define DPISCALEX_S(argX) ((int)((double)(argX) * g_DPIscaleX)) - -static double g_DPIscaleX, g_DPIscaleY; -static class CSrmmToolbarOptions *g_pDialog = nullptr; - -static CMOption<uint8_t> g_iButtonGap(BB_MODULE_NAME, "ButtonsBarGap", 1); - -static int SortButtons(const CustomButtonData *p1, const CustomButtonData *p2) -{ - if (p1->m_bRSided != p2->m_bRSided) - return (p2->m_bRSided) ? -1 : 1; - if (p1->m_dwPosition != p2->m_dwPosition) - return p1->m_dwPosition - p2->m_dwPosition; - int res = mir_strcmp(p1->m_pszModuleName, p2->m_pszModuleName); - if (res != 0) - return res; - return p1->m_dwButtonID - p2->m_dwButtonID; -} - -static LIST<CustomButtonData> arButtonsList(1, SortButtons); - -int LastCID = MIN_CBUTTONID; -int dwSepCount = 0; - -static mir_cs csToolBar; -static HANDLE hHookToolBarLoadedEvt, hHookButtonPressedEvt; - -static int sstSortButtons(const void *p1, const void *p2) -{ - return SortButtons(*(CustomButtonData**)p1, *(CustomButtonData**)p2); -} - -static void CB_RegisterSeparators() -{ - BBButton bbd = {}; - bbd.pszModuleName = "Tabsrmm_sep"; - for (int i = 0; dwSepCount > i; i++) { - bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; - bbd.dwButtonID = i + 1; - bbd.dwDefPos = 410 + i; - Srmm_AddButton(&bbd, &g_plugin); - } -} - -MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i) -{ - return arButtonsList[i]; -} - -MIR_APP_DLL(int) Srmm_GetButtonCount(void) -{ - return arButtonsList.getCount(); -} - -MIR_APP_DLL(int) Srmm_GetButtonGap() -{ - return g_iButtonGap; -} - -MIR_APP_DLL(int) Srmm_GetButtonState(HWND hwndDlg, BBButton *bbdi) -{ - if (hwndDlg == nullptr || bbdi == nullptr) - return 1; - - uint32_t tempCID = 0; - bbdi->bbbFlags = 0; - for (auto &cbd : arButtonsList) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { - tempCID = cbd->m_dwButtonCID; - break; - } - - if (!tempCID) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED); - return 0; -} - -MIR_APP_DLL(int) Srmm_SetButtonState(MCONTACT hContact, BBButton *bbdi) -{ - if (hContact == 0 || bbdi == nullptr) - return 1; - - uint32_t tempCID = 0; - for (auto &cbd : arButtonsList) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { - tempCID = cbd->m_dwButtonCID; - break; - } - - if (!tempCID) - return 1; - - HWND hwndDlg = WindowList_Find(g_hWindowList, hContact); - if (hwndDlg == nullptr) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - if (hwndBtn == nullptr) - return 1; - - SetWindowTextA(hwndBtn, bbdi->pszModuleName); - if (bbdi->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(bbdi->hIcon)); - if (bbdi->pwszTooltip) - SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)bbdi->pwszTooltip, BATF_UNICODE); - if (bbdi->bbbFlags) { - ShowWindow(hwndBtn, (bbdi->bbbFlags & BBSF_HIDDEN) ? SW_HIDE : SW_SHOW); - EnableWindow(hwndBtn, !(bbdi->bbbFlags & BBSF_DISABLED)); - Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_PUSHED) != 0); - Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_RELEASED) == 0); - } - return 0; -} - -MIR_APP_DLL(int) Srmm_ModifyButton(BBButton *bbdi) -{ - if (!bbdi) - return 1; - - CustomButtonData *cbd = nullptr; - { - mir_cslock lck(csToolBar); - - for (auto &p : arButtonsList) - if (!mir_strcmp(p->m_pszModuleName, bbdi->pszModuleName) && (p->m_dwButtonID == bbdi->dwButtonID)) { - cbd = p; - break; - } - - if (cbd != nullptr) { - if (bbdi->pwszTooltip) - cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); - if (bbdi->hIcon) - cbd->m_hIcon = bbdi->hIcon; - if (bbdi->bbbFlags) { - cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - } - } - } - - if (cbd != nullptr) - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, (LPARAM)cbd); - return 0; -} - -MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndDlg, BOOL code) -{ - bool bFromArrow = false; - HWND hwndFrom = nullptr; - - CustomButtonClickData cbcd = {}; - - for (auto &cbd : arButtonsList) { - if (cbd->m_dwButtonCID == idFrom) { - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, idFrom); - } - else if (cbd->m_dwArrowCID == idFrom) { - bFromArrow = true; - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, idFrom-1); - } - } - - if (hwndFrom == nullptr) - return; - - RECT rc; - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - cbcd.hwndFrom = GetParent(hwndFrom); - cbcd.hContact = hContact; - cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); -} - -void Srmm_ProcessToolbarHotkey(MCONTACT hContact, INT_PTR iButtonFrom, HWND hwndDlg) -{ - HWND hwndFrom = nullptr; - - CustomButtonClickData cbcd = {}; - - for (auto &cbd : arButtonsList) { - if (cbd->m_hotkey == nullptr || cbd->m_bDisabled) - continue; - - if (cbd->m_hotkey->lParam == iButtonFrom) { - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - break; - } - } - - if (hwndFrom == nullptr) - return; - - RECT rc; - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - cbcd.hwndFrom = GetParent(hwndFrom); - cbcd.hContact = hContact; - cbcd.flags = (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); -} - -MIR_APP_DLL(void) Srmm_ResetToolbar() -{ - for (auto &cbd : arButtonsList) { - cbd->m_dwPosition = cbd->m_dwOrigPosition; - cbd->m_bRSided = cbd->m_dwOrigFlags.bit1; - cbd->m_bIMButton = cbd->m_dwOrigFlags.bit2; - cbd->m_bChatButton = cbd->m_dwOrigFlags.bit3; - cbd->m_bCanBeHidden = cbd->m_dwOrigFlags.bit4; - } -} - -void Srmm_CreateToolbarIcons(HWND hwndDlg, int flags) -{ - HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hwndDlg, GWLP_HINSTANCE); - - CDlgBase *pDlg = CDlgBase::Find(hwndDlg); - - for (auto &cbd : arButtonsList) { - if (cbd->m_bSeparator) - continue; - - HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if ((flags & BBBF_ISIMBUTTON) && cbd->m_bIMButton || (flags & BBBF_ISCHATBUTTON) && cbd->m_bChatButton) { - if (hwndButton == nullptr) { - hwndButton = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, cbd->m_iButtonWidth, DPISCALEX_S(22), hwndDlg, (HMENU)cbd->m_dwButtonCID, hInstance, nullptr); - if (hwndButton == nullptr) // smth went wrong - continue; - - // if there's a pre-created button control in a class, initialize it - if (pDlg != nullptr) { - CCtrlBase *pControl = (*pDlg)[cbd->m_dwButtonCID]; - if (pControl) - pControl->OnInit(); - } - } - SendMessage(hwndButton, BUTTONSETASFLATBTN, TRUE, 0); - if (cbd->m_pwszText) - SetWindowTextW(hwndButton, cbd->m_pwszText); - if (cbd->m_pwszTooltip) - SendMessage(hwndButton, BUTTONADDTOOLTIP, LPARAM(cbd->m_pwszTooltip), BATF_UNICODE); - if (cbd->m_hIcon) - SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); - - if (cbd->m_dwArrowCID) - SendMessage(hwndButton, BUTTONSETARROW, cbd->m_dwArrowCID, 0); - if (cbd->m_bPushButton) - SendMessage(hwndButton, BUTTONSETASPUSHBTN, TRUE, 0); - - if (cbd->m_bDisabled) - EnableWindow(hwndButton, FALSE); - if (cbd->m_bHidden) - ShowWindow(hwndButton, SW_HIDE); - } - else if (hwndButton) - DestroyWindow(hwndButton); - } -} - -MIR_APP_DLL(void) Srmm_UpdateToolbarIcons(HWND hwndDlg) -{ - for (auto &cbd : arButtonsList) { - if (cbd->m_bSeparator || cbd->m_hIcon == nullptr) - continue; - - HWND hwndBtn = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if (hwndBtn) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); - } -} - -MIR_APP_DLL(void) Srmm_RedrawToolbarIcons(HWND hwndDlg) -{ - for (auto &cbd : arButtonsList) { - HWND hwnd = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if (hwnd) - InvalidateRect(hwnd, nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void CB_ReInitCustomButtons() -{ - for (auto &cbd : arButtonsList.rev_iter()) - if (cbd->m_opFlags & (BBSF_NTBSWAPED | BBSF_NTBDESTRUCT)) { - cbd->m_opFlags ^= BBSF_NTBSWAPED; - - if (cbd->m_opFlags & BBSF_NTBDESTRUCT) - arButtonsList.removeItem(&cbd); - } - - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - - WindowList_Broadcast(g_hWindowList, WM_CBD_RECREATE, 0, 0); - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); - WindowList_Broadcast(g_hWindowList, WM_CBD_LOADICONS, 0, 0); -} - -static void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd) -{ - char SettingName[1024]; - char SettingParameter[1024]; - - //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - - mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); - mir_snprintf(SettingParameter, "%d_%u_%u_%u_%u_%u", cbd->m_dwPosition, cbd->m_bIMButton, cbd->m_bChatButton, 0, cbd->m_bRSided, cbd->m_bCanBeHidden); - if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) - db_set_s(hContact, BB_MODULE_NAME, SettingName, SettingParameter); - else - db_unset(hContact, BB_MODULE_NAME, SettingName); -} - -#define MIDDLE_SEPARATOR L">-------M-------<" - -class CSrmmToolbarOptions : public CDlgBase -{ - CCtrlTreeView m_toolBar; - CCtrlCheck m_btnIM, m_btnChat, m_btnHidden; - CCtrlButton m_btnReset, m_btnSeparator; - CCtrlSpin m_gap; - CTimer timer; - - HIMAGELIST m_hImgl; - - void SaveTree() - { - bool RSide = false; - int count = 10; - uint32_t loc_sepcout = 0; - wchar_t strbuf[128]; - - TVITEMEX tvi; - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE; - tvi.hItem = m_toolBar.GetRoot(); - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - { - mir_cslock lck(csToolBar); - - while (tvi.hItem != nullptr) { - m_toolBar.GetItem(&tvi); - - if (mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) { - RSide = true; - count = m_toolBar.GetCount() * 10 - count; - tvi.hItem = m_toolBar.GetNextSibling(tvi.hItem); - continue; - } - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd && arButtonsList.indexOf(cbd) != -1) { - if (cbd->m_opFlags) { - cbd->m_bIMButton = (cbd->m_opFlags & BBSF_IMBUTTON) != 0; - cbd->m_bChatButton = (cbd->m_opFlags & BBSF_CHATBUTTON) != 0; - cbd->m_bCanBeHidden = (cbd->m_opFlags & BBSF_CANBEHIDDEN) != 0; - } - - if (RSide && !cbd->m_bRSided) { - cbd->m_bRSided = true; - cbd->m_opFlags |= BBSF_NTBSWAPED; - } - else if (!RSide && cbd->m_bRSided) { - cbd->m_bRSided = false; - cbd->m_opFlags |= BBSF_NTBSWAPED; - } - - if (!cbd->m_bCantBeHidden && !m_toolBar.GetCheckState(tvi.hItem)) { - cbd->m_bIMButton = false; - cbd->m_bChatButton = false; - - if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) - cbd->m_opFlags = BBSF_NTBDESTRUCT; - } - else { - if (!cbd->m_bIMButton && !cbd->m_bChatButton) - cbd->m_bIMButton = true; - if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) { - cbd->m_bHidden = false; - cbd->m_opFlags &= ~BBSF_NTBDESTRUCT; - ++loc_sepcout; - } - } - - cbd->m_dwPosition = (uint32_t)count; - CB_WriteButtonSettings(0, cbd); - - if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) - (RSide) ? (count -= 10) : (count += 10); - } - - HTREEITEM hItem = m_toolBar.GetNextSibling(tvi.hItem); - if (cbd->m_opFlags & BBSF_NTBDESTRUCT) - m_toolBar.DeleteItem(tvi.hItem); - tvi.hItem = hItem; - } - - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - } - db_set_dw(0, BB_MODULE_NAME, "SeparatorsCount", loc_sepcout); - dwSepCount = loc_sepcout; - } - - void BuildMenuObjectsTree() - { - m_toolBar.DeleteAllItems(); - - m_hImgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon(m_hImgl, Skin_LoadIcon(SKINICON_OTHER_SMALLDOT)); - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); - m_toolBar.SetImageList(m_hImgl, TVSIL_NORMAL); - - if (arButtonsList.getCount() == 0) - return; - - bool bPrevSide = false; - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE; - - mir_cslock lck(csToolBar); - for (auto &cbd : arButtonsList) { - if (bPrevSide != cbd->m_bRSided) { - bPrevSide = true; - - TVINSERTSTRUCT tvis2 = {}; - tvis.hInsertAfter = TVI_LAST; - tvis2.itemex.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_STATE | TVIF_STATEEX; - tvis2.itemex.pszText = MIDDLE_SEPARATOR; - tvis2.itemex.stateMask = TVIS_BOLD; - tvis2.itemex.state = TVIS_BOLD; - tvis2.itemex.iImage = tvis.item.iSelectedImage = -1; - tvis2.itemex.uStateEx = TVIS_EX_DISABLED; - tvis.hInsertAfter = m_toolBar.InsertItem(&tvis2); - m_toolBar.SetItemState(tvis.hInsertAfter, 0x3000, TVIS_STATEIMAGEMASK); - } - - tvis.item.lParam = (LPARAM)cbd; - - if (cbd->m_bSeparator) { - tvis.item.pszText = TranslateT("<Separator>"); - tvis.item.iImage = tvis.item.iSelectedImage = 0; - } - else { - tvis.item.pszText = TranslateW(cbd->m_pwszTooltip); - tvis.item.iImage = tvis.item.iSelectedImage = ImageList_AddIcon(m_hImgl, IcoLib_GetIconByHandle(cbd->m_hIcon)); - } - cbd->m_opFlags = 0; - HTREEITEM hti = m_toolBar.InsertItem(&tvis); - - m_toolBar.SetCheckState(hti, (cbd->m_bIMButton || cbd->m_bChatButton)); - if (cbd->m_bCantBeHidden) - m_toolBar.SetItemState(hti, 0x3000, TVIS_STATEIMAGEMASK); - } - } - -public: - CSrmmToolbarOptions() : - CDlgBase(g_plugin, IDD_OPT_TOOLBAR), - m_gap(this, IDC_SPIN1, 10), - m_btnIM(this, IDC_IMCHECK), - m_btnChat(this, IDC_CHATCHECK), - m_toolBar(this, IDC_TOOLBARTREE), - m_btnReset(this, IDC_BBRESET), - m_btnHidden(this, IDC_CANBEHIDDEN), - m_btnSeparator(this, IDC_SEPARATOR), - m_hImgl(nullptr), - timer(this, 1) - { - timer.OnEvent = Callback(this, &CSrmmToolbarOptions::OnTimer); - - m_toolBar.SetFlags(MTREE_DND); // enable drag-n-drop - m_toolBar.OnSelChanged = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanged); - m_toolBar.OnSelChanging = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanging); - m_toolBar.OnItemChanged = Callback(this, &CSrmmToolbarOptions::OnTreeItemChanged); - - m_btnReset.OnClick = Callback(this, &CSrmmToolbarOptions::btnResetClicked); - m_btnSeparator.OnClick = Callback(this, &CSrmmToolbarOptions::btnSeparatorClicked); - } - - bool OnInitDialog() override - { - g_pDialog = this; - BuildMenuObjectsTree(); - - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - - m_gap.SetPosition(g_iButtonGap); - return true; - } - - void OnDestroy() override - { - g_pDialog = nullptr; - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_STATE)); - } - - bool OnApply() override - { - OnTreeSelChanging(nullptr); // save latest changes - SaveTree(); // save the whole tree then - CB_ReInitCustomButtons(); - Chat_UpdateOptions(); // also restore chat windows - - uint16_t newGap = m_gap.GetPosition(); - if (newGap != g_iButtonGap) { - g_iButtonGap = newGap; - WindowList_BroadcastAsync(g_hWindowList, WM_SIZE, 0, 0); - } - - BuildMenuObjectsTree(); - - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - return true; - } - - virtual void OnReset() override - { - CB_ReInitCustomButtons(); - dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); - } - - void btnResetClicked(void*) - { - db_delete_module(0, BB_MODULE_NAME); - - Srmm_ResetToolbar(); - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - - BuildMenuObjectsTree(); - NotifyChange(); - } - - void btnSeparatorClicked(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (!hItem) - hItem = TVI_FIRST; - - BBButton bbd = {}; - bbd.pszModuleName = "Tabsrmm_sep"; - bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; - bbd.dwButtonID = ++dwSepCount; - - CustomButtonData *cbd = (CustomButtonData*)Srmm_AddButton(&bbd, &g_plugin); - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = hItem; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = TranslateT("<Separator>"); - tvis.item.iImage = tvis.item.iSelectedImage = -1; - tvis.item.lParam = (LPARAM)cbd; - hItem = m_toolBar.InsertItem(&tvis); - - m_toolBar.SetCheckState(hItem, (cbd->m_bIMButton || cbd->m_bChatButton)); - NotifyChange(); - } - - void OnTreeSelChanging(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (hItem == nullptr) - return; - - wchar_t strbuf[128]; - TVITEMEX tvi; - tvi.hItem = hItem; - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - m_toolBar.GetItem(&tvi); - - if (tvi.lParam == 0 || !m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) - return; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - cbd->m_bIMButton = m_btnIM.GetState() != 0; - cbd->m_bChatButton = m_btnChat.GetState() != 0; - cbd->m_bCanBeHidden = !cbd->m_bCantBeHidden && m_btnHidden.GetState() != 0; - cbd->m_opFlags = (cbd->m_bIMButton ? BBSF_IMBUTTON : 0) + (cbd->m_bChatButton ? BBSF_CHATBUTTON : 0) + (cbd->m_bCanBeHidden ? BBSF_CANBEHIDDEN : 0); - - if (!cbd->m_bChatButton && !cbd->m_bIMButton) - m_toolBar.SetCheckState(tvi.hItem, 0); - } - - void OnTreeSelChanged(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (hItem == nullptr) - return; - - wchar_t strbuf[128]; - TVITEMEX tvi; - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_toolBar.GetItem(&tvi); - - if (!m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) { - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - return; - } - - if (tvi.lParam == 0) - return; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - m_btnIM.Enable(); m_btnIM.SetState(cbd->m_bIMButton); - m_btnChat.Enable(); m_btnChat.SetState(cbd->m_bChatButton); - m_btnHidden.Enable(); m_btnHidden.SetState(cbd->m_bCanBeHidden); - } - - void OnTreeItemChanged(CCtrlTreeView::TEventInfo *evt) - { - bool iNewState = !m_toolBar.GetCheckState(evt->hItem); - m_btnIM.Enable(iNewState); - m_btnChat.Enable(iNewState); - m_btnHidden.Enable(iNewState); - if (iNewState) - m_btnIM.SetState(true); - } - - void OnTimer(CTimer *pTimer) - { - pTimer->Stop(); - BuildMenuObjectsTree(); - } - - static void RereadButtons() - { - if (g_pDialog) - g_pDialog->timer.Start(100); - } -}; - -void SrmmLogOptionsInit(WPARAM wParam); - -static int SrmmOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Toolbar"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CSrmmToolbarOptions(); - g_plugin.addOptions(wParam, &odp); - - ChatOptionsInit(wParam); - SrmmLogOptionsInit(wParam); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Srmm_AddButton(const BBButton *bbdi, HPLUGIN _hLang) -{ - if (bbdi == nullptr) - return nullptr; - - CustomButtonData *cbd = new CustomButtonData(); - cbd->m_pszModuleName = mir_strdup(bbdi->pszModuleName); - cbd->m_pwszText = mir_wstrdup(bbdi->pwszText); - cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); - - cbd->m_dwButtonID = bbdi->dwButtonID; - cbd->m_hIcon = bbdi->hIcon; - cbd->m_dwPosition = cbd->m_dwOrigPosition = bbdi->dwDefPos; - cbd->m_dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID; - cbd->m_dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? cbd->m_dwButtonCID + 1 : 0; - cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->m_bSeparator = (bbdi->bbbFlags & BBBF_ISSEPARATOR) != 0; - - cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - cbd->m_bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0; - cbd->m_pPlugin = _hLang; - - cbd->m_dwOrigFlags.bit1 = cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->m_dwOrigFlags.bit2 = cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->m_dwOrigFlags.bit3 = cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->m_dwOrigFlags.bit4 = cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - - if (cbd->m_bSeparator) - cbd->m_iButtonWidth = DPISCALEX_S(10); - else if (bbdi->bbbFlags & BBBF_ISARROWBUTTON) - cbd->m_iButtonWidth = DPISCALEX_S(34); - else - cbd->m_iButtonWidth = DPISCALEX_S(22); - - if (bbdi->pszHotkey) { - for (auto &p : hotkeys) { - if (!mir_strcmp(p->getName(), bbdi->pszHotkey)) { - cbd->m_hotkey = p; - break; - } - } - } - - // download database settings - char SettingName[1024]; - mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); - - DBVARIANT dbv = { 0 }; - if (!db_get_s(0, BB_MODULE_NAME, SettingName, &dbv)) { - // modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - char *token = strtok(dbv.pszVal, "_"); - cbd->m_dwPosition = (uint32_t)atoi(token); - token = strtok(nullptr, "_"); - cbd->m_bIMButton = atoi(token) != 0; - token = strtok(nullptr, "_"); - cbd->m_bChatButton = atoi(token) != 0; - token = strtok(nullptr, "_"); - token = strtok(nullptr, "_"); - cbd->m_bRSided = atoi(token) != 0; - token = strtok(nullptr, "_"); - cbd->m_bCanBeHidden = atoi(token) != 0; - - db_free(&dbv); - } - - arButtonsList.insert(cbd); - - if (cbd->m_dwButtonCID != cbd->m_dwButtonID) - LastCID++; - if (cbd->m_dwArrowCID == LastCID) - LastCID++; - - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); - CSrmmToolbarOptions::RereadButtons(); - return cbd; -} - -MIR_APP_DLL(int) Srmm_RemoveButton(BBButton *bbdi) -{ - if (!bbdi) - return 1; - - CustomButtonData *pFound = nullptr; - { - mir_cslock lck(csToolBar); - - for (auto &cbd : arButtonsList.rev_iter()) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && cbd->m_dwButtonID == bbdi->dwButtonID) - pFound = arButtonsList.removeItem(&cbd); - } - - if (pFound) { - CSrmmToolbarOptions::RereadButtons(); - WindowList_Broadcast(g_hWindowList, WM_CBD_REMOVED, pFound->m_dwButtonCID, (LPARAM)pFound); - delete pFound; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void KillModuleToolbarIcons(CMPluginBase *pPlugin) -{ - int oldCount = arButtonsList.getCount(); - - auto T = arButtonsList.rev_iter(); - for (auto &cbd : T) - if (cbd->m_pPlugin == pPlugin) - delete arButtonsList.removeItem(&cbd); - - if (oldCount != arButtonsList.getCount()) - CSrmmToolbarOptions::RereadButtons(); -} - -static INT_PTR BroadcastMessage(WPARAM, LPARAM lParam) -{ - Srmm_Broadcast((UINT)lParam, 0, 0); - return 0; -} - -static void CALLBACK SrmmLoadToolbar() -{ - NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0); - DestroyHookableEvent(hHookToolBarLoadedEvt); - - HookEvent(ME_OPT_INITIALISE, SrmmOptionsInit); -} - -static int ConvertToolbarData(const char *szSetting, void*) -{ - DBVARIANT dbv; - if (!db_get(0, "Tab" BB_MODULE_NAME, szSetting, &dbv)) { - db_set(0, BB_MODULE_NAME, szSetting, &dbv); - db_free(&dbv); - } - return 0; -} - -void LoadSrmmToolbarModule() -{ - CreateServiceFunction("SRMsg/BroadcastMessage", BroadcastMessage); - - Miranda_WaitOnHandle(SrmmLoadToolbar); - - hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED); - hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED); - - HDC hScrnDC = GetDC(nullptr); - g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0; - g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0; - ReleaseDC(nullptr, hScrnDC); - - // old data? convert them - if (db_get_dw(0, "Tab" BB_MODULE_NAME, "SeparatorsCount", -1) != -1) { - db_enum_settings(0, ConvertToolbarData, "Tab" BB_MODULE_NAME, nullptr); - db_delete_module(0, "Tab" BB_MODULE_NAME); - } - - dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); - CB_RegisterSeparators(); -} - -void UnloadSrmmToolbarModule() -{ - DestroyHookableEvent(hHookButtonPressedEvt); - - for (auto &it : arButtonsList) - delete it; - arButtonsList.destroy(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "chat.h"
+#include "skin.h"
+
+#define BB_MODULE_NAME "SRMM_Toolbar"
+
+#define DPISCALEY_S(argY) ((int)((double)(argY) * g_DPIscaleY))
+#define DPISCALEX_S(argX) ((int)((double)(argX) * g_DPIscaleX))
+
+static double g_DPIscaleX, g_DPIscaleY;
+static class CSrmmToolbarOptions *g_pDialog = nullptr;
+
+static CMOption<uint8_t> g_iButtonGap(BB_MODULE_NAME, "ButtonsBarGap", 1);
+
+static int SortButtons(const CustomButtonData *p1, const CustomButtonData *p2)
+{
+ if (p1->m_bRSided != p2->m_bRSided)
+ return (p2->m_bRSided) ? -1 : 1;
+ if (p1->m_dwPosition != p2->m_dwPosition)
+ return p1->m_dwPosition - p2->m_dwPosition;
+ int res = mir_strcmp(p1->m_pszModuleName, p2->m_pszModuleName);
+ if (res != 0)
+ return res;
+ return p1->m_dwButtonID - p2->m_dwButtonID;
+}
+
+static LIST<CustomButtonData> arButtonsList(1, SortButtons);
+
+int LastCID = MIN_CBUTTONID;
+int dwSepCount = 0;
+
+static mir_cs csToolBar;
+static HANDLE hHookToolBarLoadedEvt, hHookButtonPressedEvt;
+
+static int sstSortButtons(const void *p1, const void *p2)
+{
+ return SortButtons(*(CustomButtonData**)p1, *(CustomButtonData**)p2);
+}
+
+static void CB_RegisterSeparators()
+{
+ BBButton bbd = {};
+ bbd.pszModuleName = "Tabsrmm_sep";
+ for (int i = 0; dwSepCount > i; i++) {
+ bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON;
+ bbd.dwButtonID = i + 1;
+ bbd.dwDefPos = 410 + i;
+ Srmm_AddButton(&bbd, &g_plugin);
+ }
+}
+
+MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i)
+{
+ return arButtonsList[i];
+}
+
+MIR_APP_DLL(int) Srmm_GetButtonCount(void)
+{
+ return arButtonsList.getCount();
+}
+
+MIR_APP_DLL(int) Srmm_GetButtonGap()
+{
+ return g_iButtonGap;
+}
+
+MIR_APP_DLL(int) Srmm_GetButtonState(HWND hwndDlg, BBButton *bbdi)
+{
+ if (hwndDlg == nullptr || bbdi == nullptr)
+ return 1;
+
+ uint32_t tempCID = 0;
+ bbdi->bbbFlags = 0;
+ for (auto &cbd : arButtonsList)
+ if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) {
+ tempCID = cbd->m_dwButtonCID;
+ break;
+ }
+
+ if (!tempCID)
+ return 1;
+
+ HWND hwndBtn = GetDlgItem(hwndDlg, tempCID);
+ bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED);
+ return 0;
+}
+
+MIR_APP_DLL(int) Srmm_SetButtonState(MCONTACT hContact, BBButton *bbdi)
+{
+ if (hContact == 0 || bbdi == nullptr)
+ return 1;
+
+ uint32_t tempCID = 0;
+ for (auto &cbd : arButtonsList)
+ if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) {
+ tempCID = cbd->m_dwButtonCID;
+ break;
+ }
+
+ if (!tempCID)
+ return 1;
+
+ HWND hwndDlg = WindowList_Find(g_hWindowList, hContact);
+ if (hwndDlg == nullptr)
+ return 1;
+
+ HWND hwndBtn = GetDlgItem(hwndDlg, tempCID);
+ if (hwndBtn == nullptr)
+ return 1;
+
+ SetWindowTextA(hwndBtn, bbdi->pszModuleName);
+ if (bbdi->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(bbdi->hIcon));
+ if (bbdi->pwszTooltip)
+ SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)bbdi->pwszTooltip, BATF_UNICODE);
+ if (bbdi->bbbFlags) {
+ ShowWindow(hwndBtn, (bbdi->bbbFlags & BBSF_HIDDEN) ? SW_HIDE : SW_SHOW);
+ EnableWindow(hwndBtn, !(bbdi->bbbFlags & BBSF_DISABLED));
+ Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_PUSHED) != 0);
+ Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_RELEASED) == 0);
+ }
+ return 0;
+}
+
+MIR_APP_DLL(int) Srmm_ModifyButton(BBButton *bbdi)
+{
+ if (!bbdi)
+ return 1;
+
+ CustomButtonData *cbd = nullptr;
+ {
+ mir_cslock lck(csToolBar);
+
+ for (auto &p : arButtonsList)
+ if (!mir_strcmp(p->m_pszModuleName, bbdi->pszModuleName) && (p->m_dwButtonID == bbdi->dwButtonID)) {
+ cbd = p;
+ break;
+ }
+
+ if (cbd != nullptr) {
+ if (bbdi->pwszTooltip)
+ cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip);
+ if (bbdi->hIcon)
+ cbd->m_hIcon = bbdi->hIcon;
+ if (bbdi->bbbFlags) {
+ cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0;
+ cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0;
+ cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0;
+ cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0;
+ cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0;
+ cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0;
+ }
+ }
+ }
+
+ if (cbd != nullptr)
+ WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, (LPARAM)cbd);
+ return 0;
+}
+
+MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndDlg, BOOL code)
+{
+ bool bFromArrow = false;
+ HWND hwndFrom = nullptr;
+
+ CustomButtonClickData cbcd = {};
+
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_dwButtonCID == idFrom) {
+ cbcd.pszModule = cbd->m_pszModuleName;
+ cbcd.dwButtonId = cbd->m_dwButtonID;
+ hwndFrom = GetDlgItem(hwndDlg, idFrom);
+ }
+ else if (cbd->m_dwArrowCID == idFrom) {
+ bFromArrow = true;
+ cbcd.pszModule = cbd->m_pszModuleName;
+ cbcd.dwButtonId = cbd->m_dwButtonID;
+ hwndFrom = GetDlgItem(hwndDlg, idFrom-1);
+ }
+ }
+
+ if (hwndFrom == nullptr)
+ return;
+
+ RECT rc;
+ GetWindowRect(hwndFrom, &rc);
+ cbcd.pt.x = rc.left;
+ cbcd.pt.y = rc.bottom;
+
+ cbcd.hwndFrom = GetParent(hwndFrom);
+ cbcd.hContact = hContact;
+ cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0);
+
+ NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd);
+}
+
+void Srmm_ProcessToolbarHotkey(MCONTACT hContact, INT_PTR iButtonFrom, HWND hwndDlg)
+{
+ HWND hwndFrom = nullptr;
+
+ CustomButtonClickData cbcd = {};
+
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_hotkey == nullptr || cbd->m_bDisabled)
+ continue;
+
+ if (cbd->m_hotkey->lParam == iButtonFrom) {
+ cbcd.pszModule = cbd->m_pszModuleName;
+ cbcd.dwButtonId = cbd->m_dwButtonID;
+ hwndFrom = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ break;
+ }
+ }
+
+ if (hwndFrom == nullptr)
+ return;
+
+ RECT rc;
+ GetWindowRect(hwndFrom, &rc);
+ cbcd.pt.x = rc.left;
+ cbcd.pt.y = rc.bottom;
+
+ cbcd.hwndFrom = GetParent(hwndFrom);
+ cbcd.hContact = hContact;
+ cbcd.flags = (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0);
+
+ NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd);
+}
+
+MIR_APP_DLL(void) Srmm_ResetToolbar()
+{
+ for (auto &cbd : arButtonsList) {
+ cbd->m_dwPosition = cbd->m_dwOrigPosition;
+ cbd->m_bRSided = cbd->m_dwOrigFlags.bit1;
+ cbd->m_bIMButton = cbd->m_dwOrigFlags.bit2;
+ cbd->m_bChatButton = cbd->m_dwOrigFlags.bit3;
+ cbd->m_bCanBeHidden = cbd->m_dwOrigFlags.bit4;
+ }
+}
+
+void Srmm_CreateToolbarIcons(HWND hwndDlg, int flags)
+{
+ HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hwndDlg, GWLP_HINSTANCE);
+
+ CDlgBase *pDlg = CDlgBase::Find(hwndDlg);
+
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_bSeparator)
+ continue;
+
+ HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ if ((flags & BBBF_ISIMBUTTON) && cbd->m_bIMButton || (flags & BBBF_ISCHATBUTTON) && cbd->m_bChatButton) {
+ if (hwndButton == nullptr) {
+ hwndButton = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, cbd->m_iButtonWidth, DPISCALEX_S(22), hwndDlg, (HMENU)cbd->m_dwButtonCID, hInstance, nullptr);
+ if (hwndButton == nullptr) // smth went wrong
+ continue;
+
+ // if there's a pre-created button control in a class, initialize it
+ if (pDlg != nullptr) {
+ CCtrlBase *pControl = (*pDlg)[cbd->m_dwButtonCID];
+ if (pControl)
+ pControl->OnInit();
+ }
+ }
+ SendMessage(hwndButton, BUTTONSETASFLATBTN, TRUE, 0);
+ if (cbd->m_pwszText)
+ SetWindowTextW(hwndButton, cbd->m_pwszText);
+ if (cbd->m_pwszTooltip)
+ SendMessage(hwndButton, BUTTONADDTOOLTIP, LPARAM(cbd->m_pwszTooltip), BATF_UNICODE);
+ if (cbd->m_hIcon)
+ SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon));
+
+ if (cbd->m_dwArrowCID)
+ SendMessage(hwndButton, BUTTONSETARROW, cbd->m_dwArrowCID, 0);
+ if (cbd->m_bPushButton)
+ SendMessage(hwndButton, BUTTONSETASPUSHBTN, TRUE, 0);
+
+ if (cbd->m_bDisabled)
+ EnableWindow(hwndButton, FALSE);
+ if (cbd->m_bHidden)
+ ShowWindow(hwndButton, SW_HIDE);
+ }
+ else if (hwndButton)
+ DestroyWindow(hwndButton);
+ }
+}
+
+MIR_APP_DLL(void) Srmm_UpdateToolbarIcons(HWND hwndDlg)
+{
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_bSeparator || cbd->m_hIcon == nullptr)
+ continue;
+
+ HWND hwndBtn = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ if (hwndBtn)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon));
+ }
+}
+
+MIR_APP_DLL(void) Srmm_RedrawToolbarIcons(HWND hwndDlg)
+{
+ for (auto &cbd : arButtonsList) {
+ HWND hwnd = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ if (hwnd)
+ InvalidateRect(hwnd, nullptr, TRUE);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void CB_ReInitCustomButtons()
+{
+ for (auto &cbd : arButtonsList.rev_iter())
+ if (cbd->m_opFlags & (BBSF_NTBSWAPED | BBSF_NTBDESTRUCT)) {
+ cbd->m_opFlags ^= BBSF_NTBSWAPED;
+
+ if (cbd->m_opFlags & BBSF_NTBDESTRUCT)
+ arButtonsList.removeItem(&cbd);
+ }
+
+ qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons);
+
+ WindowList_Broadcast(g_hWindowList, WM_CBD_RECREATE, 0, 0);
+ WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0);
+ WindowList_Broadcast(g_hWindowList, WM_CBD_LOADICONS, 0, 0);
+}
+
+static void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd)
+{
+ char SettingName[1024];
+ char SettingParameter[1024];
+
+ //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden
+
+ mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID);
+ mir_snprintf(SettingParameter, "%d_%u_%u_%u_%u_%u", cbd->m_dwPosition, cbd->m_bIMButton, cbd->m_bChatButton, 0, cbd->m_bRSided, cbd->m_bCanBeHidden);
+ if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT))
+ db_set_s(hContact, BB_MODULE_NAME, SettingName, SettingParameter);
+ else
+ db_unset(hContact, BB_MODULE_NAME, SettingName);
+}
+
+#define MIDDLE_SEPARATOR L">-------M-------<"
+
+class CSrmmToolbarOptions : public CDlgBase
+{
+ CCtrlTreeView m_toolBar;
+ CCtrlCheck m_btnIM, m_btnChat, m_btnHidden;
+ CCtrlButton m_btnReset, m_btnSeparator;
+ CCtrlSpin m_gap;
+ CTimer timer;
+
+ HIMAGELIST m_hImgl;
+
+ void SaveTree()
+ {
+ bool RSide = false;
+ int count = 10;
+ uint32_t loc_sepcout = 0;
+ wchar_t strbuf[128];
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE;
+ tvi.hItem = m_toolBar.GetRoot();
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = _countof(strbuf);
+ {
+ mir_cslock lck(csToolBar);
+
+ while (tvi.hItem != nullptr) {
+ m_toolBar.GetItem(&tvi);
+
+ if (mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) {
+ RSide = true;
+ count = m_toolBar.GetCount() * 10 - count;
+ tvi.hItem = m_toolBar.GetNextSibling(tvi.hItem);
+ continue;
+ }
+ CustomButtonData *cbd = (CustomButtonData*)tvi.lParam;
+ if (cbd && arButtonsList.indexOf(cbd) != -1) {
+ if (cbd->m_opFlags) {
+ cbd->m_bIMButton = (cbd->m_opFlags & BBSF_IMBUTTON) != 0;
+ cbd->m_bChatButton = (cbd->m_opFlags & BBSF_CHATBUTTON) != 0;
+ cbd->m_bCanBeHidden = (cbd->m_opFlags & BBSF_CANBEHIDDEN) != 0;
+ }
+
+ if (RSide && !cbd->m_bRSided) {
+ cbd->m_bRSided = true;
+ cbd->m_opFlags |= BBSF_NTBSWAPED;
+ }
+ else if (!RSide && cbd->m_bRSided) {
+ cbd->m_bRSided = false;
+ cbd->m_opFlags |= BBSF_NTBSWAPED;
+ }
+
+ if (!cbd->m_bCantBeHidden && !m_toolBar.GetCheckState(tvi.hItem)) {
+ cbd->m_bIMButton = false;
+ cbd->m_bChatButton = false;
+
+ if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep"))
+ cbd->m_opFlags = BBSF_NTBDESTRUCT;
+ }
+ else {
+ if (!cbd->m_bIMButton && !cbd->m_bChatButton)
+ cbd->m_bIMButton = true;
+ if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) {
+ cbd->m_bHidden = false;
+ cbd->m_opFlags &= ~BBSF_NTBDESTRUCT;
+ ++loc_sepcout;
+ }
+ }
+
+ cbd->m_dwPosition = (uint32_t)count;
+ CB_WriteButtonSettings(0, cbd);
+
+ if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT))
+ (RSide) ? (count -= 10) : (count += 10);
+ }
+
+ HTREEITEM hItem = m_toolBar.GetNextSibling(tvi.hItem);
+ if (cbd->m_opFlags & BBSF_NTBDESTRUCT)
+ m_toolBar.DeleteItem(tvi.hItem);
+ tvi.hItem = hItem;
+ }
+
+ qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons);
+ }
+ db_set_dw(0, BB_MODULE_NAME, "SeparatorsCount", loc_sepcout);
+ dwSepCount = loc_sepcout;
+ }
+
+ void BuildMenuObjectsTree()
+ {
+ m_toolBar.DeleteAllItems();
+
+ m_hImgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2);
+ ImageList_AddIcon(m_hImgl, Skin_LoadIcon(SKINICON_OTHER_SMALLDOT));
+ ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL));
+ m_toolBar.SetImageList(m_hImgl, TVSIL_NORMAL);
+
+ if (arButtonsList.getCount() == 0)
+ return;
+
+ bool bPrevSide = false;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE;
+
+ mir_cslock lck(csToolBar);
+ for (auto &cbd : arButtonsList) {
+ if (bPrevSide != cbd->m_bRSided) {
+ bPrevSide = true;
+
+ TVINSERTSTRUCT tvis2 = {};
+ tvis.hInsertAfter = TVI_LAST;
+ tvis2.itemex.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_STATE | TVIF_STATEEX;
+ tvis2.itemex.pszText = MIDDLE_SEPARATOR;
+ tvis2.itemex.stateMask = TVIS_BOLD;
+ tvis2.itemex.state = TVIS_BOLD;
+ tvis2.itemex.iImage = tvis.item.iSelectedImage = -1;
+ tvis2.itemex.uStateEx = TVIS_EX_DISABLED;
+ tvis.hInsertAfter = m_toolBar.InsertItem(&tvis2);
+ m_toolBar.SetItemState(tvis.hInsertAfter, 0x3000, TVIS_STATEIMAGEMASK);
+ }
+
+ tvis.item.lParam = (LPARAM)cbd;
+
+ if (cbd->m_bSeparator) {
+ tvis.item.pszText = TranslateT("<Separator>");
+ tvis.item.iImage = tvis.item.iSelectedImage = 0;
+ }
+ else {
+ tvis.item.pszText = TranslateW(cbd->m_pwszTooltip);
+ tvis.item.iImage = tvis.item.iSelectedImage = ImageList_AddIcon(m_hImgl, IcoLib_GetIconByHandle(cbd->m_hIcon));
+ }
+ cbd->m_opFlags = 0;
+ HTREEITEM hti = m_toolBar.InsertItem(&tvis);
+
+ m_toolBar.SetCheckState(hti, (cbd->m_bIMButton || cbd->m_bChatButton));
+ if (cbd->m_bCantBeHidden)
+ m_toolBar.SetItemState(hti, 0x3000, TVIS_STATEIMAGEMASK);
+ }
+ }
+
+public:
+ CSrmmToolbarOptions() :
+ CDlgBase(g_plugin, IDD_OPT_TOOLBAR),
+ m_gap(this, IDC_SPIN1, 10),
+ m_btnIM(this, IDC_IMCHECK),
+ m_btnChat(this, IDC_CHATCHECK),
+ m_toolBar(this, IDC_TOOLBARTREE),
+ m_btnReset(this, IDC_BBRESET),
+ m_btnHidden(this, IDC_CANBEHIDDEN),
+ m_btnSeparator(this, IDC_SEPARATOR),
+ m_hImgl(nullptr),
+ timer(this, 1)
+ {
+ timer.OnEvent = Callback(this, &CSrmmToolbarOptions::OnTimer);
+
+ m_toolBar.SetFlags(MTREE_DND); // enable drag-n-drop
+ m_toolBar.OnSelChanged = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanged);
+ m_toolBar.OnSelChanging = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanging);
+ m_toolBar.OnItemChanged = Callback(this, &CSrmmToolbarOptions::OnTreeItemChanged);
+
+ m_btnReset.OnClick = Callback(this, &CSrmmToolbarOptions::btnResetClicked);
+ m_btnSeparator.OnClick = Callback(this, &CSrmmToolbarOptions::btnSeparatorClicked);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_pDialog = this;
+ BuildMenuObjectsTree();
+
+ m_btnIM.Disable();
+ m_btnChat.Disable();
+ m_btnHidden.Disable();
+
+ m_gap.SetPosition(g_iButtonGap);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ g_pDialog = nullptr;
+ ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL));
+ ImageList_Destroy(m_toolBar.GetImageList(TVSIL_STATE));
+ }
+
+ bool OnApply() override
+ {
+ OnTreeSelChanging(nullptr); // save latest changes
+ SaveTree(); // save the whole tree then
+ CB_ReInitCustomButtons();
+ Chat_UpdateOptions(); // also restore chat windows
+
+ uint16_t newGap = m_gap.GetPosition();
+ if (newGap != g_iButtonGap) {
+ g_iButtonGap = newGap;
+ WindowList_BroadcastAsync(g_hWindowList, WM_SIZE, 0, 0);
+ }
+
+ BuildMenuObjectsTree();
+
+ m_btnIM.Disable();
+ m_btnChat.Disable();
+ m_btnHidden.Disable();
+ return true;
+ }
+
+ virtual void OnReset() override
+ {
+ CB_ReInitCustomButtons();
+ dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0);
+ }
+
+ void btnResetClicked(void*)
+ {
+ db_delete_module(0, BB_MODULE_NAME);
+
+ Srmm_ResetToolbar();
+ qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons);
+
+ BuildMenuObjectsTree();
+ NotifyChange();
+ }
+
+ void btnSeparatorClicked(void*)
+ {
+ HTREEITEM hItem = m_toolBar.GetSelection();
+ if (!hItem)
+ hItem = TVI_FIRST;
+
+ BBButton bbd = {};
+ bbd.pszModuleName = "Tabsrmm_sep";
+ bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON;
+ bbd.dwButtonID = ++dwSepCount;
+
+ CustomButtonData *cbd = (CustomButtonData*)Srmm_AddButton(&bbd, &g_plugin);
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = hItem;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = TranslateT("<Separator>");
+ tvis.item.iImage = tvis.item.iSelectedImage = -1;
+ tvis.item.lParam = (LPARAM)cbd;
+ hItem = m_toolBar.InsertItem(&tvis);
+
+ m_toolBar.SetCheckState(hItem, (cbd->m_bIMButton || cbd->m_bChatButton));
+ NotifyChange();
+ }
+
+ void OnTreeSelChanging(void*)
+ {
+ HTREEITEM hItem = m_toolBar.GetSelection();
+ if (hItem == nullptr)
+ return;
+
+ wchar_t strbuf[128];
+ TVITEMEX tvi;
+ tvi.hItem = hItem;
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = _countof(strbuf);
+ tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM;
+ m_toolBar.GetItem(&tvi);
+
+ if (tvi.lParam == 0 || !m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR))
+ return;
+
+ CustomButtonData *cbd = (CustomButtonData*)tvi.lParam;
+ cbd->m_bIMButton = m_btnIM.GetState() != 0;
+ cbd->m_bChatButton = m_btnChat.GetState() != 0;
+ cbd->m_bCanBeHidden = !cbd->m_bCantBeHidden && m_btnHidden.GetState() != 0;
+ cbd->m_opFlags = (cbd->m_bIMButton ? BBSF_IMBUTTON : 0) + (cbd->m_bChatButton ? BBSF_CHATBUTTON : 0) + (cbd->m_bCanBeHidden ? BBSF_CANBEHIDDEN : 0);
+
+ if (!cbd->m_bChatButton && !cbd->m_bIMButton)
+ m_toolBar.SetCheckState(tvi.hItem, 0);
+ }
+
+ void OnTreeSelChanged(void*)
+ {
+ HTREEITEM hItem = m_toolBar.GetSelection();
+ if (hItem == nullptr)
+ return;
+
+ wchar_t strbuf[128];
+ TVITEMEX tvi;
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = _countof(strbuf);
+ tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_toolBar.GetItem(&tvi);
+
+ if (!m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) {
+ m_btnIM.Disable();
+ m_btnChat.Disable();
+ m_btnHidden.Disable();
+ return;
+ }
+
+ if (tvi.lParam == 0)
+ return;
+
+ CustomButtonData *cbd = (CustomButtonData*)tvi.lParam;
+ m_btnIM.Enable(); m_btnIM.SetState(cbd->m_bIMButton);
+ m_btnChat.Enable(); m_btnChat.SetState(cbd->m_bChatButton);
+ m_btnHidden.Enable(); m_btnHidden.SetState(cbd->m_bCanBeHidden);
+ }
+
+ void OnTreeItemChanged(CCtrlTreeView::TEventInfo *evt)
+ {
+ bool iNewState = !m_toolBar.GetCheckState(evt->hItem);
+ m_btnIM.Enable(iNewState);
+ m_btnChat.Enable(iNewState);
+ m_btnHidden.Enable(iNewState);
+ if (iNewState)
+ m_btnIM.SetState(true);
+ }
+
+ void OnTimer(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ BuildMenuObjectsTree();
+ }
+
+ static void RereadButtons()
+ {
+ if (g_pDialog)
+ g_pDialog->timer.Start(100);
+ }
+};
+
+void SrmmLogOptionsInit(WPARAM wParam);
+
+static int SrmmOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 910000000;
+ odp.szGroup.a = LPGEN("Message sessions");
+ odp.szTitle.a = LPGEN("Toolbar");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CSrmmToolbarOptions();
+ g_plugin.addOptions(wParam, &odp);
+
+ ChatOptionsInit(wParam);
+ SrmmLogOptionsInit(wParam);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) Srmm_AddButton(const BBButton *bbdi, HPLUGIN _hLang)
+{
+ if (bbdi == nullptr)
+ return nullptr;
+
+ CustomButtonData *cbd = new CustomButtonData();
+ cbd->m_pszModuleName = mir_strdup(bbdi->pszModuleName);
+ cbd->m_pwszText = mir_wstrdup(bbdi->pwszText);
+ cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip);
+
+ cbd->m_dwButtonID = bbdi->dwButtonID;
+ cbd->m_hIcon = bbdi->hIcon;
+ cbd->m_dwPosition = cbd->m_dwOrigPosition = bbdi->dwDefPos;
+ cbd->m_dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID;
+ cbd->m_dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? cbd->m_dwButtonCID + 1 : 0;
+ cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0;
+ cbd->m_bSeparator = (bbdi->bbbFlags & BBBF_ISSEPARATOR) != 0;
+
+ cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0;
+ cbd->m_bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0;
+ cbd->m_pPlugin = _hLang;
+
+ cbd->m_dwOrigFlags.bit1 = cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0;
+ cbd->m_dwOrigFlags.bit2 = cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0;
+ cbd->m_dwOrigFlags.bit3 = cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0;
+ cbd->m_dwOrigFlags.bit4 = cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0;
+
+ if (cbd->m_bSeparator)
+ cbd->m_iButtonWidth = DPISCALEX_S(10);
+ else if (bbdi->bbbFlags & BBBF_ISARROWBUTTON)
+ cbd->m_iButtonWidth = DPISCALEX_S(34);
+ else
+ cbd->m_iButtonWidth = DPISCALEX_S(22);
+
+ if (bbdi->pszHotkey) {
+ for (auto &p : hotkeys) {
+ if (!mir_strcmp(p->getName(), bbdi->pszHotkey)) {
+ cbd->m_hotkey = p;
+ break;
+ }
+ }
+ }
+
+ // download database settings
+ char SettingName[1024];
+ mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID);
+
+ DBVARIANT dbv = { 0 };
+ if (!db_get_s(0, BB_MODULE_NAME, SettingName, &dbv)) {
+ // modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden
+ char *token = strtok(dbv.pszVal, "_");
+ cbd->m_dwPosition = (uint32_t)atoi(token);
+ token = strtok(nullptr, "_");
+ cbd->m_bIMButton = atoi(token) != 0;
+ token = strtok(nullptr, "_");
+ cbd->m_bChatButton = atoi(token) != 0;
+ token = strtok(nullptr, "_");
+ token = strtok(nullptr, "_");
+ cbd->m_bRSided = atoi(token) != 0;
+ token = strtok(nullptr, "_");
+ cbd->m_bCanBeHidden = atoi(token) != 0;
+
+ db_free(&dbv);
+ }
+
+ arButtonsList.insert(cbd);
+
+ if (cbd->m_dwButtonCID != cbd->m_dwButtonID)
+ LastCID++;
+ if (cbd->m_dwArrowCID == LastCID)
+ LastCID++;
+
+ WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0);
+ CSrmmToolbarOptions::RereadButtons();
+ return cbd;
+}
+
+MIR_APP_DLL(int) Srmm_RemoveButton(BBButton *bbdi)
+{
+ if (!bbdi)
+ return 1;
+
+ CustomButtonData *pFound = nullptr;
+ {
+ mir_cslock lck(csToolBar);
+
+ for (auto &cbd : arButtonsList.rev_iter())
+ if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && cbd->m_dwButtonID == bbdi->dwButtonID)
+ pFound = arButtonsList.removeItem(&cbd);
+ }
+
+ if (pFound) {
+ CSrmmToolbarOptions::RereadButtons();
+ WindowList_Broadcast(g_hWindowList, WM_CBD_REMOVED, pFound->m_dwButtonCID, (LPARAM)pFound);
+ delete pFound;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void KillModuleToolbarIcons(CMPluginBase *pPlugin)
+{
+ int oldCount = arButtonsList.getCount();
+
+ auto T = arButtonsList.rev_iter();
+ for (auto &cbd : T)
+ if (cbd->m_pPlugin == pPlugin)
+ delete arButtonsList.removeItem(&cbd);
+
+ if (oldCount != arButtonsList.getCount())
+ CSrmmToolbarOptions::RereadButtons();
+}
+
+static INT_PTR BroadcastMessage(WPARAM, LPARAM lParam)
+{
+ Srmm_Broadcast((UINT)lParam, 0, 0);
+ return 0;
+}
+
+static void CALLBACK SrmmLoadToolbar()
+{
+ NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0);
+ DestroyHookableEvent(hHookToolBarLoadedEvt);
+
+ HookEvent(ME_OPT_INITIALISE, SrmmOptionsInit);
+}
+
+static int ConvertToolbarData(const char *szSetting, void*)
+{
+ DBVARIANT dbv;
+ if (!db_get(0, "Tab" BB_MODULE_NAME, szSetting, &dbv)) {
+ db_set(0, BB_MODULE_NAME, szSetting, &dbv);
+ db_free(&dbv);
+ }
+ return 0;
+}
+
+void LoadSrmmToolbarModule()
+{
+ CreateServiceFunction("SRMsg/BroadcastMessage", BroadcastMessage);
+
+ Miranda_WaitOnHandle(SrmmLoadToolbar);
+
+ hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED);
+ hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED);
+
+ HDC hScrnDC = GetDC(nullptr);
+ g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0;
+ g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0;
+ ReleaseDC(nullptr, hScrnDC);
+
+ // old data? convert them
+ if (db_get_dw(0, "Tab" BB_MODULE_NAME, "SeparatorsCount", -1) != -1) {
+ db_enum_settings(0, ConvertToolbarData, "Tab" BB_MODULE_NAME, nullptr);
+ db_delete_module(0, "Tab" BB_MODULE_NAME);
+ }
+
+ dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0);
+ CB_RegisterSeparators();
+}
+
+void UnloadSrmmToolbarModule()
+{
+ DestroyHookableEvent(hHookButtonPressedEvt);
+
+ for (auto &it : arButtonsList)
+ delete it;
+ arButtonsList.destroy();
+}
diff --git a/src/mir_app/src/srmm_util.cpp b/src/mir_app/src/srmm_util.cpp index 4f66b9ffd2..2ca887a54a 100644 --- a/src/mir_app/src/srmm_util.cpp +++ b/src/mir_app/src/srmm_util.cpp @@ -1,147 +1,147 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "chat.h" - -const char *g_pszHotkeySection; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb) -{ - LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA*)dwCookie; - if (lstrdat) { - // create the RTF - if (lstrdat->buffer == nullptr) { - lstrdat->bufferOffset = 0; - lstrdat->buffer = g_chatApi.Log_CreateRTF(lstrdat); - lstrdat->bufferLen = (int)mir_strlen(lstrdat->buffer); - } - - // give the RTF to the RE control - *pcb = min(cb, LONG(lstrdat->bufferLen - lstrdat->bufferOffset)); - memcpy(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb); - lstrdat->bufferOffset += *pcb; - - // free stuff if the streaming operation is complete - if (lstrdat->bufferOffset == lstrdat->bufferLen) { - mir_free(lstrdat->buffer); - lstrdat->buffer = nullptr; - } - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Srmm_GetWindowData(MCONTACT hContact, MessageWindowData &mwd) -{ - if (hContact == 0) - return 1; - - HWND hwnd = WindowList_Find(g_hWindowList, hContact); - if (hwnd == nullptr) - return 1; - - mwd.hwndWindow = hwnd; - mwd.pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - mwd.uState = MSG_WINDOW_STATE_EXISTS; - if (IsWindowVisible(hwnd)) - mwd.uState |= MSG_WINDOW_STATE_VISIBLE; - if (GetForegroundWindow() == hwnd) - mwd.uState |= MSG_WINDOW_STATE_FOCUS; - if (IsIconic(hwnd)) - mwd.uState |= MSG_WINDOW_STATE_ICONIC; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Srmm_Broadcast(UINT msg, WPARAM wParam, LPARAM lParam) -{ - WindowList_Broadcast(g_hWindowList, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription) -{ - g_pszHotkeySection = pszSection; - - uint16_t wHotKey = HOTKEYCODE(0, VK_RETURN); - if (db_get_b(0, SRMM_MODULE, "SendOnCtrlEnter")) - wHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_RETURN); - - if (db_get_b(0, "Tab_SRMsg", "sendonshiftenter")) - wHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_RETURN); - - HOTKEYDESC hd = { "tabsrmm_send", pszDescription, pszSection, 0, wHotKey, 0, 100 }; - Hotkey_Register(&hd, g_pChatPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact) -{ - return WindowList_Find(g_hWindowList, hContact); -} - -MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact) -{ - HWND hwnd = Srmm_FindWindow(hContact); - return (hwnd) ? (CMsgDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA) : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// serializes all thread-unsafe operation to the first thread - -struct SSTParam -{ - HWND hwnd; - const wchar_t *wszText; - HICON hIcon; -}; - -static INT_PTR CALLBACK sttSetStatusText(void *_p) -{ - SSTParam *param = (SSTParam*)_p; - - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(param->hwnd, GWLP_USERDATA); - if (pDlg != nullptr) - pDlg->SetStatusText(param->wszText, param->hIcon); - return 0; -} - -MIR_APP_DLL(void) Srmm_SetStatusText(MCONTACT hContact, const wchar_t *wszText, HICON hIcon) -{ - HWND hwnd = Srmm_FindWindow(hContact); - if (hwnd == nullptr) - hwnd = Srmm_FindWindow(db_mc_getMeta(hContact)); - if (hwnd == nullptr) - return; - - SSTParam param = { hwnd, wszText, hIcon }; - CallFunctionSync(sttSetStatusText, ¶m); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "chat.h"
+
+const char *g_pszHotkeySection;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
+{
+ LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA*)dwCookie;
+ if (lstrdat) {
+ // create the RTF
+ if (lstrdat->buffer == nullptr) {
+ lstrdat->bufferOffset = 0;
+ lstrdat->buffer = g_chatApi.Log_CreateRTF(lstrdat);
+ lstrdat->bufferLen = (int)mir_strlen(lstrdat->buffer);
+ }
+
+ // give the RTF to the RE control
+ *pcb = min(cb, LONG(lstrdat->bufferLen - lstrdat->bufferOffset));
+ memcpy(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb);
+ lstrdat->bufferOffset += *pcb;
+
+ // free stuff if the streaming operation is complete
+ if (lstrdat->bufferOffset == lstrdat->bufferLen) {
+ mir_free(lstrdat->buffer);
+ lstrdat->buffer = nullptr;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Srmm_GetWindowData(MCONTACT hContact, MessageWindowData &mwd)
+{
+ if (hContact == 0)
+ return 1;
+
+ HWND hwnd = WindowList_Find(g_hWindowList, hContact);
+ if (hwnd == nullptr)
+ return 1;
+
+ mwd.hwndWindow = hwnd;
+ mwd.pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ mwd.uState = MSG_WINDOW_STATE_EXISTS;
+ if (IsWindowVisible(hwnd))
+ mwd.uState |= MSG_WINDOW_STATE_VISIBLE;
+ if (GetForegroundWindow() == hwnd)
+ mwd.uState |= MSG_WINDOW_STATE_FOCUS;
+ if (IsIconic(hwnd))
+ mwd.uState |= MSG_WINDOW_STATE_ICONIC;
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Srmm_Broadcast(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WindowList_Broadcast(g_hWindowList, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription)
+{
+ g_pszHotkeySection = pszSection;
+
+ uint16_t wHotKey = HOTKEYCODE(0, VK_RETURN);
+ if (db_get_b(0, SRMM_MODULE, "SendOnCtrlEnter"))
+ wHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_RETURN);
+
+ if (db_get_b(0, "Tab_SRMsg", "sendonshiftenter"))
+ wHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_RETURN);
+
+ HOTKEYDESC hd = { "tabsrmm_send", pszDescription, pszSection, 0, wHotKey, 0, 100 };
+ Hotkey_Register(&hd, g_pChatPlugin);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact)
+{
+ return WindowList_Find(g_hWindowList, hContact);
+}
+
+MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact)
+{
+ HWND hwnd = Srmm_FindWindow(hContact);
+ return (hwnd) ? (CMsgDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA) : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// serializes all thread-unsafe operation to the first thread
+
+struct SSTParam
+{
+ HWND hwnd;
+ const wchar_t *wszText;
+ HICON hIcon;
+};
+
+static INT_PTR CALLBACK sttSetStatusText(void *_p)
+{
+ SSTParam *param = (SSTParam*)_p;
+
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(param->hwnd, GWLP_USERDATA);
+ if (pDlg != nullptr)
+ pDlg->SetStatusText(param->wszText, param->hIcon);
+ return 0;
+}
+
+MIR_APP_DLL(void) Srmm_SetStatusText(MCONTACT hContact, const wchar_t *wszText, HICON hIcon)
+{
+ HWND hwnd = Srmm_FindWindow(hContact);
+ if (hwnd == nullptr)
+ hwnd = Srmm_FindWindow(db_mc_getMeta(hContact));
+ if (hwnd == nullptr)
+ return;
+
+ SSTParam param = { hwnd, wszText, hIcon };
+ CallFunctionSync(sttSetStatusText, ¶m);
+}
diff --git a/src/mir_app/src/stdafx.cxx b/src/mir_app/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/mir_app/src/stdafx.cxx +++ b/src/mir_app/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/mir_app/src/stdafx.h b/src/mir_app/src/stdafx.h index e40748c95a..cbb17fac19 100644 --- a/src/mir_app/src/stdafx.h +++ b/src/mir_app/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/usedIcons.cpp b/src/mir_app/src/usedIcons.cpp index dec0672025..fc3274aa60 100644 --- a/src/mir_app/src/usedIcons.cpp +++ b/src/mir_app/src/usedIcons.cpp @@ -1,7 +1,7 @@ /*
Copyright (C) 2009 Ricardo Pescuma Domenecci
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/src/mir_app/src/usedIcons.h b/src/mir_app/src/usedIcons.h index 62c5539bd7..699f30ea7f 100644 --- a/src/mir_app/src/usedIcons.h +++ b/src/mir_app/src/usedIcons.h @@ -1,7 +1,7 @@ /*
Copyright (C) 2009 Ricardo Pescuma Domenecci
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/src/mir_app/src/userInfo.cpp b/src/mir_app/src/userInfo.cpp index deffc648a3..196ea0ae0a 100644 --- a/src/mir_app/src/userInfo.cpp +++ b/src/mir_app/src/userInfo.cpp @@ -1,45 +1,45 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// User info page base dialog - -CUserInfoPageDlg::CUserInfoPageDlg(class CMPluginBase &pPlug, int idDialog) : - CDlgBase(pPlug, idDialog) -{ - m_forceResizable = true; -} - -bool CUserInfoPageDlg::IsEmpty() const -{ - return false; -} - -bool CUserInfoPageDlg::OnRefresh() -{ - return false; -} - +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// User info page base dialog
+
+CUserInfoPageDlg::CUserInfoPageDlg(class CMPluginBase &pPlug, int idDialog) :
+ CDlgBase(pPlug, idDialog)
+{
+ m_forceResizable = true;
+}
+
+bool CUserInfoPageDlg::IsEmpty() const
+{
+ return false;
+}
+
+bool CUserInfoPageDlg::OnRefresh()
+{
+ return false;
+}
+
diff --git a/src/mir_app/src/utils.cpp b/src/mir_app/src/utils.cpp index 36260b3905..42e916d7d7 100644 --- a/src/mir_app/src/utils.cpp +++ b/src/mir_app/src/utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/visibility.cpp b/src/mir_app/src/visibility.cpp index 1aaa1421a5..8c116a2497 100644 --- a/src/mir_app/src/visibility.cpp +++ b/src/mir_app/src/visibility.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_core/res/version.rc b/src/mir_core/res/version.rc index 626717e99b..6d60274913 100644 --- a/src/mir_core/res/version.rc +++ b/src/mir_core/res/version.rc @@ -1,57 +1,57 @@ -#ifdef APSTUDIO_INVOKED -#error this file is not editable by Microsoft Visual C++ -#endif //APSTUDIO_INVOKED - -#include <windows.h> -#include <winres.h> - -#include "../include/m_version.h" - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1250) -#endif //_WIN32 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION MIRANDA_VERSION_FILEVERSION - PRODUCTVERSION MIRANDA_VERSION_FILEVERSION - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000004b0" - BEGIN - VALUE "Comments", "Licensed under the terms of the GNU General Public License\0" - VALUE "CompanyName", "Miranda NG team\0" - VALUE "FileDescription", "Miranda NG\0" - VALUE "FileVersion", MIRANDA_VERSION_DISPLAY - VALUE "InternalName", "mir_core\0" - VALUE "LegalCopyright", "Copyright 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "mir_core.mir\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Miranda NG\0" - VALUE "ProductVersion", MIRANDA_VERSION_DISPLAY - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 - END -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// +#ifdef APSTUDIO_INVOKED
+#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+#include <windows.h>
+#include <winres.h>
+
+#include "../include/m_version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1250)
+#endif //_WIN32
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MIRANDA_VERSION_FILEVERSION
+ PRODUCTVERSION MIRANDA_VERSION_FILEVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "Licensed under the terms of the GNU General Public License\0"
+ VALUE "CompanyName", "Miranda NG team\0"
+ VALUE "FileDescription", "Miranda NG\0"
+ VALUE "FileVersion", MIRANDA_VERSION_DISPLAY
+ VALUE "InternalName", "mir_core\0"
+ VALUE "LegalCopyright", "Copyright 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "mir_core.mir\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Miranda NG\0"
+ VALUE "ProductVersion", MIRANDA_VERSION_DISPLAY
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
diff --git a/src/mir_core/src/Linux/CCtrlBase.cpp b/src/mir_core/src/Linux/CCtrlBase.cpp index cba8567e52..ec84f1b77c 100644 --- a/src/mir_core/src/Linux/CCtrlBase.cpp +++ b/src/mir_core/src/Linux/CCtrlBase.cpp @@ -1,205 +1,205 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csCtrl; - -static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} - -static LIST<CCtrlBase> arControls(10, CompareControls); - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlBase - -CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : - m_parentWnd(wnd), - m_idCtrl(idCtrl) -{ - if (wnd) - wnd->AddControl(this); -} - -CCtrlBase::~CCtrlBase() -{ -} - -void CCtrlBase::OnInit() -{ - m_hwnd = nullptr; -} - -void CCtrlBase::OnDestroy() -{ - void *bullshit[2]; // vfptr + hwnd - bullshit[1] = m_hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - pCtrl->Unsubclass(); - arControls.remove(pCtrl); - } - - evas_object_del(m_hwnd); - m_hwnd = nullptr; -} - -bool CCtrlBase::OnApply() -{ - m_bChanged = false; - return true; -} - -void CCtrlBase::OnReset() -{ -} - -void CCtrlBase::Show(bool bShow) -{ - // ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); -} - -void CCtrlBase::Enable(bool bIsEnable) -{ - // ::EnableWindow(m_hwnd, bIsEnable); -} - -bool CCtrlBase::Enabled() const -{ - return (m_hwnd != nullptr); -} - -void CCtrlBase::NotifyChange() -{ - if (!m_parentWnd) - return; - - if (m_parentWnd->IsInitialized()) { - m_bChanged = true; - if (!m_bSilent) - m_parentWnd->NotifyChange(); - } - - OnChange(this); -} - -LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const -{ - // return ::SendMessage(m_hwnd, Msg, wParam, lParam); - return 0; -} - -void CCtrlBase::SetText(const wchar_t *text) -{ - // ::SetWindowText(m_hwnd, text); -} - -void CCtrlBase::SetTextA(const char *text) -{ - // ::SetWindowTextA(m_hwnd, text); -} - -void CCtrlBase::SetDraw(bool bEnable) -{ - // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CCtrlBase::SetInt(int value) -{ - wchar_t buf[32] = { 0 }; - mir_snwprintf(buf, L"%d", value); - SetText(buf); -} - -wchar_t* CCtrlBase::GetText() const -{ - return mir_wstrdup(L""); -} - -char* CCtrlBase::GetTextA() const -{ - return mir_strdup(""); -} - -char* CCtrlBase::GetTextU() const -{ - return mir_utf8encodeW(ptrW(GetText())); -} - -wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const -{ - // GetWindowTextW(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextA(char *buf, size_t size) const -{ - // GetWindowTextA(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextU(char *buf, size_t size) const -{ - ptrW wszText(GetText()); - strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); - return buf; -} - -int CCtrlBase::GetInt() const -{ - // int length = GetWindowTextLengthW(m_hwnd) + 1; - // wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); - // GetWindowTextW(m_hwnd, result, length); - // return _wtoi(result); - return 0; -} - -void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - pos.iCurr = -1; - - // if (pos.pt.x == 0 && pos.pt.y == 0) - // GetCursorPos(&pos.pt); -} - -LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) -{ - return FALSE; -} - -void CCtrlBase::Subclass() -{ - // mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); - - mir_cslock lck(csCtrl); - arControls.insert(this); -} - -void CCtrlBase::Unsubclass() -{ - // mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static mir_cs csCtrl;
+
+static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+
+static LIST<CCtrlBase> arControls(10, CompareControls);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlBase
+
+CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) :
+ m_parentWnd(wnd),
+ m_idCtrl(idCtrl)
+{
+ if (wnd)
+ wnd->AddControl(this);
+}
+
+CCtrlBase::~CCtrlBase()
+{
+}
+
+void CCtrlBase::OnInit()
+{
+ m_hwnd = nullptr;
+}
+
+void CCtrlBase::OnDestroy()
+{
+ void *bullshit[2]; // vfptr + hwnd
+ bullshit[1] = m_hwnd;
+ CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit);
+ if (pCtrl) {
+ pCtrl->Unsubclass();
+ arControls.remove(pCtrl);
+ }
+
+ evas_object_del(m_hwnd);
+ m_hwnd = nullptr;
+}
+
+bool CCtrlBase::OnApply()
+{
+ m_bChanged = false;
+ return true;
+}
+
+void CCtrlBase::OnReset()
+{
+}
+
+void CCtrlBase::Show(bool bShow)
+{
+ // ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE);
+}
+
+void CCtrlBase::Enable(bool bIsEnable)
+{
+ // ::EnableWindow(m_hwnd, bIsEnable);
+}
+
+bool CCtrlBase::Enabled() const
+{
+ return (m_hwnd != nullptr);
+}
+
+void CCtrlBase::NotifyChange()
+{
+ if (!m_parentWnd)
+ return;
+
+ if (m_parentWnd->IsInitialized()) {
+ m_bChanged = true;
+ if (!m_bSilent)
+ m_parentWnd->NotifyChange();
+ }
+
+ OnChange(this);
+}
+
+LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const
+{
+ // return ::SendMessage(m_hwnd, Msg, wParam, lParam);
+ return 0;
+}
+
+void CCtrlBase::SetText(const wchar_t *text)
+{
+ // ::SetWindowText(m_hwnd, text);
+}
+
+void CCtrlBase::SetTextA(const char *text)
+{
+ // ::SetWindowTextA(m_hwnd, text);
+}
+
+void CCtrlBase::SetDraw(bool bEnable)
+{
+ // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CCtrlBase::SetInt(int value)
+{
+ wchar_t buf[32] = { 0 };
+ mir_snwprintf(buf, L"%d", value);
+ SetText(buf);
+}
+
+wchar_t* CCtrlBase::GetText() const
+{
+ return mir_wstrdup(L"");
+}
+
+char* CCtrlBase::GetTextA() const
+{
+ return mir_strdup("");
+}
+
+char* CCtrlBase::GetTextU() const
+{
+ return mir_utf8encodeW(ptrW(GetText()));
+}
+
+wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const
+{
+ // GetWindowTextW(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextA(char *buf, size_t size) const
+{
+ // GetWindowTextA(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextU(char *buf, size_t size) const
+{
+ ptrW wszText(GetText());
+ strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE);
+ return buf;
+}
+
+int CCtrlBase::GetInt() const
+{
+ // int length = GetWindowTextLengthW(m_hwnd) + 1;
+ // wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t));
+ // GetWindowTextW(m_hwnd, result, length);
+ // return _wtoi(result);
+ return 0;
+}
+
+void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ pos.iCurr = -1;
+
+ // if (pos.pt.x == 0 && pos.pt.y == 0)
+ // GetCursorPos(&pos.pt);
+}
+
+LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM)
+{
+ return FALSE;
+}
+
+void CCtrlBase::Subclass()
+{
+ // mir_subclassWindow(m_hwnd, GlobalSubclassWndProc);
+
+ mir_cslock lck(csCtrl);
+ arControls.insert(this);
+}
+
+void CCtrlBase::Unsubclass()
+{
+ // mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc);
+}
diff --git a/src/mir_core/src/Linux/CCtrlButton.cpp b/src/mir_core/src/Linux/CCtrlButton.cpp index e8c107b888..da569730c2 100644 --- a/src/mir_core/src/Linux/CCtrlButton.cpp +++ b/src/mir_core/src/Linux/CCtrlButton.cpp @@ -1,55 +1,55 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlButton - -CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - -BOOL CCtrlButton::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - // if (idCode == BN_CLICKED) - // OnClick(this); - return FALSE; -} - -void CCtrlButton::Click() -{ - // if (Enabled()) - // ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); -} - -bool CCtrlButton::IsPushed() const -{ - // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; - return false; -} - -void CCtrlButton::Push(bool bPushed) -{ - // if (Enabled()) - // ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlButton
+
+CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
+
+BOOL CCtrlButton::OnCommand(MWindow, uint16_t, uint16_t idCode)
+{
+ // if (idCode == BN_CLICKED)
+ // OnClick(this);
+ return FALSE;
+}
+
+void CCtrlButton::Click()
+{
+ // if (Enabled())
+ // ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0);
+}
+
+bool CCtrlButton::IsPushed() const
+{
+ // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ return false;
+}
+
+void CCtrlButton::Push(bool bPushed)
+{
+ // if (Enabled())
+ // ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0);
+}
diff --git a/src/mir_core/src/Linux/CCtrlCheck.cpp b/src/mir_core/src/Linux/CCtrlCheck.cpp index 7c821f0329..112db0b66a 100644 --- a/src/mir_core/src/Linux/CCtrlCheck.cpp +++ b/src/mir_core/src/Linux/CCtrlCheck.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCheck class - -CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{ - m_bNotifiable = true; -} - -BOOL CCtrlCheck::OnCommand(MWindow, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlCheck::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetState()); - return true; -} - -void CCtrlCheck::OnReset() -{ - if (m_dbLink != nullptr) - SetState(LoadInt()); -} - -int CCtrlCheck::GetState() const -{ - // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); - return false; -} - -void CCtrlCheck::SetState(int state) -{ - // ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); -} - -bool CCtrlCheck::IsChecked() -{ - // return GetState() == BST_CHECKED; - return false; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCheck class
+
+CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlCheck::OnCommand(MWindow, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlCheck::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetState());
+ return true;
+}
+
+void CCtrlCheck::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetState(LoadInt());
+}
+
+int CCtrlCheck::GetState() const
+{
+ // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0);
+ return false;
+}
+
+void CCtrlCheck::SetState(int state)
+{
+ // ::SendMessage(m_hwnd, BM_SETCHECK, state, 0);
+}
+
+bool CCtrlCheck::IsChecked()
+{
+ // return GetState() == BST_CHECKED;
+ return false;
+}
diff --git a/src/mir_core/src/Linux/CCtrlClc.cpp b/src/mir_core/src/Linux/CCtrlClc.cpp index cf4f05cda6..57f5607ef2 100644 --- a/src/mir_core/src/Linux/CCtrlClc.cpp +++ b/src/mir_core/src/Linux/CCtrlClc.cpp @@ -1,207 +1,207 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlClc - -CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; - switch (pnmh->code) { - case CLN_EXPANDED: OnExpanded(&evt); break; - case CLN_LISTREBUILT: OnListRebuilt(&evt); break; - case CLN_ITEMCHECKED: OnItemChecked(&evt); break; - case CLN_DRAGGING: OnDragging(&evt); break; - case CLN_DROPPED: OnDropped(&evt); break; - case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; - case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; - case CLN_DRAGSTOP: OnDragStop(&evt); break; - case CLN_NEWCONTACT: OnNewContact(&evt); break; - case CLN_CONTACTMOVED: OnContactMoved(&evt); break; - case CLN_CHECKCHANGED: OnCheckChanged(&evt); break; - case NM_CLICK: OnClick(&evt); break; - } - return FALSE; -} - -void CCtrlClc::AddContact(MCONTACT hContact) -{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); -} - -void CCtrlClc::AddGroup(HANDLE hGroup) -{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); -} - -void CCtrlClc::AutoRebuild() -{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); -} - -void CCtrlClc::DeleteItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::EditLabel(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); -} - -void CCtrlClc::EndEditLabel(bool save) -{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); -} - -void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) -{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); -} - -void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) -{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); -} - -HANDLE CCtrlClc::FindContact(MCONTACT hContact) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); -} - -HANDLE CCtrlClc::FindGroup(MGROUP hGroup) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); -} - -COLORREF CCtrlClc::GetBkColor() const -{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); -} - -bool CCtrlClc::GetCheck(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; -} - -int CCtrlClc::GetCount() const -{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); -} - -HWND CCtrlClc::GetEditControl() const -{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); -} - -uint32_t CCtrlClc::GetExpand(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); -} - -int CCtrlClc::GetExtraColumns() const -{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); -} - -uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const -{ - return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); -} - -HIMAGELIST CCtrlClc::GetExtraImageList() const -{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); -} - -HFONT CCtrlClc::GetFont(int iFontId) const -{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); -} - -HANDLE CCtrlClc::GetSelection() const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); -} - -HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); -} - -void CCtrlClc::SelectItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::SetBkColor(COLORREF clBack) -{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); -} - -void CCtrlClc::SetCheck(HANDLE hItem, bool check) -{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); -} - -void CCtrlClc::SetExtraColumns(int iColumns) -{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); -} - -void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); -} - -void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); -} - -void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) -{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); -} - -void CCtrlClc::SetItemText(HANDLE hItem, char *szText) -{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); -} - -void CCtrlClc::SetHideEmptyGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); -} - -bool CCtrlClc::GetHideOfflineRoot() const -{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; -} - -void CCtrlClc::SetHideOfflineRoot(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); -} - -void CCtrlClc::SetUseGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); -} - -void CCtrlClc::SetOfflineModes(uint32_t modes) -{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); -} - -uint32_t CCtrlClc::GetExStyle() const -{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); -} - -void CCtrlClc::SetExStyle(uint32_t exStyle) -{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); -} - -HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) -{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); -} - -int CCtrlClc::GetItemType(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); -} - -HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlClc
+
+CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh };
+ switch (pnmh->code) {
+ case CLN_EXPANDED: OnExpanded(&evt); break;
+ case CLN_LISTREBUILT: OnListRebuilt(&evt); break;
+ case CLN_ITEMCHECKED: OnItemChecked(&evt); break;
+ case CLN_DRAGGING: OnDragging(&evt); break;
+ case CLN_DROPPED: OnDropped(&evt); break;
+ case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break;
+ case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break;
+ case CLN_DRAGSTOP: OnDragStop(&evt); break;
+ case CLN_NEWCONTACT: OnNewContact(&evt); break;
+ case CLN_CONTACTMOVED: OnContactMoved(&evt); break;
+ case CLN_CHECKCHANGED: OnCheckChanged(&evt); break;
+ case NM_CLICK: OnClick(&evt); break;
+ }
+ return FALSE;
+}
+
+void CCtrlClc::AddContact(MCONTACT hContact)
+{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0);
+}
+
+void CCtrlClc::AddGroup(HANDLE hGroup)
+{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0);
+}
+
+void CCtrlClc::AutoRebuild()
+{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0);
+}
+
+void CCtrlClc::DeleteItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EditLabel(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EndEditLabel(bool save)
+{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0);
+}
+
+void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk)
+{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE);
+}
+
+void CCtrlClc::Expand(HANDLE hItem, uint32_t flags)
+{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags);
+}
+
+HANDLE CCtrlClc::FindContact(MCONTACT hContact)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0);
+}
+
+HANDLE CCtrlClc::FindGroup(MGROUP hGroup)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0);
+}
+
+COLORREF CCtrlClc::GetBkColor() const
+{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0);
+}
+
+bool CCtrlClc::GetCheck(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false;
+}
+
+int CCtrlClc::GetCount() const
+{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0);
+}
+
+HWND CCtrlClc::GetEditControl() const
+{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0);
+}
+
+uint32_t CCtrlClc::GetExpand(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0);
+}
+
+int CCtrlClc::GetExtraColumns() const
+{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0);
+}
+
+uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const
+{
+ return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF);
+}
+
+HIMAGELIST CCtrlClc::GetExtraImageList() const
+{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0);
+}
+
+HFONT CCtrlClc::GetFont(int iFontId) const
+{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0);
+}
+
+HANDLE CCtrlClc::GetSelection() const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0);
+}
+
+HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y));
+}
+
+void CCtrlClc::SelectItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::SetBkColor(COLORREF clBack)
+{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0);
+}
+
+void CCtrlClc::SetCheck(HANDLE hItem, bool check)
+{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0);
+}
+
+void CCtrlClc::SetExtraColumns(int iColumns)
+{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0);
+}
+
+void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage));
+}
+
+void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList);
+}
+
+void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw)
+{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId));
+}
+
+void CCtrlClc::SetItemText(HANDLE hItem, char *szText)
+{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText);
+}
+
+void CCtrlClc::SetHideEmptyGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0);
+}
+
+bool CCtrlClc::GetHideOfflineRoot() const
+{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false;
+}
+
+void CCtrlClc::SetHideOfflineRoot(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9);
+}
+
+void CCtrlClc::SetUseGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0);
+}
+
+void CCtrlClc::SetOfflineModes(uint32_t modes)
+{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0);
+}
+
+uint32_t CCtrlClc::GetExStyle() const
+{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0);
+}
+
+void CCtrlClc::SetExStyle(uint32_t exStyle)
+{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0);
+}
+
+HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii);
+}
+
+int CCtrlClc::GetItemType(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+}
+
+HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem);
+}
diff --git a/src/mir_core/src/Linux/CCtrlColor.cpp b/src/mir_core/src/Linux/CCtrlColor.cpp index 97f5e48e02..b61fb5760a 100644 --- a/src/mir_core/src/Linux/CCtrlColor.cpp +++ b/src/mir_core/src/Linux/CCtrlColor.cpp @@ -1,61 +1,61 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlColor class - -CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlColor::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetColor()); - return true; -} - -void CCtrlColor::OnReset() -{ - if (m_dbLink != nullptr) - SetColor(LoadInt()); -} - -uint32_t CCtrlColor::GetColor() -{ - return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); -} - -void CCtrlColor::SetColor(uint32_t dwValue) -{ - ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlColor class
+
+CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlColor::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetColor());
+ return true;
+}
+
+void CCtrlColor::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetColor(LoadInt());
+}
+
+uint32_t CCtrlColor::GetColor()
+{
+ return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0);
+}
+
+void CCtrlColor::SetColor(uint32_t dwValue)
+{
+ ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue);
+}
diff --git a/src/mir_core/src/Linux/CCtrlCombo.cpp b/src/mir_core/src/Linux/CCtrlCombo.cpp index 063588e39e..52a85f1115 100644 --- a/src/mir_core/src/Linux/CCtrlCombo.cpp +++ b/src/mir_core/src/Linux/CCtrlCombo.cpp @@ -1,169 +1,169 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCombo class - -CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlCombo::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - switch (idCode) { - // case CBN_CLOSEUP: OnCloseup(this); break; - // case CBN_DROPDOWN: OnDropdown(this); break; - // case CBN_SELCHANGE: OnSelChanged(this); break; - // case CBN_KILLFOCUS: OnKillFocus(this); break; - - // case CBN_EDITCHANGE: - // case CBN_EDITUPDATE: - // case CBN_SELENDOK: - // NotifyChange(); - break; - } - return TRUE; -} - -void CCtrlCombo::OnInit() -{ - CSuper::OnInit(); - OnReset(); -} - -bool CCtrlCombo::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - // int len = GetWindowTextLength(m_hwnd) + 1; - // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - // GetWindowText(m_hwnd, buf, len); - // SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlCombo::OnReset() -{ - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -LPARAM CCtrlCombo::GetCurData() const -{ - return GetItemData(GetCurSel()); -} - -// selects line with userdata passed -int CCtrlCombo::SelectData(LPARAM data) -{ - int ret = -1, nCount = GetCount(); - - for (int i = 0; i < nCount; i++) - if (GetItemData(i) == data) { - ret = i; - break; - } - - return SetCurSel(ret); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Windows API - -int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) -{ - return -1; -} - -int CCtrlCombo::AddStringA(const char *text, LPARAM data) -{ - return -1; -} - -void CCtrlCombo::DeleteString(int index) -{ -} - -int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) -{ return 0; -} - -int CCtrlCombo::FindStringA(const char *str, int index, bool exact) -{ return 0; -} - -int CCtrlCombo::GetCount() const -{ return 0; -} - -int CCtrlCombo::GetCurSel() const -{ return 0; -} - -bool CCtrlCombo::GetDroppedState() const -{ return 0; -} - -LPARAM CCtrlCombo::GetItemData(int index) const -{ return 0; -} - -wchar_t* CCtrlCombo::GetItemText(int index) const -{ return 0; -} - -wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const -{ return 0; -} - -int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) -{ return 0; -} - -void CCtrlCombo::ResetContent() -{ -} - -int CCtrlCombo::SelectString(const wchar_t *str) -{ return 0; -} - -int CCtrlCombo::SetCurSel(int index) -{ return 0; -} - -void CCtrlCombo::SetItemData(int index, LPARAM data) -{ -} - -void CCtrlCombo::ShowDropdown(bool show) -{ -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCombo class
+
+CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlCombo::OnCommand(MWindow, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ // case CBN_CLOSEUP: OnCloseup(this); break;
+ // case CBN_DROPDOWN: OnDropdown(this); break;
+ // case CBN_SELCHANGE: OnSelChanged(this); break;
+ // case CBN_KILLFOCUS: OnKillFocus(this); break;
+
+ // case CBN_EDITCHANGE:
+ // case CBN_EDITUPDATE:
+ // case CBN_SELENDOK:
+ // NotifyChange();
+ break;
+ }
+ return TRUE;
+}
+
+void CCtrlCombo::OnInit()
+{
+ CSuper::OnInit();
+ OnReset();
+}
+
+bool CCtrlCombo::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ // int len = GetWindowTextLength(m_hwnd) + 1;
+ // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ // GetWindowText(m_hwnd, buf, len);
+ // SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlCombo::OnReset()
+{
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+LPARAM CCtrlCombo::GetCurData() const
+{
+ return GetItemData(GetCurSel());
+}
+
+// selects line with userdata passed
+int CCtrlCombo::SelectData(LPARAM data)
+{
+ int ret = -1, nCount = GetCount();
+
+ for (int i = 0; i < nCount; i++)
+ if (GetItemData(i) == data) {
+ ret = i;
+ break;
+ }
+
+ return SetCurSel(ret);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Windows API
+
+int CCtrlCombo::AddString(const wchar_t *text, LPARAM data)
+{
+ return -1;
+}
+
+int CCtrlCombo::AddStringA(const char *text, LPARAM data)
+{
+ return -1;
+}
+
+void CCtrlCombo::DeleteString(int index)
+{
+}
+
+int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact)
+{ return 0;
+}
+
+int CCtrlCombo::FindStringA(const char *str, int index, bool exact)
+{ return 0;
+}
+
+int CCtrlCombo::GetCount() const
+{ return 0;
+}
+
+int CCtrlCombo::GetCurSel() const
+{ return 0;
+}
+
+bool CCtrlCombo::GetDroppedState() const
+{ return 0;
+}
+
+LPARAM CCtrlCombo::GetItemData(int index) const
+{ return 0;
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index) const
+{ return 0;
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const
+{ return 0;
+}
+
+int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data)
+{ return 0;
+}
+
+void CCtrlCombo::ResetContent()
+{
+}
+
+int CCtrlCombo::SelectString(const wchar_t *str)
+{ return 0;
+}
+
+int CCtrlCombo::SetCurSel(int index)
+{ return 0;
+}
+
+void CCtrlCombo::SetItemData(int index, LPARAM data)
+{
+}
+
+void CCtrlCombo::ShowDropdown(bool show)
+{
+}
diff --git a/src/mir_core/src/Linux/CCtrlData.cpp b/src/mir_core/src/Linux/CCtrlData.cpp index fab5ca0409..2a60d9d31a 100644 --- a/src/mir_core/src/Linux/CCtrlData.cpp +++ b/src/mir_core/src/Linux/CCtrlData.cpp @@ -1,52 +1,52 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlData class - -CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_dbLink(nullptr) -{} - -CCtrlData::~CCtrlData() -{ - delete m_dbLink; -} - -void CCtrlData::OnInit() -{ - CCtrlBase::OnInit(); - OnReset(); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlData class
+
+CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_dbLink(nullptr)
+{}
+
+CCtrlData::~CCtrlData()
+{
+ delete m_dbLink;
+}
+
+void CCtrlData::OnInit()
+{
+ CCtrlBase::OnInit();
+ OnReset();
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue);
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue);
+}
diff --git a/src/mir_core/src/Linux/CCtrlEdit.cpp b/src/mir_core/src/Linux/CCtrlEdit.cpp index 7f1ac14cc2..42a9dd6327 100644 --- a/src/mir_core/src/Linux/CCtrlEdit.cpp +++ b/src/mir_core/src/Linux/CCtrlEdit.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlEdit class - -CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlEdit::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - // if (idCode == EN_CHANGE) - // NotifyChange(); - return TRUE; -} - -bool CCtrlEdit::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - // int len = GetWindowTextLength(m_hwnd) + 1; - // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - // GetWindowText(m_hwnd, buf, len); - // SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlEdit::OnReset() -{ - // m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; - - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -void CCtrlEdit::SetMaxLength(unsigned int len) -{ - // SendMsg(EM_SETLIMITTEXT, len, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlEdit class
+
+CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlEdit::OnCommand(MWindow, uint16_t, uint16_t idCode)
+{
+ // if (idCode == EN_CHANGE)
+ // NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlEdit::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ // int len = GetWindowTextLength(m_hwnd) + 1;
+ // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ // GetWindowText(m_hwnd, buf, len);
+ // SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlEdit::OnReset()
+{
+ // m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0;
+
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+void CCtrlEdit::SetMaxLength(unsigned int len)
+{
+ // SendMsg(EM_SETLIMITTEXT, len, 0);
+}
diff --git a/src/mir_core/src/Linux/CCtrlHyperlink.cpp b/src/mir_core/src/Linux/CCtrlHyperlink.cpp index ca92d19dbc..0e0d93a689 100644 --- a/src/mir_core/src/Linux/CCtrlHyperlink.cpp +++ b/src/mir_core/src/Linux/CCtrlHyperlink.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlHyperlink - -CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) - : CCtrlBase(wnd, idCtrl), - m_url(url) -{ - OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); -} - -BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) -{ - OnClick(this); - return FALSE; -} - -void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) -{ - ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); -} - -void CCtrlHyperlink::SetUrl(const char *url) -{ - m_url = url; -} - -const char* CCtrlHyperlink::GetUrl() -{ - return m_url; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlHyperlink
+
+CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url)
+ : CCtrlBase(wnd, idCtrl),
+ m_url(url)
+{
+ OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick);
+}
+
+BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t)
+{
+ OnClick(this);
+ return FALSE;
+}
+
+void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*)
+{
+ ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW);
+}
+
+void CCtrlHyperlink::SetUrl(const char *url)
+{
+ m_url = url;
+}
+
+const char* CCtrlHyperlink::GetUrl()
+{
+ return m_url;
+}
diff --git a/src/mir_core/src/Linux/CCtrlLabel.cpp b/src/mir_core/src/Linux/CCtrlLabel.cpp index 0c097c0655..ff05f3c8cf 100644 --- a/src/mir_core/src/Linux/CCtrlLabel.cpp +++ b/src/mir_core/src/Linux/CCtrlLabel.cpp @@ -1,30 +1,30 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlLabel - -CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlLabel
+
+CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
diff --git a/src/mir_core/src/Linux/CCtrlListBox.cpp b/src/mir_core/src/Linux/CCtrlListBox.cpp index abaa31a786..6d9f58fc23 100644 --- a/src/mir_core/src/Linux/CCtrlListBox.cpp +++ b/src/mir_core/src/Linux/CCtrlListBox.cpp @@ -1,160 +1,160 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListBox class - -CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case LBN_DBLCLK: OnDblClick(this); break; - case LBN_SELCANCEL: OnSelCancel(this); break; - case LBN_SELCHANGE: OnSelChange(this); break; - } - return TRUE; -} - -void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetCurSel(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = ListBox_AddString(m_hwnd, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::DeleteString(int index) -{ ListBox_DeleteString(m_hwnd, index); -} - -int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlListBox::GetCount() const -{ return ListBox_GetCount(m_hwnd); -} - -int CCtrlListBox::GetCurSel() const -{ return ListBox_GetCurSel(m_hwnd); -} - -LPARAM CCtrlListBox::GetItemData(int index) const -{ return ListBox_GetItemData(m_hwnd, index); -} - -int CCtrlListBox::GetItemRect(int index, RECT *pResult) const -{ return ListBox_GetItemRect(m_hwnd, index, pResult); -} - -wchar_t* CCtrlListBox::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -bool CCtrlListBox::GetSel(int index) const -{ return ListBox_GetSel(m_hwnd, index) ? true : false; -} - -int CCtrlListBox::GetSelCount() const -{ return ListBox_GetSelCount(m_hwnd); -} - -int* CCtrlListBox::GetSelItems(int *items, int count) const -{ - ListBox_GetSelItems(m_hwnd, count, items); - return items; -} - -int* CCtrlListBox::GetSelItems() const -{ - int count = GetSelCount() + 1; - int *result = (int *)mir_alloc(sizeof(int) * count); - ListBox_GetSelItems(m_hwnd, count, result); - result[count-1] = -1; - return result; -} - -int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = ListBox_InsertString(m_hwnd, pos, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::ResetContent() -{ ListBox_ResetContent(m_hwnd); -} - -int CCtrlListBox::SelectString(const wchar_t *str) -{ return ListBox_SelectString(m_hwnd, 0, str); -} - -int CCtrlListBox::SetCurSel(int index) -{ return ListBox_SetCurSel(m_hwnd, index); -} - -void CCtrlListBox::SetItemData(int index, LPARAM data) -{ ListBox_SetItemData(m_hwnd, index, data); -} - -void CCtrlListBox::SetItemHeight(int index, int iHeight) -{ ListBox_SetItemHeight(m_hwnd, index, iHeight); -} - -void CCtrlListBox::SetSel(int index, bool sel) -{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListBox class
+
+CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ case LBN_DBLCLK: OnDblClick(this); break;
+ case LBN_SELCANCEL: OnSelCancel(this); break;
+ case LBN_SELCHANGE: OnSelChange(this); break;
+ }
+ return TRUE;
+}
+
+void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetCurSel();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+
+ CSuper::GetCaretPos(pos);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CCtrlListBox::AddString(const wchar_t *text, LPARAM data)
+{
+ int iItem = ListBox_AddString(m_hwnd, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::DeleteString(int index)
+{ ListBox_DeleteString(m_hwnd, index);
+}
+
+int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlListBox::GetCount() const
+{ return ListBox_GetCount(m_hwnd);
+}
+
+int CCtrlListBox::GetCurSel() const
+{ return ListBox_GetCurSel(m_hwnd);
+}
+
+LPARAM CCtrlListBox::GetItemData(int index) const
+{ return ListBox_GetItemData(m_hwnd, index);
+}
+
+int CCtrlListBox::GetItemRect(int index, RECT *pResult) const
+{ return ListBox_GetItemRect(m_hwnd, index, pResult);
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index) const
+{
+ wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ return result;
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const
+{
+ wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ mir_wstrncpy(buf, result, size);
+ return buf;
+}
+
+bool CCtrlListBox::GetSel(int index) const
+{ return ListBox_GetSel(m_hwnd, index) ? true : false;
+}
+
+int CCtrlListBox::GetSelCount() const
+{ return ListBox_GetSelCount(m_hwnd);
+}
+
+int* CCtrlListBox::GetSelItems(int *items, int count) const
+{
+ ListBox_GetSelItems(m_hwnd, count, items);
+ return items;
+}
+
+int* CCtrlListBox::GetSelItems() const
+{
+ int count = GetSelCount() + 1;
+ int *result = (int *)mir_alloc(sizeof(int) * count);
+ ListBox_GetSelItems(m_hwnd, count, result);
+ result[count-1] = -1;
+ return result;
+}
+
+int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data)
+{
+ int iItem = ListBox_InsertString(m_hwnd, pos, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::ResetContent()
+{ ListBox_ResetContent(m_hwnd);
+}
+
+int CCtrlListBox::SelectString(const wchar_t *str)
+{ return ListBox_SelectString(m_hwnd, 0, str);
+}
+
+int CCtrlListBox::SetCurSel(int index)
+{ return ListBox_SetCurSel(m_hwnd, index);
+}
+
+void CCtrlListBox::SetItemData(int index, LPARAM data)
+{ ListBox_SetItemData(m_hwnd, index, data);
+}
+
+void CCtrlListBox::SetItemHeight(int index, int iHeight)
+{ ListBox_SetItemHeight(m_hwnd, index, iHeight);
+}
+
+void CCtrlListBox::SetSel(int index, bool sel)
+{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index);
+}
diff --git a/src/mir_core/src/Linux/CCtrlListView.cpp b/src/mir_core/src/Linux/CCtrlListView.cpp index eb57951a3c..40bb1f481e 100644 --- a/src/mir_core/src/Linux/CCtrlListView.cpp +++ b/src/mir_core/src/Linux/CCtrlListView.cpp @@ -1,551 +1,551 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListView - -CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_CLICK: OnClick(&evt); return TRUE; - case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; - case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; - case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; - case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; - case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; - case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; - case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; - case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; - case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; - case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; - case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; - case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - - case LVN_ITEMCHANGED: - if (!m_parentWnd || !m_parentWnd->IsInitialized()) - return FALSE; - - OnItemChanged(&evt); - - // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes - if (evt.nmlv->uChanged & LVIF_STATE) - if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) - NotifyChange(); - return TRUE; - - case LVN_ODSTATECHANGED: - NotifyChange(); - return TRUE; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) -{ - int result = l1 - l2; - int newItem = HIWORD(param); - int oldItem = LOWORD(param); - if (newItem > oldItem) - return (l1 == oldItem && l2 <= newItem) ? 1 : result; - - return (l2 == oldItem && l1 >= newItem) ? 1 : result; -} - -int CCtrlListView::MoveItem(int idx, int direction) -{ - if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) - return idx; - - if (idx < 0) - idx = GetNextItem(-1, LVNI_FOCUSED); - SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); - return idx + direction; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CCtrlListView::SetCurSel(int idx) -{ - SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -// additional api -HIMAGELIST CCtrlListView::CreateImageList(int iImageList) -{ - HIMAGELIST hIml = GetImageList(iImageList); - if (hIml) - return hIml; - - hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - SetImageList(hIml, iImageList); - return hIml; -} - -void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) -{ - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc.iImage = 0; - lvc.pszText = (LPWSTR)name; - lvc.cx = cx; - lvc.iSubItem = iSubItem; - InsertColumn(iSubItem, &lvc); -} - -void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) -{ - LVGROUP lvg = { 0 }; - lvg.cbSize = sizeof(lvg); - lvg.mask = LVGF_HEADER | LVGF_GROUPID; - lvg.pszHeader = (LPWSTR)name; - lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); - lvg.iGroupId = iGroupId; - InsertGroup(-1, &lvg); -} - -int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = 0; - lvi.pszText = (LPWSTR)text; - lvi.iImage = iIcon; - lvi.lParam = lParam; - if (iGroupId >= 0) { - lvi.mask |= LVIF_GROUPID; - lvi.iGroupId = iGroupId; - } - - return InsertItem(&lvi); -} - -void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_TEXT; - lvi.iItem = iItem; - lvi.iSubItem = iSubItem; - lvi.pszText = (LPWSTR)text; - if (iIcon >= 0) { - lvi.mask |= LVIF_IMAGE; - lvi.iImage = iIcon; - } - - SetItem(&lvi); -} - -LPARAM CCtrlListView::GetItemData(int iItem) const -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - return GetItem(&lvi) ? lvi.lParam : -1; -} - -void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetSelectionMark(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - LVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (SubItemHitTest(&hti) != -1) { - pos.iCurr = hti.iItem; - return; - } - } - CSuper::GetCaretPos(pos); -} - -// classic api -uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) -{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); -} -void CCtrlListView::Arrange(UINT code) -{ ListView_Arrange(m_hwnd, code); -} -void CCtrlListView::CancelEditLabel() -{ ListView_CancelEditLabel(m_hwnd); -} -HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) -{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); -} -void CCtrlListView::DeleteAllItems() -{ ListView_DeleteAllItems(m_hwnd); -} -void CCtrlListView::DeleteColumn(int iCol) -{ ListView_DeleteColumn(m_hwnd, iCol); -} -void CCtrlListView::DeleteItem(int iItem) -{ ListView_DeleteItem(m_hwnd, iItem); -} -HWND CCtrlListView::EditLabel(int iItem) -{ return ListView_EditLabel(m_hwnd, iItem); -} -int CCtrlListView::EnableGroupView(BOOL fEnable) -{ return ListView_EnableGroupView(m_hwnd, fEnable); -} -BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) -{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); -} -int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) -{ return ListView_FindItem(m_hwnd, iStart, plvfi); -} -COLORREF CCtrlListView::GetBkColor() const -{ return ListView_GetBkColor(m_hwnd); -} -void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const -{ ListView_GetBkImage(m_hwnd, plvbki); -} -UINT CCtrlListView::GetCallbackMask() const -{ return ListView_GetCallbackMask(m_hwnd); -} -BOOL CCtrlListView::GetCheckState(UINT iIndex) const -{ return ListView_GetCheckState(m_hwnd, iIndex); -} -void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const -{ ListView_GetColumn(m_hwnd, iCol, pcol); -} -void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const -{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -int CCtrlListView::GetColumnWidth(int iCol) const -{ return ListView_GetColumnWidth(m_hwnd, iCol); -} -int CCtrlListView::GetCountPerPage() const -{ return ListView_GetCountPerPage(m_hwnd); -} -HWND CCtrlListView::GetEditControl() const -{ return ListView_GetEditControl(m_hwnd); -} -uint32_t CCtrlListView::GetExtendedListViewStyle() const -{ return ListView_GetExtendedListViewStyle(m_hwnd); -} -void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const -{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); -} -HWND CCtrlListView::GetHeader() const -{ return ListView_GetHeader(m_hwnd); -} -HCURSOR CCtrlListView::GetHotCursor() const -{ return ListView_GetHotCursor(m_hwnd); -} -INT CCtrlListView::GetHotItem() const -{ return ListView_GetHotItem(m_hwnd); -} -uint32_t CCtrlListView::GetHoverTime() const -{ return ListView_GetHoverTime(m_hwnd); -} -HIMAGELIST CCtrlListView::GetImageList(int iImageList) const -{ return ListView_GetImageList(m_hwnd, iImageList); -} -BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const -{ return ListView_GetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::GetInsertMarkColor() const -{ return ListView_GetInsertMarkColor(m_hwnd); -} -int CCtrlListView::GetInsertMarkRect(LPRECT prc) const -{ return ListView_GetInsertMarkRect(m_hwnd, prc); -} -BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const -{ return ListView_GetISearchString(m_hwnd, lpsz); -} -bool CCtrlListView::GetItem(LPLVITEM pitem) const -{ return ListView_GetItem(m_hwnd, pitem) == TRUE; -} -int CCtrlListView::GetItemCount() const -{ return ListView_GetItemCount(m_hwnd); -} -void CCtrlListView::GetItemPosition(int i, POINT *ppt) const -{ ListView_GetItemPosition(m_hwnd, i, ppt); -} -void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const -{ ListView_GetItemRect(m_hwnd, i, prc, code); -} -uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const -{ return ListView_GetItemSpacing(m_hwnd, fSmall); -} -UINT CCtrlListView::GetItemState(int i, UINT mask) const -{ return ListView_GetItemState(m_hwnd, i, mask); -} -void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const -{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); -} -int CCtrlListView::GetNextItem(int iStart, UINT flags) const -{ return ListView_GetNextItem(m_hwnd, iStart, flags); -} -BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const -{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); -} -BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const -{ return ListView_GetOrigin(m_hwnd, lpptOrg); -} -COLORREF CCtrlListView::GetOutlineColor() const -{ return ListView_GetOutlineColor(m_hwnd); -} -UINT CCtrlListView::GetSelectedColumn() const -{ return ListView_GetSelectedColumn(m_hwnd); -} -UINT CCtrlListView::GetSelectedCount() const -{ return ListView_GetSelectedCount(m_hwnd); -} -INT CCtrlListView::GetSelectionMark() const -{ return ListView_GetSelectionMark(m_hwnd); -} -int CCtrlListView::GetStringWidth(LPCSTR psz) const -{ return ListView_GetStringWidth(m_hwnd, psz); -} -BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const -{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); -} -COLORREF CCtrlListView::GetTextBkColor() const -{ return ListView_GetTextBkColor(m_hwnd); -} -COLORREF CCtrlListView::GetTextColor() const -{ return ListView_GetTextColor(m_hwnd); -} -void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const -{ ListView_GetTileInfo(m_hwnd, plvtinfo); -} -void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const -{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::GetToolTips() const -{ return ListView_GetToolTips(m_hwnd); -} -int CCtrlListView::GetTopIndex() const -{ return ListView_GetTopIndex(m_hwnd); -} -BOOL CCtrlListView::GetUnicodeFormat() const -{ return ListView_GetUnicodeFormat(m_hwnd); -} -uint32_t CCtrlListView::GetView() const -{ return ListView_GetView(m_hwnd); -} -BOOL CCtrlListView::GetViewRect(RECT *prc) const -{ return ListView_GetViewRect(m_hwnd, prc); -} -void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const -{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -BOOL CCtrlListView::HasGroup(int dwGroupId) -{ return ListView_HasGroup(m_hwnd, dwGroupId); -} -int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const -{ return ListView_HitTest(m_hwnd, pinfo); -} -int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) -{ return ListView_InsertColumn(m_hwnd, iCol, pcol); -} -int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) -{ return ListView_InsertGroup(m_hwnd, index, pgrp); -} -void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) -{ ListView_InsertGroupSorted(m_hwnd, structInsert); -} -int CCtrlListView::InsertItem(const LVITEM *pitem) -{ return ListView_InsertItem(m_hwnd, pitem); -} -BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) -{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); -} -BOOL CCtrlListView::IsGroupViewEnabled() -{ return ListView_IsGroupViewEnabled(m_hwnd); -} -UINT CCtrlListView::MapIDToIndex(UINT id) -{ return ListView_MapIDToIndex(m_hwnd, id); -} -UINT CCtrlListView::MapIndexToID(UINT index) -{ return ListView_MapIndexToID(m_hwnd, index); -} -BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) -{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); -} -void CCtrlListView::RemoveAllGroups() -{ ListView_RemoveAllGroups(m_hwnd); -} -int CCtrlListView::RemoveGroup(int iGroupId) -{ return ListView_RemoveGroup(m_hwnd, iGroupId); -} -BOOL CCtrlListView::Scroll(int dx, int dy) -{ return ListView_Scroll(m_hwnd, dx, dy); -} -BOOL CCtrlListView::SetBkColor(COLORREF clrBk) -{ return ListView_SetBkColor(m_hwnd, clrBk); -} -BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) -{ return ListView_SetBkImage(m_hwnd, plvbki); -} -BOOL CCtrlListView::SetCallbackMask(UINT mask) -{ return ListView_SetCallbackMask(m_hwnd, mask); -} -void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) -{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); -} -BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) -{ return ListView_SetColumn(m_hwnd, iCol, pcol); -} -BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) -{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) -{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); -} -void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); -} -void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); -} -int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) -{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); -} -void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) -{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); -} -HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) -{ return ListView_SetHotCursor(m_hwnd, hCursor); -} -INT CCtrlListView::SetHotItem(INT iIndex) -{ return ListView_SetHotItem(m_hwnd, iIndex); -} -void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) -{ ListView_SetHoverTime(m_hwnd, dwHoverTime); -} -uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) -{ return ListView_SetIconSpacing(m_hwnd, cx, cy); -} -HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) -{ return ListView_SetImageList(m_hwnd, himl, iImageList); -} -BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) -{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); -} -BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) -{ return ListView_SetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) -{ return ListView_SetInsertMarkColor(m_hwnd, color); -} -BOOL CCtrlListView::SetItem(const LVITEM *pitem) -{ return ListView_SetItem(m_hwnd, pitem); -} -void CCtrlListView::SetItemCount(int cItems) -{ ListView_SetItemCount(m_hwnd, cItems); -} -void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) -{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); -} -BOOL CCtrlListView::SetItemPosition(int i, int x, int y) -{ return ListView_SetItemPosition(m_hwnd, i, x, y); -} -void CCtrlListView::SetItemPosition32(int iItem, int x, int y) -{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); -} -void CCtrlListView::SetItemState(int i, UINT state, UINT mask) -{ ListView_SetItemState(m_hwnd, i, state, mask); -} -void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) -{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); -} -COLORREF CCtrlListView::SetOutlineColor(COLORREF color) -{ return ListView_SetOutlineColor(m_hwnd, color); -} -void CCtrlListView::SetSelectedColumn(int iCol) -{ ListView_SetSelectedColumn(m_hwnd, iCol); -} -INT CCtrlListView::SetSelectionMark(INT iIndex) -{ return ListView_SetSelectionMark(m_hwnd, iIndex); -} -BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) -{ return ListView_SetTextBkColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTextColor(COLORREF clrText) -{ return ListView_SetTextColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) -{ return ListView_SetTileInfo(m_hwnd, plvtinfo); -} -BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) -{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::SetToolTips(HWND ToolTip) -{ return ListView_SetToolTips(m_hwnd, ToolTip); -} -BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) -{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); -} -int CCtrlListView::SetView(uint32_t iView) -{ return ListView_SetView(m_hwnd, iView); -} -void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) -{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) -{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); -} -BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); -} -BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); -} -INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const -{ return ListView_SubItemHitTest(m_hwnd, pInfo); -} -BOOL CCtrlListView::Update(int iItem) -{ return ListView_Update(m_hwnd, iItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListView
+
+CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_CLICK: OnClick(&evt); return TRUE;
+ case NM_DBLCLK: OnDoubleClick(&evt); return TRUE;
+ case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE;
+ case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE;
+ case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE;
+ case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE;
+ case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE;
+ case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE;
+ case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE;
+ case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE;
+ case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE;
+ case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE;
+ case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE;
+ case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE;
+ case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+
+ case LVN_ITEMCHANGED:
+ if (!m_parentWnd || !m_parentWnd->IsInitialized())
+ return FALSE;
+
+ OnItemChanged(&evt);
+
+ // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes
+ if (evt.nmlv->uChanged & LVIF_STATE)
+ if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0)
+ NotifyChange();
+ return TRUE;
+
+ case LVN_ODSTATECHANGED:
+ NotifyChange();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param)
+{
+ int result = l1 - l2;
+ int newItem = HIWORD(param);
+ int oldItem = LOWORD(param);
+ if (newItem > oldItem)
+ return (l1 == oldItem && l2 <= newItem) ? 1 : result;
+
+ return (l2 == oldItem && l1 >= newItem) ? 1 : result;
+}
+
+int CCtrlListView::MoveItem(int idx, int direction)
+{
+ if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0))
+ return idx;
+
+ if (idx < 0)
+ idx = GetNextItem(-1, LVNI_FOCUSED);
+ SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction));
+ return idx + direction;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CCtrlListView::SetCurSel(int idx)
+{
+ SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+}
+
+// additional api
+HIMAGELIST CCtrlListView::CreateImageList(int iImageList)
+{
+ HIMAGELIST hIml = GetImageList(iImageList);
+ if (hIml)
+ return hIml;
+
+ hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ SetImageList(hIml, iImageList);
+ return hIml;
+}
+
+void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx)
+{
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+ lvc.iImage = 0;
+ lvc.pszText = (LPWSTR)name;
+ lvc.cx = cx;
+ lvc.iSubItem = iSubItem;
+ InsertColumn(iSubItem, &lvc);
+}
+
+void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name)
+{
+ LVGROUP lvg = { 0 };
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER | LVGF_GROUPID;
+ lvg.pszHeader = (LPWSTR)name;
+ lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader);
+ lvg.iGroupId = iGroupId;
+ InsertGroup(-1, &lvg);
+}
+
+int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
+ lvi.iSubItem = 0;
+ lvi.pszText = (LPWSTR)text;
+ lvi.iImage = iIcon;
+ lvi.lParam = lParam;
+ if (iGroupId >= 0) {
+ lvi.mask |= LVIF_GROUPID;
+ lvi.iGroupId = iGroupId;
+ }
+
+ return InsertItem(&lvi);
+}
+
+void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = (LPWSTR)text;
+ if (iIcon >= 0) {
+ lvi.mask |= LVIF_IMAGE;
+ lvi.iImage = iIcon;
+ }
+
+ SetItem(&lvi);
+}
+
+LPARAM CCtrlListView::GetItemData(int iItem) const
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ return GetItem(&lvi) ? lvi.lParam : -1;
+}
+
+void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+
+ // position is empty, let's fill it using selection
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetSelectionMark();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc, TRUE);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+ // position is present, let's calculate current item
+ else {
+ LVHITTESTINFO hti;
+ hti.pt = pos.pt;
+ ScreenToClient(m_hwnd, &hti.pt);
+ if (SubItemHitTest(&hti) != -1) {
+ pos.iCurr = hti.iItem;
+ return;
+ }
+ }
+ CSuper::GetCaretPos(pos);
+}
+
+// classic api
+uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount)
+{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount);
+}
+void CCtrlListView::Arrange(UINT code)
+{ ListView_Arrange(m_hwnd, code);
+}
+void CCtrlListView::CancelEditLabel()
+{ ListView_CancelEditLabel(m_hwnd);
+}
+HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft)
+{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft);
+}
+void CCtrlListView::DeleteAllItems()
+{ ListView_DeleteAllItems(m_hwnd);
+}
+void CCtrlListView::DeleteColumn(int iCol)
+{ ListView_DeleteColumn(m_hwnd, iCol);
+}
+void CCtrlListView::DeleteItem(int iItem)
+{ ListView_DeleteItem(m_hwnd, iItem);
+}
+HWND CCtrlListView::EditLabel(int iItem)
+{ return ListView_EditLabel(m_hwnd, iItem);
+}
+int CCtrlListView::EnableGroupView(BOOL fEnable)
+{ return ListView_EnableGroupView(m_hwnd, fEnable);
+}
+BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK)
+{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK);
+}
+int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi)
+{ return ListView_FindItem(m_hwnd, iStart, plvfi);
+}
+COLORREF CCtrlListView::GetBkColor() const
+{ return ListView_GetBkColor(m_hwnd);
+}
+void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const
+{ ListView_GetBkImage(m_hwnd, plvbki);
+}
+UINT CCtrlListView::GetCallbackMask() const
+{ return ListView_GetCallbackMask(m_hwnd);
+}
+BOOL CCtrlListView::GetCheckState(UINT iIndex) const
+{ return ListView_GetCheckState(m_hwnd, iIndex);
+}
+void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const
+{ ListView_GetColumn(m_hwnd, iCol, pcol);
+}
+void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const
+{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+int CCtrlListView::GetColumnWidth(int iCol) const
+{ return ListView_GetColumnWidth(m_hwnd, iCol);
+}
+int CCtrlListView::GetCountPerPage() const
+{ return ListView_GetCountPerPage(m_hwnd);
+}
+HWND CCtrlListView::GetEditControl() const
+{ return ListView_GetEditControl(m_hwnd);
+}
+uint32_t CCtrlListView::GetExtendedListViewStyle() const
+{ return ListView_GetExtendedListViewStyle(m_hwnd);
+}
+void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const
+{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HWND CCtrlListView::GetHeader() const
+{ return ListView_GetHeader(m_hwnd);
+}
+HCURSOR CCtrlListView::GetHotCursor() const
+{ return ListView_GetHotCursor(m_hwnd);
+}
+INT CCtrlListView::GetHotItem() const
+{ return ListView_GetHotItem(m_hwnd);
+}
+uint32_t CCtrlListView::GetHoverTime() const
+{ return ListView_GetHoverTime(m_hwnd);
+}
+HIMAGELIST CCtrlListView::GetImageList(int iImageList) const
+{ return ListView_GetImageList(m_hwnd, iImageList);
+}
+BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const
+{ return ListView_GetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::GetInsertMarkColor() const
+{ return ListView_GetInsertMarkColor(m_hwnd);
+}
+int CCtrlListView::GetInsertMarkRect(LPRECT prc) const
+{ return ListView_GetInsertMarkRect(m_hwnd, prc);
+}
+BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const
+{ return ListView_GetISearchString(m_hwnd, lpsz);
+}
+bool CCtrlListView::GetItem(LPLVITEM pitem) const
+{ return ListView_GetItem(m_hwnd, pitem) == TRUE;
+}
+int CCtrlListView::GetItemCount() const
+{ return ListView_GetItemCount(m_hwnd);
+}
+void CCtrlListView::GetItemPosition(int i, POINT *ppt) const
+{ ListView_GetItemPosition(m_hwnd, i, ppt);
+}
+void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const
+{ ListView_GetItemRect(m_hwnd, i, prc, code);
+}
+uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const
+{ return ListView_GetItemSpacing(m_hwnd, fSmall);
+}
+UINT CCtrlListView::GetItemState(int i, UINT mask) const
+{ return ListView_GetItemState(m_hwnd, i, mask);
+}
+void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const
+{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax);
+}
+int CCtrlListView::GetNextItem(int iStart, UINT flags) const
+{ return ListView_GetNextItem(m_hwnd, iStart, flags);
+}
+BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const
+{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas);
+}
+BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const
+{ return ListView_GetOrigin(m_hwnd, lpptOrg);
+}
+COLORREF CCtrlListView::GetOutlineColor() const
+{ return ListView_GetOutlineColor(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedColumn() const
+{ return ListView_GetSelectedColumn(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedCount() const
+{ return ListView_GetSelectedCount(m_hwnd);
+}
+INT CCtrlListView::GetSelectionMark() const
+{ return ListView_GetSelectionMark(m_hwnd);
+}
+int CCtrlListView::GetStringWidth(LPCSTR psz) const
+{ return ListView_GetStringWidth(m_hwnd, psz);
+}
+BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const
+{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect);
+}
+COLORREF CCtrlListView::GetTextBkColor() const
+{ return ListView_GetTextBkColor(m_hwnd);
+}
+COLORREF CCtrlListView::GetTextColor() const
+{ return ListView_GetTextColor(m_hwnd);
+}
+void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const
+{ ListView_GetTileInfo(m_hwnd, plvtinfo);
+}
+void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const
+{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::GetToolTips() const
+{ return ListView_GetToolTips(m_hwnd);
+}
+int CCtrlListView::GetTopIndex() const
+{ return ListView_GetTopIndex(m_hwnd);
+}
+BOOL CCtrlListView::GetUnicodeFormat() const
+{ return ListView_GetUnicodeFormat(m_hwnd);
+}
+uint32_t CCtrlListView::GetView() const
+{ return ListView_GetView(m_hwnd);
+}
+BOOL CCtrlListView::GetViewRect(RECT *prc) const
+{ return ListView_GetViewRect(m_hwnd, prc);
+}
+void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const
+{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+BOOL CCtrlListView::HasGroup(int dwGroupId)
+{ return ListView_HasGroup(m_hwnd, dwGroupId);
+}
+int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const
+{ return ListView_HitTest(m_hwnd, pinfo);
+}
+int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol)
+{ return ListView_InsertColumn(m_hwnd, iCol, pcol);
+}
+int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp)
+{ return ListView_InsertGroup(m_hwnd, index, pgrp);
+}
+void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert)
+{ ListView_InsertGroupSorted(m_hwnd, structInsert);
+}
+int CCtrlListView::InsertItem(const LVITEM *pitem)
+{ return ListView_InsertItem(m_hwnd, pitem);
+}
+BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim)
+{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim);
+}
+BOOL CCtrlListView::IsGroupViewEnabled()
+{ return ListView_IsGroupViewEnabled(m_hwnd);
+}
+UINT CCtrlListView::MapIDToIndex(UINT id)
+{ return ListView_MapIDToIndex(m_hwnd, id);
+}
+UINT CCtrlListView::MapIndexToID(UINT index)
+{ return ListView_MapIndexToID(m_hwnd, index);
+}
+BOOL CCtrlListView::RedrawItems(int iFirst, int iLast)
+{ return ListView_RedrawItems(m_hwnd, iFirst, iLast);
+}
+void CCtrlListView::RemoveAllGroups()
+{ ListView_RemoveAllGroups(m_hwnd);
+}
+int CCtrlListView::RemoveGroup(int iGroupId)
+{ return ListView_RemoveGroup(m_hwnd, iGroupId);
+}
+BOOL CCtrlListView::Scroll(int dx, int dy)
+{ return ListView_Scroll(m_hwnd, dx, dy);
+}
+BOOL CCtrlListView::SetBkColor(COLORREF clrBk)
+{ return ListView_SetBkColor(m_hwnd, clrBk);
+}
+BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki)
+{ return ListView_SetBkImage(m_hwnd, plvbki);
+}
+BOOL CCtrlListView::SetCallbackMask(UINT mask)
+{ return ListView_SetCallbackMask(m_hwnd, mask);
+}
+void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck)
+{ ListView_SetCheckState(m_hwnd, iIndex, fCheck);
+}
+BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol)
+{ return ListView_SetColumn(m_hwnd, iCol, pcol);
+}
+BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray)
+{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+BOOL CCtrlListView::SetColumnWidth(int iCol, int cx)
+{ return ListView_SetColumnWidth(m_hwnd, iCol, cx);
+}
+void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle);
+}
+void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle);
+}
+int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp)
+{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp);
+}
+void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)
+{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor)
+{ return ListView_SetHotCursor(m_hwnd, hCursor);
+}
+INT CCtrlListView::SetHotItem(INT iIndex)
+{ return ListView_SetHotItem(m_hwnd, iIndex);
+}
+void CCtrlListView::SetHoverTime(uint32_t dwHoverTime)
+{ ListView_SetHoverTime(m_hwnd, dwHoverTime);
+}
+uint32_t CCtrlListView::SetIconSpacing(int cx, int cy)
+{ return ListView_SetIconSpacing(m_hwnd, cx, cy);
+}
+HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList)
+{ return ListView_SetImageList(m_hwnd, himl, iImageList);
+}
+BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip)
+{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip);
+}
+BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim)
+{ return ListView_SetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color)
+{ return ListView_SetInsertMarkColor(m_hwnd, color);
+}
+BOOL CCtrlListView::SetItem(const LVITEM *pitem)
+{ return ListView_SetItem(m_hwnd, pitem);
+}
+void CCtrlListView::SetItemCount(int cItems)
+{ ListView_SetItemCount(m_hwnd, cItems);
+}
+void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags)
+{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags);
+}
+BOOL CCtrlListView::SetItemPosition(int i, int x, int y)
+{ return ListView_SetItemPosition(m_hwnd, i, x, y);
+}
+void CCtrlListView::SetItemPosition32(int iItem, int x, int y)
+{ ListView_SetItemPosition32(m_hwnd, iItem, x, y);
+}
+void CCtrlListView::SetItemState(int i, UINT state, UINT mask)
+{ ListView_SetItemState(m_hwnd, i, state, mask);
+}
+void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText)
+{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText);
+}
+COLORREF CCtrlListView::SetOutlineColor(COLORREF color)
+{ return ListView_SetOutlineColor(m_hwnd, color);
+}
+void CCtrlListView::SetSelectedColumn(int iCol)
+{ ListView_SetSelectedColumn(m_hwnd, iCol);
+}
+INT CCtrlListView::SetSelectionMark(INT iIndex)
+{ return ListView_SetSelectionMark(m_hwnd, iIndex);
+}
+BOOL CCtrlListView::SetTextBkColor(COLORREF clrText)
+{ return ListView_SetTextBkColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTextColor(COLORREF clrText)
+{ return ListView_SetTextColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo)
+{ return ListView_SetTileInfo(m_hwnd, plvtinfo);
+}
+BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo)
+{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::SetToolTips(HWND ToolTip)
+{ return ListView_SetToolTips(m_hwnd, ToolTip);
+}
+BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode)
+{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+int CCtrlListView::SetView(uint32_t iView)
+{ return ListView_SetView(m_hwnd, iView);
+}
+void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc)
+{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv)
+{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv);
+}
+BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort);
+}
+BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort);
+}
+INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const
+{ return ListView_SubItemHitTest(m_hwnd, pInfo);
+}
+BOOL CCtrlListView::Update(int iItem)
+{ return ListView_Update(m_hwnd, iItem);
+}
diff --git a/src/mir_core/src/Linux/CCtrlMButton.cpp b/src/mir_core/src/Linux/CCtrlMButton.cpp index 8d9198e144..cc05a3a8e4 100644 --- a/src/mir_core/src/Linux/CCtrlMButton.cpp +++ b/src/mir_core/src/Linux/CCtrlMButton.cpp @@ -1,62 +1,62 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlMButton - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(hIcon), - m_toolTip(tooltip) -{} - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(::Skin_LoadIcon(iCoreIcon)), - m_toolTip(tooltip) -{} - -CCtrlMButton::~CCtrlMButton() -{ - ::IcoLib_ReleaseIcon(m_hIcon); -} - -void CCtrlMButton::OnInit() -{ - CCtrlButton::OnInit(); - - SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); - SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); - SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); -} - -void CCtrlMButton::MakeFlat() -{ - SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); -} - -void CCtrlMButton::MakePush() -{ - SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlMButton
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(hIcon),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(::Skin_LoadIcon(iCoreIcon)),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::~CCtrlMButton()
+{
+ ::IcoLib_ReleaseIcon(m_hIcon);
+}
+
+void CCtrlMButton::OnInit()
+{
+ CCtrlButton::OnInit();
+
+ SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon);
+ SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0);
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0);
+}
+
+void CCtrlMButton::MakeFlat()
+{
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0);
+}
+
+void CCtrlMButton::MakePush()
+{
+ SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0);
+}
diff --git a/src/mir_core/src/Linux/CCtrlPages.cpp b/src/mir_core/src/Linux/CCtrlPages.cpp index 512c32e142..c2a95553c5 100644 --- a/src/mir_core/src/Linux/CCtrlPages.cpp +++ b/src/mir_core/src/Linux/CCtrlPages.cpp @@ -1,411 +1,411 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static volatile long g_order = 1; - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlPages - -struct CCtrlPages::TPageInfo : public MZeroedObject -{ - TPageInfo() - { - m_iOrder = InterlockedIncrement(&g_order); - } - - ~TPageInfo() - { - if (m_hIcon) - DestroyIcon(m_hIcon); - } - - int m_iOrder; - ptrW m_ptszHeader; - HICON m_hIcon; - bool m_bChanged, m_bScheduledResize; - CDlgBase *m_pDlg; -}; - -CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId), - m_hIml(nullptr), - m_pActivePage(nullptr), - m_pages(4, NumericKeySortT) -{} - -void CCtrlPages::OnInit() -{ - CSuper::OnInit(); - Subclass(); - - for (auto &it : m_pages) - InsertPage(it); - m_pages.destroy(); - - ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); - - TPageInfo *info = GetCurrPage(); - if (info) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - int tabCount; - - switch (msg) { - case WM_SIZE: - if (TPageInfo *pCurrInfo = GetCurrPage()) { - tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p == nullptr) - continue; - if (p == pCurrInfo) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - else p->m_bScheduledResize = true; - } - } - break; - - case PSM_CHANGED: - if (TPageInfo *info = GetCurrPage()) - info->m_bChanged = TRUE; - return TRUE; - - case PSM_FORCECHANGED: - tabCount = GetCount(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p) { - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - if (pshn.hdr.hwndFrom != nullptr) - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) -{ - TPageInfo *info = new TPageInfo; - info->m_pDlg = pDlg; - info->m_hIcon = hIcon; - info->m_ptszHeader = mir_wstrdup(ptszName); - - if (m_hwnd != nullptr) { - InsertPage(info); - - if (GetCount() == 1) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - } - m_pages.insert(info); -} - -void CCtrlPages::ActivatePage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - if (info == nullptr || info->m_pDlg == nullptr) - return; - - if (m_pActivePage != nullptr) - ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); - - m_pActivePage = info->m_pDlg; - if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - - TabCtrl_SetCurSel(m_hwnd, iPage); - ShowPage(m_pActivePage); - ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); -} - -void CCtrlPages::CheckRowCount() -{ - int iRowCount = TabCtrl_GetRowCount(m_hwnd); - if (m_numRows != iRowCount) { - m_numRows = iRowCount; - for (auto &p : m_pages) - p->m_bScheduledResize = true; - } -} - -int CCtrlPages::GetCount() -{ - return TabCtrl_GetItemCount(m_hwnd); -} - -CDlgBase* CCtrlPages::GetNthPage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - return (info == nullptr) ? nullptr : info->m_pDlg; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) -{ - int tabCount = TabCtrl_GetItemCount(m_hwnd); - for (int i = 0; i < tabCount; i++) { - TCITEM tci; - tci.mask = TCIF_PARAM | TCIF_IMAGE; - TabCtrl_GetItem(m_hwnd, i, &tci); - TPageInfo *pPage = (TPageInfo *)tci.lParam; - if (pPage == nullptr) - continue; - - if (pPage->m_pDlg == pDlg) - return i; - } - - return -1; -} - -void CCtrlPages::InsertPage(TPageInfo *pPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)pPage; - tci.pszText = TranslateW_LP(pPage->m_ptszHeader); - if (pPage->m_hIcon) { - if (!m_hIml) { - m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - TabCtrl_SetImageList(m_hwnd, m_hIml); - } - - tci.mask |= TCIF_IMAGE; - tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); - } - - TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); - - CheckRowCount(); -} - -void CCtrlPages::RemovePage(int iPage) -{ - TPageInfo *p = GetItemPage(iPage); - if (p == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, iPage); - m_pages.remove(p); - delete p; - - CheckRowCount(); -} - -void CCtrlPages::ShowPage(CDlgBase *pDlg) -{ - if (pDlg->GetHwnd() == nullptr) { - pDlg->SetParent(m_hwnd); - pDlg->Create(); - - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); - - EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(pDlg->GetHwnd(), SW_SHOW); -} - -void CCtrlPages::SwapPages(int idx1, int idx2) -{ - TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); - if (p1 == nullptr || p2 == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, idx1); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)p1; - tci.pszText = TranslateW_LP(p1->m_ptszHeader); - TabCtrl_InsertItem(m_hwnd, idx2, &tci); -} - -BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) -{ - TPageInfo *info; - PSHNOTIFY pshn; - - switch (pnmh->code) { - case TCN_SELCHANGING: - if (info = GetCurrPage()) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - return TRUE; - - case TCN_SELCHANGE: - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - - if (info = GetCurrPage()) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - else m_pActivePage = nullptr; - return TRUE; - } - - return FALSE; -} - -void CCtrlPages::OnReset() -{ - CSuper::OnReset(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -bool CCtrlPages::OnApply() -{ - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - if (m_pActivePage != nullptr) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) - return false; - } - - pshn.hdr.code = PSN_APPLY; - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(m_hwnd, i); - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - m_pActivePage = p->m_pDlg; - m_pActivePage->Show(); - return false; - } - } - - CSuper::OnApply(); - return true; -} - -void CCtrlPages::OnDestroy() -{ - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; - if (pDlg->GetHwnd()) - pDlg->Close(); - delete p; - } - - TabCtrl_DeleteAllItems(m_hwnd); - - if (m_hIml) { - TabCtrl_SetImageList(m_hwnd, nullptr); - ImageList_Destroy(m_hIml); - } - - CSuper::OnDestroy(); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static volatile long g_order = 1;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlPages
+
+struct CCtrlPages::TPageInfo : public MZeroedObject
+{
+ TPageInfo()
+ {
+ m_iOrder = InterlockedIncrement(&g_order);
+ }
+
+ ~TPageInfo()
+ {
+ if (m_hIcon)
+ DestroyIcon(m_hIcon);
+ }
+
+ int m_iOrder;
+ ptrW m_ptszHeader;
+ HICON m_hIcon;
+ bool m_bChanged, m_bScheduledResize;
+ CDlgBase *m_pDlg;
+};
+
+CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId),
+ m_hIml(nullptr),
+ m_pActivePage(nullptr),
+ m_pages(4, NumericKeySortT)
+{}
+
+void CCtrlPages::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+
+ for (auto &it : m_pages)
+ InsertPage(it);
+ m_pages.destroy();
+
+ ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT);
+
+ TPageInfo *info = GetCurrPage();
+ if (info) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int tabCount;
+
+ switch (msg) {
+ case WM_SIZE:
+ if (TPageInfo *pCurrInfo = GetCurrPage()) {
+ tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p == nullptr)
+ continue;
+ if (p == pCurrInfo) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ else p->m_bScheduledResize = true;
+ }
+ }
+ break;
+
+ case PSM_CHANGED:
+ if (TPageInfo *info = GetCurrPage())
+ info->m_bChanged = TRUE;
+ return TRUE;
+
+ case PSM_FORCECHANGED:
+ tabCount = GetCount();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p) {
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ if (pshn.hdr.hwndFrom != nullptr)
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ }
+ break;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg)
+{
+ TPageInfo *info = new TPageInfo;
+ info->m_pDlg = pDlg;
+ info->m_hIcon = hIcon;
+ info->m_ptszHeader = mir_wstrdup(ptszName);
+
+ if (m_hwnd != nullptr) {
+ InsertPage(info);
+
+ if (GetCount() == 1) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ }
+ m_pages.insert(info);
+}
+
+void CCtrlPages::ActivatePage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ if (info == nullptr || info->m_pDlg == nullptr)
+ return;
+
+ if (m_pActivePage != nullptr)
+ ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE);
+
+ m_pActivePage = info->m_pDlg;
+ if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+
+ TabCtrl_SetCurSel(m_hwnd, iPage);
+ ShowPage(m_pActivePage);
+ ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0);
+}
+
+void CCtrlPages::CheckRowCount()
+{
+ int iRowCount = TabCtrl_GetRowCount(m_hwnd);
+ if (m_numRows != iRowCount) {
+ m_numRows = iRowCount;
+ for (auto &p : m_pages)
+ p->m_bScheduledResize = true;
+ }
+}
+
+int CCtrlPages::GetCount()
+{
+ return TabCtrl_GetItemCount(m_hwnd);
+}
+
+CDlgBase* CCtrlPages::GetNthPage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ return (info == nullptr) ? nullptr : info->m_pDlg;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage()
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, iPage, &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+int CCtrlPages::GetDlgIndex(CDlgBase *pDlg)
+{
+ int tabCount = TabCtrl_GetItemCount(m_hwnd);
+ for (int i = 0; i < tabCount; i++) {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM | TCIF_IMAGE;
+ TabCtrl_GetItem(m_hwnd, i, &tci);
+ TPageInfo *pPage = (TPageInfo *)tci.lParam;
+ if (pPage == nullptr)
+ continue;
+
+ if (pPage->m_pDlg == pDlg)
+ return i;
+ }
+
+ return -1;
+}
+
+void CCtrlPages::InsertPage(TPageInfo *pPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)pPage;
+ tci.pszText = TranslateW_LP(pPage->m_ptszHeader);
+ if (pPage->m_hIcon) {
+ if (!m_hIml) {
+ m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ TabCtrl_SetImageList(m_hwnd, m_hIml);
+ }
+
+ tci.mask |= TCIF_IMAGE;
+ tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon);
+ }
+
+ TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci);
+
+ CheckRowCount();
+}
+
+void CCtrlPages::RemovePage(int iPage)
+{
+ TPageInfo *p = GetItemPage(iPage);
+ if (p == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, iPage);
+ m_pages.remove(p);
+ delete p;
+
+ CheckRowCount();
+}
+
+void CCtrlPages::ShowPage(CDlgBase *pDlg)
+{
+ if (pDlg->GetHwnd() == nullptr) {
+ pDlg->SetParent(m_hwnd);
+ pDlg->Create();
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE);
+
+ EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ ShowWindow(pDlg->GetHwnd(), SW_SHOW);
+}
+
+void CCtrlPages::SwapPages(int idx1, int idx2)
+{
+ TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2);
+ if (p1 == nullptr || p2 == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, idx1);
+
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)p1;
+ tci.pszText = TranslateW_LP(p1->m_ptszHeader);
+ TabCtrl_InsertItem(m_hwnd, idx2, &tci);
+}
+
+BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh)
+{
+ TPageInfo *info;
+ PSHNOTIFY pshn;
+
+ switch (pnmh->code) {
+ case TCN_SELCHANGING:
+ if (info = GetCurrPage()) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ return TRUE;
+
+ case TCN_SELCHANGE:
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+
+ if (info = GetCurrPage()) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ else m_pActivePage = nullptr;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CCtrlPages::OnReset()
+{
+ CSuper::OnReset();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+bool CCtrlPages::OnApply()
+{
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ if (m_pActivePage != nullptr) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn))
+ return false;
+ }
+
+ pshn.hdr.code = PSN_APPLY;
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) {
+ TabCtrl_SetCurSel(m_hwnd, i);
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+ m_pActivePage = p->m_pDlg;
+ m_pActivePage->Show();
+ return false;
+ }
+ }
+
+ CSuper::OnApply();
+ return true;
+}
+
+void CCtrlPages::OnDestroy()
+{
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr;
+ if (pDlg->GetHwnd())
+ pDlg->Close();
+ delete p;
+ }
+
+ TabCtrl_DeleteAllItems(m_hwnd);
+
+ if (m_hIml) {
+ TabCtrl_SetImageList(m_hwnd, nullptr);
+ ImageList_Destroy(m_hIml);
+ }
+
+ CSuper::OnDestroy();
+}
diff --git a/src/mir_core/src/Linux/CCtrlSlider.cpp b/src/mir_core/src/Linux/CCtrlSlider.cpp index 69aeb24796..9938736e42 100644 --- a/src/mir_core/src/Linux/CCtrlSlider.cpp +++ b/src/mir_core/src/Linux/CCtrlSlider.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSlider class - -CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax) -{ - m_bNotifiable = true; -} - -BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == WM_HSCROLL) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSlider::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetPosition()); - return true; -} - -void CCtrlSlider::OnReset() -{ - SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int CCtrlSlider::GetPosition() const -{ - return SendMsg(TBM_GETPOS, 0, 0); -} - -void CCtrlSlider::SetPosition(int wPos) -{ - SendMsg(TBM_SETPOS, TRUE, wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSlider class
+
+CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == WM_HSCROLL) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSlider::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetPosition());
+ return true;
+}
+
+void CCtrlSlider::OnReset()
+{
+ SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int CCtrlSlider::GetPosition() const
+{
+ return SendMsg(TBM_GETPOS, 0, 0);
+}
+
+void CCtrlSlider::SetPosition(int wPos)
+{
+ SendMsg(TBM_SETPOS, TRUE, wPos);
+}
diff --git a/src/mir_core/src/Linux/CCtrlSpin.cpp b/src/mir_core/src/Linux/CCtrlSpin.cpp index 54d43e933a..54dc5ffd07 100644 --- a/src/mir_core/src/Linux/CCtrlSpin.cpp +++ b/src/mir_core/src/Linux/CCtrlSpin.cpp @@ -1,81 +1,81 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSpin class - -CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax), - m_wCurr(0) -{} - -BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - auto *pEvent = (NMUPDOWN *)pnmh; - m_wCurr = pEvent->iPos + pEvent->iDelta; - - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSpin::OnApply() -{ - CSuper::OnApply(); - - m_wCurr = SendMsg(UDM_GETPOS, 0, 0); - if (m_dbLink != nullptr) - SaveInt(m_wCurr); - - HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); - if (hwndBuddy) { - wchar_t buf[100]; - _itow(m_wCurr, buf, 10); - ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); - } - - return true; -} - -void CCtrlSpin::OnReset() -{ - SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int16_t CCtrlSpin::GetPosition() -{ - return m_wCurr; -} - -void CCtrlSpin::SetPosition(int16_t wPos) -{ - SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSpin class
+
+CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax),
+ m_wCurr(0)
+{}
+
+BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == UDN_DELTAPOS) {
+ auto *pEvent = (NMUPDOWN *)pnmh;
+ m_wCurr = pEvent->iPos + pEvent->iDelta;
+
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSpin::OnApply()
+{
+ CSuper::OnApply();
+
+ m_wCurr = SendMsg(UDM_GETPOS, 0, 0);
+ if (m_dbLink != nullptr)
+ SaveInt(m_wCurr);
+
+ HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0);
+ if (hwndBuddy) {
+ wchar_t buf[100];
+ _itow(m_wCurr, buf, 10);
+ ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf));
+ }
+
+ return true;
+}
+
+void CCtrlSpin::OnReset()
+{
+ SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int16_t CCtrlSpin::GetPosition()
+{
+ return m_wCurr;
+}
+
+void CCtrlSpin::SetPosition(int16_t wPos)
+{
+ SendMsg(UDM_SETPOS, 0, m_wCurr = wPos);
+}
diff --git a/src/mir_core/src/Linux/CCtrlTreeOpts.cpp b/src/mir_core/src/Linux/CCtrlTreeOpts.cpp index 13a6b79bd3..f0b1be1fb9 100644 --- a/src/mir_core/src/Linux/CCtrlTreeOpts.cpp +++ b/src/mir_core/src/Linux/CCtrlTreeOpts.cpp @@ -1,216 +1,216 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_GRPOPEN, IMG_GRPCLOSED }; - -CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId): - CCtrlTreeView(dlg, ctrlId), - m_options(5) -{ -} - -CCtrlTreeOpts::~CCtrlTreeOpts() -{ -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, CMOption<bool> &option) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::CMOPTION); - p->m_option = &option; - m_options.insert(p, m_options.getCount()); -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, bool &option) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::BOOL); - p->m_pBool = &option; - m_options.insert(p, m_options.getCount()); -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, uint32_t &option, uint32_t mask) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::MASK); - p->m_pDword = &option; - p->m_mask = mask; - m_options.insert(p, m_options.getCount()); -} - -BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh) -{ - switch (pnmh->code) { - case TVN_KEYDOWN: - { - LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh; - HTREEITEM hti; - if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection())) - ProcessItemClick(hti); - } - break; - - case NM_CLICK: - TVHITTESTINFO htti; - htti.pt.x = (short)LOWORD(GetMessagePos()); - htti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pnmh->hwndFrom, &htti.pt); - if (HitTest(&htti)) - if (htti.flags & TVHT_ONITEMICON) - ProcessItemClick(htti.hItem); - break; - - case TVN_ITEMEXPANDED: - LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW)pnmh; - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvi.hItem = lpnmtv->itemNew.hItem; - tvi.iImage = tvi.iSelectedImage = (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED; - SendMessage(pnmh->hwndFrom, TVM_SETITEM, 0, (LPARAM)&tvi); - break; - } - - return CSuper::OnNotify(idCtrl, pnmh); -} - -void CCtrlTreeOpts::OnInit() -{ - CSuper::OnInit(); - - SelectItem(nullptr); - DeleteAllItems(); - - HIMAGELIST hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_MIRANDA); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_TICK); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_NOTICK); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPOPEN); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPSHUT); - SetImageList(hImgLst, TVSIL_NORMAL); - - /* build options tree. based on code from IcoLib */ - for (auto &it : m_options) { - if (it->m_pwszSection) { - HTREEITEM hSection = FindNamedItem(nullptr, it->m_pwszSection); - if (!hSection) { - TVINSERTSTRUCT tvis = {}; - tvis.hParent = hSection; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = (LPWSTR)it->m_pwszSection; - tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD; - tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN; - hSection = InsertItem(&tvis); - } - - bool bValue; - switch (it->m_type) { - case COptionsItem::CMOPTION: - bValue = *it->m_option; - break; - case COptionsItem::BOOL: - bValue = *it->m_pBool; - break; - case COptionsItem::MASK: - bValue = (*it->m_pDword & it->m_mask) != 0; - break; - default: - continue; - } - - TVINSERTSTRUCT tvis = {}; - tvis.hParent = hSection; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = (LPWSTR)it->m_pwszName; - tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED; - tvis.item.lParam = m_options.indexOf(&it); - tvis.item.iImage = tvis.item.iSelectedImage = (bValue) ? IMG_CHECK : IMG_NOCHECK; - - it->m_hItem = InsertItem(&tvis); - } - } - - TranslateTree(); - ShowWindow(m_hwnd, SW_SHOW); - SelectItem(FindNamedItem(nullptr, nullptr)); -} - -void CCtrlTreeOpts::OnDestroy() -{ - ImageList_Destroy(GetImageList(TVSIL_NORMAL)); -} - -bool CCtrlTreeOpts::OnApply() -{ - CSuper::OnApply(); - - for (auto &it : m_options) { - TVITEMEX tvi; - GetItem(it->m_hItem, &tvi); - - bool bValue = (tvi.iImage == IMG_CHECK); - switch (it->m_type) { - case COptionsItem::CMOPTION: - *it->m_option = bValue; - break; - case COptionsItem::BOOL: - *it->m_pBool = bValue; - break; - case COptionsItem::MASK: - if (bValue) - *it->m_pDword |= it->m_mask; - else - *it->m_pDword &= ~it->m_mask; - break; - } - } - return true; -} - -void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti) -{ - TVITEMEX tvi; - GetItem(hti, &tvi); - switch (tvi.iImage) { - case IMG_GRPOPEN: - tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED; - Expand(tvi.hItem, TVE_COLLAPSE); - break; - - case IMG_GRPCLOSED: - tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN; - Expand(tvi.hItem, TVE_EXPAND); - break; - - case IMG_CHECK: - tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK; - NotifyChange(); - break; - - case IMG_NOCHECK: - tvi.iImage = tvi.iSelectedImage = IMG_CHECK; - NotifyChange(); - break; - } - - SetItem(&tvi); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_GRPOPEN, IMG_GRPCLOSED };
+
+CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId):
+ CCtrlTreeView(dlg, ctrlId),
+ m_options(5)
+{
+}
+
+CCtrlTreeOpts::~CCtrlTreeOpts()
+{
+}
+
+void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, CMOption<bool> &option)
+{
+ auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::CMOPTION);
+ p->m_option = &option;
+ m_options.insert(p, m_options.getCount());
+}
+
+void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, bool &option)
+{
+ auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::BOOL);
+ p->m_pBool = &option;
+ m_options.insert(p, m_options.getCount());
+}
+
+void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, uint32_t &option, uint32_t mask)
+{
+ auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::MASK);
+ p->m_pDword = &option;
+ p->m_mask = mask;
+ m_options.insert(p, m_options.getCount());
+}
+
+BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh)
+{
+ switch (pnmh->code) {
+ case TVN_KEYDOWN:
+ {
+ LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh;
+ HTREEITEM hti;
+ if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection()))
+ ProcessItemClick(hti);
+ }
+ break;
+
+ case NM_CLICK:
+ TVHITTESTINFO htti;
+ htti.pt.x = (short)LOWORD(GetMessagePos());
+ htti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(pnmh->hwndFrom, &htti.pt);
+ if (HitTest(&htti))
+ if (htti.flags & TVHT_ONITEMICON)
+ ProcessItemClick(htti.hItem);
+ break;
+
+ case TVN_ITEMEXPANDED:
+ LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW)pnmh;
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = lpnmtv->itemNew.hItem;
+ tvi.iImage = tvi.iSelectedImage = (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED;
+ SendMessage(pnmh->hwndFrom, TVM_SETITEM, 0, (LPARAM)&tvi);
+ break;
+ }
+
+ return CSuper::OnNotify(idCtrl, pnmh);
+}
+
+void CCtrlTreeOpts::OnInit()
+{
+ CSuper::OnInit();
+
+ SelectItem(nullptr);
+ DeleteAllItems();
+
+ HIMAGELIST hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_MIRANDA);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_TICK);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_NOTICK);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPOPEN);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPSHUT);
+ SetImageList(hImgLst, TVSIL_NORMAL);
+
+ /* build options tree. based on code from IcoLib */
+ for (auto &it : m_options) {
+ if (it->m_pwszSection) {
+ HTREEITEM hSection = FindNamedItem(nullptr, it->m_pwszSection);
+ if (!hSection) {
+ TVINSERTSTRUCT tvis = {};
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = (LPWSTR)it->m_pwszSection;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD;
+ tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN;
+ hSection = InsertItem(&tvis);
+ }
+
+ bool bValue;
+ switch (it->m_type) {
+ case COptionsItem::CMOPTION:
+ bValue = *it->m_option;
+ break;
+ case COptionsItem::BOOL:
+ bValue = *it->m_pBool;
+ break;
+ case COptionsItem::MASK:
+ bValue = (*it->m_pDword & it->m_mask) != 0;
+ break;
+ default:
+ continue;
+ }
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = (LPWSTR)it->m_pwszName;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED;
+ tvis.item.lParam = m_options.indexOf(&it);
+ tvis.item.iImage = tvis.item.iSelectedImage = (bValue) ? IMG_CHECK : IMG_NOCHECK;
+
+ it->m_hItem = InsertItem(&tvis);
+ }
+ }
+
+ TranslateTree();
+ ShowWindow(m_hwnd, SW_SHOW);
+ SelectItem(FindNamedItem(nullptr, nullptr));
+}
+
+void CCtrlTreeOpts::OnDestroy()
+{
+ ImageList_Destroy(GetImageList(TVSIL_NORMAL));
+}
+
+bool CCtrlTreeOpts::OnApply()
+{
+ CSuper::OnApply();
+
+ for (auto &it : m_options) {
+ TVITEMEX tvi;
+ GetItem(it->m_hItem, &tvi);
+
+ bool bValue = (tvi.iImage == IMG_CHECK);
+ switch (it->m_type) {
+ case COptionsItem::CMOPTION:
+ *it->m_option = bValue;
+ break;
+ case COptionsItem::BOOL:
+ *it->m_pBool = bValue;
+ break;
+ case COptionsItem::MASK:
+ if (bValue)
+ *it->m_pDword |= it->m_mask;
+ else
+ *it->m_pDword &= ~it->m_mask;
+ break;
+ }
+ }
+ return true;
+}
+
+void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti)
+{
+ TVITEMEX tvi;
+ GetItem(hti, &tvi);
+ switch (tvi.iImage) {
+ case IMG_GRPOPEN:
+ tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED;
+ Expand(tvi.hItem, TVE_COLLAPSE);
+ break;
+
+ case IMG_GRPCLOSED:
+ tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN;
+ Expand(tvi.hItem, TVE_EXPAND);
+ break;
+
+ case IMG_CHECK:
+ tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK;
+ NotifyChange();
+ break;
+
+ case IMG_NOCHECK:
+ tvi.iImage = tvi.iSelectedImage = IMG_CHECK;
+ NotifyChange();
+ break;
+ }
+
+ SetItem(&tvi);
+}
diff --git a/src/mir_core/src/Linux/CCtrlTreeView.cpp b/src/mir_core/src/Linux/CCtrlTreeView.cpp index 390f1618fc..5d2174fc16 100644 --- a/src/mir_core/src/Linux/CCtrlTreeView.cpp +++ b/src/mir_core/src/Linux/CCtrlTreeView.cpp @@ -1,817 +1,817 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId) -{ - HICON hIcon = Skin_LoadIcon(iconId); - int res = ImageList_AddIcon(hIml, hIcon); - IcoLib_ReleaseIcon(hIcon); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlTreeView - -CCtrlTreeView::CCtrlTreeView(CDlgBase *dlg, int ctrlId) : - CCtrlBase(dlg, ctrlId), - m_dwFlags(0), - m_hDragItem(nullptr) -{} - -void CCtrlTreeView::SetFlags(uint32_t dwFlags) -{ - if (dwFlags & MTREE_CHECKBOX) - m_bCheckBox = true; - - if (dwFlags & MTREE_MULTISELECT) - m_bMultiSelect = true; - - if (dwFlags & MTREE_DND) { - m_bDndEnabled = true; - m_bDragging = false; - m_hDragItem = nullptr; - } -} - -void CCtrlTreeView::OnInit() -{ - CSuper::OnInit(); - - Subclass(); - - if (m_bCheckBox) { - HIMAGELIST himlCheckBoxes = ::ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2); - ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK); - ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK); - SetImageList(himlCheckBoxes, TVSIL_NORMAL); - } -} - -void CCtrlTreeView::OnDestroy() -{ - if (m_bCheckBox) - ::ImageList_Destroy(GetImageList(TVSIL_NORMAL)); - - CSuper::OnDestroy(); -} - -HTREEITEM CCtrlTreeView::MoveItemAbove(HTREEITEM hItem, HTREEITEM hInsertAfter, HTREEITEM hParent) -{ - if (hItem == nullptr || hInsertAfter == nullptr) - return nullptr; - - if (hItem == hInsertAfter) - return hItem; - - wchar_t name[128]; - TVINSERTSTRUCT tvis = {}; - tvis.itemex.mask = (UINT)-1; - tvis.itemex.pszText = name; - tvis.itemex.cchTextMax = _countof(name); - tvis.itemex.hItem = hItem; - if (!GetItem(&tvis.itemex)) - return nullptr; - - OBJLIST<TVINSERTSTRUCT> arChildren(1); - for (HTREEITEM p = GetChild(hItem); p; p = GetNextSibling(p)) { - wchar_t buf[128]; - TVINSERTSTRUCT tvis2 = {}; - tvis2.itemex.mask = (UINT)-1; - tvis2.itemex.pszText = buf; - tvis2.itemex.cchTextMax = _countof(buf); - tvis2.itemex.hItem = p; - if (GetItem(&tvis2.itemex)) { - tvis2.itemex.pszText = mir_wstrdup(tvis2.itemex.pszText); - arChildren.insert(new TVINSERTSTRUCT(tvis2)); - - tvis2.itemex.lParam = 0; - SetItem(&tvis2.itemex); - } - } - - // the pointed lParam will be freed inside TVN_DELETEITEM - // so lets substitute it with 0 - LPARAM saveOldData = tvis.itemex.lParam; - tvis.itemex.lParam = 0; - SetItem(&tvis.itemex); - - // now current item contain lParam = 0 we can delete it. the memory will be kept. - DeleteItem(hItem); - - for (auto &it : arChildren) - DeleteItem(it->itemex.hItem); - - tvis.itemex.stateMask = tvis.itemex.state; - tvis.itemex.lParam = saveOldData; - tvis.hParent = hParent; - tvis.hInsertAfter = hInsertAfter; - auto hNewItem = InsertItem(&tvis); - - hInsertAfter = nullptr; - for (auto &it : arChildren) { - it->hParent = hNewItem; - it->hInsertAfter = hInsertAfter; - hInsertAfter = InsertItem(it); - - mir_free(it->itemex.pszText); - } - - return hNewItem; -} - -LRESULT CCtrlTreeView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - TVHITTESTINFO hti; - - switch (msg) { - case WM_MOUSEMOVE: - if (m_bDragging) { - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - HitTest(&hti); - if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) { - HTREEITEM it = hti.hItem; - hti.pt.y -= GetItemHeight() / 2; - HitTest(&hti); - if (!(hti.flags & TVHT_ABOVE)) - SetInsertMark(hti.hItem, 1); - else - SetInsertMark(it, 0); - } - else { - if (hti.flags & TVHT_ABOVE) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); - if (hti.flags & TVHT_BELOW) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); - SetInsertMark(nullptr, 0); - } - } - break; - - case WM_LBUTTONUP: - if (m_bDragging) { - SetInsertMark(nullptr, 0); - m_bDragging = false; - ReleaseCapture(); - - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam) - GetItemHeight() / 2; - HitTest(&hti); - if (m_hDragItem == hti.hItem) - break; - - if (hti.flags & TVHT_ABOVE) - hti.hItem = TVI_FIRST; - else if (hti.flags & TVHT_BELOW) - hti.hItem = TVI_LAST; - - HTREEITEM insertAfter = hti.hItem, hParent; - if (insertAfter != TVI_FIRST) { - hParent = GetParent(insertAfter); - if (GetChild(insertAfter) != nullptr) { - hParent = insertAfter; - insertAfter = TVI_FIRST; - } - } - else hParent = nullptr; - - HTREEITEM FirstItem = nullptr; - if (m_bMultiSelect) { - LIST<_TREEITEM> arItems(10); - GetSelected(arItems); - - // Proceed moving - for (auto &it : arItems) { - if (!insertAfter) - break; - if (GetParent(it) != hParent) // prevent subitems from being inserted at the same level - continue; - - insertAfter = MoveItemAbove(it, insertAfter, hParent); - if (it == arItems[0]) - FirstItem = insertAfter; - } - } - else FirstItem = MoveItemAbove(m_hDragItem, insertAfter, hParent); - if (FirstItem) - SelectItem(FirstItem); - - NotifyChange(); - } - break; - - case WM_LBUTTONDOWN: - if (!m_bMultiSelect) - break; - - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - if (!TreeView_HitTest(m_hwnd, &hti)) { - UnselectAll(); - break; - } - - if (!m_bDndEnabled) - if (!(wParam & (MK_CONTROL | MK_SHIFT)) || !(hti.flags & (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))) { - UnselectAll(); - TreeView_SelectItem(m_hwnd, hti.hItem); - break; - } - - if (wParam & MK_CONTROL) { - LIST<_TREEITEM> selected(1); - GetSelected(selected); - - // Check if have to deselect it - for (int i = 0; i < selected.getCount(); i++) { - if (selected[i] == hti.hItem) { - // Deselect it - UnselectAll(); - selected.remove(i); - - if (i > 0) - hti.hItem = selected[0]; - else if (i < selected.getCount()) - hti.hItem = selected[i]; - else - hti.hItem = nullptr; - break; - } - } - - TreeView_SelectItem(m_hwnd, hti.hItem); - Select(selected); - } - else if (wParam & MK_SHIFT) { - HTREEITEM hItem = TreeView_GetSelection(m_hwnd); - if (hItem == nullptr) - break; - - LIST<_TREEITEM> selected(1); - GetSelected(selected); - - TreeView_SelectItem(m_hwnd, hti.hItem); - Select(selected); - SelectRange(hItem, hti.hItem); - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_RCLICK: OnRightClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE; - case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE; - case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE; - case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE; - case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE; - - case TVN_BEGINDRAG: - OnBeginDrag(&evt); - - // user-defined can clear the event code to disable dragging - if (m_bDndEnabled && pnmh->code) { - ::SetCapture(m_hwnd); - m_bDragging = true; - m_hDragItem = evt.nmtv->itemNew.hItem; - SelectItem(m_hDragItem); - } - return TRUE; - - case TVN_KEYDOWN: - if (evt.nmtvkey->wVKey == VK_SPACE) { - evt.hItem = GetSelection(); - if (m_bCheckBox) - InvertCheck(evt.hItem); - OnItemChanged(&evt); - NotifyChange(); - } - - OnKeyDown(&evt); - return TRUE; - } - - if (pnmh->code == NM_CLICK) { - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pnmh->hwndFrom, &hti.pt); - if (HitTest(&hti)) { - if (m_bCheckBox && (hti.flags & TVHT_ONITEMICON) || !m_bCheckBox && (hti.flags & TVHT_ONITEMSTATEICON)) { - if (m_bCheckBox) - InvertCheck(hti.hItem); - else - SelectItem(hti.hItem); - - evt.hItem = hti.hItem; - OnItemChanged(&evt); - NotifyChange(); - } - } - } - - return FALSE; -} - -void CCtrlTreeView::InvertCheck(HTREEITEM hItem) -{ - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATEEX; - tvi.hItem = hItem; - if (!GetItem(&tvi)) - return; - - if (IsWinVerVistaPlus() && (tvi.uStateEx & TVIS_EX_DISABLED)) - return; - - tvi.iImage = tvi.iSelectedImage = !tvi.iImage; - SetItem(&tvi); - - SelectItem(hItem); -} - -void CCtrlTreeView::TranslateItem(HTREEITEM hItem) -{ - TVITEMEX tvi; - wchar_t buf[128]; - GetItem(hItem, &tvi, buf, _countof(buf)); - tvi.pszText = TranslateW_LP(tvi.pszText); - SetItem(&tvi); -} - -void CCtrlTreeView::TranslateTree() -{ - HTREEITEM hItem = GetRoot(); - while (hItem) { - TranslateItem(hItem); - - HTREEITEM hItemTmp = nullptr; - if (hItemTmp = GetChild(hItem)) - hItem = hItemTmp; - else if (hItemTmp = GetNextSibling(hItem)) - hItem = hItemTmp; - else { - while (true) { - if (!(hItem = GetParent(hItem))) - break; - if (hItemTmp = GetNextSibling(hItem)) { - hItem = hItemTmp; - break; - } - } - } - } -} - -HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const wchar_t *name) -{ - TVITEMEX tvi = { 0 }; - wchar_t str[MAX_PATH]; - - if (hItem) - tvi.hItem = GetChild(hItem); - else - tvi.hItem = GetRoot(); - - if (!name) - return tvi.hItem; - - tvi.mask = TVIF_TEXT; - tvi.pszText = str; - tvi.cchTextMax = _countof(str); - - while (tvi.hItem) { - GetItem(&tvi); - - if (!mir_wstrcmp(tvi.pszText, name)) - return tvi.hItem; - - tvi.hItem = GetNextSibling(tvi.hItem); - } - return nullptr; -} - -void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi) const -{ - memset(tvi, 0, sizeof(*tvi)); - tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvi->hItem = hItem; - GetItem(tvi); -} - -void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, wchar_t *szText, int iTextLength) const -{ - memset(tvi, 0, sizeof(*tvi)); - tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT; - tvi->hItem = hItem; - tvi->pszText = szText; - tvi->cchTextMax = iTextLength; - GetItem(tvi); -} - -bool CCtrlTreeView::IsSelected(HTREEITEM hItem) -{ - return (TVIS_SELECTED & TreeView_GetItemState(m_hwnd, hItem, TVIS_SELECTED)) == TVIS_SELECTED; -} - -void CCtrlTreeView::Select(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, TVIS_SELECTED, TVIS_SELECTED); -} - -void CCtrlTreeView::Unselect(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_SELECTED); -} - -void CCtrlTreeView::DropHilite(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, TVIS_DROPHILITED, TVIS_DROPHILITED); -} - -void CCtrlTreeView::DropUnhilite(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_DROPHILITED); -} - -void CCtrlTreeView::SelectAll() -{ - TreeView_SelectItem(m_hwnd, nullptr); - - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - Select(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::UnselectAll() -{ - TreeView_SelectItem(m_hwnd, nullptr); - - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - Unselect(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::SelectRange(HTREEITEM hStart, HTREEITEM hEnd) -{ - int start = 0, end = 0, i = 0; - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (hItem == hStart) - start = i; - if (hItem == hEnd) - end = i; - - i++; - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } - - if (end < start) { - int tmp = start; - start = end; - end = tmp; - } - - i = 0; - hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (i >= start) - Select(hItem); - if (i == end) - break; - - i++; - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -int CCtrlTreeView::GetNumSelected() -{ - int ret = 0; - for (HTREEITEM hItem = TreeView_GetRoot(m_hwnd); hItem; hItem = TreeView_GetNextSibling(m_hwnd, hItem)) - if (IsSelected(hItem)) - ret++; - - return ret; -} - -void CCtrlTreeView::GetSelected(LIST<_TREEITEM> &selected) -{ - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (IsSelected(hItem)) - selected.insert(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::Select(LIST<_TREEITEM> &selected) -{ - for (auto &it : selected) - if (it != nullptr) - Select(it); -} - -void CCtrlTreeView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - HTREEITEM hItem = GetSelection(); - if (hItem != nullptr) { - pos.pCtrl = this; - pos.hItem = hItem; - - RECT rc; - GetItemRect(hItem, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - TVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (HitTest(&hti) && (hti.flags & TVHT_ONITEM)) { - pos.hItem = hti.hItem; - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem) -{ return TreeView_CreateDragImage(m_hwnd, hItem); -} - -void CCtrlTreeView::DeleteAllItems() -{ TreeView_DeleteAllItems(m_hwnd); -} - -void CCtrlTreeView::DeleteItem(HTREEITEM hItem) -{ TreeView_DeleteItem(m_hwnd, hItem); -} - -HWND CCtrlTreeView::EditLabel(HTREEITEM hItem) -{ return TreeView_EditLabel(m_hwnd, hItem); -} - -void CCtrlTreeView::EndEditLabelNow(BOOL cancel) -{ TreeView_EndEditLabelNow(m_hwnd, cancel); -} - -void CCtrlTreeView::EnsureVisible(HTREEITEM hItem) -{ TreeView_EnsureVisible(m_hwnd, hItem); -} - -void CCtrlTreeView::Expand(HTREEITEM hItem, uint32_t flag) -{ TreeView_Expand(m_hwnd, hItem, flag); -} - -COLORREF CCtrlTreeView::GetBkColor() const -{ return TreeView_GetBkColor(m_hwnd); -} - -uint32_t CCtrlTreeView::GetCheckState(HTREEITEM hItem) const -{ return TreeView_GetCheckState(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem) const -{ return TreeView_GetChild(m_hwnd, hItem); -} - -int CCtrlTreeView::GetCount() const -{ return TreeView_GetCount(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetDropHilight() const -{ return TreeView_GetDropHilight(m_hwnd); -} - -HWND CCtrlTreeView::GetEditControl() const -{ return TreeView_GetEditControl(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetFirstVisible() const -{ return TreeView_GetFirstVisible(m_hwnd); -} - -HIMAGELIST CCtrlTreeView::GetImageList(int iImage) const -{ return TreeView_GetImageList(m_hwnd, iImage); -} - -int CCtrlTreeView::GetIndent() const -{ return TreeView_GetIndent(m_hwnd); -} - -COLORREF CCtrlTreeView::GetInsertMarkColor() const -{ return TreeView_GetInsertMarkColor(m_hwnd); -} - -bool CCtrlTreeView::GetItem(TVITEMEX *tvi) const -{ return TreeView_GetItem(m_hwnd, tvi) == TRUE; -} - -int CCtrlTreeView::GetItemHeight() const -{ return TreeView_GetItemHeight(m_hwnd); -} - -void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect) const -{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect); -} - -uint32_t CCtrlTreeView::GetItemState(HTREEITEM hItem, uint32_t stateMask) const -{ return TreeView_GetItemState(m_hwnd, hItem, stateMask); -} - -HTREEITEM CCtrlTreeView::GetLastVisible() const -{ return TreeView_GetLastVisible(m_hwnd); -} - -COLORREF CCtrlTreeView::GetLineColor() const -{ return TreeView_GetLineColor(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, uint32_t flag) const -{ return TreeView_GetNextItem(m_hwnd, hItem, flag); -} - -HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem) const -{ return TreeView_GetNextSibling(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem) const -{ return TreeView_GetNextVisible(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem) const -{ return TreeView_GetParent(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem) const -{ return TreeView_GetPrevSibling(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem) const -{ return TreeView_GetPrevVisible(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetRoot() const -{ return TreeView_GetRoot(m_hwnd); -} - -uint32_t CCtrlTreeView::GetScrollTime() const -{ return TreeView_GetScrollTime(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetSelection() const -{ return TreeView_GetSelection(m_hwnd); -} - -COLORREF CCtrlTreeView::GetTextColor() const -{ return TreeView_GetTextColor(m_hwnd); -} - -HWND CCtrlTreeView::GetToolTips() const -{ return TreeView_GetToolTips(m_hwnd); -} - -BOOL CCtrlTreeView::GetUnicodeFormat() const -{ return TreeView_GetUnicodeFormat(m_hwnd); -} - -unsigned CCtrlTreeView::GetVisibleCount() const -{ return TreeView_GetVisibleCount(m_hwnd); -} - -HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti) const -{ return TreeView_HitTest(m_hwnd, hti); -} - -HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis) -{ return TreeView_InsertItem(m_hwnd, tvis); -} - -void CCtrlTreeView::Select(HTREEITEM hItem, uint32_t flag) -{ TreeView_Select(m_hwnd, hItem, flag); -} - -void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem) -{ TreeView_SelectDropTarget(m_hwnd, hItem); -} - -void CCtrlTreeView::SelectItem(HTREEITEM hItem) -{ TreeView_SelectItem(m_hwnd, hItem); -} - -void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem) -{ TreeView_SelectSetFirstVisible(m_hwnd, hItem); -} - -COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack) -{ return TreeView_SetBkColor(m_hwnd, clBack); -} - -void CCtrlTreeView::SetCheckState(HTREEITEM hItem, uint32_t state) -{ TreeView_SetCheckState(m_hwnd, hItem, state); -} - -void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage) -{ TreeView_SetImageList(m_hwnd, hIml, iImage); -} - -void CCtrlTreeView::SetIndent(int iIndent) -{ TreeView_SetIndent(m_hwnd, iIndent); -} - -void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter) -{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter); -} - -COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark) -{ return TreeView_SetInsertMarkColor(m_hwnd, clMark); -} - -void CCtrlTreeView::SetItem(TVITEMEX *tvi) -{ TreeView_SetItem(m_hwnd, tvi); -} - -void CCtrlTreeView::SetItemHeight(short cyItem) -{ TreeView_SetItemHeight(m_hwnd, cyItem); -} - -void CCtrlTreeView::SetItemState(HTREEITEM hItem, uint32_t state, uint32_t stateMask) -{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask); -} - -COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine) -{ return TreeView_SetLineColor(m_hwnd, clLine); -} - -void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime) -{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime); -} - -COLORREF CCtrlTreeView::SetTextColor(COLORREF clText) -{ return TreeView_SetTextColor(m_hwnd, clText); -} - -HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips) -{ return TreeView_SetToolTips(m_hwnd, hwndToolTips); -} - -BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode) -{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode); -} - -void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse) -{ TreeView_SortChildren(m_hwnd, hItem, fRecurse); -} - -void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse) -{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId)
+{
+ HICON hIcon = Skin_LoadIcon(iconId);
+ int res = ImageList_AddIcon(hIml, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlTreeView
+
+CCtrlTreeView::CCtrlTreeView(CDlgBase *dlg, int ctrlId) :
+ CCtrlBase(dlg, ctrlId),
+ m_dwFlags(0),
+ m_hDragItem(nullptr)
+{}
+
+void CCtrlTreeView::SetFlags(uint32_t dwFlags)
+{
+ if (dwFlags & MTREE_CHECKBOX)
+ m_bCheckBox = true;
+
+ if (dwFlags & MTREE_MULTISELECT)
+ m_bMultiSelect = true;
+
+ if (dwFlags & MTREE_DND) {
+ m_bDndEnabled = true;
+ m_bDragging = false;
+ m_hDragItem = nullptr;
+ }
+}
+
+void CCtrlTreeView::OnInit()
+{
+ CSuper::OnInit();
+
+ Subclass();
+
+ if (m_bCheckBox) {
+ HIMAGELIST himlCheckBoxes = ::ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2);
+ ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK);
+ ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK);
+ SetImageList(himlCheckBoxes, TVSIL_NORMAL);
+ }
+}
+
+void CCtrlTreeView::OnDestroy()
+{
+ if (m_bCheckBox)
+ ::ImageList_Destroy(GetImageList(TVSIL_NORMAL));
+
+ CSuper::OnDestroy();
+}
+
+HTREEITEM CCtrlTreeView::MoveItemAbove(HTREEITEM hItem, HTREEITEM hInsertAfter, HTREEITEM hParent)
+{
+ if (hItem == nullptr || hInsertAfter == nullptr)
+ return nullptr;
+
+ if (hItem == hInsertAfter)
+ return hItem;
+
+ wchar_t name[128];
+ TVINSERTSTRUCT tvis = {};
+ tvis.itemex.mask = (UINT)-1;
+ tvis.itemex.pszText = name;
+ tvis.itemex.cchTextMax = _countof(name);
+ tvis.itemex.hItem = hItem;
+ if (!GetItem(&tvis.itemex))
+ return nullptr;
+
+ OBJLIST<TVINSERTSTRUCT> arChildren(1);
+ for (HTREEITEM p = GetChild(hItem); p; p = GetNextSibling(p)) {
+ wchar_t buf[128];
+ TVINSERTSTRUCT tvis2 = {};
+ tvis2.itemex.mask = (UINT)-1;
+ tvis2.itemex.pszText = buf;
+ tvis2.itemex.cchTextMax = _countof(buf);
+ tvis2.itemex.hItem = p;
+ if (GetItem(&tvis2.itemex)) {
+ tvis2.itemex.pszText = mir_wstrdup(tvis2.itemex.pszText);
+ arChildren.insert(new TVINSERTSTRUCT(tvis2));
+
+ tvis2.itemex.lParam = 0;
+ SetItem(&tvis2.itemex);
+ }
+ }
+
+ // the pointed lParam will be freed inside TVN_DELETEITEM
+ // so lets substitute it with 0
+ LPARAM saveOldData = tvis.itemex.lParam;
+ tvis.itemex.lParam = 0;
+ SetItem(&tvis.itemex);
+
+ // now current item contain lParam = 0 we can delete it. the memory will be kept.
+ DeleteItem(hItem);
+
+ for (auto &it : arChildren)
+ DeleteItem(it->itemex.hItem);
+
+ tvis.itemex.stateMask = tvis.itemex.state;
+ tvis.itemex.lParam = saveOldData;
+ tvis.hParent = hParent;
+ tvis.hInsertAfter = hInsertAfter;
+ auto hNewItem = InsertItem(&tvis);
+
+ hInsertAfter = nullptr;
+ for (auto &it : arChildren) {
+ it->hParent = hNewItem;
+ it->hInsertAfter = hInsertAfter;
+ hInsertAfter = InsertItem(it);
+
+ mir_free(it->itemex.pszText);
+ }
+
+ return hNewItem;
+}
+
+LRESULT CCtrlTreeView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ TVHITTESTINFO hti;
+
+ switch (msg) {
+ case WM_MOUSEMOVE:
+ if (m_bDragging) {
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam);
+ HitTest(&hti);
+ if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) {
+ HTREEITEM it = hti.hItem;
+ hti.pt.y -= GetItemHeight() / 2;
+ HitTest(&hti);
+ if (!(hti.flags & TVHT_ABOVE))
+ SetInsertMark(hti.hItem, 1);
+ else
+ SetInsertMark(it, 0);
+ }
+ else {
+ if (hti.flags & TVHT_ABOVE) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
+ if (hti.flags & TVHT_BELOW) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
+ SetInsertMark(nullptr, 0);
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (m_bDragging) {
+ SetInsertMark(nullptr, 0);
+ m_bDragging = false;
+ ReleaseCapture();
+
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam) - GetItemHeight() / 2;
+ HitTest(&hti);
+ if (m_hDragItem == hti.hItem)
+ break;
+
+ if (hti.flags & TVHT_ABOVE)
+ hti.hItem = TVI_FIRST;
+ else if (hti.flags & TVHT_BELOW)
+ hti.hItem = TVI_LAST;
+
+ HTREEITEM insertAfter = hti.hItem, hParent;
+ if (insertAfter != TVI_FIRST) {
+ hParent = GetParent(insertAfter);
+ if (GetChild(insertAfter) != nullptr) {
+ hParent = insertAfter;
+ insertAfter = TVI_FIRST;
+ }
+ }
+ else hParent = nullptr;
+
+ HTREEITEM FirstItem = nullptr;
+ if (m_bMultiSelect) {
+ LIST<_TREEITEM> arItems(10);
+ GetSelected(arItems);
+
+ // Proceed moving
+ for (auto &it : arItems) {
+ if (!insertAfter)
+ break;
+ if (GetParent(it) != hParent) // prevent subitems from being inserted at the same level
+ continue;
+
+ insertAfter = MoveItemAbove(it, insertAfter, hParent);
+ if (it == arItems[0])
+ FirstItem = insertAfter;
+ }
+ }
+ else FirstItem = MoveItemAbove(m_hDragItem, insertAfter, hParent);
+ if (FirstItem)
+ SelectItem(FirstItem);
+
+ NotifyChange();
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ if (!m_bMultiSelect)
+ break;
+
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam);
+ if (!TreeView_HitTest(m_hwnd, &hti)) {
+ UnselectAll();
+ break;
+ }
+
+ if (!m_bDndEnabled)
+ if (!(wParam & (MK_CONTROL | MK_SHIFT)) || !(hti.flags & (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))) {
+ UnselectAll();
+ TreeView_SelectItem(m_hwnd, hti.hItem);
+ break;
+ }
+
+ if (wParam & MK_CONTROL) {
+ LIST<_TREEITEM> selected(1);
+ GetSelected(selected);
+
+ // Check if have to deselect it
+ for (int i = 0; i < selected.getCount(); i++) {
+ if (selected[i] == hti.hItem) {
+ // Deselect it
+ UnselectAll();
+ selected.remove(i);
+
+ if (i > 0)
+ hti.hItem = selected[0];
+ else if (i < selected.getCount())
+ hti.hItem = selected[i];
+ else
+ hti.hItem = nullptr;
+ break;
+ }
+ }
+
+ TreeView_SelectItem(m_hwnd, hti.hItem);
+ Select(selected);
+ }
+ else if (wParam & MK_SHIFT) {
+ HTREEITEM hItem = TreeView_GetSelection(m_hwnd);
+ if (hItem == nullptr)
+ break;
+
+ LIST<_TREEITEM> selected(1);
+ GetSelected(selected);
+
+ TreeView_SelectItem(m_hwnd, hti.hItem);
+ Select(selected);
+ SelectRange(hItem, hti.hItem);
+ }
+ break;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_RCLICK: OnRightClick(&evt); return TRUE;
+ case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE;
+ case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE;
+ case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE;
+ case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE;
+ case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE;
+ case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+ case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE;
+
+ case TVN_BEGINDRAG:
+ OnBeginDrag(&evt);
+
+ // user-defined can clear the event code to disable dragging
+ if (m_bDndEnabled && pnmh->code) {
+ ::SetCapture(m_hwnd);
+ m_bDragging = true;
+ m_hDragItem = evt.nmtv->itemNew.hItem;
+ SelectItem(m_hDragItem);
+ }
+ return TRUE;
+
+ case TVN_KEYDOWN:
+ if (evt.nmtvkey->wVKey == VK_SPACE) {
+ evt.hItem = GetSelection();
+ if (m_bCheckBox)
+ InvertCheck(evt.hItem);
+ OnItemChanged(&evt);
+ NotifyChange();
+ }
+
+ OnKeyDown(&evt);
+ return TRUE;
+ }
+
+ if (pnmh->code == NM_CLICK) {
+ TVHITTESTINFO hti;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(pnmh->hwndFrom, &hti.pt);
+ if (HitTest(&hti)) {
+ if (m_bCheckBox && (hti.flags & TVHT_ONITEMICON) || !m_bCheckBox && (hti.flags & TVHT_ONITEMSTATEICON)) {
+ if (m_bCheckBox)
+ InvertCheck(hti.hItem);
+ else
+ SelectItem(hti.hItem);
+
+ evt.hItem = hti.hItem;
+ OnItemChanged(&evt);
+ NotifyChange();
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+void CCtrlTreeView::InvertCheck(HTREEITEM hItem)
+{
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATEEX;
+ tvi.hItem = hItem;
+ if (!GetItem(&tvi))
+ return;
+
+ if (IsWinVerVistaPlus() && (tvi.uStateEx & TVIS_EX_DISABLED))
+ return;
+
+ tvi.iImage = tvi.iSelectedImage = !tvi.iImage;
+ SetItem(&tvi);
+
+ SelectItem(hItem);
+}
+
+void CCtrlTreeView::TranslateItem(HTREEITEM hItem)
+{
+ TVITEMEX tvi;
+ wchar_t buf[128];
+ GetItem(hItem, &tvi, buf, _countof(buf));
+ tvi.pszText = TranslateW_LP(tvi.pszText);
+ SetItem(&tvi);
+}
+
+void CCtrlTreeView::TranslateTree()
+{
+ HTREEITEM hItem = GetRoot();
+ while (hItem) {
+ TranslateItem(hItem);
+
+ HTREEITEM hItemTmp = nullptr;
+ if (hItemTmp = GetChild(hItem))
+ hItem = hItemTmp;
+ else if (hItemTmp = GetNextSibling(hItem))
+ hItem = hItemTmp;
+ else {
+ while (true) {
+ if (!(hItem = GetParent(hItem)))
+ break;
+ if (hItemTmp = GetNextSibling(hItem)) {
+ hItem = hItemTmp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const wchar_t *name)
+{
+ TVITEMEX tvi = { 0 };
+ wchar_t str[MAX_PATH];
+
+ if (hItem)
+ tvi.hItem = GetChild(hItem);
+ else
+ tvi.hItem = GetRoot();
+
+ if (!name)
+ return tvi.hItem;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = _countof(str);
+
+ while (tvi.hItem) {
+ GetItem(&tvi);
+
+ if (!mir_wstrcmp(tvi.pszText, name))
+ return tvi.hItem;
+
+ tvi.hItem = GetNextSibling(tvi.hItem);
+ }
+ return nullptr;
+}
+
+void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi) const
+{
+ memset(tvi, 0, sizeof(*tvi));
+ tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE;
+ tvi->hItem = hItem;
+ GetItem(tvi);
+}
+
+void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, wchar_t *szText, int iTextLength) const
+{
+ memset(tvi, 0, sizeof(*tvi));
+ tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT;
+ tvi->hItem = hItem;
+ tvi->pszText = szText;
+ tvi->cchTextMax = iTextLength;
+ GetItem(tvi);
+}
+
+bool CCtrlTreeView::IsSelected(HTREEITEM hItem)
+{
+ return (TVIS_SELECTED & TreeView_GetItemState(m_hwnd, hItem, TVIS_SELECTED)) == TVIS_SELECTED;
+}
+
+void CCtrlTreeView::Select(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, TVIS_SELECTED, TVIS_SELECTED);
+}
+
+void CCtrlTreeView::Unselect(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_SELECTED);
+}
+
+void CCtrlTreeView::DropHilite(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, TVIS_DROPHILITED, TVIS_DROPHILITED);
+}
+
+void CCtrlTreeView::DropUnhilite(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_DROPHILITED);
+}
+
+void CCtrlTreeView::SelectAll()
+{
+ TreeView_SelectItem(m_hwnd, nullptr);
+
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ Select(hItem);
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+void CCtrlTreeView::UnselectAll()
+{
+ TreeView_SelectItem(m_hwnd, nullptr);
+
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ Unselect(hItem);
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+void CCtrlTreeView::SelectRange(HTREEITEM hStart, HTREEITEM hEnd)
+{
+ int start = 0, end = 0, i = 0;
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ if (hItem == hStart)
+ start = i;
+ if (hItem == hEnd)
+ end = i;
+
+ i++;
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+
+ if (end < start) {
+ int tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ i = 0;
+ hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ if (i >= start)
+ Select(hItem);
+ if (i == end)
+ break;
+
+ i++;
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+int CCtrlTreeView::GetNumSelected()
+{
+ int ret = 0;
+ for (HTREEITEM hItem = TreeView_GetRoot(m_hwnd); hItem; hItem = TreeView_GetNextSibling(m_hwnd, hItem))
+ if (IsSelected(hItem))
+ ret++;
+
+ return ret;
+}
+
+void CCtrlTreeView::GetSelected(LIST<_TREEITEM> &selected)
+{
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ if (IsSelected(hItem))
+ selected.insert(hItem);
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+void CCtrlTreeView::Select(LIST<_TREEITEM> &selected)
+{
+ for (auto &it : selected)
+ if (it != nullptr)
+ Select(it);
+}
+
+void CCtrlTreeView::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+
+ // position is empty, let's fill it using selection
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ HTREEITEM hItem = GetSelection();
+ if (hItem != nullptr) {
+ pos.pCtrl = this;
+ pos.hItem = hItem;
+
+ RECT rc;
+ GetItemRect(hItem, &rc, TRUE);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+ // position is present, let's calculate current item
+ else {
+ TVHITTESTINFO hti;
+ hti.pt = pos.pt;
+ ScreenToClient(m_hwnd, &hti.pt);
+ if (HitTest(&hti) && (hti.flags & TVHT_ONITEM)) {
+ pos.hItem = hti.hItem;
+ return;
+ }
+ }
+
+ CSuper::GetCaretPos(pos);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem)
+{ return TreeView_CreateDragImage(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::DeleteAllItems()
+{ TreeView_DeleteAllItems(m_hwnd);
+}
+
+void CCtrlTreeView::DeleteItem(HTREEITEM hItem)
+{ TreeView_DeleteItem(m_hwnd, hItem);
+}
+
+HWND CCtrlTreeView::EditLabel(HTREEITEM hItem)
+{ return TreeView_EditLabel(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::EndEditLabelNow(BOOL cancel)
+{ TreeView_EndEditLabelNow(m_hwnd, cancel);
+}
+
+void CCtrlTreeView::EnsureVisible(HTREEITEM hItem)
+{ TreeView_EnsureVisible(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::Expand(HTREEITEM hItem, uint32_t flag)
+{ TreeView_Expand(m_hwnd, hItem, flag);
+}
+
+COLORREF CCtrlTreeView::GetBkColor() const
+{ return TreeView_GetBkColor(m_hwnd);
+}
+
+uint32_t CCtrlTreeView::GetCheckState(HTREEITEM hItem) const
+{ return TreeView_GetCheckState(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem) const
+{ return TreeView_GetChild(m_hwnd, hItem);
+}
+
+int CCtrlTreeView::GetCount() const
+{ return TreeView_GetCount(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetDropHilight() const
+{ return TreeView_GetDropHilight(m_hwnd);
+}
+
+HWND CCtrlTreeView::GetEditControl() const
+{ return TreeView_GetEditControl(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetFirstVisible() const
+{ return TreeView_GetFirstVisible(m_hwnd);
+}
+
+HIMAGELIST CCtrlTreeView::GetImageList(int iImage) const
+{ return TreeView_GetImageList(m_hwnd, iImage);
+}
+
+int CCtrlTreeView::GetIndent() const
+{ return TreeView_GetIndent(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetInsertMarkColor() const
+{ return TreeView_GetInsertMarkColor(m_hwnd);
+}
+
+bool CCtrlTreeView::GetItem(TVITEMEX *tvi) const
+{ return TreeView_GetItem(m_hwnd, tvi) == TRUE;
+}
+
+int CCtrlTreeView::GetItemHeight() const
+{ return TreeView_GetItemHeight(m_hwnd);
+}
+
+void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect) const
+{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect);
+}
+
+uint32_t CCtrlTreeView::GetItemState(HTREEITEM hItem, uint32_t stateMask) const
+{ return TreeView_GetItemState(m_hwnd, hItem, stateMask);
+}
+
+HTREEITEM CCtrlTreeView::GetLastVisible() const
+{ return TreeView_GetLastVisible(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetLineColor() const
+{ return TreeView_GetLineColor(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, uint32_t flag) const
+{ return TreeView_GetNextItem(m_hwnd, hItem, flag);
+}
+
+HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem) const
+{ return TreeView_GetNextSibling(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem) const
+{ return TreeView_GetNextVisible(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem) const
+{ return TreeView_GetParent(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem) const
+{ return TreeView_GetPrevSibling(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem) const
+{ return TreeView_GetPrevVisible(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetRoot() const
+{ return TreeView_GetRoot(m_hwnd);
+}
+
+uint32_t CCtrlTreeView::GetScrollTime() const
+{ return TreeView_GetScrollTime(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetSelection() const
+{ return TreeView_GetSelection(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetTextColor() const
+{ return TreeView_GetTextColor(m_hwnd);
+}
+
+HWND CCtrlTreeView::GetToolTips() const
+{ return TreeView_GetToolTips(m_hwnd);
+}
+
+BOOL CCtrlTreeView::GetUnicodeFormat() const
+{ return TreeView_GetUnicodeFormat(m_hwnd);
+}
+
+unsigned CCtrlTreeView::GetVisibleCount() const
+{ return TreeView_GetVisibleCount(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti) const
+{ return TreeView_HitTest(m_hwnd, hti);
+}
+
+HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis)
+{ return TreeView_InsertItem(m_hwnd, tvis);
+}
+
+void CCtrlTreeView::Select(HTREEITEM hItem, uint32_t flag)
+{ TreeView_Select(m_hwnd, hItem, flag);
+}
+
+void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem)
+{ TreeView_SelectDropTarget(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::SelectItem(HTREEITEM hItem)
+{ TreeView_SelectItem(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem)
+{ TreeView_SelectSetFirstVisible(m_hwnd, hItem);
+}
+
+COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack)
+{ return TreeView_SetBkColor(m_hwnd, clBack);
+}
+
+void CCtrlTreeView::SetCheckState(HTREEITEM hItem, uint32_t state)
+{ TreeView_SetCheckState(m_hwnd, hItem, state);
+}
+
+void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage)
+{ TreeView_SetImageList(m_hwnd, hIml, iImage);
+}
+
+void CCtrlTreeView::SetIndent(int iIndent)
+{ TreeView_SetIndent(m_hwnd, iIndent);
+}
+
+void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter)
+{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter);
+}
+
+COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark)
+{ return TreeView_SetInsertMarkColor(m_hwnd, clMark);
+}
+
+void CCtrlTreeView::SetItem(TVITEMEX *tvi)
+{ TreeView_SetItem(m_hwnd, tvi);
+}
+
+void CCtrlTreeView::SetItemHeight(short cyItem)
+{ TreeView_SetItemHeight(m_hwnd, cyItem);
+}
+
+void CCtrlTreeView::SetItemState(HTREEITEM hItem, uint32_t state, uint32_t stateMask)
+{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask);
+}
+
+COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine)
+{ return TreeView_SetLineColor(m_hwnd, clLine);
+}
+
+void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime)
+{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime);
+}
+
+COLORREF CCtrlTreeView::SetTextColor(COLORREF clText)
+{ return TreeView_SetTextColor(m_hwnd, clText);
+}
+
+HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips)
+{ return TreeView_SetToolTips(m_hwnd, hwndToolTips);
+}
+
+BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode)
+{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+
+void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse)
+{ TreeView_SortChildren(m_hwnd, hItem, fRecurse);
+}
+
+void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse)
+{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse);
+}
diff --git a/src/mir_core/src/Linux/CDbLink.cpp b/src/mir_core/src/Linux/CDbLink.cpp index 2a0734cd3b..05925f871b 100644 --- a/src/mir_core/src/Linux/CDbLink.cpp +++ b/src/mir_core/src/Linux/CDbLink.cpp @@ -1,92 +1,92 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbLink class - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) - : CDataLink(type) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_iDefault = iValue; - m_szDefault = nullptr; - dbv.type = DBVT_DELETED; -} - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) - : CDataLink(type), - m_iDefault(0) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_szDefault = mir_wstrdup(szValue); - dbv.type = DBVT_DELETED; -} - -CDbLink::~CDbLink() -{ - mir_free(m_szModule); - mir_free(m_szSetting); - mir_free(m_szDefault); - if (dbv.type != DBVT_DELETED) - db_free(&dbv); -} - -uint32_t CDbLink::LoadInt() -{ - switch (m_type) { - case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); - default: return m_iDefault; - } -} - -void CDbLink::SaveInt(uint32_t value) -{ - switch (m_type) { - case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; - case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; - case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; - } -} - -wchar_t* CDbLink::LoadText() -{ - if (dbv.type != DBVT_DELETED) db_free(&dbv); - if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { - if (dbv.type == DBVT_WCHAR) - return dbv.pwszVal; - return m_szDefault; - } - - dbv.type = DBVT_DELETED; - return m_szDefault; -} - -void CDbLink::SaveText(wchar_t *value) -{ - db_set_ws(0, m_szModule, m_szSetting, value); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbLink class
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue)
+ : CDataLink(type)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_iDefault = iValue;
+ m_szDefault = nullptr;
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue)
+ : CDataLink(type),
+ m_iDefault(0)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_szDefault = mir_wstrdup(szValue);
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::~CDbLink()
+{
+ mir_free(m_szModule);
+ mir_free(m_szSetting);
+ mir_free(m_szDefault);
+ if (dbv.type != DBVT_DELETED)
+ db_free(&dbv);
+}
+
+uint32_t CDbLink::LoadInt()
+{
+ switch (m_type) {
+ case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault);
+ default: return m_iDefault;
+ }
+}
+
+void CDbLink::SaveInt(uint32_t value)
+{
+ switch (m_type) {
+ case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break;
+ case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break;
+ case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break;
+ }
+}
+
+wchar_t* CDbLink::LoadText()
+{
+ if (dbv.type != DBVT_DELETED) db_free(&dbv);
+ if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) {
+ if (dbv.type == DBVT_WCHAR)
+ return dbv.pwszVal;
+ return m_szDefault;
+ }
+
+ dbv.type = DBVT_DELETED;
+ return m_szDefault;
+}
+
+void CDbLink::SaveText(wchar_t *value)
+{
+ db_set_ws(0, m_szModule, m_szSetting, value);
+}
diff --git a/src/mir_core/src/Linux/CDlgBase.cpp b/src/mir_core/src/Linux/CDlgBase.cpp index 3504a7e74c..2ec9ceb8c8 100644 --- a/src/mir_core/src/Linux/CDlgBase.cpp +++ b/src/mir_core/src/Linux/CDlgBase.cpp @@ -1,260 +1,260 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csDialogs; - -static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST<CDlgBase> arDialogs(10, CompareDialogs); - -#pragma comment(lib, "uxtheme") - -///////////////////////////////////////////////////////////////////////////////////////// -// CDlgBase - -static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) -{ - return c1->GetCtrlId() - c2->GetCtrlId(); -} - -static int CompareTimerId(const CTimer *t1, const CTimer *t2) -{ - return t1->GetEventId() - t2->GetEventId(); -} - -CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) - : m_controls(1, CompareControlId), - m_timers(1, CompareTimerId), - m_pPlugin(pPlug) -{ - m_idDialog = idDialog; - m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; -} - -CDlgBase::~CDlgBase() -{ - m_bInitialized = false; // prevent double call of destructor - // if (m_hwnd) - // DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// events - -bool CDlgBase::OnInitDialog() -{ - return true; -} - -bool CDlgBase::OnClose() -{ - return true; -} - -bool CDlgBase::OnApply() -{ - return true; -} - -void CDlgBase::OnChange() -{} - -void CDlgBase::OnDestroy() -{} - -void CDlgBase::OnReset() -{} - -void CDlgBase::OnTimer(CTimer*) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// methods - -void CDlgBase::Close() -{ - // ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); -} - -void CDlgBase::Create() -{ - // CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -int CDlgBase::DoModal() -{ - m_isModal = true; - // return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); - return 0; //!!!!!!!! -} - -void CDlgBase::EndModal(INT_PTR nResult) -{ - // ::EndDialog(m_hwnd, nResult); -} - -HINSTANCE CDlgBase::GetInst() const -{ - return m_pPlugin.getInst(); -} - -void CDlgBase::NotifyChange(void) -{ - if (!m_bInitialized) - return; - - OnChange(); - - // if (m_hwndParent) - // SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); -} - -void CDlgBase::Resize() -{ - // SendMessage(m_hwnd, WM_SIZE, 0, 0); -} - -void CDlgBase::SetCaption(const wchar_t *ptszCaption) -{ - // if (m_hwnd && ptszCaption) - // SetText(ptszCaption); -} - -void CDlgBase::SetDraw(bool bEnable) -{ - // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CDlgBase::Show(int nCmdShow) -{ - if (m_hwnd == nullptr) - Create(); - // ShowWindow(m_hwnd, nCmdShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); -} - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// virtual methods - -int CDlgBase::Resizer(UTILRESIZECONTROL*) -{ - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -BOOL CALLBACK CDlgBase::GlobalFieldEnum(MWindow hwnd, LPARAM lParam) -{ - return TRUE; -} - -INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - return FALSE; -} - -int CDlgBase::GlobalDlgResizer(MWindow hwnd, LPARAM, UTILRESIZECONTROL *urc) -{ - CDlgBase *wnd = CDlgBase::Find(hwnd); - return (wnd == nullptr) ? 0 : wnd->Resizer(urc); -} - -void CDlgBase::ThemeDialogBackground(BOOL tabbed) -{ -} - -void CDlgBase::AddControl(CCtrlBase *ctrl) -{ - m_controls.insert(ctrl); -} - -void CDlgBase::RemoveControl(CCtrlBase *ctrl) -{ - m_controls.remove(ctrl); -} - -void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - (it->*fn)(); -} - -bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - if (!(it->*fn)()) - return false; - - return true; -} - -CCtrlBase* CDlgBase::FindControl(int idCtrl) -{ - CCtrlBase search(nullptr, idCtrl); - return m_controls.find(&search); -} - -CCtrlBase* CDlgBase::FindControl(MWindow hwnd) -{ - for (auto &it : m_controls) - if (it->GetHwnd() == hwnd) - return it; - - return nullptr; -} - -void CDlgBase::AddTimer(CTimer *timer) -{ - m_timers.insert(timer); -} - -void CDlgBase::RemoveTimer(UINT_PTR idEvent) -{ - CTimer search(nullptr, idEvent); - m_timers.remove(&search); -} - -CTimer* CDlgBase::FindTimer(int idEvent) -{ - CTimer search(nullptr, idEvent); - return m_timers.find(&search); -} - -CDlgBase* CDlgBase::Find(MWindow hwnd) -{ - void *bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - return arDialogs.find((CDlgBase*)&bullshit); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static mir_cs csDialogs;
+
+static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+static LIST<CDlgBase> arDialogs(10, CompareDialogs);
+
+#pragma comment(lib, "uxtheme")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDlgBase
+
+static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2)
+{
+ return c1->GetCtrlId() - c2->GetCtrlId();
+}
+
+static int CompareTimerId(const CTimer *t1, const CTimer *t2)
+{
+ return t1->GetEventId() - t2->GetEventId();
+}
+
+CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog)
+ : m_controls(1, CompareControlId),
+ m_timers(1, CompareTimerId),
+ m_pPlugin(pPlug)
+{
+ m_idDialog = idDialog;
+ m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL;
+}
+
+CDlgBase::~CDlgBase()
+{
+ m_bInitialized = false; // prevent double call of destructor
+ // if (m_hwnd)
+ // DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// events
+
+bool CDlgBase::OnInitDialog()
+{
+ return true;
+}
+
+bool CDlgBase::OnClose()
+{
+ return true;
+}
+
+bool CDlgBase::OnApply()
+{
+ return true;
+}
+
+void CDlgBase::OnChange()
+{}
+
+void CDlgBase::OnDestroy()
+{}
+
+void CDlgBase::OnReset()
+{}
+
+void CDlgBase::OnTimer(CTimer*)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// methods
+
+void CDlgBase::Close()
+{
+ // ::SendMessage(m_hwnd, WM_CLOSE, 0, 0);
+}
+
+void CDlgBase::Create()
+{
+ // CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+}
+
+int CDlgBase::DoModal()
+{
+ m_isModal = true;
+ // return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+ return 0; //!!!!!!!!
+}
+
+void CDlgBase::EndModal(INT_PTR nResult)
+{
+ // ::EndDialog(m_hwnd, nResult);
+}
+
+HINSTANCE CDlgBase::GetInst() const
+{
+ return m_pPlugin.getInst();
+}
+
+void CDlgBase::NotifyChange(void)
+{
+ if (!m_bInitialized)
+ return;
+
+ OnChange();
+
+ // if (m_hwndParent)
+ // SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0);
+}
+
+void CDlgBase::Resize()
+{
+ // SendMessage(m_hwnd, WM_SIZE, 0, 0);
+}
+
+void CDlgBase::SetCaption(const wchar_t *ptszCaption)
+{
+ // if (m_hwnd && ptszCaption)
+ // SetText(ptszCaption);
+}
+
+void CDlgBase::SetDraw(bool bEnable)
+{
+ // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CDlgBase::Show(int nCmdShow)
+{
+ if (m_hwnd == nullptr)
+ Create();
+ // ShowWindow(m_hwnd, nCmdShow);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue);
+}
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// virtual methods
+
+int CDlgBase::Resizer(UTILRESIZECONTROL*)
+{
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+BOOL CALLBACK CDlgBase::GlobalFieldEnum(MWindow hwnd, LPARAM lParam)
+{
+ return TRUE;
+}
+
+INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return FALSE;
+}
+
+int CDlgBase::GlobalDlgResizer(MWindow hwnd, LPARAM, UTILRESIZECONTROL *urc)
+{
+ CDlgBase *wnd = CDlgBase::Find(hwnd);
+ return (wnd == nullptr) ? 0 : wnd->Resizer(urc);
+}
+
+void CDlgBase::ThemeDialogBackground(BOOL tabbed)
+{
+}
+
+void CDlgBase::AddControl(CCtrlBase *ctrl)
+{
+ m_controls.insert(ctrl);
+}
+
+void CDlgBase::RemoveControl(CCtrlBase *ctrl)
+{
+ m_controls.remove(ctrl);
+}
+
+void CDlgBase::NotifyControls(void (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ (it->*fn)();
+}
+
+bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ if (!(it->*fn)())
+ return false;
+
+ return true;
+}
+
+CCtrlBase* CDlgBase::FindControl(int idCtrl)
+{
+ CCtrlBase search(nullptr, idCtrl);
+ return m_controls.find(&search);
+}
+
+CCtrlBase* CDlgBase::FindControl(MWindow hwnd)
+{
+ for (auto &it : m_controls)
+ if (it->GetHwnd() == hwnd)
+ return it;
+
+ return nullptr;
+}
+
+void CDlgBase::AddTimer(CTimer *timer)
+{
+ m_timers.insert(timer);
+}
+
+void CDlgBase::RemoveTimer(UINT_PTR idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ m_timers.remove(&search);
+}
+
+CTimer* CDlgBase::FindTimer(int idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ return m_timers.find(&search);
+}
+
+CDlgBase* CDlgBase::Find(MWindow hwnd)
+{
+ void *bullshit[2]; // vfptr + hwnd
+ bullshit[1] = hwnd;
+ return arDialogs.find((CDlgBase*)&bullshit);
+}
diff --git a/src/mir_core/src/Linux/CProgress.cpp b/src/mir_core/src/Linux/CProgress.cpp index 991c6f239d..e2d5321f59 100644 --- a/src/mir_core/src/Linux/CProgress.cpp +++ b/src/mir_core/src/Linux/CProgress.cpp @@ -1,53 +1,53 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlProgress - -CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{ -} - -void CCtrlProgress::SetRange(uint16_t max, uint16_t min) -{ - SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); -} - -void CCtrlProgress::SetPosition(uint16_t value) -{ - SendMsg(PBM_SETPOS, value, 0); -} - -void CCtrlProgress::SetStep(uint16_t value) -{ - SendMsg(PBM_SETSTEP, value, 0); -} - -uint16_t CCtrlProgress::Move(uint16_t delta) -{ - return delta == 0 - ? SendMsg(PBM_STEPIT, 0, 0) - : SendMsg(PBM_DELTAPOS, delta, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlProgress
+
+CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{
+}
+
+void CCtrlProgress::SetRange(uint16_t max, uint16_t min)
+{
+ SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max));
+}
+
+void CCtrlProgress::SetPosition(uint16_t value)
+{
+ SendMsg(PBM_SETPOS, value, 0);
+}
+
+void CCtrlProgress::SetStep(uint16_t value)
+{
+ SendMsg(PBM_SETSTEP, value, 0);
+}
+
+uint16_t CCtrlProgress::Move(uint16_t delta)
+{
+ return delta == 0
+ ? SendMsg(PBM_STEPIT, 0, 0)
+ : SendMsg(PBM_DELTAPOS, delta, 0);
+}
diff --git a/src/mir_core/src/Linux/CSplitter.cpp b/src/mir_core/src/Linux/CSplitter.cpp index e2ee6b6fc8..153beaf95b 100644 --- a/src/mir_core/src/Linux/CSplitter.cpp +++ b/src/mir_core/src/Linux/CSplitter.cpp @@ -1,83 +1,83 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CSplitter - -CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_iPosition(0) -{ -} - -void CSplitter::OnInit() -{ - CSuper::OnInit(); - Subclass(); -} - -LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - RECT rc; - GetClientRect(m_hwnd, &rc); - SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); - return TRUE; - - case WM_LBUTTONDOWN: - SetCapture(m_hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == m_hwnd) { - POINT pt = { 0, 0 }; - GetClientRect(m_hwnd, &rc); - if (rc.right > rc.bottom) { - pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.y; - } - else { - pt.x = LOWORD(GetMessagePos()) + rc.right / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.x; - } - - OnChange(this); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - } - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - return 0; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CSplitter
+
+CSplitter::CSplitter(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_iPosition(0)
+{
+}
+
+void CSplitter::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+}
+
+LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCHITTEST:
+ return HTCLIENT;
+
+ case WM_SETCURSOR:
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE);
+ return TRUE;
+
+ case WM_LBUTTONDOWN:
+ SetCapture(m_hwnd);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (GetCapture() == m_hwnd) {
+ POINT pt = { 0, 0 };
+ GetClientRect(m_hwnd, &rc);
+ if (rc.right > rc.bottom) {
+ pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.y;
+ }
+ else {
+ pt.x = LOWORD(GetMessagePos()) + rc.right / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.x;
+ }
+
+ OnChange(this);
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ ReleaseCapture();
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ return 0;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
diff --git a/src/mir_core/src/Linux/CTimer.cpp b/src/mir_core/src/Linux/CTimer.cpp index 5d3c8b01b1..07fc863c60 100644 --- a/src/mir_core/src/Linux/CTimer.cpp +++ b/src/mir_core/src/Linux/CTimer.cpp @@ -1,93 +1,93 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CTimer - -CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) - : m_wnd(wnd), m_idEvent(idEvent) -{ - if (wnd) - wnd->AddTimer(this); -} - -CTimer::~CTimer() -{ - if (m_wnd) - m_wnd->RemoveTimer(m_idEvent); -} - -BOOL CTimer::OnTimer() -{ - OnEvent(this); - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CTimer::Start(int elapse) -{ - // ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); -} - -bool CTimer::Stop() -{ - // return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TStartParam -{ - CTimer *pTimer; - int period; -}; - -static INT_PTR CALLBACK stubStart(void *param) -{ - auto *p = (TStartParam *)param; - // return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); - return 0; -} - -void CTimer::StartSafe(int elapse) -{ - TStartParam param = { this, elapse }; - CallFunctionSync(stubStart, ¶m); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK stubStop(void *param) -{ - auto *p = (CTimer*)param; - // return ::KillTimer(p->GetHwnd(), p->GetEventId()); - return 0; -} - -void CTimer::StopSafe() -{ - CallFunctionSync(stubStop, this); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CTimer
+
+CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent)
+ : m_wnd(wnd), m_idEvent(idEvent)
+{
+ if (wnd)
+ wnd->AddTimer(this);
+}
+
+CTimer::~CTimer()
+{
+ if (m_wnd)
+ m_wnd->RemoveTimer(m_idEvent);
+}
+
+BOOL CTimer::OnTimer()
+{
+ OnEvent(this);
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTimer::Start(int elapse)
+{
+ // ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr);
+}
+
+bool CTimer::Stop()
+{
+ // return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TStartParam
+{
+ CTimer *pTimer;
+ int period;
+};
+
+static INT_PTR CALLBACK stubStart(void *param)
+{
+ auto *p = (TStartParam *)param;
+ // return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr);
+ return 0;
+}
+
+void CTimer::StartSafe(int elapse)
+{
+ TStartParam param = { this, elapse };
+ CallFunctionSync(stubStart, ¶m);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK stubStop(void *param)
+{
+ auto *p = (CTimer*)param;
+ // return ::KillTimer(p->GetHwnd(), p->GetEventId());
+ return 0;
+}
+
+void CTimer::StopSafe()
+{
+ CallFunctionSync(stubStop, this);
+}
diff --git a/src/mir_core/src/Linux/cctrldate.cpp b/src/mir_core/src/Linux/cctrldate.cpp index 1967cb5678..253b549870 100644 --- a/src/mir_core/src/Linux/cctrldate.cpp +++ b/src/mir_core/src/Linux/cctrldate.cpp @@ -1,49 +1,49 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlDate class - -CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == DTN_DATETIMECHANGE) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -void CCtrlDate::GetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); -} - -void CCtrlDate::SetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlDate class
+
+CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == DTN_DATETIMECHANGE) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void CCtrlDate::GetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate);
+}
+
+void CCtrlDate::SetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate);
+}
diff --git a/src/mir_core/src/Linux/fileutil.cpp b/src/mir_core/src/Linux/fileutil.cpp index a5002f0a66..fadc70e430 100644 --- a/src/mir_core/src/Linux/fileutil.cpp +++ b/src/mir_core/src/Linux/fileutil.cpp @@ -1,89 +1,89 @@ -/* -Copyright (C) 2012-22 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" -#include <unistd.h> - -MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode) -{ - return fopen(T2Utf(pwszFileName), T2Utf(pwszMode)); -} - -MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath) -{ - return chdir(T2Utf(pwszPath)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() -{ - // if (ptr != nullptr) { - // if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { - // ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; - // ptr = nullptr; - // } - // } - return *this; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) -{ - // if (pwszPath != nullptr) - // m_hFind = ::FindFirstFileW(pwszPath, &m_data); -} - -MFilePath::MFileIterator::~MFileIterator() -{ - // if (m_hFind != INVALID_HANDLE_VALUE) - // ::FindClose(m_hFind); -} - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() -{ - // if (m_hFind == INVALID_HANDLE_VALUE) - // return MFilePath::MFileIterator::iterator(nullptr); - - return MFilePath::MFileIterator::iterator(this); -} - -bool MFilePath::MFileIterator::isDir() const -{ - //if (m_hFind == INVALID_HANDLE_VALUE) - // return false; - - return (m_flags & 1) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool MFilePath::isExist() const -{ - return ::access(T2Utf(c_str()), 0) == 0; -} - -bool MFilePath::move(const wchar_t *pwszDest) -{ - return ::rename(T2Utf(c_str()), T2Utf(pwszDest)) != 0; -} - -MFilePath::MFileIterator MFilePath::search() -{ - return MFileIterator(c_str()); -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+#include <unistd.h>
+
+MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode)
+{
+ return fopen(T2Utf(pwszFileName), T2Utf(pwszMode));
+}
+
+MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath)
+{
+ return chdir(T2Utf(pwszPath));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++()
+{
+ // if (ptr != nullptr) {
+ // if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) {
+ // ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE;
+ // ptr = nullptr;
+ // }
+ // }
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath)
+{
+ // if (pwszPath != nullptr)
+ // m_hFind = ::FindFirstFileW(pwszPath, &m_data);
+}
+
+MFilePath::MFileIterator::~MFileIterator()
+{
+ // if (m_hFind != INVALID_HANDLE_VALUE)
+ // ::FindClose(m_hFind);
+}
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin()
+{
+ // if (m_hFind == INVALID_HANDLE_VALUE)
+ // return MFilePath::MFileIterator::iterator(nullptr);
+
+ return MFilePath::MFileIterator::iterator(this);
+}
+
+bool MFilePath::MFileIterator::isDir() const
+{
+ //if (m_hFind == INVALID_HANDLE_VALUE)
+ // return false;
+
+ return (m_flags & 1) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool MFilePath::isExist() const
+{
+ return ::access(T2Utf(c_str()), 0) == 0;
+}
+
+bool MFilePath::move(const wchar_t *pwszDest)
+{
+ return ::rename(T2Utf(c_str()), T2Utf(pwszDest)) != 0;
+}
+
+MFilePath::MFileIterator MFilePath::search()
+{
+ return MFileIterator(c_str());
+}
diff --git a/src/mir_core/src/Linux/strutil.cpp b/src/mir_core/src/Linux/strutil.cpp index a2b854fcc8..8c44a3dd2b 100644 --- a/src/mir_core/src/Linux/strutil.cpp +++ b/src/mir_core/src/Linux/strutil.cpp @@ -1,48 +1,48 @@ -/* -Copyright (C) 2012-22 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" - -MIR_CORE_DLL(char*) strlwr(char *str) -{ - for (char *p = str; *p; p++) - *p = tolower(*p); - - return str; -} - -MIR_CORE_DLL(char*) strupr(char *str) -{ - for (char *p = str; *p; p++) - *p = toupper(*p); - - return str; -} - -MIR_CORE_DLL(char*) strrev(char *str) -{ - if (!str || !*str) - return str; - - char *p1, *p2; - for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { - *p1 ^= *p2; - *p2 ^= *p1; - *p1 ^= *p2; - } - return str; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+MIR_CORE_DLL(char*) strlwr(char *str)
+{
+ for (char *p = str; *p; p++)
+ *p = tolower(*p);
+
+ return str;
+}
+
+MIR_CORE_DLL(char*) strupr(char *str)
+{
+ for (char *p = str; *p; p++)
+ *p = toupper(*p);
+
+ return str;
+}
+
+MIR_CORE_DLL(char*) strrev(char *str)
+{
+ if (!str || !*str)
+ return str;
+
+ char *p1, *p2;
+ for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) {
+ *p1 ^= *p2;
+ *p2 ^= *p1;
+ *p1 ^= *p2;
+ }
+ return str;
+}
diff --git a/src/mir_core/src/Windows/CCtrlBase.cpp b/src/mir_core/src/Windows/CCtrlBase.cpp index 58787b02db..ad80ccffe7 100644 --- a/src/mir_core/src/Windows/CCtrlBase.cpp +++ b/src/mir_core/src/Windows/CCtrlBase.cpp @@ -1,224 +1,224 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csCtrl; - -static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST<CCtrlBase> arControls(10, CompareControls); - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlBase - -CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : - m_parentWnd(wnd), - m_idCtrl(idCtrl) -{ - if (wnd) - wnd->AddControl(this); -} - -CCtrlBase::~CCtrlBase() -{ -} - -void CCtrlBase::OnInit() -{ - m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : nullptr; -} - -void CCtrlBase::OnDestroy() -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = m_hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - pCtrl->Unsubclass(); - arControls.remove(pCtrl); - } - - m_hwnd = nullptr; -} - -bool CCtrlBase::OnApply() -{ - m_bChanged = false; - return true; -} - -void CCtrlBase::OnReset() -{} - -void CCtrlBase::Show(bool bShow) -{ - ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); -} - -void CCtrlBase::Enable(bool bIsEnable) -{ - ::EnableWindow(m_hwnd, bIsEnable); -} - -bool CCtrlBase::Enabled() const -{ - return (m_hwnd) ? IsWindowEnabled(m_hwnd) != 0 : false; -} - -void CCtrlBase::NotifyChange() -{ - if (!m_parentWnd) - return; - - if (m_parentWnd->IsInitialized()) { - m_bChanged = true; - if (!m_bSilent) - m_parentWnd->NotifyChange(); - } - - OnChange(this); -} - -LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const -{ - return ::SendMessage(m_hwnd, Msg, wParam, lParam); -} - -void CCtrlBase::SetText(const wchar_t *text) -{ - ::SetWindowText(m_hwnd, text); -} - -void CCtrlBase::SetTextA(const char *text) -{ - ::SetWindowTextA(m_hwnd, text); -} - -void CCtrlBase::SetDraw(bool bEnable) -{ - ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CCtrlBase::SetInt(int value) -{ - wchar_t buf[32] = { 0 }; - mir_snwprintf(buf, L"%d", value); - SetWindowText(m_hwnd, buf); -} - -wchar_t* CCtrlBase::GetText() const -{ - int length = GetWindowTextLengthW(m_hwnd); - wchar_t *result = (wchar_t *)mir_alloc((length+1) * sizeof(wchar_t)); - if (length) - GetWindowTextW(m_hwnd, result, length+1); - result[length] = 0; - return result; -} - -char* CCtrlBase::GetTextA() const -{ - int length = GetWindowTextLengthA(m_hwnd); - char *result = (char *)mir_alloc((length+1) * sizeof(char)); - if (length) - GetWindowTextA(m_hwnd, result, length+1); - result[length] = 0; - return result; -} - -char* CCtrlBase::GetTextU() const -{ - return mir_utf8encodeW(ptrW(GetText())); -} - -wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const -{ - GetWindowTextW(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextA(char *buf, size_t size) const -{ - GetWindowTextA(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextU(char *buf, size_t size) const -{ - ptrW wszText(GetText()); - strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); - return buf; -} - -int CCtrlBase::GetInt() const -{ - int length = GetWindowTextLengthW(m_hwnd) + 1; - wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); - GetWindowTextW(m_hwnd, result, length); - return _wtoi(result); -} - -void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - pos.iCurr = -1; - - if (pos.pt.x == 0 && pos.pt.y == 0) - GetCursorPos(&pos.pt); -} - -LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) -{ - return FALSE; -} - -LRESULT CALLBACK CCtrlBase::GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - LRESULT res = pCtrl->CustomWndProc(msg, wParam, lParam); - if (res != 0) - return res; - } - - return mir_callNextSubclass(hwnd, GlobalSubclassWndProc, msg, wParam, lParam); -} - -void CCtrlBase::Subclass() -{ - mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); - - mir_cslock lck(csCtrl); - arControls.insert(this); -} - -void CCtrlBase::Unsubclass() -{ - mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static mir_cs csCtrl;
+
+static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+static LIST<CCtrlBase> arControls(10, CompareControls);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlBase
+
+CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) :
+ m_parentWnd(wnd),
+ m_idCtrl(idCtrl)
+{
+ if (wnd)
+ wnd->AddControl(this);
+}
+
+CCtrlBase::~CCtrlBase()
+{
+}
+
+void CCtrlBase::OnInit()
+{
+ m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : nullptr;
+}
+
+void CCtrlBase::OnDestroy()
+{
+ PVOID bullshit[2]; // vfptr + hwnd
+ bullshit[1] = m_hwnd;
+ CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit);
+ if (pCtrl) {
+ pCtrl->Unsubclass();
+ arControls.remove(pCtrl);
+ }
+
+ m_hwnd = nullptr;
+}
+
+bool CCtrlBase::OnApply()
+{
+ m_bChanged = false;
+ return true;
+}
+
+void CCtrlBase::OnReset()
+{}
+
+void CCtrlBase::Show(bool bShow)
+{
+ ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE);
+}
+
+void CCtrlBase::Enable(bool bIsEnable)
+{
+ ::EnableWindow(m_hwnd, bIsEnable);
+}
+
+bool CCtrlBase::Enabled() const
+{
+ return (m_hwnd) ? IsWindowEnabled(m_hwnd) != 0 : false;
+}
+
+void CCtrlBase::NotifyChange()
+{
+ if (!m_parentWnd)
+ return;
+
+ if (m_parentWnd->IsInitialized()) {
+ m_bChanged = true;
+ if (!m_bSilent)
+ m_parentWnd->NotifyChange();
+ }
+
+ OnChange(this);
+}
+
+LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const
+{
+ return ::SendMessage(m_hwnd, Msg, wParam, lParam);
+}
+
+void CCtrlBase::SetText(const wchar_t *text)
+{
+ ::SetWindowText(m_hwnd, text);
+}
+
+void CCtrlBase::SetTextA(const char *text)
+{
+ ::SetWindowTextA(m_hwnd, text);
+}
+
+void CCtrlBase::SetDraw(bool bEnable)
+{
+ ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CCtrlBase::SetInt(int value)
+{
+ wchar_t buf[32] = { 0 };
+ mir_snwprintf(buf, L"%d", value);
+ SetWindowText(m_hwnd, buf);
+}
+
+wchar_t* CCtrlBase::GetText() const
+{
+ int length = GetWindowTextLengthW(m_hwnd);
+ wchar_t *result = (wchar_t *)mir_alloc((length+1) * sizeof(wchar_t));
+ if (length)
+ GetWindowTextW(m_hwnd, result, length+1);
+ result[length] = 0;
+ return result;
+}
+
+char* CCtrlBase::GetTextA() const
+{
+ int length = GetWindowTextLengthA(m_hwnd);
+ char *result = (char *)mir_alloc((length+1) * sizeof(char));
+ if (length)
+ GetWindowTextA(m_hwnd, result, length+1);
+ result[length] = 0;
+ return result;
+}
+
+char* CCtrlBase::GetTextU() const
+{
+ return mir_utf8encodeW(ptrW(GetText()));
+}
+
+wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const
+{
+ GetWindowTextW(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextA(char *buf, size_t size) const
+{
+ GetWindowTextA(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextU(char *buf, size_t size) const
+{
+ ptrW wszText(GetText());
+ strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE);
+ return buf;
+}
+
+int CCtrlBase::GetInt() const
+{
+ int length = GetWindowTextLengthW(m_hwnd) + 1;
+ wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t));
+ GetWindowTextW(m_hwnd, result, length);
+ return _wtoi(result);
+}
+
+void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ pos.iCurr = -1;
+
+ if (pos.pt.x == 0 && pos.pt.y == 0)
+ GetCursorPos(&pos.pt);
+}
+
+LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM)
+{
+ return FALSE;
+}
+
+LRESULT CALLBACK CCtrlBase::GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PVOID bullshit[2]; // vfptr + hwnd
+ bullshit[1] = hwnd;
+ CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit);
+ if (pCtrl) {
+ LRESULT res = pCtrl->CustomWndProc(msg, wParam, lParam);
+ if (res != 0)
+ return res;
+ }
+
+ return mir_callNextSubclass(hwnd, GlobalSubclassWndProc, msg, wParam, lParam);
+}
+
+void CCtrlBase::Subclass()
+{
+ mir_subclassWindow(m_hwnd, GlobalSubclassWndProc);
+
+ mir_cslock lck(csCtrl);
+ arControls.insert(this);
+}
+
+void CCtrlBase::Unsubclass()
+{
+ mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc);
+}
diff --git a/src/mir_core/src/Windows/CCtrlButton.cpp b/src/mir_core/src/Windows/CCtrlButton.cpp index bf84cad74f..b39e855332 100644 --- a/src/mir_core/src/Windows/CCtrlButton.cpp +++ b/src/mir_core/src/Windows/CCtrlButton.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlButton - -CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - -BOOL CCtrlButton::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == BN_CLICKED) - OnClick(this); - return FALSE; -} - -void CCtrlButton::Click() -{ - if (Enabled()) - ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); -} - -bool CCtrlButton::IsPushed() const -{ - return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; -} - -void CCtrlButton::Push(bool bPushed) -{ - if (Enabled()) - ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlButton
+
+CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
+
+BOOL CCtrlButton::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == BN_CLICKED)
+ OnClick(this);
+ return FALSE;
+}
+
+void CCtrlButton::Click()
+{
+ if (Enabled())
+ ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0);
+}
+
+bool CCtrlButton::IsPushed() const
+{
+ return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED;
+}
+
+void CCtrlButton::Push(bool bPushed)
+{
+ if (Enabled())
+ ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlCheck.cpp b/src/mir_core/src/Windows/CCtrlCheck.cpp index 9c1281e8b8..3258dce27f 100644 --- a/src/mir_core/src/Windows/CCtrlCheck.cpp +++ b/src/mir_core/src/Windows/CCtrlCheck.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCheck class - -CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{ - m_bNotifiable = true; -} - -BOOL CCtrlCheck::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlCheck::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetState()); - return true; -} - -void CCtrlCheck::OnReset() -{ - if (m_dbLink != nullptr) - SetState(LoadInt()); -} - -int CCtrlCheck::GetState() const -{ - return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); -} - -void CCtrlCheck::SetState(int state) -{ - ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); -} - -bool CCtrlCheck::IsChecked() -{ - return GetState() == BST_CHECKED; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCheck class
+
+CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlCheck::OnCommand(HWND, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlCheck::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetState());
+ return true;
+}
+
+void CCtrlCheck::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetState(LoadInt());
+}
+
+int CCtrlCheck::GetState() const
+{
+ return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0);
+}
+
+void CCtrlCheck::SetState(int state)
+{
+ ::SendMessage(m_hwnd, BM_SETCHECK, state, 0);
+}
+
+bool CCtrlCheck::IsChecked()
+{
+ return GetState() == BST_CHECKED;
+}
diff --git a/src/mir_core/src/Windows/CCtrlClc.cpp b/src/mir_core/src/Windows/CCtrlClc.cpp index 3e208679d4..14e88335a0 100644 --- a/src/mir_core/src/Windows/CCtrlClc.cpp +++ b/src/mir_core/src/Windows/CCtrlClc.cpp @@ -1,211 +1,211 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlClc - -CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; - switch (pnmh->code) { - case CLN_EXPANDED: OnExpanded(&evt); break; - case CLN_LISTREBUILT: OnListRebuilt(&evt); break; - case CLN_ITEMCHECKED: OnItemChecked(&evt); break; - case CLN_DRAGGING: OnDragging(&evt); break; - case CLN_DROPPED: OnDropped(&evt); break; - case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; - case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; - case CLN_DRAGSTOP: OnDragStop(&evt); break; - case CLN_NEWCONTACT: OnNewContact(&evt); break; - case CLN_CONTACTMOVED: OnContactMoved(&evt); break; - case NM_CLICK: OnClick(&evt); break; - - case CLN_CHECKCHANGED: - OnCheckChanged(&evt); - NotifyChange(); - break; - } - return FALSE; -} - -void CCtrlClc::AddContact(MCONTACT hContact) -{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); -} - -void CCtrlClc::AddGroup(HANDLE hGroup) -{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); -} - -void CCtrlClc::AutoRebuild() -{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); -} - -void CCtrlClc::DeleteItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::EditLabel(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); -} - -void CCtrlClc::EndEditLabel(bool save) -{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); -} - -void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) -{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); -} - -void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) -{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); -} - -HANDLE CCtrlClc::FindContact(MCONTACT hContact) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); -} - -HANDLE CCtrlClc::FindGroup(MGROUP hGroup) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); -} - -COLORREF CCtrlClc::GetBkColor() const -{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); -} - -bool CCtrlClc::GetCheck(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; -} - -int CCtrlClc::GetCount() const -{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); -} - -HWND CCtrlClc::GetEditControl() const -{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); -} - -uint32_t CCtrlClc::GetExpand(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); -} - -int CCtrlClc::GetExtraColumns() const -{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); -} - -uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const -{ - return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); -} - -HIMAGELIST CCtrlClc::GetExtraImageList() const -{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); -} - -HFONT CCtrlClc::GetFont(int iFontId) const -{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); -} - -HANDLE CCtrlClc::GetSelection() const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); -} - -HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); -} - -void CCtrlClc::SelectItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::SetBkColor(COLORREF clBack) -{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); -} - -void CCtrlClc::SetCheck(HANDLE hItem, bool check) -{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); -} - -void CCtrlClc::SetExtraColumns(int iColumns) -{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); -} - -void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); -} - -void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); -} - -void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) -{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); -} - -void CCtrlClc::SetItemText(HANDLE hItem, char *szText) -{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); -} - -void CCtrlClc::SetHideEmptyGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); -} - -bool CCtrlClc::GetHideOfflineRoot() const -{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; -} - -void CCtrlClc::SetHideOfflineRoot(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); -} - -void CCtrlClc::SetUseGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); -} - -void CCtrlClc::SetOfflineModes(uint32_t modes) -{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); -} - -uint32_t CCtrlClc::GetExStyle() const -{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); -} - -void CCtrlClc::SetExStyle(uint32_t exStyle) -{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); -} - -HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) -{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); -} - -int CCtrlClc::GetItemType(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); -} - -HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlClc
+
+CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh };
+ switch (pnmh->code) {
+ case CLN_EXPANDED: OnExpanded(&evt); break;
+ case CLN_LISTREBUILT: OnListRebuilt(&evt); break;
+ case CLN_ITEMCHECKED: OnItemChecked(&evt); break;
+ case CLN_DRAGGING: OnDragging(&evt); break;
+ case CLN_DROPPED: OnDropped(&evt); break;
+ case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break;
+ case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break;
+ case CLN_DRAGSTOP: OnDragStop(&evt); break;
+ case CLN_NEWCONTACT: OnNewContact(&evt); break;
+ case CLN_CONTACTMOVED: OnContactMoved(&evt); break;
+ case NM_CLICK: OnClick(&evt); break;
+
+ case CLN_CHECKCHANGED:
+ OnCheckChanged(&evt);
+ NotifyChange();
+ break;
+ }
+ return FALSE;
+}
+
+void CCtrlClc::AddContact(MCONTACT hContact)
+{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0);
+}
+
+void CCtrlClc::AddGroup(HANDLE hGroup)
+{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0);
+}
+
+void CCtrlClc::AutoRebuild()
+{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0);
+}
+
+void CCtrlClc::DeleteItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EditLabel(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EndEditLabel(bool save)
+{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0);
+}
+
+void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk)
+{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE);
+}
+
+void CCtrlClc::Expand(HANDLE hItem, uint32_t flags)
+{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags);
+}
+
+HANDLE CCtrlClc::FindContact(MCONTACT hContact)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0);
+}
+
+HANDLE CCtrlClc::FindGroup(MGROUP hGroup)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0);
+}
+
+COLORREF CCtrlClc::GetBkColor() const
+{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0);
+}
+
+bool CCtrlClc::GetCheck(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false;
+}
+
+int CCtrlClc::GetCount() const
+{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0);
+}
+
+HWND CCtrlClc::GetEditControl() const
+{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0);
+}
+
+uint32_t CCtrlClc::GetExpand(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0);
+}
+
+int CCtrlClc::GetExtraColumns() const
+{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0);
+}
+
+uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const
+{
+ return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF);
+}
+
+HIMAGELIST CCtrlClc::GetExtraImageList() const
+{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0);
+}
+
+HFONT CCtrlClc::GetFont(int iFontId) const
+{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0);
+}
+
+HANDLE CCtrlClc::GetSelection() const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0);
+}
+
+HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y));
+}
+
+void CCtrlClc::SelectItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::SetBkColor(COLORREF clBack)
+{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0);
+}
+
+void CCtrlClc::SetCheck(HANDLE hItem, bool check)
+{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0);
+}
+
+void CCtrlClc::SetExtraColumns(int iColumns)
+{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0);
+}
+
+void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage));
+}
+
+void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList);
+}
+
+void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw)
+{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId));
+}
+
+void CCtrlClc::SetItemText(HANDLE hItem, char *szText)
+{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText);
+}
+
+void CCtrlClc::SetHideEmptyGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0);
+}
+
+bool CCtrlClc::GetHideOfflineRoot() const
+{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false;
+}
+
+void CCtrlClc::SetHideOfflineRoot(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9);
+}
+
+void CCtrlClc::SetUseGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0);
+}
+
+void CCtrlClc::SetOfflineModes(uint32_t modes)
+{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0);
+}
+
+uint32_t CCtrlClc::GetExStyle() const
+{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0);
+}
+
+void CCtrlClc::SetExStyle(uint32_t exStyle)
+{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0);
+}
+
+HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii);
+}
+
+int CCtrlClc::GetItemType(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+}
+
+HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem);
+}
diff --git a/src/mir_core/src/Windows/CCtrlColor.cpp b/src/mir_core/src/Windows/CCtrlColor.cpp index 97f5e48e02..b61fb5760a 100644 --- a/src/mir_core/src/Windows/CCtrlColor.cpp +++ b/src/mir_core/src/Windows/CCtrlColor.cpp @@ -1,61 +1,61 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlColor class - -CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlColor::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetColor()); - return true; -} - -void CCtrlColor::OnReset() -{ - if (m_dbLink != nullptr) - SetColor(LoadInt()); -} - -uint32_t CCtrlColor::GetColor() -{ - return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); -} - -void CCtrlColor::SetColor(uint32_t dwValue) -{ - ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlColor class
+
+CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlColor::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetColor());
+ return true;
+}
+
+void CCtrlColor::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetColor(LoadInt());
+}
+
+uint32_t CCtrlColor::GetColor()
+{
+ return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0);
+}
+
+void CCtrlColor::SetColor(uint32_t dwValue)
+{
+ ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue);
+}
diff --git a/src/mir_core/src/Windows/CCtrlCombo.cpp b/src/mir_core/src/Windows/CCtrlCombo.cpp index 83cd721494..b935129b3f 100644 --- a/src/mir_core/src/Windows/CCtrlCombo.cpp +++ b/src/mir_core/src/Windows/CCtrlCombo.cpp @@ -1,185 +1,185 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCombo class - -CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlCombo::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case CBN_CLOSEUP: OnCloseup(this); break; - case CBN_DROPDOWN: OnDropdown(this); break; - case CBN_SELCHANGE: OnSelChanged(this); break; - case CBN_KILLFOCUS: OnKillFocus(this); break; - - case CBN_EDITCHANGE: - case CBN_EDITUPDATE: - case CBN_SELENDOK: - NotifyChange(); - break; - } - return TRUE; -} - -void CCtrlCombo::OnInit() -{ - CSuper::OnInit(); - OnReset(); -} - -bool CCtrlCombo::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - int len = GetWindowTextLength(m_hwnd) + 1; - wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - GetWindowText(m_hwnd, buf, len); - SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlCombo::OnReset() -{ - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -LPARAM CCtrlCombo::GetCurData() const -{ - return GetItemData(GetCurSel()); -} - -// selects line with userdata passed -int CCtrlCombo::SelectData(LPARAM data) -{ - int ret = -1, nCount = GetCount(); - - for (int i = 0; i < nCount; i++) - if (GetItemData(i) == data) { - ret = i; - break; - } - - return SetCurSel(ret); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Windows API - -int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); - if (data) - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -int CCtrlCombo::AddStringA(const char *text, LPARAM data) -{ - int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); - if (data) - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -void CCtrlCombo::DeleteString(int index) -{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0); -} - -int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlCombo::FindStringA(const char *str, int index, bool exact) -{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlCombo::GetCount() const -{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0); -} - -int CCtrlCombo::GetCurSel() const -{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0); -} - -bool CCtrlCombo::GetDroppedState() const -{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false; -} - -LPARAM CCtrlCombo::GetItemData(int index) const -{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0); -} - -wchar_t* CCtrlCombo::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text); - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -void CCtrlCombo::ResetContent() -{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0); -} - -int CCtrlCombo::SelectString(const wchar_t *str) -{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str); -} - -int CCtrlCombo::SetCurSel(int index) -{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0); -} - -void CCtrlCombo::SetItemData(int index, LPARAM data) -{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data); -} - -void CCtrlCombo::ShowDropdown(bool show) -{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCombo class
+
+CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlCombo::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ case CBN_CLOSEUP: OnCloseup(this); break;
+ case CBN_DROPDOWN: OnDropdown(this); break;
+ case CBN_SELCHANGE: OnSelChanged(this); break;
+ case CBN_KILLFOCUS: OnKillFocus(this); break;
+
+ case CBN_EDITCHANGE:
+ case CBN_EDITUPDATE:
+ case CBN_SELENDOK:
+ NotifyChange();
+ break;
+ }
+ return TRUE;
+}
+
+void CCtrlCombo::OnInit()
+{
+ CSuper::OnInit();
+ OnReset();
+}
+
+bool CCtrlCombo::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ int len = GetWindowTextLength(m_hwnd) + 1;
+ wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ GetWindowText(m_hwnd, buf, len);
+ SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlCombo::OnReset()
+{
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+LPARAM CCtrlCombo::GetCurData() const
+{
+ return GetItemData(GetCurSel());
+}
+
+// selects line with userdata passed
+int CCtrlCombo::SelectData(LPARAM data)
+{
+ int ret = -1, nCount = GetCount();
+
+ for (int i = 0; i < nCount; i++)
+ if (GetItemData(i) == data) {
+ ret = i;
+ break;
+ }
+
+ return SetCurSel(ret);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Windows API
+
+int CCtrlCombo::AddString(const wchar_t *text, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text);
+ if (data)
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+int CCtrlCombo::AddStringA(const char *text, LPARAM data)
+{
+ int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text);
+ if (data)
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlCombo::DeleteString(int index)
+{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0);
+}
+
+int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlCombo::FindStringA(const char *str, int index, bool exact)
+{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlCombo::GetCount() const
+{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0);
+}
+
+int CCtrlCombo::GetCurSel() const
+{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0);
+}
+
+bool CCtrlCombo::GetDroppedState() const
+{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false;
+}
+
+LPARAM CCtrlCombo::GetItemData(int index) const
+{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0);
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index) const
+{
+ wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result);
+ return result;
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const
+{
+ wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result);
+ mir_wstrncpy(buf, result, size);
+ return buf;
+}
+
+int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text);
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlCombo::ResetContent()
+{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0);
+}
+
+int CCtrlCombo::SelectString(const wchar_t *str)
+{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str);
+}
+
+int CCtrlCombo::SetCurSel(int index)
+{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0);
+}
+
+void CCtrlCombo::SetItemData(int index, LPARAM data)
+{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data);
+}
+
+void CCtrlCombo::ShowDropdown(bool show)
+{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlData.cpp b/src/mir_core/src/Windows/CCtrlData.cpp index fab5ca0409..2a60d9d31a 100644 --- a/src/mir_core/src/Windows/CCtrlData.cpp +++ b/src/mir_core/src/Windows/CCtrlData.cpp @@ -1,52 +1,52 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlData class - -CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_dbLink(nullptr) -{} - -CCtrlData::~CCtrlData() -{ - delete m_dbLink; -} - -void CCtrlData::OnInit() -{ - CCtrlBase::OnInit(); - OnReset(); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlData class
+
+CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_dbLink(nullptr)
+{}
+
+CCtrlData::~CCtrlData()
+{
+ delete m_dbLink;
+}
+
+void CCtrlData::OnInit()
+{
+ CCtrlBase::OnInit();
+ OnReset();
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue);
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue);
+}
diff --git a/src/mir_core/src/Windows/CCtrlDate.cpp b/src/mir_core/src/Windows/CCtrlDate.cpp index 1967cb5678..253b549870 100644 --- a/src/mir_core/src/Windows/CCtrlDate.cpp +++ b/src/mir_core/src/Windows/CCtrlDate.cpp @@ -1,49 +1,49 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlDate class - -CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == DTN_DATETIMECHANGE) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -void CCtrlDate::GetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); -} - -void CCtrlDate::SetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlDate class
+
+CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == DTN_DATETIMECHANGE) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void CCtrlDate::GetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate);
+}
+
+void CCtrlDate::SetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate);
+}
diff --git a/src/mir_core/src/Windows/CCtrlEdit.cpp b/src/mir_core/src/Windows/CCtrlEdit.cpp index f035b5b14a..dd5bd2927e 100644 --- a/src/mir_core/src/Windows/CCtrlEdit.cpp +++ b/src/mir_core/src/Windows/CCtrlEdit.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlEdit class - -CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlEdit::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == EN_CHANGE) - NotifyChange(); - return TRUE; -} - -bool CCtrlEdit::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - int len = GetWindowTextLength(m_hwnd) + 1; - wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - GetWindowText(m_hwnd, buf, len); - SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlEdit::OnReset() -{ - m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; - - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -void CCtrlEdit::SetMaxLength(unsigned int len) -{ - SendMsg(EM_SETLIMITTEXT, len, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlEdit class
+
+CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlEdit::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == EN_CHANGE)
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlEdit::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ int len = GetWindowTextLength(m_hwnd) + 1;
+ wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ GetWindowText(m_hwnd, buf, len);
+ SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlEdit::OnReset()
+{
+ m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0;
+
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+void CCtrlEdit::SetMaxLength(unsigned int len)
+{
+ SendMsg(EM_SETLIMITTEXT, len, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlHyperlink.cpp b/src/mir_core/src/Windows/CCtrlHyperlink.cpp index ca92d19dbc..0e0d93a689 100644 --- a/src/mir_core/src/Windows/CCtrlHyperlink.cpp +++ b/src/mir_core/src/Windows/CCtrlHyperlink.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlHyperlink - -CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) - : CCtrlBase(wnd, idCtrl), - m_url(url) -{ - OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); -} - -BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) -{ - OnClick(this); - return FALSE; -} - -void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) -{ - ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); -} - -void CCtrlHyperlink::SetUrl(const char *url) -{ - m_url = url; -} - -const char* CCtrlHyperlink::GetUrl() -{ - return m_url; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlHyperlink
+
+CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url)
+ : CCtrlBase(wnd, idCtrl),
+ m_url(url)
+{
+ OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick);
+}
+
+BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t)
+{
+ OnClick(this);
+ return FALSE;
+}
+
+void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*)
+{
+ ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW);
+}
+
+void CCtrlHyperlink::SetUrl(const char *url)
+{
+ m_url = url;
+}
+
+const char* CCtrlHyperlink::GetUrl()
+{
+ return m_url;
+}
diff --git a/src/mir_core/src/Windows/CCtrlLabel.cpp b/src/mir_core/src/Windows/CCtrlLabel.cpp index fd460fdf8b..8e1de5e33f 100644 --- a/src/mir_core/src/Windows/CCtrlLabel.cpp +++ b/src/mir_core/src/Windows/CCtrlLabel.cpp @@ -1,31 +1,31 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlLabel - -CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlLabel
+
+CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
+
diff --git a/src/mir_core/src/Windows/CCtrlListBox.cpp b/src/mir_core/src/Windows/CCtrlListBox.cpp index abaa31a786..6d9f58fc23 100644 --- a/src/mir_core/src/Windows/CCtrlListBox.cpp +++ b/src/mir_core/src/Windows/CCtrlListBox.cpp @@ -1,160 +1,160 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListBox class - -CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case LBN_DBLCLK: OnDblClick(this); break; - case LBN_SELCANCEL: OnSelCancel(this); break; - case LBN_SELCHANGE: OnSelChange(this); break; - } - return TRUE; -} - -void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetCurSel(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = ListBox_AddString(m_hwnd, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::DeleteString(int index) -{ ListBox_DeleteString(m_hwnd, index); -} - -int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlListBox::GetCount() const -{ return ListBox_GetCount(m_hwnd); -} - -int CCtrlListBox::GetCurSel() const -{ return ListBox_GetCurSel(m_hwnd); -} - -LPARAM CCtrlListBox::GetItemData(int index) const -{ return ListBox_GetItemData(m_hwnd, index); -} - -int CCtrlListBox::GetItemRect(int index, RECT *pResult) const -{ return ListBox_GetItemRect(m_hwnd, index, pResult); -} - -wchar_t* CCtrlListBox::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -bool CCtrlListBox::GetSel(int index) const -{ return ListBox_GetSel(m_hwnd, index) ? true : false; -} - -int CCtrlListBox::GetSelCount() const -{ return ListBox_GetSelCount(m_hwnd); -} - -int* CCtrlListBox::GetSelItems(int *items, int count) const -{ - ListBox_GetSelItems(m_hwnd, count, items); - return items; -} - -int* CCtrlListBox::GetSelItems() const -{ - int count = GetSelCount() + 1; - int *result = (int *)mir_alloc(sizeof(int) * count); - ListBox_GetSelItems(m_hwnd, count, result); - result[count-1] = -1; - return result; -} - -int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = ListBox_InsertString(m_hwnd, pos, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::ResetContent() -{ ListBox_ResetContent(m_hwnd); -} - -int CCtrlListBox::SelectString(const wchar_t *str) -{ return ListBox_SelectString(m_hwnd, 0, str); -} - -int CCtrlListBox::SetCurSel(int index) -{ return ListBox_SetCurSel(m_hwnd, index); -} - -void CCtrlListBox::SetItemData(int index, LPARAM data) -{ ListBox_SetItemData(m_hwnd, index, data); -} - -void CCtrlListBox::SetItemHeight(int index, int iHeight) -{ ListBox_SetItemHeight(m_hwnd, index, iHeight); -} - -void CCtrlListBox::SetSel(int index, bool sel) -{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListBox class
+
+CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ case LBN_DBLCLK: OnDblClick(this); break;
+ case LBN_SELCANCEL: OnSelCancel(this); break;
+ case LBN_SELCHANGE: OnSelChange(this); break;
+ }
+ return TRUE;
+}
+
+void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetCurSel();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+
+ CSuper::GetCaretPos(pos);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CCtrlListBox::AddString(const wchar_t *text, LPARAM data)
+{
+ int iItem = ListBox_AddString(m_hwnd, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::DeleteString(int index)
+{ ListBox_DeleteString(m_hwnd, index);
+}
+
+int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlListBox::GetCount() const
+{ return ListBox_GetCount(m_hwnd);
+}
+
+int CCtrlListBox::GetCurSel() const
+{ return ListBox_GetCurSel(m_hwnd);
+}
+
+LPARAM CCtrlListBox::GetItemData(int index) const
+{ return ListBox_GetItemData(m_hwnd, index);
+}
+
+int CCtrlListBox::GetItemRect(int index, RECT *pResult) const
+{ return ListBox_GetItemRect(m_hwnd, index, pResult);
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index) const
+{
+ wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ return result;
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const
+{
+ wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ mir_wstrncpy(buf, result, size);
+ return buf;
+}
+
+bool CCtrlListBox::GetSel(int index) const
+{ return ListBox_GetSel(m_hwnd, index) ? true : false;
+}
+
+int CCtrlListBox::GetSelCount() const
+{ return ListBox_GetSelCount(m_hwnd);
+}
+
+int* CCtrlListBox::GetSelItems(int *items, int count) const
+{
+ ListBox_GetSelItems(m_hwnd, count, items);
+ return items;
+}
+
+int* CCtrlListBox::GetSelItems() const
+{
+ int count = GetSelCount() + 1;
+ int *result = (int *)mir_alloc(sizeof(int) * count);
+ ListBox_GetSelItems(m_hwnd, count, result);
+ result[count-1] = -1;
+ return result;
+}
+
+int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data)
+{
+ int iItem = ListBox_InsertString(m_hwnd, pos, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::ResetContent()
+{ ListBox_ResetContent(m_hwnd);
+}
+
+int CCtrlListBox::SelectString(const wchar_t *str)
+{ return ListBox_SelectString(m_hwnd, 0, str);
+}
+
+int CCtrlListBox::SetCurSel(int index)
+{ return ListBox_SetCurSel(m_hwnd, index);
+}
+
+void CCtrlListBox::SetItemData(int index, LPARAM data)
+{ ListBox_SetItemData(m_hwnd, index, data);
+}
+
+void CCtrlListBox::SetItemHeight(int index, int iHeight)
+{ ListBox_SetItemHeight(m_hwnd, index, iHeight);
+}
+
+void CCtrlListBox::SetSel(int index, bool sel)
+{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index);
+}
diff --git a/src/mir_core/src/Windows/CCtrlListView.cpp b/src/mir_core/src/Windows/CCtrlListView.cpp index eb57951a3c..40bb1f481e 100644 --- a/src/mir_core/src/Windows/CCtrlListView.cpp +++ b/src/mir_core/src/Windows/CCtrlListView.cpp @@ -1,551 +1,551 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListView - -CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_CLICK: OnClick(&evt); return TRUE; - case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; - case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; - case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; - case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; - case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; - case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; - case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; - case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; - case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; - case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; - case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; - case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - - case LVN_ITEMCHANGED: - if (!m_parentWnd || !m_parentWnd->IsInitialized()) - return FALSE; - - OnItemChanged(&evt); - - // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes - if (evt.nmlv->uChanged & LVIF_STATE) - if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) - NotifyChange(); - return TRUE; - - case LVN_ODSTATECHANGED: - NotifyChange(); - return TRUE; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) -{ - int result = l1 - l2; - int newItem = HIWORD(param); - int oldItem = LOWORD(param); - if (newItem > oldItem) - return (l1 == oldItem && l2 <= newItem) ? 1 : result; - - return (l2 == oldItem && l1 >= newItem) ? 1 : result; -} - -int CCtrlListView::MoveItem(int idx, int direction) -{ - if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) - return idx; - - if (idx < 0) - idx = GetNextItem(-1, LVNI_FOCUSED); - SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); - return idx + direction; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CCtrlListView::SetCurSel(int idx) -{ - SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -// additional api -HIMAGELIST CCtrlListView::CreateImageList(int iImageList) -{ - HIMAGELIST hIml = GetImageList(iImageList); - if (hIml) - return hIml; - - hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - SetImageList(hIml, iImageList); - return hIml; -} - -void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) -{ - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc.iImage = 0; - lvc.pszText = (LPWSTR)name; - lvc.cx = cx; - lvc.iSubItem = iSubItem; - InsertColumn(iSubItem, &lvc); -} - -void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) -{ - LVGROUP lvg = { 0 }; - lvg.cbSize = sizeof(lvg); - lvg.mask = LVGF_HEADER | LVGF_GROUPID; - lvg.pszHeader = (LPWSTR)name; - lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); - lvg.iGroupId = iGroupId; - InsertGroup(-1, &lvg); -} - -int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = 0; - lvi.pszText = (LPWSTR)text; - lvi.iImage = iIcon; - lvi.lParam = lParam; - if (iGroupId >= 0) { - lvi.mask |= LVIF_GROUPID; - lvi.iGroupId = iGroupId; - } - - return InsertItem(&lvi); -} - -void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_TEXT; - lvi.iItem = iItem; - lvi.iSubItem = iSubItem; - lvi.pszText = (LPWSTR)text; - if (iIcon >= 0) { - lvi.mask |= LVIF_IMAGE; - lvi.iImage = iIcon; - } - - SetItem(&lvi); -} - -LPARAM CCtrlListView::GetItemData(int iItem) const -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - return GetItem(&lvi) ? lvi.lParam : -1; -} - -void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetSelectionMark(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - LVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (SubItemHitTest(&hti) != -1) { - pos.iCurr = hti.iItem; - return; - } - } - CSuper::GetCaretPos(pos); -} - -// classic api -uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) -{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); -} -void CCtrlListView::Arrange(UINT code) -{ ListView_Arrange(m_hwnd, code); -} -void CCtrlListView::CancelEditLabel() -{ ListView_CancelEditLabel(m_hwnd); -} -HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) -{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); -} -void CCtrlListView::DeleteAllItems() -{ ListView_DeleteAllItems(m_hwnd); -} -void CCtrlListView::DeleteColumn(int iCol) -{ ListView_DeleteColumn(m_hwnd, iCol); -} -void CCtrlListView::DeleteItem(int iItem) -{ ListView_DeleteItem(m_hwnd, iItem); -} -HWND CCtrlListView::EditLabel(int iItem) -{ return ListView_EditLabel(m_hwnd, iItem); -} -int CCtrlListView::EnableGroupView(BOOL fEnable) -{ return ListView_EnableGroupView(m_hwnd, fEnable); -} -BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) -{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); -} -int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) -{ return ListView_FindItem(m_hwnd, iStart, plvfi); -} -COLORREF CCtrlListView::GetBkColor() const -{ return ListView_GetBkColor(m_hwnd); -} -void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const -{ ListView_GetBkImage(m_hwnd, plvbki); -} -UINT CCtrlListView::GetCallbackMask() const -{ return ListView_GetCallbackMask(m_hwnd); -} -BOOL CCtrlListView::GetCheckState(UINT iIndex) const -{ return ListView_GetCheckState(m_hwnd, iIndex); -} -void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const -{ ListView_GetColumn(m_hwnd, iCol, pcol); -} -void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const -{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -int CCtrlListView::GetColumnWidth(int iCol) const -{ return ListView_GetColumnWidth(m_hwnd, iCol); -} -int CCtrlListView::GetCountPerPage() const -{ return ListView_GetCountPerPage(m_hwnd); -} -HWND CCtrlListView::GetEditControl() const -{ return ListView_GetEditControl(m_hwnd); -} -uint32_t CCtrlListView::GetExtendedListViewStyle() const -{ return ListView_GetExtendedListViewStyle(m_hwnd); -} -void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const -{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); -} -HWND CCtrlListView::GetHeader() const -{ return ListView_GetHeader(m_hwnd); -} -HCURSOR CCtrlListView::GetHotCursor() const -{ return ListView_GetHotCursor(m_hwnd); -} -INT CCtrlListView::GetHotItem() const -{ return ListView_GetHotItem(m_hwnd); -} -uint32_t CCtrlListView::GetHoverTime() const -{ return ListView_GetHoverTime(m_hwnd); -} -HIMAGELIST CCtrlListView::GetImageList(int iImageList) const -{ return ListView_GetImageList(m_hwnd, iImageList); -} -BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const -{ return ListView_GetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::GetInsertMarkColor() const -{ return ListView_GetInsertMarkColor(m_hwnd); -} -int CCtrlListView::GetInsertMarkRect(LPRECT prc) const -{ return ListView_GetInsertMarkRect(m_hwnd, prc); -} -BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const -{ return ListView_GetISearchString(m_hwnd, lpsz); -} -bool CCtrlListView::GetItem(LPLVITEM pitem) const -{ return ListView_GetItem(m_hwnd, pitem) == TRUE; -} -int CCtrlListView::GetItemCount() const -{ return ListView_GetItemCount(m_hwnd); -} -void CCtrlListView::GetItemPosition(int i, POINT *ppt) const -{ ListView_GetItemPosition(m_hwnd, i, ppt); -} -void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const -{ ListView_GetItemRect(m_hwnd, i, prc, code); -} -uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const -{ return ListView_GetItemSpacing(m_hwnd, fSmall); -} -UINT CCtrlListView::GetItemState(int i, UINT mask) const -{ return ListView_GetItemState(m_hwnd, i, mask); -} -void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const -{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); -} -int CCtrlListView::GetNextItem(int iStart, UINT flags) const -{ return ListView_GetNextItem(m_hwnd, iStart, flags); -} -BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const -{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); -} -BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const -{ return ListView_GetOrigin(m_hwnd, lpptOrg); -} -COLORREF CCtrlListView::GetOutlineColor() const -{ return ListView_GetOutlineColor(m_hwnd); -} -UINT CCtrlListView::GetSelectedColumn() const -{ return ListView_GetSelectedColumn(m_hwnd); -} -UINT CCtrlListView::GetSelectedCount() const -{ return ListView_GetSelectedCount(m_hwnd); -} -INT CCtrlListView::GetSelectionMark() const -{ return ListView_GetSelectionMark(m_hwnd); -} -int CCtrlListView::GetStringWidth(LPCSTR psz) const -{ return ListView_GetStringWidth(m_hwnd, psz); -} -BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const -{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); -} -COLORREF CCtrlListView::GetTextBkColor() const -{ return ListView_GetTextBkColor(m_hwnd); -} -COLORREF CCtrlListView::GetTextColor() const -{ return ListView_GetTextColor(m_hwnd); -} -void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const -{ ListView_GetTileInfo(m_hwnd, plvtinfo); -} -void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const -{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::GetToolTips() const -{ return ListView_GetToolTips(m_hwnd); -} -int CCtrlListView::GetTopIndex() const -{ return ListView_GetTopIndex(m_hwnd); -} -BOOL CCtrlListView::GetUnicodeFormat() const -{ return ListView_GetUnicodeFormat(m_hwnd); -} -uint32_t CCtrlListView::GetView() const -{ return ListView_GetView(m_hwnd); -} -BOOL CCtrlListView::GetViewRect(RECT *prc) const -{ return ListView_GetViewRect(m_hwnd, prc); -} -void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const -{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -BOOL CCtrlListView::HasGroup(int dwGroupId) -{ return ListView_HasGroup(m_hwnd, dwGroupId); -} -int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const -{ return ListView_HitTest(m_hwnd, pinfo); -} -int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) -{ return ListView_InsertColumn(m_hwnd, iCol, pcol); -} -int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) -{ return ListView_InsertGroup(m_hwnd, index, pgrp); -} -void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) -{ ListView_InsertGroupSorted(m_hwnd, structInsert); -} -int CCtrlListView::InsertItem(const LVITEM *pitem) -{ return ListView_InsertItem(m_hwnd, pitem); -} -BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) -{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); -} -BOOL CCtrlListView::IsGroupViewEnabled() -{ return ListView_IsGroupViewEnabled(m_hwnd); -} -UINT CCtrlListView::MapIDToIndex(UINT id) -{ return ListView_MapIDToIndex(m_hwnd, id); -} -UINT CCtrlListView::MapIndexToID(UINT index) -{ return ListView_MapIndexToID(m_hwnd, index); -} -BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) -{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); -} -void CCtrlListView::RemoveAllGroups() -{ ListView_RemoveAllGroups(m_hwnd); -} -int CCtrlListView::RemoveGroup(int iGroupId) -{ return ListView_RemoveGroup(m_hwnd, iGroupId); -} -BOOL CCtrlListView::Scroll(int dx, int dy) -{ return ListView_Scroll(m_hwnd, dx, dy); -} -BOOL CCtrlListView::SetBkColor(COLORREF clrBk) -{ return ListView_SetBkColor(m_hwnd, clrBk); -} -BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) -{ return ListView_SetBkImage(m_hwnd, plvbki); -} -BOOL CCtrlListView::SetCallbackMask(UINT mask) -{ return ListView_SetCallbackMask(m_hwnd, mask); -} -void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) -{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); -} -BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) -{ return ListView_SetColumn(m_hwnd, iCol, pcol); -} -BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) -{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) -{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); -} -void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); -} -void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); -} -int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) -{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); -} -void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) -{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); -} -HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) -{ return ListView_SetHotCursor(m_hwnd, hCursor); -} -INT CCtrlListView::SetHotItem(INT iIndex) -{ return ListView_SetHotItem(m_hwnd, iIndex); -} -void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) -{ ListView_SetHoverTime(m_hwnd, dwHoverTime); -} -uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) -{ return ListView_SetIconSpacing(m_hwnd, cx, cy); -} -HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) -{ return ListView_SetImageList(m_hwnd, himl, iImageList); -} -BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) -{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); -} -BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) -{ return ListView_SetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) -{ return ListView_SetInsertMarkColor(m_hwnd, color); -} -BOOL CCtrlListView::SetItem(const LVITEM *pitem) -{ return ListView_SetItem(m_hwnd, pitem); -} -void CCtrlListView::SetItemCount(int cItems) -{ ListView_SetItemCount(m_hwnd, cItems); -} -void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) -{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); -} -BOOL CCtrlListView::SetItemPosition(int i, int x, int y) -{ return ListView_SetItemPosition(m_hwnd, i, x, y); -} -void CCtrlListView::SetItemPosition32(int iItem, int x, int y) -{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); -} -void CCtrlListView::SetItemState(int i, UINT state, UINT mask) -{ ListView_SetItemState(m_hwnd, i, state, mask); -} -void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) -{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); -} -COLORREF CCtrlListView::SetOutlineColor(COLORREF color) -{ return ListView_SetOutlineColor(m_hwnd, color); -} -void CCtrlListView::SetSelectedColumn(int iCol) -{ ListView_SetSelectedColumn(m_hwnd, iCol); -} -INT CCtrlListView::SetSelectionMark(INT iIndex) -{ return ListView_SetSelectionMark(m_hwnd, iIndex); -} -BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) -{ return ListView_SetTextBkColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTextColor(COLORREF clrText) -{ return ListView_SetTextColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) -{ return ListView_SetTileInfo(m_hwnd, plvtinfo); -} -BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) -{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::SetToolTips(HWND ToolTip) -{ return ListView_SetToolTips(m_hwnd, ToolTip); -} -BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) -{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); -} -int CCtrlListView::SetView(uint32_t iView) -{ return ListView_SetView(m_hwnd, iView); -} -void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) -{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) -{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); -} -BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); -} -BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); -} -INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const -{ return ListView_SubItemHitTest(m_hwnd, pInfo); -} -BOOL CCtrlListView::Update(int iItem) -{ return ListView_Update(m_hwnd, iItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListView
+
+CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_CLICK: OnClick(&evt); return TRUE;
+ case NM_DBLCLK: OnDoubleClick(&evt); return TRUE;
+ case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE;
+ case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE;
+ case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE;
+ case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE;
+ case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE;
+ case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE;
+ case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE;
+ case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE;
+ case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE;
+ case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE;
+ case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE;
+ case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE;
+ case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+
+ case LVN_ITEMCHANGED:
+ if (!m_parentWnd || !m_parentWnd->IsInitialized())
+ return FALSE;
+
+ OnItemChanged(&evt);
+
+ // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes
+ if (evt.nmlv->uChanged & LVIF_STATE)
+ if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0)
+ NotifyChange();
+ return TRUE;
+
+ case LVN_ODSTATECHANGED:
+ NotifyChange();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param)
+{
+ int result = l1 - l2;
+ int newItem = HIWORD(param);
+ int oldItem = LOWORD(param);
+ if (newItem > oldItem)
+ return (l1 == oldItem && l2 <= newItem) ? 1 : result;
+
+ return (l2 == oldItem && l1 >= newItem) ? 1 : result;
+}
+
+int CCtrlListView::MoveItem(int idx, int direction)
+{
+ if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0))
+ return idx;
+
+ if (idx < 0)
+ idx = GetNextItem(-1, LVNI_FOCUSED);
+ SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction));
+ return idx + direction;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CCtrlListView::SetCurSel(int idx)
+{
+ SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+}
+
+// additional api
+HIMAGELIST CCtrlListView::CreateImageList(int iImageList)
+{
+ HIMAGELIST hIml = GetImageList(iImageList);
+ if (hIml)
+ return hIml;
+
+ hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ SetImageList(hIml, iImageList);
+ return hIml;
+}
+
+void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx)
+{
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+ lvc.iImage = 0;
+ lvc.pszText = (LPWSTR)name;
+ lvc.cx = cx;
+ lvc.iSubItem = iSubItem;
+ InsertColumn(iSubItem, &lvc);
+}
+
+void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name)
+{
+ LVGROUP lvg = { 0 };
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER | LVGF_GROUPID;
+ lvg.pszHeader = (LPWSTR)name;
+ lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader);
+ lvg.iGroupId = iGroupId;
+ InsertGroup(-1, &lvg);
+}
+
+int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
+ lvi.iSubItem = 0;
+ lvi.pszText = (LPWSTR)text;
+ lvi.iImage = iIcon;
+ lvi.lParam = lParam;
+ if (iGroupId >= 0) {
+ lvi.mask |= LVIF_GROUPID;
+ lvi.iGroupId = iGroupId;
+ }
+
+ return InsertItem(&lvi);
+}
+
+void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = (LPWSTR)text;
+ if (iIcon >= 0) {
+ lvi.mask |= LVIF_IMAGE;
+ lvi.iImage = iIcon;
+ }
+
+ SetItem(&lvi);
+}
+
+LPARAM CCtrlListView::GetItemData(int iItem) const
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ return GetItem(&lvi) ? lvi.lParam : -1;
+}
+
+void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+
+ // position is empty, let's fill it using selection
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetSelectionMark();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc, TRUE);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+ // position is present, let's calculate current item
+ else {
+ LVHITTESTINFO hti;
+ hti.pt = pos.pt;
+ ScreenToClient(m_hwnd, &hti.pt);
+ if (SubItemHitTest(&hti) != -1) {
+ pos.iCurr = hti.iItem;
+ return;
+ }
+ }
+ CSuper::GetCaretPos(pos);
+}
+
+// classic api
+uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount)
+{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount);
+}
+void CCtrlListView::Arrange(UINT code)
+{ ListView_Arrange(m_hwnd, code);
+}
+void CCtrlListView::CancelEditLabel()
+{ ListView_CancelEditLabel(m_hwnd);
+}
+HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft)
+{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft);
+}
+void CCtrlListView::DeleteAllItems()
+{ ListView_DeleteAllItems(m_hwnd);
+}
+void CCtrlListView::DeleteColumn(int iCol)
+{ ListView_DeleteColumn(m_hwnd, iCol);
+}
+void CCtrlListView::DeleteItem(int iItem)
+{ ListView_DeleteItem(m_hwnd, iItem);
+}
+HWND CCtrlListView::EditLabel(int iItem)
+{ return ListView_EditLabel(m_hwnd, iItem);
+}
+int CCtrlListView::EnableGroupView(BOOL fEnable)
+{ return ListView_EnableGroupView(m_hwnd, fEnable);
+}
+BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK)
+{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK);
+}
+int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi)
+{ return ListView_FindItem(m_hwnd, iStart, plvfi);
+}
+COLORREF CCtrlListView::GetBkColor() const
+{ return ListView_GetBkColor(m_hwnd);
+}
+void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const
+{ ListView_GetBkImage(m_hwnd, plvbki);
+}
+UINT CCtrlListView::GetCallbackMask() const
+{ return ListView_GetCallbackMask(m_hwnd);
+}
+BOOL CCtrlListView::GetCheckState(UINT iIndex) const
+{ return ListView_GetCheckState(m_hwnd, iIndex);
+}
+void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const
+{ ListView_GetColumn(m_hwnd, iCol, pcol);
+}
+void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const
+{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+int CCtrlListView::GetColumnWidth(int iCol) const
+{ return ListView_GetColumnWidth(m_hwnd, iCol);
+}
+int CCtrlListView::GetCountPerPage() const
+{ return ListView_GetCountPerPage(m_hwnd);
+}
+HWND CCtrlListView::GetEditControl() const
+{ return ListView_GetEditControl(m_hwnd);
+}
+uint32_t CCtrlListView::GetExtendedListViewStyle() const
+{ return ListView_GetExtendedListViewStyle(m_hwnd);
+}
+void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const
+{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HWND CCtrlListView::GetHeader() const
+{ return ListView_GetHeader(m_hwnd);
+}
+HCURSOR CCtrlListView::GetHotCursor() const
+{ return ListView_GetHotCursor(m_hwnd);
+}
+INT CCtrlListView::GetHotItem() const
+{ return ListView_GetHotItem(m_hwnd);
+}
+uint32_t CCtrlListView::GetHoverTime() const
+{ return ListView_GetHoverTime(m_hwnd);
+}
+HIMAGELIST CCtrlListView::GetImageList(int iImageList) const
+{ return ListView_GetImageList(m_hwnd, iImageList);
+}
+BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const
+{ return ListView_GetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::GetInsertMarkColor() const
+{ return ListView_GetInsertMarkColor(m_hwnd);
+}
+int CCtrlListView::GetInsertMarkRect(LPRECT prc) const
+{ return ListView_GetInsertMarkRect(m_hwnd, prc);
+}
+BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const
+{ return ListView_GetISearchString(m_hwnd, lpsz);
+}
+bool CCtrlListView::GetItem(LPLVITEM pitem) const
+{ return ListView_GetItem(m_hwnd, pitem) == TRUE;
+}
+int CCtrlListView::GetItemCount() const
+{ return ListView_GetItemCount(m_hwnd);
+}
+void CCtrlListView::GetItemPosition(int i, POINT *ppt) const
+{ ListView_GetItemPosition(m_hwnd, i, ppt);
+}
+void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const
+{ ListView_GetItemRect(m_hwnd, i, prc, code);
+}
+uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const
+{ return ListView_GetItemSpacing(m_hwnd, fSmall);
+}
+UINT CCtrlListView::GetItemState(int i, UINT mask) const
+{ return ListView_GetItemState(m_hwnd, i, mask);
+}
+void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const
+{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax);
+}
+int CCtrlListView::GetNextItem(int iStart, UINT flags) const
+{ return ListView_GetNextItem(m_hwnd, iStart, flags);
+}
+BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const
+{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas);
+}
+BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const
+{ return ListView_GetOrigin(m_hwnd, lpptOrg);
+}
+COLORREF CCtrlListView::GetOutlineColor() const
+{ return ListView_GetOutlineColor(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedColumn() const
+{ return ListView_GetSelectedColumn(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedCount() const
+{ return ListView_GetSelectedCount(m_hwnd);
+}
+INT CCtrlListView::GetSelectionMark() const
+{ return ListView_GetSelectionMark(m_hwnd);
+}
+int CCtrlListView::GetStringWidth(LPCSTR psz) const
+{ return ListView_GetStringWidth(m_hwnd, psz);
+}
+BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const
+{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect);
+}
+COLORREF CCtrlListView::GetTextBkColor() const
+{ return ListView_GetTextBkColor(m_hwnd);
+}
+COLORREF CCtrlListView::GetTextColor() const
+{ return ListView_GetTextColor(m_hwnd);
+}
+void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const
+{ ListView_GetTileInfo(m_hwnd, plvtinfo);
+}
+void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const
+{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::GetToolTips() const
+{ return ListView_GetToolTips(m_hwnd);
+}
+int CCtrlListView::GetTopIndex() const
+{ return ListView_GetTopIndex(m_hwnd);
+}
+BOOL CCtrlListView::GetUnicodeFormat() const
+{ return ListView_GetUnicodeFormat(m_hwnd);
+}
+uint32_t CCtrlListView::GetView() const
+{ return ListView_GetView(m_hwnd);
+}
+BOOL CCtrlListView::GetViewRect(RECT *prc) const
+{ return ListView_GetViewRect(m_hwnd, prc);
+}
+void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const
+{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+BOOL CCtrlListView::HasGroup(int dwGroupId)
+{ return ListView_HasGroup(m_hwnd, dwGroupId);
+}
+int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const
+{ return ListView_HitTest(m_hwnd, pinfo);
+}
+int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol)
+{ return ListView_InsertColumn(m_hwnd, iCol, pcol);
+}
+int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp)
+{ return ListView_InsertGroup(m_hwnd, index, pgrp);
+}
+void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert)
+{ ListView_InsertGroupSorted(m_hwnd, structInsert);
+}
+int CCtrlListView::InsertItem(const LVITEM *pitem)
+{ return ListView_InsertItem(m_hwnd, pitem);
+}
+BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim)
+{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim);
+}
+BOOL CCtrlListView::IsGroupViewEnabled()
+{ return ListView_IsGroupViewEnabled(m_hwnd);
+}
+UINT CCtrlListView::MapIDToIndex(UINT id)
+{ return ListView_MapIDToIndex(m_hwnd, id);
+}
+UINT CCtrlListView::MapIndexToID(UINT index)
+{ return ListView_MapIndexToID(m_hwnd, index);
+}
+BOOL CCtrlListView::RedrawItems(int iFirst, int iLast)
+{ return ListView_RedrawItems(m_hwnd, iFirst, iLast);
+}
+void CCtrlListView::RemoveAllGroups()
+{ ListView_RemoveAllGroups(m_hwnd);
+}
+int CCtrlListView::RemoveGroup(int iGroupId)
+{ return ListView_RemoveGroup(m_hwnd, iGroupId);
+}
+BOOL CCtrlListView::Scroll(int dx, int dy)
+{ return ListView_Scroll(m_hwnd, dx, dy);
+}
+BOOL CCtrlListView::SetBkColor(COLORREF clrBk)
+{ return ListView_SetBkColor(m_hwnd, clrBk);
+}
+BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki)
+{ return ListView_SetBkImage(m_hwnd, plvbki);
+}
+BOOL CCtrlListView::SetCallbackMask(UINT mask)
+{ return ListView_SetCallbackMask(m_hwnd, mask);
+}
+void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck)
+{ ListView_SetCheckState(m_hwnd, iIndex, fCheck);
+}
+BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol)
+{ return ListView_SetColumn(m_hwnd, iCol, pcol);
+}
+BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray)
+{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+BOOL CCtrlListView::SetColumnWidth(int iCol, int cx)
+{ return ListView_SetColumnWidth(m_hwnd, iCol, cx);
+}
+void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle);
+}
+void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle);
+}
+int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp)
+{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp);
+}
+void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)
+{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor)
+{ return ListView_SetHotCursor(m_hwnd, hCursor);
+}
+INT CCtrlListView::SetHotItem(INT iIndex)
+{ return ListView_SetHotItem(m_hwnd, iIndex);
+}
+void CCtrlListView::SetHoverTime(uint32_t dwHoverTime)
+{ ListView_SetHoverTime(m_hwnd, dwHoverTime);
+}
+uint32_t CCtrlListView::SetIconSpacing(int cx, int cy)
+{ return ListView_SetIconSpacing(m_hwnd, cx, cy);
+}
+HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList)
+{ return ListView_SetImageList(m_hwnd, himl, iImageList);
+}
+BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip)
+{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip);
+}
+BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim)
+{ return ListView_SetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color)
+{ return ListView_SetInsertMarkColor(m_hwnd, color);
+}
+BOOL CCtrlListView::SetItem(const LVITEM *pitem)
+{ return ListView_SetItem(m_hwnd, pitem);
+}
+void CCtrlListView::SetItemCount(int cItems)
+{ ListView_SetItemCount(m_hwnd, cItems);
+}
+void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags)
+{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags);
+}
+BOOL CCtrlListView::SetItemPosition(int i, int x, int y)
+{ return ListView_SetItemPosition(m_hwnd, i, x, y);
+}
+void CCtrlListView::SetItemPosition32(int iItem, int x, int y)
+{ ListView_SetItemPosition32(m_hwnd, iItem, x, y);
+}
+void CCtrlListView::SetItemState(int i, UINT state, UINT mask)
+{ ListView_SetItemState(m_hwnd, i, state, mask);
+}
+void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText)
+{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText);
+}
+COLORREF CCtrlListView::SetOutlineColor(COLORREF color)
+{ return ListView_SetOutlineColor(m_hwnd, color);
+}
+void CCtrlListView::SetSelectedColumn(int iCol)
+{ ListView_SetSelectedColumn(m_hwnd, iCol);
+}
+INT CCtrlListView::SetSelectionMark(INT iIndex)
+{ return ListView_SetSelectionMark(m_hwnd, iIndex);
+}
+BOOL CCtrlListView::SetTextBkColor(COLORREF clrText)
+{ return ListView_SetTextBkColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTextColor(COLORREF clrText)
+{ return ListView_SetTextColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo)
+{ return ListView_SetTileInfo(m_hwnd, plvtinfo);
+}
+BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo)
+{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::SetToolTips(HWND ToolTip)
+{ return ListView_SetToolTips(m_hwnd, ToolTip);
+}
+BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode)
+{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+int CCtrlListView::SetView(uint32_t iView)
+{ return ListView_SetView(m_hwnd, iView);
+}
+void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc)
+{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv)
+{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv);
+}
+BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort);
+}
+BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort);
+}
+INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const
+{ return ListView_SubItemHitTest(m_hwnd, pInfo);
+}
+BOOL CCtrlListView::Update(int iItem)
+{ return ListView_Update(m_hwnd, iItem);
+}
diff --git a/src/mir_core/src/Windows/CCtrlMButton.cpp b/src/mir_core/src/Windows/CCtrlMButton.cpp index 8d9198e144..cc05a3a8e4 100644 --- a/src/mir_core/src/Windows/CCtrlMButton.cpp +++ b/src/mir_core/src/Windows/CCtrlMButton.cpp @@ -1,62 +1,62 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlMButton - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(hIcon), - m_toolTip(tooltip) -{} - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(::Skin_LoadIcon(iCoreIcon)), - m_toolTip(tooltip) -{} - -CCtrlMButton::~CCtrlMButton() -{ - ::IcoLib_ReleaseIcon(m_hIcon); -} - -void CCtrlMButton::OnInit() -{ - CCtrlButton::OnInit(); - - SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); - SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); - SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); -} - -void CCtrlMButton::MakeFlat() -{ - SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); -} - -void CCtrlMButton::MakePush() -{ - SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlMButton
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(hIcon),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(::Skin_LoadIcon(iCoreIcon)),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::~CCtrlMButton()
+{
+ ::IcoLib_ReleaseIcon(m_hIcon);
+}
+
+void CCtrlMButton::OnInit()
+{
+ CCtrlButton::OnInit();
+
+ SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon);
+ SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0);
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0);
+}
+
+void CCtrlMButton::MakeFlat()
+{
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0);
+}
+
+void CCtrlMButton::MakePush()
+{
+ SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlPages.cpp b/src/mir_core/src/Windows/CCtrlPages.cpp index 512c32e142..c2a95553c5 100644 --- a/src/mir_core/src/Windows/CCtrlPages.cpp +++ b/src/mir_core/src/Windows/CCtrlPages.cpp @@ -1,411 +1,411 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static volatile long g_order = 1; - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlPages - -struct CCtrlPages::TPageInfo : public MZeroedObject -{ - TPageInfo() - { - m_iOrder = InterlockedIncrement(&g_order); - } - - ~TPageInfo() - { - if (m_hIcon) - DestroyIcon(m_hIcon); - } - - int m_iOrder; - ptrW m_ptszHeader; - HICON m_hIcon; - bool m_bChanged, m_bScheduledResize; - CDlgBase *m_pDlg; -}; - -CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId), - m_hIml(nullptr), - m_pActivePage(nullptr), - m_pages(4, NumericKeySortT) -{} - -void CCtrlPages::OnInit() -{ - CSuper::OnInit(); - Subclass(); - - for (auto &it : m_pages) - InsertPage(it); - m_pages.destroy(); - - ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); - - TPageInfo *info = GetCurrPage(); - if (info) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - int tabCount; - - switch (msg) { - case WM_SIZE: - if (TPageInfo *pCurrInfo = GetCurrPage()) { - tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p == nullptr) - continue; - if (p == pCurrInfo) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - else p->m_bScheduledResize = true; - } - } - break; - - case PSM_CHANGED: - if (TPageInfo *info = GetCurrPage()) - info->m_bChanged = TRUE; - return TRUE; - - case PSM_FORCECHANGED: - tabCount = GetCount(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p) { - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - if (pshn.hdr.hwndFrom != nullptr) - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) -{ - TPageInfo *info = new TPageInfo; - info->m_pDlg = pDlg; - info->m_hIcon = hIcon; - info->m_ptszHeader = mir_wstrdup(ptszName); - - if (m_hwnd != nullptr) { - InsertPage(info); - - if (GetCount() == 1) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - } - m_pages.insert(info); -} - -void CCtrlPages::ActivatePage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - if (info == nullptr || info->m_pDlg == nullptr) - return; - - if (m_pActivePage != nullptr) - ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); - - m_pActivePage = info->m_pDlg; - if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - - TabCtrl_SetCurSel(m_hwnd, iPage); - ShowPage(m_pActivePage); - ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); -} - -void CCtrlPages::CheckRowCount() -{ - int iRowCount = TabCtrl_GetRowCount(m_hwnd); - if (m_numRows != iRowCount) { - m_numRows = iRowCount; - for (auto &p : m_pages) - p->m_bScheduledResize = true; - } -} - -int CCtrlPages::GetCount() -{ - return TabCtrl_GetItemCount(m_hwnd); -} - -CDlgBase* CCtrlPages::GetNthPage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - return (info == nullptr) ? nullptr : info->m_pDlg; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) -{ - int tabCount = TabCtrl_GetItemCount(m_hwnd); - for (int i = 0; i < tabCount; i++) { - TCITEM tci; - tci.mask = TCIF_PARAM | TCIF_IMAGE; - TabCtrl_GetItem(m_hwnd, i, &tci); - TPageInfo *pPage = (TPageInfo *)tci.lParam; - if (pPage == nullptr) - continue; - - if (pPage->m_pDlg == pDlg) - return i; - } - - return -1; -} - -void CCtrlPages::InsertPage(TPageInfo *pPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)pPage; - tci.pszText = TranslateW_LP(pPage->m_ptszHeader); - if (pPage->m_hIcon) { - if (!m_hIml) { - m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - TabCtrl_SetImageList(m_hwnd, m_hIml); - } - - tci.mask |= TCIF_IMAGE; - tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); - } - - TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); - - CheckRowCount(); -} - -void CCtrlPages::RemovePage(int iPage) -{ - TPageInfo *p = GetItemPage(iPage); - if (p == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, iPage); - m_pages.remove(p); - delete p; - - CheckRowCount(); -} - -void CCtrlPages::ShowPage(CDlgBase *pDlg) -{ - if (pDlg->GetHwnd() == nullptr) { - pDlg->SetParent(m_hwnd); - pDlg->Create(); - - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); - - EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(pDlg->GetHwnd(), SW_SHOW); -} - -void CCtrlPages::SwapPages(int idx1, int idx2) -{ - TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); - if (p1 == nullptr || p2 == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, idx1); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)p1; - tci.pszText = TranslateW_LP(p1->m_ptszHeader); - TabCtrl_InsertItem(m_hwnd, idx2, &tci); -} - -BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) -{ - TPageInfo *info; - PSHNOTIFY pshn; - - switch (pnmh->code) { - case TCN_SELCHANGING: - if (info = GetCurrPage()) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - return TRUE; - - case TCN_SELCHANGE: - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - - if (info = GetCurrPage()) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - else m_pActivePage = nullptr; - return TRUE; - } - - return FALSE; -} - -void CCtrlPages::OnReset() -{ - CSuper::OnReset(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -bool CCtrlPages::OnApply() -{ - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - if (m_pActivePage != nullptr) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) - return false; - } - - pshn.hdr.code = PSN_APPLY; - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(m_hwnd, i); - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - m_pActivePage = p->m_pDlg; - m_pActivePage->Show(); - return false; - } - } - - CSuper::OnApply(); - return true; -} - -void CCtrlPages::OnDestroy() -{ - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; - if (pDlg->GetHwnd()) - pDlg->Close(); - delete p; - } - - TabCtrl_DeleteAllItems(m_hwnd); - - if (m_hIml) { - TabCtrl_SetImageList(m_hwnd, nullptr); - ImageList_Destroy(m_hIml); - } - - CSuper::OnDestroy(); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static volatile long g_order = 1;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlPages
+
+struct CCtrlPages::TPageInfo : public MZeroedObject
+{
+ TPageInfo()
+ {
+ m_iOrder = InterlockedIncrement(&g_order);
+ }
+
+ ~TPageInfo()
+ {
+ if (m_hIcon)
+ DestroyIcon(m_hIcon);
+ }
+
+ int m_iOrder;
+ ptrW m_ptszHeader;
+ HICON m_hIcon;
+ bool m_bChanged, m_bScheduledResize;
+ CDlgBase *m_pDlg;
+};
+
+CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId),
+ m_hIml(nullptr),
+ m_pActivePage(nullptr),
+ m_pages(4, NumericKeySortT)
+{}
+
+void CCtrlPages::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+
+ for (auto &it : m_pages)
+ InsertPage(it);
+ m_pages.destroy();
+
+ ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT);
+
+ TPageInfo *info = GetCurrPage();
+ if (info) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int tabCount;
+
+ switch (msg) {
+ case WM_SIZE:
+ if (TPageInfo *pCurrInfo = GetCurrPage()) {
+ tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p == nullptr)
+ continue;
+ if (p == pCurrInfo) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ else p->m_bScheduledResize = true;
+ }
+ }
+ break;
+
+ case PSM_CHANGED:
+ if (TPageInfo *info = GetCurrPage())
+ info->m_bChanged = TRUE;
+ return TRUE;
+
+ case PSM_FORCECHANGED:
+ tabCount = GetCount();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p) {
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ if (pshn.hdr.hwndFrom != nullptr)
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ }
+ break;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg)
+{
+ TPageInfo *info = new TPageInfo;
+ info->m_pDlg = pDlg;
+ info->m_hIcon = hIcon;
+ info->m_ptszHeader = mir_wstrdup(ptszName);
+
+ if (m_hwnd != nullptr) {
+ InsertPage(info);
+
+ if (GetCount() == 1) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ }
+ m_pages.insert(info);
+}
+
+void CCtrlPages::ActivatePage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ if (info == nullptr || info->m_pDlg == nullptr)
+ return;
+
+ if (m_pActivePage != nullptr)
+ ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE);
+
+ m_pActivePage = info->m_pDlg;
+ if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+
+ TabCtrl_SetCurSel(m_hwnd, iPage);
+ ShowPage(m_pActivePage);
+ ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0);
+}
+
+void CCtrlPages::CheckRowCount()
+{
+ int iRowCount = TabCtrl_GetRowCount(m_hwnd);
+ if (m_numRows != iRowCount) {
+ m_numRows = iRowCount;
+ for (auto &p : m_pages)
+ p->m_bScheduledResize = true;
+ }
+}
+
+int CCtrlPages::GetCount()
+{
+ return TabCtrl_GetItemCount(m_hwnd);
+}
+
+CDlgBase* CCtrlPages::GetNthPage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ return (info == nullptr) ? nullptr : info->m_pDlg;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage()
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, iPage, &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+int CCtrlPages::GetDlgIndex(CDlgBase *pDlg)
+{
+ int tabCount = TabCtrl_GetItemCount(m_hwnd);
+ for (int i = 0; i < tabCount; i++) {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM | TCIF_IMAGE;
+ TabCtrl_GetItem(m_hwnd, i, &tci);
+ TPageInfo *pPage = (TPageInfo *)tci.lParam;
+ if (pPage == nullptr)
+ continue;
+
+ if (pPage->m_pDlg == pDlg)
+ return i;
+ }
+
+ return -1;
+}
+
+void CCtrlPages::InsertPage(TPageInfo *pPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)pPage;
+ tci.pszText = TranslateW_LP(pPage->m_ptszHeader);
+ if (pPage->m_hIcon) {
+ if (!m_hIml) {
+ m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ TabCtrl_SetImageList(m_hwnd, m_hIml);
+ }
+
+ tci.mask |= TCIF_IMAGE;
+ tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon);
+ }
+
+ TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci);
+
+ CheckRowCount();
+}
+
+void CCtrlPages::RemovePage(int iPage)
+{
+ TPageInfo *p = GetItemPage(iPage);
+ if (p == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, iPage);
+ m_pages.remove(p);
+ delete p;
+
+ CheckRowCount();
+}
+
+void CCtrlPages::ShowPage(CDlgBase *pDlg)
+{
+ if (pDlg->GetHwnd() == nullptr) {
+ pDlg->SetParent(m_hwnd);
+ pDlg->Create();
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE);
+
+ EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ ShowWindow(pDlg->GetHwnd(), SW_SHOW);
+}
+
+void CCtrlPages::SwapPages(int idx1, int idx2)
+{
+ TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2);
+ if (p1 == nullptr || p2 == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, idx1);
+
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)p1;
+ tci.pszText = TranslateW_LP(p1->m_ptszHeader);
+ TabCtrl_InsertItem(m_hwnd, idx2, &tci);
+}
+
+BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh)
+{
+ TPageInfo *info;
+ PSHNOTIFY pshn;
+
+ switch (pnmh->code) {
+ case TCN_SELCHANGING:
+ if (info = GetCurrPage()) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ return TRUE;
+
+ case TCN_SELCHANGE:
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+
+ if (info = GetCurrPage()) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ else m_pActivePage = nullptr;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CCtrlPages::OnReset()
+{
+ CSuper::OnReset();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+bool CCtrlPages::OnApply()
+{
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ if (m_pActivePage != nullptr) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn))
+ return false;
+ }
+
+ pshn.hdr.code = PSN_APPLY;
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) {
+ TabCtrl_SetCurSel(m_hwnd, i);
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+ m_pActivePage = p->m_pDlg;
+ m_pActivePage->Show();
+ return false;
+ }
+ }
+
+ CSuper::OnApply();
+ return true;
+}
+
+void CCtrlPages::OnDestroy()
+{
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr;
+ if (pDlg->GetHwnd())
+ pDlg->Close();
+ delete p;
+ }
+
+ TabCtrl_DeleteAllItems(m_hwnd);
+
+ if (m_hIml) {
+ TabCtrl_SetImageList(m_hwnd, nullptr);
+ ImageList_Destroy(m_hIml);
+ }
+
+ CSuper::OnDestroy();
+}
diff --git a/src/mir_core/src/Windows/CCtrlRichEdit.cpp b/src/mir_core/src/Windows/CCtrlRichEdit.cpp index 06bca26cb5..f41cad3f98 100644 --- a/src/mir_core/src/Windows/CCtrlRichEdit.cpp +++ b/src/mir_core/src/Windows/CCtrlRichEdit.cpp @@ -1,192 +1,192 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -#include <RichOle.h> - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlRichEdit class - -CCtrlRichEdit::CCtrlRichEdit(CDlgBase *dlg, int ctrlId) - : CCtrlEdit(dlg, ctrlId) -{} - -int CCtrlRichEdit::GetRichTextLength(int iCodePage) const -{ - GETTEXTLENGTHEX gtl; - gtl.codepage = iCodePage; - gtl.flags = GTL_PRECISE; - if (iCodePage == CP_ACP) - gtl.flags |= GTL_NUMBYTES; - else - gtl.flags |= GTL_NUMCHARS | GT_USECRLF; - - return (int)SendMessage(m_hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); -} - -int CCtrlRichEdit::SetRichText(const wchar_t *text) -{ - SETTEXTEX st; - st.flags = ST_DEFAULT; - st.codepage = 1200; - SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); - - return GetRichTextLength(1200); -} - -int CCtrlRichEdit::SetRichTextRtf(const char *text) -{ - SETTEXTEX st; - st.flags = ST_DEFAULT; - st.codepage = CP_UTF8; - SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); - - return GetRichTextLength(1200); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static DWORD CALLBACK MessageStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - static uint32_t dwRead; - char **ppText = (char **)dwCookie; - - if (*ppText == nullptr) { - *ppText = (char *)mir_alloc(cb + 2); - memcpy(*ppText, pbBuff, cb); - *pcb = cb; - dwRead = cb; - *(*ppText + cb) = '\0'; - } - else { - char *p = (char *)mir_realloc(*ppText, dwRead + cb + 2); - memcpy(p + dwRead, pbBuff, cb); - *ppText = p; - *pcb = cb; - dwRead += cb; - *(*ppText + dwRead) = '\0'; - } - return 0; -} - -char* CCtrlRichEdit::GetRichTextRtf(bool bText, bool bSelection) const -{ - char *pszText = nullptr; - uint32_t dwFlags = SF_USECODEPAGE | (CP_UTF8 << 16); - if (bText) - dwFlags |= SF_TEXT; - else - dwFlags |= SF_RTFNOOBJS | SFF_PLAINRTF; - if (bSelection) - dwFlags |= SFF_SELECTION; - - EDITSTREAM stream = { 0 }; - stream.pfnCallback = MessageStreamCallback; - stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer - SendMessage(m_hwnd, EM_STREAMOUT, dwFlags, (LPARAM)&stream); - return pszText; // pszText contains the text -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct CREOleCallback : public IRichEditOleCallback -{ - CREOleCallback() : refCount(0), nextStgId(0), pictStg(nullptr) {} - unsigned refCount; - IStorage *pictStg; - int nextStgId; - - STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR *ppvObj) - { - if (IsEqualIID(riid, IID_IRichEditOleCallback)) { - *ppvObj = this; - AddRef(); - return S_OK; - } - *ppvObj = nullptr; - return E_NOINTERFACE; - } - - STDMETHOD_(ULONG, AddRef)(THIS) - { - if (refCount == 0) - StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &pictStg); - - return ++refCount; - } - - STDMETHOD_(ULONG, Release)(THIS) - { - if (--refCount == 0) { - if (pictStg) { - pictStg->Release(); - pictStg = nullptr; - } - } - return refCount; - } - - STDMETHOD(GetNewStorage)(LPSTORAGE *lplpstg) - { - wchar_t sztName[64]; - mir_snwprintf(sztName, L"s%u", nextStgId++); - if (pictStg == nullptr) - return STG_E_MEDIUMFULL; - return pictStg->CreateStorage(sztName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg); - } - - STDMETHOD(ContextSensitiveHelp)(BOOL) - { return S_OK; } - STDMETHOD(GetInPlaceContext)(LPOLEINPLACEFRAME*, LPOLEINPLACEUIWINDOW*, LPOLEINPLACEFRAMEINFO) - { return E_INVALIDARG; } - STDMETHOD(ShowContainerUI)(BOOL) - { return S_OK; } - STDMETHOD(QueryInsertObject)(LPCLSID, LPSTORAGE, LONG) - { return S_OK; } - STDMETHOD(DeleteObject)(LPOLEOBJECT) - { return S_OK; } - STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT*, DWORD, BOOL, HGLOBAL) - { return S_OK; } - STDMETHOD(GetClipboardData)(CHARRANGE*, DWORD, LPDATAOBJECT*) - { return E_NOTIMPL; } - STDMETHOD(GetDragDropEffect)(BOOL, DWORD, LPDWORD) - { return S_OK; } - STDMETHOD(GetContextMenu)(uint16_t, LPOLEOBJECT, CHARRANGE*, HMENU*) - { return E_INVALIDARG; } -}; - -struct CREOleCallback2 : public CREOleCallback -{ - STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT *lpcfFormat, DWORD, BOOL, HGLOBAL) - { *lpcfFormat = CF_UNICODETEXT; - return S_OK; - } -}; - -CREOleCallback reOleCallback; -CREOleCallback2 reOleCallback2; - -void CCtrlRichEdit::SetReadOnly(bool bReadOnly) -{ - SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)(bReadOnly ? &reOleCallback : &reOleCallback2)); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+#include <RichOle.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlRichEdit class
+
+CCtrlRichEdit::CCtrlRichEdit(CDlgBase *dlg, int ctrlId)
+ : CCtrlEdit(dlg, ctrlId)
+{}
+
+int CCtrlRichEdit::GetRichTextLength(int iCodePage) const
+{
+ GETTEXTLENGTHEX gtl;
+ gtl.codepage = iCodePage;
+ gtl.flags = GTL_PRECISE;
+ if (iCodePage == CP_ACP)
+ gtl.flags |= GTL_NUMBYTES;
+ else
+ gtl.flags |= GTL_NUMCHARS | GT_USECRLF;
+
+ return (int)SendMessage(m_hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
+}
+
+int CCtrlRichEdit::SetRichText(const wchar_t *text)
+{
+ SETTEXTEX st;
+ st.flags = ST_DEFAULT;
+ st.codepage = 1200;
+ SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text);
+
+ return GetRichTextLength(1200);
+}
+
+int CCtrlRichEdit::SetRichTextRtf(const char *text)
+{
+ SETTEXTEX st;
+ st.flags = ST_DEFAULT;
+ st.codepage = CP_UTF8;
+ SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text);
+
+ return GetRichTextLength(1200);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static DWORD CALLBACK MessageStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ static uint32_t dwRead;
+ char **ppText = (char **)dwCookie;
+
+ if (*ppText == nullptr) {
+ *ppText = (char *)mir_alloc(cb + 2);
+ memcpy(*ppText, pbBuff, cb);
+ *pcb = cb;
+ dwRead = cb;
+ *(*ppText + cb) = '\0';
+ }
+ else {
+ char *p = (char *)mir_realloc(*ppText, dwRead + cb + 2);
+ memcpy(p + dwRead, pbBuff, cb);
+ *ppText = p;
+ *pcb = cb;
+ dwRead += cb;
+ *(*ppText + dwRead) = '\0';
+ }
+ return 0;
+}
+
+char* CCtrlRichEdit::GetRichTextRtf(bool bText, bool bSelection) const
+{
+ char *pszText = nullptr;
+ uint32_t dwFlags = SF_USECODEPAGE | (CP_UTF8 << 16);
+ if (bText)
+ dwFlags |= SF_TEXT;
+ else
+ dwFlags |= SF_RTFNOOBJS | SFF_PLAINRTF;
+ if (bSelection)
+ dwFlags |= SFF_SELECTION;
+
+ EDITSTREAM stream = { 0 };
+ stream.pfnCallback = MessageStreamCallback;
+ stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer
+ SendMessage(m_hwnd, EM_STREAMOUT, dwFlags, (LPARAM)&stream);
+ return pszText; // pszText contains the text
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct CREOleCallback : public IRichEditOleCallback
+{
+ CREOleCallback() : refCount(0), nextStgId(0), pictStg(nullptr) {}
+ unsigned refCount;
+ IStorage *pictStg;
+ int nextStgId;
+
+ STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR *ppvObj)
+ {
+ if (IsEqualIID(riid, IID_IRichEditOleCallback)) {
+ *ppvObj = this;
+ AddRef();
+ return S_OK;
+ }
+ *ppvObj = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ STDMETHOD_(ULONG, AddRef)(THIS)
+ {
+ if (refCount == 0)
+ StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &pictStg);
+
+ return ++refCount;
+ }
+
+ STDMETHOD_(ULONG, Release)(THIS)
+ {
+ if (--refCount == 0) {
+ if (pictStg) {
+ pictStg->Release();
+ pictStg = nullptr;
+ }
+ }
+ return refCount;
+ }
+
+ STDMETHOD(GetNewStorage)(LPSTORAGE *lplpstg)
+ {
+ wchar_t sztName[64];
+ mir_snwprintf(sztName, L"s%u", nextStgId++);
+ if (pictStg == nullptr)
+ return STG_E_MEDIUMFULL;
+ return pictStg->CreateStorage(sztName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg);
+ }
+
+ STDMETHOD(ContextSensitiveHelp)(BOOL)
+ { return S_OK; }
+ STDMETHOD(GetInPlaceContext)(LPOLEINPLACEFRAME*, LPOLEINPLACEUIWINDOW*, LPOLEINPLACEFRAMEINFO)
+ { return E_INVALIDARG; }
+ STDMETHOD(ShowContainerUI)(BOOL)
+ { return S_OK; }
+ STDMETHOD(QueryInsertObject)(LPCLSID, LPSTORAGE, LONG)
+ { return S_OK; }
+ STDMETHOD(DeleteObject)(LPOLEOBJECT)
+ { return S_OK; }
+ STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT*, DWORD, BOOL, HGLOBAL)
+ { return S_OK; }
+ STDMETHOD(GetClipboardData)(CHARRANGE*, DWORD, LPDATAOBJECT*)
+ { return E_NOTIMPL; }
+ STDMETHOD(GetDragDropEffect)(BOOL, DWORD, LPDWORD)
+ { return S_OK; }
+ STDMETHOD(GetContextMenu)(uint16_t, LPOLEOBJECT, CHARRANGE*, HMENU*)
+ { return E_INVALIDARG; }
+};
+
+struct CREOleCallback2 : public CREOleCallback
+{
+ STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT *lpcfFormat, DWORD, BOOL, HGLOBAL)
+ { *lpcfFormat = CF_UNICODETEXT;
+ return S_OK;
+ }
+};
+
+CREOleCallback reOleCallback;
+CREOleCallback2 reOleCallback2;
+
+void CCtrlRichEdit::SetReadOnly(bool bReadOnly)
+{
+ SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)(bReadOnly ? &reOleCallback : &reOleCallback2));
+}
diff --git a/src/mir_core/src/Windows/CCtrlSlider.cpp b/src/mir_core/src/Windows/CCtrlSlider.cpp index 69aeb24796..9938736e42 100644 --- a/src/mir_core/src/Windows/CCtrlSlider.cpp +++ b/src/mir_core/src/Windows/CCtrlSlider.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSlider class - -CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax) -{ - m_bNotifiable = true; -} - -BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == WM_HSCROLL) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSlider::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetPosition()); - return true; -} - -void CCtrlSlider::OnReset() -{ - SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int CCtrlSlider::GetPosition() const -{ - return SendMsg(TBM_GETPOS, 0, 0); -} - -void CCtrlSlider::SetPosition(int wPos) -{ - SendMsg(TBM_SETPOS, TRUE, wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSlider class
+
+CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == WM_HSCROLL) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSlider::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetPosition());
+ return true;
+}
+
+void CCtrlSlider::OnReset()
+{
+ SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int CCtrlSlider::GetPosition() const
+{
+ return SendMsg(TBM_GETPOS, 0, 0);
+}
+
+void CCtrlSlider::SetPosition(int wPos)
+{
+ SendMsg(TBM_SETPOS, TRUE, wPos);
+}
diff --git a/src/mir_core/src/Windows/CCtrlSpin.cpp b/src/mir_core/src/Windows/CCtrlSpin.cpp index 54d43e933a..54dc5ffd07 100644 --- a/src/mir_core/src/Windows/CCtrlSpin.cpp +++ b/src/mir_core/src/Windows/CCtrlSpin.cpp @@ -1,81 +1,81 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSpin class - -CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax), - m_wCurr(0) -{} - -BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - auto *pEvent = (NMUPDOWN *)pnmh; - m_wCurr = pEvent->iPos + pEvent->iDelta; - - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSpin::OnApply() -{ - CSuper::OnApply(); - - m_wCurr = SendMsg(UDM_GETPOS, 0, 0); - if (m_dbLink != nullptr) - SaveInt(m_wCurr); - - HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); - if (hwndBuddy) { - wchar_t buf[100]; - _itow(m_wCurr, buf, 10); - ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); - } - - return true; -} - -void CCtrlSpin::OnReset() -{ - SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int16_t CCtrlSpin::GetPosition() -{ - return m_wCurr; -} - -void CCtrlSpin::SetPosition(int16_t wPos) -{ - SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSpin class
+
+CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax),
+ m_wCurr(0)
+{}
+
+BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == UDN_DELTAPOS) {
+ auto *pEvent = (NMUPDOWN *)pnmh;
+ m_wCurr = pEvent->iPos + pEvent->iDelta;
+
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSpin::OnApply()
+{
+ CSuper::OnApply();
+
+ m_wCurr = SendMsg(UDM_GETPOS, 0, 0);
+ if (m_dbLink != nullptr)
+ SaveInt(m_wCurr);
+
+ HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0);
+ if (hwndBuddy) {
+ wchar_t buf[100];
+ _itow(m_wCurr, buf, 10);
+ ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf));
+ }
+
+ return true;
+}
+
+void CCtrlSpin::OnReset()
+{
+ SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int16_t CCtrlSpin::GetPosition()
+{
+ return m_wCurr;
+}
+
+void CCtrlSpin::SetPosition(int16_t wPos)
+{
+ SendMsg(UDM_SETPOS, 0, m_wCurr = wPos);
+}
diff --git a/src/mir_core/src/Windows/CCtrlTreeOpts.cpp b/src/mir_core/src/Windows/CCtrlTreeOpts.cpp index d15f3d4b1b..7935283285 100644 --- a/src/mir_core/src/Windows/CCtrlTreeOpts.cpp +++ b/src/mir_core/src/Windows/CCtrlTreeOpts.cpp @@ -2,7 +2,7 @@ Object UI extensions Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/mir_core/src/Windows/CCtrlTreeView.cpp b/src/mir_core/src/Windows/CCtrlTreeView.cpp index 6f435920f2..178b6b730e 100644 --- a/src/mir_core/src/Windows/CCtrlTreeView.cpp +++ b/src/mir_core/src/Windows/CCtrlTreeView.cpp @@ -2,7 +2,7 @@ Object UI extensions Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/mir_core/src/Windows/CDbLink.cpp b/src/mir_core/src/Windows/CDbLink.cpp index 2a0734cd3b..05925f871b 100644 --- a/src/mir_core/src/Windows/CDbLink.cpp +++ b/src/mir_core/src/Windows/CDbLink.cpp @@ -1,92 +1,92 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbLink class - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) - : CDataLink(type) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_iDefault = iValue; - m_szDefault = nullptr; - dbv.type = DBVT_DELETED; -} - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) - : CDataLink(type), - m_iDefault(0) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_szDefault = mir_wstrdup(szValue); - dbv.type = DBVT_DELETED; -} - -CDbLink::~CDbLink() -{ - mir_free(m_szModule); - mir_free(m_szSetting); - mir_free(m_szDefault); - if (dbv.type != DBVT_DELETED) - db_free(&dbv); -} - -uint32_t CDbLink::LoadInt() -{ - switch (m_type) { - case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); - default: return m_iDefault; - } -} - -void CDbLink::SaveInt(uint32_t value) -{ - switch (m_type) { - case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; - case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; - case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; - } -} - -wchar_t* CDbLink::LoadText() -{ - if (dbv.type != DBVT_DELETED) db_free(&dbv); - if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { - if (dbv.type == DBVT_WCHAR) - return dbv.pwszVal; - return m_szDefault; - } - - dbv.type = DBVT_DELETED; - return m_szDefault; -} - -void CDbLink::SaveText(wchar_t *value) -{ - db_set_ws(0, m_szModule, m_szSetting, value); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbLink class
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue)
+ : CDataLink(type)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_iDefault = iValue;
+ m_szDefault = nullptr;
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue)
+ : CDataLink(type),
+ m_iDefault(0)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_szDefault = mir_wstrdup(szValue);
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::~CDbLink()
+{
+ mir_free(m_szModule);
+ mir_free(m_szSetting);
+ mir_free(m_szDefault);
+ if (dbv.type != DBVT_DELETED)
+ db_free(&dbv);
+}
+
+uint32_t CDbLink::LoadInt()
+{
+ switch (m_type) {
+ case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault);
+ default: return m_iDefault;
+ }
+}
+
+void CDbLink::SaveInt(uint32_t value)
+{
+ switch (m_type) {
+ case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break;
+ case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break;
+ case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break;
+ }
+}
+
+wchar_t* CDbLink::LoadText()
+{
+ if (dbv.type != DBVT_DELETED) db_free(&dbv);
+ if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) {
+ if (dbv.type == DBVT_WCHAR)
+ return dbv.pwszVal;
+ return m_szDefault;
+ }
+
+ dbv.type = DBVT_DELETED;
+ return m_szDefault;
+}
+
+void CDbLink::SaveText(wchar_t *value)
+{
+ db_set_ws(0, m_szModule, m_szSetting, value);
+}
diff --git a/src/mir_core/src/Windows/CDlgBase.cpp b/src/mir_core/src/Windows/CDlgBase.cpp index 84066de414..45ae944e21 100644 --- a/src/mir_core/src/Windows/CDlgBase.cpp +++ b/src/mir_core/src/Windows/CDlgBase.cpp @@ -1,522 +1,522 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" -#include "diatheme.h" - -static mir_cs csDialogs; - -static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST<CDlgBase> arDialogs(10, CompareDialogs); - -#pragma comment(lib, "uxtheme") - -///////////////////////////////////////////////////////////////////////////////////////// -// CDlgBase - -static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) -{ - return c1->GetCtrlId() - c2->GetCtrlId(); -} - -static int CompareTimerId(const CTimer *t1, const CTimer *t2) -{ - return t1->GetEventId() - t2->GetEventId(); -} - -CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) : - m_controls(1, CompareControlId), - m_timers(1, CompareTimerId), - m_pPlugin(pPlug), - m_bFixedSize(!g_bEnableDpiAware) -{ - m_idDialog = idDialog; - m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; -} - -CDlgBase::~CDlgBase() -{ - m_bInitialized = false; // prevent double call of destructor - if (m_hwnd) - DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// events - -bool CDlgBase::OnInitDialog() -{ - return true; -} - -bool CDlgBase::OnClose() -{ - return true; -} - -bool CDlgBase::OnApply() -{ - return true; -} - -void CDlgBase::OnChange() -{} - -void CDlgBase::OnDestroy() -{} - -void CDlgBase::OnReset() -{} - -void CDlgBase::OnTimer(CTimer*) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// methods - -void CDlgBase::Close() -{ - ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); -} - -void CDlgBase::Create() -{ - if (!m_bFixedSize) { - mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); - CreateDialogIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); - } - else CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -int CDlgBase::DoModal() -{ - m_isModal = true; - - if (m_bFixedSize) - return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); - - mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); - return DialogBoxIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -void CDlgBase::EndModal(INT_PTR nResult) -{ - ::EndDialog(m_hwnd, nResult); -} - -HINSTANCE CDlgBase::GetInst() const -{ - return m_pPlugin.getInst(); -} - -void CDlgBase::NotifyChange(void) -{ - if (!m_bInitialized) - return; - - OnChange(); - - if (m_hwndParent) - SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); -} - -void CDlgBase::Resize() -{ - SendMessage(m_hwnd, WM_SIZE, 0, 0); -} - -void CDlgBase::SetCaption(const wchar_t *ptszCaption) -{ - if (m_hwnd && ptszCaption) - SetWindowText(m_hwnd, ptszCaption); -} - -void CDlgBase::SetDraw(bool bEnable) -{ - ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CDlgBase::Show(int nCmdShow) -{ - if (m_hwnd == nullptr) - Create(); - ShowWindow(m_hwnd, nCmdShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); -} - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// virtual methods - -int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc) -{ - CDlgBase *wnd = CDlgBase::Find(hwnd); - return (wnd == nullptr) ? 0 : wnd->Resizer(urc); -} - -void CDlgBase::OnResize() -{ - if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_THICKFRAME)) - Utils_ResizeDialog(m_hwnd, m_pPlugin.getInst(), MAKEINTRESOURCEA(m_idDialog), GlobalDlgResizer); -} - -int CDlgBase::Resizer(UTILRESIZECONTROL*) -{ - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -BOOL CALLBACK CDlgBase::GlobalFieldEnum(HWND hwnd, LPARAM lParam) -{ - CDlgBase *pDlg = (CDlgBase*)lParam; - int id = GetWindowLongPtrW(hwnd, GWLP_ID); - if (id <= 0) - return TRUE; - - // already declared inside the class? skipping - CCtrlBase *ctrl = pDlg->FindControl(id); - if (ctrl != nullptr) - return TRUE; - - wchar_t wszClass[100]; - GetClassNameW(hwnd, wszClass, _countof(wszClass)); - if (!wcsicmp(wszClass, L"Static")) - new CCtrlLabel(pDlg, id); - if (!wcsicmp(wszClass, L"Edit")) - new CCtrlEdit(pDlg, id); - else if (!wcsicmp(wszClass, L"ComboBox")) - new CCtrlCombo(pDlg, id); - else if (!wcsicmp(wszClass, L"Button")) { - switch (GetWindowLongW(hwnd, GWL_STYLE) & (BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON)) { - case BS_CHECKBOX: - case BS_AUTOCHECKBOX: - case BS_RADIOBUTTON: - case BS_AUTORADIOBUTTON: - new CCtrlCheck(pDlg, id); - break; - - default: - new CCtrlButton(pDlg, id); - } - } - else if (!wcsicmp(wszClass, L"RichEdit50W")) - new CCtrlRichEdit(pDlg, id); - else if (!wcsicmp(wszClass, L"msctls_updown32")) - new CCtrlSpin(pDlg, id); - - return TRUE; -} - -INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - m_bInitialized = m_bSucceeded = false; - TranslateDialog_LP(m_hwnd, &m_pPlugin); - - ::EnumChildWindows(m_hwnd, &GlobalFieldEnum, LPARAM(this)); - - NotifyControls(&CCtrlBase::OnInit); - if (!OnInitDialog()) - return FALSE; - - for (auto &it : m_controls) - if (it->m_bNotifiable) - it->OnChange(it); - - m_bInitialized = true; - return TRUE; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - if (CCtrlBase *ctrl = FindControl(HWND(lParam))) { - if (ctrl->m_bUseSystemColors) { - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - } - } - break; - - case WM_GETMINMAXINFO: - if (m_iMinHeight != -1 && m_iMinWidth != -1) { - MINMAXINFO *lpmmi = (MINMAXINFO*)lParam; - lpmmi->ptMinTrackSize.y = m_iMinHeight; - lpmmi->ptMinTrackSize.x = m_iMinWidth; - return 0; - } - break; - - case WM_MEASUREITEM: - if (!Menu_MeasureItem(lParam)) { - MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnMeasureItem(param); - } - return FALSE; - - case WM_DRAWITEM: - if (!Menu_DrawItem(lParam)) { - DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnDrawItem(param); - } - return FALSE; - - case WM_DELETEITEM: - { - DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnDeleteItem(param); - } - return FALSE; - - case WM_COMMAND: - { - HWND hwndCtrl = (HWND)lParam; - uint16_t idCtrl = LOWORD(wParam); - uint16_t idCode = HIWORD(wParam); - if (CCtrlBase *ctrl = FindControl(idCtrl)) { - BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode); - if (result != FALSE) - return result; - } - - if (idCode == BN_CLICKED) { - // close dialog automatically if 'Cancel' button is pressed - if (idCtrl == IDCANCEL && (m_autoClose & CLOSE_ON_CANCEL)) { - m_bExiting = true; - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - } - - // close dialog automatically if 'OK' button is pressed - if (idCtrl == IDOK && (m_autoClose & CLOSE_ON_OK)) { - // validate dialog data first - if (VerifyControls(&CCtrlBase::OnApply)) { - m_bExiting = true; - - // everything ok? good, let's close it - if (OnApply()) { - m_bSucceeded = true; - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - } - else m_bExiting = false; - } - } - } - } - return FALSE; - - case WM_NOTIFY: - { - int idCtrl = wParam; - NMHDR *pnmh = (NMHDR *)lParam; - if (pnmh->idFrom == 0) { - switch (pnmh->code) { - case PSN_APPLY: - if (LPPSHNOTIFY(lParam)->lParam != 3) // IDC_APPLY - m_bExiting = true; - - if (!VerifyControls(&CCtrlBase::OnApply)) - m_bExiting = false; - else if (!OnApply()) - m_bExiting = false; - break; - - case PSN_RESET: - NotifyControls(&CCtrlBase::OnReset); - OnReset(); - break; - - case PSN_WIZFINISH: - m_OnFinishWizard(this); - break; - } - } - - if (CCtrlBase *ctrl = FindControl(pnmh->idFrom)) - return ctrl->OnNotify(idCtrl, pnmh); - } - return FALSE; - - case WM_HSCROLL: - if (auto *pCtrl = FindControl(HWND(lParam))) - pCtrl->OnCommand(HWND(lParam), pCtrl->m_idCtrl, WM_HSCROLL); - break; - - case PSM_CHANGED: - if (m_bInitialized) - OnChange(); - break; - - case WM_CONTEXTMENU: - if (CCtrlBase *ctrl = FindControl(HWND(wParam))) { - CContextMenuPos pos = {}; - if (lParam != -1) { - pos.pt.x = GET_X_LPARAM(lParam); - pos.pt.y = GET_Y_LPARAM(lParam); - } - ctrl->GetCaretPos(pos); - ctrl->OnBuildMenu(&pos); - } - break; - - case WM_SIZE: - OnResize(); - return TRUE; - - case WM_TIMER: - if (CTimer *timer = FindTimer(wParam)) - return timer->OnTimer(); - return FALSE; - - case WM_CLOSE: - if (OnClose()) { - m_bExiting = true; - if (m_isModal) - EndModal(m_bSucceeded ? IDOK : FALSE); - else - DestroyWindow(m_hwnd); - } - return TRUE; - - case WM_DESTROY: - m_bExiting = true; - OnDestroy(); - NotifyControls(&CCtrlBase::OnDestroy); - { - mir_cslock lck(csDialogs); - int idx = arDialogs.getIndex(this); - if (idx != -1) - arDialogs.remove(idx); - } - m_hwnd = nullptr; - if (m_bInitialized) { - if (m_isModal) - m_isModal = false; - else // modeless dialogs MUST be allocated with 'new' - delete this; - } - - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CDlgBase *wnd; - if (msg == WM_INITDIALOG) { - wnd = (CDlgBase*)lParam; - wnd->m_hwnd = hwnd; - - mir_cslock lck(csDialogs); - arDialogs.insert(wnd); - } - else wnd = CDlgBase::Find(hwnd); - - return (wnd == nullptr) ? FALSE : wnd->DlgProc(msg, wParam, lParam); -} - -void CDlgBase::ThemeDialogBackground(BOOL tabbed) -{ - EnableThemeDialogTexture(m_hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE); -} - -void CDlgBase::AddControl(CCtrlBase *ctrl) -{ - m_controls.insert(ctrl); -} - -void CDlgBase::RemoveControl(CCtrlBase *ctrl) -{ - m_controls.remove(ctrl); -} - -void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - (it->*fn)(); -} - -bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - if (!(it->*fn)()) - return false; - - return true; -} - -CCtrlBase* CDlgBase::FindControl(int idCtrl) -{ - CCtrlBase search(nullptr, idCtrl); - return m_controls.find(&search); -} - -CCtrlBase* CDlgBase::FindControl(HWND hwnd) -{ - for (auto &it : m_controls) - if (it->GetHwnd() == hwnd) - return it; - - return nullptr; -} - -void CDlgBase::AddTimer(CTimer *timer) -{ - m_timers.insert(timer); -} - -void CDlgBase::RemoveTimer(UINT_PTR idEvent) -{ - CTimer search(nullptr, idEvent); - m_timers.remove(&search); -} - -CTimer* CDlgBase::FindTimer(int idEvent) -{ - CTimer search(nullptr, idEvent); - return m_timers.find(&search); -} - -CDlgBase* CDlgBase::Find(HWND hwnd) -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - return arDialogs.find((CDlgBase*)&bullshit); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+#include "diatheme.h"
+
+static mir_cs csDialogs;
+
+static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+static LIST<CDlgBase> arDialogs(10, CompareDialogs);
+
+#pragma comment(lib, "uxtheme")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDlgBase
+
+static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2)
+{
+ return c1->GetCtrlId() - c2->GetCtrlId();
+}
+
+static int CompareTimerId(const CTimer *t1, const CTimer *t2)
+{
+ return t1->GetEventId() - t2->GetEventId();
+}
+
+CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) :
+ m_controls(1, CompareControlId),
+ m_timers(1, CompareTimerId),
+ m_pPlugin(pPlug),
+ m_bFixedSize(!g_bEnableDpiAware)
+{
+ m_idDialog = idDialog;
+ m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL;
+}
+
+CDlgBase::~CDlgBase()
+{
+ m_bInitialized = false; // prevent double call of destructor
+ if (m_hwnd)
+ DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// events
+
+bool CDlgBase::OnInitDialog()
+{
+ return true;
+}
+
+bool CDlgBase::OnClose()
+{
+ return true;
+}
+
+bool CDlgBase::OnApply()
+{
+ return true;
+}
+
+void CDlgBase::OnChange()
+{}
+
+void CDlgBase::OnDestroy()
+{}
+
+void CDlgBase::OnReset()
+{}
+
+void CDlgBase::OnTimer(CTimer*)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// methods
+
+void CDlgBase::Close()
+{
+ ::SendMessage(m_hwnd, WM_CLOSE, 0, 0);
+}
+
+void CDlgBase::Create()
+{
+ if (!m_bFixedSize) {
+ mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst()));
+ CreateDialogIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this);
+ }
+ else CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+}
+
+int CDlgBase::DoModal()
+{
+ m_isModal = true;
+
+ if (m_bFixedSize)
+ return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+
+ mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst()));
+ return DialogBoxIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this);
+}
+
+void CDlgBase::EndModal(INT_PTR nResult)
+{
+ ::EndDialog(m_hwnd, nResult);
+}
+
+HINSTANCE CDlgBase::GetInst() const
+{
+ return m_pPlugin.getInst();
+}
+
+void CDlgBase::NotifyChange(void)
+{
+ if (!m_bInitialized)
+ return;
+
+ OnChange();
+
+ if (m_hwndParent)
+ SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0);
+}
+
+void CDlgBase::Resize()
+{
+ SendMessage(m_hwnd, WM_SIZE, 0, 0);
+}
+
+void CDlgBase::SetCaption(const wchar_t *ptszCaption)
+{
+ if (m_hwnd && ptszCaption)
+ SetWindowText(m_hwnd, ptszCaption);
+}
+
+void CDlgBase::SetDraw(bool bEnable)
+{
+ ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CDlgBase::Show(int nCmdShow)
+{
+ if (m_hwnd == nullptr)
+ Create();
+ ShowWindow(m_hwnd, nCmdShow);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue);
+}
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// virtual methods
+
+int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc)
+{
+ CDlgBase *wnd = CDlgBase::Find(hwnd);
+ return (wnd == nullptr) ? 0 : wnd->Resizer(urc);
+}
+
+void CDlgBase::OnResize()
+{
+ if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_THICKFRAME))
+ Utils_ResizeDialog(m_hwnd, m_pPlugin.getInst(), MAKEINTRESOURCEA(m_idDialog), GlobalDlgResizer);
+}
+
+int CDlgBase::Resizer(UTILRESIZECONTROL*)
+{
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+BOOL CALLBACK CDlgBase::GlobalFieldEnum(HWND hwnd, LPARAM lParam)
+{
+ CDlgBase *pDlg = (CDlgBase*)lParam;
+ int id = GetWindowLongPtrW(hwnd, GWLP_ID);
+ if (id <= 0)
+ return TRUE;
+
+ // already declared inside the class? skipping
+ CCtrlBase *ctrl = pDlg->FindControl(id);
+ if (ctrl != nullptr)
+ return TRUE;
+
+ wchar_t wszClass[100];
+ GetClassNameW(hwnd, wszClass, _countof(wszClass));
+ if (!wcsicmp(wszClass, L"Static"))
+ new CCtrlLabel(pDlg, id);
+ if (!wcsicmp(wszClass, L"Edit"))
+ new CCtrlEdit(pDlg, id);
+ else if (!wcsicmp(wszClass, L"ComboBox"))
+ new CCtrlCombo(pDlg, id);
+ else if (!wcsicmp(wszClass, L"Button")) {
+ switch (GetWindowLongW(hwnd, GWL_STYLE) & (BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON)) {
+ case BS_CHECKBOX:
+ case BS_AUTOCHECKBOX:
+ case BS_RADIOBUTTON:
+ case BS_AUTORADIOBUTTON:
+ new CCtrlCheck(pDlg, id);
+ break;
+
+ default:
+ new CCtrlButton(pDlg, id);
+ }
+ }
+ else if (!wcsicmp(wszClass, L"RichEdit50W"))
+ new CCtrlRichEdit(pDlg, id);
+ else if (!wcsicmp(wszClass, L"msctls_updown32"))
+ new CCtrlSpin(pDlg, id);
+
+ return TRUE;
+}
+
+INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ m_bInitialized = m_bSucceeded = false;
+ TranslateDialog_LP(m_hwnd, &m_pPlugin);
+
+ ::EnumChildWindows(m_hwnd, &GlobalFieldEnum, LPARAM(this));
+
+ NotifyControls(&CCtrlBase::OnInit);
+ if (!OnInitDialog())
+ return FALSE;
+
+ for (auto &it : m_controls)
+ if (it->m_bNotifiable)
+ it->OnChange(it);
+
+ m_bInitialized = true;
+ return TRUE;
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ if (CCtrlBase *ctrl = FindControl(HWND(lParam))) {
+ if (ctrl->m_bUseSystemColors) {
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+ }
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ if (m_iMinHeight != -1 && m_iMinWidth != -1) {
+ MINMAXINFO *lpmmi = (MINMAXINFO*)lParam;
+ lpmmi->ptMinTrackSize.y = m_iMinHeight;
+ lpmmi->ptMinTrackSize.x = m_iMinWidth;
+ return 0;
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ if (!Menu_MeasureItem(lParam)) {
+ MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnMeasureItem(param);
+ }
+ return FALSE;
+
+ case WM_DRAWITEM:
+ if (!Menu_DrawItem(lParam)) {
+ DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnDrawItem(param);
+ }
+ return FALSE;
+
+ case WM_DELETEITEM:
+ {
+ DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnDeleteItem(param);
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ {
+ HWND hwndCtrl = (HWND)lParam;
+ uint16_t idCtrl = LOWORD(wParam);
+ uint16_t idCode = HIWORD(wParam);
+ if (CCtrlBase *ctrl = FindControl(idCtrl)) {
+ BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode);
+ if (result != FALSE)
+ return result;
+ }
+
+ if (idCode == BN_CLICKED) {
+ // close dialog automatically if 'Cancel' button is pressed
+ if (idCtrl == IDCANCEL && (m_autoClose & CLOSE_ON_CANCEL)) {
+ m_bExiting = true;
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ }
+
+ // close dialog automatically if 'OK' button is pressed
+ if (idCtrl == IDOK && (m_autoClose & CLOSE_ON_OK)) {
+ // validate dialog data first
+ if (VerifyControls(&CCtrlBase::OnApply)) {
+ m_bExiting = true;
+
+ // everything ok? good, let's close it
+ if (OnApply()) {
+ m_bSucceeded = true;
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ }
+ else m_bExiting = false;
+ }
+ }
+ }
+ }
+ return FALSE;
+
+ case WM_NOTIFY:
+ {
+ int idCtrl = wParam;
+ NMHDR *pnmh = (NMHDR *)lParam;
+ if (pnmh->idFrom == 0) {
+ switch (pnmh->code) {
+ case PSN_APPLY:
+ if (LPPSHNOTIFY(lParam)->lParam != 3) // IDC_APPLY
+ m_bExiting = true;
+
+ if (!VerifyControls(&CCtrlBase::OnApply))
+ m_bExiting = false;
+ else if (!OnApply())
+ m_bExiting = false;
+ break;
+
+ case PSN_RESET:
+ NotifyControls(&CCtrlBase::OnReset);
+ OnReset();
+ break;
+
+ case PSN_WIZFINISH:
+ m_OnFinishWizard(this);
+ break;
+ }
+ }
+
+ if (CCtrlBase *ctrl = FindControl(pnmh->idFrom))
+ return ctrl->OnNotify(idCtrl, pnmh);
+ }
+ return FALSE;
+
+ case WM_HSCROLL:
+ if (auto *pCtrl = FindControl(HWND(lParam)))
+ pCtrl->OnCommand(HWND(lParam), pCtrl->m_idCtrl, WM_HSCROLL);
+ break;
+
+ case PSM_CHANGED:
+ if (m_bInitialized)
+ OnChange();
+ break;
+
+ case WM_CONTEXTMENU:
+ if (CCtrlBase *ctrl = FindControl(HWND(wParam))) {
+ CContextMenuPos pos = {};
+ if (lParam != -1) {
+ pos.pt.x = GET_X_LPARAM(lParam);
+ pos.pt.y = GET_Y_LPARAM(lParam);
+ }
+ ctrl->GetCaretPos(pos);
+ ctrl->OnBuildMenu(&pos);
+ }
+ break;
+
+ case WM_SIZE:
+ OnResize();
+ return TRUE;
+
+ case WM_TIMER:
+ if (CTimer *timer = FindTimer(wParam))
+ return timer->OnTimer();
+ return FALSE;
+
+ case WM_CLOSE:
+ if (OnClose()) {
+ m_bExiting = true;
+ if (m_isModal)
+ EndModal(m_bSucceeded ? IDOK : FALSE);
+ else
+ DestroyWindow(m_hwnd);
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ m_bExiting = true;
+ OnDestroy();
+ NotifyControls(&CCtrlBase::OnDestroy);
+ {
+ mir_cslock lck(csDialogs);
+ int idx = arDialogs.getIndex(this);
+ if (idx != -1)
+ arDialogs.remove(idx);
+ }
+ m_hwnd = nullptr;
+ if (m_bInitialized) {
+ if (m_isModal)
+ m_isModal = false;
+ else // modeless dialogs MUST be allocated with 'new'
+ delete this;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CDlgBase *wnd;
+ if (msg == WM_INITDIALOG) {
+ wnd = (CDlgBase*)lParam;
+ wnd->m_hwnd = hwnd;
+
+ mir_cslock lck(csDialogs);
+ arDialogs.insert(wnd);
+ }
+ else wnd = CDlgBase::Find(hwnd);
+
+ return (wnd == nullptr) ? FALSE : wnd->DlgProc(msg, wParam, lParam);
+}
+
+void CDlgBase::ThemeDialogBackground(BOOL tabbed)
+{
+ EnableThemeDialogTexture(m_hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE);
+}
+
+void CDlgBase::AddControl(CCtrlBase *ctrl)
+{
+ m_controls.insert(ctrl);
+}
+
+void CDlgBase::RemoveControl(CCtrlBase *ctrl)
+{
+ m_controls.remove(ctrl);
+}
+
+void CDlgBase::NotifyControls(void (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ (it->*fn)();
+}
+
+bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ if (!(it->*fn)())
+ return false;
+
+ return true;
+}
+
+CCtrlBase* CDlgBase::FindControl(int idCtrl)
+{
+ CCtrlBase search(nullptr, idCtrl);
+ return m_controls.find(&search);
+}
+
+CCtrlBase* CDlgBase::FindControl(HWND hwnd)
+{
+ for (auto &it : m_controls)
+ if (it->GetHwnd() == hwnd)
+ return it;
+
+ return nullptr;
+}
+
+void CDlgBase::AddTimer(CTimer *timer)
+{
+ m_timers.insert(timer);
+}
+
+void CDlgBase::RemoveTimer(UINT_PTR idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ m_timers.remove(&search);
+}
+
+CTimer* CDlgBase::FindTimer(int idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ return m_timers.find(&search);
+}
+
+CDlgBase* CDlgBase::Find(HWND hwnd)
+{
+ PVOID bullshit[2]; // vfptr + hwnd
+ bullshit[1] = hwnd;
+ return arDialogs.find((CDlgBase*)&bullshit);
+}
diff --git a/src/mir_core/src/Windows/CProgress.cpp b/src/mir_core/src/Windows/CProgress.cpp index 991c6f239d..e2d5321f59 100644 --- a/src/mir_core/src/Windows/CProgress.cpp +++ b/src/mir_core/src/Windows/CProgress.cpp @@ -1,53 +1,53 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlProgress - -CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{ -} - -void CCtrlProgress::SetRange(uint16_t max, uint16_t min) -{ - SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); -} - -void CCtrlProgress::SetPosition(uint16_t value) -{ - SendMsg(PBM_SETPOS, value, 0); -} - -void CCtrlProgress::SetStep(uint16_t value) -{ - SendMsg(PBM_SETSTEP, value, 0); -} - -uint16_t CCtrlProgress::Move(uint16_t delta) -{ - return delta == 0 - ? SendMsg(PBM_STEPIT, 0, 0) - : SendMsg(PBM_DELTAPOS, delta, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlProgress
+
+CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{
+}
+
+void CCtrlProgress::SetRange(uint16_t max, uint16_t min)
+{
+ SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max));
+}
+
+void CCtrlProgress::SetPosition(uint16_t value)
+{
+ SendMsg(PBM_SETPOS, value, 0);
+}
+
+void CCtrlProgress::SetStep(uint16_t value)
+{
+ SendMsg(PBM_SETSTEP, value, 0);
+}
+
+uint16_t CCtrlProgress::Move(uint16_t delta)
+{
+ return delta == 0
+ ? SendMsg(PBM_STEPIT, 0, 0)
+ : SendMsg(PBM_DELTAPOS, delta, 0);
+}
diff --git a/src/mir_core/src/Windows/CSplitter.cpp b/src/mir_core/src/Windows/CSplitter.cpp index e2ee6b6fc8..153beaf95b 100644 --- a/src/mir_core/src/Windows/CSplitter.cpp +++ b/src/mir_core/src/Windows/CSplitter.cpp @@ -1,83 +1,83 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CSplitter - -CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_iPosition(0) -{ -} - -void CSplitter::OnInit() -{ - CSuper::OnInit(); - Subclass(); -} - -LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - RECT rc; - GetClientRect(m_hwnd, &rc); - SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); - return TRUE; - - case WM_LBUTTONDOWN: - SetCapture(m_hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == m_hwnd) { - POINT pt = { 0, 0 }; - GetClientRect(m_hwnd, &rc); - if (rc.right > rc.bottom) { - pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.y; - } - else { - pt.x = LOWORD(GetMessagePos()) + rc.right / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.x; - } - - OnChange(this); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - } - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - return 0; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CSplitter
+
+CSplitter::CSplitter(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_iPosition(0)
+{
+}
+
+void CSplitter::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+}
+
+LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCHITTEST:
+ return HTCLIENT;
+
+ case WM_SETCURSOR:
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE);
+ return TRUE;
+
+ case WM_LBUTTONDOWN:
+ SetCapture(m_hwnd);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (GetCapture() == m_hwnd) {
+ POINT pt = { 0, 0 };
+ GetClientRect(m_hwnd, &rc);
+ if (rc.right > rc.bottom) {
+ pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.y;
+ }
+ else {
+ pt.x = LOWORD(GetMessagePos()) + rc.right / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.x;
+ }
+
+ OnChange(this);
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ ReleaseCapture();
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ return 0;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
diff --git a/src/mir_core/src/Windows/CTimer.cpp b/src/mir_core/src/Windows/CTimer.cpp index b346138707..4f1141f329 100644 --- a/src/mir_core/src/Windows/CTimer.cpp +++ b/src/mir_core/src/Windows/CTimer.cpp @@ -1,90 +1,90 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CTimer - -CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) - : m_wnd(wnd), m_idEvent(idEvent) -{ - if (wnd) - wnd->AddTimer(this); -} - -CTimer::~CTimer() -{ - if (m_wnd) - m_wnd->RemoveTimer(m_idEvent); -} - -BOOL CTimer::OnTimer() -{ - OnEvent(this); - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CTimer::Start(int elapse) -{ - ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); -} - -bool CTimer::Stop() -{ - return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TStartParam -{ - CTimer *pTimer; - int period; -}; - -static INT_PTR CALLBACK stubStart(void *param) -{ - auto *p = (TStartParam *)param; - return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); -} - -void CTimer::StartSafe(int elapse) -{ - TStartParam param = { this, elapse }; - CallFunctionSync(stubStart, ¶m); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK stubStop(void *param) -{ - auto *p = (CTimer*)param; - return ::KillTimer(p->GetHwnd(), p->GetEventId()); -} - -void CTimer::StopSafe() -{ - CallFunctionSync(stubStop, this); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CTimer
+
+CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent)
+ : m_wnd(wnd), m_idEvent(idEvent)
+{
+ if (wnd)
+ wnd->AddTimer(this);
+}
+
+CTimer::~CTimer()
+{
+ if (m_wnd)
+ m_wnd->RemoveTimer(m_idEvent);
+}
+
+BOOL CTimer::OnTimer()
+{
+ OnEvent(this);
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTimer::Start(int elapse)
+{
+ ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr);
+}
+
+bool CTimer::Stop()
+{
+ return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TStartParam
+{
+ CTimer *pTimer;
+ int period;
+};
+
+static INT_PTR CALLBACK stubStart(void *param)
+{
+ auto *p = (TStartParam *)param;
+ return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr);
+}
+
+void CTimer::StartSafe(int elapse)
+{
+ TStartParam param = { this, elapse };
+ CallFunctionSync(stubStart, ¶m);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK stubStop(void *param)
+{
+ auto *p = (CTimer*)param;
+ return ::KillTimer(p->GetHwnd(), p->GetEventId());
+}
+
+void CTimer::StopSafe()
+{
+ CallFunctionSync(stubStop, this);
+}
diff --git a/src/mir_core/src/Windows/cmdline.cpp b/src/mir_core/src/Windows/cmdline.cpp index a4a61b2c6e..0e43610e0c 100644 --- a/src/mir_core/src/Windows/cmdline.cpp +++ b/src/mir_core/src/Windows/cmdline.cpp @@ -1,77 +1,77 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -/* command line support */ - -struct CmdLineParam -{ - __inline CmdLineParam(const wchar_t *_name, const wchar_t *_value) : - name(mir_wstrdup(_name)), value(mir_wstrdup(_value)) - {} - - ptrW name, value; -}; - -static int CompareParams(const CmdLineParam *p1, const CmdLineParam *p2) -{ - return wcscmp(p1->name, p2->name); -} - -static OBJLIST<CmdLineParam> arParams(5, CompareParams); - -MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine) -{ - int nArgs = 0; - wchar_t **pArgs = CommandLineToArgvW(ptszCmdLine, &nArgs); - if (pArgs == nullptr) - return; - - for (int i=0; i < nArgs; i++) { - wchar_t *pOptionName = pArgs[i], *p; - - // not an option? skip it - if (*pOptionName != '/' && *pOptionName != '-') - continue; - - pOptionName++; - if ((p = wcspbrk(pOptionName, L"=:")) == nullptr) { // no more text in string - arParams.insert(new CmdLineParam(pOptionName, L"")); - break; - } - - // parameter with value - *p = 0; - arParams.insert(new CmdLineParam(pOptionName, p+1)); - } - - LocalFree(pArgs); -} - -MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t* ptszParameter) -{ - CmdLineParam tmp(ptszParameter, nullptr); - int idx = arParams.getIndex(&tmp); - return (idx == -1) ? nullptr : arParams[idx].value.get(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+/* command line support */
+
+struct CmdLineParam
+{
+ __inline CmdLineParam(const wchar_t *_name, const wchar_t *_value) :
+ name(mir_wstrdup(_name)), value(mir_wstrdup(_value))
+ {}
+
+ ptrW name, value;
+};
+
+static int CompareParams(const CmdLineParam *p1, const CmdLineParam *p2)
+{
+ return wcscmp(p1->name, p2->name);
+}
+
+static OBJLIST<CmdLineParam> arParams(5, CompareParams);
+
+MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine)
+{
+ int nArgs = 0;
+ wchar_t **pArgs = CommandLineToArgvW(ptszCmdLine, &nArgs);
+ if (pArgs == nullptr)
+ return;
+
+ for (int i=0; i < nArgs; i++) {
+ wchar_t *pOptionName = pArgs[i], *p;
+
+ // not an option? skip it
+ if (*pOptionName != '/' && *pOptionName != '-')
+ continue;
+
+ pOptionName++;
+ if ((p = wcspbrk(pOptionName, L"=:")) == nullptr) { // no more text in string
+ arParams.insert(new CmdLineParam(pOptionName, L""));
+ break;
+ }
+
+ // parameter with value
+ *p = 0;
+ arParams.insert(new CmdLineParam(pOptionName, p+1));
+ }
+
+ LocalFree(pArgs);
+}
+
+MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t* ptszParameter)
+{
+ CmdLineParam tmp(ptszParameter, nullptr);
+ int idx = arParams.getIndex(&tmp);
+ return (idx == -1) ? nullptr : arParams[idx].value.get();
+}
diff --git a/src/mir_core/src/Windows/colourpicker.cpp b/src/mir_core/src/Windows/colourpicker.cpp index 3272cc37ac..135f8c4096 100644 --- a/src/mir_core/src/Windows/colourpicker.cpp +++ b/src/mir_core/src/Windows/colourpicker.cpp @@ -1,105 +1,105 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_CREATE: - SetWindowLongPtr(hwnd, 0, 0); - SetWindowLongPtr(hwnd, sizeof(COLORREF), 0); - break; - - case CPM_SETDEFAULTCOLOUR: - SetWindowLongPtr(hwnd, sizeof(COLORREF), lParam); - break; - - case CPM_GETDEFAULTCOLOUR: - return GetWindowLongPtr(hwnd, sizeof(COLORREF)); - - case CPM_SETCOLOUR: - SetWindowLongPtr(hwnd, 0, lParam); - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case CPM_GETCOLOUR: - return GetWindowLongPtr(hwnd, 0); - - case WM_LBUTTONUP: - { - COLORREF custColours[16] = { 0 }; - custColours[0] = GetWindowLongPtr(hwnd, sizeof(COLORREF)); - - CHOOSECOLOR cc = { 0 }; - cc.lStructSize = sizeof(CHOOSECOLOR); - cc.hwndOwner = hwnd; - cc.hInstance = (HWND)g_hInst; - cc.rgbResult = GetWindowLongPtr(hwnd, 0); - cc.lpCustColors = custColours; - cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; - if (ChooseColor(&cc)) { - SetWindowLongPtr(hwnd, 0, cc.rgbResult); - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), CPN_COLOURCHANGED), (LPARAM)hwnd); - InvalidateRect(hwnd, nullptr, FALSE); - } - } - break; - - case WM_ENABLE: - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case WM_NCPAINT: - case WM_PAINT: - PAINTSTRUCT ps; - HDC hdc1 = BeginPaint(hwnd, &ps); - - RECT rc; - GetClientRect(hwnd, &rc); - DrawEdge(hdc1, &rc, EDGE_ETCHED, BF_RECT); - InflateRect(&rc, -2, -2); - - HBRUSH hBrush = (IsWindowEnabled(hwnd)) ? CreateSolidBrush(GetWindowLongPtr(hwnd, 0)) : CreateHatchBrush(HS_BDIAGONAL, GetSysColor(COLOR_GRAYTEXT)); - SetBkColor(hdc1, GetSysColor(COLOR_BTNFACE)); - FillRect(hdc1, &rc, hBrush); - DeleteObject(hBrush); - - EndPaint(hwnd, &ps); - break; - } - return DefWindowProc(hwnd, message, wParam, lParam); -} - -void InitColourPicker(void) -{ - WNDCLASS wcl = { 0 }; - wcl.lpfnWndProc = ColourPickerWndProc; - wcl.cbWndExtra = sizeof(COLORREF) * 2; - wcl.hInstance = g_hInst; - wcl.lpszClassName = _T(WNDCLASS_COLOURPICKER); - wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; - RegisterClass(&wcl); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_CREATE:
+ SetWindowLongPtr(hwnd, 0, 0);
+ SetWindowLongPtr(hwnd, sizeof(COLORREF), 0);
+ break;
+
+ case CPM_SETDEFAULTCOLOUR:
+ SetWindowLongPtr(hwnd, sizeof(COLORREF), lParam);
+ break;
+
+ case CPM_GETDEFAULTCOLOUR:
+ return GetWindowLongPtr(hwnd, sizeof(COLORREF));
+
+ case CPM_SETCOLOUR:
+ SetWindowLongPtr(hwnd, 0, lParam);
+ InvalidateRect(hwnd, nullptr, FALSE);
+ break;
+
+ case CPM_GETCOLOUR:
+ return GetWindowLongPtr(hwnd, 0);
+
+ case WM_LBUTTONUP:
+ {
+ COLORREF custColours[16] = { 0 };
+ custColours[0] = GetWindowLongPtr(hwnd, sizeof(COLORREF));
+
+ CHOOSECOLOR cc = { 0 };
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = hwnd;
+ cc.hInstance = (HWND)g_hInst;
+ cc.rgbResult = GetWindowLongPtr(hwnd, 0);
+ cc.lpCustColors = custColours;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT;
+ if (ChooseColor(&cc)) {
+ SetWindowLongPtr(hwnd, 0, cc.rgbResult);
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), CPN_COLOURCHANGED), (LPARAM)hwnd);
+ InvalidateRect(hwnd, nullptr, FALSE);
+ }
+ }
+ break;
+
+ case WM_ENABLE:
+ InvalidateRect(hwnd, nullptr, FALSE);
+ break;
+
+ case WM_NCPAINT:
+ case WM_PAINT:
+ PAINTSTRUCT ps;
+ HDC hdc1 = BeginPaint(hwnd, &ps);
+
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+ DrawEdge(hdc1, &rc, EDGE_ETCHED, BF_RECT);
+ InflateRect(&rc, -2, -2);
+
+ HBRUSH hBrush = (IsWindowEnabled(hwnd)) ? CreateSolidBrush(GetWindowLongPtr(hwnd, 0)) : CreateHatchBrush(HS_BDIAGONAL, GetSysColor(COLOR_GRAYTEXT));
+ SetBkColor(hdc1, GetSysColor(COLOR_BTNFACE));
+ FillRect(hdc1, &rc, hBrush);
+ DeleteObject(hBrush);
+
+ EndPaint(hwnd, &ps);
+ break;
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+void InitColourPicker(void)
+{
+ WNDCLASS wcl = { 0 };
+ wcl.lpfnWndProc = ColourPickerWndProc;
+ wcl.cbWndExtra = sizeof(COLORREF) * 2;
+ wcl.hInstance = g_hInst;
+ wcl.lpszClassName = _T(WNDCLASS_COLOURPICKER);
+ wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+ wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
+ RegisterClass(&wcl);
+}
diff --git a/src/mir_core/src/Windows/diatheme.cpp b/src/mir_core/src/Windows/diatheme.cpp index 2c16131643..12e37ff578 100644 --- a/src/mir_core/src/Windows/diatheme.cpp +++ b/src/mir_core/src/Windows/diatheme.cpp @@ -1,170 +1,170 @@ -/* -Copyright (C) 2012-22 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" -#include "diatheme.h" - -// The following code was borrowed from Notepad2 sources and adapted for Miranda NG - -typedef HTHEME (WINAPI *OTD)(HWND hwnd,LPCWSTR pszClassList); -typedef HRESULT (WINAPI *GTSF)(HTHEME hTheme,int iFontId,LOGFONT *plf); -typedef HRESULT (WINAPI *CTD)(HTHEME hTheme); - -BOOL GetThemedDialogFont(LPWSTR lpFaceName, WORD *wSize) -{ - BOOL bSucceed = FALSE; - - HDC hDC = GetDC(nullptr); - int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY); - ReleaseDC(nullptr, hDC); - - if (HMODULE hModUxTheme = GetModuleHandle(L"uxtheme.dll")) { - OTD _OpenThemeData = (OTD)GetProcAddress(hModUxTheme, "OpenThemeData"); - GTSF _GetThemeSysFont = (GTSF)GetProcAddress(hModUxTheme, "GetThemeSysFont"); - CTD _CloseThemeData = (CTD)GetProcAddress(hModUxTheme, "CloseThemeData"); - - if (_CloseThemeData && _GetThemeSysFont && _OpenThemeData) { - if (HTHEME hTheme = _OpenThemeData(NULL, L"WINDOWSTYLE;WINDOW")) { - LOGFONT lf; - if (S_OK == _GetThemeSysFont(hTheme,/*TMT_MSGBOXFONT*/805, &lf)) { - if (lf.lfHeight < 0) - lf.lfHeight = -lf.lfHeight; - *wSize = (WORD)MulDiv(lf.lfHeight, 72, iLogPixelsY); - if (*wSize == 0) - *wSize = 8; - wcsncpy_s(lpFaceName, LF_FACESIZE, lf.lfFaceName, _TRUNCATE); - bSucceed = TRUE; - } - _CloseThemeData(hTheme); - } - } - } - - if (!bSucceed) { - NONCLIENTMETRICS ncm; - ncm.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0); - if (ncm.lfMessageFont.lfHeight < 0) - ncm.lfMessageFont.lfHeight = -ncm.lfMessageFont.lfHeight; - *wSize = (WORD)MulDiv(ncm.lfMessageFont.lfHeight, 72, iLogPixelsY); - if (*wSize == 0) - *wSize = 8; - - wcsncpy_s(lpFaceName, LF_FACESIZE, ncm.lfMessageFont.lfFaceName, _TRUNCATE); - } - - return TRUE; -} - -BOOL DialogTemplate_IsDialogEx(const DLGTEMPLATE *pTemplate) -{ - return ((DLGTEMPLATEEX *)pTemplate)->signature == 0xFFFF; -} - -BOOL DialogTemplate_HasFont(const DLGTEMPLATE *pTemplate) -{ - return (DS_SETFONT & (DialogTemplate_IsDialogEx(pTemplate) ? ((DLGTEMPLATEEX *)pTemplate)->style : pTemplate->style)); -} - -int DialogTemplate_FontAttrSize(BOOL bDialogEx) -{ - return (int)sizeof(WORD) * (bDialogEx ? 3 : 1); -} - -BYTE *DialogTemplate_GetFontSizeField(const DLGTEMPLATE *pTemplate) -{ - BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - - WORD *pw; - if (bDialogEx) - pw = (WORD *)((DLGTEMPLATEEX *)pTemplate + 1); - else - pw = (WORD *)(pTemplate + 1); - - if (*pw == (WORD)-1) - pw += 2; - else - while (*pw++); - - if (*pw == (WORD)-1) - pw += 2; - else - while (*pw++); - - while (*pw++); - - return (BYTE *)pw; -} - -DLGTEMPLATE* LoadThemedDialogTemplate(LPCTSTR lpDialogTemplateID, HINSTANCE hInstance) -{ - WCHAR wchFaceName[LF_FACESIZE]; - - HRSRC hRsrc = FindResource(hInstance, lpDialogTemplateID, RT_DIALOG); - if (hRsrc == nullptr) - return nullptr; - - HGLOBAL hRsrcMem = LoadResource(hInstance, hRsrc); - if (hRsrcMem == nullptr) - return nullptr; - - DLGTEMPLATE *pRsrcMem = (DLGTEMPLATE *)LockResource(hRsrcMem); - if (pRsrcMem == nullptr) - return nullptr; - - size_t dwTemplateSize = (UINT)SizeofResource(hInstance, hRsrc); - UnlockResource(hRsrcMem); - FreeResource(hRsrcMem); - - if (dwTemplateSize == 0) - return nullptr; - - auto *pTemplate = (DLGTEMPLATE *)mir_alloc(dwTemplateSize + LF_FACESIZE * 2); - memcpy(pTemplate, pRsrcMem, dwTemplateSize); - UnlockResource(hRsrcMem); - FreeResource(hRsrcMem); - - WORD wFontSize; - if (!GetThemedDialogFont(wchFaceName, &wFontSize)) - return(pTemplate); - - BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - BOOL bHasFont = DialogTemplate_HasFont(pTemplate); - size_t cbFontAttr = DialogTemplate_FontAttrSize(bDialogEx); - - if (bDialogEx) - ((DLGTEMPLATEEX *)pTemplate)->style |= DS_SHELLFONT; - else - pTemplate->style |= DS_SHELLFONT; - - size_t cbNew = cbFontAttr + ((mir_wstrlen(wchFaceName) + 1) * sizeof(WCHAR)); - BYTE *pbNew = (BYTE *)wchFaceName; - - BYTE *pb = DialogTemplate_GetFontSizeField(pTemplate); - size_t cbOld = (int)(bHasFont ? cbFontAttr + 2 * (mir_wstrlen((WCHAR *)(pb + cbFontAttr)) + 1) : 0); - - BYTE *pOldControls = (BYTE *)(((DWORD_PTR)pb + cbOld + 3) & ~(DWORD_PTR)3); - BYTE *pNewControls = (BYTE *)(((DWORD_PTR)pb + cbNew + 3) & ~(DWORD_PTR)3); - - WORD nCtrl = bDialogEx ? (WORD)((DLGTEMPLATEEX *)pTemplate)->cDlgItems : (WORD)pTemplate->cdit; - if (cbNew != cbOld && nCtrl > 0) - MoveMemory(pNewControls, pOldControls, dwTemplateSize - (pOldControls - (BYTE *)pTemplate)); - - *(WORD *)pb = wFontSize; - MoveMemory(pb + cbFontAttr, pbNew, cbNew - cbFontAttr); - return pTemplate; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+#include "diatheme.h"
+
+// The following code was borrowed from Notepad2 sources and adapted for Miranda NG
+
+typedef HTHEME (WINAPI *OTD)(HWND hwnd,LPCWSTR pszClassList);
+typedef HRESULT (WINAPI *GTSF)(HTHEME hTheme,int iFontId,LOGFONT *plf);
+typedef HRESULT (WINAPI *CTD)(HTHEME hTheme);
+
+BOOL GetThemedDialogFont(LPWSTR lpFaceName, WORD *wSize)
+{
+ BOOL bSucceed = FALSE;
+
+ HDC hDC = GetDC(nullptr);
+ int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(nullptr, hDC);
+
+ if (HMODULE hModUxTheme = GetModuleHandle(L"uxtheme.dll")) {
+ OTD _OpenThemeData = (OTD)GetProcAddress(hModUxTheme, "OpenThemeData");
+ GTSF _GetThemeSysFont = (GTSF)GetProcAddress(hModUxTheme, "GetThemeSysFont");
+ CTD _CloseThemeData = (CTD)GetProcAddress(hModUxTheme, "CloseThemeData");
+
+ if (_CloseThemeData && _GetThemeSysFont && _OpenThemeData) {
+ if (HTHEME hTheme = _OpenThemeData(NULL, L"WINDOWSTYLE;WINDOW")) {
+ LOGFONT lf;
+ if (S_OK == _GetThemeSysFont(hTheme,/*TMT_MSGBOXFONT*/805, &lf)) {
+ if (lf.lfHeight < 0)
+ lf.lfHeight = -lf.lfHeight;
+ *wSize = (WORD)MulDiv(lf.lfHeight, 72, iLogPixelsY);
+ if (*wSize == 0)
+ *wSize = 8;
+ wcsncpy_s(lpFaceName, LF_FACESIZE, lf.lfFaceName, _TRUNCATE);
+ bSucceed = TRUE;
+ }
+ _CloseThemeData(hTheme);
+ }
+ }
+ }
+
+ if (!bSucceed) {
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
+ if (ncm.lfMessageFont.lfHeight < 0)
+ ncm.lfMessageFont.lfHeight = -ncm.lfMessageFont.lfHeight;
+ *wSize = (WORD)MulDiv(ncm.lfMessageFont.lfHeight, 72, iLogPixelsY);
+ if (*wSize == 0)
+ *wSize = 8;
+
+ wcsncpy_s(lpFaceName, LF_FACESIZE, ncm.lfMessageFont.lfFaceName, _TRUNCATE);
+ }
+
+ return TRUE;
+}
+
+BOOL DialogTemplate_IsDialogEx(const DLGTEMPLATE *pTemplate)
+{
+ return ((DLGTEMPLATEEX *)pTemplate)->signature == 0xFFFF;
+}
+
+BOOL DialogTemplate_HasFont(const DLGTEMPLATE *pTemplate)
+{
+ return (DS_SETFONT & (DialogTemplate_IsDialogEx(pTemplate) ? ((DLGTEMPLATEEX *)pTemplate)->style : pTemplate->style));
+}
+
+int DialogTemplate_FontAttrSize(BOOL bDialogEx)
+{
+ return (int)sizeof(WORD) * (bDialogEx ? 3 : 1);
+}
+
+BYTE *DialogTemplate_GetFontSizeField(const DLGTEMPLATE *pTemplate)
+{
+ BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate);
+
+ WORD *pw;
+ if (bDialogEx)
+ pw = (WORD *)((DLGTEMPLATEEX *)pTemplate + 1);
+ else
+ pw = (WORD *)(pTemplate + 1);
+
+ if (*pw == (WORD)-1)
+ pw += 2;
+ else
+ while (*pw++);
+
+ if (*pw == (WORD)-1)
+ pw += 2;
+ else
+ while (*pw++);
+
+ while (*pw++);
+
+ return (BYTE *)pw;
+}
+
+DLGTEMPLATE* LoadThemedDialogTemplate(LPCTSTR lpDialogTemplateID, HINSTANCE hInstance)
+{
+ WCHAR wchFaceName[LF_FACESIZE];
+
+ HRSRC hRsrc = FindResource(hInstance, lpDialogTemplateID, RT_DIALOG);
+ if (hRsrc == nullptr)
+ return nullptr;
+
+ HGLOBAL hRsrcMem = LoadResource(hInstance, hRsrc);
+ if (hRsrcMem == nullptr)
+ return nullptr;
+
+ DLGTEMPLATE *pRsrcMem = (DLGTEMPLATE *)LockResource(hRsrcMem);
+ if (pRsrcMem == nullptr)
+ return nullptr;
+
+ size_t dwTemplateSize = (UINT)SizeofResource(hInstance, hRsrc);
+ UnlockResource(hRsrcMem);
+ FreeResource(hRsrcMem);
+
+ if (dwTemplateSize == 0)
+ return nullptr;
+
+ auto *pTemplate = (DLGTEMPLATE *)mir_alloc(dwTemplateSize + LF_FACESIZE * 2);
+ memcpy(pTemplate, pRsrcMem, dwTemplateSize);
+ UnlockResource(hRsrcMem);
+ FreeResource(hRsrcMem);
+
+ WORD wFontSize;
+ if (!GetThemedDialogFont(wchFaceName, &wFontSize))
+ return(pTemplate);
+
+ BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate);
+ BOOL bHasFont = DialogTemplate_HasFont(pTemplate);
+ size_t cbFontAttr = DialogTemplate_FontAttrSize(bDialogEx);
+
+ if (bDialogEx)
+ ((DLGTEMPLATEEX *)pTemplate)->style |= DS_SHELLFONT;
+ else
+ pTemplate->style |= DS_SHELLFONT;
+
+ size_t cbNew = cbFontAttr + ((mir_wstrlen(wchFaceName) + 1) * sizeof(WCHAR));
+ BYTE *pbNew = (BYTE *)wchFaceName;
+
+ BYTE *pb = DialogTemplate_GetFontSizeField(pTemplate);
+ size_t cbOld = (int)(bHasFont ? cbFontAttr + 2 * (mir_wstrlen((WCHAR *)(pb + cbFontAttr)) + 1) : 0);
+
+ BYTE *pOldControls = (BYTE *)(((DWORD_PTR)pb + cbOld + 3) & ~(DWORD_PTR)3);
+ BYTE *pNewControls = (BYTE *)(((DWORD_PTR)pb + cbNew + 3) & ~(DWORD_PTR)3);
+
+ WORD nCtrl = bDialogEx ? (WORD)((DLGTEMPLATEEX *)pTemplate)->cDlgItems : (WORD)pTemplate->cdit;
+ if (cbNew != cbOld && nCtrl > 0)
+ MoveMemory(pNewControls, pOldControls, dwTemplateSize - (pOldControls - (BYTE *)pTemplate));
+
+ *(WORD *)pb = wFontSize;
+ MoveMemory(pb + cbFontAttr, pbNew, cbNew - cbFontAttr);
+ return pTemplate;
+}
diff --git a/src/mir_core/src/Windows/fileutil.cpp b/src/mir_core/src/Windows/fileutil.cpp index 2522cc7cbe..a0dcee68f6 100644 --- a/src/mir_core/src/Windows/fileutil.cpp +++ b/src/mir_core/src/Windows/fileutil.cpp @@ -1,78 +1,78 @@ -/* -Copyright (C) 2012-22 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" - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() -{ - if (ptr != nullptr) { - if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { - ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; - ptr = nullptr; - } - } - return *this; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) -{ - if (pwszPath != nullptr) - m_hFind = ::FindFirstFileW(pwszPath, &m_data); -} - -MFilePath::MFileIterator::~MFileIterator() -{ - if (m_hFind != INVALID_HANDLE_VALUE) - ::FindClose(m_hFind); -} - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() -{ - if (m_hFind == INVALID_HANDLE_VALUE) - return MFilePath::MFileIterator::iterator(nullptr); - - return MFilePath::MFileIterator::iterator(this); -} - -bool MFilePath::MFileIterator::isDir() const -{ - if (m_hFind == INVALID_HANDLE_VALUE) - return false; - - return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool MFilePath::isExist() const -{ - return _waccess(c_str(), 0) == 0; -} - -bool MFilePath::move(const wchar_t *pwszDest) -{ - return MoveFileW(c_str(), pwszDest) != 0; -} - -MFilePath::MFileIterator MFilePath::search() -{ - return MFileIterator(c_str()); -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++()
+{
+ if (ptr != nullptr) {
+ if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) {
+ ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE;
+ ptr = nullptr;
+ }
+ }
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath)
+{
+ if (pwszPath != nullptr)
+ m_hFind = ::FindFirstFileW(pwszPath, &m_data);
+}
+
+MFilePath::MFileIterator::~MFileIterator()
+{
+ if (m_hFind != INVALID_HANDLE_VALUE)
+ ::FindClose(m_hFind);
+}
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin()
+{
+ if (m_hFind == INVALID_HANDLE_VALUE)
+ return MFilePath::MFileIterator::iterator(nullptr);
+
+ return MFilePath::MFileIterator::iterator(this);
+}
+
+bool MFilePath::MFileIterator::isDir() const
+{
+ if (m_hFind == INVALID_HANDLE_VALUE)
+ return false;
+
+ return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool MFilePath::isExist() const
+{
+ return _waccess(c_str(), 0) == 0;
+}
+
+bool MFilePath::move(const wchar_t *pwszDest)
+{
+ return MoveFileW(c_str(), pwszDest) != 0;
+}
+
+MFilePath::MFileIterator MFilePath::search()
+{
+ return MFileIterator(c_str());
+}
diff --git a/src/mir_core/src/Windows/hyperlink.cpp b/src/mir_core/src/Windows/hyperlink.cpp index 2f70d23103..e83f8ff569 100644 --- a/src/mir_core/src/Windows/hyperlink.cpp +++ b/src/mir_core/src/Windows/hyperlink.cpp @@ -1,277 +1,277 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -struct HyperlinkWndData -{ - HFONT hEnableFont, hDisableFont; - RECT rcText; - COLORREF enableColor, disableColor, focusColor; - uint8_t flags; /* see HLKF_* */ -}; - -/* flags */ -#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */ -#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */ - -/* internal messages */ -#define HLK_MEASURETEXT (WM_USER+1) -#define HLK_INVALIDATE (WM_USER+2) - -static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - HyperlinkWndData *dat = (HyperlinkWndData*)GetWindowLongPtr(hwnd, 0); - - HDC hdc; - RECT rc; - POINT pt; - HFONT hFont; - LOGFONT lf; - HCURSOR hCursor; - COLORREF prevColor; - - switch (msg) { - case WM_NCCREATE: - dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData)); - if (dat == nullptr) - return FALSE; /* fail creation */ - SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */ - /* fall thru */ - - case WM_SYSCOLORCHANGE: - if (!(dat->flags&HLKF_HASENABLECOLOR)) { - if (GetSysColorBrush(COLOR_HOTLIGHT) == nullptr) dat->enableColor = RGB(0, 0, 255); - else dat->enableColor = GetSysColor(COLOR_HOTLIGHT); - dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); - } - if (!(dat->flags&HLKF_HASDISABLECOLOR)) - dat->disableColor = GetSysColor(COLOR_GRAYTEXT); - break; - - case WM_SETFOCUS: - case WM_KILLFOCUS: - RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE); - break; - - case WM_MOUSEACTIVATE: - SetFocus(hwnd); - return MA_ACTIVATE; - - case WM_GETDLGCODE: - if (lParam) { - MSG *pMsg = (MSG *)lParam; - if (pMsg->message == WM_KEYDOWN) { - if (pMsg->wParam == VK_TAB) - return 0; - if (pMsg->wParam == VK_ESCAPE) - return 0; - } - else if (pMsg->message == WM_CHAR) { - if (pMsg->wParam == '\t') - return 0; - if (pMsg->wParam == 27) - return 0; - } - } - return DLGC_WANTMESSAGE; - - case WM_KEYDOWN: - switch (wParam) { - case VK_SPACE: - case VK_RETURN: - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); - break; - } - return 0; - - case WM_LBUTTONDOWN: - POINTSTOPOINT(pt, MAKEPOINTS(lParam)); - if (!PtInRect(&dat->rcText, pt)) break; - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); - return 0; - - case WM_SETFONT: - if ((HFONT)wParam == nullptr) { /* use default system color */ - dat->hEnableFont = dat->hDisableFont = nullptr; - return 0; - } - if (GetObject((HFONT)wParam, sizeof(lf), &lf)) { - lf.lfUnderline = 1; - hFont = CreateFontIndirect(&lf); - if (hFont != nullptr) { - dat->hEnableFont = hFont; - dat->hDisableFont = (HFONT)wParam; - if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0); - SendMessage(hwnd, HLK_MEASURETEXT, 0, 0); - } - } - return 0; - - case WM_ERASEBKGND: - return TRUE; - - case WM_ENABLE: - case HLK_INVALIDATE: - if (GetWindowRect(hwnd, &rc)) { - pt.x = rc.left; - pt.y = rc.top; - - HWND hwndParent = GetParent(hwnd); - if (hwndParent == nullptr) - hwndParent = hwnd; - if (!ScreenToClient(hwndParent, &pt)) - break; - - rc.right = pt.x + (rc.right - rc.left); - rc.bottom = pt.y + (rc.bottom - rc.top); - rc.left = pt.x; - rc.top = pt.y; - InvalidateRect(hwndParent, &rc, TRUE); - } - return 0; - - case WM_GETFONT: - return (LRESULT)dat->hDisableFont; - - case WM_CREATE: - case HLK_MEASURETEXT: - wchar_t szText[256]; - if (!GetWindowText(hwnd, szText, _countof(szText))) return 0; - lParam = (LPARAM)szText; - /* fall thru */ - - case WM_SETTEXT: - hdc = GetDC(hwnd); - if (hdc == nullptr) /* text change failed */ - return 0; - else { - BOOL fMeasured = FALSE; - HFONT hPrevFont = nullptr; - if (dat->hEnableFont != nullptr) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); - if (dat->hEnableFont == nullptr || hPrevFont != nullptr) { /* select failed? */ - SIZE textSize; - if (GetTextExtentPoint32(hdc, (wchar_t*)lParam, (int)mir_wstrlen((wchar_t*)lParam), &textSize)) { - if (GetClientRect(hwnd, &rc)) { - dat->rcText.top = 0; - dat->rcText.bottom = dat->rcText.top + textSize.cy; - LONG style = GetWindowLongPtr(hwnd, GWL_STYLE); - if (style & SS_CENTER) dat->rcText.left = (rc.right - textSize.cx) / 2; - else if (style & SS_RIGHT) dat->rcText.left = rc.right - textSize.cx; - else dat->rcText.left = 0; - dat->rcText.right = dat->rcText.left + textSize.cx; - fMeasured = TRUE; - } - } - } - if (dat->hEnableFont != nullptr && hPrevFont != nullptr) - SelectObject(hdc, hPrevFont); - ReleaseDC(hwnd, hdc); - if (!fMeasured) /* text change failed */ - return 0; - - SendMessage(hwnd, HLK_INVALIDATE, 0, 0); - } - break; - - case WM_SETCURSOR: - if (!GetCursorPos(&pt)) return FALSE; - if (!ScreenToClient(hwnd, &pt)) return FALSE; - if (PtInRect(&dat->rcText, pt)) { - hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR); - if (hCursor == nullptr) - hCursor = LoadCursor(nullptr, IDC_HAND); /* Win2000+ */ - } - else hCursor = LoadCursor(nullptr, IDC_ARROW); - SetCursor(hCursor); - return TRUE; - - case HLK_SETENABLECOLOUR: - prevColor = dat->enableColor; - dat->enableColor = (COLORREF)wParam; - dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); - dat->flags |= HLKF_HASENABLECOLOR; - return (LRESULT)prevColor; - - case HLK_SETDISABLECOLOUR: - prevColor = dat->disableColor; - dat->disableColor = (COLORREF)wParam; - dat->flags |= HLKF_HASDISABLECOLOR; - return (LRESULT)prevColor; - - case WM_NCPAINT: - return 0; - - case WM_PAINT: - PAINTSTRUCT ps; - hdc = BeginPaint(hwnd, &ps); - if (hdc != nullptr) { - HFONT hPrevFont; - COLORREF textColor; - if (IsWindowEnabled(hwnd)) { - hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); - textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor; - } - else { - hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont); - textColor = dat->disableColor; - } - if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, _countof(szText))) { - BOOL fSmoothing; - UINT fSmoothingType; - SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0); - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0); - if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE) - DrawThemeParentBackground(hwnd, hdc, &rc); - SetBkMode(hdc, TRANSPARENT); - SetTextColor(hdc, textColor); - UINT alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE) & (SS_CENTER | SS_RIGHT | SS_LEFT)); - DrawText(hdc, szText, -1, &rc, alignFlag | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); - } - if (hPrevFont != nullptr) SelectObject(hdc, hPrevFont); - EndPaint(hwnd, &ps); - } - return 0; - - case WM_NCDESTROY: - if (dat->hEnableFont != nullptr) DeleteObject(dat->hEnableFont); - mir_free(dat); - break; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -void InitHyperlink(void) -{ - g_hCursorNS = LoadCursor(nullptr, IDC_SIZENS); - g_hCursorWE = LoadCursor(nullptr, IDC_SIZEWE); - - WNDCLASS wcl = { 0 }; - wcl.lpfnWndProc = HyperlinkWndProc; - wcl.cbWndExtra = sizeof(struct HyperlinkWndData*); - wcl.hInstance = g_hInst; - wcl.lpszClassName = WNDCLASS_HYPERLINK; - wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC; - RegisterClass(&wcl); /* automatically unregistered on exit */ -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+struct HyperlinkWndData
+{
+ HFONT hEnableFont, hDisableFont;
+ RECT rcText;
+ COLORREF enableColor, disableColor, focusColor;
+ uint8_t flags; /* see HLKF_* */
+};
+
+/* flags */
+#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */
+#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */
+
+/* internal messages */
+#define HLK_MEASURETEXT (WM_USER+1)
+#define HLK_INVALIDATE (WM_USER+2)
+
+static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HyperlinkWndData *dat = (HyperlinkWndData*)GetWindowLongPtr(hwnd, 0);
+
+ HDC hdc;
+ RECT rc;
+ POINT pt;
+ HFONT hFont;
+ LOGFONT lf;
+ HCURSOR hCursor;
+ COLORREF prevColor;
+
+ switch (msg) {
+ case WM_NCCREATE:
+ dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData));
+ if (dat == nullptr)
+ return FALSE; /* fail creation */
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */
+ /* fall thru */
+
+ case WM_SYSCOLORCHANGE:
+ if (!(dat->flags&HLKF_HASENABLECOLOR)) {
+ if (GetSysColorBrush(COLOR_HOTLIGHT) == nullptr) dat->enableColor = RGB(0, 0, 255);
+ else dat->enableColor = GetSysColor(COLOR_HOTLIGHT);
+ dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
+ }
+ if (!(dat->flags&HLKF_HASDISABLECOLOR))
+ dat->disableColor = GetSysColor(COLOR_GRAYTEXT);
+ break;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE);
+ break;
+
+ case WM_MOUSEACTIVATE:
+ SetFocus(hwnd);
+ return MA_ACTIVATE;
+
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *pMsg = (MSG *)lParam;
+ if (pMsg->message == WM_KEYDOWN) {
+ if (pMsg->wParam == VK_TAB)
+ return 0;
+ if (pMsg->wParam == VK_ESCAPE)
+ return 0;
+ }
+ else if (pMsg->message == WM_CHAR) {
+ if (pMsg->wParam == '\t')
+ return 0;
+ if (pMsg->wParam == 27)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_SPACE:
+ case VK_RETURN:
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd);
+ break;
+ }
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ if (!PtInRect(&dat->rcText, pt)) break;
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd);
+ return 0;
+
+ case WM_SETFONT:
+ if ((HFONT)wParam == nullptr) { /* use default system color */
+ dat->hEnableFont = dat->hDisableFont = nullptr;
+ return 0;
+ }
+ if (GetObject((HFONT)wParam, sizeof(lf), &lf)) {
+ lf.lfUnderline = 1;
+ hFont = CreateFontIndirect(&lf);
+ if (hFont != nullptr) {
+ dat->hEnableFont = hFont;
+ dat->hDisableFont = (HFONT)wParam;
+ if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0);
+ SendMessage(hwnd, HLK_MEASURETEXT, 0, 0);
+ }
+ }
+ return 0;
+
+ case WM_ERASEBKGND:
+ return TRUE;
+
+ case WM_ENABLE:
+ case HLK_INVALIDATE:
+ if (GetWindowRect(hwnd, &rc)) {
+ pt.x = rc.left;
+ pt.y = rc.top;
+
+ HWND hwndParent = GetParent(hwnd);
+ if (hwndParent == nullptr)
+ hwndParent = hwnd;
+ if (!ScreenToClient(hwndParent, &pt))
+ break;
+
+ rc.right = pt.x + (rc.right - rc.left);
+ rc.bottom = pt.y + (rc.bottom - rc.top);
+ rc.left = pt.x;
+ rc.top = pt.y;
+ InvalidateRect(hwndParent, &rc, TRUE);
+ }
+ return 0;
+
+ case WM_GETFONT:
+ return (LRESULT)dat->hDisableFont;
+
+ case WM_CREATE:
+ case HLK_MEASURETEXT:
+ wchar_t szText[256];
+ if (!GetWindowText(hwnd, szText, _countof(szText))) return 0;
+ lParam = (LPARAM)szText;
+ /* fall thru */
+
+ case WM_SETTEXT:
+ hdc = GetDC(hwnd);
+ if (hdc == nullptr) /* text change failed */
+ return 0;
+ else {
+ BOOL fMeasured = FALSE;
+ HFONT hPrevFont = nullptr;
+ if (dat->hEnableFont != nullptr) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont);
+ if (dat->hEnableFont == nullptr || hPrevFont != nullptr) { /* select failed? */
+ SIZE textSize;
+ if (GetTextExtentPoint32(hdc, (wchar_t*)lParam, (int)mir_wstrlen((wchar_t*)lParam), &textSize)) {
+ if (GetClientRect(hwnd, &rc)) {
+ dat->rcText.top = 0;
+ dat->rcText.bottom = dat->rcText.top + textSize.cy;
+ LONG style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ if (style & SS_CENTER) dat->rcText.left = (rc.right - textSize.cx) / 2;
+ else if (style & SS_RIGHT) dat->rcText.left = rc.right - textSize.cx;
+ else dat->rcText.left = 0;
+ dat->rcText.right = dat->rcText.left + textSize.cx;
+ fMeasured = TRUE;
+ }
+ }
+ }
+ if (dat->hEnableFont != nullptr && hPrevFont != nullptr)
+ SelectObject(hdc, hPrevFont);
+ ReleaseDC(hwnd, hdc);
+ if (!fMeasured) /* text change failed */
+ return 0;
+
+ SendMessage(hwnd, HLK_INVALIDATE, 0, 0);
+ }
+ break;
+
+ case WM_SETCURSOR:
+ if (!GetCursorPos(&pt)) return FALSE;
+ if (!ScreenToClient(hwnd, &pt)) return FALSE;
+ if (PtInRect(&dat->rcText, pt)) {
+ hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR);
+ if (hCursor == nullptr)
+ hCursor = LoadCursor(nullptr, IDC_HAND); /* Win2000+ */
+ }
+ else hCursor = LoadCursor(nullptr, IDC_ARROW);
+ SetCursor(hCursor);
+ return TRUE;
+
+ case HLK_SETENABLECOLOUR:
+ prevColor = dat->enableColor;
+ dat->enableColor = (COLORREF)wParam;
+ dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
+ dat->flags |= HLKF_HASENABLECOLOR;
+ return (LRESULT)prevColor;
+
+ case HLK_SETDISABLECOLOUR:
+ prevColor = dat->disableColor;
+ dat->disableColor = (COLORREF)wParam;
+ dat->flags |= HLKF_HASDISABLECOLOR;
+ return (LRESULT)prevColor;
+
+ case WM_NCPAINT:
+ return 0;
+
+ case WM_PAINT:
+ PAINTSTRUCT ps;
+ hdc = BeginPaint(hwnd, &ps);
+ if (hdc != nullptr) {
+ HFONT hPrevFont;
+ COLORREF textColor;
+ if (IsWindowEnabled(hwnd)) {
+ hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont);
+ textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor;
+ }
+ else {
+ hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont);
+ textColor = dat->disableColor;
+ }
+ if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, _countof(szText))) {
+ BOOL fSmoothing;
+ UINT fSmoothingType;
+ SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0);
+ SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0);
+ if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE)
+ DrawThemeParentBackground(hwnd, hdc, &rc);
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, textColor);
+ UINT alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE) & (SS_CENTER | SS_RIGHT | SS_LEFT));
+ DrawText(hdc, szText, -1, &rc, alignFlag | DT_NOPREFIX | DT_SINGLELINE | DT_TOP);
+ }
+ if (hPrevFont != nullptr) SelectObject(hdc, hPrevFont);
+ EndPaint(hwnd, &ps);
+ }
+ return 0;
+
+ case WM_NCDESTROY:
+ if (dat->hEnableFont != nullptr) DeleteObject(dat->hEnableFont);
+ mir_free(dat);
+ break;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+void InitHyperlink(void)
+{
+ g_hCursorNS = LoadCursor(nullptr, IDC_SIZENS);
+ g_hCursorWE = LoadCursor(nullptr, IDC_SIZEWE);
+
+ WNDCLASS wcl = { 0 };
+ wcl.lpfnWndProc = HyperlinkWndProc;
+ wcl.cbWndExtra = sizeof(struct HyperlinkWndData*);
+ wcl.hInstance = g_hInst;
+ wcl.lpszClassName = WNDCLASS_HYPERLINK;
+ wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC;
+ RegisterClass(&wcl); /* automatically unregistered on exit */
+}
diff --git a/src/mir_core/src/Windows/icons.cpp b/src/mir_core/src/Windows/icons.cpp index a4555211de..122ecad5e5 100644 --- a/src/mir_core/src/Windows/icons.cpp +++ b/src/mir_core/src/Windows/icons.cpp @@ -1,74 +1,74 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) -{ - wchar_t szFile[MAX_PATH]; - GetModuleFileName(hInst, szFile, MAX_PATH); - - SKINICONDESC sid = {}; - sid.defaultFile.w = szFile; - sid.section.a = (char*)szSection; - sid.flags = SIDF_PATH_UNICODE; - - for (unsigned i = 0; i < iCount; i++) { - char szSetting[100]; - if (prefix) { - mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); - sid.pszName = szSetting; - } - else sid.pszName = pIcons[i].szName; - - sid.cx = sid.cy = pIcons[i].size; - sid.description.a = pIcons[i].szDescr; - sid.iDefaultIndex = -pIcons[i].defIconID; - pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); - } -} - -MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) -{ - wchar_t szFile[MAX_PATH]; - GetModuleFileName(hInst, szFile, MAX_PATH); - - SKINICONDESC sid = {}; - sid.defaultFile.w = szFile; - sid.section.w = (wchar_t*)szSection; - sid.flags = SIDF_ALL_UNICODE; - - for (unsigned i = 0; i < iCount; i++) { - char szSetting[100]; - if (prefix) { - mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); - sid.pszName = szSetting; - } - else sid.pszName = pIcons[i].szName; - - sid.cx = sid.cy = pIcons[i].size; - sid.description.w = pIcons[i].tszDescr; - sid.iDefaultIndex = -pIcons[i].defIconID; - pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); - } -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin)
+{
+ wchar_t szFile[MAX_PATH];
+ GetModuleFileName(hInst, szFile, MAX_PATH);
+
+ SKINICONDESC sid = {};
+ sid.defaultFile.w = szFile;
+ sid.section.a = (char*)szSection;
+ sid.flags = SIDF_PATH_UNICODE;
+
+ for (unsigned i = 0; i < iCount; i++) {
+ char szSetting[100];
+ if (prefix) {
+ mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName);
+ sid.pszName = szSetting;
+ }
+ else sid.pszName = pIcons[i].szName;
+
+ sid.cx = sid.cy = pIcons[i].size;
+ sid.description.a = pIcons[i].szDescr;
+ sid.iDefaultIndex = -pIcons[i].defIconID;
+ pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin);
+ }
+}
+
+MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin)
+{
+ wchar_t szFile[MAX_PATH];
+ GetModuleFileName(hInst, szFile, MAX_PATH);
+
+ SKINICONDESC sid = {};
+ sid.defaultFile.w = szFile;
+ sid.section.w = (wchar_t*)szSection;
+ sid.flags = SIDF_ALL_UNICODE;
+
+ for (unsigned i = 0; i < iCount; i++) {
+ char szSetting[100];
+ if (prefix) {
+ mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName);
+ sid.pszName = szSetting;
+ }
+ else sid.pszName = pIcons[i].szName;
+
+ sid.cx = sid.cy = pIcons[i].size;
+ sid.description.w = pIcons[i].tszDescr;
+ sid.iDefaultIndex = -pIcons[i].defIconID;
+ pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin);
+ }
+}
diff --git a/src/mir_core/src/Windows/langpack.cpp b/src/mir_core/src/Windows/langpack.cpp index 00b7d996e1..010b1311ca 100644 --- a/src/mir_core/src/Windows/langpack.cpp +++ b/src/mir_core/src/Windows/langpack.cpp @@ -1,765 +1,765 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -#include "../../../mir_app/src/langpack.h" - -#define LANGPACK_BUF_SIZE 4000 - -static int CompareMuuids(const MUUID *p1, const MUUID *p2) -{ - return memcmp(p1, p2, sizeof(MUUID)); -} - -static LIST<MUUID> lMuuids(10, CompareMuuids); -static MUUID *pCurrentMuuid = nullptr; -static HANDLE hevChanged = nullptr; - -static BOOL bModuleInitialized = FALSE; - -struct LangPackEntry -{ - uint32_t englishHash; - char *szLocal; - char *utfLocal; - wchar_t *wszLocal; - MUUID *pMuuid; - LangPackEntry* pNext; // for langpack items with the same hash value -}; - -static LANGPACK_INFO langPack; -static wchar_t g_tszRoot[MAX_PATH]; - -static LangPackEntry *g_pEntries; -static int g_entryCount, g_entriesAlloced; - -static int IsEmpty(const char *str) -{ - for (int i = 0; str[i]; i++) - if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') - return 0; - - return 1; -} - -static int ConvertBackslashes(char *str, UINT fileCp) -{ - int shift = 0; - char *pstr; - for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0)) { - if (*pstr == '\\') { - shift++; - switch (pstr[1]) { - case 'n': *pstr = '\n'; break; - case 't': *pstr = '\t'; break; - case 'r': *pstr = '\r'; break; - case 's': *pstr = ' '; break; - default: *pstr = pstr[1]; break; - } - memmove(pstr + 1, pstr + 2, strlen(pstr + 2) + 1); - } - } - return shift; -} - -#ifdef _DEBUG -//#pragma optimize("gt", on) -#endif - -// MurmurHash2 -MIR_CORE_DLL(unsigned int) mir_hash(const void * key, unsigned int len) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - const unsigned int m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - unsigned int h = len; - - // Mix 4 bytes at a time into the hash - const unsigned char *data = (const unsigned char*)key; - - while (len >= 4) { - unsigned int k = *(unsigned int*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - switch (len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - } - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -static unsigned int __fastcall hashstrW(const char *key) -{ - if (key == nullptr) return 0; - const unsigned int len = (unsigned int)wcslen((const wchar_t*)key); - char *buf = (char*)alloca(len + 1); - for (unsigned i = 0; i <= len; ++i) - buf[i] = key[i << 1]; - return mir_hash(buf, len); -} - -static const MUUID* GetMuid(HPLUGIN pPlugin) -{ - if (!pPlugin) - return nullptr; - - __try { - return &pPlugin->getInfo().uuid; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return nullptr; - } -} - -static int SortLangPackHashesProc(LangPackEntry *arg1, LangPackEntry *arg2) -{ - if (arg1->englishHash < arg2->englishHash) return -1; - if (arg1->englishHash > arg2->englishHash) return 1; - - return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1; -} - -static void swapBytes(void *p, size_t iSize) -{ - char *head = (char*)p; // here - char *tail = head + iSize - 1; - - for (; tail > head; --tail, ++head) { - char temp = *head; - *head = *tail; - *tail = temp; - } -} - -static bool EnterMuuid(const char *p, MUUID &result) -{ - if (*p++ != '{') - return false; - - uint8_t *d = (uint8_t*)&result; - - for (int nBytes = 0; *p && nBytes < 24; p++) { - if (*p == '-') - continue; - - if (*p == '}') - break; - - if (!isxdigit(*p)) - return false; - - if (!isxdigit(p[1])) - return false; - - int c = 0; - if (sscanf(p, "%2x", &c) != 1) - return false; - - *d++ = (uint8_t)c; - nBytes++; - p++; - } - - if (*p != '}') - return false; - - swapBytes(&result.a, sizeof(result.a)); - swapBytes(&result.b, sizeof(result.b)); - swapBytes(&result.c, sizeof(result.c)); - return true; -} - -static void LoadLangPackFile(FILE *fp, char *line) -{ - while (!feof(fp)) { - if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) - break; - - if (IsEmpty(line) || line[0] == ';' || line[0] == 0) - continue; - - rtrim(line); - - if (line[0] == '#') { - strlwr(line); - - if (!memcmp(line + 1, "include", 7)) { - wchar_t tszFileName[MAX_PATH]; - wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); - if (p) - *p = 0; - mir_snwprintf(tszFileName, L"%s\\%S", langPack.tszFullPath, ltrim(line + 9)); - if (p) - *p = '\\'; - - FILE *fpNew = _wfopen(tszFileName, L"r"); - if (fpNew) { - line[0] = 0; - fgets(line, LANGPACK_BUF_SIZE, fpNew); - - if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') - fseek(fpNew, 3, SEEK_SET); - else - fseek(fpNew, 0, SEEK_SET); - - LoadLangPackFile(fpNew, line); - fclose(fpNew); - } - } - else if (!memcmp(line + 1, "muuid", 5)) { - MUUID t; - if (!EnterMuuid(line + 7, t)) - continue; - - MUUID *pNew = (MUUID*)mir_alloc(sizeof(MUUID)); - memcpy(pNew, &t, sizeof(t)); - lMuuids.insert(pNew); - pCurrentMuuid = pNew; - } - - continue; - } - - char cFirst = line[0]; - - ConvertBackslashes(line, CP_UTF8); - - size_t cbLen = strlen(line) - 1; - if (cFirst == '[' && line[cbLen] == ']') { - if (g_entryCount && g_pEntries[g_entryCount-1].wszLocal == nullptr) - g_entryCount--; - - char *pszLine = line + 1; - line[cbLen] = '\0'; - if (++g_entryCount > g_entriesAlloced) { - g_entriesAlloced += 128; - g_pEntries = (LangPackEntry*)mir_realloc(g_pEntries, sizeof(LangPackEntry)*g_entriesAlloced); - } - - LangPackEntry *E = &g_pEntries[g_entryCount - 1]; - E->englishHash = mir_hashstr(pszLine); - E->szLocal = E->utfLocal = nullptr; - E->wszLocal = nullptr; - E->pMuuid = pCurrentMuuid; - E->pNext = nullptr; - continue; - } - - if (!g_entryCount) - continue; - - LangPackEntry *E = &g_pEntries[g_entryCount - 1]; - int iNeeded = MultiByteToWideChar(CP_UTF8, 0, line, -1, nullptr, 0), iOldLen; - if (E->wszLocal == nullptr) { - iOldLen = 0; - E->wszLocal = (wchar_t *)mir_alloc((iNeeded + 1) * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal, iNeeded); - } - else { - iOldLen = (int)wcslen(E->wszLocal); - E->wszLocal = (wchar_t*)mir_realloc(E->wszLocal, (sizeof(wchar_t)* (iOldLen + iNeeded + 2))); - E->wszLocal[iOldLen++] = '\n'; - } - MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal + iOldLen, iNeeded); - } -} - -static int LoadLangDescr(LANGPACK_INFO &lpinfo, FILE *fp, char *line, int &startOfLine) -{ - char szLanguage[64]; szLanguage[0] = 0; - CMStringA szAuthors; - - lpinfo.codepage = CP_ACP; - lpinfo.flags = 0; - lpinfo.tszLanguage[0] = 0; - - fgets(line, LANGPACK_BUF_SIZE, fp); - size_t lineLen = strlen(line); - if (lineLen >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') - memmove(line, line + 3, lineLen - 2); - - lrtrim(line); - if (mir_strcmp(line, "Miranda Language Pack Version 1")) - return 2; - - // headers - while (!feof(fp)) { - startOfLine = ftell(fp); - if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) - break; - - lrtrim(line); - if (IsEmpty(line) || line[0] == ';' || line[0] == 0) - continue; - - if (line[0] == '[' || line[0] == '#') - break; - - char *pszColon = strchr(line, ':'); - if (pszColon == nullptr) - return 3; - - *pszColon++ = 0; - if (!mir_strcmp(line, "Language")) { - strncpy_s(szLanguage, pszColon, _TRUNCATE); - lrtrim(szLanguage); - } - else if (!mir_strcmp(line, "Last-Modified-Using")) { - lpinfo.szLastModifiedUsing = pszColon; - lpinfo.szLastModifiedUsing.Trim(); - } - else if (!mir_strcmp(line, "Authors")) { - if (!szAuthors.IsEmpty()) - szAuthors.AppendChar(' '); - szAuthors.Append(lrtrim(pszColon)); - } - else if (!mir_strcmp(line, "Locale")) { - char szBuf[20], *stopped; - - lrtrim(pszColon + 1); - USHORT langID = (USHORT)strtol(pszColon, &stopped, 16); - lpinfo.Locale = MAKELCID(langID, 0); - GetLocaleInfoA(lpinfo.Locale, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10); - szBuf[5] = 0; // codepages have max. 5 digits - lpinfo.codepage = atoi(szBuf); - } - } - - lpinfo.szAuthors = szAuthors; - - ptrW buf(mir_utf8decodeW(szLanguage)); - if (buf) - wcsncpy_s(lpinfo.tszLanguage, buf, _TRUNCATE); - else if (lpinfo.Locale != 0) - GetLocaleInfo(lpinfo.Locale, LOCALE_SENGLANGUAGE, lpinfo.tszLanguage, _countof(lpinfo.tszLanguage)); - - if (!lpinfo.tszLanguage[0]) { - wchar_t *p = wcschr(lpinfo.tszFileName, '_'); - wcsncpy_s(lpinfo.tszLanguage, ((p != nullptr) ? (p + 1) : lpinfo.tszFileName), _TRUNCATE); - p = wcsrchr(lpinfo.tszLanguage, '.'); - if (p != nullptr) *p = '\0'; - } - return 0; -} - -MIR_CORE_DLL(int) LoadLangPack(const wchar_t *ptszLangPack) -{ - if (ptszLangPack == nullptr || !mir_wstrcmpi(ptszLangPack, L"")) - return 1; - - // ensure that a lang's name is a full file name - wchar_t tszFullPath[MAX_PATH]; - if (!PathIsAbsoluteW(ptszLangPack)) - mir_snwprintf(tszFullPath, L"%s\\%s", g_tszRoot, ptszLangPack); - else - wcsncpy_s(tszFullPath, ptszLangPack, _TRUNCATE); - - // this lang is already loaded? nothing to do then - if (!mir_wstrcmp(tszFullPath, langPack.tszFullPath)) - return 0; - - // ok... loading a new langpack. remove the old one if needed - if (g_entryCount) - UnloadLangPackModule(); - - langPack.Locale = 0; - langPack.codepage = CP_ACP; - langPack.flags = 0; - - // exists & not a directory? - uint32_t dwAttrib = GetFileAttributes(tszFullPath); - if (dwAttrib == INVALID_FILE_ATTRIBUTES || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) - return 3; - - // copy the full file name and extract a file name from it - wcsncpy_s(langPack.tszFullPath, tszFullPath, _TRUNCATE); - wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); - wcsncpy_s(langPack.tszFileName, (p == nullptr) ? tszFullPath : p + 1, _TRUNCATE); - CharLower(langPack.tszFileName); - - FILE *fp = _wfopen(tszFullPath, L"rt"); - if (fp == nullptr) - return 1; - - char line[LANGPACK_BUF_SIZE] = ""; - int startOfLine = 0; - if (LoadLangDescr(langPack, fp, line, startOfLine)) { - fclose(fp); - return 1; - } - - // body - fseek(fp, startOfLine, SEEK_SET); - - LoadLangPackFile(fp, line); - fclose(fp); - pCurrentMuuid = nullptr; - - qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); - return 0; -} - -MIR_CORE_DLL(int) LoadLangPackDescr(const wchar_t *ptszLangPack, LANGPACK_INFO *lpInfo) -{ - if (lpInfo == nullptr) - return 1; - - wcsncpy_s(lpInfo->tszFullPath, ptszLangPack, _TRUNCATE); - wchar_t *p = wcsrchr(lpInfo->tszFullPath, '\\'); - wcsncpy_s(lpInfo->tszFileName, (p == nullptr) ? ptszLangPack : p+1, _TRUNCATE); - CharLower(lpInfo->tszFileName); - - FILE *fp = _wfopen(ptszLangPack, L"rt"); - if (fp == nullptr) - return 1; - - char line[LANGPACK_BUF_SIZE] = ""; - int startOfLine = 0; - int res = LoadLangDescr(*lpInfo, fp, line, startOfLine); - fclose(fp); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int SortLangPackHashesProc2(LangPackEntry *arg1, LangPackEntry *arg2) -{ - if (arg1->englishHash < arg2->englishHash) return -1; - if (arg1->englishHash > arg2->englishHash) return 1; - return 0; -} - -char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W) -{ - if (g_entryCount == 0 || szEnglish == nullptr) - return (char*)szEnglish; - - LangPackEntry key, *entry; - key.englishHash = (W == 1) ? hashstrW(szEnglish) : mir_hashstr(szEnglish); - entry = (LangPackEntry*)bsearch(&key, g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc2); - if (entry == nullptr) - return (char*)szEnglish; - - // try to find the exact match, otherwise the first entry will be returned - if (pUuid) { - for (LangPackEntry *p = entry->pNext; p != nullptr; p = p->pNext) { - if (p->pMuuid && *p->pMuuid == *pUuid) { - entry = p; - break; - } - } - } - - switch (W) { - case 0: - if (entry->szLocal == nullptr && entry->wszLocal != nullptr) - entry->szLocal = mir_u2a_cp(entry->wszLocal, langPack.codepage); - return entry->szLocal; - - case 1: - return (char*)entry->wszLocal; - - case 2: - if (entry->utfLocal == nullptr && entry->wszLocal != nullptr) - entry->utfLocal = mir_utf8encodeW(entry->wszLocal); - return entry->utfLocal; - } - - return nullptr; -} - -MIR_CORE_DLL(int) Langpack_GetDefaultCodePage() -{ - return langPack.codepage; -} - -MIR_CORE_DLL(int) Langpack_GetDefaultLocale() -{ - return (langPack.Locale == 0) ? LOCALE_USER_DEFAULT : langPack.Locale; -} - -MIR_CORE_DLL(wchar_t*) Langpack_PcharToTchar(const char *pszStr) -{ - if (pszStr == nullptr) - return nullptr; - - int len = (int)strlen(pszStr); - wchar_t *result = (wchar_t*)alloca((len + 1)*sizeof(wchar_t)); - MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pszStr, -1, result, len); - result[len] = 0; - return mir_wstrdup(TranslateW_LP(result)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(char*) TranslateA_LP(const char *str, HPLUGIN pPlugin) -{ - return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 0); -} - -MIR_CORE_DLL(char*) TranslateU_LP(const char *str, HPLUGIN pPlugin) -{ - return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 2); -} - -MIR_CORE_DLL(wchar_t*) TranslateW_LP(const wchar_t *str, HPLUGIN pPlugin) -{ - return (wchar_t*)LangPackTranslateString(GetMuid(pPlugin), (LPCSTR)str, 1); -} - -MIR_CORE_DLL(void) TranslateMenu_LP(HMENU hMenu, HPLUGIN pPlugin) -{ - const MUUID *uuid = &pPlugin->getInfo().uuid; - - MENUITEMINFO mii = { 0 }; - mii.cbSize = sizeof(mii); - - for (int i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) { - wchar_t str[256]; - mii.fMask = MIIM_TYPE | MIIM_SUBMENU; - mii.dwTypeData = (wchar_t*)str; - mii.cch = _countof(str); - GetMenuItemInfo(hMenu, i, TRUE, &mii); - - if (mii.cch && mii.dwTypeData) { - wchar_t *result = (wchar_t*)LangPackTranslateString(uuid, (const char*)mii.dwTypeData, TRUE); - if (result != mii.dwTypeData) { - mii.dwTypeData = result; - mii.fMask = MIIM_TYPE; - SetMenuItemInfo(hMenu, i, TRUE, &mii); - } - } - - if (mii.hSubMenu != nullptr) - TranslateMenu_LP(mii.hSubMenu, pPlugin); - } -} - -static void TranslateWindow(const MUUID *pUuid, HWND hwnd) -{ - wchar_t title[2048]; - GetWindowText(hwnd, title, _countof(title)); - - wchar_t *result = (wchar_t*)LangPackTranslateString(pUuid, (const char*)title, TRUE); - if (result != title) - SetWindowText(hwnd, result); -} - -static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd, LPARAM lParam) -{ - HPLUGIN pPlugin = (HPLUGIN)lParam; - const MUUID *uuid = GetMuid(pPlugin); - - wchar_t szClass[32]; - GetClassName(hwnd, szClass, _countof(szClass)); - if (!mir_wstrcmpi(szClass, L"static") || !mir_wstrcmpi(szClass, L"hyperlink") || !mir_wstrcmpi(szClass, L"button") || !mir_wstrcmpi(szClass, L"MButtonClass") || !mir_wstrcmpi(szClass, L"MHeaderbarCtrl")) - TranslateWindow(uuid, hwnd); - else if (!mir_wstrcmpi(szClass, L"edit")) { - if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) - TranslateWindow(uuid, hwnd); - } - return TRUE; -} - -MIR_CORE_DLL(void) TranslateDialog_LP(HWND hDlg, HPLUGIN pPlugin) -{ - TranslateWindow(GetMuid(pPlugin), hDlg); - EnumChildWindows(hDlg, TranslateDialogEnumProc, (LPARAM)pPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Langpack_SortDuplicates(void) -{ - if (g_entryCount == 0) - return; - - LangPackEntry *s = g_pEntries + 1, *d = s, *pLast = g_pEntries; - uint32_t dwSavedHash = g_pEntries->englishHash; - bool bSortNeeded = false; - - for (int i = 1; i < g_entryCount; i++, s++) { - if (s->englishHash != dwSavedHash) { - pLast = d; - if (s != d) - *d++ = *s; - else - d++; - dwSavedHash = s->englishHash; - } - else { - bSortNeeded = true; - LangPackEntry *p = (LangPackEntry*)mir_alloc(sizeof(LangPackEntry)); - *p = *s; - pLast->pNext = p; pLast = p; - } - } - - if (bSortNeeded) { - g_entryCount = (int)(d - g_pEntries); - qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void GetDefaultLang() -{ - // calculate the langpacks' root - PathToAbsoluteW(L"\\Languages", g_tszRoot); - if (_waccess(g_tszRoot, 0) != 0) // directory Languages exists - PathToAbsoluteW(L".", g_tszRoot); - - // look into mirandaboot.ini - wchar_t tszLangName[256]; - Profile_GetSetting(L"Language/DefaultLanguage", tszLangName); - if (tszLangName[0]) { - if (!mir_wstrcmpi(tszLangName, L"default")) { - db_set_ws(0, "Langpack", "Current", L"default"); - return; - } - if (!LoadLangPack(tszLangName)) { - db_set_ws(0, "Langpack", "Current", tszLangName); - return; - } - } - - // try to load langpack that matches UserDefaultUILanguage - wchar_t tszPath[MAX_PATH]; - if (GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SENGLANGUAGE, tszLangName, _countof(tszLangName))) { - mir_snwprintf(tszPath, L"langpack_%s.txt", wcslwr(tszLangName)); - if (!LoadLangPack(tszPath)) { - db_set_ws(0, "Langpack", "Current", tszPath); - return; - } - } - - // finally try to load first file - mir_snwprintf(tszPath, L"%s\\langpack_*.txt", g_tszRoot); - - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile(tszPath, &fd); - if (hFind != INVALID_HANDLE_VALUE) { - do { - /* search first langpack that could be loaded */ - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - continue; - - if (!LoadLangPack(fd.cFileName)) { - db_set_ws(0, "Langpack", "Current", fd.cFileName); - break; - } - } while (FindNextFile(hFind, &fd)); - FindClose(hFind); - } - else db_set_ws(0, "Langpack", "Current", L"default"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) ReloadLangpack(wchar_t *pszStr) -{ - if (pszStr == nullptr) - pszStr = NEWWSTR_ALLOCA(langPack.tszFileName); - - UnloadLangPackModule(); - LoadLangPack(pszStr); - Langpack_SortDuplicates(); - - NotifyEventHooks(hevChanged, 0, 0); -} - -static INT_PTR srvReloadLangpack(WPARAM, LPARAM lParam) -{ - ReloadLangpack((wchar_t*)lParam); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) LoadLangPackModule(void) -{ - bModuleInitialized = TRUE; - hevChanged = CreateHookableEvent(ME_LANGPACK_CHANGED); - CreateServiceFunction(MS_LANGPACK_RELOAD, srvReloadLangpack); - GetDefaultLang(); - return 0; -} - -void UnloadLangPackModule() -{ - if (!bModuleInitialized) return; - - for (auto &it : lMuuids) - mir_free(it); - lMuuids.destroy(); - - LangPackEntry *p = g_pEntries; - for (int i = 0; i < g_entryCount; i++, p++) { - if (p->pNext != nullptr) { - for (LangPackEntry *p1 = p->pNext; p1 != nullptr;) { - LangPackEntry *p2 = p1; p1 = p1->pNext; - mir_free(p2->szLocal); - mir_free(p2->wszLocal); - mir_free(p2); - } - } - - mir_free(p->szLocal); - mir_free(p->wszLocal); - } - - if (g_entryCount) { - mir_free(g_pEntries); - g_pEntries = nullptr; - g_entryCount = g_entriesAlloced = 0; - } - - langPack.tszFileName[0] = langPack.tszFullPath[0] = 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+#include "../../../mir_app/src/langpack.h"
+
+#define LANGPACK_BUF_SIZE 4000
+
+static int CompareMuuids(const MUUID *p1, const MUUID *p2)
+{
+ return memcmp(p1, p2, sizeof(MUUID));
+}
+
+static LIST<MUUID> lMuuids(10, CompareMuuids);
+static MUUID *pCurrentMuuid = nullptr;
+static HANDLE hevChanged = nullptr;
+
+static BOOL bModuleInitialized = FALSE;
+
+struct LangPackEntry
+{
+ uint32_t englishHash;
+ char *szLocal;
+ char *utfLocal;
+ wchar_t *wszLocal;
+ MUUID *pMuuid;
+ LangPackEntry* pNext; // for langpack items with the same hash value
+};
+
+static LANGPACK_INFO langPack;
+static wchar_t g_tszRoot[MAX_PATH];
+
+static LangPackEntry *g_pEntries;
+static int g_entryCount, g_entriesAlloced;
+
+static int IsEmpty(const char *str)
+{
+ for (int i = 0; str[i]; i++)
+ if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n')
+ return 0;
+
+ return 1;
+}
+
+static int ConvertBackslashes(char *str, UINT fileCp)
+{
+ int shift = 0;
+ char *pstr;
+ for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0)) {
+ if (*pstr == '\\') {
+ shift++;
+ switch (pstr[1]) {
+ case 'n': *pstr = '\n'; break;
+ case 't': *pstr = '\t'; break;
+ case 'r': *pstr = '\r'; break;
+ case 's': *pstr = ' '; break;
+ default: *pstr = pstr[1]; break;
+ }
+ memmove(pstr + 1, pstr + 2, strlen(pstr + 2) + 1);
+ }
+ }
+ return shift;
+}
+
+#ifdef _DEBUG
+//#pragma optimize("gt", on)
+#endif
+
+// MurmurHash2
+MIR_CORE_DLL(unsigned int) mir_hash(const void * key, unsigned int len)
+{
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ const unsigned int m = 0x5bd1e995;
+ const int r = 24;
+
+ // Initialize the hash to a 'random' value
+ unsigned int h = len;
+
+ // Mix 4 bytes at a time into the hash
+ const unsigned char *data = (const unsigned char*)key;
+
+ while (len >= 4) {
+ unsigned int k = *(unsigned int*)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ // Handle the last few bytes of the input array
+ switch (len) {
+ case 3: h ^= data[2] << 16;
+ case 2: h ^= data[1] << 8;
+ case 1: h ^= data[0];
+ h *= m;
+ }
+
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+
+static unsigned int __fastcall hashstrW(const char *key)
+{
+ if (key == nullptr) return 0;
+ const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+ char *buf = (char*)alloca(len + 1);
+ for (unsigned i = 0; i <= len; ++i)
+ buf[i] = key[i << 1];
+ return mir_hash(buf, len);
+}
+
+static const MUUID* GetMuid(HPLUGIN pPlugin)
+{
+ if (!pPlugin)
+ return nullptr;
+
+ __try {
+ return &pPlugin->getInfo().uuid;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return nullptr;
+ }
+}
+
+static int SortLangPackHashesProc(LangPackEntry *arg1, LangPackEntry *arg2)
+{
+ if (arg1->englishHash < arg2->englishHash) return -1;
+ if (arg1->englishHash > arg2->englishHash) return 1;
+
+ return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1;
+}
+
+static void swapBytes(void *p, size_t iSize)
+{
+ char *head = (char*)p; // here
+ char *tail = head + iSize - 1;
+
+ for (; tail > head; --tail, ++head) {
+ char temp = *head;
+ *head = *tail;
+ *tail = temp;
+ }
+}
+
+static bool EnterMuuid(const char *p, MUUID &result)
+{
+ if (*p++ != '{')
+ return false;
+
+ uint8_t *d = (uint8_t*)&result;
+
+ for (int nBytes = 0; *p && nBytes < 24; p++) {
+ if (*p == '-')
+ continue;
+
+ if (*p == '}')
+ break;
+
+ if (!isxdigit(*p))
+ return false;
+
+ if (!isxdigit(p[1]))
+ return false;
+
+ int c = 0;
+ if (sscanf(p, "%2x", &c) != 1)
+ return false;
+
+ *d++ = (uint8_t)c;
+ nBytes++;
+ p++;
+ }
+
+ if (*p != '}')
+ return false;
+
+ swapBytes(&result.a, sizeof(result.a));
+ swapBytes(&result.b, sizeof(result.b));
+ swapBytes(&result.c, sizeof(result.c));
+ return true;
+}
+
+static void LoadLangPackFile(FILE *fp, char *line)
+{
+ while (!feof(fp)) {
+ if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr)
+ break;
+
+ if (IsEmpty(line) || line[0] == ';' || line[0] == 0)
+ continue;
+
+ rtrim(line);
+
+ if (line[0] == '#') {
+ strlwr(line);
+
+ if (!memcmp(line + 1, "include", 7)) {
+ wchar_t tszFileName[MAX_PATH];
+ wchar_t *p = wcsrchr(langPack.tszFullPath, '\\');
+ if (p)
+ *p = 0;
+ mir_snwprintf(tszFileName, L"%s\\%S", langPack.tszFullPath, ltrim(line + 9));
+ if (p)
+ *p = '\\';
+
+ FILE *fpNew = _wfopen(tszFileName, L"r");
+ if (fpNew) {
+ line[0] = 0;
+ fgets(line, LANGPACK_BUF_SIZE, fpNew);
+
+ if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf')
+ fseek(fpNew, 3, SEEK_SET);
+ else
+ fseek(fpNew, 0, SEEK_SET);
+
+ LoadLangPackFile(fpNew, line);
+ fclose(fpNew);
+ }
+ }
+ else if (!memcmp(line + 1, "muuid", 5)) {
+ MUUID t;
+ if (!EnterMuuid(line + 7, t))
+ continue;
+
+ MUUID *pNew = (MUUID*)mir_alloc(sizeof(MUUID));
+ memcpy(pNew, &t, sizeof(t));
+ lMuuids.insert(pNew);
+ pCurrentMuuid = pNew;
+ }
+
+ continue;
+ }
+
+ char cFirst = line[0];
+
+ ConvertBackslashes(line, CP_UTF8);
+
+ size_t cbLen = strlen(line) - 1;
+ if (cFirst == '[' && line[cbLen] == ']') {
+ if (g_entryCount && g_pEntries[g_entryCount-1].wszLocal == nullptr)
+ g_entryCount--;
+
+ char *pszLine = line + 1;
+ line[cbLen] = '\0';
+ if (++g_entryCount > g_entriesAlloced) {
+ g_entriesAlloced += 128;
+ g_pEntries = (LangPackEntry*)mir_realloc(g_pEntries, sizeof(LangPackEntry)*g_entriesAlloced);
+ }
+
+ LangPackEntry *E = &g_pEntries[g_entryCount - 1];
+ E->englishHash = mir_hashstr(pszLine);
+ E->szLocal = E->utfLocal = nullptr;
+ E->wszLocal = nullptr;
+ E->pMuuid = pCurrentMuuid;
+ E->pNext = nullptr;
+ continue;
+ }
+
+ if (!g_entryCount)
+ continue;
+
+ LangPackEntry *E = &g_pEntries[g_entryCount - 1];
+ int iNeeded = MultiByteToWideChar(CP_UTF8, 0, line, -1, nullptr, 0), iOldLen;
+ if (E->wszLocal == nullptr) {
+ iOldLen = 0;
+ E->wszLocal = (wchar_t *)mir_alloc((iNeeded + 1) * sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal, iNeeded);
+ }
+ else {
+ iOldLen = (int)wcslen(E->wszLocal);
+ E->wszLocal = (wchar_t*)mir_realloc(E->wszLocal, (sizeof(wchar_t)* (iOldLen + iNeeded + 2)));
+ E->wszLocal[iOldLen++] = '\n';
+ }
+ MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal + iOldLen, iNeeded);
+ }
+}
+
+static int LoadLangDescr(LANGPACK_INFO &lpinfo, FILE *fp, char *line, int &startOfLine)
+{
+ char szLanguage[64]; szLanguage[0] = 0;
+ CMStringA szAuthors;
+
+ lpinfo.codepage = CP_ACP;
+ lpinfo.flags = 0;
+ lpinfo.tszLanguage[0] = 0;
+
+ fgets(line, LANGPACK_BUF_SIZE, fp);
+ size_t lineLen = strlen(line);
+ if (lineLen >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf')
+ memmove(line, line + 3, lineLen - 2);
+
+ lrtrim(line);
+ if (mir_strcmp(line, "Miranda Language Pack Version 1"))
+ return 2;
+
+ // headers
+ while (!feof(fp)) {
+ startOfLine = ftell(fp);
+ if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr)
+ break;
+
+ lrtrim(line);
+ if (IsEmpty(line) || line[0] == ';' || line[0] == 0)
+ continue;
+
+ if (line[0] == '[' || line[0] == '#')
+ break;
+
+ char *pszColon = strchr(line, ':');
+ if (pszColon == nullptr)
+ return 3;
+
+ *pszColon++ = 0;
+ if (!mir_strcmp(line, "Language")) {
+ strncpy_s(szLanguage, pszColon, _TRUNCATE);
+ lrtrim(szLanguage);
+ }
+ else if (!mir_strcmp(line, "Last-Modified-Using")) {
+ lpinfo.szLastModifiedUsing = pszColon;
+ lpinfo.szLastModifiedUsing.Trim();
+ }
+ else if (!mir_strcmp(line, "Authors")) {
+ if (!szAuthors.IsEmpty())
+ szAuthors.AppendChar(' ');
+ szAuthors.Append(lrtrim(pszColon));
+ }
+ else if (!mir_strcmp(line, "Locale")) {
+ char szBuf[20], *stopped;
+
+ lrtrim(pszColon + 1);
+ USHORT langID = (USHORT)strtol(pszColon, &stopped, 16);
+ lpinfo.Locale = MAKELCID(langID, 0);
+ GetLocaleInfoA(lpinfo.Locale, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10);
+ szBuf[5] = 0; // codepages have max. 5 digits
+ lpinfo.codepage = atoi(szBuf);
+ }
+ }
+
+ lpinfo.szAuthors = szAuthors;
+
+ ptrW buf(mir_utf8decodeW(szLanguage));
+ if (buf)
+ wcsncpy_s(lpinfo.tszLanguage, buf, _TRUNCATE);
+ else if (lpinfo.Locale != 0)
+ GetLocaleInfo(lpinfo.Locale, LOCALE_SENGLANGUAGE, lpinfo.tszLanguage, _countof(lpinfo.tszLanguage));
+
+ if (!lpinfo.tszLanguage[0]) {
+ wchar_t *p = wcschr(lpinfo.tszFileName, '_');
+ wcsncpy_s(lpinfo.tszLanguage, ((p != nullptr) ? (p + 1) : lpinfo.tszFileName), _TRUNCATE);
+ p = wcsrchr(lpinfo.tszLanguage, '.');
+ if (p != nullptr) *p = '\0';
+ }
+ return 0;
+}
+
+MIR_CORE_DLL(int) LoadLangPack(const wchar_t *ptszLangPack)
+{
+ if (ptszLangPack == nullptr || !mir_wstrcmpi(ptszLangPack, L""))
+ return 1;
+
+ // ensure that a lang's name is a full file name
+ wchar_t tszFullPath[MAX_PATH];
+ if (!PathIsAbsoluteW(ptszLangPack))
+ mir_snwprintf(tszFullPath, L"%s\\%s", g_tszRoot, ptszLangPack);
+ else
+ wcsncpy_s(tszFullPath, ptszLangPack, _TRUNCATE);
+
+ // this lang is already loaded? nothing to do then
+ if (!mir_wstrcmp(tszFullPath, langPack.tszFullPath))
+ return 0;
+
+ // ok... loading a new langpack. remove the old one if needed
+ if (g_entryCount)
+ UnloadLangPackModule();
+
+ langPack.Locale = 0;
+ langPack.codepage = CP_ACP;
+ langPack.flags = 0;
+
+ // exists & not a directory?
+ uint32_t dwAttrib = GetFileAttributes(tszFullPath);
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
+ return 3;
+
+ // copy the full file name and extract a file name from it
+ wcsncpy_s(langPack.tszFullPath, tszFullPath, _TRUNCATE);
+ wchar_t *p = wcsrchr(langPack.tszFullPath, '\\');
+ wcsncpy_s(langPack.tszFileName, (p == nullptr) ? tszFullPath : p + 1, _TRUNCATE);
+ CharLower(langPack.tszFileName);
+
+ FILE *fp = _wfopen(tszFullPath, L"rt");
+ if (fp == nullptr)
+ return 1;
+
+ char line[LANGPACK_BUF_SIZE] = "";
+ int startOfLine = 0;
+ if (LoadLangDescr(langPack, fp, line, startOfLine)) {
+ fclose(fp);
+ return 1;
+ }
+
+ // body
+ fseek(fp, startOfLine, SEEK_SET);
+
+ LoadLangPackFile(fp, line);
+ fclose(fp);
+ pCurrentMuuid = nullptr;
+
+ qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc);
+ return 0;
+}
+
+MIR_CORE_DLL(int) LoadLangPackDescr(const wchar_t *ptszLangPack, LANGPACK_INFO *lpInfo)
+{
+ if (lpInfo == nullptr)
+ return 1;
+
+ wcsncpy_s(lpInfo->tszFullPath, ptszLangPack, _TRUNCATE);
+ wchar_t *p = wcsrchr(lpInfo->tszFullPath, '\\');
+ wcsncpy_s(lpInfo->tszFileName, (p == nullptr) ? ptszLangPack : p+1, _TRUNCATE);
+ CharLower(lpInfo->tszFileName);
+
+ FILE *fp = _wfopen(ptszLangPack, L"rt");
+ if (fp == nullptr)
+ return 1;
+
+ char line[LANGPACK_BUF_SIZE] = "";
+ int startOfLine = 0;
+ int res = LoadLangDescr(*lpInfo, fp, line, startOfLine);
+ fclose(fp);
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int SortLangPackHashesProc2(LangPackEntry *arg1, LangPackEntry *arg2)
+{
+ if (arg1->englishHash < arg2->englishHash) return -1;
+ if (arg1->englishHash > arg2->englishHash) return 1;
+ return 0;
+}
+
+char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W)
+{
+ if (g_entryCount == 0 || szEnglish == nullptr)
+ return (char*)szEnglish;
+
+ LangPackEntry key, *entry;
+ key.englishHash = (W == 1) ? hashstrW(szEnglish) : mir_hashstr(szEnglish);
+ entry = (LangPackEntry*)bsearch(&key, g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc2);
+ if (entry == nullptr)
+ return (char*)szEnglish;
+
+ // try to find the exact match, otherwise the first entry will be returned
+ if (pUuid) {
+ for (LangPackEntry *p = entry->pNext; p != nullptr; p = p->pNext) {
+ if (p->pMuuid && *p->pMuuid == *pUuid) {
+ entry = p;
+ break;
+ }
+ }
+ }
+
+ switch (W) {
+ case 0:
+ if (entry->szLocal == nullptr && entry->wszLocal != nullptr)
+ entry->szLocal = mir_u2a_cp(entry->wszLocal, langPack.codepage);
+ return entry->szLocal;
+
+ case 1:
+ return (char*)entry->wszLocal;
+
+ case 2:
+ if (entry->utfLocal == nullptr && entry->wszLocal != nullptr)
+ entry->utfLocal = mir_utf8encodeW(entry->wszLocal);
+ return entry->utfLocal;
+ }
+
+ return nullptr;
+}
+
+MIR_CORE_DLL(int) Langpack_GetDefaultCodePage()
+{
+ return langPack.codepage;
+}
+
+MIR_CORE_DLL(int) Langpack_GetDefaultLocale()
+{
+ return (langPack.Locale == 0) ? LOCALE_USER_DEFAULT : langPack.Locale;
+}
+
+MIR_CORE_DLL(wchar_t*) Langpack_PcharToTchar(const char *pszStr)
+{
+ if (pszStr == nullptr)
+ return nullptr;
+
+ int len = (int)strlen(pszStr);
+ wchar_t *result = (wchar_t*)alloca((len + 1)*sizeof(wchar_t));
+ MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pszStr, -1, result, len);
+ result[len] = 0;
+ return mir_wstrdup(TranslateW_LP(result));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(char*) TranslateA_LP(const char *str, HPLUGIN pPlugin)
+{
+ return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 0);
+}
+
+MIR_CORE_DLL(char*) TranslateU_LP(const char *str, HPLUGIN pPlugin)
+{
+ return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 2);
+}
+
+MIR_CORE_DLL(wchar_t*) TranslateW_LP(const wchar_t *str, HPLUGIN pPlugin)
+{
+ return (wchar_t*)LangPackTranslateString(GetMuid(pPlugin), (LPCSTR)str, 1);
+}
+
+MIR_CORE_DLL(void) TranslateMenu_LP(HMENU hMenu, HPLUGIN pPlugin)
+{
+ const MUUID *uuid = &pPlugin->getInfo().uuid;
+
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = sizeof(mii);
+
+ for (int i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) {
+ wchar_t str[256];
+ mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
+ mii.dwTypeData = (wchar_t*)str;
+ mii.cch = _countof(str);
+ GetMenuItemInfo(hMenu, i, TRUE, &mii);
+
+ if (mii.cch && mii.dwTypeData) {
+ wchar_t *result = (wchar_t*)LangPackTranslateString(uuid, (const char*)mii.dwTypeData, TRUE);
+ if (result != mii.dwTypeData) {
+ mii.dwTypeData = result;
+ mii.fMask = MIIM_TYPE;
+ SetMenuItemInfo(hMenu, i, TRUE, &mii);
+ }
+ }
+
+ if (mii.hSubMenu != nullptr)
+ TranslateMenu_LP(mii.hSubMenu, pPlugin);
+ }
+}
+
+static void TranslateWindow(const MUUID *pUuid, HWND hwnd)
+{
+ wchar_t title[2048];
+ GetWindowText(hwnd, title, _countof(title));
+
+ wchar_t *result = (wchar_t*)LangPackTranslateString(pUuid, (const char*)title, TRUE);
+ if (result != title)
+ SetWindowText(hwnd, result);
+}
+
+static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd, LPARAM lParam)
+{
+ HPLUGIN pPlugin = (HPLUGIN)lParam;
+ const MUUID *uuid = GetMuid(pPlugin);
+
+ wchar_t szClass[32];
+ GetClassName(hwnd, szClass, _countof(szClass));
+ if (!mir_wstrcmpi(szClass, L"static") || !mir_wstrcmpi(szClass, L"hyperlink") || !mir_wstrcmpi(szClass, L"button") || !mir_wstrcmpi(szClass, L"MButtonClass") || !mir_wstrcmpi(szClass, L"MHeaderbarCtrl"))
+ TranslateWindow(uuid, hwnd);
+ else if (!mir_wstrcmpi(szClass, L"edit")) {
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY)
+ TranslateWindow(uuid, hwnd);
+ }
+ return TRUE;
+}
+
+MIR_CORE_DLL(void) TranslateDialog_LP(HWND hDlg, HPLUGIN pPlugin)
+{
+ TranslateWindow(GetMuid(pPlugin), hDlg);
+ EnumChildWindows(hDlg, TranslateDialogEnumProc, (LPARAM)pPlugin);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) Langpack_SortDuplicates(void)
+{
+ if (g_entryCount == 0)
+ return;
+
+ LangPackEntry *s = g_pEntries + 1, *d = s, *pLast = g_pEntries;
+ uint32_t dwSavedHash = g_pEntries->englishHash;
+ bool bSortNeeded = false;
+
+ for (int i = 1; i < g_entryCount; i++, s++) {
+ if (s->englishHash != dwSavedHash) {
+ pLast = d;
+ if (s != d)
+ *d++ = *s;
+ else
+ d++;
+ dwSavedHash = s->englishHash;
+ }
+ else {
+ bSortNeeded = true;
+ LangPackEntry *p = (LangPackEntry*)mir_alloc(sizeof(LangPackEntry));
+ *p = *s;
+ pLast->pNext = p; pLast = p;
+ }
+ }
+
+ if (bSortNeeded) {
+ g_entryCount = (int)(d - g_pEntries);
+ qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void GetDefaultLang()
+{
+ // calculate the langpacks' root
+ PathToAbsoluteW(L"\\Languages", g_tszRoot);
+ if (_waccess(g_tszRoot, 0) != 0) // directory Languages exists
+ PathToAbsoluteW(L".", g_tszRoot);
+
+ // look into mirandaboot.ini
+ wchar_t tszLangName[256];
+ Profile_GetSetting(L"Language/DefaultLanguage", tszLangName);
+ if (tszLangName[0]) {
+ if (!mir_wstrcmpi(tszLangName, L"default")) {
+ db_set_ws(0, "Langpack", "Current", L"default");
+ return;
+ }
+ if (!LoadLangPack(tszLangName)) {
+ db_set_ws(0, "Langpack", "Current", tszLangName);
+ return;
+ }
+ }
+
+ // try to load langpack that matches UserDefaultUILanguage
+ wchar_t tszPath[MAX_PATH];
+ if (GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SENGLANGUAGE, tszLangName, _countof(tszLangName))) {
+ mir_snwprintf(tszPath, L"langpack_%s.txt", wcslwr(tszLangName));
+ if (!LoadLangPack(tszPath)) {
+ db_set_ws(0, "Langpack", "Current", tszPath);
+ return;
+ }
+ }
+
+ // finally try to load first file
+ mir_snwprintf(tszPath, L"%s\\langpack_*.txt", g_tszRoot);
+
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(tszPath, &fd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ /* search first langpack that could be loaded */
+ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+
+ if (!LoadLangPack(fd.cFileName)) {
+ db_set_ws(0, "Langpack", "Current", fd.cFileName);
+ break;
+ }
+ } while (FindNextFile(hFind, &fd));
+ FindClose(hFind);
+ }
+ else db_set_ws(0, "Langpack", "Current", L"default");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) ReloadLangpack(wchar_t *pszStr)
+{
+ if (pszStr == nullptr)
+ pszStr = NEWWSTR_ALLOCA(langPack.tszFileName);
+
+ UnloadLangPackModule();
+ LoadLangPack(pszStr);
+ Langpack_SortDuplicates();
+
+ NotifyEventHooks(hevChanged, 0, 0);
+}
+
+static INT_PTR srvReloadLangpack(WPARAM, LPARAM lParam)
+{
+ ReloadLangpack((wchar_t*)lParam);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) LoadLangPackModule(void)
+{
+ bModuleInitialized = TRUE;
+ hevChanged = CreateHookableEvent(ME_LANGPACK_CHANGED);
+ CreateServiceFunction(MS_LANGPACK_RELOAD, srvReloadLangpack);
+ GetDefaultLang();
+ return 0;
+}
+
+void UnloadLangPackModule()
+{
+ if (!bModuleInitialized) return;
+
+ for (auto &it : lMuuids)
+ mir_free(it);
+ lMuuids.destroy();
+
+ LangPackEntry *p = g_pEntries;
+ for (int i = 0; i < g_entryCount; i++, p++) {
+ if (p->pNext != nullptr) {
+ for (LangPackEntry *p1 = p->pNext; p1 != nullptr;) {
+ LangPackEntry *p2 = p1; p1 = p1->pNext;
+ mir_free(p2->szLocal);
+ mir_free(p2->wszLocal);
+ mir_free(p2);
+ }
+ }
+
+ mir_free(p->szLocal);
+ mir_free(p->wszLocal);
+ }
+
+ if (g_entryCount) {
+ mir_free(g_pEntries);
+ g_pEntries = nullptr;
+ g_entryCount = g_entriesAlloced = 0;
+ }
+
+ langPack.tszFileName[0] = langPack.tszFullPath[0] = 0;
+}
diff --git a/src/mir_core/src/Windows/locks.cpp b/src/mir_core/src/Windows/locks.cpp index 88f4639e80..dca0e98966 100644 --- a/src/mir_core/src/Windows/locks.cpp +++ b/src/mir_core/src/Windows/locks.cpp @@ -1,46 +1,46 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -mir_cs::mir_cs() -{ - ::InitializeCriticalSection(&m_cs); -} - -mir_cs::~mir_cs() -{ - ::DeleteCriticalSection(&m_cs); -} - -void mir_cs::Lock() -{ - while (::TryEnterCriticalSection(&m_cs) == 0) - SleepEx(50, TRUE); -} - -void mir_cs::Unlock() -{ - ::LeaveCriticalSection(&m_cs); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+mir_cs::mir_cs()
+{
+ ::InitializeCriticalSection(&m_cs);
+}
+
+mir_cs::~mir_cs()
+{
+ ::DeleteCriticalSection(&m_cs);
+}
+
+void mir_cs::Lock()
+{
+ while (::TryEnterCriticalSection(&m_cs) == 0)
+ SleepEx(50, TRUE);
+}
+
+void mir_cs::Unlock()
+{
+ ::LeaveCriticalSection(&m_cs);
+}
diff --git a/src/mir_core/src/Windows/miranda.cpp b/src/mir_core/src/Windows/miranda.cpp index d13e69ff1f..ce52b20b0b 100644 --- a/src/mir_core/src/Windows/miranda.cpp +++ b/src/mir_core/src/Windows/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/Windows/openurl.cpp b/src/mir_core/src/Windows/openurl.cpp index 3a9a3d2024..4bb8310e75 100644 --- a/src/mir_core/src/Windows/openurl.cpp +++ b/src/mir_core/src/Windows/openurl.cpp @@ -1,76 +1,76 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" -#include <ctype.h> - -struct TOpenUrlInfo -{ - TOpenUrlInfo(wchar_t *_url, int _bNew) : - szUrl(_url), - newWindow(_bNew) - {} - - ptrW szUrl; - int newWindow; -}; - -static void __cdecl OpenURLThread(TOpenUrlInfo *hUrlInfo) -{ - // wack a protocol on it - CMStringW tszUrl; - if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\') - tszUrl.Format(L"file:///%s", hUrlInfo->szUrl.get()); - else { - int i; - for (i = 0; iswalpha(hUrlInfo->szUrl[i]); i++); - if (hUrlInfo->szUrl[i] == ':') - tszUrl = hUrlInfo->szUrl; - else if (!wcsnicmp(hUrlInfo->szUrl, L"ftp.", 4)) - tszUrl.Format(L"ftp://%s", hUrlInfo->szUrl.get()); - else - tszUrl.Format(L"http://%s", hUrlInfo->szUrl.get()); - } - - // check user defined browser for opening urls - ptrW tszBrowser(db_get_wsa(0, "Miranda", "OpenUrlBrowser")); - if (tszBrowser) - ShellExecute(nullptr, L"open", tszBrowser, tszUrl, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); - else - ShellExecute(nullptr, L"open", tszUrl, nullptr, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); - - delete hUrlInfo; -} - -MIR_CORE_DLL(void) Utils_OpenUrl(const char *pszUrl, bool bOpenInNewWindow) -{ - if (pszUrl) - mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow)); -} - -MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow) -{ - if (pszUrl) - mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_wstrdup(pszUrl), bOpenInNewWindow)); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+#include <ctype.h>
+
+struct TOpenUrlInfo
+{
+ TOpenUrlInfo(wchar_t *_url, int _bNew) :
+ szUrl(_url),
+ newWindow(_bNew)
+ {}
+
+ ptrW szUrl;
+ int newWindow;
+};
+
+static void __cdecl OpenURLThread(TOpenUrlInfo *hUrlInfo)
+{
+ // wack a protocol on it
+ CMStringW tszUrl;
+ if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\')
+ tszUrl.Format(L"file:///%s", hUrlInfo->szUrl.get());
+ else {
+ int i;
+ for (i = 0; iswalpha(hUrlInfo->szUrl[i]); i++);
+ if (hUrlInfo->szUrl[i] == ':')
+ tszUrl = hUrlInfo->szUrl;
+ else if (!wcsnicmp(hUrlInfo->szUrl, L"ftp.", 4))
+ tszUrl.Format(L"ftp://%s", hUrlInfo->szUrl.get());
+ else
+ tszUrl.Format(L"http://%s", hUrlInfo->szUrl.get());
+ }
+
+ // check user defined browser for opening urls
+ ptrW tszBrowser(db_get_wsa(0, "Miranda", "OpenUrlBrowser"));
+ if (tszBrowser)
+ ShellExecute(nullptr, L"open", tszBrowser, tszUrl, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT);
+ else
+ ShellExecute(nullptr, L"open", tszUrl, nullptr, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT);
+
+ delete hUrlInfo;
+}
+
+MIR_CORE_DLL(void) Utils_OpenUrl(const char *pszUrl, bool bOpenInNewWindow)
+{
+ if (pszUrl)
+ mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow));
+}
+
+MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow)
+{
+ if (pszUrl)
+ mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_wstrdup(pszUrl), bOpenInNewWindow));
+}
diff --git a/src/mir_core/src/Windows/path.cpp b/src/mir_core/src/Windows/path.cpp index 5623ad416f..eb14c6b0de 100644 --- a/src/mir_core/src/Windows/path.cpp +++ b/src/mir_core/src/Windows/path.cpp @@ -1,246 +1,246 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -static char szMirandaPath[MAX_PATH]; -static wchar_t szMirandaPathW[MAX_PATH]; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) PathIsAbsolute(const char *path) -{ - if (path && strlen(path) > 2) - if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) - return 1; - return 0; -} - -MIR_CORE_DLL(int) PathToRelative(const char *pSrc, char *pOut, const char *pBase) -{ - if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - if (!PathIsAbsolute(pSrc)) - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - else { - if (pBase == nullptr) - pBase = szMirandaPath; - - size_t cbBaseLen = strlen(pBase); - if (!strnicmp(pSrc, pBase, cbBaseLen)) - strncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); - else - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - } - - return (int)strlen(pOut); -} - -MIR_CORE_DLL(int) PathToAbsolute(const char *pSrc, char *pOut, const char *base) -{ - if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - char buf[MAX_PATH]; - if (pSrc[0] < ' ') - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - - if (PathIsAbsolute(pSrc)) - return GetFullPathNameA(pSrc, MAX_PATH, pOut, nullptr); - - if (base == nullptr) - base = szMirandaPath; - - if (pSrc[0] == '\\') - pSrc++; - mir_snprintf(buf, "%s%s", base, pSrc); - return GetFullPathNameA(buf, _countof(buf), pOut, nullptr); -} - -MIR_CORE_DLL(int) CreatePathToFile(const char *szFilePath) -{ - if (szFilePath == nullptr) - return ERROR_INVALID_PARAMETER; - - char *buf = NEWSTR_ALLOCA(szFilePath); - char *p = strrchr(buf, '\\'); - if (p == nullptr) - return 0; - - *p = '\0'; - return CreateDirectoryTree(buf); -} - -MIR_CORE_DLL(int) CreateDirectoryTree(const char *szDir) -{ - if (szDir == nullptr) - return 1; - - uint32_t dwAttributes = GetFileAttributesA(szDir); - if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - - char szTestDir[MAX_PATH]; - mir_strncpy(szTestDir, szDir, _countof(szTestDir)); - char *pszLastBackslash = strrchr(szTestDir, '\\'); - if (pszLastBackslash == nullptr) - return 0; - - *pszLastBackslash = '\0'; - CreateDirectoryTree(szTestDir); - *pszLastBackslash = '\\'; - return (CreateDirectoryA(szTestDir, nullptr) == 0) ? GetLastError() : 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) PathIsAbsoluteW(const wchar_t *path) -{ - if (path && wcslen(path) > 2) - if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) - return 1; - return 0; -} - -MIR_CORE_DLL(int) PathToRelativeW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *pBase) -{ - if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) - return 0; - - if (!PathIsAbsoluteW(pSrc)) - wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - else { - if (pBase == nullptr) - pBase = szMirandaPathW; - - size_t cbBaseLen = wcslen(pBase); - if (!wcsnicmp(pSrc, pBase, cbBaseLen)) - wcsncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); - else - wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - } - return (int)wcslen(pOut); -} - -MIR_CORE_DLL(int) PathToAbsoluteW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *base) -{ - if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - wchar_t buf[MAX_PATH]; - if (pSrc[0] < ' ') - return mir_snwprintf(pOut, MAX_PATH, L"%s", pSrc); - - if (PathIsAbsoluteW(pSrc)) - return GetFullPathName(pSrc, MAX_PATH, pOut, nullptr); - - if (base == nullptr) - base = szMirandaPathW; - - if (pSrc[0] == '\\') - pSrc++; - - mir_snwprintf(buf, MAX_PATH, L"%s%s", base, pSrc); - return GetFullPathName(buf, MAX_PATH, pOut, nullptr); -} - -MIR_CORE_DLL(int) CreatePathToFileW(const wchar_t *wszFilePath) -{ - if (wszFilePath == nullptr) - return ERROR_INVALID_PARAMETER; - - wchar_t *buf = NEWWSTR_ALLOCA(wszFilePath); - wchar_t *p = wcsrchr(buf, '\\'); - if (p == nullptr) - return 0; - - *p = '\0'; - return CreateDirectoryTreeW(buf); -} - -MIR_CORE_DLL(int) CreateDirectoryTreeW(const wchar_t *szDir) -{ - if (szDir == nullptr) - return 1; - - uint32_t dwAttributes = GetFileAttributesW(szDir); - if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - - wchar_t szTestDir[MAX_PATH]; - mir_wstrncpy(szTestDir, szDir, _countof(szTestDir)); - wchar_t *pszLastBackslash = wcsrchr(szTestDir, '\\'); - if (pszLastBackslash == nullptr) - return 0; - - *pszLastBackslash = '\0'; - CreateDirectoryTreeW(szTestDir); - *pszLastBackslash = '\\'; - return (CreateDirectoryW(szTestDir, nullptr) == 0) ? GetLastError() : 0; -} - -MIR_CORE_DLL(int) DeleteDirectoryTreeW(const wchar_t *pwszDir, bool bAllowUndo) -{ - if (pwszDir == nullptr) - return ERROR_BAD_ARGUMENTS; - - CMStringW wszPath(pwszDir); - wszPath.AppendChar(0); - - SHFILEOPSTRUCTW file_op = { - nullptr, - FO_DELETE, - wszPath, - L"", - FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION, - false, - nullptr, - L"" }; - - if (bAllowUndo) - file_op.fFlags |= FOF_ALLOWUNDO; - - return SHFileOperationW(&file_op); -} - -int InitPathUtils(void) -{ - GetModuleFileNameA(nullptr, szMirandaPath, _countof(szMirandaPath)); - char *p = strrchr(szMirandaPath, '\\'); - if (p) - p[1] = 0; - - GetModuleFileNameW(nullptr, szMirandaPathW, _countof(szMirandaPathW)); - wchar_t *tp = wcsrchr(szMirandaPathW, '\\'); - if (tp) - tp[1] = 0; - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+static char szMirandaPath[MAX_PATH];
+static wchar_t szMirandaPathW[MAX_PATH];
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) PathIsAbsolute(const char *path)
+{
+ if (path && strlen(path) > 2)
+ if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\'))
+ return 1;
+ return 0;
+}
+
+MIR_CORE_DLL(int) PathToRelative(const char *pSrc, char *pOut, const char *pBase)
+{
+ if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) {
+ *pOut = 0;
+ return 0;
+ }
+
+ if (!PathIsAbsolute(pSrc))
+ strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ else {
+ if (pBase == nullptr)
+ pBase = szMirandaPath;
+
+ size_t cbBaseLen = strlen(pBase);
+ if (!strnicmp(pSrc, pBase, cbBaseLen))
+ strncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE);
+ else
+ strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ }
+
+ return (int)strlen(pOut);
+}
+
+MIR_CORE_DLL(int) PathToAbsolute(const char *pSrc, char *pOut, const char *base)
+{
+ if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) {
+ *pOut = 0;
+ return 0;
+ }
+
+ char buf[MAX_PATH];
+ if (pSrc[0] < ' ')
+ strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+
+ if (PathIsAbsolute(pSrc))
+ return GetFullPathNameA(pSrc, MAX_PATH, pOut, nullptr);
+
+ if (base == nullptr)
+ base = szMirandaPath;
+
+ if (pSrc[0] == '\\')
+ pSrc++;
+ mir_snprintf(buf, "%s%s", base, pSrc);
+ return GetFullPathNameA(buf, _countof(buf), pOut, nullptr);
+}
+
+MIR_CORE_DLL(int) CreatePathToFile(const char *szFilePath)
+{
+ if (szFilePath == nullptr)
+ return ERROR_INVALID_PARAMETER;
+
+ char *buf = NEWSTR_ALLOCA(szFilePath);
+ char *p = strrchr(buf, '\\');
+ if (p == nullptr)
+ return 0;
+
+ *p = '\0';
+ return CreateDirectoryTree(buf);
+}
+
+MIR_CORE_DLL(int) CreateDirectoryTree(const char *szDir)
+{
+ if (szDir == nullptr)
+ return 1;
+
+ uint32_t dwAttributes = GetFileAttributesA(szDir);
+ if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ return 0;
+
+ char szTestDir[MAX_PATH];
+ mir_strncpy(szTestDir, szDir, _countof(szTestDir));
+ char *pszLastBackslash = strrchr(szTestDir, '\\');
+ if (pszLastBackslash == nullptr)
+ return 0;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTree(szTestDir);
+ *pszLastBackslash = '\\';
+ return (CreateDirectoryA(szTestDir, nullptr) == 0) ? GetLastError() : 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) PathIsAbsoluteW(const wchar_t *path)
+{
+ if (path && wcslen(path) > 2)
+ if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\'))
+ return 1;
+ return 0;
+}
+
+MIR_CORE_DLL(int) PathToRelativeW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *pBase)
+{
+ if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH)
+ return 0;
+
+ if (!PathIsAbsoluteW(pSrc))
+ wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ else {
+ if (pBase == nullptr)
+ pBase = szMirandaPathW;
+
+ size_t cbBaseLen = wcslen(pBase);
+ if (!wcsnicmp(pSrc, pBase, cbBaseLen))
+ wcsncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE);
+ else
+ wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ }
+ return (int)wcslen(pOut);
+}
+
+MIR_CORE_DLL(int) PathToAbsoluteW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *base)
+{
+ if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) {
+ *pOut = 0;
+ return 0;
+ }
+
+ wchar_t buf[MAX_PATH];
+ if (pSrc[0] < ' ')
+ return mir_snwprintf(pOut, MAX_PATH, L"%s", pSrc);
+
+ if (PathIsAbsoluteW(pSrc))
+ return GetFullPathName(pSrc, MAX_PATH, pOut, nullptr);
+
+ if (base == nullptr)
+ base = szMirandaPathW;
+
+ if (pSrc[0] == '\\')
+ pSrc++;
+
+ mir_snwprintf(buf, MAX_PATH, L"%s%s", base, pSrc);
+ return GetFullPathName(buf, MAX_PATH, pOut, nullptr);
+}
+
+MIR_CORE_DLL(int) CreatePathToFileW(const wchar_t *wszFilePath)
+{
+ if (wszFilePath == nullptr)
+ return ERROR_INVALID_PARAMETER;
+
+ wchar_t *buf = NEWWSTR_ALLOCA(wszFilePath);
+ wchar_t *p = wcsrchr(buf, '\\');
+ if (p == nullptr)
+ return 0;
+
+ *p = '\0';
+ return CreateDirectoryTreeW(buf);
+}
+
+MIR_CORE_DLL(int) CreateDirectoryTreeW(const wchar_t *szDir)
+{
+ if (szDir == nullptr)
+ return 1;
+
+ uint32_t dwAttributes = GetFileAttributesW(szDir);
+ if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ return 0;
+
+ wchar_t szTestDir[MAX_PATH];
+ mir_wstrncpy(szTestDir, szDir, _countof(szTestDir));
+ wchar_t *pszLastBackslash = wcsrchr(szTestDir, '\\');
+ if (pszLastBackslash == nullptr)
+ return 0;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTreeW(szTestDir);
+ *pszLastBackslash = '\\';
+ return (CreateDirectoryW(szTestDir, nullptr) == 0) ? GetLastError() : 0;
+}
+
+MIR_CORE_DLL(int) DeleteDirectoryTreeW(const wchar_t *pwszDir, bool bAllowUndo)
+{
+ if (pwszDir == nullptr)
+ return ERROR_BAD_ARGUMENTS;
+
+ CMStringW wszPath(pwszDir);
+ wszPath.AppendChar(0);
+
+ SHFILEOPSTRUCTW file_op = {
+ nullptr,
+ FO_DELETE,
+ wszPath,
+ L"",
+ FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION,
+ false,
+ nullptr,
+ L"" };
+
+ if (bAllowUndo)
+ file_op.fFlags |= FOF_ALLOWUNDO;
+
+ return SHFileOperationW(&file_op);
+}
+
+int InitPathUtils(void)
+{
+ GetModuleFileNameA(nullptr, szMirandaPath, _countof(szMirandaPath));
+ char *p = strrchr(szMirandaPath, '\\');
+ if (p)
+ p[1] = 0;
+
+ GetModuleFileNameW(nullptr, szMirandaPathW, _countof(szMirandaPathW));
+ wchar_t *tp = wcsrchr(szMirandaPathW, '\\');
+ if (tp)
+ tp[1] = 0;
+ return 0;
+}
diff --git a/src/mir_core/src/Windows/resizer.cpp b/src/mir_core/src/Windows/resizer.cpp index 3b5de46fcc..519659a27f 100644 --- a/src/mir_core/src/Windows/resizer.cpp +++ b/src/mir_core/src/Windows/resizer.cpp @@ -1,151 +1,151 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -#pragma pack(2) - -struct START_OF_DLGITEMTEMPLATEEX -{ - uint32_t helpID; - uint32_t exStyle; - uint32_t style; - short x, y, cx, cy; - uint32_t id; -}; - -struct START_OF_DLGTEMPLATEEX -{ - uint16_t dlgVer; - uint16_t signature; - uint32_t helpID; - uint32_t exStyle; - uint32_t style; - uint16_t cDlgItems; - short x, y, cx, cy; -}; - -MIR_CORE_DLL(int) Utils_ResizeDialog(HWND hwndDlg, HINSTANCE hInstance, LPCSTR lpTemplate, DIALOGRESIZERPROC pfnResizer, LPARAM lParam) -{ - DLGTEMPLATE *pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(hInstance, FindResourceA(hInstance, lpTemplate, MAKEINTRESOURCEA(5)))); - START_OF_DLGTEMPLATEEX *pTemplateEx = (START_OF_DLGTEMPLATEEX*)pTemplate; - int extendedDlg = pTemplateEx->signature == 0xFFFF; - if (extendedDlg && pTemplateEx->dlgVer != 1) - return 1; - - PWORD pWord = (extendedDlg) ? (PWORD)(pTemplateEx + 1) : (PWORD)(pTemplate + 1); - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class - while (*pWord++); // skip title - if (extendedDlg) { - if (pTemplateEx->style & DS_SETFONT) { - pWord += 3; // font size, weight, italic - while (*pWord++); // font name - } - } - else { - if (pTemplate->style & DS_SETFONT) { - pWord++; // font size - while (*pWord++); // font name - } - } - - UTILRESIZECONTROL urc; - urc.cbSize = sizeof(UTILRESIZECONTROL); - - RECT rc; - rc.left = 0; rc.top = 0; - if (extendedDlg) { - rc.right = pTemplateEx->cx; - rc.bottom = pTemplateEx->cy; - } - else { - rc.right = pTemplate->cx; - rc.bottom = pTemplate->cy; - } - - MapDialogRect(hwndDlg, &rc); - urc.dlgOriginalSize.cx = rc.right; urc.dlgOriginalSize.cy = rc.bottom; - GetClientRect(hwndDlg, &rc); - urc.dlgNewSize.cx = rc.right; urc.dlgNewSize.cy = rc.bottom; - - int itemCount = (extendedDlg) ? pTemplateEx->cDlgItems : pTemplate->cdit; - - HDWP hDwp = BeginDeferWindowPos(itemCount); - for (int i = 0; i < itemCount; i++) { - if ((UINT_PTR)pWord & 2) pWord++; //dword align - - if (extendedDlg) { - START_OF_DLGITEMTEMPLATEEX *pItemEx = (START_OF_DLGITEMTEMPLATEEX*)pWord; - pWord = (PWORD)(pItemEx + 1); - - urc.wId = pItemEx->id; - urc.rcItem.left = pItemEx->x; urc.rcItem.top = pItemEx->y; - urc.rcItem.right = urc.rcItem.left + pItemEx->cx; urc.rcItem.bottom = urc.rcItem.top + pItemEx->cy; - } - else { - DLGITEMTEMPLATE *pItem = (DLGITEMTEMPLATE*)pWord; - pWord = (PWORD)(pItem + 1); - - urc.wId = pItem->id; - urc.rcItem.left = pItem->x; urc.rcItem.top = pItem->y; - urc.rcItem.right = urc.rcItem.left + pItem->cx; urc.rcItem.bottom = urc.rcItem.top + pItem->cy; - } - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class - pWord += 1 + (1 + *pWord) / 2; //creation data - - if (urc.wId == 65535) // using this breaks the dwp, so just ignore it - continue; - - MapDialogRect(hwndDlg, &urc.rcItem); - int procResult = (pfnResizer)(hwndDlg, lParam, &urc); - if (procResult & RD_ANCHORX_RIGHT) { - urc.rcItem.left += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - } - else if (procResult & RD_ANCHORX_WIDTH) - urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - else if (procResult & RD_ANCHORX_CENTRE) { - urc.rcItem.left += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; - urc.rcItem.right += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; - } - if (procResult & RD_ANCHORY_BOTTOM) { - urc.rcItem.top += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - } - else if (procResult & RD_ANCHORY_HEIGHT) - urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - else if (procResult & RD_ANCHORY_CENTRE) { - urc.rcItem.top += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; - urc.rcItem.bottom += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; - } - - HWND hCtrl = GetDlgItem(hwndDlg, urc.wId); - if (hCtrl != nullptr && urc.wId != UINT(-1)) - hDwp = DeferWindowPos(hDwp, hCtrl, nullptr, urc.rcItem.left, urc.rcItem.top, urc.rcItem.right - urc.rcItem.left, urc.rcItem.bottom - urc.rcItem.top, SWP_NOZORDER); - } - EndDeferWindowPos(hDwp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+#pragma pack(2)
+
+struct START_OF_DLGITEMTEMPLATEEX
+{
+ uint32_t helpID;
+ uint32_t exStyle;
+ uint32_t style;
+ short x, y, cx, cy;
+ uint32_t id;
+};
+
+struct START_OF_DLGTEMPLATEEX
+{
+ uint16_t dlgVer;
+ uint16_t signature;
+ uint32_t helpID;
+ uint32_t exStyle;
+ uint32_t style;
+ uint16_t cDlgItems;
+ short x, y, cx, cy;
+};
+
+MIR_CORE_DLL(int) Utils_ResizeDialog(HWND hwndDlg, HINSTANCE hInstance, LPCSTR lpTemplate, DIALOGRESIZERPROC pfnResizer, LPARAM lParam)
+{
+ DLGTEMPLATE *pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(hInstance, FindResourceA(hInstance, lpTemplate, MAKEINTRESOURCEA(5))));
+ START_OF_DLGTEMPLATEEX *pTemplateEx = (START_OF_DLGTEMPLATEEX*)pTemplate;
+ int extendedDlg = pTemplateEx->signature == 0xFFFF;
+ if (extendedDlg && pTemplateEx->dlgVer != 1)
+ return 1;
+
+ PWORD pWord = (extendedDlg) ? (PWORD)(pTemplateEx + 1) : (PWORD)(pTemplate + 1);
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class
+ while (*pWord++); // skip title
+ if (extendedDlg) {
+ if (pTemplateEx->style & DS_SETFONT) {
+ pWord += 3; // font size, weight, italic
+ while (*pWord++); // font name
+ }
+ }
+ else {
+ if (pTemplate->style & DS_SETFONT) {
+ pWord++; // font size
+ while (*pWord++); // font name
+ }
+ }
+
+ UTILRESIZECONTROL urc;
+ urc.cbSize = sizeof(UTILRESIZECONTROL);
+
+ RECT rc;
+ rc.left = 0; rc.top = 0;
+ if (extendedDlg) {
+ rc.right = pTemplateEx->cx;
+ rc.bottom = pTemplateEx->cy;
+ }
+ else {
+ rc.right = pTemplate->cx;
+ rc.bottom = pTemplate->cy;
+ }
+
+ MapDialogRect(hwndDlg, &rc);
+ urc.dlgOriginalSize.cx = rc.right; urc.dlgOriginalSize.cy = rc.bottom;
+ GetClientRect(hwndDlg, &rc);
+ urc.dlgNewSize.cx = rc.right; urc.dlgNewSize.cy = rc.bottom;
+
+ int itemCount = (extendedDlg) ? pTemplateEx->cDlgItems : pTemplate->cdit;
+
+ HDWP hDwp = BeginDeferWindowPos(itemCount);
+ for (int i = 0; i < itemCount; i++) {
+ if ((UINT_PTR)pWord & 2) pWord++; //dword align
+
+ if (extendedDlg) {
+ START_OF_DLGITEMTEMPLATEEX *pItemEx = (START_OF_DLGITEMTEMPLATEEX*)pWord;
+ pWord = (PWORD)(pItemEx + 1);
+
+ urc.wId = pItemEx->id;
+ urc.rcItem.left = pItemEx->x; urc.rcItem.top = pItemEx->y;
+ urc.rcItem.right = urc.rcItem.left + pItemEx->cx; urc.rcItem.bottom = urc.rcItem.top + pItemEx->cy;
+ }
+ else {
+ DLGITEMTEMPLATE *pItem = (DLGITEMTEMPLATE*)pWord;
+ pWord = (PWORD)(pItem + 1);
+
+ urc.wId = pItem->id;
+ urc.rcItem.left = pItem->x; urc.rcItem.top = pItem->y;
+ urc.rcItem.right = urc.rcItem.left + pItem->cx; urc.rcItem.bottom = urc.rcItem.top + pItem->cy;
+ }
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class
+ pWord += 1 + (1 + *pWord) / 2; //creation data
+
+ if (urc.wId == 65535) // using this breaks the dwp, so just ignore it
+ continue;
+
+ MapDialogRect(hwndDlg, &urc.rcItem);
+ int procResult = (pfnResizer)(hwndDlg, lParam, &urc);
+ if (procResult & RD_ANCHORX_RIGHT) {
+ urc.rcItem.left += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx;
+ urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx;
+ }
+ else if (procResult & RD_ANCHORX_WIDTH)
+ urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx;
+ else if (procResult & RD_ANCHORX_CENTRE) {
+ urc.rcItem.left += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2;
+ urc.rcItem.right += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2;
+ }
+ if (procResult & RD_ANCHORY_BOTTOM) {
+ urc.rcItem.top += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy;
+ urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy;
+ }
+ else if (procResult & RD_ANCHORY_HEIGHT)
+ urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy;
+ else if (procResult & RD_ANCHORY_CENTRE) {
+ urc.rcItem.top += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2;
+ urc.rcItem.bottom += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2;
+ }
+
+ HWND hCtrl = GetDlgItem(hwndDlg, urc.wId);
+ if (hCtrl != nullptr && urc.wId != UINT(-1))
+ hDwp = DeferWindowPos(hDwp, hCtrl, nullptr, urc.rcItem.left, urc.rcItem.top, urc.rcItem.right - urc.rcItem.left, urc.rcItem.bottom - urc.rcItem.top, SWP_NOZORDER);
+ }
+ EndDeferWindowPos(hDwp);
+ return 0;
+}
diff --git a/src/mir_core/src/Windows/subclass.cpp b/src/mir_core/src/Windows/subclass.cpp index ecc56206af..8ca02d78e3 100644 --- a/src/mir_core/src/Windows/subclass.cpp +++ b/src/mir_core/src/Windows/subclass.cpp @@ -1,201 +1,201 @@ -/* -Copyright (C) 2012-22 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" - -struct MSubclassData -{ - HWND m_hWnd; - - int m_iHooks; - WNDPROC *m_hooks; - WNDPROC m_origWndProc; - - ~MSubclassData() - { - free(m_hooks); - } -}; - -static LIST<MSubclassData> arSubclass(10, HandleKeySortT); - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK MSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hwnd); - if (p != nullptr) { - if (p->m_iHooks) - return p->m_hooks[p->m_iHooks-1](hwnd, uMsg, wParam, lParam); - - return p->m_origWndProc(hwnd, uMsg, wParam, lParam); - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// This is for wine: it return wrong WNDPROC for edit control in some cases. -#ifdef WIN64 -#define STD_WND_PROC_ADDR_MASK 0x7FFF00000 -#else -#define STD_WND_PROC_ADDR_MASK 0xFFFF0000 -#endif - -MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc) -{ - if (hWnd == nullptr) - return; - - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) { - p = new MSubclassData; - p->m_hWnd = hWnd; - p->m_origWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); - if (((SIZE_T)p->m_origWndProc & STD_WND_PROC_ADDR_MASK) == STD_WND_PROC_ADDR_MASK) { /* XXX: fix me. Wine fix. */ - p->m_origWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC); - if (((SIZE_T)p->m_origWndProc & 0x7FFF0000) == 0x7FFF0000) /* Delay crash. */ - p->m_origWndProc = DefWindowProc; - } - p->m_iHooks = 0; - p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); - arSubclass.insert(p); - } - else { - for (int i=0; i < p->m_iHooks; i++) - if (p->m_hooks[i] == wndProc) - return; - - void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); - if (tmp == nullptr) - return; - - p->m_hooks = (WNDPROC *)tmp; - } - - p->m_hooks[p->m_iHooks++] = wndProc; -} - -MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) { - p = new MSubclassData; - p->m_hWnd = hWnd; - p->m_origWndProc = oldWndProc; - p->m_iHooks = 0; - p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); - arSubclass.insert(p); - - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); - } - else { - for (int i=0; i < p->m_iHooks; i++) - if (p->m_hooks[i] == wndProc) - return; - - void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); - if (tmp == nullptr) - return; - - p->m_hooks = (WNDPROC *)tmp; - } - - p->m_hooks[p->m_iHooks++] = wndProc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void removeHook(MSubclassData *p, int idx) -{ - // untie hook from a window to prevent calling mir_callNextSubclass from saveProc - for (int i = idx + 1; i < p->m_iHooks; i++) - p->m_hooks[i-1] = p->m_hooks[i]; - p->m_iHooks--; -} - -static WNDPROC finalizeSubclassing(HWND hWnd, MSubclassData *p) -{ - WNDPROC saveProc = p->m_origWndProc; - arSubclass.remove(p); - delete p; - - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)saveProc); - return saveProc; -} - -MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) - return; - - for (int i = 0; i < p->m_iHooks; i++) { - if (p->m_hooks[i] == wndProc) { - removeHook(p, i); - i--; - } - } - - if (p->m_iHooks == 0) - finalizeSubclassing(hWnd, p); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) - return DefWindowProc(hWnd, uMsg, wParam, lParam); - - for (int i = p->m_iHooks - 1; i >= 0; i--) { - if (p->m_hooks[i] != wndProc) - continue; - - // next hook exists, call it - if (i != 0) - return p->m_hooks[i-1](hWnd, uMsg, wParam, lParam); - - // last hook called, ping the default window procedure - if (uMsg != WM_NCDESTROY) - return p->m_origWndProc(hWnd, uMsg, wParam, lParam); - - WNDPROC saveProc = finalizeSubclassing(hWnd, p); - return saveProc(hWnd, uMsg, wParam, lParam); - } - - // invalid / closed hook - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst) -{ - for (auto &it : arSubclass.rev_iter()) { - for (int j = 0; j < it->m_iHooks; j++) { - if (GetInstByAddress(it->m_hooks[j]) == hInst) { - removeHook(it, j); - j--; - } - } - - if (it->m_iHooks == 0) - finalizeSubclassing(it->m_hWnd, it); - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+struct MSubclassData
+{
+ HWND m_hWnd;
+
+ int m_iHooks;
+ WNDPROC *m_hooks;
+ WNDPROC m_origWndProc;
+
+ ~MSubclassData()
+ {
+ free(m_hooks);
+ }
+};
+
+static LIST<MSubclassData> arSubclass(10, HandleKeySortT);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK MSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hwnd);
+ if (p != nullptr) {
+ if (p->m_iHooks)
+ return p->m_hooks[p->m_iHooks-1](hwnd, uMsg, wParam, lParam);
+
+ return p->m_origWndProc(hwnd, uMsg, wParam, lParam);
+ }
+
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This is for wine: it return wrong WNDPROC for edit control in some cases.
+#ifdef WIN64
+#define STD_WND_PROC_ADDR_MASK 0x7FFF00000
+#else
+#define STD_WND_PROC_ADDR_MASK 0xFFFF0000
+#endif
+
+MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc)
+{
+ if (hWnd == nullptr)
+ return;
+
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr) {
+ p = new MSubclassData;
+ p->m_hWnd = hWnd;
+ p->m_origWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc);
+ if (((SIZE_T)p->m_origWndProc & STD_WND_PROC_ADDR_MASK) == STD_WND_PROC_ADDR_MASK) { /* XXX: fix me. Wine fix. */
+ p->m_origWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC);
+ if (((SIZE_T)p->m_origWndProc & 0x7FFF0000) == 0x7FFF0000) /* Delay crash. */
+ p->m_origWndProc = DefWindowProc;
+ }
+ p->m_iHooks = 0;
+ p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC));
+ arSubclass.insert(p);
+ }
+ else {
+ for (int i=0; i < p->m_iHooks; i++)
+ if (p->m_hooks[i] == wndProc)
+ return;
+
+ void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC));
+ if (tmp == nullptr)
+ return;
+
+ p->m_hooks = (WNDPROC *)tmp;
+ }
+
+ p->m_hooks[p->m_iHooks++] = wndProc;
+}
+
+MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr) {
+ p = new MSubclassData;
+ p->m_hWnd = hWnd;
+ p->m_origWndProc = oldWndProc;
+ p->m_iHooks = 0;
+ p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC));
+ arSubclass.insert(p);
+
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc);
+ }
+ else {
+ for (int i=0; i < p->m_iHooks; i++)
+ if (p->m_hooks[i] == wndProc)
+ return;
+
+ void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC));
+ if (tmp == nullptr)
+ return;
+
+ p->m_hooks = (WNDPROC *)tmp;
+ }
+
+ p->m_hooks[p->m_iHooks++] = wndProc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void removeHook(MSubclassData *p, int idx)
+{
+ // untie hook from a window to prevent calling mir_callNextSubclass from saveProc
+ for (int i = idx + 1; i < p->m_iHooks; i++)
+ p->m_hooks[i-1] = p->m_hooks[i];
+ p->m_iHooks--;
+}
+
+static WNDPROC finalizeSubclassing(HWND hWnd, MSubclassData *p)
+{
+ WNDPROC saveProc = p->m_origWndProc;
+ arSubclass.remove(p);
+ delete p;
+
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)saveProc);
+ return saveProc;
+}
+
+MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr)
+ return;
+
+ for (int i = 0; i < p->m_iHooks; i++) {
+ if (p->m_hooks[i] == wndProc) {
+ removeHook(p, i);
+ i--;
+ }
+ }
+
+ if (p->m_iHooks == 0)
+ finalizeSubclassing(hWnd, p);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr)
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+
+ for (int i = p->m_iHooks - 1; i >= 0; i--) {
+ if (p->m_hooks[i] != wndProc)
+ continue;
+
+ // next hook exists, call it
+ if (i != 0)
+ return p->m_hooks[i-1](hWnd, uMsg, wParam, lParam);
+
+ // last hook called, ping the default window procedure
+ if (uMsg != WM_NCDESTROY)
+ return p->m_origWndProc(hWnd, uMsg, wParam, lParam);
+
+ WNDPROC saveProc = finalizeSubclassing(hWnd, p);
+ return saveProc(hWnd, uMsg, wParam, lParam);
+ }
+
+ // invalid / closed hook
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst)
+{
+ for (auto &it : arSubclass.rev_iter()) {
+ for (int j = 0; j < it->m_iHooks; j++) {
+ if (GetInstByAddress(it->m_hooks[j]) == hInst) {
+ removeHook(it, j);
+ j--;
+ }
+ }
+
+ if (it->m_iHooks == 0)
+ finalizeSubclassing(it->m_hWnd, it);
+ }
+}
diff --git a/src/mir_core/src/Windows/threads.cpp b/src/mir_core/src/Windows/threads.cpp index 6af62d3633..c33a80faed 100644 --- a/src/mir_core/src/Windows/threads.cpp +++ b/src/mir_core/src/Windows/threads.cpp @@ -1,400 +1,400 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -#include <m_netlib.h> - -static mir_cs csThreads; - -///////////////////////////////////////////////////////////////////////////////////////// -// APC and mutex functions - -static void __stdcall DummyAPCFunc(ULONG_PTR) -{ - /* called in the context of thread that cleared it's APC queue */ - return; -} - -static int MirandaWaitForMutex(HANDLE hEvent) -{ - // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result - for (;;) { - uint32_t rc = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE); - if (rc == WAIT_OBJECT_0 + 1) { - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - if (msg.hwnd != nullptr && IsDialogMessage(msg.hwnd, &msg)) /* Wine fix. */ - continue; - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - else if (rc == WAIT_OBJECT_0) { // got object - return 1; - } - else if (rc == WAIT_ABANDONED_0 || rc == WAIT_FAILED) - return 0; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// exception handling - -static uint32_t __cdecl sttDefaultFilter(uint32_t, EXCEPTION_POINTERS*) -{ - return EXCEPTION_EXECUTE_HANDLER; -} - -pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter; - -MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter() -{ - return pMirandaExceptFilter; -} - -MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter _mirandaExceptFilter) -{ - pfnExceptionFilter oldOne = pMirandaExceptFilter; - if (_mirandaExceptFilter != nullptr) - pMirandaExceptFilter = _mirandaExceptFilter; - return oldOne; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// thread support functions - -struct THREAD_WAIT_ENTRY -{ - uint32_t dwThreadId; // valid if hThread isn't signalled - HANDLE hThread; - HINSTANCE hOwner; - void *pObject, *pEntryPoint; -}; - -static LIST<THREAD_WAIT_ENTRY> threads(10, NumericKeySortT); - -struct FORK_ARG -{ - HANDLE hEvent, hThread; - union - { - pThreadFunc threadcode; - pThreadFuncEx threadcodeex; - }; - void *arg, *owner; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// forkthread - starts a new thread - -DWORD WINAPI forkthread_r(void *arg) -{ - FORK_ARG *fa = (FORK_ARG*)arg; - pThreadFunc callercode = fa->threadcode; - void *cookie = fa->arg; - HANDLE hThread = fa->hThread; - Thread_Push((HINSTANCE)callercode); - SetEvent(fa->hEvent); - - callercode(cookie); - - CloseHandle(hThread); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - Thread_Pop(); - return 0; -} - -MIR_CORE_DLL(HANDLE) mir_forkthread(void(__cdecl *threadcode)(void*), void *arg) -{ - FORK_ARG fa; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - fa.threadcode = threadcode; - fa.arg = arg; - - DWORD threadID; - fa.hThread = CreateThread(nullptr, 0, forkthread_r, &fa, 0, &threadID); - if (fa.hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - CloseHandle(fa.hEvent); - return fa.hThread; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// forkthreadex - starts a new thread with the extended info and returns the thread id - -DWORD WINAPI forkthreadex_r(void *arg) -{ - struct FORK_ARG *fa = (struct FORK_ARG *)arg; - pThreadFuncEx threadcode = fa->threadcodeex; - pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex; - void *cookie = fa->arg; - void *owner = fa->owner; - unsigned long rc = 0; - - Thread_Push((HINSTANCE)threadcode, fa->owner); - SetEvent(fa->hEvent); - if (owner) - rc = threadcodeex(owner, cookie); - else - rc = threadcode(cookie); - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - Thread_Pop(); - return rc; -} - -MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void* arg, unsigned *pThreadID) -{ - struct FORK_ARG fa = {}; - fa.threadcodeex = aFunc; - fa.arg = arg; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - - DWORD threadID = 0; - HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); - if (hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - if (pThreadID != nullptr) - *pThreadID = threadID; - - CloseHandle(fa.hEvent); - return hThread; -} - -MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID) -{ - struct FORK_ARG fa = {}; - fa.threadcodeex = (pThreadFuncEx)aFunc; - fa.arg = arg; - fa.owner = owner; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - - DWORD threadID = 0; - HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); - if (hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - if (pThreadID != nullptr) - *pThreadID = threadID; - - CloseHandle(fa.hEvent); - return hThread; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void __cdecl KillObjectThreadsWorker(void* owner) -{ - HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE)); - int threadCount = 0; - { - mir_cslock lck(csThreads); - - for (auto &it : threads) - if (it->pObject == owner) - threadPool[threadCount++] = it->hThread; - } - - // is there anything to kill? - if (threadCount == 0) - return; - - // wait'em all - if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT) - return; - - // forcibly kill all remaining threads after 5 secs - mir_cslock lck(csThreads); - auto T = threads.rev_iter(); - for (auto &it : T) { - if (it->pObject == owner) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing object thread %s:%08x", szModuleName, it->dwThreadId); - TerminateThread(it->hThread, 9999); - CloseHandle(it->hThread); - mir_free(it); - threads.removeItem(&it); - } - } -} - -MIR_CORE_DLL(void) KillObjectThreads(void* owner) -{ - if (owner == nullptr) - return; - - uint32_t dwTicks = GetTickCount() + 6000; - HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner); - while (GetTickCount() < dwTicks) { - int res = MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE); - if (res == WAIT_OBJECT_0 || res == WAIT_FAILED) - break; - - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD) -{ - mir_cslock lck(csThreads); - for (auto &p : threads) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing thread %s:%08x (%p)", szModuleName, p->dwThreadId, p->pEntryPoint); - TerminateThread(p->hThread, 9999); - CloseHandle(p->hThread); - mir_free(p); - } - - threads.destroy(); - - SetEvent(hThreadQueueEmpty); -} - -MIR_CORE_DLL(void) Thread_Wait(void) -{ - // acquire the list and wake up any alertable threads - { - mir_cslock lck(csThreads); - for (auto &p : threads) - QueueUserAPC(DummyAPCFunc, p->hThread, 0); - } - - // give all unclosed threads 5 seconds to close - SetTimer(nullptr, 0, 5000, KillAllThreads); - - // wait til the thread list is empty - MirandaWaitForMutex(hThreadQueueEmpty); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG); -#define ThreadQuerySetWin32StartAddress 9 - -static void* GetCurrentThreadEntryPoint() -{ - pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread"); - if (NtQueryInformationThread == nullptr) - return nullptr; - - HANDLE hDupHandle, hCurrentProcess = GetCurrentProcess(); - if (!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) { - SetLastError(ERROR_ACCESS_DENIED); - return nullptr; - } - - DWORD_PTR dwStartAddress; - LONG ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), nullptr); - CloseHandle(hDupHandle); - - return (ntStatus != ERROR_SUCCESS) ? nullptr : (void*)dwStartAddress; -} - -MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner) -{ - ResetEvent(hThreadQueueEmpty); // thread list is not empty - - mir_cslock lck(csThreads); - - THREAD_WAIT_ENTRY *p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY)); - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS); - p->dwThreadId = GetCurrentThreadId(); - p->pObject = pOwner; - p->pEntryPoint = hInst; - - // try to find the precise match - CMPluginBase &pPlugin = GetPluginByInstance(hInst); - if (pPlugin.getInst() == hInst) - p->hOwner = hInst; - else - GetInstByAddress((hInst != nullptr) ? (PVOID)hInst : GetCurrentThreadEntryPoint()); - - threads.insert(p); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(INT_PTR) Thread_Pop() -{ - uint32_t dwThreadId = GetCurrentThreadId(); - - mir_cslock lck(csThreads); - THREAD_WAIT_ENTRY *p = threads.find((THREAD_WAIT_ENTRY*)&dwThreadId); - if (p == nullptr) - return 1; - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - CloseHandle(p->hThread); - threads.remove(p); - mir_free(p); - - if (!threads.getCount()) { - threads.destroy(); - SetEvent(hThreadQueueEmpty); // thread list is empty now - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -const uint32_t MS_VC_EXCEPTION=0x406D1388; - -#pragma pack(push,8) -typedef struct tagTHREADNAME_INFO -{ - uint32_t dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - uint32_t dwThreadID; // Thread ID (-1=caller thread). - uint32_t dwFlags; // Reserved for future use, must be zero. -} THREADNAME_INFO; -#pragma pack(pop) - -MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = GetCurrentThreadId(); - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER) - {} -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+#include <m_netlib.h>
+
+static mir_cs csThreads;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// APC and mutex functions
+
+static void __stdcall DummyAPCFunc(ULONG_PTR)
+{
+ /* called in the context of thread that cleared it's APC queue */
+ return;
+}
+
+static int MirandaWaitForMutex(HANDLE hEvent)
+{
+ // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result
+ for (;;) {
+ uint32_t rc = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
+ if (rc == WAIT_OBJECT_0 + 1) {
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ if (msg.hwnd != nullptr && IsDialogMessage(msg.hwnd, &msg)) /* Wine fix. */
+ continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ else if (rc == WAIT_OBJECT_0) { // got object
+ return 1;
+ }
+ else if (rc == WAIT_ABANDONED_0 || rc == WAIT_FAILED)
+ return 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// exception handling
+
+static uint32_t __cdecl sttDefaultFilter(uint32_t, EXCEPTION_POINTERS*)
+{
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter;
+
+MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter()
+{
+ return pMirandaExceptFilter;
+}
+
+MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter _mirandaExceptFilter)
+{
+ pfnExceptionFilter oldOne = pMirandaExceptFilter;
+ if (_mirandaExceptFilter != nullptr)
+ pMirandaExceptFilter = _mirandaExceptFilter;
+ return oldOne;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// thread support functions
+
+struct THREAD_WAIT_ENTRY
+{
+ uint32_t dwThreadId; // valid if hThread isn't signalled
+ HANDLE hThread;
+ HINSTANCE hOwner;
+ void *pObject, *pEntryPoint;
+};
+
+static LIST<THREAD_WAIT_ENTRY> threads(10, NumericKeySortT);
+
+struct FORK_ARG
+{
+ HANDLE hEvent, hThread;
+ union
+ {
+ pThreadFunc threadcode;
+ pThreadFuncEx threadcodeex;
+ };
+ void *arg, *owner;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// forkthread - starts a new thread
+
+DWORD WINAPI forkthread_r(void *arg)
+{
+ FORK_ARG *fa = (FORK_ARG*)arg;
+ pThreadFunc callercode = fa->threadcode;
+ void *cookie = fa->arg;
+ HANDLE hThread = fa->hThread;
+ Thread_Push((HINSTANCE)callercode);
+ SetEvent(fa->hEvent);
+
+ callercode(cookie);
+
+ CloseHandle(hThread);
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ Thread_Pop();
+ return 0;
+}
+
+MIR_CORE_DLL(HANDLE) mir_forkthread(void(__cdecl *threadcode)(void*), void *arg)
+{
+ FORK_ARG fa;
+ fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ fa.threadcode = threadcode;
+ fa.arg = arg;
+
+ DWORD threadID;
+ fa.hThread = CreateThread(nullptr, 0, forkthread_r, &fa, 0, &threadID);
+ if (fa.hThread != nullptr)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ CloseHandle(fa.hEvent);
+ return fa.hThread;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// forkthreadex - starts a new thread with the extended info and returns the thread id
+
+DWORD WINAPI forkthreadex_r(void *arg)
+{
+ struct FORK_ARG *fa = (struct FORK_ARG *)arg;
+ pThreadFuncEx threadcode = fa->threadcodeex;
+ pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex;
+ void *cookie = fa->arg;
+ void *owner = fa->owner;
+ unsigned long rc = 0;
+
+ Thread_Push((HINSTANCE)threadcode, fa->owner);
+ SetEvent(fa->hEvent);
+ if (owner)
+ rc = threadcodeex(owner, cookie);
+ else
+ rc = threadcode(cookie);
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ Thread_Pop();
+ return rc;
+}
+
+MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void* arg, unsigned *pThreadID)
+{
+ struct FORK_ARG fa = {};
+ fa.threadcodeex = aFunc;
+ fa.arg = arg;
+ fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ DWORD threadID = 0;
+ HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID);
+ if (hThread != nullptr)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ if (pThreadID != nullptr)
+ *pThreadID = threadID;
+
+ CloseHandle(fa.hEvent);
+ return hThread;
+}
+
+MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID)
+{
+ struct FORK_ARG fa = {};
+ fa.threadcodeex = (pThreadFuncEx)aFunc;
+ fa.arg = arg;
+ fa.owner = owner;
+ fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ DWORD threadID = 0;
+ HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID);
+ if (hThread != nullptr)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ if (pThreadID != nullptr)
+ *pThreadID = threadID;
+
+ CloseHandle(fa.hEvent);
+ return hThread;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void __cdecl KillObjectThreadsWorker(void* owner)
+{
+ HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE));
+ int threadCount = 0;
+ {
+ mir_cslock lck(csThreads);
+
+ for (auto &it : threads)
+ if (it->pObject == owner)
+ threadPool[threadCount++] = it->hThread;
+ }
+
+ // is there anything to kill?
+ if (threadCount == 0)
+ return;
+
+ // wait'em all
+ if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT)
+ return;
+
+ // forcibly kill all remaining threads after 5 secs
+ mir_cslock lck(csThreads);
+ auto T = threads.rev_iter();
+ for (auto &it : T) {
+ if (it->pObject == owner) {
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName));
+ Netlib_Logf(nullptr, "Killing object thread %s:%08x", szModuleName, it->dwThreadId);
+ TerminateThread(it->hThread, 9999);
+ CloseHandle(it->hThread);
+ mir_free(it);
+ threads.removeItem(&it);
+ }
+ }
+}
+
+MIR_CORE_DLL(void) KillObjectThreads(void* owner)
+{
+ if (owner == nullptr)
+ return;
+
+ uint32_t dwTicks = GetTickCount() + 6000;
+ HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner);
+ while (GetTickCount() < dwTicks) {
+ int res = MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE);
+ if (res == WAIT_OBJECT_0 || res == WAIT_FAILED)
+ break;
+
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD)
+{
+ mir_cslock lck(csThreads);
+ for (auto &p : threads) {
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName));
+ Netlib_Logf(nullptr, "Killing thread %s:%08x (%p)", szModuleName, p->dwThreadId, p->pEntryPoint);
+ TerminateThread(p->hThread, 9999);
+ CloseHandle(p->hThread);
+ mir_free(p);
+ }
+
+ threads.destroy();
+
+ SetEvent(hThreadQueueEmpty);
+}
+
+MIR_CORE_DLL(void) Thread_Wait(void)
+{
+ // acquire the list and wake up any alertable threads
+ {
+ mir_cslock lck(csThreads);
+ for (auto &p : threads)
+ QueueUserAPC(DummyAPCFunc, p->hThread, 0);
+ }
+
+ // give all unclosed threads 5 seconds to close
+ SetTimer(nullptr, 0, 5000, KillAllThreads);
+
+ // wait til the thread list is empty
+ MirandaWaitForMutex(hThreadQueueEmpty);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG);
+#define ThreadQuerySetWin32StartAddress 9
+
+static void* GetCurrentThreadEntryPoint()
+{
+ pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread");
+ if (NtQueryInformationThread == nullptr)
+ return nullptr;
+
+ HANDLE hDupHandle, hCurrentProcess = GetCurrentProcess();
+ if (!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) {
+ SetLastError(ERROR_ACCESS_DENIED);
+ return nullptr;
+ }
+
+ DWORD_PTR dwStartAddress;
+ LONG ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), nullptr);
+ CloseHandle(hDupHandle);
+
+ return (ntStatus != ERROR_SUCCESS) ? nullptr : (void*)dwStartAddress;
+}
+
+MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner)
+{
+ ResetEvent(hThreadQueueEmpty); // thread list is not empty
+
+ mir_cslock lck(csThreads);
+
+ THREAD_WAIT_ENTRY *p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY));
+ DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ p->dwThreadId = GetCurrentThreadId();
+ p->pObject = pOwner;
+ p->pEntryPoint = hInst;
+
+ // try to find the precise match
+ CMPluginBase &pPlugin = GetPluginByInstance(hInst);
+ if (pPlugin.getInst() == hInst)
+ p->hOwner = hInst;
+ else
+ GetInstByAddress((hInst != nullptr) ? (PVOID)hInst : GetCurrentThreadEntryPoint());
+
+ threads.insert(p);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(INT_PTR) Thread_Pop()
+{
+ uint32_t dwThreadId = GetCurrentThreadId();
+
+ mir_cslock lck(csThreads);
+ THREAD_WAIT_ENTRY *p = threads.find((THREAD_WAIT_ENTRY*)&dwThreadId);
+ if (p == nullptr)
+ return 1;
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ CloseHandle(p->hThread);
+ threads.remove(p);
+ mir_free(p);
+
+ if (!threads.getCount()) {
+ threads.destroy();
+ SetEvent(hThreadQueueEmpty); // thread list is empty now
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+const uint32_t MS_VC_EXCEPTION=0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ uint32_t dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ uint32_t dwThreadID; // Thread ID (-1=caller thread).
+ uint32_t dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = GetCurrentThreadId();
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {}
+}
diff --git a/src/mir_core/src/Windows/timezones.cpp b/src/mir_core/src/Windows/timezones.cpp index 63d026a3eb..13e2fddba9 100644 --- a/src/mir_core/src/Windows/timezones.cpp +++ b/src/mir_core/src/Windows/timezones.cpp @@ -1,593 +1,593 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -implements services to handle location - based timezones, instead of -simple UTC offsets. -*/ - -#include "../stdafx.h" - -typedef uint32_t (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi); -static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation; - -struct REG_TZI_FORMAT -{ - LONG Bias; - LONG StandardBias; - LONG DaylightBias; - SYSTEMTIME StandardDate; - SYSTEMTIME DaylightDate; -}; - -#define MIM_TZ_DISPLAYLEN 128 - -struct MIM_TIMEZONE -{ - unsigned hash; - int offset; - - wchar_t tszName[MIM_TZ_NAMELEN]; // windows name for the time zone - wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs) - // every hour should be sufficient. - TIME_ZONE_INFORMATION tzi; - - static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2) - { return p2->tzi.Bias - p1->tzi.Bias; - } -}; - -struct TZ_INT_INFO -{ - uint32_t timestamp; // last time updated - MIM_TIMEZONE myTZ; // set to my own timezone -}; - -static TZ_INT_INFO myInfo; - -static OBJLIST<MIM_TIMEZONE> g_timezones(55, NumericKeySortT); -static LIST<MIM_TIMEZONE> g_timezonesBias(55, MIM_TIMEZONE::compareBias); - -// KB167296 -void UnixTimeToFileTime(mir_time ts, LPFILETIME pft) -{ - unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64; - pft->dwLowDateTime = (uint32_t)ll; - pft->dwHighDateTime = ll >> 32; -} - -mir_time FileTimeToUnixTime(LPFILETIME pft) -{ - unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime; - ll -= 116444736000000000i64; - return (mir_time)(ll / 10000000); -} - -void FormatTime(const SYSTEMTIME *st, const wchar_t *szFormat, wchar_t *szDest, size_t cbDest) -{ - if (szDest == nullptr || cbDest == 0) return; - - CMStringW tszTemp; - - for (const wchar_t* pFormat = szFormat; *pFormat; ++pFormat) { - uint32_t fmt = 0; - bool date = false, iso = false; - switch (*pFormat) { - case 't': - fmt = TIME_NOSECONDS; - date = false; - break; - - case 's': - fmt = 0; - date = false; - break; - - case 'm': - fmt = TIME_NOMINUTESORSECONDS; - date = false; - break; - - case 'd': - fmt = DATE_SHORTDATE; - date = true; - break; - - case 'D': - fmt = DATE_LONGDATE; - date = true; - break; - - case 'I': - iso = true; - break; - - default: - tszTemp.AppendChar(*pFormat); - continue; - } - - wchar_t dateTimeStr[64]; - if (iso) - tszTemp.AppendFormat(L"%d-%02d-%02dT%02d:%02d:%02dZ", st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond); - else if (date) { - GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); - tszTemp.Append(dateTimeStr); - } - else { - GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); - tszTemp.Append(dateTimeStr); - } - } - - wcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE); -} - -MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st) -{ - if (st == nullptr) return 1; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == UTC_TIME_HANDLE) - GetSystemTime(st); - else if (tz && tz != &myInfo.myTZ) { - SYSTEMTIME sto; - GetSystemTime(&sto); - return !SystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st); - } - else - GetLocalTime(st); - - return 0; -} - -MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr) - return myInfo.myTZ.tszName; - if (tz == UTC_TIME_HANDLE) - return L"UTC"; - - return tz->tszName; -} - -MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname) -{ - for (auto &tz : g_timezonesBias) - if (!mir_wstrcmp(tz->tszName, TZname)) - return tz->szDisplay; - - return L""; -} - -static void CalcTsOffset(MIM_TIMEZONE *tz) -{ - SYSTEMTIME st, stl; - GetSystemTime(&st); - - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - mir_time ts1 = FileTimeToUnixTime(&ft); - - if (!SystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl)) - return; - - SystemTimeToFileTime(&stl, &ft); - mir_time ts2 = FileTimeToUnixTime(&ft); - - tz->offset = ts2 - ts1; -} - -static bool IsSameTime(MIM_TIMEZONE *tz) -{ - SYSTEMTIME st, stl; - - if (tz == &myInfo.myTZ) - return true; - - TimeZone_GetTimeZoneTime(tz, &stl); - TimeZone_GetTimeZoneTime(nullptr, &st); - - return st.wHour == stl.wHour && st.wMinute == stl.wMinute; -} - -MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags) -{ - if (tszName == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (!(dwFlags & TZF_PLF_CB)) - if (mir_wstrcmp(myInfo.myTZ.tszName, tszName) == 0) - return (dwFlags & TZF_DIFONLY) ? nullptr : &myInfo.myTZ; - - MIM_TIMEZONE tzsearch; - tzsearch.hash = mir_hashstrT(tszName); - - MIM_TIMEZONE *tz = g_timezones.find(&tzsearch); - if (tz == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (dwFlags & TZF_DIFONLY) - return IsSameTime(tz) ? nullptr : tz; - - return tz; -} - -MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags) -{ - if (hContact == NULL && szModule == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (szModule == nullptr) szModule = "UserInfo"; - - DBVARIANT dbv; - if (!db_get_ws(hContact, szModule, "TzName", &dbv)) { - HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); - db_free(&dbv); - if (res) return res; - } - - signed char timezone = (signed char)db_get_b(hContact, szModule, "Timezone", -1); - if (timezone == -1) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (!db_get_ws(hContact, szProto, "TzName", &dbv)) { - HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); - db_free(&dbv); - if (res) return res; - } - timezone = (signed char)db_get_b(hContact, szProto, "Timezone", -1); - } - - if (timezone != -1) { - MIM_TIMEZONE tzsearch; - tzsearch.tzi.Bias = timezone * 30; - if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias) { - if (dwFlags & TZF_DIFONLY) return nullptr; - return &myInfo.myTZ; - } - - int i = g_timezonesBias.getIndex(&tzsearch); - while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i; - - int delta = LONG_MAX; - for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j) { - int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth); - if (delta1 <= delta) { - delta = delta1; - i = j; - } - } - - if (i >= 0) { - MIM_TIMEZONE *tz = g_timezonesBias[i]; - return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? nullptr : tz; - } - } - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; -} - -MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ) -{ - if (szModule == nullptr) szModule = "UserInfo"; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz) { - db_set_ws(hContact, szModule, "TzName", tz->tszName); - db_set_b(hContact, szModule, "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30)); - } - else { - db_unset(hContact, szModule, "TzName"); - db_unset(hContact, szModule, "Timezone"); - } -} - -MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) - return 1; - - if (tz == nullptr) - tz = &myInfo.myTZ; - - SYSTEMTIME st; - if (TimeZone_GetTimeZoneTime(tz, &st)) - return 1; - - FormatTime(&st, szFormat, szDest, cbDest); - return 0; -} - -MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time ts, SYSTEMTIME *dest, uint32_t dwFlags) -{ - if (dest == nullptr) - return 2; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE *)hTZ; - if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) { - memset(dest, 0, sizeof(SYSTEMTIME)); - return 1; - } - - if (tz == nullptr) - tz = &myInfo.myTZ; - - FILETIME ft; - if (tz == UTC_TIME_HANDLE) - UnixTimeToFileTime(ts, &ft); - else { - if (tz->offset == INT_MIN) - CalcTsOffset(tz); - - UnixTimeToFileTime(ts + tz->offset, &ft); - } - - FileTimeToSystemTime(&ft, dest); - return 0; -} - -MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) -{ - SYSTEMTIME st; - if (!TimeZone_GetSystemTime(hTZ, ts, &st, dwFlags)) - FormatTime(&st, szFormat, szDest, cbDest); - return 0; -} - -MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - return tz ? &tz->tzi : &myInfo.myTZ.tzi; -} - -MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr) - tz = &myInfo.myTZ; - - if (tz == UTC_TIME_HANDLE) - return ts; - - if (tz->offset == INT_MIN) - CalcTsOffset(tz); - - return ts + tz->offset; -} - -/////////////////////////////////////////////////////////////////////////////// - -struct ListMessages -{ - UINT addStr, getSel, setSel, getData, setData; -}; - -static const ListMessages lbMessages = { LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA }; -static const ListMessages cbMessages = { CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA }; - -static const ListMessages* GetListMessages(HWND hWnd, uint32_t dwFlags) -{ - if (hWnd == nullptr) - return nullptr; - - if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB))) { - wchar_t tszClassName[128]; - GetClassName(hWnd, tszClassName, _countof(tszClassName)); - if (!mir_wstrcmpi(tszClassName, L"COMBOBOX")) - dwFlags |= TZF_PLF_CB; - else if (!mir_wstrcmpi(tszClassName, L"LISTBOX")) - dwFlags |= TZF_PLF_LB; - } - - if (dwFlags & TZF_PLF_CB) - return &cbMessages; - if (dwFlags & TZF_PLF_LB) - return &lbMessages; - return nullptr; -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg == nullptr) - return -1; - - if (szModule == nullptr) - szModule = "UserInfo"; - - int iSelection = 0; - ptrW tszName(db_get_wsa(hContact, szModule, "TzName")); - if (tszName != NULL) { - unsigned hash = mir_hashstrT(tszName); - for (auto &it : g_timezonesBias) { - if (hash == it->hash) { - iSelection = g_timezonesBias.indexOf(&it) + 1; - break; - } - } - } - else { - signed char cBias = db_get_b(hContact, szModule, "Timezone", -100); - if (cBias != -100) { - int iBias = cBias * 30; - for (auto &it : g_timezonesBias) { - if (iBias == it->tzi.Bias) { - iSelection = g_timezonesBias.indexOf(&it) + 1; - break; - } - } - } - } - - SendMessage(hWnd, lstMsg->setSel, iSelection, 0); - return iSelection; -} - -MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg == nullptr) - return 0; - - SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateW_LP(L"<unspecified>")); - - for (auto &it : g_timezonesBias) { - SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)it->szDisplay); - SendMessage(hWnd, lstMsg->setData, g_timezonesBias.indexOf(&it) + 1, (LPARAM)it); - } - - return TimeZone_SelectListItem(hContact, szModule, hWnd, dwFlags); -} - -MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - if (szModule == nullptr) szModule = "UserInfo"; - - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg) { - LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0); - if (offset > 0) { - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0); - if ((INT_PTR)tz != CB_ERR && tz != nullptr) - TimeZone_StoreByContact(hContact, szModule, tz); - } - else TimeZone_StoreByContact(hContact, szModule, nullptr); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t timeVal) -{ - return TimeZone_UtcToLocal(nullptr, (mir_time)timeVal); -} - -MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest) -{ - wchar_t *szTemp = (wchar_t*)alloca(cchDest*sizeof(wchar_t)); - TimeZone_PrintTimeStamp(nullptr, timeVal, _A2T(szFormat), szTemp, cchDest, 0); - WideCharToMultiByte(CP_ACP, 0, szTemp, -1, szDest, (int)cchDest, nullptr, nullptr); - return szDest; -} - -MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest) -{ - TimeZone_PrintTimeStamp(nullptr, timeVal, wszFormat, wszDest, cchDest, 0); - return wszDest; -} - -/////////////////////////////////////////////////////////////////////////////// - -void GetLocalizedString(HKEY hSubKey, const wchar_t *szName, wchar_t *szBuf, uint32_t cbLen) -{ - DWORD dwLength = cbLen * sizeof(wchar_t); - RegQueryValueEx(hSubKey, szName, nullptr, nullptr, (unsigned char *)szBuf, &dwLength); - szBuf[min(dwLength / sizeof(wchar_t), cbLen - 1)] = 0; -} - -void RecalculateTime(void) -{ - GetTimeZoneInformation(&myInfo.myTZ.tzi); - myInfo.timestamp = time(0); - myInfo.myTZ.offset = INT_MIN; - - bool found = false; - DYNAMIC_TIME_ZONE_INFORMATION dtzi; - - if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID) { - wcsncpy_s(myInfo.myTZ.tszName, dtzi.TimeZoneKeyName, _TRUNCATE); - found = true; - } - - for (auto &tz : g_timezones) { - if (tz->offset != INT_MIN) - tz->offset = INT_MIN; - - if (!found) { - if (!mir_wstrcmp(tz->tzi.StandardName, myInfo.myTZ.tzi.StandardName) || !mir_wstrcmp(tz->tzi.DaylightName, myInfo.myTZ.tzi.DaylightName)) { - wcsncpy_s(myInfo.myTZ.tszName, tz->tszName, _TRUNCATE); - found = true; - } - } - } -} - -void InitTimeZones(void) -{ - REG_TZI_FORMAT tzi; - HKEY hKey; - - const wchar_t *tszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"; - - /* - * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with - * the registry key name, so finding our own time zone later will be MUCH easier for - * localized systems or systems with a MUI pack installed - */ - if (IsWinVerVistaPlus()) - pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(L"kernel32"), "GetDynamicTimeZoneInformation"); - - if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) { - uint32_t dwIndex = 0; - HKEY hSubKey; - wchar_t tszName[MIM_TZ_NAMELEN]; - - DWORD dwSize = _countof(tszName); - while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, nullptr, nullptr, nullptr, nullptr)) { - if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey)) { - dwSize = sizeof(tszName); - - DWORD dwLength = sizeof(tzi); - if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, L"TZI", nullptr, nullptr, (unsigned char *)&tzi, &dwLength)) - continue; - - MIM_TIMEZONE *tz = new MIM_TIMEZONE; - - tz->tzi.Bias = tzi.Bias; - tz->tzi.StandardDate = tzi.StandardDate; - tz->tzi.StandardBias = tzi.StandardBias; - tz->tzi.DaylightDate = tzi.DaylightDate; - tz->tzi.DaylightBias = tzi.DaylightBias; - - mir_wstrcpy(tz->tszName, tszName); - tz->hash = mir_hashstrT(tszName); - tz->offset = INT_MIN; - - GetLocalizedString(hSubKey, L"Display", tz->szDisplay, _countof(tz->szDisplay)); - GetLocalizedString(hSubKey, L"Std", tz->tzi.StandardName, _countof(tz->tzi.StandardName)); - GetLocalizedString(hSubKey, L"Dlt", tz->tzi.DaylightName, _countof(tz->tzi.DaylightName)); - - g_timezones.insert(tz); - g_timezonesBias.insert(tz); - - RegCloseKey(hSubKey); - } - dwSize = _countof(tszName); - } - RegCloseKey(hKey); - } - - RecalculateTime(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+implements services to handle location - based timezones, instead of
+simple UTC offsets.
+*/
+
+#include "../stdafx.h"
+
+typedef uint32_t (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi);
+static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation;
+
+struct REG_TZI_FORMAT
+{
+ LONG Bias;
+ LONG StandardBias;
+ LONG DaylightBias;
+ SYSTEMTIME StandardDate;
+ SYSTEMTIME DaylightDate;
+};
+
+#define MIM_TZ_DISPLAYLEN 128
+
+struct MIM_TIMEZONE
+{
+ unsigned hash;
+ int offset;
+
+ wchar_t tszName[MIM_TZ_NAMELEN]; // windows name for the time zone
+ wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs)
+ // every hour should be sufficient.
+ TIME_ZONE_INFORMATION tzi;
+
+ static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2)
+ { return p2->tzi.Bias - p1->tzi.Bias;
+ }
+};
+
+struct TZ_INT_INFO
+{
+ uint32_t timestamp; // last time updated
+ MIM_TIMEZONE myTZ; // set to my own timezone
+};
+
+static TZ_INT_INFO myInfo;
+
+static OBJLIST<MIM_TIMEZONE> g_timezones(55, NumericKeySortT);
+static LIST<MIM_TIMEZONE> g_timezonesBias(55, MIM_TIMEZONE::compareBias);
+
+// KB167296
+void UnixTimeToFileTime(mir_time ts, LPFILETIME pft)
+{
+ unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64;
+ pft->dwLowDateTime = (uint32_t)ll;
+ pft->dwHighDateTime = ll >> 32;
+}
+
+mir_time FileTimeToUnixTime(LPFILETIME pft)
+{
+ unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime;
+ ll -= 116444736000000000i64;
+ return (mir_time)(ll / 10000000);
+}
+
+void FormatTime(const SYSTEMTIME *st, const wchar_t *szFormat, wchar_t *szDest, size_t cbDest)
+{
+ if (szDest == nullptr || cbDest == 0) return;
+
+ CMStringW tszTemp;
+
+ for (const wchar_t* pFormat = szFormat; *pFormat; ++pFormat) {
+ uint32_t fmt = 0;
+ bool date = false, iso = false;
+ switch (*pFormat) {
+ case 't':
+ fmt = TIME_NOSECONDS;
+ date = false;
+ break;
+
+ case 's':
+ fmt = 0;
+ date = false;
+ break;
+
+ case 'm':
+ fmt = TIME_NOMINUTESORSECONDS;
+ date = false;
+ break;
+
+ case 'd':
+ fmt = DATE_SHORTDATE;
+ date = true;
+ break;
+
+ case 'D':
+ fmt = DATE_LONGDATE;
+ date = true;
+ break;
+
+ case 'I':
+ iso = true;
+ break;
+
+ default:
+ tszTemp.AppendChar(*pFormat);
+ continue;
+ }
+
+ wchar_t dateTimeStr[64];
+ if (iso)
+ tszTemp.AppendFormat(L"%d-%02d-%02dT%02d:%02d:%02dZ", st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond);
+ else if (date) {
+ GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr));
+ tszTemp.Append(dateTimeStr);
+ }
+ else {
+ GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr));
+ tszTemp.Append(dateTimeStr);
+ }
+ }
+
+ wcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE);
+}
+
+MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st)
+{
+ if (st == nullptr) return 1;
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == UTC_TIME_HANDLE)
+ GetSystemTime(st);
+ else if (tz && tz != &myInfo.myTZ) {
+ SYSTEMTIME sto;
+ GetSystemTime(&sto);
+ return !SystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st);
+ }
+ else
+ GetLocalTime(st);
+
+ return 0;
+}
+
+MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == nullptr)
+ return myInfo.myTZ.tszName;
+ if (tz == UTC_TIME_HANDLE)
+ return L"UTC";
+
+ return tz->tszName;
+}
+
+MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname)
+{
+ for (auto &tz : g_timezonesBias)
+ if (!mir_wstrcmp(tz->tszName, TZname))
+ return tz->szDisplay;
+
+ return L"";
+}
+
+static void CalcTsOffset(MIM_TIMEZONE *tz)
+{
+ SYSTEMTIME st, stl;
+ GetSystemTime(&st);
+
+ FILETIME ft;
+ SystemTimeToFileTime(&st, &ft);
+ mir_time ts1 = FileTimeToUnixTime(&ft);
+
+ if (!SystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl))
+ return;
+
+ SystemTimeToFileTime(&stl, &ft);
+ mir_time ts2 = FileTimeToUnixTime(&ft);
+
+ tz->offset = ts2 - ts1;
+}
+
+static bool IsSameTime(MIM_TIMEZONE *tz)
+{
+ SYSTEMTIME st, stl;
+
+ if (tz == &myInfo.myTZ)
+ return true;
+
+ TimeZone_GetTimeZoneTime(tz, &stl);
+ TimeZone_GetTimeZoneTime(nullptr, &st);
+
+ return st.wHour == stl.wHour && st.wMinute == stl.wMinute;
+}
+
+MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags)
+{
+ if (tszName == nullptr)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+
+ if (!(dwFlags & TZF_PLF_CB))
+ if (mir_wstrcmp(myInfo.myTZ.tszName, tszName) == 0)
+ return (dwFlags & TZF_DIFONLY) ? nullptr : &myInfo.myTZ;
+
+ MIM_TIMEZONE tzsearch;
+ tzsearch.hash = mir_hashstrT(tszName);
+
+ MIM_TIMEZONE *tz = g_timezones.find(&tzsearch);
+ if (tz == nullptr)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+
+ if (dwFlags & TZF_DIFONLY)
+ return IsSameTime(tz) ? nullptr : tz;
+
+ return tz;
+}
+
+MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags)
+{
+ if (hContact == NULL && szModule == nullptr)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+
+ if (szModule == nullptr) szModule = "UserInfo";
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, szModule, "TzName", &dbv)) {
+ HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags);
+ db_free(&dbv);
+ if (res) return res;
+ }
+
+ signed char timezone = (signed char)db_get_b(hContact, szModule, "Timezone", -1);
+ if (timezone == -1) {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (!db_get_ws(hContact, szProto, "TzName", &dbv)) {
+ HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags);
+ db_free(&dbv);
+ if (res) return res;
+ }
+ timezone = (signed char)db_get_b(hContact, szProto, "Timezone", -1);
+ }
+
+ if (timezone != -1) {
+ MIM_TIMEZONE tzsearch;
+ tzsearch.tzi.Bias = timezone * 30;
+ if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias) {
+ if (dwFlags & TZF_DIFONLY) return nullptr;
+ return &myInfo.myTZ;
+ }
+
+ int i = g_timezonesBias.getIndex(&tzsearch);
+ while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i;
+
+ int delta = LONG_MAX;
+ for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j) {
+ int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth);
+ if (delta1 <= delta) {
+ delta = delta1;
+ i = j;
+ }
+ }
+
+ if (i >= 0) {
+ MIM_TIMEZONE *tz = g_timezonesBias[i];
+ return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? nullptr : tz;
+ }
+ }
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+}
+
+MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ)
+{
+ if (szModule == nullptr) szModule = "UserInfo";
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz) {
+ db_set_ws(hContact, szModule, "TzName", tz->tszName);
+ db_set_b(hContact, szModule, "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30));
+ }
+ else {
+ db_unset(hContact, szModule, "TzName");
+ db_unset(hContact, szModule, "Timezone");
+ }
+}
+
+MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)))
+ return 1;
+
+ if (tz == nullptr)
+ tz = &myInfo.myTZ;
+
+ SYSTEMTIME st;
+ if (TimeZone_GetTimeZoneTime(tz, &st))
+ return 1;
+
+ FormatTime(&st, szFormat, szDest, cbDest);
+ return 0;
+}
+
+MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time ts, SYSTEMTIME *dest, uint32_t dwFlags)
+{
+ if (dest == nullptr)
+ return 2;
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE *)hTZ;
+ if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) {
+ memset(dest, 0, sizeof(SYSTEMTIME));
+ return 1;
+ }
+
+ if (tz == nullptr)
+ tz = &myInfo.myTZ;
+
+ FILETIME ft;
+ if (tz == UTC_TIME_HANDLE)
+ UnixTimeToFileTime(ts, &ft);
+ else {
+ if (tz->offset == INT_MIN)
+ CalcTsOffset(tz);
+
+ UnixTimeToFileTime(ts + tz->offset, &ft);
+ }
+
+ FileTimeToSystemTime(&ft, dest);
+ return 0;
+}
+
+MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags)
+{
+ SYSTEMTIME st;
+ if (!TimeZone_GetSystemTime(hTZ, ts, &st, dwFlags))
+ FormatTime(&st, szFormat, szDest, cbDest);
+ return 0;
+}
+
+MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ return tz ? &tz->tzi : &myInfo.myTZ.tzi;
+}
+
+MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == nullptr)
+ tz = &myInfo.myTZ;
+
+ if (tz == UTC_TIME_HANDLE)
+ return ts;
+
+ if (tz->offset == INT_MIN)
+ CalcTsOffset(tz);
+
+ return ts + tz->offset;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct ListMessages
+{
+ UINT addStr, getSel, setSel, getData, setData;
+};
+
+static const ListMessages lbMessages = { LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA };
+static const ListMessages cbMessages = { CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA };
+
+static const ListMessages* GetListMessages(HWND hWnd, uint32_t dwFlags)
+{
+ if (hWnd == nullptr)
+ return nullptr;
+
+ if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB))) {
+ wchar_t tszClassName[128];
+ GetClassName(hWnd, tszClassName, _countof(tszClassName));
+ if (!mir_wstrcmpi(tszClassName, L"COMBOBOX"))
+ dwFlags |= TZF_PLF_CB;
+ else if (!mir_wstrcmpi(tszClassName, L"LISTBOX"))
+ dwFlags |= TZF_PLF_LB;
+ }
+
+ if (dwFlags & TZF_PLF_CB)
+ return &cbMessages;
+ if (dwFlags & TZF_PLF_LB)
+ return &lbMessages;
+ return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags)
+{
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == nullptr)
+ return -1;
+
+ if (szModule == nullptr)
+ szModule = "UserInfo";
+
+ int iSelection = 0;
+ ptrW tszName(db_get_wsa(hContact, szModule, "TzName"));
+ if (tszName != NULL) {
+ unsigned hash = mir_hashstrT(tszName);
+ for (auto &it : g_timezonesBias) {
+ if (hash == it->hash) {
+ iSelection = g_timezonesBias.indexOf(&it) + 1;
+ break;
+ }
+ }
+ }
+ else {
+ signed char cBias = db_get_b(hContact, szModule, "Timezone", -100);
+ if (cBias != -100) {
+ int iBias = cBias * 30;
+ for (auto &it : g_timezonesBias) {
+ if (iBias == it->tzi.Bias) {
+ iSelection = g_timezonesBias.indexOf(&it) + 1;
+ break;
+ }
+ }
+ }
+ }
+
+ SendMessage(hWnd, lstMsg->setSel, iSelection, 0);
+ return iSelection;
+}
+
+MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags)
+{
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == nullptr)
+ return 0;
+
+ SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateW_LP(L"<unspecified>"));
+
+ for (auto &it : g_timezonesBias) {
+ SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)it->szDisplay);
+ SendMessage(hWnd, lstMsg->setData, g_timezonesBias.indexOf(&it) + 1, (LPARAM)it);
+ }
+
+ return TimeZone_SelectListItem(hContact, szModule, hWnd, dwFlags);
+}
+
+MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags)
+{
+ if (szModule == nullptr) szModule = "UserInfo";
+
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg) {
+ LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0);
+ if (offset > 0) {
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0);
+ if ((INT_PTR)tz != CB_ERR && tz != nullptr)
+ TimeZone_StoreByContact(hContact, szModule, tz);
+ }
+ else TimeZone_StoreByContact(hContact, szModule, nullptr);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t timeVal)
+{
+ return TimeZone_UtcToLocal(nullptr, (mir_time)timeVal);
+}
+
+MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest)
+{
+ wchar_t *szTemp = (wchar_t*)alloca(cchDest*sizeof(wchar_t));
+ TimeZone_PrintTimeStamp(nullptr, timeVal, _A2T(szFormat), szTemp, cchDest, 0);
+ WideCharToMultiByte(CP_ACP, 0, szTemp, -1, szDest, (int)cchDest, nullptr, nullptr);
+ return szDest;
+}
+
+MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest)
+{
+ TimeZone_PrintTimeStamp(nullptr, timeVal, wszFormat, wszDest, cchDest, 0);
+ return wszDest;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GetLocalizedString(HKEY hSubKey, const wchar_t *szName, wchar_t *szBuf, uint32_t cbLen)
+{
+ DWORD dwLength = cbLen * sizeof(wchar_t);
+ RegQueryValueEx(hSubKey, szName, nullptr, nullptr, (unsigned char *)szBuf, &dwLength);
+ szBuf[min(dwLength / sizeof(wchar_t), cbLen - 1)] = 0;
+}
+
+void RecalculateTime(void)
+{
+ GetTimeZoneInformation(&myInfo.myTZ.tzi);
+ myInfo.timestamp = time(0);
+ myInfo.myTZ.offset = INT_MIN;
+
+ bool found = false;
+ DYNAMIC_TIME_ZONE_INFORMATION dtzi;
+
+ if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID) {
+ wcsncpy_s(myInfo.myTZ.tszName, dtzi.TimeZoneKeyName, _TRUNCATE);
+ found = true;
+ }
+
+ for (auto &tz : g_timezones) {
+ if (tz->offset != INT_MIN)
+ tz->offset = INT_MIN;
+
+ if (!found) {
+ if (!mir_wstrcmp(tz->tzi.StandardName, myInfo.myTZ.tzi.StandardName) || !mir_wstrcmp(tz->tzi.DaylightName, myInfo.myTZ.tzi.DaylightName)) {
+ wcsncpy_s(myInfo.myTZ.tszName, tz->tszName, _TRUNCATE);
+ found = true;
+ }
+ }
+ }
+}
+
+void InitTimeZones(void)
+{
+ REG_TZI_FORMAT tzi;
+ HKEY hKey;
+
+ const wchar_t *tszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones";
+
+ /*
+ * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with
+ * the registry key name, so finding our own time zone later will be MUCH easier for
+ * localized systems or systems with a MUI pack installed
+ */
+ if (IsWinVerVistaPlus())
+ pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(L"kernel32"), "GetDynamicTimeZoneInformation");
+
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) {
+ uint32_t dwIndex = 0;
+ HKEY hSubKey;
+ wchar_t tszName[MIM_TZ_NAMELEN];
+
+ DWORD dwSize = _countof(tszName);
+ while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, nullptr, nullptr, nullptr, nullptr)) {
+ if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey)) {
+ dwSize = sizeof(tszName);
+
+ DWORD dwLength = sizeof(tzi);
+ if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, L"TZI", nullptr, nullptr, (unsigned char *)&tzi, &dwLength))
+ continue;
+
+ MIM_TIMEZONE *tz = new MIM_TIMEZONE;
+
+ tz->tzi.Bias = tzi.Bias;
+ tz->tzi.StandardDate = tzi.StandardDate;
+ tz->tzi.StandardBias = tzi.StandardBias;
+ tz->tzi.DaylightDate = tzi.DaylightDate;
+ tz->tzi.DaylightBias = tzi.DaylightBias;
+
+ mir_wstrcpy(tz->tszName, tszName);
+ tz->hash = mir_hashstrT(tszName);
+ tz->offset = INT_MIN;
+
+ GetLocalizedString(hSubKey, L"Display", tz->szDisplay, _countof(tz->szDisplay));
+ GetLocalizedString(hSubKey, L"Std", tz->tzi.StandardName, _countof(tz->tzi.StandardName));
+ GetLocalizedString(hSubKey, L"Dlt", tz->tzi.DaylightName, _countof(tz->tzi.DaylightName));
+
+ g_timezones.insert(tz);
+ g_timezonesBias.insert(tz);
+
+ RegCloseKey(hSubKey);
+ }
+ dwSize = _countof(tszName);
+ }
+ RegCloseKey(hKey);
+ }
+
+ RecalculateTime();
+}
diff --git a/src/mir_core/src/Windows/windowlist.cpp b/src/mir_core/src/Windows/windowlist.cpp index 6b6e28ad66..616db3df5c 100644 --- a/src/mir_core/src/Windows/windowlist.cpp +++ b/src/mir_core/src/Windows/windowlist.cpp @@ -1,105 +1,105 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -struct TWindowListItem -{ - TWindowListItem(UINT_PTR _param, HWND _wnd) : - param(_param), - hWnd(_wnd) - {} - - UINT_PTR param; - HWND hWnd; -}; - -struct TWindowList : public OBJLIST<TWindowListItem> -{ - TWindowList() : - OBJLIST<TWindowListItem>(10, NumericKeySortT) - {} -}; - -MIR_CORE_DLL(MWindowList) WindowList_Create(void) -{ - return new TWindowList(); -} - -MIR_CORE_DLL(void) WindowList_Destroy(MWindowList &hList) -{ - delete hList; - hList = nullptr; -} - -MIR_CORE_DLL(int) WindowList_Add(MWindowList hList, HWND hwnd, UINT_PTR param) -{ - if (hList == nullptr) - return 1; - - hList->insert(new TWindowListItem(param, hwnd)); - return 0; -} - -MIR_CORE_DLL(int) WindowList_Remove(MWindowList hList, HWND hwnd) -{ - if (hList == nullptr) return 1; - - for (auto &it : *hList) - if (it->hWnd == hwnd) { - hList->removeItem(&it); - return 0; - } - - return 1; -} - -MIR_CORE_DLL(HWND) WindowList_Find(MWindowList hList, UINT_PTR param) -{ - if (hList == nullptr) - return nullptr; - - TWindowListItem *p = hList->find((TWindowListItem*)¶m); - return (p == nullptr) ? nullptr : p->hWnd; -} - -MIR_CORE_DLL(int) WindowList_Broadcast(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (hList == nullptr) - return 0; - - for (auto &it : hList->rev_iter()) - SendMessage(it->hWnd, message, wParam, lParam); - return 0; -} - -MIR_CORE_DLL(int) WindowList_BroadcastAsync(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (hList == nullptr) - return 0; - - for (auto &it : hList->rev_iter()) - PostMessage(it->hWnd, message, wParam, lParam); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+struct TWindowListItem
+{
+ TWindowListItem(UINT_PTR _param, HWND _wnd) :
+ param(_param),
+ hWnd(_wnd)
+ {}
+
+ UINT_PTR param;
+ HWND hWnd;
+};
+
+struct TWindowList : public OBJLIST<TWindowListItem>
+{
+ TWindowList() :
+ OBJLIST<TWindowListItem>(10, NumericKeySortT)
+ {}
+};
+
+MIR_CORE_DLL(MWindowList) WindowList_Create(void)
+{
+ return new TWindowList();
+}
+
+MIR_CORE_DLL(void) WindowList_Destroy(MWindowList &hList)
+{
+ delete hList;
+ hList = nullptr;
+}
+
+MIR_CORE_DLL(int) WindowList_Add(MWindowList hList, HWND hwnd, UINT_PTR param)
+{
+ if (hList == nullptr)
+ return 1;
+
+ hList->insert(new TWindowListItem(param, hwnd));
+ return 0;
+}
+
+MIR_CORE_DLL(int) WindowList_Remove(MWindowList hList, HWND hwnd)
+{
+ if (hList == nullptr) return 1;
+
+ for (auto &it : *hList)
+ if (it->hWnd == hwnd) {
+ hList->removeItem(&it);
+ return 0;
+ }
+
+ return 1;
+}
+
+MIR_CORE_DLL(HWND) WindowList_Find(MWindowList hList, UINT_PTR param)
+{
+ if (hList == nullptr)
+ return nullptr;
+
+ TWindowListItem *p = hList->find((TWindowListItem*)¶m);
+ return (p == nullptr) ? nullptr : p->hWnd;
+}
+
+MIR_CORE_DLL(int) WindowList_Broadcast(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (hList == nullptr)
+ return 0;
+
+ for (auto &it : hList->rev_iter())
+ SendMessage(it->hWnd, message, wParam, lParam);
+ return 0;
+}
+
+MIR_CORE_DLL(int) WindowList_BroadcastAsync(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (hList == nullptr)
+ return 0;
+
+ for (auto &it : hList->rev_iter())
+ PostMessage(it->hWnd, message, wParam, lParam);
+ return 0;
+}
diff --git a/src/mir_core/src/Windows/winutil.cpp b/src/mir_core/src/Windows/winutil.cpp index 7a25523482..1d18041529 100644 --- a/src/mir_core/src/Windows/winutil.cpp +++ b/src/mir_core/src/Windows/winutil.cpp @@ -1,177 +1,177 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(int) Utils_SaveWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix) -{ - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(hwnd, &wp); - - char szSettingName[64]; - mir_snprintf(szSettingName, "%sx", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.left); - - mir_snprintf(szSettingName, "%sy", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.top); - - mir_snprintf(szSettingName, "%swidth", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left); - - mir_snprintf(szSettingName, "%sheight", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_CORE_DLL(int) Utils_RestoreWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix, int flags) -{ - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(hwnd, &wp); - - char szSettingName[64]; - mir_snprintf(szSettingName, "%sx", szNamePrefix); - int x = db_get_dw(hContact, szModule, szSettingName, -1); - if (x == -1) - return 1; - - mir_snprintf(szSettingName, "%sy", szNamePrefix); - int y = (int)db_get_dw(hContact, szModule, szSettingName, -1); - - if (flags & RWPF_NOSIZE) - OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top); - else { - wp.rcNormalPosition.left = x; - wp.rcNormalPosition.top = y; - - mir_snprintf(szSettingName, "%swidth", szNamePrefix); - wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(hContact, szModule, szSettingName, -1); - - mir_snprintf(szSettingName, "%sheight", szNamePrefix); - wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(hContact, szModule, szSettingName, -1); - } - wp.flags = 0; - if (flags & RWPF_HIDDEN) - wp.showCmd = SW_HIDE; - if (flags & RWPF_NOACTIVATE) - wp.showCmd = SW_SHOWNOACTIVATE; - - if (!(flags & RWPF_NOMOVE)) - Utils_AssertInsideScreen(&wp.rcNormalPosition); - - SetWindowPlacement(hwnd, &wp); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) Utils_AssertInsideScreen(RECT *rc) -{ - if (rc == nullptr) - return -1; - - RECT rcScreen; - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE); - if (MonitorFromRect(rc, MONITOR_DEFAULTTONULL)) - return 0; - - MONITORINFO mi = { 0 }; - HMONITOR hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMonitor, &mi)) - rcScreen = mi.rcWork; - - if (rc->top >= rcScreen.bottom) - OffsetRect(rc, 0, rcScreen.bottom - rc->bottom); - else if (rc->bottom <= rcScreen.top) - OffsetRect(rc, 0, rcScreen.top - rc->top); - if (rc->left >= rcScreen.right) - OffsetRect(rc, rcScreen.right - rc->right, 0); - else if (rc->right <= rcScreen.left) - OffsetRect(rc, rcScreen.left - rc->left, 0); - - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static NONCLIENTMETRICSW ncm = {}; - -MIR_CORE_DLL(int) Utils_CorrectFontSize(int size) -{ - if (!g_bEnableDpiAware) - return size; - - if (!ncm.cbSize) { - ncm.cbSize = sizeof(ncm); - SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); - } - - return size * ncm.lfMessageFont.lfHeight / -12; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Utils_ClipboardCopy(const char *pszText) -{ - size_t cbLen = mir_strlen(pszText); - if (!cbLen) - return; - - if (!OpenClipboard(nullptr)) - return; - - EmptyClipboard(); - - HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbLen); - if (hData) { - mir_strcpy((char *)GlobalLock(hData), pszText); - GlobalUnlock(hData); - SetClipboardData(CF_TEXT, hData); - } - CloseClipboard(); -} - -MIR_CORE_DLL(void) Utils_ClipboardCopy(const wchar_t *pwszText) -{ - size_t cbLen = mir_wstrlen(pwszText); - if (!cbLen) - return; - - if (!OpenClipboard(nullptr)) - return; - - EmptyClipboard(); - - HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (cbLen + 1) * sizeof(wchar_t)); - if (hData) { - mir_wstrcpy((wchar_t *)GlobalLock(hData), pwszText); - GlobalUnlock(hData); - SetClipboardData(CF_UNICODETEXT, hData); - } - CloseClipboard(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+MIR_CORE_DLL(int) Utils_SaveWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix)
+{
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(hwnd, &wp);
+
+ char szSettingName[64];
+ mir_snprintf(szSettingName, "%sx", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.left);
+
+ mir_snprintf(szSettingName, "%sy", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.top);
+
+ mir_snprintf(szSettingName, "%swidth", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left);
+
+ mir_snprintf(szSettingName, "%sheight", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+EXTERN_C MIR_CORE_DLL(int) Utils_RestoreWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix, int flags)
+{
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(hwnd, &wp);
+
+ char szSettingName[64];
+ mir_snprintf(szSettingName, "%sx", szNamePrefix);
+ int x = db_get_dw(hContact, szModule, szSettingName, -1);
+ if (x == -1)
+ return 1;
+
+ mir_snprintf(szSettingName, "%sy", szNamePrefix);
+ int y = (int)db_get_dw(hContact, szModule, szSettingName, -1);
+
+ if (flags & RWPF_NOSIZE)
+ OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top);
+ else {
+ wp.rcNormalPosition.left = x;
+ wp.rcNormalPosition.top = y;
+
+ mir_snprintf(szSettingName, "%swidth", szNamePrefix);
+ wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(hContact, szModule, szSettingName, -1);
+
+ mir_snprintf(szSettingName, "%sheight", szNamePrefix);
+ wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(hContact, szModule, szSettingName, -1);
+ }
+ wp.flags = 0;
+ if (flags & RWPF_HIDDEN)
+ wp.showCmd = SW_HIDE;
+ if (flags & RWPF_NOACTIVATE)
+ wp.showCmd = SW_SHOWNOACTIVATE;
+
+ if (!(flags & RWPF_NOMOVE))
+ Utils_AssertInsideScreen(&wp.rcNormalPosition);
+
+ SetWindowPlacement(hwnd, &wp);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) Utils_AssertInsideScreen(RECT *rc)
+{
+ if (rc == nullptr)
+ return -1;
+
+ RECT rcScreen;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE);
+ if (MonitorFromRect(rc, MONITOR_DEFAULTTONULL))
+ return 0;
+
+ MONITORINFO mi = { 0 };
+ HMONITOR hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST);
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMonitor, &mi))
+ rcScreen = mi.rcWork;
+
+ if (rc->top >= rcScreen.bottom)
+ OffsetRect(rc, 0, rcScreen.bottom - rc->bottom);
+ else if (rc->bottom <= rcScreen.top)
+ OffsetRect(rc, 0, rcScreen.top - rc->top);
+ if (rc->left >= rcScreen.right)
+ OffsetRect(rc, rcScreen.right - rc->right, 0);
+ else if (rc->right <= rcScreen.left)
+ OffsetRect(rc, rcScreen.left - rc->left, 0);
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static NONCLIENTMETRICSW ncm = {};
+
+MIR_CORE_DLL(int) Utils_CorrectFontSize(int size)
+{
+ if (!g_bEnableDpiAware)
+ return size;
+
+ if (!ncm.cbSize) {
+ ncm.cbSize = sizeof(ncm);
+ SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE);
+ }
+
+ return size * ncm.lfMessageFont.lfHeight / -12;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) Utils_ClipboardCopy(const char *pszText)
+{
+ size_t cbLen = mir_strlen(pszText);
+ if (!cbLen)
+ return;
+
+ if (!OpenClipboard(nullptr))
+ return;
+
+ EmptyClipboard();
+
+ HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbLen);
+ if (hData) {
+ mir_strcpy((char *)GlobalLock(hData), pszText);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_TEXT, hData);
+ }
+ CloseClipboard();
+}
+
+MIR_CORE_DLL(void) Utils_ClipboardCopy(const wchar_t *pwszText)
+{
+ size_t cbLen = mir_wstrlen(pwszText);
+ if (!cbLen)
+ return;
+
+ if (!OpenClipboard(nullptr))
+ return;
+
+ EmptyClipboard();
+
+ HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (cbLen + 1) * sizeof(wchar_t));
+ if (hData) {
+ mir_wstrcpy((wchar_t *)GlobalLock(hData), pwszText);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_UNICODETEXT, hData);
+ }
+ CloseClipboard();
+}
diff --git a/src/mir_core/src/Windows/winver.cpp b/src/mir_core/src/Windows/winver.cpp index 29a6fa7f3c..f52567ccab 100644 --- a/src/mir_core/src/Windows/winver.cpp +++ b/src/mir_core/src/Windows/winver.cpp @@ -1,372 +1,372 @@ -/* -Copyright (C) 2012-22 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" - -#ifndef _WIN32_WINNT_WIN8 -#define _WIN32_WINNT_WIN8 0x0602 // Windows 8 -#endif - -#ifndef _WIN32_WINNT_WINBLUE -#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1 -#endif - -#ifndef _WIN32_WINNT_WIN10 -#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10 -#endif - -static int dwWinVer; - -void InitWinver() -{ - uint32_t dwVer = LOWORD(GetVersion()); - dwWinVer = MAKEWORD(HIBYTE(dwVer), LOBYTE(dwVer)); -} - -MIR_CORE_DLL(BOOL) IsWinVerVistaPlus() -{ - return dwWinVer >= _WIN32_WINNT_VISTA; -} - -MIR_CORE_DLL(BOOL) IsWinVer7Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN7; -} - -MIR_CORE_DLL(BOOL) IsWinVer8Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN8; -} - -MIR_CORE_DLL(BOOL) IsWinVer81Plus() -{ - return dwWinVer >= _WIN32_WINNT_WINBLUE; -} - -MIR_CORE_DLL(BOOL) IsWinVer10Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN10; -} - -MIR_CORE_DLL(BOOL) IsFullScreen() -{ - RECT rcScreen = { 0 }; - - rcScreen.right = GetSystemMetrics(SM_CXSCREEN); - rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); - - HMONITOR hMon = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMon, &mi)) - rcScreen = mi.rcMonitor; - - HWND hWndDesktop = GetDesktopWindow(); - HWND hWndShell = GetShellWindow(); - - // check foregroundwindow - HWND hWnd = GetForegroundWindow(); - if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell) { - wchar_t tszClassName[128] = L""; - GetClassName(hWnd, tszClassName, _countof(tszClassName)); - if (wcscmp(tszClassName, L"WorkerW")) { - RECT rect, rectw, recti; - GetWindowRect(hWnd, &rectw); - - GetClientRect(hWnd, &rect); - ClientToScreen(hWnd, (LPPOINT)&rect); - ClientToScreen(hWnd, (LPPOINT)&rect.right); - - if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) && EqualRect(&recti, &rcScreen)) - return true; - } - } - - return false; -} - -MIR_CORE_DLL(BOOL) IsWorkstationLocked(void) -{ - HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP); - if (hDesk == nullptr) - return true; - - wchar_t tszName[100]; - DWORD cbName; - BOOL bLocked = (!GetUserObjectInformation(hDesk, UOI_NAME, tszName, _countof(tszName), &cbName) || mir_wstrcmpi(tszName, L"default") != 0); - CloseDesktop(hDesk); - return bLocked; -} - -MIR_CORE_DLL(BOOL) IsTerminalDisconnected(void) -{ - PVOID pBuffer = nullptr; - DWORD pBytesReturned = 0; - BOOL result = FALSE; - - if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&pBuffer, &pBytesReturned)) - if (*(PDWORD)pBuffer == WTSDisconnected) - result = TRUE; - - if (pBuffer) - WTSFreeMemory(pBuffer); - return result; -} - -MIR_CORE_DLL(BOOL) IsScreenSaverRunning(void) -{ - BOOL rc = FALSE; - SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE); - return rc != 0; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize) -{ - if (buf == nullptr || bufSize == 0) - return false; - - mir_snprintf(buf, bufSize, "Windows NT %d.%d", HIBYTE(dwWinVer), LOBYTE(dwWinVer)); - return true; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef PRODUCT_CORE_N // Win8 -#define PRODUCT_CORE_SINGLELANGUAGE 0x00000064 -#define PRODUCT_PROFESSIONAL_WMC 0x00000067 -#endif - -typedef BOOL(WINAPI *PGPI)(uint32_t, uint32_t, uint32_t, uint32_t, PDWORD); -typedef LPCSTR(WINAPI *WGV)(void); - -MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize) -{ - if (buf == nullptr || bufSize == 0) - return 0; - - buf[0] = 0; - - OSVERSIONINFOEX osvi = { 0 }; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi); - if (!bOsVersionInfoEx) { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (!GetVersionEx((OSVERSIONINFO*)&osvi)) - return false; - } - - if (VER_PLATFORM_WIN32_NT != osvi.dwPlatformId || osvi.dwMajorVersion <= 4) - return false; - - SYSTEM_INFO sysInfo = { 0 }; - GetNativeSystemInfo(&sysInfo); - - CMStringA ret("Microsoft "); - - // Test for the specific product. - if (osvi.dwMajorVersion >= 6) { - if (osvi.dwMajorVersion == 10) { - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 10 "); - else - ret.Append("Windows Server 10 "); - } - else switch (osvi.dwMinorVersion) { - case 0: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows Vista "); - else - ret.Append("Windows Server 2008 "); - break; - - case 1: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 7 "); - else - ret.Append("Windows Server 2008 R2 "); - break; - - case 2: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 8 "); - else - ret.Append("Windows Server 2012 "); - break; - - case 3: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 8.1 "); - else - ret.Append("Windows Server 2012 R2 "); - break; - } - - DWORD dwType = 0; - HMODULE hKernel = GetModuleHandle(L"kernel32.dll"); - PGPI pGPI = (PGPI)GetProcAddress(hKernel, "GetProductInfo"); - if (pGPI != nullptr) - pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); - - switch (dwType) { - case PRODUCT_ULTIMATE: - ret.Append("Ultimate Edition"); - break; - case PRODUCT_PROFESSIONAL: - ret.Append("Professional Edition"); - break; - case PRODUCT_PROFESSIONAL_WMC: - ret.Append("Professional Edition with Media Center"); - break; - case PRODUCT_HOME_PREMIUM: - ret.Append("Home Premium Edition"); - break; - case PRODUCT_HOME_BASIC: - ret.Append("Home Basic Edition"); - break; - case PRODUCT_ENTERPRISE: - ret.Append("Enterprise Edition"); - break; - case PRODUCT_BUSINESS: - ret.Append("Business Edition"); - break; - case PRODUCT_STARTER: - ret.Append("Starter Edition"); - break; - case PRODUCT_CLUSTER_SERVER: - ret.Append("Cluster Server Edition"); - break; - case PRODUCT_DATACENTER_SERVER: - ret.Append("Datacenter Edition"); - break; - case PRODUCT_DATACENTER_SERVER_CORE: - ret.Append("Datacenter Edition (core installation)"); - break; - case PRODUCT_ENTERPRISE_SERVER: - ret.Append("Enterprise Edition"); - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - ret.Append("Enterprise Edition (core installation)"); - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - ret.Append("Enterprise Edition for Itanium-based Systems"); - break; - case PRODUCT_SMALLBUSINESS_SERVER: - ret.Append("Small Business Server"); - break; - case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: - ret.Append("Small Business Server Premium Edition"); - break; - case PRODUCT_STANDARD_SERVER: - ret.Append("Standard Edition"); - break; - case PRODUCT_STANDARD_SERVER_CORE: - ret.Append("Standard Edition (core installation)"); - break; - case PRODUCT_WEB_SERVER: - ret.Append("Web Server Edition"); - break; - case PRODUCT_CORE_SINGLELANGUAGE: - ret.Append("Home Single Language"); - break; - } - if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - ret.Append(", 64-bit"); - else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) - ret.Append(", 32-bit"); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { - if (GetSystemMetrics(SM_SERVERR2)) - ret.Append("Windows Server 2003 R2, "); - else if (osvi.wSuiteMask == VER_SUITE_STORAGE_SERVER) - ret.Append("Windows Storage Server 2003"); - else if (osvi.wSuiteMask == VER_SUITE_WH_SERVER) - ret.Append("Windows Home Server"); - else if (osvi.wProductType == VER_NT_WORKSTATION && sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - ret.Append("Windows XP Professional x64 Edition"); - else - ret.Append("Windows Server 2003, "); - - // Test for the server type. - if (osvi.wProductType != VER_NT_WORKSTATION) { - if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Edition for Itanium-based Systems"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise Edition for Itanium-based Systems"); - } - else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter x64 Edition"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise x64 Edition"); - else ret.Append("Standard x64 Edition"); - } - else { - if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER) - ret.Append("Compute Cluster Edition"); - else if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Edition"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise Edition"); - else if (osvi.wSuiteMask & VER_SUITE_BLADE) - ret.Append("Web Edition"); - else ret.Append("Standard Edition"); - } - } - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { - ret.Append("Windows XP "); - if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - ret.Append("Home Edition"); - else ret.Append("Professional"); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { - ret.Append("Windows 2000 "); - - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Professional"); - else { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Server"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Advanced Server"); - else ret.Append("Server"); - } - } - - // Include service pack (if any) and build number. - if (mir_wstrlen(osvi.szCSDVersion) > 0) { - ret.Append(" "); - ret.Append(_T2A(osvi.szCSDVersion)); - } - - ret.AppendFormat(" (build %d)", osvi.dwBuildNumber); - - HMODULE hNtDll = GetModuleHandleA("ntdll.dll"); - if (WGV wine_get_version = (WGV)GetProcAddress(hNtDll, "wine_get_version")) - { - ret.AppendFormat(" (Wine %s)", wine_get_version()); - } - - mir_strncpy(buf, ret, bufSize); - return true; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+#ifndef _WIN32_WINNT_WIN8
+#define _WIN32_WINNT_WIN8 0x0602 // Windows 8
+#endif
+
+#ifndef _WIN32_WINNT_WINBLUE
+#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1
+#endif
+
+#ifndef _WIN32_WINNT_WIN10
+#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10
+#endif
+
+static int dwWinVer;
+
+void InitWinver()
+{
+ uint32_t dwVer = LOWORD(GetVersion());
+ dwWinVer = MAKEWORD(HIBYTE(dwVer), LOBYTE(dwVer));
+}
+
+MIR_CORE_DLL(BOOL) IsWinVerVistaPlus()
+{
+ return dwWinVer >= _WIN32_WINNT_VISTA;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer7Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WIN7;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer8Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WIN8;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer81Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WINBLUE;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer10Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WIN10;
+}
+
+MIR_CORE_DLL(BOOL) IsFullScreen()
+{
+ RECT rcScreen = { 0 };
+
+ rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
+ rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
+
+ HMONITOR hMon = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ rcScreen = mi.rcMonitor;
+
+ HWND hWndDesktop = GetDesktopWindow();
+ HWND hWndShell = GetShellWindow();
+
+ // check foregroundwindow
+ HWND hWnd = GetForegroundWindow();
+ if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell) {
+ wchar_t tszClassName[128] = L"";
+ GetClassName(hWnd, tszClassName, _countof(tszClassName));
+ if (wcscmp(tszClassName, L"WorkerW")) {
+ RECT rect, rectw, recti;
+ GetWindowRect(hWnd, &rectw);
+
+ GetClientRect(hWnd, &rect);
+ ClientToScreen(hWnd, (LPPOINT)&rect);
+ ClientToScreen(hWnd, (LPPOINT)&rect.right);
+
+ if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) && EqualRect(&recti, &rcScreen))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+MIR_CORE_DLL(BOOL) IsWorkstationLocked(void)
+{
+ HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
+ if (hDesk == nullptr)
+ return true;
+
+ wchar_t tszName[100];
+ DWORD cbName;
+ BOOL bLocked = (!GetUserObjectInformation(hDesk, UOI_NAME, tszName, _countof(tszName), &cbName) || mir_wstrcmpi(tszName, L"default") != 0);
+ CloseDesktop(hDesk);
+ return bLocked;
+}
+
+MIR_CORE_DLL(BOOL) IsTerminalDisconnected(void)
+{
+ PVOID pBuffer = nullptr;
+ DWORD pBytesReturned = 0;
+ BOOL result = FALSE;
+
+ if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&pBuffer, &pBytesReturned))
+ if (*(PDWORD)pBuffer == WTSDisconnected)
+ result = TRUE;
+
+ if (pBuffer)
+ WTSFreeMemory(pBuffer);
+ return result;
+}
+
+MIR_CORE_DLL(BOOL) IsScreenSaverRunning(void)
+{
+ BOOL rc = FALSE;
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE);
+ return rc != 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize)
+{
+ if (buf == nullptr || bufSize == 0)
+ return false;
+
+ mir_snprintf(buf, bufSize, "Windows NT %d.%d", HIBYTE(dwWinVer), LOBYTE(dwWinVer));
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PRODUCT_CORE_N // Win8
+#define PRODUCT_CORE_SINGLELANGUAGE 0x00000064
+#define PRODUCT_PROFESSIONAL_WMC 0x00000067
+#endif
+
+typedef BOOL(WINAPI *PGPI)(uint32_t, uint32_t, uint32_t, uint32_t, PDWORD);
+typedef LPCSTR(WINAPI *WGV)(void);
+
+MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize)
+{
+ if (buf == nullptr || bufSize == 0)
+ return 0;
+
+ buf[0] = 0;
+
+ OSVERSIONINFOEX osvi = { 0 };
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi);
+ if (!bOsVersionInfoEx) {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx((OSVERSIONINFO*)&osvi))
+ return false;
+ }
+
+ if (VER_PLATFORM_WIN32_NT != osvi.dwPlatformId || osvi.dwMajorVersion <= 4)
+ return false;
+
+ SYSTEM_INFO sysInfo = { 0 };
+ GetNativeSystemInfo(&sysInfo);
+
+ CMStringA ret("Microsoft ");
+
+ // Test for the specific product.
+ if (osvi.dwMajorVersion >= 6) {
+ if (osvi.dwMajorVersion == 10) {
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 10 ");
+ else
+ ret.Append("Windows Server 10 ");
+ }
+ else switch (osvi.dwMinorVersion) {
+ case 0:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows Vista ");
+ else
+ ret.Append("Windows Server 2008 ");
+ break;
+
+ case 1:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 7 ");
+ else
+ ret.Append("Windows Server 2008 R2 ");
+ break;
+
+ case 2:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 8 ");
+ else
+ ret.Append("Windows Server 2012 ");
+ break;
+
+ case 3:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 8.1 ");
+ else
+ ret.Append("Windows Server 2012 R2 ");
+ break;
+ }
+
+ DWORD dwType = 0;
+ HMODULE hKernel = GetModuleHandle(L"kernel32.dll");
+ PGPI pGPI = (PGPI)GetProcAddress(hKernel, "GetProductInfo");
+ if (pGPI != nullptr)
+ pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
+
+ switch (dwType) {
+ case PRODUCT_ULTIMATE:
+ ret.Append("Ultimate Edition");
+ break;
+ case PRODUCT_PROFESSIONAL:
+ ret.Append("Professional Edition");
+ break;
+ case PRODUCT_PROFESSIONAL_WMC:
+ ret.Append("Professional Edition with Media Center");
+ break;
+ case PRODUCT_HOME_PREMIUM:
+ ret.Append("Home Premium Edition");
+ break;
+ case PRODUCT_HOME_BASIC:
+ ret.Append("Home Basic Edition");
+ break;
+ case PRODUCT_ENTERPRISE:
+ ret.Append("Enterprise Edition");
+ break;
+ case PRODUCT_BUSINESS:
+ ret.Append("Business Edition");
+ break;
+ case PRODUCT_STARTER:
+ ret.Append("Starter Edition");
+ break;
+ case PRODUCT_CLUSTER_SERVER:
+ ret.Append("Cluster Server Edition");
+ break;
+ case PRODUCT_DATACENTER_SERVER:
+ ret.Append("Datacenter Edition");
+ break;
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ ret.Append("Datacenter Edition (core installation)");
+ break;
+ case PRODUCT_ENTERPRISE_SERVER:
+ ret.Append("Enterprise Edition");
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ ret.Append("Enterprise Edition (core installation)");
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_IA64:
+ ret.Append("Enterprise Edition for Itanium-based Systems");
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ ret.Append("Small Business Server");
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ ret.Append("Small Business Server Premium Edition");
+ break;
+ case PRODUCT_STANDARD_SERVER:
+ ret.Append("Standard Edition");
+ break;
+ case PRODUCT_STANDARD_SERVER_CORE:
+ ret.Append("Standard Edition (core installation)");
+ break;
+ case PRODUCT_WEB_SERVER:
+ ret.Append("Web Server Edition");
+ break;
+ case PRODUCT_CORE_SINGLELANGUAGE:
+ ret.Append("Home Single Language");
+ break;
+ }
+ if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ ret.Append(", 64-bit");
+ else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
+ ret.Append(", 32-bit");
+ }
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
+ if (GetSystemMetrics(SM_SERVERR2))
+ ret.Append("Windows Server 2003 R2, ");
+ else if (osvi.wSuiteMask == VER_SUITE_STORAGE_SERVER)
+ ret.Append("Windows Storage Server 2003");
+ else if (osvi.wSuiteMask == VER_SUITE_WH_SERVER)
+ ret.Append("Windows Home Server");
+ else if (osvi.wProductType == VER_NT_WORKSTATION && sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ ret.Append("Windows XP Professional x64 Edition");
+ else
+ ret.Append("Windows Server 2003, ");
+
+ // Test for the server type.
+ if (osvi.wProductType != VER_NT_WORKSTATION) {
+ if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter Edition for Itanium-based Systems");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Enterprise Edition for Itanium-based Systems");
+ }
+ else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter x64 Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Enterprise x64 Edition");
+ else ret.Append("Standard x64 Edition");
+ }
+ else {
+ if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER)
+ ret.Append("Compute Cluster Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Enterprise Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_BLADE)
+ ret.Append("Web Edition");
+ else ret.Append("Standard Edition");
+ }
+ }
+ }
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
+ ret.Append("Windows XP ");
+ if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
+ ret.Append("Home Edition");
+ else ret.Append("Professional");
+ }
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
+ ret.Append("Windows 2000 ");
+
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Professional");
+ else {
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter Server");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Advanced Server");
+ else ret.Append("Server");
+ }
+ }
+
+ // Include service pack (if any) and build number.
+ if (mir_wstrlen(osvi.szCSDVersion) > 0) {
+ ret.Append(" ");
+ ret.Append(_T2A(osvi.szCSDVersion));
+ }
+
+ ret.AppendFormat(" (build %d)", osvi.dwBuildNumber);
+
+ HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
+ if (WGV wine_get_version = (WGV)GetProcAddress(hNtDll, "wine_get_version"))
+ {
+ ret.AppendFormat(" (Wine %s)", wine_get_version());
+ }
+
+ mir_strncpy(buf, ret, bufSize);
+ return true;
+}
diff --git a/src/mir_core/src/binbuffer.cpp b/src/mir_core/src/binbuffer.cpp index fbde7bfb4d..77016fbda9 100644 --- a/src/mir_core/src/binbuffer.cpp +++ b/src/mir_core/src/binbuffer.cpp @@ -1,170 +1,170 @@ -/* -Copyright (C) 2012-22 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" - -struct BufImpl -{ - uint32_t size, lockCount; - - BufImpl* alloc(size_t newSize) - { - bool bEmpty = (this == nullptr); - auto *res = (BufImpl *)mir_realloc(this, newSize + sizeof(BufImpl)); - if (bEmpty) { - res->lockCount = 1; - res->size = 0; - } - return res; - } - - BufImpl* realloc(size_t newSize) - { - bool bEmpty; - newSize += sizeof(BufImpl); - if (this != nullptr) { - newSize += size; - bEmpty = false; - } - else bEmpty = true; - - auto *res = (BufImpl *)mir_realloc(this, newSize); - if (bEmpty) { - res->lockCount = 1; - res->size = 0; - } - return res; - } - - void free() - { - if (this == nullptr) - return; - - if (lockCount == 1) - mir_free(this); - else - lockCount--; - } -}; - -__forceinline BufImpl* ptr2buf(uint8_t *p) -{ - return (p == nullptr) ? nullptr : (BufImpl*)p-1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MBinBuffer::MBinBuffer() -{} - -MBinBuffer::MBinBuffer(const MBinBuffer &orig) -{ - ptr2buf(m_buf)->free(); - - BufImpl *p = ptr2buf(m_buf = orig.m_buf); - if (p) - p->lockCount++; -} - -MBinBuffer::MBinBuffer(size_t preAlloc) -{ - BufImpl *p = (BufImpl *)mir_alloc(sizeof(BufImpl) + preAlloc); - p->lockCount = 1; - p->size = (unsigned)preAlloc; - m_buf = (uint8_t *)(p + 1); -} - -MBinBuffer& MBinBuffer::operator=(MBinBuffer &&from) noexcept -{ - m_buf = from.m_buf; - from.m_buf = nullptr; - return *this; -} - -MBinBuffer::~MBinBuffer() -{ - ptr2buf(m_buf)->free(); -} - -void MBinBuffer::append(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); - if (p) { - m_buf = (uint8_t *)(p + 1); - memcpy(m_buf + p->size, pBuf, bufLen); - p->size += (unsigned)bufLen; - } - else m_buf = nullptr; -} - -void MBinBuffer::appendBefore(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); - if (p) { - m_buf = (uint8_t *)(p + 1); - memmove(m_buf + bufLen, m_buf, p->size); - memcpy(m_buf, pBuf, bufLen); - p->size += (unsigned)bufLen; - } - else m_buf = nullptr; -} - -void MBinBuffer::assign(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->alloc(bufLen); - if (p) { - p->size = (unsigned)bufLen; - m_buf = (uint8_t *)(p + 1); - memcpy(m_buf, pBuf, bufLen); - } - else m_buf = nullptr; -} - -size_t MBinBuffer::length() const -{ - BufImpl *p = ptr2buf(m_buf); - return (p) ? p->size : 0; -} - -void MBinBuffer::remove(size_t sz) -{ - BufImpl *p = ptr2buf(m_buf); - if (!p) - return; - - if (sz > p->size) - sz = p->size; - - if (p->size == sz) { - p->free(); - m_buf = nullptr; - } - else { - memmove(m_buf, m_buf + sz, p->size - sz); - p->size -= (unsigned)sz; - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+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"
+
+struct BufImpl
+{
+ uint32_t size, lockCount;
+
+ BufImpl* alloc(size_t newSize)
+ {
+ bool bEmpty = (this == nullptr);
+ auto *res = (BufImpl *)mir_realloc(this, newSize + sizeof(BufImpl));
+ if (bEmpty) {
+ res->lockCount = 1;
+ res->size = 0;
+ }
+ return res;
+ }
+
+ BufImpl* realloc(size_t newSize)
+ {
+ bool bEmpty;
+ newSize += sizeof(BufImpl);
+ if (this != nullptr) {
+ newSize += size;
+ bEmpty = false;
+ }
+ else bEmpty = true;
+
+ auto *res = (BufImpl *)mir_realloc(this, newSize);
+ if (bEmpty) {
+ res->lockCount = 1;
+ res->size = 0;
+ }
+ return res;
+ }
+
+ void free()
+ {
+ if (this == nullptr)
+ return;
+
+ if (lockCount == 1)
+ mir_free(this);
+ else
+ lockCount--;
+ }
+};
+
+__forceinline BufImpl* ptr2buf(uint8_t *p)
+{
+ return (p == nullptr) ? nullptr : (BufImpl*)p-1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MBinBuffer::MBinBuffer()
+{}
+
+MBinBuffer::MBinBuffer(const MBinBuffer &orig)
+{
+ ptr2buf(m_buf)->free();
+
+ BufImpl *p = ptr2buf(m_buf = orig.m_buf);
+ if (p)
+ p->lockCount++;
+}
+
+MBinBuffer::MBinBuffer(size_t preAlloc)
+{
+ BufImpl *p = (BufImpl *)mir_alloc(sizeof(BufImpl) + preAlloc);
+ p->lockCount = 1;
+ p->size = (unsigned)preAlloc;
+ m_buf = (uint8_t *)(p + 1);
+}
+
+MBinBuffer& MBinBuffer::operator=(MBinBuffer &&from) noexcept
+{
+ m_buf = from.m_buf;
+ from.m_buf = nullptr;
+ return *this;
+}
+
+MBinBuffer::~MBinBuffer()
+{
+ ptr2buf(m_buf)->free();
+}
+
+void MBinBuffer::append(const void *pBuf, size_t bufLen)
+{
+ if (pBuf == nullptr || bufLen == 0)
+ return;
+
+ BufImpl *p = ptr2buf(m_buf)->realloc(bufLen);
+ if (p) {
+ m_buf = (uint8_t *)(p + 1);
+ memcpy(m_buf + p->size, pBuf, bufLen);
+ p->size += (unsigned)bufLen;
+ }
+ else m_buf = nullptr;
+}
+
+void MBinBuffer::appendBefore(const void *pBuf, size_t bufLen)
+{
+ if (pBuf == nullptr || bufLen == 0)
+ return;
+
+ BufImpl *p = ptr2buf(m_buf)->realloc(bufLen);
+ if (p) {
+ m_buf = (uint8_t *)(p + 1);
+ memmove(m_buf + bufLen, m_buf, p->size);
+ memcpy(m_buf, pBuf, bufLen);
+ p->size += (unsigned)bufLen;
+ }
+ else m_buf = nullptr;
+}
+
+void MBinBuffer::assign(const void *pBuf, size_t bufLen)
+{
+ if (pBuf == nullptr || bufLen == 0)
+ return;
+
+ BufImpl *p = ptr2buf(m_buf)->alloc(bufLen);
+ if (p) {
+ p->size = (unsigned)bufLen;
+ m_buf = (uint8_t *)(p + 1);
+ memcpy(m_buf, pBuf, bufLen);
+ }
+ else m_buf = nullptr;
+}
+
+size_t MBinBuffer::length() const
+{
+ BufImpl *p = ptr2buf(m_buf);
+ return (p) ? p->size : 0;
+}
+
+void MBinBuffer::remove(size_t sz)
+{
+ BufImpl *p = ptr2buf(m_buf);
+ if (!p)
+ return;
+
+ if (sz > p->size)
+ sz = p->size;
+
+ if (p->size == sz) {
+ p->free();
+ m_buf = nullptr;
+ }
+ else {
+ memmove(m_buf, m_buf + sz, p->size - sz);
+ p->size -= (unsigned)sz;
+ }
+}
diff --git a/src/mir_core/src/bitmaps.cpp b/src/mir_core/src/bitmaps.cpp index eb1a8b02d2..5ab665df84 100644 --- a/src/mir_core/src/bitmaps.cpp +++ b/src/mir_core/src/bitmaps.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_core/src/db.cpp b/src/mir_core/src/db.cpp index 8fb13fd4d3..362d359f17 100644 --- a/src/mir_core/src/db.cpp +++ b/src/mir_core/src/db.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/http.cpp b/src/mir_core/src/http.cpp index e983676dd0..879df3b038 100644 --- a/src/mir_core/src/http.cpp +++ b/src/mir_core/src/http.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/mir_core/src/lists.cpp b/src/mir_core/src/lists.cpp index 21d890a148..c5b1b36825 100644 --- a/src/mir_core/src/lists.cpp +++ b/src/mir_core/src/lists.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_core/src/logger.cpp b/src/mir_core/src/logger.cpp index 24240d691d..83218f88ed 100644 --- a/src/mir_core/src/logger.cpp +++ b/src/mir_core/src/logger.cpp @@ -1,203 +1,203 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define SECRET_SIGNATURE 0x87654321 - -struct Logger -{ - Logger(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFilename, unsigned options) : - m_name(mir_strdup(pszName)), - m_descr(mir_wstrdup(ptszDescr)), - m_fileName(mir_wstrdup(ptszFilename)), - m_options(options), - m_signature(SECRET_SIGNATURE), - m_out(nullptr), - m_lastwrite(0) - { - } - - ~Logger() - { - if (m_out) - fclose(m_out); - } - - int m_signature; - ptrA m_name; - ptrW m_fileName, m_descr; - FILE *m_out; - time_t m_lastwrite; - unsigned m_options; - mir_cs m_cs; -}; - -static int CompareLoggers(const Logger *p1, const Logger *p2) -{ return strcmp(p1->m_name, p2->m_name); -} - -static OBJLIST<Logger> arLoggers(1, CompareLoggers); - -void InitLogs() -{ -} - -void UninitLogs() -{ - arLoggers.destroy(); -} - -void CheckLogs() -{ - time_t tm = time(0); - - for (auto &p : arLoggers) { - mir_cslock lck(p->m_cs); - if (p->m_out && tm - p->m_lastwrite > 5) { - fclose(p->m_out); - p->m_out = nullptr; - } - else fflush(p->m_out); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(HANDLE) mir_createLog(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options) -{ - if (ptszFile == nullptr) - return nullptr; - - Logger *result = new Logger(pszName, ptszDescr, ptszFile, options); - if (result == nullptr) - return nullptr; - - int idx = arLoggers.getIndex(result); - if (idx != -1) { - delete result; - return &arLoggers[idx]; - } - - CreatePathToFileW(ptszFile); - _wremove(ptszFile); - arLoggers.insert(result); - return result; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -static Logger* prepareLogger(HANDLE hLogger) -{ - if (hLogger == nullptr) - return nullptr; - - Logger *p = (Logger*)hLogger; - return (p->m_signature == SECRET_SIGNATURE) ? p : nullptr; -} - -MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger) -{ - Logger *p = prepareLogger(hLogger); - if (p != nullptr) - arLoggers.remove(p); -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - va_list args; - va_start(args, format); - vfprintf(p->m_out, format, args); - va_end(args); - - p->m_lastwrite = time(0); - return 0; -} - -MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - va_list args; - va_start(args, format); - vfwprintf(p->m_out, format, args); - va_end(args); - - p->m_lastwrite = time(0); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - vfprintf(p->m_out, format, args); - - p->m_lastwrite = time(0); - return 0; -} - -MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - vfwprintf(p->m_out, format, args); - - p->m_lastwrite = time(0); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define SECRET_SIGNATURE 0x87654321
+
+struct Logger
+{
+ Logger(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFilename, unsigned options) :
+ m_name(mir_strdup(pszName)),
+ m_descr(mir_wstrdup(ptszDescr)),
+ m_fileName(mir_wstrdup(ptszFilename)),
+ m_options(options),
+ m_signature(SECRET_SIGNATURE),
+ m_out(nullptr),
+ m_lastwrite(0)
+ {
+ }
+
+ ~Logger()
+ {
+ if (m_out)
+ fclose(m_out);
+ }
+
+ int m_signature;
+ ptrA m_name;
+ ptrW m_fileName, m_descr;
+ FILE *m_out;
+ time_t m_lastwrite;
+ unsigned m_options;
+ mir_cs m_cs;
+};
+
+static int CompareLoggers(const Logger *p1, const Logger *p2)
+{ return strcmp(p1->m_name, p2->m_name);
+}
+
+static OBJLIST<Logger> arLoggers(1, CompareLoggers);
+
+void InitLogs()
+{
+}
+
+void UninitLogs()
+{
+ arLoggers.destroy();
+}
+
+void CheckLogs()
+{
+ time_t tm = time(0);
+
+ for (auto &p : arLoggers) {
+ mir_cslock lck(p->m_cs);
+ if (p->m_out && tm - p->m_lastwrite > 5) {
+ fclose(p->m_out);
+ p->m_out = nullptr;
+ }
+ else fflush(p->m_out);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(HANDLE) mir_createLog(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options)
+{
+ if (ptszFile == nullptr)
+ return nullptr;
+
+ Logger *result = new Logger(pszName, ptszDescr, ptszFile, options);
+ if (result == nullptr)
+ return nullptr;
+
+ int idx = arLoggers.getIndex(result);
+ if (idx != -1) {
+ delete result;
+ return &arLoggers[idx];
+ }
+
+ CreatePathToFileW(ptszFile);
+ _wremove(ptszFile);
+ arLoggers.insert(result);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+static Logger* prepareLogger(HANDLE hLogger)
+{
+ if (hLogger == nullptr)
+ return nullptr;
+
+ Logger *p = (Logger*)hLogger;
+ return (p->m_signature == SECRET_SIGNATURE) ? p : nullptr;
+}
+
+MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p != nullptr)
+ arLoggers.remove(p);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ va_list args;
+ va_start(args, format);
+ vfprintf(p->m_out, format, args);
+ va_end(args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
+
+MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ va_list args;
+ va_start(args, format);
+ vfwprintf(p->m_out, format, args);
+ va_end(args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ vfprintf(p->m_out, format, args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
+
+MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ vfwprintf(p->m_out, format, args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
diff --git a/src/mir_core/src/memory.cpp b/src/mir_core/src/memory.cpp index 8cf95b81ab..5183310b48 100644 --- a/src/mir_core/src/memory.cpp +++ b/src/mir_core/src/memory.cpp @@ -1,295 +1,295 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define BLOCK_ALLOCED 0xABBABABA -#define BLOCK_FREED 0xDEADBEEF - -static int CheckBlock(void* blk) -{ - int result = FALSE; - char* p = (char*)blk - sizeof(uint32_t)*2; - uint32_t size, *b, *e; - - __try - { - size = *(uint32_t*)p; - b = (uint32_t*)&p[ sizeof(uint32_t) ]; - e = (uint32_t*)&p[ sizeof(uint32_t)*2 + size ]; - - if (*b != BLOCK_ALLOCED || *e != BLOCK_ALLOCED) - { - #ifdef _MSC_VER - if (*b == BLOCK_FREED && *e == BLOCK_FREED) - OutputDebugStringA("memory block is already deleted\n"); - else - OutputDebugStringA("memory block is corrupted\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - } - else result = TRUE; - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - #ifdef _MSC_VER - OutputDebugStringA("access violation during checking memory block\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - } - - return result; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_alloc(size_t size) -{ - if (size == 0) - return nullptr; - - char *p = (char*)malloc(size + sizeof(uint32_t)* 3); - if (p == nullptr) { - #ifdef _MSC_VER - OutputDebugStringA("memory overflow\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - return nullptr; - } - - *(uint32_t*)p = (uint32_t)size; - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; - return p + sizeof(uint32_t)* 2; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_calloc(size_t size) -{ - void* p = mir_alloc(size); - if (p != nullptr) - memset(p, 0, size); - return p; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t size) -{ - char *p; - - if (ptr != nullptr) { - if (!CheckBlock(ptr)) - return nullptr; - p = (char*)ptr - sizeof(uint32_t)*2; - } - else p = nullptr; - - p = (char*)realloc(p, size + sizeof(uint32_t)*3); - if (p == nullptr) { - #ifdef _MSC_VER - OutputDebugStringA("memory overflow\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - return nullptr; - } - - *(uint32_t*)p = (uint32_t)size; - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; - return p + sizeof(uint32_t)*2; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void) mir_free(void* ptr) -{ - char* p; - uint32_t size; - - if (ptr == nullptr) - return; - if (!CheckBlock(ptr)) - return; - - p = (char*)ptr - sizeof(uint32_t)*2; - size = *(uint32_t*)p; - - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_FREED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_FREED; - free(p); -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_strdup(const char *str) -{ - if (str == nullptr) - return nullptr; - - char *p = (char*)mir_alloc(strlen(str)+1); - if (p) - strcpy(p, str); - return p; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(wcslen(str)+1)); - if (p) - wcscpy(p, str); - return p; -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_strndup(const char *str, size_t len) -{ - if (str == nullptr || len == 0) - return nullptr; - - char *p = (char*)mir_alloc(len+1); - if (p) { - memcpy(p, str, len); - p[len] = 0; - } - return p; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len) -{ - if (str == nullptr || len == 0) - return nullptr; - - wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(len+1)); - if (p) { - memcpy(p, str, sizeof(wchar_t)*len); - p[len] = 0; - } - return p; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_snprintf(char *buffer, size_t count, const char* fmt, ...) -{ - va_list va; - va_start(va, fmt); - int len = _vsnprintf(buffer, count-1, fmt, va); - va_end(va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_snwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, ...) -{ - va_list va; - va_start(va, fmt); - int len = _vsnwprintf(buffer, count-1, fmt, va); - va_end(va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_vsnprintf(char *buffer, size_t count, const char* fmt, va_list va) -{ - int len = _vsnprintf(buffer, count-1, fmt, va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_vsnwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, va_list va) -{ - int len = _vsnwprintf(buffer, count-1, fmt, va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -#ifdef _MSC_VER -MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage) -{ - if (src == nullptr) - return nullptr; - - int cbLen = MultiByteToWideChar(codepage, 0, src, -1, nullptr, 0); - wchar_t* result = (wchar_t*)mir_alloc(sizeof(wchar_t)*(cbLen+1)); - if (result == nullptr) - return nullptr; - - MultiByteToWideChar(codepage, 0, src, -1, result, cbLen); - result[cbLen] = 0; - return result; -} - -/******************************************************************************/ - -MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src) -{ - return mir_a2u_cp(src, Langpack_GetDefaultCodePage()); -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage) -{ - if (src == nullptr) - return nullptr; - - int cbLen = WideCharToMultiByte(codepage, 0, src, -1, nullptr, 0, nullptr, nullptr); - char* result = (char*)mir_alloc(cbLen+1); - if (result == nullptr) - return nullptr; - - WideCharToMultiByte(codepage, 0, src, -1, result, cbLen, nullptr, nullptr); - result[cbLen] = 0; - return result; -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src) -{ - return mir_u2a_cp(src, Langpack_GetDefaultCodePage()); -} -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define BLOCK_ALLOCED 0xABBABABA
+#define BLOCK_FREED 0xDEADBEEF
+
+static int CheckBlock(void* blk)
+{
+ int result = FALSE;
+ char* p = (char*)blk - sizeof(uint32_t)*2;
+ uint32_t size, *b, *e;
+
+ __try
+ {
+ size = *(uint32_t*)p;
+ b = (uint32_t*)&p[ sizeof(uint32_t) ];
+ e = (uint32_t*)&p[ sizeof(uint32_t)*2 + size ];
+
+ if (*b != BLOCK_ALLOCED || *e != BLOCK_ALLOCED)
+ {
+ #ifdef _MSC_VER
+ if (*b == BLOCK_FREED && *e == BLOCK_FREED)
+ OutputDebugStringA("memory block is already deleted\n");
+ else
+ OutputDebugStringA("memory block is corrupted\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ }
+ else result = TRUE;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ #ifdef _MSC_VER
+ OutputDebugStringA("access violation during checking memory block\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ }
+
+ return result;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void*) mir_alloc(size_t size)
+{
+ if (size == 0)
+ return nullptr;
+
+ char *p = (char*)malloc(size + sizeof(uint32_t)* 3);
+ if (p == nullptr) {
+ #ifdef _MSC_VER
+ OutputDebugStringA("memory overflow\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ return nullptr;
+ }
+
+ *(uint32_t*)p = (uint32_t)size;
+ *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED;
+ *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED;
+ return p + sizeof(uint32_t)* 2;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void*) mir_calloc(size_t size)
+{
+ void* p = mir_alloc(size);
+ if (p != nullptr)
+ memset(p, 0, size);
+ return p;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t size)
+{
+ char *p;
+
+ if (ptr != nullptr) {
+ if (!CheckBlock(ptr))
+ return nullptr;
+ p = (char*)ptr - sizeof(uint32_t)*2;
+ }
+ else p = nullptr;
+
+ p = (char*)realloc(p, size + sizeof(uint32_t)*3);
+ if (p == nullptr) {
+ #ifdef _MSC_VER
+ OutputDebugStringA("memory overflow\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ return nullptr;
+ }
+
+ *(uint32_t*)p = (uint32_t)size;
+ *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED;
+ *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED;
+ return p + sizeof(uint32_t)*2;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void) mir_free(void* ptr)
+{
+ char* p;
+ uint32_t size;
+
+ if (ptr == nullptr)
+ return;
+ if (!CheckBlock(ptr))
+ return;
+
+ p = (char*)ptr - sizeof(uint32_t)*2;
+ size = *(uint32_t*)p;
+
+ *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_FREED;
+ *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_FREED;
+ free(p);
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_strdup(const char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char *p = (char*)mir_alloc(strlen(str)+1);
+ if (p)
+ strcpy(p, str);
+ return p;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(wcslen(str)+1));
+ if (p)
+ wcscpy(p, str);
+ return p;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_strndup(const char *str, size_t len)
+{
+ if (str == nullptr || len == 0)
+ return nullptr;
+
+ char *p = (char*)mir_alloc(len+1);
+ if (p) {
+ memcpy(p, str, len);
+ p[len] = 0;
+ }
+ return p;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len)
+{
+ if (str == nullptr || len == 0)
+ return nullptr;
+
+ wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(len+1));
+ if (p) {
+ memcpy(p, str, sizeof(wchar_t)*len);
+ p[len] = 0;
+ }
+ return p;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_snprintf(char *buffer, size_t count, const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ int len = _vsnprintf(buffer, count-1, fmt, va);
+ va_end(va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_snwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ int len = _vsnwprintf(buffer, count-1, fmt, va);
+ va_end(va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_vsnprintf(char *buffer, size_t count, const char* fmt, va_list va)
+{
+ int len = _vsnprintf(buffer, count-1, fmt, va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_vsnwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, va_list va)
+{
+ int len = _vsnwprintf(buffer, count-1, fmt, va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage)
+{
+ if (src == nullptr)
+ return nullptr;
+
+ int cbLen = MultiByteToWideChar(codepage, 0, src, -1, nullptr, 0);
+ wchar_t* result = (wchar_t*)mir_alloc(sizeof(wchar_t)*(cbLen+1));
+ if (result == nullptr)
+ return nullptr;
+
+ MultiByteToWideChar(codepage, 0, src, -1, result, cbLen);
+ result[cbLen] = 0;
+ return result;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src)
+{
+ return mir_a2u_cp(src, Langpack_GetDefaultCodePage());
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage)
+{
+ if (src == nullptr)
+ return nullptr;
+
+ int cbLen = WideCharToMultiByte(codepage, 0, src, -1, nullptr, 0, nullptr, nullptr);
+ char* result = (char*)mir_alloc(cbLen+1);
+ if (result == nullptr)
+ return nullptr;
+
+ WideCharToMultiByte(codepage, 0, src, -1, result, cbLen, nullptr, nullptr);
+ result[cbLen] = 0;
+ return result;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src)
+{
+ return mir_u2a_cp(src, Langpack_GetDefaultCodePage());
+}
+#endif
diff --git a/src/mir_core/src/miranda.h b/src/mir_core/src/miranda.h index 7264f27444..2b725888f2 100644 --- a/src/mir_core/src/miranda.h +++ b/src/mir_core/src/miranda.h @@ -1,101 +1,101 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -void UnloadLangPackModule(void); - -int InitialiseModularEngine(void); -void DestroyModularEngine(void); - -int InitPathUtils(void); - -extern HINSTANCE g_hInst; -extern HWND hAPCWindow; -extern HANDLE hThreadQueueEmpty; -extern HCURSOR g_hCursorNS, g_hCursorWE; -extern bool g_bEnableDpiAware; - -///////////////////////////////////////////////////////////////////////////////////////// -// modules.cpp - -struct THookSubscriber -{ - HINSTANCE hOwner; - int type; - union { - struct { - union { - MIRANDAHOOK pfnHook; - MIRANDAHOOKPARAM pfnHookParam; - MIRANDAHOOKOBJ pfnHookObj; - MIRANDAHOOKOBJPARAM pfnHookObjParam; - }; - void* object; - LPARAM lParam; - }; - struct { - HWND hwnd; - UINT message; - }; - }; -}; - -#define HOOK_SECRET_SIGNATURE 0xDEADBABA - -struct THook : public MZeroedObject -{ - char name[MAXMODULELABELLENGTH]; - int id; - int subscriberCount; - THookSubscriber* subscriber; - MIRANDAHOOK pfnHook; - uint32_t secretSignature = HOOK_SECRET_SIGNATURE; - mir_cs csHook; -}; - -extern LIST<CMPluginBase> pluginListAddr; - -///////////////////////////////////////////////////////////////////////////////////////// -// langpack.cpp - -char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W); - -///////////////////////////////////////////////////////////////////////////////////////// -// miranda.cpp - -EXTERN_C MIR_CORE_DLL(void) BeginMessageLoop(void); -EXTERN_C MIR_CORE_DLL(void) EnterMessageLoop(void); -EXTERN_C MIR_CORE_DLL(void) LeaveMessageLoop(void); - -///////////////////////////////////////////////////////////////////////////////////////// -// threads.cpp - -extern uint32_t mir_tls; - -///////////////////////////////////////////////////////////////////////////////////////// -// utils.cpp - -typedef BOOL(MIR_SYSCALL *PGENRANDOM)(void*, uint32_t); -extern PGENRANDOM pfnRtlGenRandom; +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+void UnloadLangPackModule(void);
+
+int InitialiseModularEngine(void);
+void DestroyModularEngine(void);
+
+int InitPathUtils(void);
+
+extern HINSTANCE g_hInst;
+extern HWND hAPCWindow;
+extern HANDLE hThreadQueueEmpty;
+extern HCURSOR g_hCursorNS, g_hCursorWE;
+extern bool g_bEnableDpiAware;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// modules.cpp
+
+struct THookSubscriber
+{
+ HINSTANCE hOwner;
+ int type;
+ union {
+ struct {
+ union {
+ MIRANDAHOOK pfnHook;
+ MIRANDAHOOKPARAM pfnHookParam;
+ MIRANDAHOOKOBJ pfnHookObj;
+ MIRANDAHOOKOBJPARAM pfnHookObjParam;
+ };
+ void* object;
+ LPARAM lParam;
+ };
+ struct {
+ HWND hwnd;
+ UINT message;
+ };
+ };
+};
+
+#define HOOK_SECRET_SIGNATURE 0xDEADBABA
+
+struct THook : public MZeroedObject
+{
+ char name[MAXMODULELABELLENGTH];
+ int id;
+ int subscriberCount;
+ THookSubscriber* subscriber;
+ MIRANDAHOOK pfnHook;
+ uint32_t secretSignature = HOOK_SECRET_SIGNATURE;
+ mir_cs csHook;
+};
+
+extern LIST<CMPluginBase> pluginListAddr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// langpack.cpp
+
+char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// miranda.cpp
+
+EXTERN_C MIR_CORE_DLL(void) BeginMessageLoop(void);
+EXTERN_C MIR_CORE_DLL(void) EnterMessageLoop(void);
+EXTERN_C MIR_CORE_DLL(void) LeaveMessageLoop(void);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// threads.cpp
+
+extern uint32_t mir_tls;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// utils.cpp
+
+typedef BOOL(MIR_SYSCALL *PGENRANDOM)(void*, uint32_t);
+extern PGENRANDOM pfnRtlGenRandom;
diff --git a/src/mir_core/src/modules.cpp b/src/mir_core/src/modules.cpp index 1636d7449e..0d471c8c6c 100644 --- a/src/mir_core/src/modules.cpp +++ b/src/mir_core/src/modules.cpp @@ -1,704 +1,704 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -// list of hooks - -static int compareHooks(const THook* p1, const THook* p2) -{ - return strcmp(p1->name, p2->name); -} - -static LIST<THook> hooks(50, compareHooks); - -struct THookToMainThreadItem -{ - THook* hook; - HANDLE hDoneEvent; - WPARAM wParam; - LPARAM lParam; - int result; -}; - -// list of services - -struct TService -{ - uint32_t nameHash; - HINSTANCE hOwner; - union - { - MIRANDASERVICE pfnService; - MIRANDASERVICEPARAM pfnServiceParam; - MIRANDASERVICEOBJ pfnServiceObj; - MIRANDASERVICEOBJPARAM pfnServiceObjParam; - }; - int flags; - LPARAM lParam; - void* object; - char name[1]; -}; - -LIST<TService> services(100, NumericKeySortT); - -struct TServiceToMainThreadItem -{ - HANDLE hDoneEvent; - WPARAM wParam; - LPARAM lParam; - int result; - const char *name; -}; - -// other static variables -static BOOL bServiceMode = FALSE; -static mir_cs csHooks, csServices; -static uint32_t mainThreadId; -static int sttHookId = 1; - -///////////////////////////////////////////////////////////////////////////////////////// - -__forceinline HANDLE getThreadEvent() -{ - HANDLE pData = (HANDLE)TlsGetValue(mir_tls); - if (pData == nullptr) { - pData = CreateEvent(nullptr, FALSE, FALSE, nullptr); - TlsSetValue(mir_tls, pData); - } - return pData; -} - -static int QueueMainThread(PAPCFUNC pFunc, void* pParam, HANDLE hDoneEvent) -{ - int result = PostMessage(hAPCWindow, WM_USER + 1, (WPARAM)pFunc, (LPARAM)pParam); // let this get processed in its own time - if (hDoneEvent) - WaitForSingleObject(hDoneEvent, INFINITE); - - return result; -} - -/////////////////////////////////////////////////////////////////////////////// -// HOOKS - -MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name) -{ - if (name == nullptr) - return nullptr; - - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) != -1) - return hooks[idx]; - - THook *newItem = new THook(); - strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[MAXMODULELABELLENGTH - 1] = 0; - newItem->id = sttHookId++; - hooks.insert(newItem); - return (HANDLE)newItem; -} - -MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) -{ - if (hEvent == nullptr) - return 1; - - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)hEvent)) == -1) - return 1; - - THook *p = hooks[idx]; - p->secretSignature = 0; - if (p->subscriberCount) { - mir_free(p->subscriber); - p->subscriber = nullptr; - p->subscriberCount = 0; - } - hooks.remove(idx); - delete p; - return 0; -} - -MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook) -{ - THook *p = (THook*)hEvent; - - mir_cslock lck(csHooks); - if (hooks.getIndex(p) != -1) - p->pfnHook = pfnHook; - return 0; -} - -MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam, LPARAM lParam) -{ - int idx; - if ((idx = hooks.getIndex((THook *)pszEvent)) == -1) - return -1; - - THook *p = hooks[idx]; - if (p == nullptr || hInst == nullptr) - return -1; - - mir_cslock lck(p->csHook); - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - if (s->hOwner != hInst) - continue; - - int returnVal; - switch (s->type) { - case 1: returnVal = s->pfnHook(wParam, lParam); break; - case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - if (p->subscriberCount == 0 && p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - THook *p = (THook*)hEvent; - if (p == nullptr || pObject == nullptr) - return -1; - - mir_cslock lck(p->csHook); - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - if (s->object != pObject) - continue; - - int returnVal; - switch (s->type) { - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - if (p->subscriberCount == 0 && p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -static int CallHookSubscribers(THook *p, WPARAM wParam, LPARAM lParam) -{ - if (p == nullptr) - return -1; - - mir_cslock lck(p->csHook); - - // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though. - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - - int returnVal; - switch (s->type) { - case 1: returnVal = s->pfnHook(wParam, lParam); break; - case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - // call the default hook if any - if (p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -enum { hookOk, hookEmpty, hookInvalid }; - -int checkHook(THook *p) -{ - if (p == nullptr) - return hookInvalid; - - int ret; - __try { - if (p->secretSignature != HOOK_SECRET_SIGNATURE) - ret = hookInvalid; - else if (p->subscriberCount == 0 && p->pfnHook == nullptr) - ret = hookEmpty; - else - ret = hookOk; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - ret = hookInvalid; - } - - return ret; -} - -static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam) -{ - THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam; - item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - switch (checkHook((THook*)hEvent)) { - case hookInvalid: return -1; - case hookEmpty: return 0; - } - - if (GetCurrentThreadId() == mainThreadId) - return CallHookSubscribers((THook*)hEvent, wParam, lParam); - - THookToMainThreadItem item; - item.hDoneEvent = getThreadEvent(); - item.hook = (THook*)hEvent; - item.wParam = wParam; - item.lParam = lParam; - QueueMainThread(HookToMainAPCFunc, &item, item.hDoneEvent); - return item.result; -} - -MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - switch (checkHook((THook*)hEvent)) { - case hookInvalid: return -1; - case hookEmpty: return 0; - } - - return CallHookSubscribers((THook*)hEvent, wParam, lParam); -} - -extern "C" MIR_CORE_DLL(int) GetSubscribersCount(THook* pHook) -{ - switch (checkHook(pHook)) { - case hookInvalid: - case hookEmpty: return 0; - } - return pHook->subscriberCount; -} - -static HANDLE HookEventInt(int type, const char *name, MIRANDAHOOK hookProc, void* object, LPARAM lParam) -{ - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) - return nullptr; - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - - THookSubscriber &s = p->subscriber[p->subscriberCount]; - s.type = type; - s.pfnHook = hookProc; - s.object = object; - s.lParam = lParam; - s.hOwner = GetInstByAddress(hookProc); - p->subscriberCount++; - - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc) -{ - return HookEventInt(1, name, hookProc, nullptr, 0); -} - -MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam) -{ - return HookEventInt(2, name, (MIRANDAHOOK)hookProc, nullptr, lParam); -} - -MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object) -{ - return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0); -} - -MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam) -{ - return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam); -} - -MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc) -{ - mir_cslockfull lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) { - lck.unlock(); - hookProc(0, 0); - return nullptr; - } - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - - THookSubscriber &s = p->subscriber[p->subscriberCount]; - memset(&s, 0, sizeof(THookSubscriber)); - s.type = 1; - s.pfnHook = hookProc; - s.hOwner = GetInstByAddress(hookProc); - - p->subscriberCount++; - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message) -{ - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) - return nullptr; - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - p->subscriber[p->subscriberCount].type = 5; - p->subscriber[p->subscriberCount].hwnd = hwnd; - p->subscriber[p->subscriberCount].message = message; - p->subscriberCount++; - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook) -{ - if (hHook == nullptr) - return 0; - - int hookId = (INT_PTR)hHook >> 16; - int subscriberId = ((INT_PTR)hHook & 0xFFFF) - 1; - - mir_cslock lck(csHooks); - - THook *p = nullptr; - for (auto &it : hooks) - if (it->id == hookId) { - p = it; - break; - } - - if (p == nullptr) - return 1; - - if (subscriberId >= p->subscriberCount || subscriberId < 0) - return 1; - - p->subscriber[subscriberId].type = 0; - p->subscriber[subscriberId].pfnHook = nullptr; - p->subscriber[subscriberId].hOwner = nullptr; - while (p->subscriberCount && p->subscriber[p->subscriberCount - 1].type == 0) - p->subscriberCount--; - if (p->subscriberCount == 0) { - mir_free(p->subscriber); - p->subscriber = nullptr; - } - return 0; -} - -MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst) -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks.rev_iter()) { - if (it->subscriberCount == 0) - continue; - - for (int j = it->subscriberCount - 1; j >= 0; j--) { - if (it->subscriber[j].hOwner != hInst) - continue; - - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); - UnhookEvent((HANDLE)((it->id << 16) + j + 1)); - if (it->subscriberCount == 0) - break; - } - } -} - -MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject) -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks.rev_iter()) { - if (it->subscriberCount == 0) - continue; - - for (int j = it->subscriberCount - 1; j >= 0; j--) { - if (it->subscriber[j].object == pObject) { - UnhookEvent((HANDLE)((it->id << 16) + j + 1)); - if (it->subscriberCount == 0) - break; - } - } - } -} - -static void DestroyHooks() -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks) { - if (it->subscriberCount) - mir_free(it->subscriber); - delete it; - } -} - -/////////////////////SERVICES - -static __inline TService* FindServiceByName(const char *name) -{ - unsigned hash = mir_hashstr(name); - return services.find((TService*)&hash); -} - -static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam) -{ - if (name == nullptr) - return nullptr; - - TService tmp; - tmp.nameHash = mir_hashstr(name); - - mir_cslock lck(csServices); - - if (services.getIndex(&tmp) != -1) - return nullptr; - - TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name)); - strcpy(p->name, name); - p->nameHash = tmp.nameHash; - p->pfnService = serviceProc; - p->hOwner = GetInstByAddress(serviceProc); - p->flags = type; - p->lParam = lParam; - p->object = object; - services.insert(p); - - return (HANDLE)tmp.nameHash; -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc) -{ - return CreateServiceInt(0, name, serviceProc, nullptr, 0); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam) -{ - return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, nullptr, lParam); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object) -{ - return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam) -{ - return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam); -} - -MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc) -{ - char str[MAXMODULELABELLENGTH * 2]; - strncpy_s(str, szModule, _TRUNCATE); - strncat_s(str, szService, _TRUNCATE); - return CreateServiceFunction(str, serviceProc); -} - -MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService) -{ - mir_cslock lck(csServices); - - int idx = services.getIndex((TService*)&hService); - if (idx != -1) { - mir_free(services[idx]); - services.remove(idx); - } - - return 0; -} - -MIR_CORE_DLL(bool) ServiceExists(const char *name) -{ - if (name == nullptr) - return FALSE; - - mir_cslock lck(csServices); - return FindServiceByName(name) != nullptr; -} - -MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam, LPARAM lParam) -{ - if (name == nullptr) - return CALLSERVICE_NOTFOUND; - - TService *pService; - { - mir_cslock lck(csServices); - if ((pService = FindServiceByName(name)) == nullptr) - return CALLSERVICE_NOTFOUND; - } - - MIRANDASERVICE pfnService = pService->pfnService; - int flags = pService->flags; - LPARAM fnParam = pService->lParam; - void* object = pService->object; - switch (flags) { - case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam); - case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam); - case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam); - default: return pfnService(wParam, lParam); - } -} - -static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam) -{ - TServiceToMainThreadItem *item = (TServiceToMainThreadItem*)dwParam; - item->result = CallService(item->name, item->wParam, item->lParam); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam) -{ - if (name == nullptr) - return CALLSERVICE_NOTFOUND; - - // the service is looked up within the main thread, since the time it takes - // for the APC queue to clear the service being called maybe removed. - // even thou it may exists before the call, the critsec can't be locked between calls. - if (GetCurrentThreadId() == mainThreadId) - return CallService(name, wParam, lParam); - - TServiceToMainThreadItem item; - item.wParam = wParam; - item.lParam = lParam; - item.name = name; - item.hDoneEvent = getThreadEvent(); - QueueMainThread(CallServiceToMainAPCFunc, &item, item.hDoneEvent); - return item.result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) CallFunctionAsync(void(__stdcall *func)(void *), void *arg) -{ - if (GetCurrentThreadId() == mainThreadId) - func(arg); - else - QueueMainThread((PAPCFUNC)func, arg, nullptr); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TSyncCallParam -{ - INT_PTR(__stdcall *func)(void *); - void *arg; - HANDLE hDoneEvent; - INT_PTR result; -}; - -static void CALLBACK CallFuncToMainAPCFunc(ULONG_PTR dwParam) -{ - TSyncCallParam *item = (TSyncCallParam*)dwParam; - item->result = (*item->func)(item->arg); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(__stdcall *func)(void *), void *arg) -{ - if (GetCurrentThreadId() == mainThreadId) - return func(arg); - - TSyncCallParam param = { func, arg, getThreadEvent() }; - QueueMainThread(CallFuncToMainAPCFunc, ¶m, param.hDoneEvent); - return param.result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst) -{ - mir_cslock lck(csServices); - - for (auto &it : services.rev_iter()) { - if (it->hOwner == hInst) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - DestroyServiceFunction((HANDLE)it->nameHash); - } - } -} - -MIR_CORE_DLL(void) KillObjectServices(void* pObject) -{ - mir_cslock lck(csServices); - - for (auto &it : services.rev_iter()) - if (it->object == pObject) - DestroyServiceFunction((HANDLE)it->nameHash); -} - -static void DestroyServices() -{ - mir_cslock lck(csServices); - - for (auto &it : services) - mir_free(it); -} - -/////////////////////////////////////////////////////////////////////////////// - -int InitialiseModularEngine(void) -{ - mainThreadId = GetCurrentThreadId(); - return 0; -} - -void DestroyModularEngine(void) -{ - DestroyHooks(); - DestroyServices(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// list of hooks
+
+static int compareHooks(const THook* p1, const THook* p2)
+{
+ return strcmp(p1->name, p2->name);
+}
+
+static LIST<THook> hooks(50, compareHooks);
+
+struct THookToMainThreadItem
+{
+ THook* hook;
+ HANDLE hDoneEvent;
+ WPARAM wParam;
+ LPARAM lParam;
+ int result;
+};
+
+// list of services
+
+struct TService
+{
+ uint32_t nameHash;
+ HINSTANCE hOwner;
+ union
+ {
+ MIRANDASERVICE pfnService;
+ MIRANDASERVICEPARAM pfnServiceParam;
+ MIRANDASERVICEOBJ pfnServiceObj;
+ MIRANDASERVICEOBJPARAM pfnServiceObjParam;
+ };
+ int flags;
+ LPARAM lParam;
+ void* object;
+ char name[1];
+};
+
+LIST<TService> services(100, NumericKeySortT);
+
+struct TServiceToMainThreadItem
+{
+ HANDLE hDoneEvent;
+ WPARAM wParam;
+ LPARAM lParam;
+ int result;
+ const char *name;
+};
+
+// other static variables
+static BOOL bServiceMode = FALSE;
+static mir_cs csHooks, csServices;
+static uint32_t mainThreadId;
+static int sttHookId = 1;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+__forceinline HANDLE getThreadEvent()
+{
+ HANDLE pData = (HANDLE)TlsGetValue(mir_tls);
+ if (pData == nullptr) {
+ pData = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ TlsSetValue(mir_tls, pData);
+ }
+ return pData;
+}
+
+static int QueueMainThread(PAPCFUNC pFunc, void* pParam, HANDLE hDoneEvent)
+{
+ int result = PostMessage(hAPCWindow, WM_USER + 1, (WPARAM)pFunc, (LPARAM)pParam); // let this get processed in its own time
+ if (hDoneEvent)
+ WaitForSingleObject(hDoneEvent, INFINITE);
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HOOKS
+
+MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name)
+{
+ if (name == nullptr)
+ return nullptr;
+
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) != -1)
+ return hooks[idx];
+
+ THook *newItem = new THook();
+ strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[MAXMODULELABELLENGTH - 1] = 0;
+ newItem->id = sttHookId++;
+ hooks.insert(newItem);
+ return (HANDLE)newItem;
+}
+
+MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent)
+{
+ if (hEvent == nullptr)
+ return 1;
+
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)hEvent)) == -1)
+ return 1;
+
+ THook *p = hooks[idx];
+ p->secretSignature = 0;
+ if (p->subscriberCount) {
+ mir_free(p->subscriber);
+ p->subscriber = nullptr;
+ p->subscriberCount = 0;
+ }
+ hooks.remove(idx);
+ delete p;
+ return 0;
+}
+
+MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook)
+{
+ THook *p = (THook*)hEvent;
+
+ mir_cslock lck(csHooks);
+ if (hooks.getIndex(p) != -1)
+ p->pfnHook = pfnHook;
+ return 0;
+}
+
+MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam, LPARAM lParam)
+{
+ int idx;
+ if ((idx = hooks.getIndex((THook *)pszEvent)) == -1)
+ return -1;
+
+ THook *p = hooks[idx];
+ if (p == nullptr || hInst == nullptr)
+ return -1;
+
+ mir_cslock lck(p->csHook);
+ for (int i = 0; i < p->subscriberCount; i++) {
+ THookSubscriber* s = &p->subscriber[i];
+ if (s->hOwner != hInst)
+ continue;
+
+ int returnVal;
+ switch (s->type) {
+ case 1: returnVal = s->pfnHook(wParam, lParam); break;
+ case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break;
+ case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break;
+ case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break;
+ case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break;
+ default: continue;
+ }
+ if (returnVal)
+ return returnVal;
+ }
+
+ if (p->subscriberCount == 0 && p->pfnHook != nullptr)
+ return p->pfnHook(wParam, lParam);
+
+ return 0;
+}
+
+MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam, LPARAM lParam)
+{
+ THook *p = (THook*)hEvent;
+ if (p == nullptr || pObject == nullptr)
+ return -1;
+
+ mir_cslock lck(p->csHook);
+ for (int i = 0; i < p->subscriberCount; i++) {
+ THookSubscriber* s = &p->subscriber[i];
+ if (s->object != pObject)
+ continue;
+
+ int returnVal;
+ switch (s->type) {
+ case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break;
+ case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break;
+ default: continue;
+ }
+ if (returnVal)
+ return returnVal;
+ }
+
+ if (p->subscriberCount == 0 && p->pfnHook != nullptr)
+ return p->pfnHook(wParam, lParam);
+
+ return 0;
+}
+
+static int CallHookSubscribers(THook *p, WPARAM wParam, LPARAM lParam)
+{
+ if (p == nullptr)
+ return -1;
+
+ mir_cslock lck(p->csHook);
+
+ // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though.
+ for (int i = 0; i < p->subscriberCount; i++) {
+ THookSubscriber* s = &p->subscriber[i];
+
+ int returnVal;
+ switch (s->type) {
+ case 1: returnVal = s->pfnHook(wParam, lParam); break;
+ case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break;
+ case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break;
+ case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break;
+ case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break;
+ default: continue;
+ }
+ if (returnVal)
+ return returnVal;
+ }
+
+ // call the default hook if any
+ if (p->pfnHook != nullptr)
+ return p->pfnHook(wParam, lParam);
+
+ return 0;
+}
+
+enum { hookOk, hookEmpty, hookInvalid };
+
+int checkHook(THook *p)
+{
+ if (p == nullptr)
+ return hookInvalid;
+
+ int ret;
+ __try {
+ if (p->secretSignature != HOOK_SECRET_SIGNATURE)
+ ret = hookInvalid;
+ else if (p->subscriberCount == 0 && p->pfnHook == nullptr)
+ ret = hookEmpty;
+ else
+ ret = hookOk;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ ret = hookInvalid;
+ }
+
+ return ret;
+}
+
+static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam)
+{
+ THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam;
+ item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam);
+ SetEvent(item->hDoneEvent);
+}
+
+MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam)
+{
+ switch (checkHook((THook*)hEvent)) {
+ case hookInvalid: return -1;
+ case hookEmpty: return 0;
+ }
+
+ if (GetCurrentThreadId() == mainThreadId)
+ return CallHookSubscribers((THook*)hEvent, wParam, lParam);
+
+ THookToMainThreadItem item;
+ item.hDoneEvent = getThreadEvent();
+ item.hook = (THook*)hEvent;
+ item.wParam = wParam;
+ item.lParam = lParam;
+ QueueMainThread(HookToMainAPCFunc, &item, item.hDoneEvent);
+ return item.result;
+}
+
+MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam, LPARAM lParam)
+{
+ switch (checkHook((THook*)hEvent)) {
+ case hookInvalid: return -1;
+ case hookEmpty: return 0;
+ }
+
+ return CallHookSubscribers((THook*)hEvent, wParam, lParam);
+}
+
+extern "C" MIR_CORE_DLL(int) GetSubscribersCount(THook* pHook)
+{
+ switch (checkHook(pHook)) {
+ case hookInvalid:
+ case hookEmpty: return 0;
+ }
+ return pHook->subscriberCount;
+}
+
+static HANDLE HookEventInt(int type, const char *name, MIRANDAHOOK hookProc, void* object, LPARAM lParam)
+{
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) == -1)
+ return nullptr;
+
+ THook *p = hooks[idx];
+ p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1));
+
+ THookSubscriber &s = p->subscriber[p->subscriberCount];
+ s.type = type;
+ s.pfnHook = hookProc;
+ s.object = object;
+ s.lParam = lParam;
+ s.hOwner = GetInstByAddress(hookProc);
+ p->subscriberCount++;
+
+ return (HANDLE)((p->id << 16) | p->subscriberCount);
+}
+
+MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc)
+{
+ return HookEventInt(1, name, hookProc, nullptr, 0);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam)
+{
+ return HookEventInt(2, name, (MIRANDAHOOK)hookProc, nullptr, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object)
+{
+ return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam)
+{
+ return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc)
+{
+ mir_cslockfull lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) == -1) {
+ lck.unlock();
+ hookProc(0, 0);
+ return nullptr;
+ }
+
+ THook *p = hooks[idx];
+ p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1));
+
+ THookSubscriber &s = p->subscriber[p->subscriberCount];
+ memset(&s, 0, sizeof(THookSubscriber));
+ s.type = 1;
+ s.pfnHook = hookProc;
+ s.hOwner = GetInstByAddress(hookProc);
+
+ p->subscriberCount++;
+ return (HANDLE)((p->id << 16) | p->subscriberCount);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message)
+{
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) == -1)
+ return nullptr;
+
+ THook *p = hooks[idx];
+ p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1));
+ p->subscriber[p->subscriberCount].type = 5;
+ p->subscriber[p->subscriberCount].hwnd = hwnd;
+ p->subscriber[p->subscriberCount].message = message;
+ p->subscriberCount++;
+ return (HANDLE)((p->id << 16) | p->subscriberCount);
+}
+
+MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook)
+{
+ if (hHook == nullptr)
+ return 0;
+
+ int hookId = (INT_PTR)hHook >> 16;
+ int subscriberId = ((INT_PTR)hHook & 0xFFFF) - 1;
+
+ mir_cslock lck(csHooks);
+
+ THook *p = nullptr;
+ for (auto &it : hooks)
+ if (it->id == hookId) {
+ p = it;
+ break;
+ }
+
+ if (p == nullptr)
+ return 1;
+
+ if (subscriberId >= p->subscriberCount || subscriberId < 0)
+ return 1;
+
+ p->subscriber[subscriberId].type = 0;
+ p->subscriber[subscriberId].pfnHook = nullptr;
+ p->subscriber[subscriberId].hOwner = nullptr;
+ while (p->subscriberCount && p->subscriber[p->subscriberCount - 1].type == 0)
+ p->subscriberCount--;
+ if (p->subscriberCount == 0) {
+ mir_free(p->subscriber);
+ p->subscriber = nullptr;
+ }
+ return 0;
+}
+
+MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst)
+{
+ mir_cslock lck(csHooks);
+
+ for (auto &it : hooks.rev_iter()) {
+ if (it->subscriberCount == 0)
+ continue;
+
+ for (int j = it->subscriberCount - 1; j >= 0; j--) {
+ if (it->subscriber[j].hOwner != hInst)
+ continue;
+
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(it->subscriber[j].hOwner, szModuleName, sizeof(szModuleName));
+ UnhookEvent((HANDLE)((it->id << 16) + j + 1));
+ if (it->subscriberCount == 0)
+ break;
+ }
+ }
+}
+
+MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject)
+{
+ mir_cslock lck(csHooks);
+
+ for (auto &it : hooks.rev_iter()) {
+ if (it->subscriberCount == 0)
+ continue;
+
+ for (int j = it->subscriberCount - 1; j >= 0; j--) {
+ if (it->subscriber[j].object == pObject) {
+ UnhookEvent((HANDLE)((it->id << 16) + j + 1));
+ if (it->subscriberCount == 0)
+ break;
+ }
+ }
+ }
+}
+
+static void DestroyHooks()
+{
+ mir_cslock lck(csHooks);
+
+ for (auto &it : hooks) {
+ if (it->subscriberCount)
+ mir_free(it->subscriber);
+ delete it;
+ }
+}
+
+/////////////////////SERVICES
+
+static __inline TService* FindServiceByName(const char *name)
+{
+ unsigned hash = mir_hashstr(name);
+ return services.find((TService*)&hash);
+}
+
+static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam)
+{
+ if (name == nullptr)
+ return nullptr;
+
+ TService tmp;
+ tmp.nameHash = mir_hashstr(name);
+
+ mir_cslock lck(csServices);
+
+ if (services.getIndex(&tmp) != -1)
+ return nullptr;
+
+ TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name));
+ strcpy(p->name, name);
+ p->nameHash = tmp.nameHash;
+ p->pfnService = serviceProc;
+ p->hOwner = GetInstByAddress(serviceProc);
+ p->flags = type;
+ p->lParam = lParam;
+ p->object = object;
+ services.insert(p);
+
+ return (HANDLE)tmp.nameHash;
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc)
+{
+ return CreateServiceInt(0, name, serviceProc, nullptr, 0);
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam)
+{
+ return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, nullptr, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object)
+{
+ return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0);
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam)
+{
+ return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc)
+{
+ char str[MAXMODULELABELLENGTH * 2];
+ strncpy_s(str, szModule, _TRUNCATE);
+ strncat_s(str, szService, _TRUNCATE);
+ return CreateServiceFunction(str, serviceProc);
+}
+
+MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService)
+{
+ mir_cslock lck(csServices);
+
+ int idx = services.getIndex((TService*)&hService);
+ if (idx != -1) {
+ mir_free(services[idx]);
+ services.remove(idx);
+ }
+
+ return 0;
+}
+
+MIR_CORE_DLL(bool) ServiceExists(const char *name)
+{
+ if (name == nullptr)
+ return FALSE;
+
+ mir_cslock lck(csServices);
+ return FindServiceByName(name) != nullptr;
+}
+
+MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam, LPARAM lParam)
+{
+ if (name == nullptr)
+ return CALLSERVICE_NOTFOUND;
+
+ TService *pService;
+ {
+ mir_cslock lck(csServices);
+ if ((pService = FindServiceByName(name)) == nullptr)
+ return CALLSERVICE_NOTFOUND;
+ }
+
+ MIRANDASERVICE pfnService = pService->pfnService;
+ int flags = pService->flags;
+ LPARAM fnParam = pService->lParam;
+ void* object = pService->object;
+ switch (flags) {
+ case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam);
+ case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam);
+ case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam);
+ default: return pfnService(wParam, lParam);
+ }
+}
+
+static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam)
+{
+ TServiceToMainThreadItem *item = (TServiceToMainThreadItem*)dwParam;
+ item->result = CallService(item->name, item->wParam, item->lParam);
+ SetEvent(item->hDoneEvent);
+}
+
+MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam)
+{
+ if (name == nullptr)
+ return CALLSERVICE_NOTFOUND;
+
+ // the service is looked up within the main thread, since the time it takes
+ // for the APC queue to clear the service being called maybe removed.
+ // even thou it may exists before the call, the critsec can't be locked between calls.
+ if (GetCurrentThreadId() == mainThreadId)
+ return CallService(name, wParam, lParam);
+
+ TServiceToMainThreadItem item;
+ item.wParam = wParam;
+ item.lParam = lParam;
+ item.name = name;
+ item.hDoneEvent = getThreadEvent();
+ QueueMainThread(CallServiceToMainAPCFunc, &item, item.hDoneEvent);
+ return item.result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) CallFunctionAsync(void(__stdcall *func)(void *), void *arg)
+{
+ if (GetCurrentThreadId() == mainThreadId)
+ func(arg);
+ else
+ QueueMainThread((PAPCFUNC)func, arg, nullptr);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TSyncCallParam
+{
+ INT_PTR(__stdcall *func)(void *);
+ void *arg;
+ HANDLE hDoneEvent;
+ INT_PTR result;
+};
+
+static void CALLBACK CallFuncToMainAPCFunc(ULONG_PTR dwParam)
+{
+ TSyncCallParam *item = (TSyncCallParam*)dwParam;
+ item->result = (*item->func)(item->arg);
+ SetEvent(item->hDoneEvent);
+}
+
+MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(__stdcall *func)(void *), void *arg)
+{
+ if (GetCurrentThreadId() == mainThreadId)
+ return func(arg);
+
+ TSyncCallParam param = { func, arg, getThreadEvent() };
+ QueueMainThread(CallFuncToMainAPCFunc, ¶m, param.hDoneEvent);
+ return param.result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst)
+{
+ mir_cslock lck(csServices);
+
+ for (auto &it : services.rev_iter()) {
+ if (it->hOwner == hInst) {
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName));
+ DestroyServiceFunction((HANDLE)it->nameHash);
+ }
+ }
+}
+
+MIR_CORE_DLL(void) KillObjectServices(void* pObject)
+{
+ mir_cslock lck(csServices);
+
+ for (auto &it : services.rev_iter())
+ if (it->object == pObject)
+ DestroyServiceFunction((HANDLE)it->nameHash);
+}
+
+static void DestroyServices()
+{
+ mir_cslock lck(csServices);
+
+ for (auto &it : services)
+ mir_free(it);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int InitialiseModularEngine(void)
+{
+ mainThreadId = GetCurrentThreadId();
+ return 0;
+}
+
+void DestroyModularEngine(void)
+{
+ DestroyHooks();
+ DestroyServices();
+}
diff --git a/src/mir_core/src/mstring.cpp b/src/mir_core/src/mstring.cpp index 2bff18e7f1..04a26f8a9d 100644 --- a/src/mir_core/src/mstring.cpp +++ b/src/mir_core/src/mstring.cpp @@ -1,145 +1,145 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CMBaseString - -class CNilMStringData : public CMStringData -{ -public: - CNilMStringData(); - -public: - wchar_t achNil[2]; -}; - -CNilMStringData::CNilMStringData() -{ - nRefs = 2; // Never gets freed - nDataLength = 0; - nAllocLength = 0; - achNil[0] = 0; - achNil[1] = 0; -} - -static CNilMStringData *m_nil = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// CMBaseString - -MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize) -{ - nChars++; // nil char - size_t nDataBytes = nCharSize * nChars; - size_t nTotalSize = nDataBytes + sizeof(CMStringData); - - CMStringData *pData = static_cast<CMStringData*>(malloc(nTotalSize)); - if (pData == nullptr) - return nullptr; - - pData->nRefs = 1; - pData->nAllocLength = nChars - 1; - pData->nDataLength = 0; - return pData; -} - -MIR_CORE_DLL(void) mirstr_free(CMStringData *pData) -{ - free(pData); -} - -MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize) -{ - nChars++; // nil char - uint32_t nDataBytes = nCharSize * nChars; - uint32_t nTotalSize = nDataBytes + sizeof(CMStringData); - - CMStringData *pNewData = static_cast<CMStringData*>(realloc(pData, nTotalSize)); - if (pNewData == nullptr) - return nullptr; - - pNewData->nAllocLength = nChars - 1; - return pNewData; -} - -MIR_CORE_DLL(CMStringData*) mirstr_getNil() -{ - if (m_nil == nullptr) - m_nil = new CNilMStringData(); - m_nil->AddRef(); - return m_nil; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// CMStringData - -MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis) -{ - pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary - if (pThis->nRefs == 0) - pThis->nRefs = -1; -} - -MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis) -{ - if (InterlockedDecrement(&pThis->nRefs) <= 0) - mirstr_free(pThis); -} - -MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis) -{ - if (pThis->IsLocked()) - { - pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary - if (pThis->nRefs == 0) - pThis->nRefs = 1; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// don't remove it -// this code just instantiates templates for CMStringW[A/W] - -#ifdef _MSC_VER -template MIR_CORE_EXPORT CMStringW; -template MIR_CORE_EXPORT CMStringA; -#endif - -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const wchar_t *psz2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const wchar_t *psz1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, wchar_t ch2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, char ch2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(wchar_t ch1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(char ch1, const CMStringW& str2); - -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const char *psz2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const char *psz1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, wchar_t ch2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, char ch2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(wchar_t ch1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(char ch1, const CMStringA& str2); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMBaseString
+
+class CNilMStringData : public CMStringData
+{
+public:
+ CNilMStringData();
+
+public:
+ wchar_t achNil[2];
+};
+
+CNilMStringData::CNilMStringData()
+{
+ nRefs = 2; // Never gets freed
+ nDataLength = 0;
+ nAllocLength = 0;
+ achNil[0] = 0;
+ achNil[1] = 0;
+}
+
+static CNilMStringData *m_nil = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMBaseString
+
+MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize)
+{
+ nChars++; // nil char
+ size_t nDataBytes = nCharSize * nChars;
+ size_t nTotalSize = nDataBytes + sizeof(CMStringData);
+
+ CMStringData *pData = static_cast<CMStringData*>(malloc(nTotalSize));
+ if (pData == nullptr)
+ return nullptr;
+
+ pData->nRefs = 1;
+ pData->nAllocLength = nChars - 1;
+ pData->nDataLength = 0;
+ return pData;
+}
+
+MIR_CORE_DLL(void) mirstr_free(CMStringData *pData)
+{
+ free(pData);
+}
+
+MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize)
+{
+ nChars++; // nil char
+ uint32_t nDataBytes = nCharSize * nChars;
+ uint32_t nTotalSize = nDataBytes + sizeof(CMStringData);
+
+ CMStringData *pNewData = static_cast<CMStringData*>(realloc(pData, nTotalSize));
+ if (pNewData == nullptr)
+ return nullptr;
+
+ pNewData->nAllocLength = nChars - 1;
+ return pNewData;
+}
+
+MIR_CORE_DLL(CMStringData*) mirstr_getNil()
+{
+ if (m_nil == nullptr)
+ m_nil = new CNilMStringData();
+ m_nil->AddRef();
+ return m_nil;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMStringData
+
+MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis)
+{
+ pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary
+ if (pThis->nRefs == 0)
+ pThis->nRefs = -1;
+}
+
+MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis)
+{
+ if (InterlockedDecrement(&pThis->nRefs) <= 0)
+ mirstr_free(pThis);
+}
+
+MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis)
+{
+ if (pThis->IsLocked())
+ {
+ pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary
+ if (pThis->nRefs == 0)
+ pThis->nRefs = 1;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// don't remove it
+// this code just instantiates templates for CMStringW[A/W]
+
+#ifdef _MSC_VER
+template MIR_CORE_EXPORT CMStringW;
+template MIR_CORE_EXPORT CMStringA;
+#endif
+
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const CMStringW& str2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const wchar_t *psz2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const wchar_t *psz1, const CMStringW& str2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, wchar_t ch2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, char ch2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(wchar_t ch1, const CMStringW& str2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(char ch1, const CMStringW& str2);
+
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const CMStringA& str2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const char *psz2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const char *psz1, const CMStringA& str2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, wchar_t ch2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, char ch2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(wchar_t ch1, const CMStringA& str2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(char ch1, const CMStringA& str2);
diff --git a/src/mir_core/src/stdafx.cxx b/src/mir_core/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/src/mir_core/src/stdafx.cxx +++ b/src/mir_core/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/mir_core/src/stdafx.h b/src/mir_core/src/stdafx.h index 54ec535a4c..dd6d93d679 100644 --- a/src/mir_core/src/stdafx.h +++ b/src/mir_core/src/stdafx.h @@ -1,84 +1,84 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#define INCL_WINSOCK_API_TYPEDEFS 1 - -#ifdef _MSC_VER - #include <winsock2.h> - #include <ws2tcpip.h> - #include <windows.h> - #include <windowsx.h> - #include <shlobj.h> - #include <commctrl.h> - #include <ShellAPI.h> - #include <vssym32.h> - #include <Uxtheme.h> - #include <Richedit.h> - #include <Wtsapi32.h> - - #include <process.h> - #include <io.h> - #include <direct.h> - - #ifdef _DEBUG - #include <crtdbg.h> - #endif -#else - #include <Elementary.h> -#endif // _WINDOWS - -#include <malloc.h> -#include <stdio.h> -#include <time.h> -#include <stdarg.h> -#include <stddef.h> -#include <limits.h> -#include <string.h> -#include <locale.h> - -#define __NO_CMPLUGIN_NEEDED -#include <m_system.h> -#include <m_database.h> -#include <m_db_int.h> -#include <newpluginapi.h> -#include <m_langpack.h> -#include <m_metacontacts.h> -#include <m_skin.h> -#include <m_icolib.h> -#include <m_netlib.h> -#include <m_timezones.h> -#include <m_protocols.h> -#include <m_button.h> -#include <m_gui.h> -#include <m_chat_int.h> - -#include "miranda.h" - -#include <m_xml.h> - -#include <m_string.inl> - -void GetDefaultLang(); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+#define INCL_WINSOCK_API_TYPEDEFS 1
+
+#ifdef _MSC_VER
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <windowsx.h>
+ #include <shlobj.h>
+ #include <commctrl.h>
+ #include <ShellAPI.h>
+ #include <vssym32.h>
+ #include <Uxtheme.h>
+ #include <Richedit.h>
+ #include <Wtsapi32.h>
+
+ #include <process.h>
+ #include <io.h>
+ #include <direct.h>
+
+ #ifdef _DEBUG
+ #include <crtdbg.h>
+ #endif
+#else
+ #include <Elementary.h>
+#endif // _WINDOWS
+
+#include <malloc.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <limits.h>
+#include <string.h>
+#include <locale.h>
+
+#define __NO_CMPLUGIN_NEEDED
+#include <m_system.h>
+#include <m_database.h>
+#include <m_db_int.h>
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_metacontacts.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_netlib.h>
+#include <m_timezones.h>
+#include <m_protocols.h>
+#include <m_button.h>
+#include <m_gui.h>
+#include <m_chat_int.h>
+
+#include "miranda.h"
+
+#include <m_xml.h>
+
+#include <m_string.inl>
+
+void GetDefaultLang();
diff --git a/src/mir_core/src/utf.cpp b/src/mir_core/src/utf.cpp index 1e17ed60e8..eefa519727 100644 --- a/src/mir_core/src/utf.cpp +++ b/src/mir_core/src/utf.cpp @@ -1,441 +1,441 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - - Copyright 2000 Alexandre Julliard of Wine project - (UTF-8 conversion routines) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */ -static const char utf8_length[128] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */ - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0-0xcf */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0-0xdf */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xe0-0xef */ - 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0-0xff */ -}; - -/* first byte mask depending on UTF-8 sequence length */ -static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 }; - -/* minimum Unicode value depending on UTF-8 sequence length */ -static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 }; - -/* get the next char value taking surrogates into account */ -static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen) -{ - if (src[0] >= 0xd800 && src[0] <= 0xdfff) { /* surrogate pair */ - if (src[0] > 0xdbff || /* invalid high surrogate */ - srclen <= 1 || /* missing low surrogate */ - src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */ - return 0; - return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff); - } - return src[0]; -} - -/* query necessary dst length for src string */ -static int mir_utf8len(const wchar_t *src, unsigned int srclen) -{ - int len; - unsigned int val; - - for (len = 0; srclen; srclen--, src++) { - if (*src < 0x80) { /* 0x00-0x7f: 1 byte */ - len++; - continue; - } - if (*src < 0x800) { /* 0x80-0x7ff: 2 bytes */ - len += 2; - continue; - } - if (!(val = getSurrogateValue(src, srclen))) - return -2; - - if (val < 0x10000) /* 0x800-0xffff: 3 bytes */ - len += 3; - else { /* 0x10000-0x10ffff: 4 bytes */ - len += 4; - src++; - srclen--; - } - } - return len; -} - -MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src) -{ - if (src == nullptr) - return 0; - - return mir_utf8len(src, (int)wcslen(src)); -} - -/* wide char to UTF-8 string conversion */ -/* return -1 on dst buffer overflow, -2 on invalid input char */ -int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen) -{ - int len; - - for (len = dstlen; srclen; srclen--, src++) { - wchar_t ch = *src; - unsigned int val; - - if (ch < 0x80) { /* 0x00-0x7f: 1 byte */ - if (!len--) return -1; /* overflow */ - *dst++ = ch; - continue; - } - - if (ch < 0x800) { /* 0x80-0x7ff: 2 bytes */ - if ((len -= 2) < 0) return -1; /* overflow */ - dst[1] = 0x80 | (ch & 0x3f); - ch >>= 6; - dst[0] = 0xc0 | ch; - dst += 2; - continue; - } - - if (!(val = getSurrogateValue(src, srclen))) - return -2; - - if (val < 0x10000) { /* 0x800-0xffff: 3 bytes */ - if ((len -= 3) < 0) return -1; /* overflow */ - dst[2] = 0x80 | (val & 0x3f); - val >>= 6; - dst[1] = 0x80 | (val & 0x3f); - val >>= 6; - dst[0] = 0xe0 | val; - dst += 3; - } - else { /* 0x10000-0x10ffff: 4 bytes */ - if ((len -= 4) < 0) return -1; /* overflow */ - dst[3] = 0x80 | (val & 0x3f); - val >>= 6; - dst[2] = 0x80 | (val & 0x3f); - val >>= 6; - dst[1] = 0x80 | (val & 0x3f); - val >>= 6; - dst[0] = 0xf0 | val; - dst += 4; - src++; - srclen--; - } - } - return dstlen - len; -} - -/* helper for the various utf8 mbstowcs functions */ -static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend) -{ - unsigned int len = utf8_length[ch - 0x80]; - unsigned int res = ch & utf8_mask[len]; - const char *end = *str + len; - - if (end > strend) return ~0; - switch (len) { - case 3: - if ((ch = end[-3] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - - case 2: - if ((ch = end[-2] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - - case 1: - if ((ch = end[-1] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - if (res < utf8_minval[len]) break; - return res; - } - return ~0; -} - -/* query necessary dst length for src string */ -static int Utf8toUcs2Len(const char *src, size_t srclen) -{ - int ret = 0; - unsigned int res; - const char *srcend = src + srclen; - - while (src < srcend) { - unsigned char ch = *src++; - if (ch < 0x80) { /* special fast case for 7-bit ASCII */ - ret++; - continue; - } - if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff) { - if (res > 0xffff) ret++; - ret++; - } - else return -2; /* bad char */ - /* otherwise ignore it */ - } - return ret; -} - -/* UTF-8 to wide char string conversion */ -/* return -1 on dst buffer overflow, -2 on invalid input char */ -MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen) -{ - unsigned int res; - const char *srcend = src + srclen; // including trailing zero - wchar_t *dstend = dst + dstlen; - - while ((dst < dstend) && (src < srcend)) { - unsigned char ch = *src++; - if (ch < 0x80) { /* special fast case for 7-bit ASCII */ - *dst++ = ch; - continue; - } - - if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff) - *dst++ = res; - else if (res <= 0x10ffff) { /* we need surrogates */ - if (dst == dstend - 1) - return -1; /* overflow */ - res -= 0x10000; - *dst++ = 0xd800 | (res >> 10); - *dst++ = 0xdc00 | (res & 0x3ff); - } - else return -2; /* bad char */ - } - - if (src < srcend) - return -1; /* overflow */ - - return (int)(dstlen - (dstend - dst)); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8decode - converts UTF8-encoded string to the UCS2/MBCS format - -#ifdef _MSC_VER -MIR_CORE_DLL(char*) mir_utf8decodecp(char *str, int codepage, wchar_t **ucs2) -{ - bool needs_free = false; - wchar_t* tempBuf = nullptr; - if (ucs2) - *ucs2 = nullptr; - - if (str == nullptr) - return nullptr; - - size_t len = strlen(str); - if (len < 2) { - if (ucs2 != nullptr) { - *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); - MultiByteToWideChar(codepage, 0, str, (int)len, tempBuf, (int)len); - tempBuf[len] = 0; - } - return str; - } - - int destlen = Utf8toUcs2Len(str, len); - if (destlen < 0) - return nullptr; - - if (ucs2 == nullptr) { - __try { - tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - tempBuf = nullptr; - needs_free = true; - } - } - - if (tempBuf == nullptr) { - tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); - if (tempBuf == nullptr) - return nullptr; - } - - Utf8toUcs2(str, len, tempBuf, destlen); - tempBuf[destlen] = 0; - WideCharToMultiByte(codepage, 0, tempBuf, -1, str, (int)len + 1, "?", nullptr); - - if (ucs2) - *ucs2 = tempBuf; - else if (needs_free) - mir_free(tempBuf); - - return str; -} - -MIR_CORE_DLL(char*) mir_utf8decode(char *str, wchar_t **ucs2) -{ - return mir_utf8decodecp(str, Langpack_GetDefaultCodePage(), ucs2); -} -#endif - -MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char *str) -{ - if (str == nullptr) - return nullptr; - - size_t len = strlen(str); - - int destlen = Utf8toUcs2Len(str, len); - if (destlen < 0) - return nullptr; - - wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); - if (ucs2 == nullptr) - return nullptr; - - if (Utf8toUcs2(str, len, ucs2, destlen) >= 0) { - ucs2[destlen] = 0; - return ucs2; - } - - mir_free(ucs2); - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8encode - converts MBCS string to the UTF8-encoded format - -#ifdef _MSC_VER -MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage) -{ - int len; - bool needs_free = false; - char* result = nullptr; - wchar_t* tempBuf; - - if (src == nullptr) - return nullptr; - - len = (int)strlen(src); - - __try { - tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); - if (tempBuf == nullptr) return nullptr; - needs_free = true; - } - - len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1); - - int destlen = mir_utf8len(tempBuf, len); - if (destlen >= 0) { - result = (char*)mir_alloc(destlen + 1); - if (result) { - Ucs2toUtf8(tempBuf, len, result, destlen); - result[destlen] = 0; - } - } - - if (needs_free) - mir_free(tempBuf); - - return result; -} - -MIR_CORE_DLL(char*) mir_utf8encode(const char* src) -{ - return mir_utf8encodecp(src, Langpack_GetDefaultCodePage()); -} -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8encode - converts UCS2 string to the UTF8-encoded format - -MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* src) -{ - if (src == nullptr) - return nullptr; - - int len = (int)wcslen(src); - - int destlen = mir_utf8len(src, len); - if (destlen < 0) return nullptr; - - char* result = (char*)mir_alloc(destlen + 1); - if (result == nullptr) - return nullptr; - - Ucs2toUtf8(src, len, result, destlen); - result[destlen] = 0; - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Utf8CheckString - checks if a string is a valid utf8-encoded string - -MIR_CORE_DLL(BOOL) Utf8CheckString(const char *str) -{ - int expect_bytes = 0, utf_found = 0; - - if (!str) return 0; - - while (*str) { - if ((*str & 0x80) == 0) { - /* Looks like an ASCII character */ - if (expect_bytes) - /* byte of UTF-8 character expected */ - return 0; - } - else { - /* Looks like byte of an UTF-8 character */ - if (expect_bytes) { - /* expect_bytes already set: first byte of UTF-8 char already seen */ - if ((*str & 0xC0) != 0x80) { - /* again first byte ?!?! */ - return 0; - } - } - else { - /* First byte of the UTF-8 character */ - /* count initial one bits and set expect_bytes to 1 less */ - char ch = *str; - while (ch & 0x80) { - expect_bytes++; - ch = (ch & 0x7f) << 1; - } - } - /* OK, next byte of UTF-8 character */ - /* Decrement number of expected bytes */ - if (--expect_bytes == 0) - utf_found = 1; - } - str++; - } - - return (utf_found && expect_bytes == 0); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+ Copyright 2000 Alexandre Julliard of Wine project
+ (UTF-8 conversion routines)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */
+static const char utf8_length[128] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0-0xcf */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0-0xdf */
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xe0-0xef */
+ 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0-0xff */
+};
+
+/* first byte mask depending on UTF-8 sequence length */
+static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
+
+/* minimum Unicode value depending on UTF-8 sequence length */
+static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 };
+
+/* get the next char value taking surrogates into account */
+static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen)
+{
+ if (src[0] >= 0xd800 && src[0] <= 0xdfff) { /* surrogate pair */
+ if (src[0] > 0xdbff || /* invalid high surrogate */
+ srclen <= 1 || /* missing low surrogate */
+ src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */
+ return 0;
+ return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff);
+ }
+ return src[0];
+}
+
+/* query necessary dst length for src string */
+static int mir_utf8len(const wchar_t *src, unsigned int srclen)
+{
+ int len;
+ unsigned int val;
+
+ for (len = 0; srclen; srclen--, src++) {
+ if (*src < 0x80) { /* 0x00-0x7f: 1 byte */
+ len++;
+ continue;
+ }
+ if (*src < 0x800) { /* 0x80-0x7ff: 2 bytes */
+ len += 2;
+ continue;
+ }
+ if (!(val = getSurrogateValue(src, srclen)))
+ return -2;
+
+ if (val < 0x10000) /* 0x800-0xffff: 3 bytes */
+ len += 3;
+ else { /* 0x10000-0x10ffff: 4 bytes */
+ len += 4;
+ src++;
+ srclen--;
+ }
+ }
+ return len;
+}
+
+MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src)
+{
+ if (src == nullptr)
+ return 0;
+
+ return mir_utf8len(src, (int)wcslen(src));
+}
+
+/* wide char to UTF-8 string conversion */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen)
+{
+ int len;
+
+ for (len = dstlen; srclen; srclen--, src++) {
+ wchar_t ch = *src;
+ unsigned int val;
+
+ if (ch < 0x80) { /* 0x00-0x7f: 1 byte */
+ if (!len--) return -1; /* overflow */
+ *dst++ = ch;
+ continue;
+ }
+
+ if (ch < 0x800) { /* 0x80-0x7ff: 2 bytes */
+ if ((len -= 2) < 0) return -1; /* overflow */
+ dst[1] = 0x80 | (ch & 0x3f);
+ ch >>= 6;
+ dst[0] = 0xc0 | ch;
+ dst += 2;
+ continue;
+ }
+
+ if (!(val = getSurrogateValue(src, srclen)))
+ return -2;
+
+ if (val < 0x10000) { /* 0x800-0xffff: 3 bytes */
+ if ((len -= 3) < 0) return -1; /* overflow */
+ dst[2] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[1] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[0] = 0xe0 | val;
+ dst += 3;
+ }
+ else { /* 0x10000-0x10ffff: 4 bytes */
+ if ((len -= 4) < 0) return -1; /* overflow */
+ dst[3] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[2] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[1] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[0] = 0xf0 | val;
+ dst += 4;
+ src++;
+ srclen--;
+ }
+ }
+ return dstlen - len;
+}
+
+/* helper for the various utf8 mbstowcs functions */
+static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend)
+{
+ unsigned int len = utf8_length[ch - 0x80];
+ unsigned int res = ch & utf8_mask[len];
+ const char *end = *str + len;
+
+ if (end > strend) return ~0;
+ switch (len) {
+ case 3:
+ if ((ch = end[-3] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+
+ case 2:
+ if ((ch = end[-2] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+
+ case 1:
+ if ((ch = end[-1] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+ if (res < utf8_minval[len]) break;
+ return res;
+ }
+ return ~0;
+}
+
+/* query necessary dst length for src string */
+static int Utf8toUcs2Len(const char *src, size_t srclen)
+{
+ int ret = 0;
+ unsigned int res;
+ const char *srcend = src + srclen;
+
+ while (src < srcend) {
+ unsigned char ch = *src++;
+ if (ch < 0x80) { /* special fast case for 7-bit ASCII */
+ ret++;
+ continue;
+ }
+ if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff) {
+ if (res > 0xffff) ret++;
+ ret++;
+ }
+ else return -2; /* bad char */
+ /* otherwise ignore it */
+ }
+ return ret;
+}
+
+/* UTF-8 to wide char string conversion */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen)
+{
+ unsigned int res;
+ const char *srcend = src + srclen; // including trailing zero
+ wchar_t *dstend = dst + dstlen;
+
+ while ((dst < dstend) && (src < srcend)) {
+ unsigned char ch = *src++;
+ if (ch < 0x80) { /* special fast case for 7-bit ASCII */
+ *dst++ = ch;
+ continue;
+ }
+
+ if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff)
+ *dst++ = res;
+ else if (res <= 0x10ffff) { /* we need surrogates */
+ if (dst == dstend - 1)
+ return -1; /* overflow */
+ res -= 0x10000;
+ *dst++ = 0xd800 | (res >> 10);
+ *dst++ = 0xdc00 | (res & 0x3ff);
+ }
+ else return -2; /* bad char */
+ }
+
+ if (src < srcend)
+ return -1; /* overflow */
+
+ return (int)(dstlen - (dstend - dst));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// mir_utf8decode - converts UTF8-encoded string to the UCS2/MBCS format
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(char*) mir_utf8decodecp(char *str, int codepage, wchar_t **ucs2)
+{
+ bool needs_free = false;
+ wchar_t* tempBuf = nullptr;
+ if (ucs2)
+ *ucs2 = nullptr;
+
+ if (str == nullptr)
+ return nullptr;
+
+ size_t len = strlen(str);
+ if (len < 2) {
+ if (ucs2 != nullptr) {
+ *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t));
+ MultiByteToWideChar(codepage, 0, str, (int)len, tempBuf, (int)len);
+ tempBuf[len] = 0;
+ }
+ return str;
+ }
+
+ int destlen = Utf8toUcs2Len(str, len);
+ if (destlen < 0)
+ return nullptr;
+
+ if (ucs2 == nullptr) {
+ __try {
+ tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ tempBuf = nullptr;
+ needs_free = true;
+ }
+ }
+
+ if (tempBuf == nullptr) {
+ tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t));
+ if (tempBuf == nullptr)
+ return nullptr;
+ }
+
+ Utf8toUcs2(str, len, tempBuf, destlen);
+ tempBuf[destlen] = 0;
+ WideCharToMultiByte(codepage, 0, tempBuf, -1, str, (int)len + 1, "?", nullptr);
+
+ if (ucs2)
+ *ucs2 = tempBuf;
+ else if (needs_free)
+ mir_free(tempBuf);
+
+ return str;
+}
+
+MIR_CORE_DLL(char*) mir_utf8decode(char *str, wchar_t **ucs2)
+{
+ return mir_utf8decodecp(str, Langpack_GetDefaultCodePage(), ucs2);
+}
+#endif
+
+MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ size_t len = strlen(str);
+
+ int destlen = Utf8toUcs2Len(str, len);
+ if (destlen < 0)
+ return nullptr;
+
+ wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t));
+ if (ucs2 == nullptr)
+ return nullptr;
+
+ if (Utf8toUcs2(str, len, ucs2, destlen) >= 0) {
+ ucs2[destlen] = 0;
+ return ucs2;
+ }
+
+ mir_free(ucs2);
+
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// mir_utf8encode - converts MBCS string to the UTF8-encoded format
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage)
+{
+ int len;
+ bool needs_free = false;
+ char* result = nullptr;
+ wchar_t* tempBuf;
+
+ if (src == nullptr)
+ return nullptr;
+
+ len = (int)strlen(src);
+
+ __try {
+ tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t));
+ if (tempBuf == nullptr) return nullptr;
+ needs_free = true;
+ }
+
+ len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1);
+
+ int destlen = mir_utf8len(tempBuf, len);
+ if (destlen >= 0) {
+ result = (char*)mir_alloc(destlen + 1);
+ if (result) {
+ Ucs2toUtf8(tempBuf, len, result, destlen);
+ result[destlen] = 0;
+ }
+ }
+
+ if (needs_free)
+ mir_free(tempBuf);
+
+ return result;
+}
+
+MIR_CORE_DLL(char*) mir_utf8encode(const char* src)
+{
+ return mir_utf8encodecp(src, Langpack_GetDefaultCodePage());
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// mir_utf8encode - converts UCS2 string to the UTF8-encoded format
+
+MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* src)
+{
+ if (src == nullptr)
+ return nullptr;
+
+ int len = (int)wcslen(src);
+
+ int destlen = mir_utf8len(src, len);
+ if (destlen < 0) return nullptr;
+
+ char* result = (char*)mir_alloc(destlen + 1);
+ if (result == nullptr)
+ return nullptr;
+
+ Ucs2toUtf8(src, len, result, destlen);
+ result[destlen] = 0;
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Utf8CheckString - checks if a string is a valid utf8-encoded string
+
+MIR_CORE_DLL(BOOL) Utf8CheckString(const char *str)
+{
+ int expect_bytes = 0, utf_found = 0;
+
+ if (!str) return 0;
+
+ while (*str) {
+ if ((*str & 0x80) == 0) {
+ /* Looks like an ASCII character */
+ if (expect_bytes)
+ /* byte of UTF-8 character expected */
+ return 0;
+ }
+ else {
+ /* Looks like byte of an UTF-8 character */
+ if (expect_bytes) {
+ /* expect_bytes already set: first byte of UTF-8 char already seen */
+ if ((*str & 0xC0) != 0x80) {
+ /* again first byte ?!?! */
+ return 0;
+ }
+ }
+ else {
+ /* First byte of the UTF-8 character */
+ /* count initial one bits and set expect_bytes to 1 less */
+ char ch = *str;
+ while (ch & 0x80) {
+ expect_bytes++;
+ ch = (ch & 0x7f) << 1;
+ }
+ }
+ /* OK, next byte of UTF-8 character */
+ /* Decrement number of expected bytes */
+ if (--expect_bytes == 0)
+ utf_found = 1;
+ }
+ str++;
+ }
+
+ return (utf_found && expect_bytes == 0);
+}
diff --git a/src/mir_core/src/utils.cpp b/src/mir_core/src/utils.cpp index b6953a027a..07613f6394 100644 --- a/src/mir_core/src/utils.cpp +++ b/src/mir_core/src/utils.cpp @@ -1,570 +1,570 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src) -{ - if (dest != nullptr) - mir_free(dest); - - return dest = (src != nullptr) ? mir_strdup(src) : nullptr; -} - -MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src) -{ - if (dest != nullptr) - mir_free(dest); - - return dest = (src != nullptr) ? mir_wstrdup(src) : nullptr; -} - -MIR_CORE_DLL(char*) rtrim(char *str) -{ - if (str == nullptr) - return nullptr; - - char* p = strchr(str, 0); - while (--p >= str) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - *p = 0; break; - default: - return str; - } - } - return str; -} - -MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = wcschr(str, 0); - while (--p >= str) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - *p = 0; break; - default: - return str; - } - } - return str; -} - -MIR_CORE_DLL(char*) ltrim(char *str) -{ - if (str == nullptr) - return nullptr; - - char* p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - memmove(str, p, strlen(p) + 1); - return str; - } - } -} - -MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - memmove(str, p, sizeof(wchar_t)*(wcslen(p) + 1)); - return str; - } - } -} - -MIR_CORE_DLL(char*) ltrimp(char *str) -{ - if (str == nullptr) - return nullptr; - - char *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - return p; - } - } -} - -MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - return p; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(char*) strdel(char *str, size_t len) -{ - char* p; - for (p = str + len; *p != 0; p++) - *(p - len) = *p; - - *(p - len) = '\0'; - return str; -} - -MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len) -{ - wchar_t* p; - for (p = str + len; *p != 0; p++) - *(p - len) = *p; - - *(p - len) = '\0'; - return str; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const char *last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && *mask != *name) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const wchar_t* last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && *mask != *name) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z')?((_c)-'a'+'A'):(_c)) - -MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const char *last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const wchar_t* last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szHexTable[] = "0123456789abcdef"; - -MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest) -{ - const uint8_t *p = (const uint8_t*)pData; - char *d = dest; - - for (size_t i = 0; i < len; i++, p++) { - *d++ = szHexTable[*p >> 4]; - *d++ = szHexTable[*p & 0x0F]; - } - *d = 0; - - return dest; -} - -MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest) -{ - const uint8_t *p = (const uint8_t*)pData; - wchar_t *d = dest; - - for (size_t i = 0; i < len; i++, p++) { - *d++ = szHexTable[*p >> 4]; - *d++ = szHexTable[*p & 0x0F]; - } - *d = 0; - - return dest; -} - -static int hex2dec(int iHex) -{ - if (iHex >= '0' && iHex <= '9') - return iHex - '0'; - if (iHex >= 'a' && iHex <= 'f') - return iHex - 'a' + 10; - if (iHex >= 'A' && iHex <= 'F') - return iHex - 'A' + 10; - return 0; -} - -MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len) -{ - if (pSrc == nullptr || pData == nullptr || len == 0) - return false; - - size_t bufLen = strlen(pSrc)/2; - if (pSrc[bufLen*2] != 0 || bufLen > len) - return false; - - uint8_t *pDest = (uint8_t*)pData; - const char *p = (const char *)pSrc; - for (size_t i = 0; i < bufLen; i++, p += 2) - pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); - - if (bufLen < len) - memset(pDest + bufLen, 0, len - bufLen); - return true; -} - -MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len) -{ - if (pSrc == nullptr || pData == nullptr || len == 0) - return false; - - size_t bufLen = wcslen(pSrc)/2; - if (pSrc[bufLen * 2] != 0 || bufLen > len) - return false; - - uint8_t *pDest = (uint8_t*)pData; - const wchar_t *p = (const wchar_t *)pSrc; - for (size_t i = 0; i < bufLen; i++, p += 2) - pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); - - if (bufLen < len) - memset(pDest+bufLen, 0, len - bufLen); - return true; -} - - -///////////////////////////////////////////////////////////////////////////////////////// - -#pragma intrinsic(strlen, strcpy, strcat, strcmp, wcslen, wcscpy, wcscat, wcscmp) - -MIR_CORE_DLL(size_t) mir_strlen(const char *p) -{ - return (p) ? strlen(p) : 0; -} - -MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p) -{ - return (p) ? wcslen(p) : 0; -} - -MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return strcpy(dest, src); -} - -MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return wcscpy(dest, src); -} - -MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - strncpy_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - wcsncpy_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return strcat(dest, src); -} - -MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return wcscat(dest, src); -} - -MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - strncat_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - wcsncat_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strcmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcscmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return stricmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsicmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strncmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsncmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strnicmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsnicmp(p1, p2, n); -} - -#ifdef _MSC_VER -MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2) -{ - for (int i = 0; s1[i]; i++) - for (int j = i, k = 0; towlower(s1[j]) == towlower(s2[k]); j++, k++) - if (!s2[k + 1]) - return s1 + i; - - return nullptr; -} -#endif - -///////////////////////////////////////////////////////////////////////////////////////// - -#ifdef _MSC_VER - PGENRANDOM pfnRtlGenRandom; -#endif - -MIR_CORE_DLL(void) Utils_GetRandom(void *pszDest, size_t cbLen) -{ - if (pszDest == nullptr || cbLen == 0) - return; - - #ifdef _MSC_VER - if (pfnRtlGenRandom != nullptr) { - pfnRtlGenRandom(pszDest, (uint32_t)cbLen); - return; - } - #endif - - srand(time(0)); - uint8_t *p = (uint8_t*)pszDest; - for (size_t i = 0; i < cbLen; i++) - p[i] = rand() & 0xFF; -} - -MIR_CORE_DLL(bool) Utils_IsRtl(const wchar_t *pszwText) -{ - #ifdef _MSC_VER - size_t iLen = mir_wstrlen(pszwText); - mir_ptr<uint16_t> infoTypeC2((uint16_t*)mir_calloc(sizeof(uint16_t) * (iLen + 2))); - GetStringTypeW(CT_CTYPE2, pszwText, (int)iLen, infoTypeC2); - - for (size_t i = 0; i < iLen; i++) - if (infoTypeC2[i] == C2_RIGHTTOLEFT) - return true; - #endif - - return false; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src)
+{
+ if (dest != nullptr)
+ mir_free(dest);
+
+ return dest = (src != nullptr) ? mir_strdup(src) : nullptr;
+}
+
+MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src)
+{
+ if (dest != nullptr)
+ mir_free(dest);
+
+ return dest = (src != nullptr) ? mir_wstrdup(src) : nullptr;
+}
+
+MIR_CORE_DLL(char*) rtrim(char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char* p = strchr(str, 0);
+ while (--p >= str) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ *p = 0; break;
+ default:
+ return str;
+ }
+ }
+ return str;
+}
+
+MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = wcschr(str, 0);
+ while (--p >= str) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ *p = 0; break;
+ default:
+ return str;
+ }
+ }
+ return str;
+}
+
+MIR_CORE_DLL(char*) ltrim(char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char* p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ memmove(str, p, strlen(p) + 1);
+ return str;
+ }
+ }
+}
+
+MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ memmove(str, p, sizeof(wchar_t)*(wcslen(p) + 1));
+ return str;
+ }
+ }
+}
+
+MIR_CORE_DLL(char*) ltrimp(char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char *p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ return p;
+ }
+ }
+}
+
+MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ return p;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(char*) strdel(char *str, size_t len)
+{
+ char* p;
+ for (p = str + len; *p != 0; p++)
+ *(p - len) = *p;
+
+ *(p - len) = '\0';
+ return str;
+}
+
+MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len)
+{
+ wchar_t* p;
+ for (p = str + len; *p != 0; p++)
+ *(p - len) = *p;
+
+ *(p - len) = '\0';
+ return str;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const char *last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && *mask != *name) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const wchar_t* last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && *mask != *name) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z')?((_c)-'a'+'A'):(_c))
+
+MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const char *last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const wchar_t* last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static char szHexTable[] = "0123456789abcdef";
+
+MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest)
+{
+ const uint8_t *p = (const uint8_t*)pData;
+ char *d = dest;
+
+ for (size_t i = 0; i < len; i++, p++) {
+ *d++ = szHexTable[*p >> 4];
+ *d++ = szHexTable[*p & 0x0F];
+ }
+ *d = 0;
+
+ return dest;
+}
+
+MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest)
+{
+ const uint8_t *p = (const uint8_t*)pData;
+ wchar_t *d = dest;
+
+ for (size_t i = 0; i < len; i++, p++) {
+ *d++ = szHexTable[*p >> 4];
+ *d++ = szHexTable[*p & 0x0F];
+ }
+ *d = 0;
+
+ return dest;
+}
+
+static int hex2dec(int iHex)
+{
+ if (iHex >= '0' && iHex <= '9')
+ return iHex - '0';
+ if (iHex >= 'a' && iHex <= 'f')
+ return iHex - 'a' + 10;
+ if (iHex >= 'A' && iHex <= 'F')
+ return iHex - 'A' + 10;
+ return 0;
+}
+
+MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len)
+{
+ if (pSrc == nullptr || pData == nullptr || len == 0)
+ return false;
+
+ size_t bufLen = strlen(pSrc)/2;
+ if (pSrc[bufLen*2] != 0 || bufLen > len)
+ return false;
+
+ uint8_t *pDest = (uint8_t*)pData;
+ const char *p = (const char *)pSrc;
+ for (size_t i = 0; i < bufLen; i++, p += 2)
+ pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]);
+
+ if (bufLen < len)
+ memset(pDest + bufLen, 0, len - bufLen);
+ return true;
+}
+
+MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len)
+{
+ if (pSrc == nullptr || pData == nullptr || len == 0)
+ return false;
+
+ size_t bufLen = wcslen(pSrc)/2;
+ if (pSrc[bufLen * 2] != 0 || bufLen > len)
+ return false;
+
+ uint8_t *pDest = (uint8_t*)pData;
+ const wchar_t *p = (const wchar_t *)pSrc;
+ for (size_t i = 0; i < bufLen; i++, p += 2)
+ pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]);
+
+ if (bufLen < len)
+ memset(pDest+bufLen, 0, len - bufLen);
+ return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#pragma intrinsic(strlen, strcpy, strcat, strcmp, wcslen, wcscpy, wcscat, wcscmp)
+
+MIR_CORE_DLL(size_t) mir_strlen(const char *p)
+{
+ return (p) ? strlen(p) : 0;
+}
+
+MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p)
+{
+ return (p) ? wcslen(p) : 0;
+}
+
+MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return strcpy(dest, src);
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return wcscpy(dest, src);
+}
+
+MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ strncpy_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ wcsncpy_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return strcat(dest, src);
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return wcscat(dest, src);
+}
+
+MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ strncat_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ wcsncat_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return strcmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcscmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return stricmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcsicmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return strncmp(p1, p2, n);
+}
+
+MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcsncmp(p1, p2, n);
+}
+
+MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return strnicmp(p1, p2, n);
+}
+
+MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcsnicmp(p1, p2, n);
+}
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2)
+{
+ for (int i = 0; s1[i]; i++)
+ for (int j = i, k = 0; towlower(s1[j]) == towlower(s2[k]); j++, k++)
+ if (!s2[k + 1])
+ return s1 + i;
+
+ return nullptr;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _MSC_VER
+ PGENRANDOM pfnRtlGenRandom;
+#endif
+
+MIR_CORE_DLL(void) Utils_GetRandom(void *pszDest, size_t cbLen)
+{
+ if (pszDest == nullptr || cbLen == 0)
+ return;
+
+ #ifdef _MSC_VER
+ if (pfnRtlGenRandom != nullptr) {
+ pfnRtlGenRandom(pszDest, (uint32_t)cbLen);
+ return;
+ }
+ #endif
+
+ srand(time(0));
+ uint8_t *p = (uint8_t*)pszDest;
+ for (size_t i = 0; i < cbLen; i++)
+ p[i] = rand() & 0xFF;
+}
+
+MIR_CORE_DLL(bool) Utils_IsRtl(const wchar_t *pszwText)
+{
+ #ifdef _MSC_VER
+ size_t iLen = mir_wstrlen(pszwText);
+ mir_ptr<uint16_t> infoTypeC2((uint16_t*)mir_calloc(sizeof(uint16_t) * (iLen + 2)));
+ GetStringTypeW(CT_CTYPE2, pszwText, (int)iLen, infoTypeC2);
+
+ for (size_t i = 0; i < iLen; i++)
+ if (infoTypeC2[i] == C2_RIGHTTOLEFT)
+ return true;
+ #endif
+
+ return false;
+}
diff --git a/src/miranda32/res/version.rc b/src/miranda32/res/version.rc index e7ba01cf8e..7106e0debb 100644 --- a/src/miranda32/res/version.rc +++ b/src/miranda32/res/version.rc @@ -38,7 +38,7 @@ BEGIN VALUE "FileDescription", "Miranda NG\0"
VALUE "FileVersion", MIRANDA_VERSION_DISPLAY
VALUE "InternalName", "miranda32\0"
- VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
+ VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "miranda32.exe\0"
VALUE "PrivateBuild", "\0"
diff --git a/src/miranda32/src/miranda.cpp b/src/miranda32/src/miranda.cpp index 59e5dde9df..59fd3cf184 100644 --- a/src/miranda32/src/miranda.cpp +++ b/src/miranda32/src/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/miranda32/src/stdafx.cxx b/src/miranda32/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/miranda32/src/stdafx.cxx +++ b/src/miranda32/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/miranda32/src/stdafx.h b/src/miranda32/src/stdafx.h index 96509b59f8..986030ab1f 100644 --- a/src/miranda32/src/stdafx.h +++ b/src/miranda32/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/tools/installer_ng/MirandaNG.iss b/tools/installer_ng/MirandaNG.iss index 0401344ab0..6ab3817511 100644 --- a/tools/installer_ng/MirandaNG.iss +++ b/tools/installer_ng/MirandaNG.iss @@ -17,7 +17,7 @@ AppVersion={#AppVer} AppName=Miranda NG
AppVerName=Miranda NG {#SetupSetting("AppVersion")}
AppPublisher=Miranda NG team
-AppCopyRight=2022 © Miranda NG team
+AppCopyRight=2023 © Miranda NG team
VersionInfoVersion={#SetupSetting("AppVersion")}
MinVersion=5.0
ArchitecturesAllowed={#ArcAllow}
diff --git a/tools/installer_ng_stable/MirandaNG.iss b/tools/installer_ng_stable/MirandaNG.iss index 04a4f62a12..5c9c277456 100644 --- a/tools/installer_ng_stable/MirandaNG.iss +++ b/tools/installer_ng_stable/MirandaNG.iss @@ -17,7 +17,7 @@ AppVersion={#AppVer} AppName=Miranda NG
AppVerName=Miranda NG {#SetupSetting("AppVersion")}
AppPublisher=Miranda NG team
-AppCopyRight=2022 © Miranda NG team
+AppCopyRight=2023 © Miranda NG team
VersionInfoVersion={#SetupSetting("AppVersion")}
MinVersion=5.0
ArchitecturesAllowed={#ArcAllow}
diff --git a/utils/std_string_utils.cpp b/utils/std_string_utils.cpp index 9e865293a1..328311d672 100644 --- a/utils/std_string_utils.cpp +++ b/utils/std_string_utils.cpp @@ -1,424 +1,424 @@ -/* - -Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-22 Miranda NG team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "std_string_utils.h" - -std::string utils::url::encode(const std::string &s) -{ - return std::string(mir_urlEncode(s.c_str())); -} - -std::string utils::url::decode(std::string data) -{ - std::string new_string; - for (std::string::size_type i = 0; i < data.length(); i++) { - if (data.at(i) == '%' && (i + 2) < data.length()) { - std::string num = data.substr(i + 1, 2); - unsigned long udn = strtoul(num.c_str(), nullptr, 16); - utils::text::append_ordinal(udn, &new_string); - i += 2; - continue; - } - - new_string += data.at(i); - } - - return new_string; -} - -std::string utils::time::unix_timestamp() -{ - time_t in = ::time(0); - return utils::conversion::to_string((void*)&in, UTILS_CONV_TIME_T); -} - -std::string utils::time::mili_timestamp() -{ - SYSTEMTIME st; - std::string timestamp = utils::time::unix_timestamp(); - GetSystemTime(&st); - timestamp.append(utils::conversion::to_string((void*)&st.wMilliseconds, UTILS_CONV_UNSIGNED_NUMBER)); - return timestamp.substr(0, 13); -} - -time_t utils::time::from_string(const std::string &data) -{ - long long timestamp = _strtoi64(data.c_str(), nullptr, 10); - - // If it is milli timestamp - if (timestamp > 100000000000) - timestamp /= 1000; - - // If conversion fails, use local time? - //if (!timestamp) - // timestamp = ::time(NULL); - - return (time_t)timestamp; -} - -std::string utils::conversion::to_string(void* data, uint16_t type) -{ - std::stringstream out; - - switch (type) { - case UTILS_CONV_BOOLEAN: - out << (data ? "true" : "false"); - break; - - case UTILS_CONV_TIME_T: - out << (*(time_t*)data); - break; - - case UTILS_CONV_SIGNED_NUMBER: - out << (*(signed int*)data); - break; - - case UTILS_CONV_UNSIGNED_NUMBER: - out << (*(unsigned int*)data); - break; - } - - return out.str(); -} - -void utils::text::replace_first(std::string* data, const std::string &from, const std::string &to) -{ - std::string::size_type position = data->find(from); - if (position != std::string::npos) { - data->replace(position, from.size(), to); - } -} - -void utils::text::replace_all(std::string* data, const std::string &from, const std::string &to) -{ - std::string::size_type position = 0; - - while ((position = data->find(from, position)) != std::string::npos) { - data->replace(position, from.size(), to); - position += to.size(); - } -} - -void utils::text::treplace_all(std::wstring* data, const std::wstring &from, const std::wstring &to) -{ - std::wstring::size_type position = 0; - - while ((position = data->find(from, position)) != std::wstring::npos) { - data->replace(position, from.size(), to); - position++; - } -} - -unsigned int utils::text::count_all(std::string* data, const std::string &term) -{ - unsigned int count = 0; - std::string::size_type position = 0; - - while ((position = data->find(term, position)) != std::string::npos) { - count++; - position++; - } - - return count; -} - -void utils::text::append_ordinal(unsigned long value, std::string* data) -{ - if (value <= 127) { // U+0000 .. U+007F - *data += (char)value; - } - else if (value >= 128 && value <= 2047) { // U+0080 .. U+07FF - *data += (char)(192 + (value / 64)); - *data += (char)(128 + (value % 64)); - } - else if (value >= 2048 && value <= 65535) { // U+0800 .. U+FFFF - *data += (char)(224 + (value / 4096)); - *data += (char)(128 + ((value / 64) % 64)); - *data += (char)(128 + (value % 64)); - } - else { - *data += (char)((value >> 24) & 0xFF); - *data += (char)((value >> 16) & 0xFF); - *data += (char)((value >> 8) & 0xFF); - *data += (char)((value) & 0xFF); - } -} - -std::string utils::text::html_entities_decode(std::string data) -{ - utils::text::replace_all(&data, "&", "&"); - utils::text::replace_all(&data, """, "\""); - utils::text::replace_all(&data, "<", "<"); - utils::text::replace_all(&data, ">", ">"); - utils::text::replace_all(&data, " ", " "); - - utils::text::replace_all(&data, "♥", "\xE2\x99\xA5"); // direct byte replacement - // utils::text::replace_all(&data, "♥", "\\u2665"); // indirect slashu replacement - - utils::text::replace_all(&data, "\\r", "\r"); - utils::text::replace_all(&data, "\\n", "\n"); - utils::text::replace_all(&data, "\\\"", "\""); - utils::text::replace_all(&data, "\\/", "/"); - utils::text::replace_all(&data, "\\\\", "\\"); - - // TODO: Add more to comply general usage - // http://www.utexas.edu/learn/html/spchar.html - // http://www.webmonkey.com/reference/Special_Characters - // http://www.degraeve.com/reference/specialcharacters.php - // http://www.chami.com/tips/internet/050798i.html - // http://www.w3schools.com/tags/ref_entities.asp - // http://www.natural-innovations.com/wa/doc-charset.html - // http://webdesign.about.com/library/bl_htmlcodes.htm - - std::string new_string; - for (std::string::size_type i = 0; i < data.length(); i++) { - if (data.at(i) == '&' && (i + 1) < data.length() && data.at(i + 1) == '#') { - std::string::size_type comma = data.find(";", i); - if (comma != std::string::npos) { - bool hexa = false; - if ((i + 2) < data.length() && data.at(i + 2) == 'x') { - hexa = true; - i += 3; - } - else { - i += 2; - } - - std::string num = data.substr(i, comma - i); - if (!num.empty()) { - unsigned long udn = strtoul(num.c_str(), nullptr, hexa ? 16 : 10); - utils::text::append_ordinal(udn, &new_string); - } - - i = comma; - continue; - } - } - - new_string += data.at(i); - } - - return new_string; -} - -std::string utils::text::remove_html(const std::string &data) -{ - std::string new_string; - - for (std::string::size_type i = 0; i < data.length(); i++) { - if (data.at(i) == '<' && (i + 1) < data.length() && data.at(i + 1) != ' ') { - i = data.find(">", i); - if (i == std::string::npos) - break; - - continue; - } - - new_string += data.at(i); - } - - return new_string; -} - -std::string utils::text::slashu_to_utf8(const std::string &data) -{ - std::string new_string; - - for (std::string::size_type i = 0; i < data.length(); i++) { - if (data.at(i) == '\\' && (i + 1) < data.length() && data.at(i + 1) == 'u') { - unsigned long udn = strtoul(data.substr(i + 2, 4).c_str(), nullptr, 16); - append_ordinal(udn, &new_string); - i += 5; - continue; - } - - new_string += data.at(i); - } - - return new_string; -} - -std::string utils::text::trim(const std::string &data, bool rtrim) -{ - std::string spaces = " \t\r\n"; - std::string::size_type begin = rtrim ? 0 : data.find_first_not_of(spaces); - std::string::size_type end = data.find_last_not_of(spaces); - - return (end != std::string::npos) ? data.substr(begin, end + 1 - begin) : ""; -} - -void utils::text::explode(std::string str, const std::string &separator, std::vector<std::string>* results) -{ - std::string::size_type pos; - pos = str.find_first_of(separator); - while (pos != std::string::npos) { - if (pos > 0) { - results->push_back(str.substr(0, pos)); - } - str = str.substr(pos + 1); - pos = str.find_first_of(separator); - } - if (str.length() > 0) { - results->push_back(str); - } -} - -std::string utils::text::source_get_value(std::string* data, unsigned int argument_count, ...) -{ - va_list arg; - std::string ret; - std::string::size_type start = 0, end = 0; - - va_start(arg, argument_count); - - for (unsigned int i = argument_count; i > 0; i--) { - if (i == 1) { - end = data->find(va_arg(arg, char*), start); - if (start == std::string::npos || end == std::string::npos) - break; - ret = data->substr(start, end - start); - } - else { - std::string term = va_arg(arg, char*); - start = data->find(term, start); - if (start == std::string::npos) - break; - start += term.length(); - } - } - - va_end(arg); - return ret; -} - -std::string utils::text::source_get_value2(std::string* data, const char *term, const char *endings, bool wholeString) -{ - std::string::size_type start = 0, end = 0; - std::string ret; - - start = data->find(term); - if (start != std::string::npos) { - start += mir_strlen(term); - - end = data->find_first_of(endings, start); - if (end != std::string::npos) { - ret = data->substr(start, end - start); - } - else if (wholeString) { - ret = data->substr(start); - } - } - - return ret; -} - -std::string utils::text::source_get_form_data(std::string* data, bool hiddenOnly) -{ - const char *search = hiddenOnly ? "<input type=\"hidden\"" : "<input"; - std::string values; - - std::string::size_type start = 0; - start = data->find(search, start); - while (start != std::string::npos) { - start++; - std::string attr, value; - - std::string::size_type pos = data->find("name=\"", start); - if (pos != std::string::npos) { - pos += 6; - std::string::size_type end = data->find("\"", pos); - if (end != std::string::npos) - attr = data->substr(pos, end - pos); - - - end = data->find(">", pos); - pos = data->find("value=\"", pos); - if (pos != std::string::npos && end != std::string::npos && pos < end) { - pos += 7; - end = data->find("\"", pos); - if (end != std::string::npos) - value = data->substr(pos, end - pos); - } - } - - if (!attr.empty()) { - if (!values.empty()) - values += "&"; - values += attr + "=" + value; - } - start = data->find(search, start); - } - - return values; -} - -std::string utils::text::prepare_name(const std::string &name, bool withSurnameLetter) -{ - std::string::size_type pos = name.find(" "); - if (pos == std::wstring::npos) - return name; - - std::string result = name.substr(0, pos); - - if (withSurnameLetter) { - pos = name.rfind(" ") + 1; // we're sure there is some space in name so we can do +1 safely - - if (pos < name.length()) - result += " " + name.substr(pos, 1) + std::string("."); - } - - return result; -} - -std::string utils::text::rand_string(int len, const char *chars, unsigned int *number) -{ - std::stringstream out; - - int strLen = (int)mir_strlen(chars); - for (int i = 0; i < len; ++i) { - out << chars[utils::number::random(0, strLen, number)]; - } - - return out.str(); -} - -std::string utils::text::truncate_utf8(const std::string &text, size_t maxLength) -{ - // To not split some unicode character we need to transform it to wchar_t first, then split it, and then convert it back, because we want std::string as result - // TODO: Probably there is much simpler and nicer way - std::wstring wtext = ptrW(mir_utf8decodeW(text.c_str())); - if (wtext.length() > maxLength) { - wtext = wtext.substr(0, maxLength) + L"\x2026"; // unicode ellipsis - return std::string(_T2A(wtext.c_str(), CP_UTF8)); - } - // It's not longer, return given string - return text; -} - -int utils::number::random(int min, int max, unsigned int *number) -{ - if (number != nullptr) { - errno_t err = rand_s(number); - if (!err) - return (*number % (max - min)) + min; - } - - // If called didn't specified "number" or there was error, fallback to rand() - return (rand() % (max - min)) + min; -} +/*
+
+Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-23 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "std_string_utils.h"
+
+std::string utils::url::encode(const std::string &s)
+{
+ return std::string(mir_urlEncode(s.c_str()));
+}
+
+std::string utils::url::decode(std::string data)
+{
+ std::string new_string;
+ for (std::string::size_type i = 0; i < data.length(); i++) {
+ if (data.at(i) == '%' && (i + 2) < data.length()) {
+ std::string num = data.substr(i + 1, 2);
+ unsigned long udn = strtoul(num.c_str(), nullptr, 16);
+ utils::text::append_ordinal(udn, &new_string);
+ i += 2;
+ continue;
+ }
+
+ new_string += data.at(i);
+ }
+
+ return new_string;
+}
+
+std::string utils::time::unix_timestamp()
+{
+ time_t in = ::time(0);
+ return utils::conversion::to_string((void*)&in, UTILS_CONV_TIME_T);
+}
+
+std::string utils::time::mili_timestamp()
+{
+ SYSTEMTIME st;
+ std::string timestamp = utils::time::unix_timestamp();
+ GetSystemTime(&st);
+ timestamp.append(utils::conversion::to_string((void*)&st.wMilliseconds, UTILS_CONV_UNSIGNED_NUMBER));
+ return timestamp.substr(0, 13);
+}
+
+time_t utils::time::from_string(const std::string &data)
+{
+ long long timestamp = _strtoi64(data.c_str(), nullptr, 10);
+
+ // If it is milli timestamp
+ if (timestamp > 100000000000)
+ timestamp /= 1000;
+
+ // If conversion fails, use local time?
+ //if (!timestamp)
+ // timestamp = ::time(NULL);
+
+ return (time_t)timestamp;
+}
+
+std::string utils::conversion::to_string(void* data, uint16_t type)
+{
+ std::stringstream out;
+
+ switch (type) {
+ case UTILS_CONV_BOOLEAN:
+ out << (data ? "true" : "false");
+ break;
+
+ case UTILS_CONV_TIME_T:
+ out << (*(time_t*)data);
+ break;
+
+ case UTILS_CONV_SIGNED_NUMBER:
+ out << (*(signed int*)data);
+ break;
+
+ case UTILS_CONV_UNSIGNED_NUMBER:
+ out << (*(unsigned int*)data);
+ break;
+ }
+
+ return out.str();
+}
+
+void utils::text::replace_first(std::string* data, const std::string &from, const std::string &to)
+{
+ std::string::size_type position = data->find(from);
+ if (position != std::string::npos) {
+ data->replace(position, from.size(), to);
+ }
+}
+
+void utils::text::replace_all(std::string* data, const std::string &from, const std::string &to)
+{
+ std::string::size_type position = 0;
+
+ while ((position = data->find(from, position)) != std::string::npos) {
+ data->replace(position, from.size(), to);
+ position += to.size();
+ }
+}
+
+void utils::text::treplace_all(std::wstring* data, const std::wstring &from, const std::wstring &to)
+{
+ std::wstring::size_type position = 0;
+
+ while ((position = data->find(from, position)) != std::wstring::npos) {
+ data->replace(position, from.size(), to);
+ position++;
+ }
+}
+
+unsigned int utils::text::count_all(std::string* data, const std::string &term)
+{
+ unsigned int count = 0;
+ std::string::size_type position = 0;
+
+ while ((position = data->find(term, position)) != std::string::npos) {
+ count++;
+ position++;
+ }
+
+ return count;
+}
+
+void utils::text::append_ordinal(unsigned long value, std::string* data)
+{
+ if (value <= 127) { // U+0000 .. U+007F
+ *data += (char)value;
+ }
+ else if (value >= 128 && value <= 2047) { // U+0080 .. U+07FF
+ *data += (char)(192 + (value / 64));
+ *data += (char)(128 + (value % 64));
+ }
+ else if (value >= 2048 && value <= 65535) { // U+0800 .. U+FFFF
+ *data += (char)(224 + (value / 4096));
+ *data += (char)(128 + ((value / 64) % 64));
+ *data += (char)(128 + (value % 64));
+ }
+ else {
+ *data += (char)((value >> 24) & 0xFF);
+ *data += (char)((value >> 16) & 0xFF);
+ *data += (char)((value >> 8) & 0xFF);
+ *data += (char)((value) & 0xFF);
+ }
+}
+
+std::string utils::text::html_entities_decode(std::string data)
+{
+ utils::text::replace_all(&data, "&", "&");
+ utils::text::replace_all(&data, """, "\"");
+ utils::text::replace_all(&data, "<", "<");
+ utils::text::replace_all(&data, ">", ">");
+ utils::text::replace_all(&data, " ", " ");
+
+ utils::text::replace_all(&data, "♥", "\xE2\x99\xA5"); // direct byte replacement
+ // utils::text::replace_all(&data, "♥", "\\u2665"); // indirect slashu replacement
+
+ utils::text::replace_all(&data, "\\r", "\r");
+ utils::text::replace_all(&data, "\\n", "\n");
+ utils::text::replace_all(&data, "\\\"", "\"");
+ utils::text::replace_all(&data, "\\/", "/");
+ utils::text::replace_all(&data, "\\\\", "\\");
+
+ // TODO: Add more to comply general usage
+ // http://www.utexas.edu/learn/html/spchar.html
+ // http://www.webmonkey.com/reference/Special_Characters
+ // http://www.degraeve.com/reference/specialcharacters.php
+ // http://www.chami.com/tips/internet/050798i.html
+ // http://www.w3schools.com/tags/ref_entities.asp
+ // http://www.natural-innovations.com/wa/doc-charset.html
+ // http://webdesign.about.com/library/bl_htmlcodes.htm
+
+ std::string new_string;
+ for (std::string::size_type i = 0; i < data.length(); i++) {
+ if (data.at(i) == '&' && (i + 1) < data.length() && data.at(i + 1) == '#') {
+ std::string::size_type comma = data.find(";", i);
+ if (comma != std::string::npos) {
+ bool hexa = false;
+ if ((i + 2) < data.length() && data.at(i + 2) == 'x') {
+ hexa = true;
+ i += 3;
+ }
+ else {
+ i += 2;
+ }
+
+ std::string num = data.substr(i, comma - i);
+ if (!num.empty()) {
+ unsigned long udn = strtoul(num.c_str(), nullptr, hexa ? 16 : 10);
+ utils::text::append_ordinal(udn, &new_string);
+ }
+
+ i = comma;
+ continue;
+ }
+ }
+
+ new_string += data.at(i);
+ }
+
+ return new_string;
+}
+
+std::string utils::text::remove_html(const std::string &data)
+{
+ std::string new_string;
+
+ for (std::string::size_type i = 0; i < data.length(); i++) {
+ if (data.at(i) == '<' && (i + 1) < data.length() && data.at(i + 1) != ' ') {
+ i = data.find(">", i);
+ if (i == std::string::npos)
+ break;
+
+ continue;
+ }
+
+ new_string += data.at(i);
+ }
+
+ return new_string;
+}
+
+std::string utils::text::slashu_to_utf8(const std::string &data)
+{
+ std::string new_string;
+
+ for (std::string::size_type i = 0; i < data.length(); i++) {
+ if (data.at(i) == '\\' && (i + 1) < data.length() && data.at(i + 1) == 'u') {
+ unsigned long udn = strtoul(data.substr(i + 2, 4).c_str(), nullptr, 16);
+ append_ordinal(udn, &new_string);
+ i += 5;
+ continue;
+ }
+
+ new_string += data.at(i);
+ }
+
+ return new_string;
+}
+
+std::string utils::text::trim(const std::string &data, bool rtrim)
+{
+ std::string spaces = " \t\r\n";
+ std::string::size_type begin = rtrim ? 0 : data.find_first_not_of(spaces);
+ std::string::size_type end = data.find_last_not_of(spaces);
+
+ return (end != std::string::npos) ? data.substr(begin, end + 1 - begin) : "";
+}
+
+void utils::text::explode(std::string str, const std::string &separator, std::vector<std::string>* results)
+{
+ std::string::size_type pos;
+ pos = str.find_first_of(separator);
+ while (pos != std::string::npos) {
+ if (pos > 0) {
+ results->push_back(str.substr(0, pos));
+ }
+ str = str.substr(pos + 1);
+ pos = str.find_first_of(separator);
+ }
+ if (str.length() > 0) {
+ results->push_back(str);
+ }
+}
+
+std::string utils::text::source_get_value(std::string* data, unsigned int argument_count, ...)
+{
+ va_list arg;
+ std::string ret;
+ std::string::size_type start = 0, end = 0;
+
+ va_start(arg, argument_count);
+
+ for (unsigned int i = argument_count; i > 0; i--) {
+ if (i == 1) {
+ end = data->find(va_arg(arg, char*), start);
+ if (start == std::string::npos || end == std::string::npos)
+ break;
+ ret = data->substr(start, end - start);
+ }
+ else {
+ std::string term = va_arg(arg, char*);
+ start = data->find(term, start);
+ if (start == std::string::npos)
+ break;
+ start += term.length();
+ }
+ }
+
+ va_end(arg);
+ return ret;
+}
+
+std::string utils::text::source_get_value2(std::string* data, const char *term, const char *endings, bool wholeString)
+{
+ std::string::size_type start = 0, end = 0;
+ std::string ret;
+
+ start = data->find(term);
+ if (start != std::string::npos) {
+ start += mir_strlen(term);
+
+ end = data->find_first_of(endings, start);
+ if (end != std::string::npos) {
+ ret = data->substr(start, end - start);
+ }
+ else if (wholeString) {
+ ret = data->substr(start);
+ }
+ }
+
+ return ret;
+}
+
+std::string utils::text::source_get_form_data(std::string* data, bool hiddenOnly)
+{
+ const char *search = hiddenOnly ? "<input type=\"hidden\"" : "<input";
+ std::string values;
+
+ std::string::size_type start = 0;
+ start = data->find(search, start);
+ while (start != std::string::npos) {
+ start++;
+ std::string attr, value;
+
+ std::string::size_type pos = data->find("name=\"", start);
+ if (pos != std::string::npos) {
+ pos += 6;
+ std::string::size_type end = data->find("\"", pos);
+ if (end != std::string::npos)
+ attr = data->substr(pos, end - pos);
+
+
+ end = data->find(">", pos);
+ pos = data->find("value=\"", pos);
+ if (pos != std::string::npos && end != std::string::npos && pos < end) {
+ pos += 7;
+ end = data->find("\"", pos);
+ if (end != std::string::npos)
+ value = data->substr(pos, end - pos);
+ }
+ }
+
+ if (!attr.empty()) {
+ if (!values.empty())
+ values += "&";
+ values += attr + "=" + value;
+ }
+ start = data->find(search, start);
+ }
+
+ return values;
+}
+
+std::string utils::text::prepare_name(const std::string &name, bool withSurnameLetter)
+{
+ std::string::size_type pos = name.find(" ");
+ if (pos == std::wstring::npos)
+ return name;
+
+ std::string result = name.substr(0, pos);
+
+ if (withSurnameLetter) {
+ pos = name.rfind(" ") + 1; // we're sure there is some space in name so we can do +1 safely
+
+ if (pos < name.length())
+ result += " " + name.substr(pos, 1) + std::string(".");
+ }
+
+ return result;
+}
+
+std::string utils::text::rand_string(int len, const char *chars, unsigned int *number)
+{
+ std::stringstream out;
+
+ int strLen = (int)mir_strlen(chars);
+ for (int i = 0; i < len; ++i) {
+ out << chars[utils::number::random(0, strLen, number)];
+ }
+
+ return out.str();
+}
+
+std::string utils::text::truncate_utf8(const std::string &text, size_t maxLength)
+{
+ // To not split some unicode character we need to transform it to wchar_t first, then split it, and then convert it back, because we want std::string as result
+ // TODO: Probably there is much simpler and nicer way
+ std::wstring wtext = ptrW(mir_utf8decodeW(text.c_str()));
+ if (wtext.length() > maxLength) {
+ wtext = wtext.substr(0, maxLength) + L"\x2026"; // unicode ellipsis
+ return std::string(_T2A(wtext.c_str(), CP_UTF8));
+ }
+ // It's not longer, return given string
+ return text;
+}
+
+int utils::number::random(int min, int max, unsigned int *number)
+{
+ if (number != nullptr) {
+ errno_t err = rand_s(number);
+ if (!err)
+ return (*number % (max - min)) + min;
+ }
+
+ // If called didn't specified "number" or there was error, fallback to rand()
+ return (rand() % (max - min)) + min;
+}
diff --git a/utils/std_string_utils.h b/utils/std_string_utils.h index d113797223..b0607fd9f3 100644 --- a/utils/std_string_utils.h +++ b/utils/std_string_utils.h @@ -1,6 +1,6 @@ /*
-Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-22 Miranda NG team
+Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-23 Miranda NG team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
|