summaryrefslogtreecommitdiff
path: root/protocols/WinPopup/src
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/WinPopup/src')
-rw-r--r--protocols/WinPopup/src/add_dialog.cpp66
-rw-r--r--protocols/WinPopup/src/add_dialog.h23
-rw-r--r--protocols/WinPopup/src/chat.cpp329
-rw-r--r--protocols/WinPopup/src/chat.h42
-rw-r--r--protocols/WinPopup/src/dllLoaderMinimal.h338
-rw-r--r--protocols/WinPopup/src/mailslot.cpp304
-rw-r--r--protocols/WinPopup/src/mailslot.h46
-rw-r--r--protocols/WinPopup/src/md5.h220
-rw-r--r--protocols/WinPopup/src/messagebox.cpp223
-rw-r--r--protocols/WinPopup/src/messagebox.h27
-rw-r--r--protocols/WinPopup/src/messenger.cpp458
-rw-r--r--protocols/WinPopup/src/messenger.h52
-rw-r--r--protocols/WinPopup/src/netbios.cpp1055
-rw-r--r--protocols/WinPopup/src/netbios.h130
-rw-r--r--protocols/WinPopup/src/netbios_name.cpp630
-rw-r--r--protocols/WinPopup/src/netbios_name.h155
-rw-r--r--protocols/WinPopup/src/network.cpp81
-rw-r--r--protocols/WinPopup/src/network.h29
-rw-r--r--protocols/WinPopup/src/options.cpp381
-rw-r--r--protocols/WinPopup/src/options.h22
-rw-r--r--protocols/WinPopup/src/processapi.cpp571
-rw-r--r--protocols/WinPopup/src/processapi.h258
-rw-r--r--protocols/WinPopup/src/resource.h66
-rw-r--r--protocols/WinPopup/src/scanner.cpp208
-rw-r--r--protocols/WinPopup/src/scanner.h47
-rw-r--r--protocols/WinPopup/src/search.cpp317
-rw-r--r--protocols/WinPopup/src/search.h58
-rw-r--r--protocols/WinPopup/src/services.cpp879
-rw-r--r--protocols/WinPopup/src/services.h34
-rw-r--r--protocols/WinPopup/src/smbconst.h106
-rw-r--r--protocols/WinPopup/src/stdafx.cpp22
-rw-r--r--protocols/WinPopup/src/stdafx.h122
-rw-r--r--protocols/WinPopup/src/user_info.cpp325
-rw-r--r--protocols/WinPopup/src/user_info.h22
-rw-r--r--protocols/WinPopup/src/winpopup_proto.cpp1193
-rw-r--r--protocols/WinPopup/src/winpopup_proto.h318
36 files changed, 9157 insertions, 0 deletions
diff --git a/protocols/WinPopup/src/add_dialog.cpp b/protocols/WinPopup/src/add_dialog.cpp
new file mode 100644
index 0000000000..2caa171835
--- /dev/null
+++ b/protocols/WinPopup/src/add_dialog.cpp
@@ -0,0 +1,66 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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_PTR CALLBACK DlgProcAddContact(HWND hwndDlg, UINT Msg,
+ WPARAM wParam, LPARAM /*lParam*/)
+{
+ switch ( Msg )
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault( hwndDlg );
+ return TRUE;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam ) )
+ {
+ case IDOK:
+ {
+ bool bGroup = IsDlgButtonChecked( hwndDlg, IDC_GROUP ) == BST_CHECKED;
+ CString sName;
+ GetDlgItemText( hwndDlg, IDC_NAME, sName.GetBuffer( 128 ), 127 );
+ sName.ReleaseBuffer();
+ sName.Trim();
+ if ( ! sName.IsEmpty() )
+ {
+ HCURSOR hCurrent = SetCursor( LoadCursor( NULL, IDC_WAIT ) );
+ HANDLE hContact = AddToListByName( sName, 0, NULL, true, bGroup );
+ SetCursor( hCurrent );
+ if ( hContact )
+ EndDialog( hwndDlg, IDOK );
+ }
+ }
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog( hwndDlg, IDCANCEL );
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+void AddDialog(HWND hParentWnd)
+{
+ DialogBox( pluginModule, MAKEINTRESOURCE( IDD_ADD ), hParentWnd, DlgProcAddContact );
+}
diff --git a/protocols/WinPopup/src/add_dialog.h b/protocols/WinPopup/src/add_dialog.h
new file mode 100644
index 0000000000..4b42136f9f
--- /dev/null
+++ b/protocols/WinPopup/src/add_dialog.h
@@ -0,0 +1,23 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// Показывает диалог для ручного добавления контакта по имени/адресу
+void AddDialog(HWND hParentWnd);
diff --git a/protocols/WinPopup/src/chat.cpp b/protocols/WinPopup/src/chat.cpp
new file mode 100644
index 0000000000..00ffd10b41
--- /dev/null
+++ b/protocols/WinPopup/src/chat.cpp
@@ -0,0 +1,329 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2008-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#ifdef CHAT_ENABLED
+
+HANDLE plugin_CHAT_EVENT = NULL;
+
+static int __cdecl CHAT_EVENT(WPARAM /* wParam */, LPARAM lParam)
+{
+ GCHOOK* pgch = (GCHOOK*)lParam;
+ switch ( pgch->pDest->iType )
+ {
+ case GC_USER_MESSAGE:
+ {
+ // Дублирование в чат
+ ChatMessage( pgch->pDest->ptszID, pgch->ptszText );
+
+ // Отправка сообщения
+ DWORD dwLastError = 0;
+ SendMessage( pgch->pDest->ptszID, pgch->ptszText, dwLastError );
+ }
+ break;
+ }
+
+ return 0;
+}
+
+bool ChatRegister()
+{
+ GCREGISTER gcr =
+ {
+ sizeof( GCREGISTER ),
+ 0,
+ modname,
+ modname,
+ 0,
+ 0,
+ NULL
+ };
+ int result = CallServiceSync( MS_GC_REGISTER, 0, (LPARAM)&gcr );
+ if ( result != 0 )
+ return false;
+
+ _ASSERT (plugin_CHAT_EVENT == NULL);
+ plugin_CHAT_EVENT = HookEvent (ME_GC_EVENT, CHAT_EVENT);
+ _ASSERT (plugin_CHAT_EVENT != NULL);
+
+ return true;
+}
+
+void ChatUnregister()
+{
+ if (plugin_CHAT_EVENT)
+ {
+ UnhookEvent (plugin_CHAT_EVENT);
+ plugin_CHAT_EVENT = NULL;
+ }
+}
+
+bool ChatNewSession(LPCTSTR szSession)
+{
+ GCSESSION gcr =
+ {
+ sizeof( GCSESSION ),
+ GCW_CHATROOM,
+ modname,
+ (LPCSTR)szSession,
+ (LPCSTR)szSession,
+ NULL,
+ 0,
+ 0
+ };
+ return ( CallServiceSync( MS_GC_NEWSESSION, 0, (LPARAM)&gcr ) == 0 );
+}
+
+bool ChatAddGroup(LPCTSTR szSession, LPCTSTR szGroup)
+{
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_ADDGROUP
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ NULL,
+ NULL,
+ NULL,
+ (LPCSTR)szGroup,
+ NULL,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ };
+ return ( CallServiceSync( MS_GC_EVENT, 0, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatJoinMe(LPCTSTR szSession, LPCTSTR szGroup)
+{
+ CString sMe;
+
+ CString sMyNick = GetNick( NULL );
+ if ( ! sMyNick.IsEmpty() )
+ sMe = sMyNick;
+ else
+ sMe = pluginMachineName;
+
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_JOIN
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ NULL,
+ (LPCSTR)(LPCTSTR)sMe,
+ (LPCSTR)(LPCTSTR)sMe,
+ (LPCSTR)szGroup,
+ NULL,
+ TRUE,
+ 0,
+ 0,
+ NULL
+ };
+ return ( CallServiceSync( MS_GC_EVENT, 0, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatJoinUser(LPCTSTR szSession, LPCTSTR szUser, LPCTSTR szGroup)
+{
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_JOIN
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ NULL,
+ (LPCSTR)szUser,
+ (LPCSTR)szUser,
+ (LPCSTR)szGroup,
+ NULL,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ };
+ return ( CallServiceSync( MS_GC_EVENT, 0, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatInitDone(LPCTSTR szSession)
+{
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_CONTROL
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ };
+ return ( CallServiceSync( MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatOnline(LPCTSTR szSession)
+{
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_CONTROL
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ };
+ return ( CallServiceSync( MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatOffline(LPCTSTR szSession)
+{
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_CONTROL
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ };
+ return ( CallServiceSync( MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatMessage(LPCTSTR szSession, LPCTSTR szFrom, LPCTSTR szMessage)
+{
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_MESSAGE
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ (LPCSTR)szMessage,
+ (LPCSTR)szFrom,
+ (LPCSTR)szFrom,
+ NULL,
+ NULL,
+ FALSE,
+ 0,
+ 0,
+ time()
+ };
+ return ( CallServiceSync( MS_GC_EVENT, 0, (LPARAM)&gce ) == 0 );
+}
+
+bool ChatMessage(LPCTSTR szSession, LPCTSTR szMessage)
+{
+ CString sMe;
+
+ CString sMyNick = GetNick( NULL );
+ if ( ! sMyNick.IsEmpty() )
+ sMe = sMyNick;
+ else
+ sMe = pluginMachineName;
+
+ GCDEST gcdest =
+ {
+ modname,
+ (LPSTR)szSession,
+ GC_EVENT_MESSAGE
+ };
+ GCEVENT gce =
+ {
+ sizeof( GCEVENT ),
+ &gcdest,
+ (LPCSTR)szMessage,
+ (LPCSTR)(LPCTSTR)sMe,
+ (LPCSTR)(LPCTSTR)sMe,
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ 0,
+ time()
+ };
+ return ( CallServiceSync( MS_GC_EVENT, 0, (LPARAM)&gce ) == 0 );
+}
+
+CString GetChatSession(HANDLE hContact)
+{
+ CString sContact;
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( hContact, modname, "ChatRoomID", &dbv ) )
+ {
+ sContact = dbv.pszVal;
+ DBFreeVariant( &dbv );
+ }
+ return sContact;
+}
+
+bool IsChatRoom(HANDLE hContact)
+{
+ return ( DBGetContactSettingByte( hContact, modname, "ChatRoom", 0 ) != 0 );
+}
+
+#endif // CHAT_ENABLED
diff --git a/protocols/WinPopup/src/chat.h b/protocols/WinPopup/src/chat.h
new file mode 100644
index 0000000000..41c6fd6476
--- /dev/null
+++ b/protocols/WinPopup/src/chat.h
@@ -0,0 +1,42 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2008-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifdef CHAT_ENABLED
+
+bool ChatRegister();
+void ChatUnregister();
+bool ChatNewSession(LPCTSTR szSession);
+bool ChatAddGroup(LPCTSTR szSession, LPCTSTR szGroup);
+bool ChatJoinMe(LPCTSTR szSession, LPCTSTR szGroup);
+bool ChatJoinUser(LPCTSTR szSession, LPCTSTR szUser, LPCTSTR szGroup);
+bool ChatInitDone(LPCTSTR szSession);
+bool ChatOnline(LPCTSTR szSession);
+bool ChatOffline(LPCTSTR szSession);
+bool ChatMessage(LPCTSTR szSession, LPCTSTR szFrom, LPCTSTR szMessage);
+bool ChatMessage(LPCTSTR szSession, LPCTSTR szMessage);
+
+// Получение идентификатора чата (обычно имя рабочей группы)
+CString GetChatSession(HANDLE hContact);
+
+// Проверка что контакт - это чат
+bool IsChatRoom(HANDLE hContact);
+
+#endif // CHAT_ENABLED
diff --git a/protocols/WinPopup/src/dllLoaderMinimal.h b/protocols/WinPopup/src/dllLoaderMinimal.h
new file mode 100644
index 0000000000..218a5a114c
--- /dev/null
+++ b/protocols/WinPopup/src/dllLoaderMinimal.h
@@ -0,0 +1,338 @@
+/*
+ * This software is the original work of DKLT.
+ * Copyright (c) 2002 DKLT. All rights reserved.
+ * email: dtung@eng.monash.edu.au
+ *
+ */
+/*
+ * Permission to make digital or hard copies of all or part of this work for personal
+ * or classroom use is granted without fee provided that copies are not distributed
+ * for profit or commercial advantage.
+ */
+
+#ifndef LOADDLL_H
+#define LOADDLL_H
+
+/// proof of concept code follows
+///////
+/////// class DllLoader and class DllFunctor<...>
+///////
+/////// Two classes are designed to provide the functionality to load a function,
+/////// using the "function name" as an identifier, from a Win32 .dll file.
+/////// Sample code are attached at the end of this file.
+///////
+/////// -04Oct2003 <Sat> 11.52pm
+/////// reworked article and sample code to be posted on codeproject.com
+/////// better defined behaviours with refined logics idealistic goals yet to be completed
+///////
+/////// -29Mar2003 <Sat> 1.47pm
+/////// Polishing code for public release. minimizing code size, removing redundent
+/////// comments, eliminating deprecated funcs.
+///////
+/////// -29Mar2003 <Sat> 12.47am
+/////// Revising the src tree. Using redundent code to achieve src level compatibility
+/////// (ie: same set of calls for both funcs attached with apps or reside in dlls)
+///////
+/////// -12Nov2002 <Mon> 1.35am
+/////// My first attempt to tidy the code for another public release.
+///////
+/////// -14Oct2002 <Mon> 1.40am
+/////// created and tested briefly inside \DKLT TestApp\
+///////
+///////
+///////
+
+//template <char* DllName>
+
+///
+/// No error message for you
+///
+#ifndef ERRORMSG
+#define DEBUGMSG(aMesg) ;
+#define ERRORMSG(aMesg) ;
+#endif
+
+///
+/// protos
+///
+class DllLoader;
+template <typename T> class DllFunctor;
+
+
+/*
+//++++++++++++++++++++++++++++++++++++++++++++++++++++
+// For Current release, you write code like these
+//++++++++++++++++++++++++++++++++++++++++++++++++++++
+ ///
+ /// u can load a dll function in two differnt ways
+ ///
+ DllLoader dll("testDll.dll", false);
+ DllFunctor<int(*)(int,int)> fAdd("Add");
+ fAdd.SetDll(dll);
+
+ int b;
+ b = fSub()(b,1); // with delay load, but not src level compatible
+
+ OR
+
+
+ DllLoader dll("testDll.dll");
+ FuncPtrType( int(*)(int,int) ) Add;
+ dll.LoadFunc(Add,"Add");
+ int a=90;
+ a = Add(a,1); // src level compatible, but no delay load
+
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++
+// For previous release, you write code like these
+//++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ //
+ // sample code for demonstrating class DllLoader {...}; and DllFunctor<...> {...};
+ //
+ FuncPtrType( int(*)(int) ) a; // define a new variable "a" of type "int(*)(int)"
+
+ DllLoader Dlldshow("dlls.dll"); // expect a dll name "dlls.dll"
+ Dllshow.LoadFunc( a, "test"); // load func named "test" from dll file
+ int i =a(10);
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++
+// For initial release, you write code like these
+//++++++++++++++++++++++++++++++++++++++++++++++++++++
+ This version enables a delay-load behaviour. Note the double ()() call on last line.
+
+ //
+ // sample code for demonstrating class DllLoader {...}; and DllFunctor<...> {...};
+ //
+
+ DllLoader Dlldshow("dlls.dll");
+ DllFunctor<void(*)(POINT*)> update("UpdatePoint");
+
+ Dlldshow.find(update);
+
+ update() (&pt);
+
+
+*/
+
+/*
+ A little comment here
+
+ My previous attempts to use operator()(...) and operator FuncPtrType () with MSVC
+ failed, where FuncPtrType is a function pointer typedef. That technique, enables
+ more control over a functor object. ie: can implement delay load among many exciting
+ features. That technique, however, works with g++ line of compilers.
+
+ This current implementation is design for use with MSVC line of compilers only.
+
+ It seems, from the MSVC compiler error message, that "operator FuncPtrType ()" is
+ never a candidate function, not to mention viability. I guess this is how they
+ design and implemented MSVC6. ".net" version doesnt "evaluate"
+ "operator FuncPtrType()" properly as well.
+
+ - DKLT March 2003
+*/
+
+
+//////
+//////++++++++++++++++++++++++++++++++++++++++++++++++++
+////// This marco is for performing the following task... GoodJob! creative man!!
+//////++++++++++++++++++++++++++++++++++++++++++++++++++
+////// normally, u define a function pointer variable this way
+//////
+////// int (*c) (int) = test; // c pointing to "int test(int) {...}"
+//////
+////// This marco enables u define a function pointer this way
+//////
+////// FuncPtrType( int(*)(int) ) c =test;
+//////
+//////
+////// took me a while to come up with this one.
+//////
+////// - DKLT 2003 March
+
+template <typename FuncTypeTTT>
+struct TypeOnlyStruct {
+typedef FuncTypeTTT FuncType;
+};
+
+#define FuncPtrType(funcType) \
+ TypeOnlyStruct<funcType>::FuncType
+
+//////
+////// potential problems
+////// - an instantiation for every differnt type on the template class
+////// thus bloated executable? need to fully test it out. not sure about
+////// behaviour at this stage.
+////// - DKLT March 2003
+
+
+//////
+////// class DllLoader {...}
+////// -init a dll file with LoadLibrary() so that its mapped into dll memory
+////// space. this class is designed to use with class DllFunctor<...>.
+//////
+//////
+/////////////////////////////////////////////////////////
+class DllLoader
+{
+/////////////////////////////////////////////////////////
+
+private:
+ TCHAR dllName[ MAX_PATH ];
+
+public:
+ HINSTANCE dll;
+
+ DllLoader (LPCTSTR n, bool loadNow = true) :
+ dll(0)
+ {
+ lstrcpy( dllName, n );
+ if (loadNow)
+ LoadLibrary();
+ }
+ ~DllLoader ()
+ {
+ FreeLibrary();
+ }
+
+ // test to see if dll is loaded already
+ operator bool () const
+ {
+ return (dll != 0);
+ }
+
+// FuncTypePtr(int(*)(int)) a;
+// Dllshow.LoadFunc( a, "test") ;
+// int i =a(10);
+
+ /// This is my latest implementation
+ ///----------------------------------------------------------
+ /// public:
+ /// template <typename FuncTTT>
+ /// DllLoader::LoadFunc(FuncTTT& c, string fNameStr)
+ ///----------------------------------------------------------
+ /// This function loads a function named "fNameStr" from a DllLoader object
+ /// and put the address of that function into c.
+ ///
+ /// - template type is derived(deduced) from 1st param.
+ ///
+ ///note: bloated executable is possible
+ template <typename FuncTTT>
+ //--------------------------
+ FuncTTT LoadFunc(FuncTTT& c, LPCSTR fNameStr) {
+ //--------------------------
+ FuncTTT fPtr;
+
+ // existing lib loaded?
+ if (!dll)
+ if (!this->LoadLibrary())
+ return (FuncTTT) NULL;
+
+ // load func from dll
+ fPtr =(FuncTTT)GetProcAddress (
+ dll, // handle to DLL module
+ fNameStr // name of function
+ );
+ if (!fPtr) {
+ /// return a pointer to a base generic function would be good. ie: ERROR prompt
+ return (FuncTTT) NULL;
+ }
+ c = fPtr;
+ return fPtr;
+ }
+
+public:
+ ///
+ /// decrement dll ref count via win32 ::FreeLibrary(...)
+ ///
+ //--------------------------
+ void FreeLibrary() {
+ //--------------------------
+ if (dll) {
+ ::FreeLibrary(dll);
+ dll=0;
+ }
+ }
+
+public:
+ ///
+ /// find the dll file and attempt to load it
+ ///
+ //------------------------
+ bool LoadLibrary (HINSTANCE hInstance = NULL) {
+ //------------------------
+
+ // existing lib loaded?
+ if (dll !=0 )
+ this->FreeLibrary();
+
+ // load from:
+ // 1. The directory from which the application loaded.
+ // 2. The current directory.
+ // 3. The Windows system directory.
+ // 4. The Windows directory.
+ // 5. The directories that are listed in the PATH environment variable.
+ dll = ::LoadLibrary( dllName );
+ if ( ! dll )
+ {
+ // 6. The module directory (if dll).
+ if ( hInstance )
+ {
+ TCHAR self[ MAX_PATH ];
+ GetModuleFileName( hInstance, self, MAX_PATH );
+ lstrcpy( lstrnrchr( self, _T('\\'), lstrlen( self )) + 1, dllName );
+ dll = ::LoadLibrary( self );
+ }
+ if ( ! dll )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ////// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ ////// All class functions below are for backward compatibility....
+ ////// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ ////// U may delete all of them if u dont need them
+ //////
+ ///
+ /// find() is deprecated. Do not use it anymore.
+ /// locate the functor inside a dll. let a DllFunctor object to do the job
+ /// instead... double dispatch??
+ ///
+public:template <typename QQ>
+ bool find(DllFunctor<QQ>& functor) {
+ return functor.LoadFromDll(this);
+ }
+
+};
+
+///
+/// DllFunctor<> is templated on the function type
+///
+template <typename FuncPtrType>
+class DllFunctor {
+ FuncPtrType fp; // pointer to dll function
+ DllLoader* dll; // which dllLoader to load from
+ CString fname; // name of function as char array
+
+public:
+ DllFunctor(FuncPtrType f, DllLoader* d=0): fp(f), dll(d) {;}
+ DllFunctor(LPCTSTR n): fname(n),fp(0), dll(0) {;}
+ FuncPtrType operator()() {
+ if (!*dll) {
+ if (!dll->LoadLibrary())
+ return (FuncPtrType) NULL;
+ }
+ if (fp == 0) {
+ dll->LoadFunc (fp, fname.c_str());
+ }
+ return fp;
+ }
+ void SetDll(DllLoader& d) { dll=&d; }
+};
+
+#endif
diff --git a/protocols/WinPopup/src/mailslot.cpp b/protocols/WinPopup/src/mailslot.cpp
new file mode 100644
index 0000000000..f632872d36
--- /dev/null
+++ b/protocols/WinPopup/src/mailslot.cpp
@@ -0,0 +1,304 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 MAX_MESSAGE_SIZE 424 // Размер пакета данных (байт) посылаемых/принимаемых
+ // через мейлслот
+
+mailslot pluginMailslot; // Мейлслот для приема сообщений
+
+/*const struct { // Список конфликтующих процессов
+ LPCTSTR name;
+} blacklist [] = {
+ _T("winpopup"),
+ _T("vikpopup"),
+ _T("netter"),
+ _T("realpopup"),
+ NULL
+};*/
+
+////////////////////////////////////////////////////////////////////////
+// Class mailslot
+
+mailslot::mailslot () :
+ m_hMailslot (INVALID_HANDLE_VALUE),
+ m_MonitorTerm (NULL),
+ m_Monitor (NULL)
+{
+}
+
+mailslot::~mailslot ()
+{
+ Destroy ();
+}
+
+bool mailslot::Create (LPCTSTR Name)
+{
+ CLock oLock( m_cs );
+
+ m_sName = Name;
+
+ bool ret = true;
+ if ( ! IsValid() )
+ {
+ // Открытие мейлслота
+ CString sAddr;
+ sAddr.Format( _T("\\\\.\\mailslot\\%s"), (LPCTSTR)m_sName );
+
+ m_hMailslot = CreateMailslot( sAddr, 0, 2000, NULL );
+ if ( ! IsValid() )
+ {
+ ret = false;
+ DWORD err = GetLastError ();
+ if (err == ERROR_ALREADY_EXISTS)
+ WarningBox (NULL, 0, _T("%s\r\n%s"), T_CREATE_ERROR,
+ TranslateT ("Please shutdown any other IM applications and/or Messenger service"));
+ else
+ WarningBox (NULL, err, T_CREATE_ERROR);
+ }
+ }
+ if (ret)
+ {
+ if (m_MonitorTerm)
+ ResetEvent (m_MonitorTerm);
+ else
+ m_MonitorTerm = CreateEvent (NULL, TRUE, FALSE, NULL);
+ m_Monitor = (HANDLE)mir_forkthread( MonitorThread, this );
+ }
+
+ return ret;
+}
+
+void mailslot::AskForDestroy()
+{
+ if (m_MonitorTerm)
+ SetEvent (m_MonitorTerm);
+}
+
+void mailslot::Destroy ()
+{
+ CLock oLock( m_cs );
+
+ // Запрос останова мониторинга мейлслота
+ AskForDestroy();
+
+ // Закрытие мейлслота
+ if ( IsValid() )
+ {
+ CloseHandle (m_hMailslot);
+ m_hMailslot = INVALID_HANDLE_VALUE;
+ }
+
+ // Ожидание останова мониторинга
+ if (m_Monitor)
+ {
+ if (WaitForSingleObject (m_Monitor, ALMOST_INFINITE) == WAIT_TIMEOUT)
+ {
+ LOG("Terminate mailslot monitor!");
+ TerminateThread (m_Monitor, 0);
+ }
+ m_Monitor = NULL;
+ }
+ if (m_MonitorTerm)
+ {
+ CloseHandle (m_MonitorTerm);
+ m_MonitorTerm = NULL;
+ }
+}
+
+bool mailslot::IsValid() const
+{
+ return ( m_hMailslot != INVALID_HANDLE_VALUE );
+}
+
+bool mailslot::SendMailslotMessage(HANDLE hContact, LPCTSTR msg, DWORD& err)
+{
+ // Получение адресата
+ CString sTo = GetNick( hContact );
+ if ( sTo.IsEmpty() )
+ {
+ err = ERROR_BAD_NETPATH;
+ return false;
+ }
+
+ // Получение своего имени
+ CString sFrom = GetNick( NULL );
+ if ( sFrom.IsEmpty() )
+ {
+ err = ERROR_BAD_NETPATH;
+ return false;
+ }
+
+ // Нет разницы группа это или компьютер
+ // bool bGroup = IsGroup( hContact );
+
+ // Сборка пакета сообщения: FROM<00>TO<00>MESSAGE<00>
+ COemString sOemMessage = msg;
+ COemString sOemTo = (LPCTSTR)sTo;
+ COemString sOemFrom = (LPCTSTR)sFrom;
+
+ // Размер заголовка пакета
+ int fixed_size = sOemFrom.GetLength() + 1 + sOemTo.GetLength() + 1 + 1;
+ if ( fixed_size >= MAX_MESSAGE_SIZE )
+ {
+ err = ERROR_BAD_LENGTH;
+ return false;
+ }
+
+ // Создание мейлслота для отправки сообщения
+ CString sAddr;
+ sAddr.Format( _T("\\\\%s\\mailslot\\%s"), sTo, (LPCTSTR)m_sName );
+ HANDLE hFile = CreateFile( sAddr, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+ if ( hFile == INVALID_HANDLE_VALUE )
+ {
+ err = GetLastError();
+ return false;
+ }
+
+ int max_message_size = MAX_MESSAGE_SIZE - fixed_size;
+ char buf[ MAX_MESSAGE_SIZE ] = {};
+ lstrcpynA( buf, sOemFrom, sOemFrom.GetLength() + 1 );
+ lstrcpynA( buf + sOemFrom.GetLength() + 1, sOemTo, sOemTo.GetLength() + 1 );
+ do
+ {
+ int message_size = ( sOemMessage.GetLength() < max_message_size ) ?
+ sOemMessage.GetLength() : max_message_size;
+ lstrcpynA( buf + fixed_size - 1, sOemMessage, message_size + 1 );
+
+ // Отсылка пакета
+ DWORD written = 0;
+ if ( ! WriteFile( hFile, buf, (DWORD)fixed_size + message_size, &written, NULL ) ||
+ ( written < (DWORD)fixed_size ) )
+ {
+ err = GetLastError();
+ CloseHandle( hFile );
+ return false;
+ }
+ Sleep( 100 );
+
+ // Укорачивание на отосланный пакет
+ sOemMessage.CutFromStart( message_size );
+ }
+ while ( sOemMessage.GetLength() );
+
+ err = ERROR_SUCCESS;
+ CloseHandle( hFile );
+ return true;
+}
+
+bool mailslot::Receive(unsigned char* buf /* OEM */, DWORD size)
+{
+ // Разборка сообщения <FROM><00><TO><00><MESSAGE><00> (последний <00> необязателен)
+ if (size)
+ {
+ char* from = (char*) buf;
+ char* to = lstrnchr (from, 0, (int)size);
+ if (to)
+ {
+ DWORD from_len = (DWORD)( to - from + 1 );
+ if ( from_len < size )
+ {
+ to++;
+ size -= from_len;
+ char* msg = lstrnchr (to, 0, (int)size);
+ if (msg)
+ {
+ DWORD to_len = (DWORD)( msg - to + 1 );
+ if (to_len < size)
+ {
+ msg++;
+ size -= to_len;
+ char* eof = lstrnchr (msg, 0, (int)size);
+ DWORD msg_len = eof ? (DWORD)( eof - msg + 1 ) : size;
+ if (msg_len == size)
+ {
+ CAnsiString sFrom (from);
+ CAnsiString sTo (to);
+ CAnsiString sMessage (msg);
+ ReceiveContactMessage(sFrom, sTo, sMessage, sMessage.GetLength ());
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void mailslot::MonitorThread(void* param)
+{
+ if ( mailslot* pMailslot = (mailslot*)param )
+ {
+ pMailslot->Monitor();
+ pMailslot->m_Monitor = NULL;
+ }
+}
+
+void mailslot::Monitor ()
+{
+ // Ожидание прерывания 500 мс при закрытом майлслоте, иначе 50 мс
+ while ( WaitForSingleObject( m_MonitorTerm, IsValid() ? 50u : 500u ) == WAIT_TIMEOUT )
+ {
+ // Проверка входящих сообщений
+ LPSTR buf = NULL;
+ for ( DWORD buf_size = MAX_MESSAGE_SIZE; IsValid(); buf_size += 1024 )
+ {
+ if ( WaitForSingleObject( m_MonitorTerm, 0 ) != WAIT_TIMEOUT )
+ break;
+
+ if ( buf ) mir_free( buf );
+ buf = (LPSTR)mir_alloc( buf_size );
+
+ DWORD readed = 0;
+ DWORD err = ReadFile (m_hMailslot, buf, buf_size,
+ &readed, NULL) ? ERROR_SUCCESS : GetLastError ();
+ if (err == ERROR_ACCESS_DENIED || err == ERROR_SEM_TIMEOUT)
+ {
+ // Тайм-аут мейлслота
+ break;
+ }
+ else if (err == ERROR_SUCCESS)
+ {
+ // Данные приняты
+ if (readed)
+ if (!Receive((LPBYTE)buf, readed))
+ LOG("Receive error (bad format?)");
+ break;
+ }
+ else if (err == ERROR_INSUFFICIENT_BUFFER)
+ {
+ // Нехватка размера буфера
+ continue;
+ }
+ else
+ {
+ // Другие ошибки
+ // ERROR_HANDLE_EOF - хендл мейлслота закрыт
+ LOG("ReadFile form mailslot error: %d", err);
+ break;
+ }
+ }
+ if ( buf ) mir_free( buf );
+ }
+}
diff --git a/protocols/WinPopup/src/mailslot.h b/protocols/WinPopup/src/mailslot.h
new file mode 100644
index 0000000000..363ac6df3e
--- /dev/null
+++ b/protocols/WinPopup/src/mailslot.h
@@ -0,0 +1,46 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+class mailslot
+{
+public:
+ mailslot();
+ ~mailslot();
+
+ bool Create(LPCTSTR Name);
+ void AskForDestroy(); // Для ускорения
+ void Destroy();
+ bool IsValid() const;
+ bool SendMailslotMessage(HANDLE hContact, LPCTSTR msg, DWORD& err);
+
+protected:
+ CString m_sName; // Имя мейлслота
+ HANDLE m_hMailslot; // Хэндлер мейлслота
+ CComAutoCriticalSection m_cs; // Защита данных
+ HANDLE m_MonitorTerm; // Событие для остановки Monitor
+ HANDLE m_Monitor; // Прием/отправка сообщений через мейлслот
+
+ bool Receive(unsigned char* buf /* OEM */, DWORD size);
+ static void MonitorThread(void* param);
+ void Monitor();
+};
+
+extern mailslot pluginMailslot; // Мейлслот для приема сообщений
diff --git a/protocols/WinPopup/src/md5.h b/protocols/WinPopup/src/md5.h
new file mode 100644
index 0000000000..b8c5ad5735
--- /dev/null
+++ b/protocols/WinPopup/src/md5.h
@@ -0,0 +1,220 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+typedef unsigned long uint32;
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+// initialization constants.
+inline void md5init (struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+// The four core functions - F1 is optimized somewhat
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+// This is the central step in the MD5 algorithm.
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+// The core of the MD5 algorithm, this alters an existing MD5 hash to
+// reflect the addition of 16 longwords of new data. MD5Update blocks
+// the data and converts bytes into longwords for this routine.
+inline void md5transform (uint32 buf[4], uint32 in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+// Update context to reflect the concatenation of another buffer full of bytes.
+inline void md5update (struct MD5Context *ctx, const unsigned char *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ CopyMemory (p, buf, len);
+ return;
+ }
+ CopyMemory (p, buf, t);
+ md5transform(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ CopyMemory (ctx->in, buf, 64);
+ md5transform(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ CopyMemory (ctx->in, buf, len);
+}
+
+// Final wrapup - pad to 64-byte boundary with the bit pattern
+// 1 0* (64-bit count of bits processed, MSB-first)
+inline void md5final (unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ ZeroMemory (p, count);
+ md5transform(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ ZeroMemory (ctx->in, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ ZeroMemory (p, count - 8);
+ }
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ md5transform(ctx->buf, (uint32 *) ctx->in);
+ CopyMemory (digest, ctx->buf, 16);
+ ZeroMemory (ctx, sizeof(ctx)); /* In case it's sensitive */
+}
diff --git a/protocols/WinPopup/src/messagebox.cpp b/protocols/WinPopup/src/messagebox.cpp
new file mode 100644
index 0000000000..eb99d07d74
--- /dev/null
+++ b/protocols/WinPopup/src/messagebox.cpp
@@ -0,0 +1,223 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 messagebox
+{
+public:
+ messagebox () :
+ m_hwndOwner (NULL),
+ m_hwndMessageBox (NULL),
+ m_Timeout (0),
+ m_MonitorTerm (NULL)
+ {
+ }
+
+ int WINAPI DoModal (const LPMSGBOXPARAMS lpMsgBoxParams, DWORD dwTimeout)
+ {
+ int ret = 0;
+ m_hwndOwner = lpMsgBoxParams->hwndOwner;
+ m_Timeout = dwTimeout;
+ m_hwndMessageBox = NULL;
+ m_MonitorTerm = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (m_MonitorTerm) {
+ HANDLE hThread = (HANDLE)mir_forkthread( MsgBox, this );
+ if (hThread) {
+
+ ret = MessageBoxIndirect (lpMsgBoxParams);
+
+ // Ожидание завершения
+ SetEvent (m_MonitorTerm);
+ WaitForSingleObject (hThread, INFINITE);
+ }
+ CloseHandle (m_MonitorTerm);
+ }
+ return ret;
+ }
+
+protected:
+ static BOOL CALLBACK EnumWindowsProc (HWND hWnd, LPARAM lParam)
+ {
+ messagebox* self = reinterpret_cast <messagebox*> (lParam);
+
+ DWORD dwProcessId;
+ GetWindowThreadProcessId (hWnd, &dwProcessId);
+ const LONG req_style = WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU;
+ HWND hButton;
+ TCHAR ClassName[ 8 ];
+ if ( GetCurrentProcessId () != dwProcessId ||
+ ( GetWindowLongPtr(hWnd, GWL_STYLE) & req_style ) != req_style ||
+ GetParent( hWnd ) != NULL ||
+ GetWindow( hWnd, GW_OWNER ) != self->m_hwndOwner ||
+ ( hButton = GetWindow( hWnd, GW_CHILD ) ) == NULL ||
+ GetClassName( hButton, ClassName, _countof( ClassName ) ) == 0 ||
+ lstrcmpi( _T("button"), ClassName ) != 0 )
+ return TRUE;
+
+ self->m_hwndMessageBox = hWnd;
+
+ return FALSE;
+ }
+
+ static void MsgBox (LPVOID param)
+ {
+ messagebox* self = reinterpret_cast <messagebox*> (param);
+
+ // Ловим диалог 10 секунд
+ DWORD i = 0;
+ while (WaitForSingleObject (self->m_MonitorTerm, 250) == WAIT_TIMEOUT &&
+ EnumWindows (messagebox::EnumWindowsProc, reinterpret_cast <LPARAM> (self)) &&
+ i++ < 10 * 4);
+ if (!self->m_hwndMessageBox)
+ // Так и не поймали
+ return;
+
+ HWND hButton = GetWindow (self->m_hwndMessageBox, GW_CHILD);
+
+ // Отсчёт времени
+ while (self->m_Timeout-- &&
+ WaitForSingleObject (self->m_MonitorTerm, 1000) == WAIT_TIMEOUT &&
+ IsWindow (self->m_hwndMessageBox) && IsWindow (hButton)) {
+ CString buf, msg;
+ int buf_size = GetWindowTextLength (hButton);
+ if (buf_size) {
+ GetWindowText (hButton, buf.GetBuffer (buf_size + 1), buf_size + 1);
+ buf.ReleaseBuffer ();
+ }
+ int n = buf.ReverseFind (_T('='));
+ msg.Format (_T(" = %u"), self->m_Timeout);
+ SetWindowText (hButton, ((n < 1) ? buf : buf.Left (n - 1)) + msg);
+ }
+
+ // Закрытие окна
+ if (IsWindow (self->m_hwndMessageBox) && IsWindow (hButton)) {
+ DWORD_PTR res;
+ SendMessageTimeout (self->m_hwndMessageBox, WM_COMMAND,
+ (WPARAM) GetDlgCtrlID (hButton),
+ (LPARAM) hButton, SMTO_ABORTIFHUNG | SMTO_NORMAL, 10000, &res);
+ }
+
+ return;
+ }
+
+ volatile HWND m_hwndOwner;
+ volatile HWND m_hwndMessageBox;
+ volatile DWORD m_Timeout;
+ HANDLE m_MonitorTerm;
+};
+
+static int WINAPI MessageBoxIndirectTimeout (const LPMSGBOXPARAMS lpMsgBoxParams, DWORD dwTimeout)
+{
+ messagebox mb;
+ return mb.DoModal (lpMsgBoxParams, dwTimeout);
+}
+
+LPCTSTR const szModules [] = {
+ _T("netapi32.dll"),
+ _T("netmsg.dll"),
+ _T("wininet.dll"),
+ _T("ntdll.dll"),
+ _T("ntdsbmsg.dll"),
+ NULL
+};
+
+void GetErrorMessage (DWORD dwLastError, CString &msg)
+{
+ CString buf;
+ if ( HRESULT_FACILITY( dwLastError ) == FACILITY_NETBIOS )
+ {
+ CString sMessage = (LPCTSTR)CA2T( GetNetbiosError( HRESULT_CODE ( dwLastError ) ) );
+ if ( ! sMessage.IsEmpty() )
+ msg.Format( _T("%s\r\nNetBIOS %s: %u"), sMessage, T_ERROR, HRESULT_CODE( dwLastError ) );
+ else
+ msg.Format( _T("NetBIOS %s: %u"), T_ERROR, HRESULT_CODE( dwLastError ) );
+ }
+ else
+ {
+ HMODULE hModule = NULL;
+ for (int i = 0; szModules [i]; i++)
+ {
+ hModule = LoadLibraryEx (szModules [i], NULL, LOAD_LIBRARY_AS_DATAFILE);
+ LPTSTR MessageBuffer = NULL;
+ if (FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0u),
+ hModule, dwLastError, 0u, (LPTSTR) &MessageBuffer, 0u, NULL))
+ {
+ buf = MessageBuffer;
+ buf.Trim (_T(" \t\r\n"));
+ LocalFree (MessageBuffer);
+ if (hModule)
+ FreeLibrary (hModule);
+ break;
+ }
+ if (hModule)
+ FreeLibrary (hModule);
+ }
+ if ( ! buf.IsEmpty() )
+ msg.Format( _T("%s\r\n%s: %u"), (LPCTSTR)buf, T_ERROR, dwLastError );
+ else
+ msg.Format( _T("%s: %u"), T_ERROR, dwLastError );
+ }
+}
+
+static void PopupOrMessageBox (LPPOPUPDATAT ppdp)
+{
+ if (CALLSERVICE_NOTFOUND == PUAddPopUpT (ppdp))
+ {
+ MSGBOXPARAMS mbp = { 0 };
+ mbp.cbSize = sizeof (MSGBOXPARAMS);
+ mbp.lpszText = ppdp->lptzText;
+ mbp.lpszCaption = ppdp->lptzContactName;
+ mbp.dwStyle = MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL;
+ MessageBoxIndirectTimeout (&mbp, (DWORD)ppdp->iSeconds);
+ }
+}
+
+void WarningBox (HANDLE hContact /* = NULL */, DWORD dwLastError /* = 0 */, LPCTSTR format, ...)
+{
+ if (!pluginInstalled)
+ return;
+
+ POPUPDATAT pdp = { 0 };
+ pdp.lchContact = hContact;
+ pdp.lchIcon = (HICON) LoadImage( pluginModule, MAKEINTRESOURCE (IDI_WINPOPUP),
+ IMAGE_ICON, 16, 16, LR_SHARED );
+ lstrcpy (pdp.lptzContactName, modtitle_t);
+ va_list marker;
+ va_start (marker, format);
+ wvsprintf( pdp.lptzText, format, marker );
+ va_end (marker);
+ pdp.iSeconds = 10;
+
+ if (dwLastError) {
+ CString msg;
+ GetErrorMessage (dwLastError, msg);
+ int len = lstrlen (pdp.lptzText);
+ pdp.lptzText [len] = _T('\r');
+ pdp.lptzText [len + 1] = _T('\n');
+ lstrcpy (pdp.lptzText + len + 2, msg);
+ }
+
+ PopupOrMessageBox (&pdp);
+}
diff --git a/protocols/WinPopup/src/messagebox.h b/protocols/WinPopup/src/messagebox.h
new file mode 100644
index 0000000000..182b40483a
--- /dev/null
+++ b/protocols/WinPopup/src/messagebox.h
@@ -0,0 +1,27 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// Отображение пояснительного текста ошибки в виде высплывающей подсказки если
+// установлен Popup Plugin, иначе в виде диалога с автозакрытием
+void WarningBox (HANDLE hContact /* = NULL */, DWORD dwLastError /* = 0 */, LPCTSTR format, ...);
+
+// Получение описания WinAPI или NetBIOS ошибки по её номеру
+void GetErrorMessage (DWORD dwLastError, CString &msg);
diff --git a/protocols/WinPopup/src/messenger.cpp b/protocols/WinPopup/src/messenger.cpp
new file mode 100644
index 0000000000..51df7b2beb
--- /dev/null
+++ b/protocols/WinPopup/src/messenger.cpp
@@ -0,0 +1,458 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 IsMessengerRunning()
+{
+ bool found = false;
+ HANDLE h = CreateMailslot (_T("\\\\.\\mailslot\\") MESSENGER_MAIL, 0, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ if (GetLastError () == ERROR_ALREADY_EXISTS)
+ found = true;
+ else
+ LOG ("IsMessengerRunning() error 0x%08x", GetLastError ());
+ } else
+ CloseHandle (h);
+ return found;
+}
+
+messenger pluginMessenger; // Прием/отправка сообщений через Messenger
+
+messenger::messenger () :
+ m_MonitorTerm (NULL),
+ m_Monitor (NULL),
+ m_ID (0),
+ m_bMSMessengerStopped (false)
+{
+}
+
+messenger::~messenger ()
+{
+ Destroy ();
+}
+
+bool messenger::Create(BOOL start)
+{
+ if ( pluginOS.dwPlatformId != VER_PLATFORM_WIN32_NT )
+ return false;
+
+ // Косвенная проверка, что Messenger запущен
+ bool messngr = IsMessengerRunning();
+ if ( start )
+ {
+ if ( ! messngr )
+ Start ();
+ }
+ else
+ {
+ if ( messngr )
+ Stop ();
+ }
+
+ m_ID = GetProcessId (_T("csrss.exe"));
+ LOG ( ( m_ID ? "Messenger host process CSRSS.EXE found : PID %u(%08x)" :
+ "Messenger host process CSRSS.EXE not found" ), m_ID, m_ID );
+
+ // Запуск сканера контактов
+ if (m_MonitorTerm)
+ ResetEvent (m_MonitorTerm);
+ else
+ m_MonitorTerm = CreateEvent (NULL, TRUE, FALSE, NULL);
+
+ if (!m_Monitor)
+ m_Monitor = (HANDLE)mir_forkthread( MonitorThread, this );
+
+ return (m_Monitor != NULL);
+}
+
+void messenger::AskForDestroy()
+{
+ if (m_MonitorTerm)
+ SetEvent (m_MonitorTerm);
+}
+
+void messenger::Destroy ()
+{
+ if (pluginOS.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ return;
+
+ AskForDestroy();
+
+ if (m_Monitor)
+ {
+ if (WaitForSingleObject (m_Monitor, ALMOST_INFINITE) == WAIT_TIMEOUT)
+ {
+ LOG("Terminate Messenger monitor!");
+ TerminateThread (m_Monitor, 0);
+ }
+ m_Monitor = NULL;
+ }
+
+ if (m_MonitorTerm)
+ {
+ CloseHandle (m_MonitorTerm);
+ m_MonitorTerm = NULL;
+ }
+
+ // Запуск "Службы Сообщений", если ее останавливали
+ if (m_bMSMessengerStopped)
+ Start ();
+}
+
+messenger::operator bool () const
+{
+ return (m_Monitor != NULL);
+}
+
+BOOL CALLBACK messenger::EnumWindowsProc (HWND hWnd, LPARAM lParam)
+{
+ DWORD dwTargetId = reinterpret_cast <messenger*> (lParam)->m_ID;
+ DWORD dwProcessId = 0;
+ GetWindowThreadProcessId (hWnd, &dwProcessId);
+ if (dwTargetId && dwTargetId != dwProcessId)
+ return TRUE;
+
+ const LONG req_style = WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU;
+ const LONG req_style_ex = WS_EX_TOPMOST;
+ HWND hButton, hText;
+ TCHAR ClassName[ 8 ] = {};
+ if ((GetWindowLongPtr(hWnd, GWL_STYLE) & req_style) != req_style ||
+ (GetWindowLongPtr(hWnd, GWL_EXSTYLE) & req_style_ex) != req_style_ex ||
+ GetParent (hWnd) != NULL ||
+ GetWindow (hWnd, GW_OWNER) != NULL ||
+ // child 1 = Button, child 2 = STATIC, child 3 = NULL
+ (hButton = GetWindow (hWnd, GW_CHILD)) == NULL ||
+ GetClassName( hButton, ClassName, _countof( ClassName ) ) == 0 ||
+ lstrcmpi (_T("button"), ClassName) != 0 ||
+ (hText = GetNextWindow (hButton, GW_HWNDNEXT)) == NULL ||
+ GetClassName( hText, ClassName, _countof( ClassName ) ) == 0 ||
+ lstrcmpi( _T("static"), ClassName ) != 0 ||
+ GetNextWindow (hText, GW_HWNDNEXT) != NULL)
+ // Чужое окно
+ return TRUE;
+
+ CString buf;
+ int buf_size = GetWindowTextLength (hText);
+ if (buf_size) {
+ GetWindowText (hText, buf.GetBuffer (buf_size + 1), buf_size + 1);
+ buf.ReleaseBuffer ();
+ }
+
+ if (!buf.IsEmpty ()) {
+ CString tok, from, to, msg;
+ int curPos = 0;
+ for (int i = 0; (tok = buf.Tokenize( _T(" "), curPos)), !tok.IsEmpty (); i++) {
+ switch (i) {
+ case 2:
+ from = tok;
+ break;
+ case 4:
+ to = tok;
+ break;
+ }
+ }
+ int n = buf.Find ( _T("\n") );
+ msg = buf.Mid (n + 3);
+ if (!from.IsEmpty () && !to.IsEmpty () && !msg.IsEmpty ()) {
+ // Закрытие окна
+ DWORD_PTR res = 0;
+ SendMessageTimeout (hWnd, WM_COMMAND, (WPARAM) GetDlgCtrlID (hButton),
+ (LPARAM) hButton, SMTO_ABORTIFHUNG | SMTO_NORMAL, 10000, &res);
+
+ // Ок
+ ReceiveContactMessage(from, to, msg, msg.GetLength ());
+ }
+ }
+
+ return FALSE;
+}
+
+void messenger::Monitor ()
+{
+ while (WaitForSingleObject (m_MonitorTerm, 250) == WAIT_TIMEOUT)
+ {
+ EnumWindows (EnumWindowsProc, reinterpret_cast <LPARAM> (this));
+ }
+ m_Monitor = NULL;
+}
+
+void messenger::MonitorThread(LPVOID lpParameter)
+{
+ reinterpret_cast< messenger* >( lpParameter )->Monitor();
+}
+
+// Запуск "Службы Сообщений"
+bool messenger::Start ()
+{
+ if (pluginOS.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ return false;
+
+ bool ret = m_bMSMessengerStopped = false;
+
+ // Открытие менеджера сервисов
+ SC_HANDLE manager = fnOpenSCManager (NULL, NULL,
+ STANDARD_RIGHTS_READ | SC_MANAGER_CONNECT);
+ if (manager) {
+ // Открытие сервиса
+ SC_HANDLE service = fnOpenService (manager, MESSENGER,
+ SERVICE_START | SERVICE_QUERY_STATUS);
+ if (service) {
+ for(;;)
+ {
+ // Проверка состояния сервиса
+ SERVICE_STATUS ss = { 0 };
+ if (fnQueryServiceStatus (service, &ss)) {
+ if (ss.dwCurrentState == SERVICE_RUNNING) {
+ // Уже работает
+ LOG ("Messenger service already running");
+ ret = true;
+ } else {
+ // Запуск сервиса
+ if (fnStartService (service, 0, NULL))
+ // Ожидание запуска сервиса
+ ret = WaitForService (T_START_ERROR, service, SERVICE_START_PENDING, SERVICE_RUNNING);
+ else {
+ DWORD err = GetLastError ();
+ if ( err == ERROR_SERVICE_DISABLED )
+ {
+ // Попытка разрешения сервиса
+ if ( Enable() )
+ // Повтор
+ continue;
+ } else
+ WarningBox (NULL, err, T_START_ERROR);
+ }
+ }
+ } else
+ WarningBox (NULL, GetLastError (), T_START_ERROR);
+ break;
+ }
+ fnCloseServiceHandle (service);
+ } else
+ WarningBox (NULL, GetLastError (), T_START_ERROR);
+ fnCloseServiceHandle (manager);
+ } else
+ WarningBox (NULL, GetLastError (), T_START_ERROR);
+ return ret;
+}
+
+// Останов "Службы Сообщений"
+bool messenger::Stop ()
+{
+ if (pluginOS.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ return false;
+
+ bool ret = m_bMSMessengerStopped = false;
+
+ // Открытие менеджера сервисов
+ SC_HANDLE manager = fnOpenSCManager (NULL, NULL,
+ STANDARD_RIGHTS_READ | SC_MANAGER_CONNECT);
+ if (manager)
+ {
+ // Открытие сервиса
+ SC_HANDLE service = fnOpenService (manager, MESSENGER,
+ SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (service)
+ {
+ // Проверка состояния сервиса
+ SERVICE_STATUS ss = {};
+ if (fnQueryServiceStatus (service, &ss))
+ {
+ if (ss.dwCurrentState == SERVICE_STOPPED)
+ {
+ // Уже остановлен
+ LOG ("Messenger service already stopped");
+ ret = true; // m_bMSMessengerStopped = false
+ }
+ else
+ {
+ // Останов сервиса
+ ZeroMemory (&ss, sizeof (SERVICE_STATUS));
+ if (fnControlService (service, SERVICE_CONTROL_STOP, &ss))
+ {
+ // Ожидание останова
+ ret = m_bMSMessengerStopped = WaitForService (
+ T_STOP_ERROR, service,
+ SERVICE_STOP_PENDING, SERVICE_STOPPED);
+ }
+ else
+ {
+ if (GetLastError () == ERROR_SERVICE_NOT_ACTIVE)
+ // Уже остановлен
+ ret = true; // m_bMSMessengerStopped = false
+ else
+ WarningBox (NULL, GetLastError (), T_STOP_ERROR);
+ }
+ }
+ }
+ else
+ WarningBox (NULL, GetLastError (), T_STOP_ERROR);
+ fnCloseServiceHandle (service);
+ }
+ else
+ {
+ if (GetLastError () == ERROR_SERVICE_DOES_NOT_EXIST)
+ // Такого сервиса нет - ок
+ ret = true; // m_bMSMessengerStopped = false
+ else
+ WarningBox (NULL, GetLastError (), T_STOP_ERROR);
+ }
+ fnCloseServiceHandle (manager);
+ }
+ else
+ WarningBox (NULL, GetLastError (), T_STOP_ERROR);
+
+ return ret;
+}
+
+// Разрешение запуска "Службы Сообщений"
+bool messenger::Enable ()
+{
+ bool ret = false;
+
+ // Открытие менеджера сервисов
+ SC_HANDLE manager = fnOpenSCManager (NULL, NULL,
+ STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG |
+ SC_MANAGER_CONNECT | SC_MANAGER_LOCK);
+ if (manager)
+ {
+ // Блокировка менеджера
+ SC_LOCK lock = fnLockServiceDatabase( manager );
+ if (lock)
+ {
+ // Открытие сервиса
+ SC_HANDLE service = fnOpenService ( manager, MESSENGER,
+ SERVICE_CHANGE_CONFIG );
+ if (service)
+ {
+ // Установка автозапуска сервиса
+ if ( fnChangeServiceConfig( service, SERVICE_NO_CHANGE,
+ SERVICE_AUTO_START, SERVICE_NO_CHANGE,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL ) )
+ ret = true;
+ else
+ WarningBox (NULL, GetLastError (), T_ENABLE_ERROR);
+
+ fnCloseServiceHandle (service);
+ }
+ else
+ WarningBox (NULL, GetLastError (), T_ENABLE_ERROR);
+
+ fnUnlockServiceDatabase( lock );
+ }
+ else
+ WarningBox (NULL, GetLastError (), T_ENABLE_ERROR);
+
+ fnCloseServiceHandle ( manager );
+ }
+ else
+ WarningBox (NULL, GetLastError (), T_ENABLE_ERROR);
+ return ret;
+}
+
+bool messenger::WaitForService (LPCTSTR reason, SC_HANDLE service, DWORD process, DWORD end)
+{
+ SERVICE_STATUS ss = { 0 };
+ if (fnQueryServiceStatus (service, &ss))
+ {
+ DWORD dwStartTickCount = GetTickCount();
+ DWORD dwOldCheckPoint = ss.dwCheckPoint;
+ while (ss.dwCurrentState == process) {
+ // Do not wait longer than the wait hint. A good interval is
+ // one tenth the wait hint, but no less than 1 second and no
+ // more than 10 seconds.
+ DWORD dwWaitTime = ss.dwWaitHint / 10;
+ if (dwWaitTime < 1000)
+ dwWaitTime = 1000;
+ else
+ if (dwWaitTime > 10000)
+ dwWaitTime = 10000;
+ Sleep (dwWaitTime);
+ // Check the status again.
+ if (!fnQueryServiceStatus (service, &ss))
+ {
+ WarningBox (NULL, GetLastError (), reason);
+ return false;
+ }
+ if (ss.dwCurrentState == end)
+ break;
+ if (ss.dwCheckPoint > dwOldCheckPoint)
+ {
+ // The service is making progress.
+ dwStartTickCount = GetTickCount();
+ dwOldCheckPoint = ss.dwCheckPoint;
+ } else
+ {
+ if (GetTickCount() - dwStartTickCount > ss.dwWaitHint)
+ {
+ WarningBox (NULL, 0, _T("%s\r\n%s"), TranslateT ("No progress"), reason);
+ return false;
+ }
+ }
+ }
+ if (ss.dwCurrentState == end)
+ return true;
+ WarningBox (NULL, 0, _T("%s\r\n%s"), TranslateT ("Unexpected service status change"), reason);
+ }
+ else
+ WarningBox (NULL, GetLastError (), reason);
+ return false;
+}
+
+bool messenger::SendMessengerMessage(HANDLE hContact, LPCTSTR msg, DWORD& err)
+{
+ if ( pluginOS.dwPlatformId != VER_PLATFORM_WIN32_NT || ! fnNetMessageBufferSend )
+ {
+ err = ERROR_NOT_SUPPORTED;
+ return false;
+ }
+
+ // Получение адресата
+ CString sTo = GetNick( hContact );
+ if ( sTo.IsEmpty() )
+ {
+ err = NERR_NameNotFound;
+ return false;
+ }
+
+ // Получение своего имени
+ CString sFrom = GetNick( NULL );
+ if ( sFrom.IsEmpty() )
+ {
+ err = NERR_NameNotFound;
+ return false;
+ }
+
+ // Нет разницы группа это или компьютер
+ // bool bGroup = IsGroup( hContact );
+
+ LPWSTR wto = mir_t2u( sTo );
+ LPWSTR wfrom = mir_t2u( sFrom );
+ LPWSTR wmsg = mir_t2u( msg );
+ err = fnNetMessageBufferSend( NULL, wto, wfrom, (LPBYTE)wmsg,
+ lstrlen( msg ) * sizeof( WCHAR ) );
+ mir_free( wto );
+ mir_free( wfrom );
+ mir_free( wmsg );
+
+ return ( err == NERR_Success );
+}
diff --git a/protocols/WinPopup/src/messenger.h b/protocols/WinPopup/src/messenger.h
new file mode 100644
index 0000000000..fe028b2657
--- /dev/null
+++ b/protocols/WinPopup/src/messenger.h
@@ -0,0 +1,52 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+class messenger
+{
+public:
+ messenger ();
+ ~messenger ();
+
+ bool Create (BOOL start);
+ void AskForDestroy(); // Для ускорения
+ void Destroy ();
+ operator bool () const;
+
+ bool Start (); // Запуск "Службы Сообщений"
+ bool Stop (); // Останов "Службы Сообщений"
+ bool Enable (); // Разрешение запуска "Службы Сообщений"
+
+ bool SendMessengerMessage(HANDLE hContact, LPCTSTR msg, DWORD& err);
+
+protected:
+ HANDLE m_MonitorTerm; // Событие для остановки
+ HANDLE m_Monitor; // Периодическая проверка
+ DWORD m_ID; // ID процесса CSRSS.EXE
+ bool m_bMSMessengerStopped; // Флаг показывающий был ли остановлен
+ // MS Messenger с помощью функции Stop()
+
+ void Monitor ();
+ static BOOL CALLBACK EnumWindowsProc (HWND hwnd, LPARAM lParam);
+ static void MonitorThread (LPVOID lpParameter);
+ static bool WaitForService (LPCTSTR reason, SC_HANDLE service, DWORD process, DWORD end);
+};
+
+extern messenger pluginMessenger; // Прием/отправка сообщений через Messenger
diff --git a/protocols/WinPopup/src/netbios.cpp b/protocols/WinPopup/src/netbios.cpp
new file mode 100644
index 0000000000..2a1c5a4f68
--- /dev/null
+++ b/protocols/WinPopup/src/netbios.cpp
@@ -0,0 +1,1055 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+netbios pluginNetBIOS; // Прием/отправка сообщений через NetBIOS
+
+// Макрос печати NetBIOS-имени
+#define NETBIOS_NAME_TRACE(bb,mm,nn) mir_snprintf((bb),(mm),"%s #%d %02x %c %s", \
+ (nn.GetANSIFullName()), (nn.name_num), (nn.name_flags), \
+ (((nn.name_flags & GROUP_NAME) == GROUP_NAME) ? 'G' : 'U'), \
+ (((nn.name_flags & (NAME_FLAGS_MASK & ~GROUP_NAME)) == REGISTERING) ? "REGISTERING" : \
+ (((nn.name_flags & (NAME_FLAGS_MASK & ~GROUP_NAME)) == REGISTERED) ? "REGISTERED" : \
+ (((nn.name_flags & (NAME_FLAGS_MASK & ~GROUP_NAME)) == DEREGISTERED) ? "DEREGISTERED" : \
+ (((nn.name_flags & (NAME_FLAGS_MASK & ~GROUP_NAME)) == DUPLICATE) ? "DUPLICATE" : \
+ (((nn.name_flags & (NAME_FLAGS_MASK & ~GROUP_NAME)) == DUPLICATE_DEREG) ? "DUPLICATE_DEREG" : \
+ "UNKNOWN"))))));
+
+// NetBIOS commands
+static const struct
+{
+ UCHAR code; // NetBIOS command code
+ LPCSTR name; // NetBIOS command description
+}
+NCBCommands[] =
+{
+ { NCBCALL, "CALL" },
+ { NCBLISTEN, "LISTEN" },
+ { NCBHANGUP, "HANG UP" },
+ { NCBSEND, "SEND" },
+ { NCBRECV, "RECEIVE" },
+ { NCBRECVANY, "RECEIVE ANY" },
+ { NCBCHAINSEND, "CHAIN SEND" },
+ { NCBDGSEND, "SEND DATAGRAM" },
+ { NCBDGRECV, "RECEIVE DATAGRAM" },
+ { NCBDGSENDBC, "SEND BROADCAST DATAGRAM" },
+ { NCBDGRECVBC, "RECEIVE BROADCAST DATAGRAM" },
+ { NCBADDNAME, "ADD NAME" },
+ { NCBDELNAME, "DELETE NAME" },
+ { NCBRESET, "RESET" },
+ { NCBASTAT, "ADAPTER STATUS" },
+ { NCBSSTAT, "SESSION STATUS" },
+ { NCBCANCEL, "CANCEL" },
+ { NCBADDGRNAME, "ADD GROUP NAME" },
+ { NCBENUM, "ENUMERATE LANA NUMBERS" },
+ { NCBUNLINK, "UNLINK" },
+ { NCBSENDNA, "SEND NO ACK" },
+ { NCBCHAINSENDNA, "CHAIN SEND NO ACK" },
+ { NCBLANSTALERT, "LAN STATUS ALERT" },
+ { NCBACTION, "ACTION" },
+ { NCBFINDNAME, "FIND NAME" },
+ { NCBTRACE, "TRACE" },
+ { 0, NULL }
+};
+
+// NetBIOS errors
+static const struct
+{
+ UCHAR error; // NetBIOS error code
+ LPCSTR message; // NetBIOS error message
+}
+NRCErrors [] =
+{
+ { NRC_GOODRET, "The operation succeeded." },
+ { NRC_BUFLEN, "An illegal buffer length was supplied." },
+ { NRC_ILLCMD, "An illegal command was supplied." },
+ { NRC_CMDTMO, "The command was timed out." },
+ { NRC_INCOMP, "The message was incomplete. The application is to issue another command." },
+ { NRC_BADDR, "The buffer address was illegal." },
+ { NRC_SNUMOUT, "The session number was out of range." },
+ { NRC_NORES, "No resource was available." },
+ { NRC_SCLOSED, "The session was closed." },
+ { NRC_CMDCAN, "The command was canceled." },
+ { NRC_DUPNAME, "A duplicate name existed in the local name table." },
+ { NRC_NAMTFUL, "The name table was full." },
+ { NRC_ACTSES, "The command finished; the name has active sessions and is no longer registered." },
+ { NRC_LOCTFUL, "The local session table was full." },
+ { NRC_REMTFUL, "The remote session table was full. The request to open a session was rejected." },
+ { NRC_ILLNN, "An illegal name number was specified." },
+ { NRC_NOCALL, "The system did not find the name that was called." },
+ { NRC_NOWILD, "Wildcards are not permitted in the ncb_name member." },
+ { NRC_INUSE, "The name was already in use on the remote adapter." },
+ { NRC_NAMERR, "The name was deleted." },
+ { NRC_SABORT, "The session ended abnormally." },
+ { NRC_NAMCONF, "A name conflict was detected." },
+ { NRC_IFBUSY, "The interface was busy." },
+ { NRC_TOOMANY, "Too many commands were outstanding; the application can retry the command later." },
+ { NRC_BRIDGE, "The ncb_lana_num member did not specify a valid network number." },
+ { NRC_CANOCCR, "The command finished while a cancel operation was occurring." },
+ { NRC_CANCEL, "The NCBCANCEL command was not valid; the command was not canceled." },
+ { NRC_DUPENV, "The name was defined by another local process." },
+ { NRC_ENVNOTDEF, "The environment was not defined." },
+ { NRC_OSRESNOTAV, "Operating system resources were exhausted. The application can retry the command later." },
+ { NRC_MAXAPPS, "The maximum number of applications was exceeded." },
+ { NRC_NOSAPS, "No service access points (SAPs) were available for NetBIOS." },
+ { NRC_NORESOURCES, "The requested resources were not available." },
+ { NRC_INVADDRESS, "The NCB address was not valid." },
+ { NRC_INVDDID, "The NCB DDID was invalid." },
+ { NRC_LOCKFAIL, "The attempt to lock the user area failed." },
+ { NRC_OPENERR, "An error occurred during an open operation being performed by the device driver." },
+ { NRC_SYSTEM, "A system error occurred." },
+ { NRC_PENDING, "An asynchronous operation is not yet finished." },
+ { 0, NULL }
+};
+
+LPCSTR GetNetbiosCommand(UCHAR command)
+{
+ command &= 0x7f; // strip ASYNCH bit
+ for ( int i = 0; NCBCommands[ i ].name; ++i )
+ if ( NCBCommands[ i ].code == command )
+ return NCBCommands[ i ].name;
+ return "UNKNOWN";
+}
+
+LPCSTR GetNetbiosError(UCHAR err)
+{
+ for ( int i = 0; NRCErrors [ i ].message; ++i )
+ if ( NRCErrors [ i ].error == err )
+ return NRCErrors [ i ].message;
+ return "Unknown error.";
+}
+
+UCHAR NetbiosEx(NCB* pNCB)
+{
+ UCHAR command = pNCB->ncb_command;
+ UCHAR ret = Netbios( pNCB );
+ //if ( ret != NRC_GOODRET )
+ if ( ret == pNCB->ncb_retcode )
+ {
+ LOG( "NetBIOS call 0x%02x \"%s\" result: 0x%02x \"%s\"",
+ command, GetNetbiosCommand( command ),
+ ret, GetNetbiosError( ret ) );
+ }
+ else
+ {
+ LOG( "NetBIOS call 0x%02x \"%s\" result: 0x%02x \"%s\", return: 0x%02x \"%s\"",
+ command, GetNetbiosCommand( command ),
+ ret, GetNetbiosError( ret ),
+ pNCB->ncb_retcode, GetNetbiosError( pNCB->ncb_retcode ) );
+ }
+ return ret;
+}
+
+// Определение NCB не на стеке
+// KB317437: "NetBIOS Listen May Return a Damaged NCB Structure"
+// The _NCB structure that is returned when a NetBIOS Listen call completes may have a changed server name offset.
+class CNCB
+{
+public:
+ CNCB() :
+ m_buf( VirtualAlloc( NULL, 4096, MEM_COMMIT, PAGE_READWRITE ) )
+ {
+ }
+
+ ~CNCB()
+ {
+ if ( m_buf )
+ {
+ VirtualFree( m_buf, 0, MEM_RELEASE );
+ }
+ }
+
+ inline operator bool() const
+ {
+ return ( m_buf != NULL );
+ }
+
+ inline operator NCB*() const
+ {
+ return (NCB*)m_buf;
+ }
+
+ inline NCB* operator->() const
+ {
+ return (NCB*)m_buf;
+ }
+
+protected:
+ void* m_buf;
+};
+
+////////////////////////////////////////////////////////////////////////
+// Class netbios
+
+netbios::netbios () :
+ m_initialized( false )
+{
+ ZeroMemory (&m_le, sizeof (m_le));
+}
+
+netbios::~netbios ()
+{
+ Destroy ();
+}
+
+bool netbios::Create (BOOL registration)
+{
+ CLock oLock( m_csData );
+
+ if ( ! m_initialized )
+ {
+ // Перечисление адаптеров
+ if ( EnumLanas( m_le ) == NRC_GOODRET )
+ {
+ // Сброс адаптеров
+ for ( UCHAR i = 0; i < m_le.length; i++ )
+ {
+ ResetLana( m_le.lana [i] );
+ }
+
+ // Регистрация имён, если нужна
+ if ( registration )
+ {
+ Register ();
+ }
+ }
+
+ m_initialized = true;
+ }
+
+ return m_initialized;
+}
+
+void netbios::AskForDestroy()
+{
+ CLock oLock( m_csData );
+
+ // Запрос на дерегистрацию имён
+ for ( size_t i = 0; i < m_names.GetCount (); ++i )
+ m_names [i]->AskForDestroy();
+}
+
+void netbios::Destroy ()
+{
+ CLock oLock( m_csData );
+
+ if ( m_initialized )
+ {
+ Deregister();
+ m_initialized = false;
+ }
+}
+
+unsigned char* netbios::SetSMBHeaderCommand (unsigned char* szHeader, BYTE iCommandCode, size_t iBufferLen)
+{
+ ZeroMemory (szHeader, iBufferLen);
+ *(DWORD*)szHeader = SMB_MAGIC;
+ szHeader [4] = iCommandCode;
+ return (szHeader + SMB_HEADER_SIZE);
+}
+
+netbios::operator bool() const
+{
+ return m_initialized;
+}
+
+bool netbios::SendNetBIOSMessage(HANDLE hContact, LPCTSTR msg, DWORD& err)
+{
+ // Created by Ilja Razinkov (also known as IPv6), 2002, IPv6Intendo@yandex.ru
+ // Keep this comment if you redistribute this file
+
+ //Схема отправки большого NB-сообщения:
+ //1) Шлется D5. Текст сообщения:
+ //- 00, длина остатка A (1 байт), 00 (3 байта)
+ //- остаток A. 04, строка КТО, 00, 04, строка КОМУ, 00
+ //в ответ приходит КОД СООБЩЕНИЯ (2 байта) и 00,00,00 (всего 5 байт), с D5 в заголовке
+ //
+ //2) Шлется D7. Текст сообщения:
+ //- КОД СООБЩЕНИЯ (2 байта), 00, длина остатка (A+B) (1 байт), 00 (всего 5 байт)
+ //- остаток A. 01, длина остатка B, 00 (3 байта)
+ //- остаток B. очередной кусок посланного СООБЩЕНИЯ
+ //в ответ приходит пустое сообщение (с 3мя 0-лями), с D7 в заголовке
+ //
+ //3) Пункт 2 повторяется пока не будет отослано все СООБЩЕНИЕ
+ //
+ //4) Шлется D6.Текст сообщения:
+ //- КОД СООБЩЕНИЯ (2 байта), 00, 00, 00 (всего 5 байт)
+ //в ответ приходит пустое сообщение (с 3мя 0-лями), с D6 в заголовке
+
+ // Получение адресата
+ CString sTo = GetNick( hContact );
+ if ( sTo.IsEmpty() )
+ {
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, NRC_NOCALL );
+ return false;
+ }
+
+ // Получение своего имени
+ CString sFrom = GetNick( NULL );
+ if ( sFrom.IsEmpty() )
+ {
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, NRC_NOCALL );
+ return false;
+ }
+
+ bool bGroup = IsGroup( hContact );
+
+ // Подготовка данных
+ CStringA sMessage( msg );
+ sMessage.AnsiToOem();
+ sMessage.Replace( "\r\n", "\x14" ); // <CR><LF> -> <14>
+ sMessage.Replace( "\r", "\x14" ); // <CR> -> <14>
+ sMessage.Replace( "\n", "\x14" ); // <LF> -> <14>
+ netbios_name nname_To( sTo, 3, bGroup );
+ netbios_name nname_From( sFrom, 3 );
+
+ // Поиск адаптера
+ UCHAR lana = 0;
+ if ( ! FindNameLana( nname_To, lana ) )
+ {
+ LOG ("SendNetBIOSMessage : Unknown name");
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, NRC_NOCALL );
+ return false;
+ }
+
+ // Коннект к клиенту
+ UCHAR lsn = 0;
+ err = Call (lana, nname_From, nname_To, lsn);
+ if (err != NRC_GOODRET)
+ {
+ LOG ("SendNetBIOSMessage : Cannot connect" );
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+
+ UCHAR SMBBlock [168] = { 0 };
+ UCHAR* szSMBData = NULL;
+ UCHAR iFromLen = (UCHAR)nname_From.GetLength ();
+ UCHAR iToLen = (UCHAR)nname_To.GetLength ();
+ UCHAR iHiMsgCode = 0, iLoMsgCode = 0;
+
+ // 1. Шлем заголовок
+ LOG ( "SendNetBIOSMessage : Send start of multi-block message" );
+ szSMBData = SetSMBHeaderCommand (SMBBlock, SMBsendstrt, sizeof (SMBBlock));
+ UCHAR dwD5ALength = (UCHAR)( 1 + iFromLen + 2 + iToLen + 1 );
+ szSMBData[1] = dwD5ALength;
+ szSMBData[3] = 4;
+ szSMBData[4+iFromLen+1] = 4;
+ CopyMemory ( szSMBData + 4, nname_From.netbiosed.name, iFromLen );
+ CopyMemory ( szSMBData + 4 + iFromLen + 2, nname_To.netbiosed.name, iToLen );
+ UCHAR dwD5Length = (UCHAR)( 3 + dwD5ALength );
+ err = Send (lana, lsn, SMBBlock, (WORD) (SMB_HEADER_SIZE + dwD5Length));
+ if (err != NRC_GOODRET)
+ {
+ LOG ( "SendNetBIOSMessage : Can`t start session" );
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+
+ // Ждем подтверждения
+ WORD length = sizeof (SMBBlock);
+ err = Recv (lana, lsn, SMBBlock, length);
+ if (err != NRC_GOODRET)
+ {
+ LOG ( "SendNetBIOSMessage : No reply (start session)" );
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+ iHiMsgCode=SMBBlock[SMB_HEADER_SIZE];
+ iLoMsgCode=SMBBlock[SMB_HEADER_SIZE+1];
+
+ // 2. Шлем сообщение (кусочками)
+ UCHAR dwD7BLength = 0;
+ for (int iSendedBytes = 0; iSendedBytes < sMessage.GetLength (); iSendedBytes += dwD7BLength)
+ {
+ dwD7BLength = sizeof (SMBBlock) - (5 + 3 + SMB_HEADER_SIZE);
+ szSMBData = SetSMBHeaderCommand (SMBBlock, SMBsendtxt, sizeof (SMBBlock));
+ if (iSendedBytes + dwD7BLength > sMessage.GetLength ())
+ dwD7BLength = (UCHAR)( sMessage.GetLength () - iSendedBytes );
+ szSMBData[0]=iHiMsgCode;
+ szSMBData[1]=iLoMsgCode;
+ szSMBData[3]=(UCHAR)( dwD7BLength + 3 );
+ szSMBData[5]=1;
+ szSMBData[6]=dwD7BLength;
+ CopyMemory (szSMBData + 5 + 3, (LPCSTR) sMessage + iSendedBytes, dwD7BLength);
+ LOG( "SendNetBIOSMessage : Send text (%u-%u bytes) of multi-block message" , iSendedBytes, iSendedBytes + dwD7BLength - 1);
+ err = Send (lana, lsn, SMBBlock, (WORD) (SMB_HEADER_SIZE + 5 + 3 + dwD7BLength));
+ if (err != NRC_GOODRET)
+ {
+ LOG ( "SendNetBIOSMessage : Can`t use session" );
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+ // Ждем подтверждения
+ length = sizeof (SMBBlock);
+ err = Recv (lana, lsn, SMBBlock, length);
+ if (err != NRC_GOODRET)
+ {
+ LOG ( "SendNetBIOSMessage : No reply (use session)" );
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+ }
+
+ // 3. Шлем извещение что все отослано
+ LOG ( "SendNetBIOSMessage : Send and of multi-block message" );
+ szSMBData = SetSMBHeaderCommand (SMBBlock, SMBsendend, sizeof (SMBBlock));
+ DWORD dwD6Length=5;
+ szSMBData[0]=iHiMsgCode;
+ szSMBData[1]=iLoMsgCode;
+ err = Send (lana, lsn, SMBBlock, (WORD) (SMB_HEADER_SIZE + dwD6Length));
+ if (err != NRC_GOODRET)
+ {
+ LOG ( "SendNetBIOSMessage : Can`t close session" );
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+
+ // Ждем подтверждения
+ length = sizeof (SMBBlock);
+ err = Recv (lana, lsn, SMBBlock, length);
+ if (err != NRC_GOODRET)
+ {
+ LOG ( "SendNetBIOSMessage : No reply (close session)" );
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 0, FACILITY_NETBIOS, err );
+ return false;
+ }
+
+ // Закрываем сессию
+ Hangup (lana, lsn);
+ err = (DWORD)MAKE_HRESULT( 1, FACILITY_NETBIOS, NRC_GOODRET );
+ return true;
+}
+
+ip netbios::FindNameIP (LPCTSTR szName, UCHAR type)
+{
+ // Поиск имени
+ netbios_name nname( szName, type );
+ ip addr = INADDR_NONE;
+ FIND_NAME_BLOCK fn = { 0 };
+ for (UCHAR i = 0; i < m_le.length; i++) {
+ UINT uReturn = FindName (nname, m_le.lana [i], fn);
+ if (uReturn == NRC_GOODRET) {
+ LOG( "Found %s at %u boxes. LAN #%u IP: %u.%u.%u.%u", nname.GetANSIFullName(),
+ fn.fnb_header.node_count, m_le.lana [i],
+ fn.fnb_Names [0].source_addr[2],
+ fn.fnb_Names [0].source_addr[3],
+ fn.fnb_Names [0].source_addr[4],
+ fn.fnb_Names [0].source_addr[5]);
+ addr = ((ip)fn.fnb_Names [0].source_addr[5]) |
+ ((ip)fn.fnb_Names [0].source_addr[4] << 8) |
+ ((ip)fn.fnb_Names [0].source_addr[3] << 16) |
+ ((ip)fn.fnb_Names [0].source_addr[2] << 24);
+ break;
+ }
+ }
+ return addr;
+}
+
+void netbios::GetRegisteredNames (netbios_name_list& names)
+{
+ CLock oLock( m_csData );
+
+ for (size_t i = 0; i < m_names.GetCount (); ++i)
+ names.AddTail (*m_names [i]);
+}
+
+bool netbios::GetNames(netbios_name_list& names, LPCTSTR name, bool bGroup)
+{
+ // Получение имен
+ ADAPTER_STATUS_BLOCK astat = { 0 };
+ netbios_name nname( name, 0, bGroup );
+ UINT uReturn = NRC_GOODRET;
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ uReturn = GetAdapterStatus (nname, m_le.lana [i], astat);
+ if (uReturn == NRC_GOODRET)
+ {
+ for (int j = 0; j < astat.asb_header.name_count; ++j)
+ {
+ names.AddTail (netbios_name (astat.asb_Names [j], m_le.lana [i]));
+ }
+ }
+ }
+ return (uReturn == NRC_GOODRET);
+}
+
+netbios_name* netbios::GetName (const netbios_name& nname)
+{
+ CLock oLock( m_csData );
+
+ netbios_name* ret = NULL;
+ for (size_t i = 0; i < m_names.GetCount (); ++i)
+ {
+ if ( nname == *(m_names [i]) )
+ {
+ // Похожее имя обнаружено
+ ret = m_names [i];
+ break;
+ }
+ }
+
+ return ret;
+}
+
+bool netbios::FindNameLana(const netbios_name& nname, UCHAR& lana)
+{
+ // Получение имен
+ ADAPTER_STATUS_BLOCK astat = {};
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ UINT uReturn = GetAdapterStatus (nname, m_le.lana [i], astat);
+ if ( uReturn == NRC_GOODRET )
+ {
+ for ( int j = 0; j < astat.asb_header.name_count; ++j )
+ {
+ if (nname == astat.asb_Names [j])
+ {
+ // Имя обнаружено
+ LOG ( "FindNameLana : Name \"%s\" found at #%d", nname.GetANSIFullName(), m_le.lana [i]);
+ lana = m_le.lana [i];
+ return true;
+ }
+ }
+ }
+ LOG( "FindNameLana : Name \"%s\" not found", nname.GetANSIFullName());
+ }
+
+ return false;
+}
+
+bool netbios::GetMAC (UCHAR lana, CString& mac)
+{
+ ADAPTER_STATUS_BLOCK astat = { 0 };
+ netbios_name nname;
+ UINT uReturn = GetAdapterStatus (nname, lana, astat);
+ if (uReturn == NRC_GOODRET) {
+ mac.Format (_T("%02x:%02x:%02x:%02x:%02x:%02x"),
+ astat.asb_header.adapter_address[0],
+ astat.asb_header.adapter_address[1],
+ astat.asb_header.adapter_address[2],
+ astat.asb_header.adapter_address[3],
+ astat.asb_header.adapter_address[4],
+ astat.asb_header.adapter_address[5]);
+ } else
+ mac.Empty ();
+ return true;
+}
+
+UCHAR netbios::FindName (const netbios_name& nname, UCHAR lana, FIND_NAME_BLOCK& fn)
+{
+ ZeroMemory (&fn, sizeof (FIND_NAME_BLOCK));
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBFINDNAME;
+ ncb->ncb_lana_num = lana;
+ CopyMemory( ncb->ncb_callname, nname.netbiosed.name, NCBNAMSZ );
+ ncb->ncb_buffer = reinterpret_cast <BYTE*> (&fn);
+ ncb->ncb_length = sizeof (FIND_NAME_BLOCK);
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::GetAdapterStatus (const netbios_name& nname, UCHAR lana, ADAPTER_STATUS_BLOCK& astat)
+{
+ ZeroMemory (&astat, sizeof (ADAPTER_STATUS_BLOCK));
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBASTAT;
+ ncb->ncb_lana_num = lana;
+ CopyMemory( ncb->ncb_callname, nname.netbiosed.name, NCBNAMSZ );
+ ncb->ncb_buffer = reinterpret_cast <BYTE*> (&astat);
+ ncb->ncb_length = sizeof (ADAPTER_STATUS_BLOCK);
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::EnumLanas (LANA_ENUM& le)
+{
+ // Перечисление адаптеров
+ ZeroMemory (&le, sizeof (LANA_ENUM));
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBENUM;
+ ncb->ncb_buffer = (PUCHAR) &le;
+ ncb->ncb_length = sizeof (LANA_ENUM);
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::ResetLana (UCHAR lana)
+{
+ // Сброс адаптера
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBRESET;
+ ncb->ncb_lana_num = lana;
+ ncb->ncb_callname [0] = 20; // maximum sessions
+ ncb->ncb_callname [2] = 30; // maximum names
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::Hangup (UCHAR lana, UCHAR lsn)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBHANGUP;
+ ncb->ncb_lana_num = lana;
+ ncb->ncb_lsn = lsn;
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::Send (UCHAR lana, UCHAR lsn, unsigned char* data, WORD length)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_lana_num = lana;
+ ncb->ncb_command = NCBSEND;
+ ncb->ncb_lsn = lsn;
+ ncb->ncb_length = length;
+ ncb->ncb_buffer = data;
+ ncb->ncb_rto = ncb->ncb_sto = (UCHAR) 10; // 10 * 500 ms = 5 s
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::Recv (UCHAR lana, UCHAR lsn, unsigned char* data, WORD& length)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBRECV;
+ ncb->ncb_lana_num = lana;
+ ncb->ncb_lsn = lsn;
+ ncb->ncb_length = length;
+ ncb->ncb_buffer = data;
+ ncb->ncb_rto = ncb->ncb_sto = (UCHAR) 10; // 10 * 500 ms = 5 s
+ NetbiosEx (ncb);
+ length = ncb->ncb_length;
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::Stat (const netbios_name& nname, SESSION_INFO_BLOCK* psibSession)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBSSTAT;
+ ncb->ncb_lana_num = nname.GetLana();
+ ncb->ncb_buffer = (unsigned char*)psibSession;
+ ncb->ncb_length = sizeof (SESSION_INFO_BLOCK);
+ CopyMemory (ncb->ncb_name, nname.netbiosed.name, NCBNAMSZ);
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::Listen (const netbios_name& nname, UCHAR& lsn)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ CopyMemory (ncb->ncb_name, nname.netbiosed.name, NCBNAMSZ);
+ CopyMemory (ncb->ncb_callname, SMB_ANY_NAME, NCBNAMSZ);
+ ncb->ncb_command = NCBLISTEN;
+ ncb->ncb_num = nname.netbiosed.name_num;
+ ncb->ncb_lana_num = nname.GetLana();
+ ncb->ncb_rto = ncb->ncb_sto = (UCHAR) 2; // 2 * 500 ms = 1 s
+ NetbiosEx (ncb);
+ lsn = ncb->ncb_lsn;
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::AddName (netbios_name& nname)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = (UCHAR)( nname.IsGroupName() ? NCBADDGRNAME : NCBADDNAME );
+ ncb->ncb_lana_num = nname.GetLana();
+ CopyMemory (ncb->ncb_name, nname.netbiosed.name, NCBNAMSZ);
+ NetbiosEx (ncb);
+ nname.netbiosed.name_num = ncb->ncb_num;
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::DeleteName (const netbios_name& nname)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ ncb->ncb_command = NCBDELNAME;
+ ncb->ncb_lana_num = nname.GetLana();
+ ncb->ncb_num = nname.netbiosed.name_num;
+ CopyMemory (ncb->ncb_name, nname.netbiosed.name, NCBNAMSZ);
+ NetbiosEx (ncb);
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::SendDatagram (const netbios_name& nname_from, const netbios_name& nname_to, unsigned char* data, WORD length)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ CopyMemory( ncb->ncb_name, nname_from.netbiosed.name, NCBNAMSZ );
+ CopyMemory( ncb->ncb_callname, nname_to.netbiosed.name, NCBNAMSZ );
+ ncb->ncb_command = NCBDGSEND;
+ ncb->ncb_num = nname_from.netbiosed.name_num;
+ ncb->ncb_lana_num = nname_from.GetLana();
+ ncb->ncb_buffer = data;
+ ncb->ncb_length = length;
+
+ CLock oLock( m_csNetBIOS );
+ Sleep( 100 );
+
+ NetbiosEx (ncb);
+
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::RecvDatagram (netbios_name& nname_from, const netbios_name& nname_to, unsigned char* data, WORD& length)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ CopyMemory (ncb->ncb_name, nname_to.netbiosed.name, NCBNAMSZ);
+ ncb->ncb_command = NCBDGRECV;
+ ncb->ncb_num = nname_to.netbiosed.name_num;
+ ncb->ncb_lana_num = nname_to.GetLana();
+ ncb->ncb_buffer = data;
+ ncb->ncb_length = length;
+ NetbiosEx (ncb);
+ nname_from = ncb->ncb_callname;
+ length = ncb->ncb_length;
+ return ncb->ncb_retcode;
+}
+
+UCHAR netbios::Call (UCHAR lana, const netbios_name& nname_from, const netbios_name& nname_to, UCHAR& lsn)
+{
+ CNCB ncb;
+ if ( ! ncb ) return NRC_SYSTEM;
+ CopyMemory( ncb->ncb_name, nname_from.netbiosed.name, NCBNAMSZ );
+ CopyMemory( ncb->ncb_callname, nname_to.netbiosed.name, NCBNAMSZ );
+ ncb->ncb_lana_num = lana;
+ ncb->ncb_rto = ncb->ncb_sto = 10; // 5 секунд
+ ncb->ncb_command = NCBCALL;
+ NetbiosEx (ncb);
+ lsn = ncb->ncb_lsn;
+ return ncb->ncb_retcode;
+}
+
+bool netbios::AskAway(const netbios_name& nname_to)
+{
+ bool ret = false;
+ if ( m_initialized )
+ {
+ // Сборка пакета
+ const WORD packet_size = sizeof( WORD ) + 1;
+ if ( UCHAR* packet = (UCHAR*)mir_alloc( packet_size ) )
+ {
+ *(WORD*)packet = SM_MAGIC;
+ packet[ 2 ] = SM_GETAWAYMESSAGE;
+
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ if ( netbios_name* nname = GetName( netbios_name(
+ pluginMachineName, 0x03, false, m_le.lana [i] ) ) )
+ {
+ LOG( "Send \"Ask Away\" request to \"%s\"", nname_to.GetANSIFullName() );
+
+ if ( SendDatagram( *nname, nname_to, packet, packet_size ) == NRC_GOODRET )
+ {
+ ret = true;
+ }
+ }
+ }
+
+ mir_free( packet );
+ }
+ }
+ return ret;
+}
+
+bool netbios::SendAway(netbios_name& nname_from, const netbios_name& nname_to)
+{
+ bool ret = false;
+ if ( m_initialized )
+ {
+ CString sAwayT;
+ pluginStatusMessage.Lookup( pluginCurrentStatus, sAwayT );
+ CT2A sAwayA( sAwayT );
+ WORD len = (WORD)min( lstrlenA( sAwayA ), 250 );
+
+ // Сборка пакета
+ WORD packet_size = (WORD)( 2 + 1 + 4 + len );
+ if ( UCHAR* packet = (UCHAR*)mir_alloc( packet_size ) )
+ {
+ *(WORD*)packet = SM_MAGIC;
+ packet [ 2 ] = SM_SENDAWAYMESSAGE;
+ *(__int32*)( packet + 2 + 1 ) = 0;
+ CopyMemory( packet + 2 + 1 + 4, sAwayA, len );
+
+ LOG( "Send \"Away\" answer from \"%s\" to \"%s\" : \"%s\"", nname_from.GetANSIFullName(), nname_to.GetANSIFullName(), (LPCSTR)sAwayA );
+
+ ret = ( SendDatagram( nname_from, nname_to, packet, packet_size ) == NRC_GOODRET );
+
+ mir_free( packet );
+ }
+ }
+ return ret;
+}
+
+bool netbios::AskStatus(const netbios_name& nname_to)
+{
+ bool ret = false;
+ if ( m_initialized )
+ {
+ // Сборка пакета
+ const WORD packet_size = 2 + 1;
+ if ( UCHAR* packet = (UCHAR*)mir_alloc( packet_size ) )
+ {
+ *(WORD*)packet = SM_MAGIC;
+ packet[ 2 ] = SM_GETSTATUS;
+
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ if ( netbios_name* nname = GetName( netbios_name(
+ pluginMachineName, 0x03, false, m_le.lana [i] ) ) )
+ {
+ LOG( "Send \"Ask Status\" request to \"%s\"", nname_to.GetANSIFullName() );
+
+ if ( SendDatagram( *nname, nname_to, packet, packet_size ) == NRC_GOODRET )
+ {
+ ret = true;
+ }
+ }
+ }
+
+ mir_free( packet );
+ }
+ }
+ return ret;
+}
+
+bool netbios::SendStatus(netbios_name& nname_from, const netbios_name& nname_to)
+{
+ bool ret = false;
+ if ( m_initialized )
+ {
+ // Сборка пакета
+ const WORD packet_size = 2 + 1 + 4;
+ if ( UCHAR* packet = (UCHAR*)mir_alloc( packet_size ) )
+ {
+ *(WORD*)packet = SM_MAGIC;
+ packet [ 2 ] = SM_SENDSTATUS;
+ *(__int32*)( packet + 2 + 1 ) = (__int32)pluginCurrentStatus;
+
+ LOG( "Send \"Status\" answer from \"%s\" to \"%s\" : \"%s\"", nname_from.GetANSIFullName(), nname_to.GetANSIFullName(), STATUS2TEXT(pluginCurrentStatus) );
+
+ ret = ( SendDatagram( nname_from, nname_to, packet, packet_size ) == NRC_GOODRET );
+
+ mir_free( packet );
+ }
+ }
+ return ret;
+}
+
+bool netbios::BroadcastStatus()
+{
+ bool ret = false;
+ if ( m_initialized )
+ {
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ netbios_name nname_to( MNS_STATUS, 0xab, true, m_le.lana [i] );
+ netbios_name* nname = GetName(
+ netbios_name ( pluginMachineName, 0x03, false, m_le.lana [i] ) );
+ if ( nname )
+ ret = SendStatus( *nname, nname_to ) || ret;
+ }
+ }
+ return ret;
+}
+
+bool netbios::AskAvatar(const netbios_name& nname_to)
+{
+ bool ret = false;
+ if ( m_initialized )
+ {
+ // Сборка пакета
+ const WORD packet_size = 2 + 1;
+ if ( UCHAR* packet = (UCHAR*)mir_alloc( packet_size ) )
+ {
+ *(WORD*)packet = SM_MAGIC;
+ packet[ 2 ] = SM_GETAVATAR;
+
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ if ( netbios_name* nname = GetName( netbios_name(
+ pluginMachineName, 0x03, false, m_le.lana [i] ) ) )
+ {
+ LOG( "Send \"Ask Avatar\" request to \"%s\"", nname_to.GetANSIFullName() );
+
+ if ( SendDatagram( *nname, nname_to, packet, packet_size ) == NRC_GOODRET )
+ {
+ ret = true;
+ }
+ }
+ }
+
+ mir_free( packet );
+ }
+ }
+ return ret;
+}
+
+bool netbios::SendAvatar(netbios_name& nname_from, const netbios_name& nname_to)
+{
+ if ( ! m_initialized )
+ return false;
+
+ if ( ! ServiceExists( MS_AV_GETMYAVATAR ) )
+ // Нет аватарского плагина
+ return false;
+
+ // Запрос аватара протокола
+ AVATARCACHEENTRY* pAvatar = (AVATARCACHEENTRY*)CallService(
+ MS_AV_GETMYAVATAR, 0, (LPARAM)modname );
+ if ( ! pAvatar )
+ // Запрос общего аватара
+ pAvatar = (AVATARCACHEENTRY*)CallService( MS_AV_GETMYAVATAR, 0, (LPARAM)"" );
+ if ( ! pAvatar || pAvatar->cbSize < sizeof( AVATARCACHEENTRY ) )
+ // Нет аватара
+ return false;
+
+ CString sFilename = pAvatar->szFilename;
+
+ CAtlFile oAvatarFile;
+ if ( FAILED( oAvatarFile.Create( sFilename, GENERIC_READ,
+ FILE_SHARE_READ, OPEN_EXISTING ) ) )
+ // Файл не найден
+ return false;
+
+ ULONGLONG avatar_size = 0;
+ if ( FAILED( oAvatarFile.GetSize( avatar_size ) ) ||
+ avatar_size < 16 || avatar_size > MAX_AVATAR_SIZE )
+ // Слишком маленький или слишком большой файл
+ return false;
+
+ bool ret = false;
+
+ // Сборка статусного пакета
+ WORD packet_size = (WORD)( 2 + 1 + avatar_size );
+ if ( UCHAR* packet = (UCHAR*)mir_alloc( packet_size ) )
+ {
+ *(WORD*)packet = SM_MAGIC;
+ packet[ 2 ] = SM_SENDAVATAR;
+
+ // Чтение аватара из файла
+ if ( SUCCEEDED( oAvatarFile.Read( packet + 2 + 1, avatar_size ) ) )
+ {
+ LOG( "Send \"Avatar\" answer from \"%s\" to \"%s\"", nname_from.GetANSIFullName(), nname_to.GetANSIFullName() );
+
+ ret = ( SendDatagram( nname_from, nname_to, packet, packet_size ) == NRC_GOODRET );
+ }
+
+ mir_free( packet );
+ }
+
+ return ret;
+}
+
+bool netbios::Register ()
+{
+ CLock oLock( m_csData );
+
+ bool ret = false;
+
+ // Удаление имён, если они есть
+ for (size_t i = 0; i < m_names.GetCount (); ++i)
+ delete m_names [i];
+ m_names.RemoveAll ();
+
+ // Добавление имен на каждом адаптере
+ for (UCHAR i = 0; i < m_le.length; i++)
+ {
+ // COMPUTER <01> U
+ netbios_name *pnn1 =
+ DBGetContactSettingByte (NULL, modname, "RegisterNick", TRUE) ?
+ new netbios_name ( pluginMachineName, 0x01, false, m_le.lana [i]) : NULL;
+ if (pnn1)
+ m_names.Add (pnn1);
+
+ // COMPUTER <03> U
+ netbios_name *pnn2 =
+ DBGetContactSettingByte (NULL, modname, "RegisterNick", TRUE) ?
+ new netbios_name ( pluginMachineName, 0x03, false, m_le.lana [i]) : NULL;
+ if (pnn2)
+ m_names.Add (pnn2);
+
+ // USER <03> U
+ netbios_name *pnn3 =
+ DBGetContactSettingByte (NULL, modname, "RegisterUser", TRUE) ?
+ new netbios_name ( pluginUserName, 0x03, false, m_le.lana [i]) : NULL;
+ if (pnn3) {
+ // Проверка на совпадение имени пользователя и имени компьютера
+ if (pnn2 && *pnn3 == *pnn2)
+ // Имена совпадают
+ delete pnn3;
+ else
+ m_names.Add (pnn3);
+ }
+
+ // MNS_STATUS <AB> G
+ netbios_name *pnn4 =
+ DBGetContactSettingByte (NULL, modname, "RegisterStatus", TRUE) ?
+ new netbios_name (MNS_STATUS, 0xab, true, m_le.lana [i]) : NULL;
+ if ( pnn4 )
+ m_names.Add( pnn4 );
+ }
+
+ // Регистрация имен
+ for ( size_t i = 0; i < m_names.GetCount (); ++i )
+ {
+ if ( m_names [i]->Register() )
+ {
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+// Дерегистрация NetBIOS-имен
+void netbios::Deregister ()
+{
+ CLock oLock( m_csData );
+
+ // Дерегистрация имён
+ for (size_t i = 0; i < m_names.GetCount (); ++i)
+ m_names [i]->Destroy();
+
+ // Удаление имён
+ for (size_t i = 0; i < m_names.GetCount (); ++i)
+ delete m_names [i];
+ m_names.RemoveAll ();
+}
diff --git a/protocols/WinPopup/src/netbios.h b/protocols/WinPopup/src/netbios.h
new file mode 100644
index 0000000000..9b5634c42a
--- /dev/null
+++ b/protocols/WinPopup/src/netbios.h
@@ -0,0 +1,130 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+const DWORD SMB_MAGIC = 0x424d53ff; // 0xff 'S' 'M' 'B'
+const WORD SM_MAGIC = 0x4d53; // 'S' 'M'
+
+// NetBIOS datagrams:
+// (obsolete)
+const UCHAR SM_CHANGESTATUS = 0x01;
+// Status request: 'S' 'M' 0x02
+const UCHAR SM_GETSTATUS = 0x02;
+// Status answer: 'S' 'M' 0x03 status(i32)
+const UCHAR SM_SENDSTATUS = 0x03;
+// Away message request: 'S' 'M' 0x04
+const UCHAR SM_GETAWAYMESSAGE = 0x04;
+// Away message answer: 'S' 'M' 0x05 0x00(i32) message(ASCII)
+const UCHAR SM_SENDAWAYMESSAGE = 0x05;
+// Avatar request: 'S' 'M' 0x06
+const UCHAR SM_GETAVATAR = 0x06;
+// Avatar answer: 'S' 'M' 0x07 avatar
+const UCHAR SM_SENDAVATAR = 0x07;
+
+const LPCTSTR MNS_STATUS = _T("MNS_STATUS");
+
+#define MAX_AVATAR_SIZE 65000 // Максимальный размер файла аватара (байт)
+
+#pragma pack (push, 1)
+
+typedef struct _FIND_NAME_BLOCK
+{
+ FIND_NAME_HEADER fnb_header;
+ FIND_NAME_BUFFER fnb_Names [256];
+} FIND_NAME_BLOCK, *PFIND_NAME_BLOCK;
+
+typedef struct _ADAPTER_STATUS_BLOCK
+{
+ ADAPTER_STATUS asb_header;
+ NAME_BUFFER asb_Names [NCBNAMSZ];
+} ADAPTER_STATUS_BLOCK, *PADAPTER_STATUS_BLOCK;
+
+#pragma pack (pop)
+
+class netbios
+{
+public:
+ netbios();
+ ~netbios();
+
+ bool Create (BOOL registration);
+ void AskForDestroy(); // Для ускорения
+ void Destroy ();
+ operator bool() const;
+
+ bool SendNetBIOSMessage (HANDLE hContact, LPCTSTR msg /* ANSI */, DWORD& err);
+ ip FindNameIP (LPCTSTR szName /* ANSI */, UCHAR type = 3);
+ void GetRegisteredNames (netbios_name_list& names);
+ bool GetNames (netbios_name_list& names, LPCTSTR name /* ANSI */, bool bGroup);
+
+ // Получение имени из списка по указанному имени
+ netbios_name* GetName (const netbios_name& nname);
+
+ // Поиск номера адаптера через который можно достучаться до указанного имени
+ bool FindNameLana (const netbios_name& nname, UCHAR& lana);
+
+ bool GetMAC (UCHAR lana, CString& mac);
+ UCHAR FindName (const netbios_name& nname, UCHAR lana, FIND_NAME_BLOCK& fn);
+ UCHAR GetAdapterStatus (const netbios_name& nname, UCHAR lana, ADAPTER_STATUS_BLOCK& astat);
+ UCHAR EnumLanas (LANA_ENUM& le);
+ UCHAR ResetLana (UCHAR lana);
+ UCHAR Hangup (UCHAR lana, UCHAR lsn);
+ UCHAR Send (UCHAR lana, UCHAR lsn, unsigned char* data, WORD length);
+ UCHAR Recv (UCHAR lana, UCHAR lsn, unsigned char* data, WORD& length);
+ UCHAR Stat (const netbios_name& nname, SESSION_INFO_BLOCK* psibSession);
+ UCHAR Listen (const netbios_name& nname, UCHAR& lsn);
+ UCHAR AddName (netbios_name& nname);
+ UCHAR DeleteName (const netbios_name& nname);
+ UCHAR SendDatagram (const netbios_name& nname_from, const netbios_name& nname_to, unsigned char* data, WORD length);
+ UCHAR RecvDatagram (netbios_name& nname_from, const netbios_name& nname_to, unsigned char* data, WORD& length);
+ UCHAR Call (UCHAR lana, const netbios_name& nname_from, const netbios_name& nname_to, UCHAR& lsn);
+ unsigned char* SetSMBHeaderCommand (unsigned char* szHeader, BYTE iCommandCode, size_t iBufferLen);
+
+ // Запрос эвей-сообщения
+ bool AskAway(const netbios_name& nname_to);
+ // Отправка эвей-сообщения
+ bool SendAway(netbios_name& nname_from, const netbios_name& nname_to);
+ // Запрос статуса
+ bool AskStatus(const netbios_name& nname_to);
+ // Отправка статуса
+ bool SendStatus(netbios_name& nname_from, const netbios_name& nname_to);
+ // Отправка статуса на всех адаптерах (от COMPUTER<03> U на MNS_STATUS<ab> G)
+ bool BroadcastStatus();
+ // Запрос аватара
+ bool AskAvatar(const netbios_name& nname_to);
+ // Отправка аватара
+ bool SendAvatar(netbios_name& nname_from, const netbios_name& nname_to);
+
+protected:
+ bool m_initialized; // Флаг инициализации подсистемы NetBIOS
+ netbios_name_array m_names; // Все регистрируемые NetBIOS-имена для слушателя
+ LANA_ENUM m_le; // Список адаптеров
+ CComAutoCriticalSection m_csData; // Защита данных
+ CComAutoCriticalSection m_csNetBIOS; // Синхронизация NetBIOS
+
+ bool Register (); // Регистрация всех NetBIOS-имен
+ void Deregister (); // Дерегистрация всех NetBIOS-имен
+};
+
+extern netbios pluginNetBIOS; // Прием/отправка сообщений через NetBIOS
+
+LPCSTR GetNetbiosCommand(UCHAR command);
+LPCSTR GetNetbiosError(UCHAR err);
+UCHAR NetbiosEx(NCB* pNCB);
diff --git a/protocols/WinPopup/src/netbios_name.cpp b/protocols/WinPopup/src/netbios_name.cpp
new file mode 100644
index 0000000000..befffb04e5
--- /dev/null
+++ b/protocols/WinPopup/src/netbios_name.cpp
@@ -0,0 +1,630 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 netbios_name
+
+netbios_name::netbios_name(LPCTSTR n, UCHAR type, bool group, UCHAR lana) :
+ m_managed (false),
+ m_registered (false),
+ m_duplicated (false),
+ m_error (false),
+ m_lana (lana),
+ m_listener (NULL),
+ m_dgreceiver (NULL),
+ m_term (NULL)
+{
+ // Преобразование имени в NetBIOS-имя
+ int len = NCBNAMSZ - 1;
+ CT2A nA( n );
+ LPCSTR src = (LPCSTR)nA;
+ LPSTR dst = (LPSTR)netbiosed.name;
+ for ( ; len && *src; --len, ++dst, ++src )
+ *dst = *src;
+ for ( ; len; --len )
+ *dst++ = ' ';
+ *dst = (CHAR)type;
+ CharUpperBuffA( (LPSTR)netbiosed.name, NCBNAMSZ - 1 );
+ CharToOemBuffA( (LPSTR)netbiosed.name, (LPSTR)netbiosed.name, NCBNAMSZ - 1 );
+ netbiosed.name_num = 0;
+ netbiosed.name_flags = (UCHAR)( group ? GROUP_NAME : UNIQUE_NAME );
+
+ original = GetANSIName();
+}
+
+netbios_name::netbios_name(const NAME_BUFFER& n, UCHAR lana) :
+ m_managed (false),
+ m_registered (false),
+ m_duplicated (false),
+ m_error (false),
+ m_lana (lana),
+ m_listener (NULL),
+ m_dgreceiver (NULL),
+ m_term (NULL)
+{
+ CopyMemory (&netbiosed, &n, sizeof (NAME_BUFFER));
+ original = GetANSIName();
+}
+
+netbios_name& netbios_name::operator=(const netbios_name& n)
+{
+ _ASSERTE (m_managed == false);
+ m_managed = n.m_managed;
+ _ASSERTE (m_registered == false);
+ m_registered = n.m_registered;
+ _ASSERTE (m_duplicated == false);
+ m_duplicated = n.m_duplicated;
+ _ASSERTE (m_error == false);
+ m_error = n.m_error;
+ m_lana = n.m_lana;
+ _ASSERTE( m_listener == NULL );
+ m_listener = NULL;
+ _ASSERTE( m_dgreceiver == NULL );
+ m_dgreceiver = NULL;
+ _ASSERTE( m_term == NULL );
+ m_term = NULL;
+ CopyMemory (&netbiosed, &n.netbiosed, sizeof( NAME_BUFFER ));
+ original = n.original;
+ return *this;
+}
+
+netbios_name& netbios_name::operator= (const UCHAR* n)
+{
+ _ASSERTE (m_managed == false);
+ m_managed = false;
+ _ASSERTE (m_registered == false);
+ m_registered = false;
+ _ASSERTE (m_duplicated == false);
+ m_duplicated = false;
+ _ASSERTE (m_error == false);
+ m_error = false;
+ m_lana = 0;
+ _ASSERTE( m_listener == NULL );
+ m_listener = NULL;
+ _ASSERTE( m_dgreceiver == NULL );
+ m_dgreceiver = NULL;
+ _ASSERTE( m_term == NULL );
+ m_term = NULL;
+ CopyMemory (netbiosed.name, n, NCBNAMSZ);
+ netbiosed.name_num = 0;
+ netbiosed.name_flags = UNIQUE_NAME;
+ original = GetANSIName();
+
+ return *this;
+}
+
+bool netbios_name::operator== (const NAME_BUFFER& n) const
+{
+ return ( netbiosed.name [NCBNAMSZ - 1] == n.name [NCBNAMSZ - 1] ) &&
+ ( ( netbiosed.name_flags & GROUP_NAME ) == ( n.name_flags & GROUP_NAME ) ) &&
+ ( memcmp( netbiosed.name, n.name, NCBNAMSZ - 1 ) == 0 );
+}
+
+bool netbios_name::operator!= (const NAME_BUFFER& n) const
+{
+ return ! operator==( n );
+}
+
+bool netbios_name::operator== (const netbios_name& n) const
+{
+ return ( m_lana == n.m_lana ) && operator==( n.netbiosed );
+}
+
+bool netbios_name::operator!= (const netbios_name& n) const
+{
+ return ( m_lana != n.m_lana ) || operator!=( n.netbiosed );
+}
+
+bool netbios_name::Register()
+{
+ m_managed = true;
+
+ UCHAR ret = AddName ();
+ LOG("Register NetBIOS name \"%s\" on lana %d num=%d : 0x%02x \"%s\"", GetANSIFullName(), m_lana, netbiosed.name_num, ret, GetNetbiosError( ret ) );
+ m_registered = (ret == NRC_GOODRET);
+ m_duplicated = (ret == NRC_DUPNAME);
+ if ( ret != NRC_GOODRET && ret != NRC_DUPNAME )
+ {
+ WarningBox (NULL, (DWORD)MAKE_HRESULT (0, FACILITY_NETBIOS, ret),
+ _T("%s: %s"), TranslateT ("Cannot register NetBIOS name"), (LPCTSTR)CA2T( GetANSIFullName() ) );
+ }
+
+ if (!m_term)
+ m_term = CreateEvent (NULL, TRUE, FALSE, NULL);
+ else
+ ResetEvent (m_term);
+
+ if ( m_term && !m_listener )
+ m_listener = (HANDLE)mir_forkthread( ListenerThread, this );
+
+ if ( m_term && !m_dgreceiver &&
+ // NOTE: Под Win9x нельзя запускать ожидание датаграмм для имён-дубликатов
+ // т.к. потом невозможно выбить управление из функции Netbios() даже если
+ // разрегистрировать имя
+ !m_duplicated )
+ {
+ m_dgreceiver = (HANDLE)mir_forkthread( DatagramReceiverThread, this );
+ }
+
+ return m_registered;
+}
+
+void netbios_name::AskForDestroy()
+{
+ if (m_term)
+ SetEvent (m_term);
+}
+
+void netbios_name::Destroy()
+{
+ // Запрос останова рабочего потока-слушателя
+ if ( m_term ) SetEvent( m_term );
+
+ // Удаление имени (если не удалить, то слушатель не завершиться)
+ UCHAR ret = DeleteName ();
+ LOG("Unregister NetBIOS name \"%s\" on lana %d num=%d : 0x%02x \"%s\"", GetANSIFullName(), m_lana, netbiosed.name_num, ret, GetNetbiosError( ret ) );
+ m_registered = !(ret == NRC_GOODRET);
+ if ( m_duplicated )
+ {
+ // Восстановление имени
+ m_duplicated = false;
+
+ // NOTE: Восстанавливать не надо - проблем больше
+ // uReturn = AddName ();
+ // LOG("Restore NetBIOS name \"%s\" on lana %d : 0x%02x", GetANSIFullName(), m_lana, uReturn);
+ }
+
+ // Ожидание, а затем принудительный останов
+ if ( m_listener )
+ {
+ if ( m_term ) SetEvent( m_term );
+ if (WaitForSingleObject (m_listener, ALMOST_INFINITE) == WAIT_TIMEOUT)
+ {
+ LOG("Terminate NetBIOS listener!");
+ TerminateThread (m_listener, 0);
+ }
+ m_listener = NULL;
+ }
+
+ if ( m_dgreceiver )
+ {
+ if ( m_term ) SetEvent( m_term );
+ if (WaitForSingleObject (m_dgreceiver, ALMOST_INFINITE) == WAIT_TIMEOUT)
+ {
+ LOG("Terminate NetBIOS datagram receiver!");
+ TerminateThread (m_dgreceiver, 0);
+ }
+ m_dgreceiver = NULL;
+ }
+
+ if ( m_term )
+ {
+ CloseHandle (m_term);
+ m_term = NULL;
+ }
+}
+
+CStringA netbios_name::GetANSIName() const
+{
+ CStringA sName;
+ LPSTR szName = sName.GetBuffer( NCBNAMSZ );
+ CopyMemory( szName, (LPCSTR)netbiosed.name, NCBNAMSZ - 1 );
+ szName[ NCBNAMSZ - 1 ] = 0;
+ sName.ReleaseBuffer();
+ sName.Trim();
+ sName.OemToAnsi();
+ return sName;
+}
+
+CStringA netbios_name::GetANSIFullName() const
+{
+ CStringA sType;
+ sType.Format( " <%02X>", GetType() );
+ return original + sType;
+}
+
+UCHAR netbios_name::GetType () const
+{
+ return netbiosed.name [NCBNAMSZ - 1];
+}
+
+bool netbios_name::IsGroupName () const
+{
+ return ((netbiosed.name_flags & GROUP_NAME) == GROUP_NAME);
+}
+
+bool netbios_name::IsRegistered () const
+{
+ return m_registered;
+}
+
+bool netbios_name::IsDuplicated () const
+{
+ return m_duplicated;
+}
+
+bool netbios_name::IsError () const
+{
+ return m_error;
+}
+
+bool netbios_name::IsOwnName () const
+{
+ return m_managed;
+}
+
+UCHAR netbios_name::GetLana () const
+{
+ return m_lana;
+}
+
+bool netbios_name::GetRealSender (UCHAR lsn, CStringA& sRealFrom) const
+{
+ sRealFrom.Empty ();
+
+ SESSION_INFO_BLOCK sibSession = {};
+ UCHAR dwInfoRes = pluginNetBIOS.Stat( *this, &sibSession );
+ if ( dwInfoRes == NRC_GOODRET )
+ {
+ for ( int i = 0; i < sibSession.sib_header.num_sess; i++ )
+ {
+ if ( sibSession.sib_Names [i].lsn == lsn )
+ {
+ // Наша сессия
+ const char* n = (const char*)sibSession.sib_Names [i].remote_name;
+ BYTE j = NCBNAMSZ - 2;
+ for ( ; j && ( n [ j ] == ' ' ); --j );
+ sRealFrom.Append( n, j + 1 );
+ sRealFrom.OemToAnsi();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+size_t netbios_name::GetLength() const
+{
+ return (size_t)original.GetLength();
+}
+
+UCHAR netbios_name::AddName()
+{
+ return pluginNetBIOS.AddName( *this );
+}
+
+UCHAR netbios_name::DeleteName()
+{
+ return pluginNetBIOS.DeleteName( *this );
+}
+
+typedef struct _ReceiverData
+{
+ netbios_name* self;
+ UCHAR lsn;
+} ReceiverData;
+
+void netbios_name::Listener()
+{
+ m_error = false;
+ while ( WaitForSingleObject( m_term, 50 ) == WAIT_TIMEOUT )
+ {
+ UCHAR lsn = 0;
+ UCHAR ret = pluginNetBIOS.Listen( *this, lsn );
+ if ( ret != NRC_GOODRET )
+ {
+ LOG( "Listener : Closing \"%s\"", GetANSIFullName() );
+ m_error = true;
+ break;
+ }
+
+ LOG( "Listener : Got packet for \"%s\"", GetANSIFullName() );
+ if ( ReceiverData* data = (ReceiverData*)mir_alloc( sizeof( ReceiverData ) ) )
+ {
+ data->self = this;
+ data->lsn = lsn;
+ mir_forkthread( ReceiverThread, data );
+ }
+ }
+}
+
+void netbios_name::ListenerThread(LPVOID param)
+{
+ if ( netbios_name* pName = (netbios_name*)param )
+ {
+ pName->Listener();
+ pName->m_listener = NULL;
+ }
+}
+
+void netbios_name::DatagramReceiver()
+{
+ UCHAR* SMBBlock = (UCHAR*)mir_alloc( 65536 );
+ if ( ! SMBBlock )
+ {
+ m_error = true;
+ return;
+ }
+
+ m_error = false;
+ while ( WaitForSingleObject ( m_term, 50 ) == WAIT_TIMEOUT )
+ {
+ ZeroMemory( SMBBlock, 65536 );
+
+ WORD iReadedBytes = 65535;
+ netbios_name nname_from;
+ UCHAR ret = pluginNetBIOS.RecvDatagram( nname_from, *this, SMBBlock, iReadedBytes );
+ if ( ret != NRC_GOODRET )
+ {
+ // Ошибка - выход
+ m_error = true;
+ break;
+ }
+
+ nname_from.m_lana = m_lana;
+
+ LOG( "Got datagram from \"%s\" to \"%s\"", nname_from.GetANSIFullName(), GetANSIFullName() );
+
+ // Свое собственное сообщение?
+ if ( IsItMe ( CA2T( nname_from.original ) ) )
+ {
+ LOG( "DatagramReceiver : Ignoring my datagram" );
+ continue;
+ }
+
+ // Обработка датаграмм
+ if ( iReadedBytes > 2 && *(WORD*)SMBBlock == SM_MAGIC )
+ {
+ UCHAR iMsgType = SMBBlock[ 2 ];
+ switch ( iMsgType )
+ {
+ case SM_GETSTATUS:
+ // Отвечаем своим статусом
+ LOG( "DatagramReceiver : It's status request" );
+ pluginNetBIOS.SendStatus( *this, nname_from );
+ break;
+
+ case SM_SENDSTATUS:
+ // Находим контакт и ставим его статус
+ if ( iReadedBytes == 2 + 1 + 4 )
+ {
+ HANDLE hContact = GetContact( CA2T( nname_from.original ) );
+ if ( hContact )
+ {
+ LOG( "DatagramReceiver : It's status answer" );
+ SetContactStatus( hContact, *(__int32*)(SMBBlock + 2 + 1), false );
+ }
+ else
+ LOG( "DatagramReceiver : Unknown contact" );
+ }
+ else
+ LOG( "DatagramReceiver : Invalid format" );
+ break;
+
+ case SM_GETAWAYMESSAGE:
+ // Отвечаем своим эвей-сообщением
+ LOG( "DatagramReceiver : It's away request" );
+ pluginNetBIOS.SendAway( *this, nname_from );
+ break;
+
+ case SM_SENDAWAYMESSAGE:
+ // Находим контакт и ставим его эвей-сообщение
+ if ( iReadedBytes >= 2 + 1 + 4 )
+ {
+ if ( HANDLE hContact = GetContact( CA2T( nname_from.original ) ) )
+ {
+ LPCSTR szAway = (LPCSTR)( SMBBlock + 2 + 1 + 4 );
+ SMBBlock[ iReadedBytes ] = 0; // ASCII -> ASCIIZ
+
+ LOG( "DatagramReceiver : It's away answer \"%s\"", szAway );
+ SetContactAway( hContact, szAway );
+ }
+ else
+ LOG( "DatagramReceiver : Unknown contact" );
+ }
+ else
+ LOG( "DatagramReceiver : Invalid format" );
+ break;
+
+ case SM_GETAVATAR:
+ // Отвечаем своим аватаром
+ LOG( "DatagramReceiver : It's avatar request." );
+ pluginNetBIOS.SendAvatar( *this, nname_from );
+ break;
+
+ case SM_SENDAVATAR:
+ // Находим контакт и ставим его аватар
+ if ( iReadedBytes >= 2 + 1 && iReadedBytes < MAX_AVATAR_SIZE + 3 )
+ {
+ if ( HANDLE hContact = GetContact( CA2T( nname_from.original ) ) )
+ {
+ LOG( "DatagramReceiver : It's avatar answer" );
+ SetContactAvatar( hContact, SMBBlock + 2 + 1, (DWORD)iReadedBytes - 3 );
+ }
+ else
+ LOG( "DatagramReceiver : Unknown contact" );
+ }
+ else
+ LOG( "DatagramReceiver : Invalid format or too big avatar" );
+ break;
+
+ default:
+ LOG( "DatagramReceiver : Unsupported message type 0x%02x", iMsgType );
+ }
+ }
+ else
+ LOG( "DatagramReceiver : Unsupported data 0x%04x", *(WORD*)SMBBlock );
+ }
+
+ mir_free( SMBBlock );
+}
+
+void netbios_name::DatagramReceiverThread(LPVOID param)
+{
+ if ( netbios_name* pName = (netbios_name*)param )
+ {
+ pName->DatagramReceiver();
+ pName->m_dgreceiver = NULL;
+ }
+}
+
+void netbios_name::Receiver(UCHAR lsn)
+{
+ // Created by Ilja Razinkov (also known as IPv6), 2002, IPv6Intendo@yandex.ru
+ // Keep this comment if you redistribute this file
+
+ UCHAR* SMBBlock = (UCHAR*)mir_alloc( 65536 );
+ if ( ! SMBBlock )
+ return;
+
+ CStringA sTo, sFrom, sMessage;
+ UCHAR nRes;
+ for (;;)
+ {
+ ZeroMemory( SMBBlock, 65536 );
+
+ // Получение очередного блока данных
+ WORD iReadedBytes = 65535;
+ nRes = pluginNetBIOS.Recv (m_lana, lsn, SMBBlock, iReadedBytes);
+ if (nRes != NRC_GOODRET)
+ {
+ LOG( "Receiver : Error while receiving data block" );
+ break;
+ }
+
+ // Смотрим что к нам пришло - сообщение или что-то неизвестное
+ if ( iReadedBytes < 4 || *(DWORD*)SMBBlock != SMB_MAGIC )
+ {
+ LOG( "Receiver : Unsupported data 0x%08x", *(DWORD*)SMBBlock );
+ break;
+ }
+
+ UCHAR iMsgType = SMBBlock [4];
+ if (iMsgType != SMBsends &&
+ iMsgType != SMBsendstrt &&
+ iMsgType != SMBsendend &&
+ iMsgType != SMBsendtxt)
+ {
+ LOG( "Receiver : Unsupported message type 0x%02x", iMsgType );
+ break;
+ }
+
+ // Шлём подтверждение
+ UCHAR szReply [SMB_HEADER_SIZE + 5];
+ UCHAR* szReplyData =
+ pluginNetBIOS.SetSMBHeaderCommand (szReply, iMsgType, sizeof (szReply));
+ if (iMsgType == SMBsendstrt)
+ {
+ // Код сообщения
+ static UCHAR rnd = 1;
+ szReplyData [0] = 1; // Номер сообщения
+ szReplyData [1] = rnd++; //
+ if ( rnd > 5 )
+ rnd = 1;
+ }
+ nRes = pluginNetBIOS.Send (m_lana, lsn, szReply,
+ (WORD)( (iMsgType == SMBsendstrt) ? (SMB_HEADER_SIZE + 5) : (SMB_HEADER_SIZE + 3) ) );
+ if ( nRes != NRC_GOODRET )
+ {
+ // Нефатальная ошибка
+ LOG( "Receiver : Error while sending ack" );
+ }
+
+ // Действия в зависимости от типа сообщения
+ if (iMsgType == SMBsends)
+ {
+ LOG( "Receiver : Got single-block message" );
+ // Короткое сообщение, вытаскиваем данные и выходим...
+ sFrom = (const char*) SMBBlock + SMB_HEADER_SIZE + 4;
+ int iFromOffset = sFrom.GetLength ();
+ sTo = (const char*) SMBBlock + SMB_HEADER_SIZE + 4 + iFromOffset + 2;
+ int iToOffset = sTo.GetLength ();
+ sMessage = (const char*) SMBBlock + SMB_HEADER_SIZE + 4 + iFromOffset + 2 +
+ iToOffset + 4;
+ break;
+ }
+ else if (iMsgType == SMBsendstrt)
+ {
+ LOG( "Receiver : Got start of multi-block message" );
+ // Кусочковое сообщение, начало, смотрим от кого и кому
+ sFrom = (const char*) SMBBlock + SMB_HEADER_SIZE + 4;
+ int iFromOffset = sFrom.GetLength ();
+ sTo = (const char*) SMBBlock + SMB_HEADER_SIZE + iFromOffset + 4 + 2;
+ }
+ else if (iMsgType == SMBsendtxt)
+ {
+ // Кусочковое сообщение, блок с данными, конкатенируем...
+ int iConcatSize = iReadedBytes - SMB_HEADER_SIZE - 8;
+ LOG( "Receiver : Got text (%d-%d bytes) of multi-block message", sMessage.GetLength(), sMessage.GetLength() + iConcatSize - 1 );
+ sMessage.Append ((const char*) (SMBBlock + SMB_HEADER_SIZE + 8), iConcatSize);
+ }
+ else if (iMsgType == SMBsendend)
+ {
+ LOG( "Receiver : Got end of multi-block message" );
+ // Кусочковое сообщение, конец, все получено, выходим
+ break;
+ }
+ }
+
+ sMessage.Replace( "\x14", "\r\n" ); // <14> -> <CR><LF>
+
+ sTo.OemToAnsi();
+ sFrom.OemToAnsi();
+ sMessage.OemToAnsi();
+
+ // Смотрим информацию о сессии
+ CStringA sRealFrom;
+ if (GetRealSender (lsn, sRealFrom))
+ {
+ LOG( "Receiver : Message from \"%s\" (real \"%s\") to \"%s\"", (LPCSTR)sFrom, (LPCSTR)sRealFrom, (LPCSTR)sTo);
+ sFrom = sRealFrom;
+ }
+ else
+ {
+ LOG( "Receiver : Message from \"%s\" (real sender unknown) to \"%s\"", (LPCSTR)sFrom, (LPCSTR)sTo);
+ }
+
+ // Скажем до свиданья...
+ pluginNetBIOS.Hangup (m_lana, lsn);
+
+ // Открываем сообщение (пустое игнорируем)
+ if ( ! sMessage.IsEmpty() )
+ {
+ ReceiveContactMessage( CA2T( sFrom ), CA2T( sTo ), CA2T( sMessage ), sMessage.GetLength ());
+ }
+ else
+ {
+ LOG( "Receiver : Ignoring empty message" );
+ }
+
+ mir_free( SMBBlock );
+}
+
+void netbios_name::ReceiverThread(LPVOID param)
+{
+ if ( ReceiverData* data = (ReceiverData*)param )
+ {
+ data->self->Receiver( data->lsn );
+ mir_free( data );
+ }
+}
diff --git a/protocols/WinPopup/src/netbios_name.h b/protocols/WinPopup/src/netbios_name.h
new file mode 100644
index 0000000000..c273082be9
--- /dev/null
+++ b/protocols/WinPopup/src/netbios_name.h
@@ -0,0 +1,155 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public 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 SMB_ANY_NAME _T("* ")
+#define FACILITY_NETBIOS (0x0f0f)
+#define SMB_HEADER_SIZE (32)
+
+#pragma pack (push, 1)
+
+typedef struct SESSION_INFO_BLOCK
+{
+ SESSION_HEADER sib_header;
+ SESSION_BUFFER sib_Names[ NCBNAMSZ ];
+} SESSION_INFO_BLOCK, *PSESSION_INFO_BLOCK;
+
+#pragma pack (pop)
+
+class netbios_name
+{
+public:
+ netbios_name(LPCTSTR n = SMB_ANY_NAME /* ANSI */, UCHAR type = 0, bool group = false, UCHAR lana = 0);
+ netbios_name(const NAME_BUFFER& n, UCHAR lana);
+ netbios_name& operator=(const netbios_name& n);
+ netbios_name& operator=(const UCHAR* n);
+ // Сравнение (производиться только по имени, номеру имени, типу имени)
+ bool operator==(const NAME_BUFFER& n) const;
+ // Сравнение (производиться только по имени, номеру имени, типу имени)
+ bool operator!=(const NAME_BUFFER& n) const;
+ // Сравнение (производиться только по имени, номеру имени, типу имени, адаптеру)
+ bool operator==(const netbios_name& n) const;
+ // Сравнение (производиться только по имени, номеру имени, типу имени, адаптеру)
+ bool operator!=(const netbios_name& n) const;
+ // Регистрация имени
+ bool Register();
+ // Для ускорения
+ void AskForDestroy();
+ // Дерегистрация имени
+ void Destroy();
+ // Обратное преобразование NetBIOS-имени из OEM в ANSI в виде: NAME <TYPE>
+ CStringA GetANSIFullName() const;
+
+ CStringA original; // Полное имя (в ANSI кодировке)
+ NAME_BUFFER netbiosed; // NetBIOS имя (в OEM кодировке):
+ // typedef struct _NAME_BUFFER {
+ // UCHAR name[NCBNAMSZ];
+ // UCHAR name_num;
+ // UCHAR name_flags;
+ // } NAME_BUFFER, *PNAME_BUFFER;
+
+ // Получение атрибутов имени:
+
+ UCHAR GetType() const;
+ bool IsGroupName() const;
+ bool IsRegistered() const;
+ bool IsDuplicated() const;
+ bool IsError() const;
+ bool IsOwnName() const;
+ UCHAR GetLana() const;
+ size_t GetLength() const; // Длина имени NetBIOS
+
+protected:
+ bool m_managed; // Флаг управляемого имени
+ bool m_registered; // Флаг успешной регистрации имени
+ bool m_duplicated; // Флаг существования имени до регистрации
+ bool m_error; // Флаг ошибки прослушивания имени
+ UCHAR m_lana; // Номер адаптера
+ HANDLE m_listener; // Хэндлер рабочего потока-слушателя сессий
+ HANDLE m_dgreceiver; // Хэндлер рабочего потока-слушателя датаграмм
+ HANDLE m_term; // Хэндлер события останова рабочего потока-слушателя
+
+ // Обратное преобразование NetBIOS-имени из OEM в ANSI
+ CStringA GetANSIName() const;
+
+ bool GetRealSender(UCHAR lsn, CStringA& sRealFrom) const;
+ UCHAR AddName();
+ UCHAR DeleteName();
+
+ // Ожидание начала сессии
+ void Listener();
+ static void ListenerThread(LPVOID param);
+
+ // Ожидание датаграммы
+ void DatagramReceiver();
+ static void DatagramReceiverThread(LPVOID param);
+
+ // Обработка сессии (из Listener)
+ void Receiver(UCHAR lsn);
+ static void ReceiverThread(LPVOID param);
+};
+
+typedef CAtlArray <netbios_name*> netbios_name_array;
+typedef CAtlList <netbios_name> netbios_name_list;
+
+/*
+Name Number(h) Type Usage
+--------------------------------------------------------------------------
+<computername> 00 U Workstation Service
+<computername> 01 U Messenger Service
+<\\--__MSBROWSE__> 01 G Master Browser
+<computername> 03 U Messenger Service
+<computername> 06 U RAS Server Service
+<computername> 1F U NetDDE Service
+<computername> 20 U File Server Service
+<computername> 21 U RAS Client Service
+<computername> 22 U Microsoft Exchange Interchange(MSMail
+Connector)
+<computername> 23 U Microsoft Exchange Store
+<computername> 24 U Microsoft Exchange Directory
+<computername> 30 U Modem Sharing Server Service
+<computername> 31 U Modem Sharing Client Service
+<computername> 43 U SMS Clients Remote Control
+<computername> 44 U SMS Administrators Remote Control
+Tool
+<computername> 45 U SMS Clients Remote Chat
+<computername> 46 U SMS Clients Remote Transfer
+<computername> 4C U DEC Pathworks TCPIP service on
+Windows NT
+<computername> 42 U mccaffee anti-virus
+<computername> 52 U DEC Pathworks TCPIP service on
+Windows NT
+<computername> 87 U Microsoft Exchange MTA
+<computername> 6A U Microsoft Exchange IMC
+<computername> BE U Network Monitor Agent
+<computername> BF U Network Monitor Application
+<username> 03 U Messenger Service
+<domain> 00 G Domain Name
+<domain> 1B U Domain Master Browser
+<domain> 1C G Domain Controllers
+<domain> 1D U Master Browser
+<domain> 1E G Browser Service Elections
+<INet~Services> 1C G IIS
+<IS~computer name> 00 U IIS
+<computername> [2B] U Lotus Notes Server Service
+IRISMULTICAST [2F] G Lotus Notes
+IRISNAMESERVER [33] G Lotus Notes
+Forte_$ND800ZA [20] U DCA IrmaLan Gateway Server Service
+*/
diff --git a/protocols/WinPopup/src/network.cpp b/protocols/WinPopup/src/network.cpp
new file mode 100644
index 0000000000..b94ac8be28
--- /dev/null
+++ b/protocols/WinPopup/src/network.cpp
@@ -0,0 +1,81 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Проверка, что строка похожа на IPv4-адрес в формате xx.xx.xx.xx
+bool IsValidIP (LPCTSTR name)
+{
+ int dots = 0;
+ bool digit = false;
+ for ( ; *name; name++ )
+ {
+ if ( *name == _T('.') )
+ {
+ if ( ! digit )
+ // Две точки подряд
+ return false;
+
+ dots++;
+ digit = false;
+ }
+ else if ( *name < _T('0') || *name > _T('9') )
+ {
+ // Это не цифра
+ return false;
+ }
+ else
+ {
+ digit = true;
+ }
+ }
+ return dots == 3 && digit;
+}
+
+// Получение IP по имени хоста и получение короткого имени хоста
+ip ResolveToIP (CString& name)
+{
+ // Получение имени из IP-адреса
+ ip addr = IsValidIP (name) ? ::inet_addr ( CT2A( name ) ) : INADDR_NONE;
+ if (addr != INADDR_NONE) {
+ struct hostent *hp = ::gethostbyaddr ((const char*) &addr, sizeof (addr), AF_INET);
+ if (hp) {
+ // Укорачиваем имя
+ name = hp->h_name;
+ int n = name.Find ('.');
+ if (n != -1)
+ name = name.Left (n);
+ }
+ }
+ // Получение IP-адреса из имени
+ if (addr == INADDR_NONE) {
+ struct hostent *hp = ::gethostbyname ( CT2A( name ) );
+ if (hp) {
+ addr = ((struct in_addr*)hp->h_addr)->s_addr;
+ // Укорачиваем имя
+ name = hp->h_name;
+ int n = name.Find ('.');
+ if (n != -1)
+ name = name.Left (n);
+ }
+ }
+ return ntohl (addr);
+}
diff --git a/protocols/WinPopup/src/network.h b/protocols/WinPopup/src/network.h
new file mode 100644
index 0000000000..9b6be5f0d3
--- /dev/null
+++ b/protocols/WinPopup/src/network.h
@@ -0,0 +1,29 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// Адрес IPv4
+typedef unsigned long ip;
+
+// Проверка, что строка похожа на IP-адрес в формате xx.xx.xx.xx
+bool IsValidIP (LPCTSTR name);
+
+// Получение IP по имени хоста и получение короткого имени хоста
+ip ResolveToIP (CString& name);
diff --git a/protocols/WinPopup/src/options.cpp b/protocols/WinPopup/src/options.cpp
new file mode 100644
index 0000000000..4dc42977fa
--- /dev/null
+++ b/protocols/WinPopup/src/options.cpp
@@ -0,0 +1,381 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 WM_FILLTREE (WM_USER+75)
+
+static netbios_name_list nns;
+static HWND hTree = NULL;
+
+typedef struct _DlgDataOptions
+{
+ HMODULE hContact;
+ HIMAGELIST hTreeImages;
+ bool need_restart;
+} DlgDataOptions;
+
+static void FillTreeThread (LPVOID param)
+{
+ // Ожидание инициализации NetBIOS (20 секунд)
+ for ( int i = 0; i < 20 && ! pluginNetBIOS && IsWindow( hTree ); i++ )
+ Sleep( 1000 );
+
+ if ( IsWindow( hTree ) )
+ {
+ // ...вначале своими именами
+ pluginNetBIOS.GetRegisteredNames( nns );
+ // ...потом остальными именами
+ pluginNetBIOS.GetNames( nns, pluginMachineName, false );
+ // ...и уведомление о готовности данных
+ PostMessage( reinterpret_cast <HWND>(param), WM_FILLTREE, 0, 0 );
+ }
+}
+
+static void Refresh (HWND hwndDlg, HWND hwndTree)
+{
+ bool bOnline = ( pluginCurrentStatus != ID_STATUS_OFFLINE );
+
+ nns.RemoveAll ();
+
+ // Заполнение дерева имён...
+ TreeView_DeleteAllItems (hwndTree);
+ TVINSERTSTRUCT tvis = { TVI_ROOT, TVI_LAST };
+ tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.iImage = tvis.item.iSelectedImage = bOnline ? 0 : 8;
+ tvis.item.pszText = bOnline ? TranslateT("Retrieving...") : TranslateT("Offline");
+ TreeView_InsertItem (hwndTree, &tvis);
+
+ if ( bOnline )
+ {
+ // Запуск опроса хоста
+ mir_forkthread( FillTreeThread, hwndDlg );
+ }
+}
+
+static INT_PTR CALLBACK DlgProcOptions (HWND hwndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+ static bool bLastOnline = false;
+ DlgDataOptions* data = reinterpret_cast <DlgDataOptions*> (GetWindowLongPtr (hwndDlg, DWLP_USER));
+
+ switch ( Msg )
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault (hwndDlg);
+
+ data = (DlgDataOptions*)mir_alloc( sizeof( DlgDataOptions ) );
+ if ( ! data )
+ return FALSE;
+
+ SetWindowLongPtr (hwndDlg, DWLP_USER, reinterpret_cast <LONG> (data));
+ data->need_restart = false;
+ data->hTreeImages = ImageList_Create (16, 16, ILC_COLOR8 | ILC_MASK, 8, 0);
+
+ hTree = GetDlgItem (hwndDlg, IDC_TREE);
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 0
+ pluginModule, MAKEINTRESOURCE (IDI_COMPUTER), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 1
+ pluginModule, MAKEINTRESOURCE (IDI_LANA), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ HICON hIcon;
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_GOOD_NAME), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 2
+ SendDlgItemMessage (hwndDlg, IDC_LEGEND_1, STM_SETICON,
+ reinterpret_cast <WPARAM> (hIcon), 0);
+
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_BAD_NAME), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 3
+ SendDlgItemMessage (hwndDlg, IDC_LEGEND_2, STM_SETICON,
+ reinterpret_cast <WPARAM> (hIcon), 0);
+
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_OTHER_NAME), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 4
+ SendDlgItemMessage (hwndDlg, IDC_LEGEND_3, STM_SETICON,
+ reinterpret_cast <WPARAM> (hIcon), 0);
+
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_GOOD_NAMES), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 5
+ SendDlgItemMessage (hwndDlg, IDC_LEGEND_4, STM_SETICON,
+ reinterpret_cast <WPARAM> (hIcon), 0);
+
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_BAD_NAMES), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 6
+ SendDlgItemMessage (hwndDlg, IDC_LEGEND_5, STM_SETICON,
+ reinterpret_cast <WPARAM> (hIcon), 0);
+
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_OTHER_NAMES), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 7
+ SendDlgItemMessage (hwndDlg, IDC_LEGEND_6, STM_SETICON,
+ reinterpret_cast <WPARAM> (hIcon), 0);
+
+ hIcon = reinterpret_cast <HICON> (LoadImage (
+ pluginModule, MAKEINTRESOURCE (IDI_COMPUTER_ERROR), IMAGE_ICON, 16, 16, LR_SHARED ));
+ ImageList_AddIcon (data->hTreeImages, hIcon); // 8
+
+ TreeView_SetImageList (hTree, data->hTreeImages, TVSIL_NORMAL);
+
+ BOOL b = DBGetContactSettingByte (NULL, modname, "RegisterNick", TRUE);
+ CheckDlgButton (hwndDlg, IDC_CHECK_NICK, (UINT)( b ? BST_CHECKED : BST_UNCHECKED ));
+ EnableWindow (GetDlgItem (hwndDlg, IDC_NICK1), b);
+ EnableWindow (GetDlgItem (hwndDlg, IDC_NICK2), b);
+
+ b = DBGetContactSettingByte (NULL, modname, "RegisterUser", TRUE);
+ CheckDlgButton (hwndDlg, IDC_CHECK_USER, (UINT)( b ? BST_CHECKED : BST_UNCHECKED ));
+ EnableWindow (GetDlgItem (hwndDlg, IDC_USER), b);
+
+ CheckDlgButton (hwndDlg, IDC_AUTOANSWER,
+ (UINT)( DBGetContactSettingByte (NULL, modname, "Auto-answer", FALSE) ?
+ BST_CHECKED : BST_UNCHECKED ) );
+ CheckDlgButton (hwndDlg, IDC_DUPS,
+ (UINT)( DBGetContactSettingByte (NULL, modname, "Filter-dups", TRUE) ?
+ BST_CHECKED : BST_UNCHECKED ) );
+ CheckDlgButton (hwndDlg, IDC_ALWAYSCHECK00FORONLINE,
+ (UINT)( IsLegacyOnline( NULL ) ? BST_CHECKED : BST_UNCHECKED ) );
+
+ BYTE method = (BYTE) DBGetContactSettingByte (NULL, modname, "SendMethod", 0);
+ CheckRadioButton (hwndDlg, IDC_USE_MAILSLOT, IDC_USE_NETSEND,
+ IDC_USE_MAILSLOT + method);
+ EnableWindow (GetDlgItem (hwndDlg, IDC_USE_NETSEND), (fnNetMessageBufferSend != NULL));
+
+ CString sMyNick = GetNick( NULL );
+ if ( ! sMyNick.IsEmpty() )
+ {
+ netbios_name nname (sMyNick, 3);
+ SetDlgItemText (hwndDlg, IDC_NICK1, CA2T( nname.GetANSIFullName() ) );
+ }
+
+ if ( ! sMyNick.IsEmpty() )
+ {
+ netbios_name nname (sMyNick, 1);
+ SetDlgItemText (hwndDlg, IDC_NICK2, CA2T( nname.GetANSIFullName() ) );
+ }
+
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( NULL, modname, "User", &dbv ) )
+ {
+ netbios_name nname (dbv.ptszVal, 3);
+ SetDlgItemText (hwndDlg, IDC_USER, CA2T( nname.GetANSIFullName() ) );
+ DBFreeVariant (&dbv);
+ }
+
+ bLastOnline = ! ( pluginCurrentStatus != ID_STATUS_OFFLINE );
+
+ SetTimer( hwndDlg, 55, 500, NULL );
+
+ return TRUE;
+ }
+
+ case WM_TIMER:
+ if ( bLastOnline != ( pluginCurrentStatus != ID_STATUS_OFFLINE ) )
+ {
+ bLastOnline = ( pluginCurrentStatus != ID_STATUS_OFFLINE );
+ Refresh( hwndDlg, hTree );
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ {
+ nns.RemoveAll();
+ SetWindowLongPtr( hwndDlg, DWLP_USER, NULL );
+ if ( data )
+ {
+ ImageList_Destroy( data->hTreeImages );
+ mir_free( data );
+ }
+ hTree = NULL;
+ break;
+ }
+
+ case WM_FILLTREE:
+ {
+ // Заполнение дерева имён
+ TreeView_DeleteAllItems (hTree);
+ TVINSERTSTRUCT tvis = { TVI_ROOT, TVI_LAST };
+ tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvis.item.pszText = (LPTSTR)(LPCTSTR)pluginMachineName;
+ tvis.item.iImage = tvis.item.iSelectedImage = nns.GetCount() ? 0 : 8;
+ HTREEITEM hRoot = TreeView_InsertItem (hTree, &tvis);
+ if ( nns.GetCount() )
+ {
+ TVITEM item = { 0 };
+ for (POSITION pos = nns.GetHeadPosition (); pos;)
+ {
+ netbios_name nname (nns.GetNext (pos));
+ tvis.item.lParam = nname.GetLana();
+
+ // Поиск ланы в дереве по номеру
+ item.hItem = TreeView_GetChild (hTree, hRoot);
+ while (item.hItem)
+ {
+ item.mask = TVIF_HANDLE | TVIF_PARAM;
+ if (TreeView_GetItem (hTree, &item) &&
+ item.lParam == nname.GetLana ())
+ // найден
+ break;
+ item.hItem = TreeView_GetNextSibling (hTree, item.hItem);
+ }
+ if (item.hItem)
+ // Лана уже есть
+ tvis.hParent = item.hItem;
+ else
+ {
+ // Ланы ещё нет
+ tvis.hParent = hRoot;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ CString tmp;
+ tmp.Format ( _T("%s #%d"), TranslateT ("LAN adapter"), nname.GetLana ());
+ tvis.item.pszText = (LPTSTR) (LPCTSTR) tmp;
+ tvis.hParent = TreeView_InsertItem (hTree, &tvis);
+
+ pluginNetBIOS.GetMAC (nname.GetLana (), tmp);
+ tmp.Insert (0, _T("MAC: "));
+ tvis.item.pszText = (LPTSTR) (LPCTSTR) tmp;
+ TreeView_InsertItem (hTree, &tvis);
+ }
+
+ CA2T textT( nname.GetANSIFullName() );
+ tvis.item.pszText = (LPTSTR)(LPCTSTR)textT;
+ tvis.item.iImage = tvis.item.iSelectedImage =
+ (nname.IsOwnName () ? (nname.IsError () ? 1 : 0) : 2) +
+ (nname.IsGroupName () ? 5 : 2);
+
+ // Поиск имени в лане по имени
+ item.hItem = TreeView_GetChild (hTree, tvis.hParent);
+ while (item.hItem)
+ {
+ item.mask = TVIF_HANDLE | TVIF_TEXT;
+ item.cchTextMax = 64;
+ CString tmp;
+ item.pszText = tmp.GetBuffer (item.cchTextMax);
+ BOOL ret = TreeView_GetItem (hTree, &item);
+ tmp.ReleaseBuffer ();
+ if (ret && tmp == textT )
+ // найден
+ break;
+ item.hItem = TreeView_GetNextSibling (hTree, item.hItem);
+ }
+ if (!item.hItem)
+ // Имени ещё нет
+ TreeView_InsertItem (hTree, &tvis);
+ }
+ }
+ TreeView_Expand (hTree, hRoot, TVE_EXPAND);
+
+ nns.RemoveAll ();
+ return TRUE;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPPSHNOTIFY lpHdr = reinterpret_cast <LPPSHNOTIFY> (lParam);
+ if (lpHdr->hdr.idFrom == 0) {
+ data->hContact = reinterpret_cast <HMODULE> (lpHdr->lParam);
+ switch (lpHdr->hdr.code) {
+ case PSN_KILLACTIVE:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE);
+ return TRUE;
+
+ case PSN_APPLY:
+ DBWriteContactSettingByte (NULL, modname, "RegisterNick",
+ (BYTE)( (IsDlgButtonChecked (hwndDlg, IDC_CHECK_NICK) == BST_CHECKED ? TRUE : FALSE ) ));
+ DBWriteContactSettingByte (NULL, modname, "RegisterUser",
+ (BYTE)( (IsDlgButtonChecked (hwndDlg, IDC_CHECK_USER) == BST_CHECKED ? TRUE : FALSE ) ));
+ DBWriteContactSettingByte (NULL, modname, "Auto-answer",
+ (BYTE)( (IsDlgButtonChecked (hwndDlg, IDC_AUTOANSWER) == BST_CHECKED ? TRUE : FALSE ) ));
+ DBWriteContactSettingByte (NULL, modname, "Filter-dups",
+ (BYTE)( (IsDlgButtonChecked (hwndDlg, IDC_DUPS) == BST_CHECKED ? TRUE : FALSE ) ));
+ DBWriteContactSettingByte (NULL, modname, "SendMethod",
+ (BYTE)( (((IsDlgButtonChecked (hwndDlg, IDC_USE_MAILSLOT) == BST_CHECKED) ? 0 :
+ ((IsDlgButtonChecked (hwndDlg, IDC_USE_NETBIOS) == BST_CHECKED) ? 1 :
+ ((IsDlgButtonChecked (hwndDlg, IDC_USE_NETSEND) == BST_CHECKED) ? 2 :
+ 0 ) )))));
+ SetLegacyOnline( NULL, ( IsDlgButtonChecked( hwndDlg,
+ IDC_ALWAYSCHECK00FORONLINE ) == BST_CHECKED ) );
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
+ if (data->need_restart) {
+ data->need_restart = false;
+ GotoOffline ();
+ Sleep (2000);
+ Refresh (hwndDlg, hTree);
+ }
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case IDC_CHECK_NICK:
+ case IDC_CHECK_USER:
+ EnableWindow (GetDlgItem (hwndDlg, IDC_NICK1),
+ IsDlgButtonChecked (hwndDlg, IDC_CHECK_NICK) == BST_CHECKED);
+ EnableWindow (GetDlgItem (hwndDlg, IDC_NICK2),
+ IsDlgButtonChecked (hwndDlg, IDC_CHECK_NICK) == BST_CHECKED);
+ EnableWindow (GetDlgItem (hwndDlg, IDC_USER),
+ IsDlgButtonChecked (hwndDlg, IDC_CHECK_USER) == BST_CHECKED);
+
+ case IDC_USE_MAILSLOT:
+ case IDC_USE_NETBIOS:
+ case IDC_USE_NETSEND:
+ data->need_restart = true;
+
+ case IDC_AUTOANSWER:
+ case IDC_ALWAYSCHECK00FORONLINE:
+ case IDC_DUPS:
+ PropSheet_Changed (GetParent (hwndDlg), hwndDlg);
+ break;
+
+ case IDC_ADD:
+ AddDialog( hwndDlg );
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+int __cdecl OPT_INITIALISE (WPARAM wParam, LPARAM /* lParam */)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof (odp);
+ odp.ptszGroup = LPGENT("Network");
+ odp.position = odp.pszGroup[0];
+ odp.hInstance = pluginModule;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.ptszTitle = LPGENT(modtitle);
+ odp.pfnDlgProc = DlgProcOptions;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ Options_AddPage(wParam, &odp);
+
+ return 0;
+}
diff --git a/protocols/WinPopup/src/options.h b/protocols/WinPopup/src/options.h
new file mode 100644
index 0000000000..3e949cc8b6
--- /dev/null
+++ b/protocols/WinPopup/src/options.h
@@ -0,0 +1,22 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+int __cdecl OPT_INITIALISE (WPARAM wParam, LPARAM lParam);
diff --git a/protocols/WinPopup/src/processapi.cpp b/protocols/WinPopup/src/processapi.cpp
new file mode 100644
index 0000000000..43e1235680
--- /dev/null
+++ b/protocols/WinPopup/src/processapi.cpp
@@ -0,0 +1,571 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+DWORD GetProcessId (LPCTSTR name)
+{
+ DWORD id = 0;
+ int name_len = lstrlen (name);
+ CProcessApi papi;
+ if (papi.Init (true)) {
+ DWORD pl = papi.ProcessesGetList();
+ if (pl) {
+ CProcessApi::tProcessInfo pi = {0};
+ while (papi.ProcessesWalk (pl, &pi)) {
+ int len = lstrlen (pi.FileName);
+ if (len >= name_len &&
+ lstrcmpi (pi.FileName + (len - name_len), name) == 0) {
+ id = pi.pid;
+ break;
+ }
+ }
+ }
+ papi.ProcessesFreeList(pl);
+ }
+ return id;
+}
+
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2003 Elias Bachaalany <elias_bachaalany@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -----------------------------------------------------------------------------
+ */
+
+#ifdef _UNICODE
+ #define Modifier "W"
+#else
+ #define Modifier "A"
+#endif
+
+// custom macro to allow me to load functions dynamically
+#define DynamicGetProcAddress(lib, prefix, mod_name, Mod) \
+ { \
+ PVOID p = GetProcAddress(lib, #mod_name Mod); \
+ if (!p) { \
+ FreeLibrary(lib); \
+ return (LastError = paeNoEntryPoint, false); \
+ } \
+ ##prefix##mod_name = (t_##prefix##mod_name)p; \
+ }
+
+//--------------------------------------------------------------------------------------------
+// The constructor only initializes some internal variables
+CProcessApi::CProcessApi() :
+ m_hPsApi (NULL),
+ m_hTlHlp (NULL),
+ m_hNtApi (NULL),
+ m_bPsApi (false),
+ m_bToolHelp (false),
+ m_bNtApi (false),
+ LastError (paeSuccess)
+{
+}
+
+//--------------------------------------------------------------------------------------------
+// This is the destructor. It frees any process helper api that has been used
+CProcessApi::~CProcessApi()
+{
+ if (m_hPsApi)
+ FreeLibrary(m_hPsApi);
+ if (m_hTlHlp)
+ FreeLibrary(m_hTlHlp);
+ if (m_hNtApi)
+ FreeLibrary(m_hNtApi);
+}
+
+//--------------------------------------------------------------------------------------------
+// Used to initialize the CProcessApi class
+bool CProcessApi::Init (bool bNtApiFirst)
+{
+ bool loaded = m_bPsApi || m_bToolHelp || m_bNtApi;
+
+ if (bNtApiFirst && !loaded) {
+ loaded = Load_NtApi();
+ }
+
+ if (!loaded) {
+ loaded = Load_PsApi();
+ }
+
+ if (!loaded) {
+ loaded = Load_TlHlp();
+ }
+
+ if (!bNtApiFirst && !loaded) {
+ loaded = Load_NtApi();
+ }
+
+ return (loaded ? (LastError = paeSuccess, true) : (LastError = paeNoApi, false));
+}
+
+//--------------------------------------------------------------------------------------------
+// This function returns a list id (list id) that can be used w/ Walking functions
+// in order to navigate through the process list
+// This function decides what to use from the helper apis
+DWORD CProcessApi::ModulesGetList(DWORD pid)
+{
+ tModulesData *md = new tModulesData;
+ if ( ! md )
+ return (LastError = paeNoMem, 0u);
+
+ // create the list
+ md->ml = new tModulesList;
+ if ( ! md->ml )
+ {
+ delete md;
+ return (LastError = paeNoMem, 0u);
+ }
+
+ // decide what to use
+ if (m_bPsApi)
+ LastError = ModulesPopulatePsApi(pid, md);
+ else if (m_bToolHelp)
+ LastError = ModulesPopulateToolHelp(pid, md);
+ else if (m_bNtApi)
+ LastError = ModulesPopulateNtApi(pid, md);
+
+ return (DWORD) md;
+}
+
+//--------------------------------------------------------------------------------------------
+// Populates the modules of a process using ToolHelp api
+DWORD CProcessApi::ModulesPopulateToolHelp(DWORD pid, tModulesData *md)
+{
+ MODULEENTRY32 me32 = {sizeof(MODULEENTRY32), 0};
+ tModuleInfo mi = {0};
+
+ // Take a snapshot of all modules in the specified process.
+ HANDLE hModuleSnap = tlhlp_CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
+
+ if (hModuleSnap == INVALID_HANDLE_VALUE)
+ return paeNoSnap;
+
+ // Start walking
+ BOOL bMore = tlhlp_Module32First(hModuleSnap, &me32);
+ do
+ {
+ lstrcpy (mi.FileName, me32.szExePath);
+ mi.ImageBase = me32.modBaseAddr;
+ mi.ImageSize = me32.modBaseSize;
+
+ // save item
+ md->ml->Add (mi);
+
+ // search next
+ bMore = tlhlp_Module32Next(hModuleSnap, &me32);
+ } while (bMore);
+
+ CloseHandle (hModuleSnap);
+ md->Pos = 0;
+ return paeSuccess;
+}
+
+//--------------------------------------------------------------------------------------------
+// Populates the modules of a process using PsApi api
+DWORD CProcessApi::ModulesPopulatePsApi(DWORD pid, tModulesData *md)
+{
+ DWORD nModules, nCount = 4096;
+ HANDLE hProcess;
+
+ // allocate memory for modules
+ HMODULE *modules = new HMODULE[nCount];
+
+ // open process for querying only
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+ if (!hProcess)
+ return paeNoSnap;
+
+ // now try to enum all modules
+ if (!psapi_EnumProcessModules(hProcess, modules, nCount * sizeof(DWORD), &nModules))
+ {
+ CloseHandle(hProcess);
+ return paeNoSnap;
+ }
+
+ // because nModules returned from EnumProcessModules() is in bytes, I divid by 4 to return n DWORDs
+ nModules /= 4;
+
+ tModuleInfo mi = {0};
+ MODULEINFO psapiMi = {0};
+
+ for (DWORD i=0; i < nModules; i++)
+ {
+ // get module name
+ psapi_GetModuleFileNameEx(hProcess, modules[i], mi.FileName, sizeof(mi.FileName));
+
+ // get module information
+ psapi_GetModuleInformation(hProcess, modules[i], &psapiMi, sizeof(MODULEINFO));
+
+ // get relevant data
+ mi.ImageBase = psapiMi.lpBaseOfDll;
+ mi.ImageSize = psapiMi.SizeOfImage;
+
+ // save item
+ md->ml->Add (mi);
+ }
+
+ md->Pos = 0;
+ CloseHandle(hProcess);
+ delete [] modules;
+
+ return paeSuccess;
+}
+
+//--------------------------------------------------------------------------------------------
+// It frees a modules list by its ID
+void CProcessApi::ModulesFreeList(DWORD_PTR lid)
+{
+ tModulesData *md = reinterpret_cast<tModulesData *>(lid);
+ delete md->ml;
+ delete md;
+}
+
+//--------------------------------------------------------------------------------------------
+// This function allows you to retrieve information about a certain module in a process
+// You can either use this function using the Pos parameter controlled by the ModulesCount()
+// Or you can keep calling it till it returns false
+bool CProcessApi::ModulesWalk(DWORD_PTR lid, tModuleInfo *mi, DWORD Pos)
+{
+ tModulesData *md = reinterpret_cast<tModulesData *>(lid);
+
+ // auto position ?
+ if (Pos == -1)
+ Pos = md->Pos;
+
+ // out of bounds?
+ if (Pos > md->ml->GetCount())
+ return (LastError = paeOutOfBounds, false);
+
+ // end reached ?
+ else if (Pos == md->ml->GetCount())
+ return (LastError = paeNoMore, false);
+
+ // copy information to user buffer
+ *mi = md->ml->GetAt (Pos);
+
+ // advance position to next item
+ md->Pos++;
+
+ return (LastError = paeSuccess, true);
+}
+
+
+//--------------------------------------------------------------------------------------------
+// This function allows you to retrieve information about a certain process in the list
+// You can either use this function using the Pos parameter controlled by the ProcessesCount()
+// Or you can keep calling it till it returns false
+bool CProcessApi::ProcessesWalk(DWORD_PTR lid, tProcessInfo *pi, DWORD Pos)
+{
+ tProcessesData *pd = reinterpret_cast<tProcessesData *>(lid);
+
+ // auto position ?
+ if (Pos == -1)
+ Pos = pd->Pos;
+
+ // out of bounds?
+ if (Pos > pd->pl->GetCount())
+ return (LastError = paeOutOfBounds, false);
+ // end reached ?
+ else if (Pos == pd->pl->GetCount())
+ return (LastError = paeNoMore, false);
+
+ // copy information to user buffer
+ *pi = pd->pl->GetAt (Pos);
+
+ // advance position to next item
+ pd->Pos++;
+ return (LastError = paeSuccess, true);
+}
+
+
+//--------------------------------------------------------------------------------------------
+// This function returns a list id (list id) that can be used w/ Walking functions
+// in order to navigate through the process list
+// This function decides what to use from the helper apis
+DWORD CProcessApi::ProcessesGetList()
+{
+ tProcessesData *pd = new tProcessesData;
+ if (!pd)
+ return (LastError = paeNoMem, 0u);
+
+ // create the list
+ pd->pl = new tProcessesList;
+ if (!pd->pl)
+ {
+ delete pd;
+ return (LastError = paeNoMem, 0u);
+ }
+
+ // decide what to use
+ if (m_bPsApi)
+ LastError = ProcessesPopulatePsApi(pd);
+ else if (m_bToolHelp)
+ LastError = ProcessesPopulateToolHelp(pd);
+ else if (m_bNtApi)
+ LastError = ProcessesPopulateNtApi(pd);
+
+ return (DWORD) pd;
+}
+
+//--------------------------------------------------------------------------------------------
+// It frees a process list by its ID
+void CProcessApi::ProcessesFreeList(DWORD_PTR lid)
+{
+ tProcessesData *pd = reinterpret_cast<tProcessesData *>(lid);
+ delete pd->pl;
+ delete pd;
+}
+
+//--------------------------------------------------------------------------------------------
+// Dynamically loads the PsApi functions
+bool CProcessApi::Load_PsApi()
+{
+ if (m_bPsApi)
+ return true;
+ if (!m_hPsApi)
+ m_hPsApi = LoadLibrary (_T("psapi.dll"));
+ if (!m_hPsApi)
+ return false;
+
+ DynamicGetProcAddress(m_hPsApi, psapi_, GetModuleFileNameEx, Modifier);
+ DynamicGetProcAddress(m_hPsApi, psapi_, EnumProcessModules, "");
+ DynamicGetProcAddress(m_hPsApi, psapi_, EnumProcesses, "");
+ DynamicGetProcAddress(m_hPsApi, psapi_, GetModuleInformation, "");
+
+ m_bPsApi = true;
+ return true;
+}
+
+
+//--------------------------------------------------------------------------------------------
+// Dynamically loads the ToolHelp functions
+bool CProcessApi::Load_TlHlp()
+{
+ if (m_bToolHelp)
+ return true;
+ if (!m_hTlHlp)
+ m_hTlHlp = LoadLibrary (_T("kernel32.dll"));
+ if (!m_hTlHlp)
+ return false;
+
+ DynamicGetProcAddress(m_hTlHlp, tlhlp_, CreateToolhelp32Snapshot, "");
+ DynamicGetProcAddress(m_hTlHlp, tlhlp_, Process32First, "");
+ DynamicGetProcAddress(m_hTlHlp, tlhlp_, Process32Next, "");
+ DynamicGetProcAddress(m_hTlHlp, tlhlp_, Module32First, "");
+ DynamicGetProcAddress(m_hTlHlp, tlhlp_, Module32Next, "");
+
+ m_bToolHelp = true;
+ return true;
+}
+
+bool CProcessApi::Load_NtApi()
+{
+ if (m_bNtApi)
+ return true;
+ if (!m_hNtApi)
+ m_hNtApi = LoadLibrary (_T("ntdll.dll"));
+ if (!m_hNtApi)
+ return false;
+
+ DynamicGetProcAddress(m_hNtApi, ntapi_, ZwQuerySystemInformation, "");
+
+ m_bNtApi = true;
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------
+// Populates a tProcessesList with the help of ToolHelp API
+// Returns an error code paeXXXX
+DWORD CProcessApi::ProcessesPopulateToolHelp(tProcessesData *pd)
+{
+ // create a process snapshot
+ HANDLE hSnap = tlhlp_CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hSnap == INVALID_HANDLE_VALUE)
+ return paeNoSnap;
+
+ BOOL bMore;
+ tProcessInfo pi = {0};
+
+ PROCESSENTRY32 pe32 = {sizeof(PROCESSENTRY32), 0};
+
+ // clear the list
+ pd->pl->RemoveAll ();
+
+ // initialize position
+ pd->Pos = 0;
+
+ bMore = tlhlp_Process32First(hSnap, &pe32);
+ while (bMore)
+ {
+ // convert from PROCESSENTRY32 to my unified tProcessInfo struct
+ pi.pid = pe32.th32ProcessID;
+ lstrcpy (pi.FileName, pe32.szExeFile);
+
+ pd->pl->Add(pi);
+ bMore = tlhlp_Process32Next(hSnap, &pe32);
+ }
+
+ CloseHandle(hSnap);
+ return paeSuccess;
+}
+
+
+//--------------------------------------------------------------------------------------------
+// Populates the list using PsApi functions
+DWORD CProcessApi::ProcessesPopulatePsApi(tProcessesData *pd)
+{
+ DWORD nProcess, // number of processes returned
+ nCount(4096); // maximum number of processes (defined by me)
+
+ // Dynamic array for storing returned processes IDs
+ DWORD *processes = new DWORD[nCount];
+
+ // enum all processes
+ if (!psapi_EnumProcesses(processes, nCount * sizeof(DWORD), &nProcess))
+ {
+ delete [] processes;
+ return paeNoSnap;
+ }
+
+ // convert fron bytes count to items count
+ nProcess /= 4;
+
+ // walk in process list
+ for (DWORD i = 0; i < nProcess; i++) {
+
+ if (processes[i] == 0)
+ // Idle
+ continue;
+
+ // open process for querying only
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processes[i]);
+ if (!hProcess)
+ continue;
+
+ // get the process's image name by getting first module
+ DWORD nmod;
+ HMODULE mod1;
+
+ tProcessInfo pi = {0};
+ pi.pid = processes [i];
+ if (!psapi_EnumProcessModules (hProcess, &mod1, sizeof(mod1), &nmod))
+ lstrcpy (pi.FileName, _T("-"));
+ else
+ psapi_GetModuleFileNameEx (hProcess, mod1, pi.FileName, sizeof(pi.FileName));
+ pd->pl->Add (pi);
+
+ CloseHandle(hProcess);
+ }
+
+ // reposition list to 0
+ pd->Pos = 0;
+ delete [] processes;
+
+ return paeSuccess;
+}
+
+DWORD CProcessApi::ProcessesPopulateNtApi (tProcessesData *pd)
+{
+ ULONG cbBuffer = 0x10000;
+ char* pBuffer;
+ NTSTATUS Status;
+ do {
+ pBuffer = new char [cbBuffer];
+ if (pBuffer == NULL)
+ return paeNoMem;
+ Status = ntapi_ZwQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ pBuffer, cbBuffer, NULL);
+ if (Status == STATUS_INFO_LENGTH_MISMATCH) {
+ delete [] pBuffer;
+ cbBuffer *= 2;
+ } else
+ if (!NT_SUCCESS(Status)) {
+ delete [] pBuffer;
+ return paeNoSnap;
+ }
+ } while (Status == STATUS_INFO_LENGTH_MISMATCH);
+
+ PSYSTEM_PROCESSES pProcesses = (PSYSTEM_PROCESSES) pBuffer;
+ for (;;) {
+ if (pProcesses->ProcessName.Buffer != NULL) {
+ tProcessInfo pi;
+ pi.pid = pProcesses->ProcessId;
+#ifdef UNICODE
+ lstrcpy (pi.FileName, pProcesses->ProcessName.Buffer);
+#else
+ WideCharToMultiByte (CP_ACP, 0, pProcesses->ProcessName.Buffer, -1,
+ pi.FileName, MAX_PATH, NULL, NULL);
+#endif
+ pd->pl->Add (pi);
+ } // else
+ // Idle
+
+ if (pProcesses->NextEntryDelta == 0)
+ break;
+
+ // find the address of the next process structure
+ pProcesses = (PSYSTEM_PROCESSES)(((LPBYTE)pProcesses) + pProcesses->NextEntryDelta);
+ }
+ pd->Pos = 0;
+ delete [] pBuffer;
+ return paeSuccess;
+}
+
+DWORD CProcessApi::ModulesPopulateNtApi(DWORD /* pid */, tModulesData* /* md */)
+{
+ return paeSuccess;
+}
+
+//--------------------------------------------------------------------------------------------
+// Returns the count in the processes list
+DWORD CProcessApi::ProcessesCount(DWORD_PTR lid) const
+{
+ return (DWORD)(reinterpret_cast<tProcessesData *>(lid))->pl->GetCount();
+}
+
+//--------------------------------------------------------------------------------------------
+// Returns the count in the modules list
+DWORD CProcessApi::ModulesCount(DWORD_PTR lid) const
+{
+ return (DWORD)(reinterpret_cast<tModulesData *>(lid))->ml->GetCount();
+}
diff --git a/protocols/WinPopup/src/processapi.h b/protocols/WinPopup/src/processapi.h
new file mode 100644
index 0000000000..17855158d4
--- /dev/null
+++ b/protocols/WinPopup/src/processapi.h
@@ -0,0 +1,258 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+typedef LONG NTSTATUS;
+typedef LONG KPRIORITY;
+typedef LONG SYSTEM_INFORMATION_CLASS;
+
+#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+#define SystemProcessesAndThreadsInformation 5
+
+typedef struct _CLIENT_ID {
+ DWORD UniqueProcess;
+ DWORD UniqueThread;
+} CLIENT_ID;
+
+typedef struct _UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING;
+
+typedef struct _VM_COUNTERS {
+ SIZE_T PeakVirtualSize;
+ SIZE_T VirtualSize;
+ ULONG PageFaultCount;
+ SIZE_T PeakWorkingSetSize;
+ SIZE_T WorkingSetSize;
+ SIZE_T QuotaPeakPagedPoolUsage;
+ SIZE_T QuotaPagedPoolUsage;
+ SIZE_T QuotaPeakNonPagedPoolUsage;
+ SIZE_T QuotaNonPagedPoolUsage;
+ SIZE_T PagefileUsage;
+ SIZE_T PeakPagefileUsage;
+} VM_COUNTERS;
+
+typedef struct _SYSTEM_THREADS {
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER CreateTime;
+ ULONG WaitTime;
+ PVOID StartAddress;
+ CLIENT_ID ClientId;
+ KPRIORITY Priority;
+ KPRIORITY BasePriority;
+ ULONG ContextSwitchCount;
+ LONG State;
+ LONG WaitReason;
+} SYSTEM_THREADS, * PSYSTEM_THREADS;
+
+typedef struct _SYSTEM_PROCESSES {
+ ULONG NextEntryDelta;
+ ULONG ThreadCount;
+ ULONG Reserved1[6];
+ LARGE_INTEGER CreateTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER KernelTime;
+ UNICODE_STRING ProcessName;
+ KPRIORITY BasePriority;
+ ULONG ProcessId;
+ ULONG InheritedFromProcessId;
+ ULONG HandleCount;
+ ULONG Reserved2[2];
+ VM_COUNTERS VmCounters;
+#if _WIN32_WINNT >= 0x500
+ IO_COUNTERS IoCounters;
+#endif
+ SYSTEM_THREADS Threads[1];
+} SYSTEM_PROCESSES, * PSYSTEM_PROCESSES;
+
+//--------------------------------------------------------------------------------------------
+// CProcessApi class - written by Elias Bachaalany.
+// Check the implementation file for more information
+//
+
+class CProcessApi
+{
+public:
+ typedef struct tProcessInfo
+ {
+ DWORD pid;
+ TCHAR FileName[MAX_PATH];
+ };
+
+ typedef struct tModuleInfo
+ {
+ LPVOID ImageBase;
+ DWORD ImageSize;
+ TCHAR FileName[MAX_PATH];
+ };
+
+private:
+ typedef CAtlArray <tProcessInfo> tProcessesList;
+ typedef CAtlArray <tModuleInfo> tModulesList;
+
+ typedef struct tProcessesData
+ {
+ DWORD Pos;
+ tProcessesList *pl;
+ };
+
+ typedef struct tModulesData
+ {
+ DWORD Pos;
+ tModulesList *ml;
+ };
+
+ // PSAPI.DLL functions prototype
+ typedef BOOL (WINAPI *t_psapi_EnumProcesses)(
+ DWORD *lpidProcess, // array of process identifiers
+ DWORD cb, // size of array
+ DWORD *cbNeeded // number of bytes returned
+ );
+
+ typedef BOOL (WINAPI *t_psapi_EnumProcessModules)(
+ HANDLE hProcess, // handle to process
+ HMODULE *lphModule, // array of module handles
+ DWORD cb, // size of array
+ LPDWORD lpcbNeeded // number of bytes required
+ );
+
+ typedef DWORD (WINAPI *t_psapi_GetModuleFileNameEx)(
+ HANDLE hProcess, // handle to process
+ HMODULE hModule, // handle to module
+ LPTSTR lpFilename, // path buffer
+ DWORD nSize // maximum characters to retrieve
+ );
+
+ typedef BOOL (WINAPI *t_psapi_GetModuleInformation)(
+ HANDLE hProcess,
+ HMODULE hModule,
+ LPMODULEINFO lpmodinfo,
+ DWORD cb
+ );
+
+ // functions instances
+ t_psapi_GetModuleFileNameEx psapi_GetModuleFileNameEx;
+ t_psapi_EnumProcessModules psapi_EnumProcessModules;
+ t_psapi_EnumProcesses psapi_EnumProcesses;
+ t_psapi_GetModuleInformation psapi_GetModuleInformation;
+
+ // TOOLHELP functions prototype
+ typedef HANDLE (WINAPI *t_tlhlp_CreateToolhelp32Snapshot)(
+ DWORD dwFlags,
+ DWORD th32ProcessID
+ );
+
+ typedef BOOL (WINAPI *t_tlhlp_Process32First)(
+ HANDLE hSnapshot,
+ LPPROCESSENTRY32 lppe
+ );
+
+ typedef BOOL (WINAPI *t_tlhlp_Process32Next)(
+ HANDLE hSnapshot,
+ LPPROCESSENTRY32 lppe
+ );
+
+ typedef BOOL (WINAPI *t_tlhlp_Module32First)(
+ HANDLE hSnapshot,
+ LPMODULEENTRY32 lpme
+ );
+
+ typedef BOOL (WINAPI *t_tlhlp_Module32Next)(
+ HANDLE hSnapshot,
+ LPMODULEENTRY32 lpme
+ );
+
+ // functions instances
+ t_tlhlp_CreateToolhelp32Snapshot tlhlp_CreateToolhelp32Snapshot;
+ t_tlhlp_Process32First tlhlp_Process32First;
+ t_tlhlp_Process32Next tlhlp_Process32Next;
+ t_tlhlp_Module32First tlhlp_Module32First;
+ t_tlhlp_Module32Next tlhlp_Module32Next;
+
+ // NTDLL.DLL functions prototype
+ typedef NTSTATUS (NTAPI *t_ntapi_ZwQuerySystemInformation)(
+ SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength,
+ PULONG ReturnLength
+ );
+
+ // functions instances
+ t_ntapi_ZwQuerySystemInformation ntapi_ZwQuerySystemInformation;
+
+ // Private member variables
+ HMODULE m_hPsApi;
+ HMODULE m_hTlHlp;
+ HMODULE m_hNtApi;
+
+ bool m_bPsApi;
+ bool m_bToolHelp;
+ bool m_bNtApi;
+
+ bool Load_PsApi();
+ bool Load_TlHlp();
+ bool Load_NtApi();
+
+ DWORD ProcessesPopulatePsApi(tProcessesData *pd);
+ DWORD ProcessesPopulateToolHelp(tProcessesData *pd);
+ DWORD ProcessesPopulateNtApi(tProcessesData *pd);
+
+ DWORD ModulesPopulatePsApi(DWORD pid, tModulesData *md);
+ DWORD ModulesPopulateToolHelp(DWORD pid, tModulesData *md);
+ DWORD ModulesPopulateNtApi(DWORD pid, tModulesData *md);
+
+public:
+ // CProcessApi error enum
+ enum
+ {
+ paeSuccess = 0, // No error
+ paeNoApi, // No process API helper dll found
+ paeNoEntryPoint, // One needed entrypoint not found in helper dll
+ paeNoMem, // Not enough memory
+ paeNoSnap, // Could not get a snapshot
+ paeNoMore, // List contains no more items
+ paeOutOfBounds, // Tried to access list w/ an invalid index
+ paeYYY
+ };
+
+ DWORD LastError; // Holds the last error
+
+ CProcessApi();
+ ~CProcessApi();
+
+ bool Init (bool bNtApiFirst = false);
+
+ DWORD ProcessesGetList();
+ bool ProcessesWalk(DWORD_PTR lid, tProcessInfo *pi, DWORD Pos = -1);
+ DWORD ProcessesCount(DWORD_PTR lid) const;
+ void ProcessesFreeList(DWORD_PTR lid);
+
+ DWORD ModulesGetList(DWORD ProcessID);
+ bool ModulesWalk(DWORD_PTR lid, tModuleInfo *mi, DWORD Pos = -1);
+ DWORD ModulesCount(DWORD_PTR lid) const;
+ void ModulesFreeList(DWORD_PTR lid);
+};
+
+// Получение PID запущенного процесса по имени его выполнимого модуля
+DWORD GetProcessId (LPCTSTR name);
diff --git a/protocols/WinPopup/src/resource.h b/protocols/WinPopup/src/resource.h
new file mode 100644
index 0000000000..be40ff446c
--- /dev/null
+++ b/protocols/WinPopup/src/resource.h
@@ -0,0 +1,66 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by winpopup_proto.rc
+//
+#define IDI_WINPOPUP 102
+#define IDI_ONLINE 104
+#define IDI_OFFLINE 105
+#define IDD_OPTIONS 106
+#define IDI_AWAY 128
+#define IDI_FREECHAT 129
+#define IDI_INVISIBLE 130
+#define IDI_NA 131
+#define IDI_DND 158
+#define IDI_OCCUPIED 159
+#define IDC_AUTOANSWER 1001
+#define IDI_ONTHEPHONE 1002
+#define IDC_NICK1 1002
+#define IDI_OUTTOLUNCH 1003
+#define IDC_USER 1003
+#define IDC_NICK2 1004
+#define IDC_ADD 1005
+#define IDC_NAME 1006
+#define IDC_DUPS 1006
+#define IDC_TREE 1007
+#define IDC_LEGEND_1 1008
+#define IDC_LEGEND_2 1009
+#define IDC_LEGEND_3 1010
+#define IDC_LEGEND_4 1011
+#define IDC_LEGEND_5 1012
+#define IDC_LEGEND_6 1013
+#define IDC_USE_MAILSLOT 1014
+#define IDC_USE_NETBIOS 1015
+#define IDC_USE_NETSEND 1016
+#define IDC_CHECK_USER 1017
+#define IDC_CHECK_NICK 1018
+#define IDC_ABOUT 1019
+#define IDC_ONLINE_CHECK 1020
+#define IDC_ALWAYSCHECK00FORONLINE 1021
+#define IDC_CHECK00FORONLINE 1022
+#define IDC_CHECK1 1023
+#define IDC_GROUP 1023
+#define IDD_ADD 2000
+#define IDI_LANA 2001
+#define IDI_COMPUTER 2002
+#define IDI_GOOD_NAME 2003
+#define IDI_BAD_NAME 2004
+#define IDI_OTHER_NAME 2005
+#define IDI_OTHER_NAMES 2006
+#define IDI_BAD_NAMES 2007
+#define IDI_GOOD_NAMES 2008
+#define IDD_USERINFO 2009
+#define IDI_EXPLORE 2011
+#define IDI_COMPUTER_ERROR 2013
+#define IDI_ADD_COMPUTER 2014
+#define IDD_CREATE 2015
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 2016
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1024
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/protocols/WinPopup/src/scanner.cpp b/protocols/WinPopup/src/scanner.cpp
new file mode 100644
index 0000000000..5074e97a5f
--- /dev/null
+++ b/protocols/WinPopup/src/scanner.cpp
@@ -0,0 +1,208 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+contact_scanner pluginScanner;
+
+contact_scanner::contact_scanner () :
+ m_ScannerTerm (NULL),
+ m_Scanner (NULL)
+{
+}
+
+contact_scanner::~contact_scanner ()
+{
+ Destroy ();
+}
+
+bool contact_scanner::Create ()
+{
+ // Запуск сканера контактов
+ if (m_ScannerTerm)
+ ResetEvent (m_ScannerTerm);
+ else
+ m_ScannerTerm = CreateEvent (NULL, TRUE, FALSE, NULL);
+
+ if ( ! m_Scanner )
+ m_Scanner = (HANDLE)mir_forkthread( ScannerThread, this );
+
+ return ( m_Scanner != NULL );
+}
+
+void contact_scanner::AskForDestroy()
+{
+ if (m_ScannerTerm)
+ SetEvent (m_ScannerTerm);
+}
+
+void contact_scanner::Destroy ()
+{
+ AskForDestroy();
+
+ if (m_Scanner)
+ {
+ if (WaitForSingleObject (m_Scanner, ALMOST_INFINITE) == WAIT_TIMEOUT)
+ {
+ LOG("Terminate scanner!");
+ TerminateThread (m_Scanner, 0);
+ }
+ m_Scanner = NULL;
+ }
+
+ if (m_ScannerTerm) {
+ CloseHandle (m_ScannerTerm);
+ m_ScannerTerm = NULL;
+ }
+}
+
+contact_scanner::operator bool () const
+{
+ return ( m_Scanner != NULL );
+}
+
+// First, Next, ... Next, NULL, First, Next...
+HANDLE contact_scanner::GetNextScannableContact()
+{
+ static HANDLE hContact = NULL;
+ if (!hContact)
+ hContact = (HANDLE) CallService (MS_DB_CONTACT_FINDFIRST, 0, 0);
+
+ // Циклический перебор контактов
+ while( hContact )
+ {
+ // Проверка на совпадение протокола контакта
+ if ( IsMyContact( hContact ) &&
+ !DBGetContactSettingByte (hContact, "CList", "NotOnList", 0) &&
+ !DBGetContactSettingByte (hContact, "CList", "Hidden", 0) )
+ {
+ // Вычисление сколько секунд прошло со времени последнего
+ // обновления статуса контакта. Нужна проверка?
+ DWORD elapsed = GetElapsed (hContact, "LastSeen");
+ if ( elapsed >= MIN_PING_INTERVAL )
+ break;
+ }
+ hContact = (HANDLE) CallService (MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ return hContact;
+}
+
+int contact_scanner::ScanContact(HANDLE hContact)
+{
+ if ( ! pluginInstalled )
+ return ID_STATUS_OFFLINE;
+
+#ifdef CHAT_ENABLED
+ if ( IsChatRoom( hContact ) )
+ {
+ CString sSession = GetChatSession( hContact );
+ if ( pluginChatEnabled && ! sSession.IsEmpty() )
+ {
+ CAtlList< CString > lst;
+ EnumWorkgroups( lst );
+ for ( POSITION pos = lst.GetHeadPosition(); pos; )
+ {
+ if ( lst.GetNext( pos ) == sSession )
+ return ID_STATUS_ONLINE;
+ }
+ }
+ return ID_STATUS_OFFLINE;
+ }
+ else
+#endif // CHAT_ENABLED
+ {
+ int status = ID_STATUS_OFFLINE;
+
+ // Получение статуса "Always Online"
+ if ( DBGetContactSettingByte( hContact, modname, "AlwaysOnline", FALSE ) )
+ status = ID_STATUS_ONLINE;
+
+ // Получение имени контакта
+ CString sNick = GetNick( hContact );
+ if ( ! sNick.IsEmpty() )
+ {
+ if ( IsGroup( hContact ) )
+ {
+ // Имя "ВСЕ" вегда онлайн
+ if ( sNick == _T("*") )
+ return ID_STATUS_ONLINE;
+
+ // Перечисление групп и сличение с нашей
+ CAtlList< CString > lst;
+ EnumWorkgroups( lst );
+ for ( POSITION pos = lst.GetHeadPosition(); pos; )
+ {
+ if ( lst.GetNext( pos ).CompareNoCase( sNick ) == 0 )
+ return ID_STATUS_ONLINE;
+ }
+ }
+ else if ( IsLegacyOnline( NULL ) || IsLegacyOnline( hContact ) )
+ {
+ // Синхронный опрос хоста на наличие NetBIOS-имени "Nick <00> U"
+ netbios_name nname( sNick, 0x00, false );
+ UCHAR foo;
+ if ( pluginNetBIOS.FindNameLana( nname, foo ) )
+ {
+ status = ID_STATUS_ONLINE;
+
+ // Асинхронный опрос контакта "Nick <00> U"
+ pluginNetBIOS.AskStatus( nname );
+ }
+ }
+ else
+ {
+ // Синхронный опрос хоста на наличие NetBIOS-имени "Nick <03> U"
+ netbios_name nname( sNick, 0x03, false );
+ UCHAR foo;
+ if ( pluginNetBIOS.FindNameLana( nname, foo ) )
+ {
+ status = ID_STATUS_ONLINE;
+
+ // Асинхронный опрос контакта "Nick <03> U"
+ pluginNetBIOS.AskStatus( nname );
+ }
+ }
+ }
+ return status;
+ }
+}
+
+void contact_scanner::Scanner ()
+{
+ while (WaitForSingleObject (m_ScannerTerm, 1000) == WAIT_TIMEOUT)
+ {
+ // Выборка следующего кандидата на проверку
+ if ( HANDLE hContact = GetNextScannableContact() )
+ {
+ // Проверка контакта
+ SetContactStatus( hContact, ScanContact( hContact ), true );
+ }
+ }
+}
+
+void contact_scanner::ScannerThread (LPVOID lpParameter)
+{
+ if ( contact_scanner* pScanner = (contact_scanner*)lpParameter )
+ {
+ pScanner->Scanner();
+ pScanner->m_Scanner = NULL;
+ }
+}
diff --git a/protocols/WinPopup/src/scanner.h b/protocols/WinPopup/src/scanner.h
new file mode 100644
index 0000000000..b184be0448
--- /dev/null
+++ b/protocols/WinPopup/src/scanner.h
@@ -0,0 +1,47 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+class contact_scanner
+{
+public:
+ contact_scanner ();
+ ~contact_scanner ();
+
+ bool Create ();
+ void AskForDestroy(); // Для ускорения
+ void Destroy ();
+ operator bool () const;
+
+ // Проверка статуса контакта (асинхронная и синхронная)
+ static int ScanContact(HANDLE hContact);
+
+protected:
+ HANDLE m_ScannerTerm; // Событие для остановки
+ HANDLE m_Scanner; // Хэндлер нити периодической проверки контактов
+
+ // Получить следующий контакт нуждающийся в проверке
+ static HANDLE GetNextScannableContact ();
+ // Рабочая нить цикла периодической проверки контактов
+ void Scanner ();
+ static void ScannerThread (LPVOID lpParameter);
+};
+
+extern contact_scanner pluginScanner;
diff --git a/protocols/WinPopup/src/search.cpp b/protocols/WinPopup/src/search.cpp
new file mode 100644
index 0000000000..0599419c88
--- /dev/null
+++ b/protocols/WinPopup/src/search.cpp
@@ -0,0 +1,317 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+search pluginSearch;
+
+// Функция простых регулярных выражений
+bool MatchPattern (LPCTSTR String, LPCTSTR Pattern);
+// Получение NetBIOS-имен хоста и проверка их при помощи простых регулярных выражений
+bool MatchPatternNetBIOS (LPCTSTR Host, LPCTSTR Pattern);
+
+search::search()
+ : m_count( 0 )
+ , m_event( NULL )
+{
+}
+
+INT_PTR search::StartSearch(LPCTSTR szId)
+{
+ LOG("Search \"%s\"", szId);
+
+ if (m_event)
+ // Запрос на останов предыдущего поиска
+ SetEvent (m_event);
+ else
+ m_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+
+ // Начало нового поиска
+ if ( BasicSearchData* data = new BasicSearchData )
+ {
+ data->me = this;
+ HANDLE cookie = data->cookie = GenerateCookie();
+ data->id = szId;
+ data->id.MakeUpper();
+ data->root = NULL;
+ mir_forkthread( BasicSearchThread, data );
+ return (INT_PTR)cookie;
+ }
+
+ return 0;
+}
+
+void search::AskForDestroy()
+{
+ if ( m_event )
+ SetEvent( m_event );
+}
+
+void search::Destroy()
+{
+ if ( m_event )
+ {
+ while( m_count )
+ {
+ SetEvent( m_event );
+ Sleep( 250 );
+ }
+ CloseHandle( m_event );
+ m_event = NULL;
+ }
+}
+
+bool MatchPattern (LPCTSTR String, LPCTSTR Pattern)
+{
+ TCHAR c, p, l;
+ for (;;)
+ {
+ switch ( p = *Pattern++ )
+ {
+ case 0:
+ // end of pattern
+ return *String ? false : true; // if end of string TRUE
+
+ case _T('*'):
+ // match zero or more char
+ while (*String)
+ if (MatchPattern (String++, Pattern))
+ return true;
+ return MatchPattern (String, Pattern);
+
+ case _T('?'):
+ // match any one char
+ if (*String++ == 0)
+ return false; // not end of string
+ break;
+
+ case _T('['):
+ // match char set
+ if ((c = *String++) == 0)
+ return false; // syntax
+ l = 0;
+ if (*Pattern == _T('!'))
+ { // match a char if NOT in set []
+ ++Pattern;
+ while ((p = *Pattern++)!= _T('\0'))
+ {
+ if (p == _T(']')) // if end of char set, then
+ break; // no match found
+ if (p == _T('-'))
+ { // check a range of chars?
+ p = *Pattern;
+ // get high limit of range
+ if (p == 0 || p == _T(']'))
+ return false; // syntax
+ if (c >= l && c <= p)
+ return false; // if in range
+ }
+ l = p;
+ // if char matches this element
+ if (c == p)
+ return false;
+ }
+ }
+ else
+ { // match if char is in set []
+ while ((p = *Pattern++) != _T('\0'))
+ {
+ if (p == _T(']')) // if end of char set, then
+ return false; // no match found
+ if (p == _T('-')) { // check a range of chars?
+ p = *Pattern;
+ // get high limit of range
+ if (p == 0 || p == _T(']'))
+ return false; // syntax
+ if (c >= l && c <= p)
+ break; // if in range, move on
+ }
+ l = p;
+ // if char matches this element
+ if (c == p)
+ break; // move on
+ }
+ while (p && p != _T(']')) // got a match in char set
+ p = *Pattern++; // skip to end of set
+ }
+ break;
+
+ case _T('#'):
+ c = *String++;
+ if (c < _T('0') || c > _T('9'))
+ return false; // not a digit
+ break;
+
+ default:
+ // check for exact char
+ c = *String++;
+ if (c != p)
+ return false; // not a match
+ break;
+ }
+ }
+}
+
+bool MatchPatternNetBIOS (LPCTSTR Host, LPCTSTR Pattern)
+{
+ netbios_name_list names;
+ if ( pluginNetBIOS.GetNames( names, Host, false ) )
+ {
+ POSITION pos = names.GetHeadPosition ();
+ CString n;
+ while ( pos )
+ {
+ netbios_name& name = names.GetNext (pos);
+ if ( name.GetType() == 3 )
+ {
+ CA2T sName( name.original );
+ if ( MatchPattern( (LPCTSTR)sName, Pattern ) )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void search::BasicSearchJob(const BasicSearchData* data)
+{
+ if (WaitForSingleObject (m_event, 0) != WAIT_TIMEOUT)
+ return;
+
+ HANDLE hEnum = NULL;
+ DWORD res = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
+ RESOURCEUSAGE_CONTAINER, data->root, &hEnum);
+ if (res == NO_ERROR)
+ {
+ for (;;)
+ {
+ if (WaitForSingleObject (m_event, 0) != WAIT_TIMEOUT)
+ return;
+
+ DWORD cCount = 1;
+ DWORD BufferSize = 4096;
+ char* Buffer = (char*)mir_alloc( BufferSize );
+ if ( ! Buffer )
+ break;
+ res = WNetEnumResource( hEnum, &cCount, Buffer, &BufferSize );
+ if ( res == NO_ERROR )
+ {
+ if (WaitForSingleObject (m_event, 0) != WAIT_TIMEOUT)
+ return;
+
+ LPNETRESOURCE lpnr = (LPNETRESOURCE)Buffer;
+ if ( lpnr->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER )
+ {
+ // Сверка названия контакта
+ // имя компьютера, комментарий, имя рабочей группы
+ CharUpper (lpnr->lpRemoteName);
+ if (MatchPattern (lpnr->lpRemoteName + 2, data->id) ||
+ (lpnr->lpComment && MatchPattern (lpnr->lpComment, data->id)) ||
+ (data->root && MatchPattern (data->root->lpRemoteName, data->id)) ||
+ MatchPatternNetBIOS (lpnr->lpRemoteName + 2, data->id))
+ {
+ // Добавление контакта
+ PROTOSEARCHRESULT psr = {};
+ psr.cbSize = sizeof( PROTOSEARCHRESULT );
+ psr.nick = lpnr->lpRemoteName + 2;
+ psr.firstName = lpnr->lpComment;
+ psr.lastName = data->root ? data->root->lpRemoteName : _T("");
+ psr.email = _T("");
+ ProtoBroadcastAck (modname, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA,
+ data->cookie, (LPARAM) &psr);
+ }
+ }
+ else
+ {
+ if ( ( lpnr->dwUsage & 0xffff ) == RESOURCEUSAGE_CONTAINER )
+ {
+ if ( BasicSearchData* data1 = new BasicSearchData )
+ {
+ data1->me = data->me;
+ data1->cookie = data->cookie;
+ data1->id = data->id;
+ data1->root = lpnr;
+ Buffer = NULL;
+ res = (DWORD)InterlockedIncrement (&m_count);
+ mir_forkthread( BasicSearchThread, data1 );
+ }
+ }
+ }
+ mir_free( Buffer );
+ }
+ else
+ {
+ mir_free( Buffer );
+ break;
+ }
+ }
+ WNetCloseEnum (hEnum);
+ }
+}
+
+void search::BasicSearchThread(LPVOID param)
+{
+ if ( BasicSearchData* data = (BasicSearchData*)param )
+ {
+ data->me->BasicSearch( data );
+ if ( data->root ) mir_free( data->root );
+ delete data;
+ }
+}
+
+void search::BasicSearch(const BasicSearchData* data)
+{
+ // Повторный запуск?
+ if ( data->root == NULL )
+ {
+ while( m_count )
+ {
+ if ( ! pluginInstalled )
+ return;
+
+ // Уже есть поиск в процессе, ожидание останова
+ Sleep (100);
+ }
+ InterlockedIncrement( &m_count );
+ ResetEvent( m_event );
+ }
+
+ // С плагином всё в порядке?
+ if ( pluginInstalled )
+ {
+ BasicSearchJob( data );
+
+ LONG res = InterlockedDecrement( &m_count );
+ _ASSERTE( res >= 0 );
+ if ( res == 0 )
+ {
+ // Поиск завершён штатно?
+ if ( WaitForSingleObject( m_event, 0 ) != WAIT_OBJECT_0 )
+ {
+ ProtoBroadcastAck (modname, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, data->cookie, 0);
+ }
+ else
+ {
+ LOG("Search aborted by another search");
+ }
+ }
+ }
+}
diff --git a/protocols/WinPopup/src/search.h b/protocols/WinPopup/src/search.h
new file mode 100644
index 0000000000..463845367d
--- /dev/null
+++ b/protocols/WinPopup/src/search.h
@@ -0,0 +1,58 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+class search
+{
+public:
+ search();
+
+ // Начало простого поиска
+ INT_PTR StartSearch(LPCTSTR szId);
+
+ // Запрос останова поиска
+ void AskForDestroy();
+
+ // Ожидание останова поиска
+ void Destroy();
+
+protected:
+ typedef struct _BasicSearchData
+ {
+ search* me; // Хэндлер хозяина поиска
+ HANDLE cookie; // Идентификатор запроса ядра миранды для ответа
+ CString id; // Что ищем (uppercase)
+ LPNETRESOURCE root; // Данные корневого контейнера (mir_alloc)
+ } BasicSearchData;
+
+ HANDLE m_event; // Хэндлер события для останова поиска
+ volatile LONG m_count; // Счётчик запущенных поисков
+
+ // Функция выполения поиска
+ void BasicSearch(const BasicSearchData* data);
+
+ // Функция выполения рекурсивного поиска
+ void BasicSearchJob(const BasicSearchData* data);
+
+ // Рабочая нить асинхронного поиска
+ static void BasicSearchThread(LPVOID param);
+};
+
+extern search pluginSearch;
diff --git a/protocols/WinPopup/src/services.cpp b/protocols/WinPopup/src/services.cpp
new file mode 100644
index 0000000000..7bb482dbbe
--- /dev/null
+++ b/protocols/WinPopup/src/services.cpp
@@ -0,0 +1,879 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Открывает "\\Nick" при помощи ShellExecute() ("Nick" из контакта)
+// wParam=hContact
+// lParam=0
+// Возвращает 0
+#define PS_EXPLORE "/Explore"
+
+// Показывает диалог для ручного добавления контакта по имени/адресу
+// wParam=Position
+// lParam=0
+// Возвращает 0
+#define PS_ADDDIALOG "/AddDialog"
+
+/////////////////////////////////////////////////////////////////////////////
+// Сервисные функции
+
+// WinPopup Protocol/Explore
+INT_PTR Explore(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/AddDialog
+INT_PTR AddDialog(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/GetCaps
+INT_PTR GetCaps(WPARAM flagNum, LPARAM lParam);
+// WinPopup Protocol/GetName
+INT_PTR GetName(WPARAM cchName, LPARAM szName);
+// WinPopup Protocol/LoadIcon
+INT_PTR LoadIcon(WPARAM whichIcon, LPARAM lParam);
+// WinPopup Protocol/SetStatus
+INT_PTR SetStatus(WPARAM newStatus, LPARAM lParam);
+// WinPopup Protocol/SetAwayMsg
+INT_PTR SetAwayMsg(WPARAM status_mode, LPARAM szMessage);
+// WinPopup Protocol/GetAwayMsg
+INT_PTR GetAwayMsg(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/GetStatus
+INT_PTR GetStatus(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/GetInfo
+INT_PTR GetInfo(WPARAM flags, LPARAM lParam);
+// WinPopup Protocol/RecvMessage
+INT_PTR RecvMessage(WPARAM flags, LPARAM lParam);
+// WinPopup Protocol/SendMsg
+INT_PTR SendMsg(WPARAM flags, LPARAM lParam);
+// WinPopup Protocol/BasicSearch
+INT_PTR BasicSearch(WPARAM wParam, LPARAM szId);
+// WinPopup Protocol/AddToList
+INT_PTR AddToList(WPARAM flags, LPARAM lParam);
+// WinPopup Protocol/GetAvatarCaps
+INT_PTR GetAvatarCaps(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/GetAvatarInformation
+INT_PTR GetAvatarInfo(WPARAM flags, LPARAM lParam);
+// WinPopup Protocol/SetMyAvatar
+INT_PTR SetMyAvatar(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/GetMyAvatar
+INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam);
+// WinPopup Protocol/CreateAccMgrUI
+INT_PTR CreateAccMgrUI(WPARAM wParam, LPARAM lParam);
+
+/////////////////////////////////////////////////////////////////////////////
+// Перехватчики
+
+// Перехватчик ME_SYSTEM_MODULESLOADED
+int __cdecl SYSTEM_MODULESLOADED(WPARAM wParam, LPARAM lParam);
+// Перехватчик ME_SYSTEM_PRESHUTDOWN
+int __cdecl SYSTEM_PRESHUTDOWN(WPARAM wParam, LPARAM lParam);
+
+/////////////////////////////////////////////////////////////////////////////
+// Данные плагина
+
+int hLangpack;
+
+const PROTOCOLDESCRIPTOR pluginPD =
+{
+ sizeof( PROTOCOLDESCRIPTOR ),
+ modname,
+ PROTOTYPE_PROTOCOL
+};
+
+static PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ modname,
+ PLUGIN_MAKE_VERSION (0,0,0,18),
+ "Allows you to send and receive messages over Microsoft LAN. "
+ "WinPopup and Net Send replacement powered by Miranda IM.",
+ "Nikolay Raspopov",
+ "ryo-oh-ki@narod.ru",
+ "© 2004-2010 Nikolay Raspopov",
+ "http://www.cherubicsoft.com/miranda/",
+ UNICODE_AWARE,
+ // {DE6EE412-ACE3-45db-A329-D618FABB4291}
+ {0xde6ee412, 0xace3, 0x45db, {0xa3, 0x29, 0xd6, 0x18, 0xfa, 0xbb, 0x42, 0x91}}
+};
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST};
+
+HANDLE plugin_FnGetCaps = NULL;
+HANDLE plugin_FnGetName = NULL;
+HANDLE plugin_FnLoadIcon = NULL;
+HANDLE plugin_FnSetStatus = NULL;
+HANDLE plugin_FnGetStatus = NULL;
+HANDLE plugin_FnGetAwayMsg = NULL;
+HANDLE plugin_FnSetAwayMsg = NULL;
+HANDLE plugin_FnGetInfo = NULL;
+HANDLE plugin_FnSendMsg = NULL;
+HANDLE plugin_FnRecvMessage = NULL;
+HANDLE plugin_FnBasicSearch = NULL;
+HANDLE plugin_FnAddToList = NULL;
+HANDLE plugin_FnExplore = NULL;
+HANDLE plugin_FnAddDialog = NULL;
+HANDLE plugin_FnGetAvatarCaps = NULL;
+HANDLE plugin_FnGetAvatarInfo = NULL;
+HANDLE plugin_FnSetMyAvatar = NULL;
+HANDLE plugin_FnGetMyAvatar = NULL;
+HANDLE plugin_FnCreateAccMgrUI = NULL;
+HANDLE plugin_SYSTEM_MODULESLOADED = NULL;
+HANDLE plugin_SYSTEM_PRESHUTDOWN = NULL;
+HANDLE plugin_OPT_INITIALISE = NULL;
+HANDLE plugin_USERINFO_INITIALISE = NULL;
+
+/////////////////////////////////////////////////////////////////////////////
+// Динамические функции загружаемые DllLoaderом
+
+DllLoader pluginNetapi32 ( _T("netapi32.dll"), false);
+DllLoader pluginAdvapi32 ( _T("advapi32.dll"), false);
+FuncPtrType(NET_API_STATUS (NET_API_FUNCTION *) (LMSTR, DWORD, LPBYTE*) ) fnNetWkstaGetInfo;
+FuncPtrType(DWORD (NET_API_FUNCTION *) (LPVOID) ) fnNetApiBufferFree;
+FuncPtrType(DWORD (NET_API_FUNCTION *) (LPCWSTR, LPCWSTR, LPCWSTR, LPBYTE, DWORD) ) fnNetMessageBufferSend;
+FuncPtrType(SC_HANDLE (WINAPI *) (LPCTSTR, LPCTSTR, DWORD) ) fnOpenSCManager;
+FuncPtrType(SC_HANDLE (WINAPI *) (SC_HANDLE, LPCTSTR, DWORD) ) fnOpenService;
+FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, DWORD, LPSERVICE_STATUS) ) fnControlService;
+FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, LPSERVICE_STATUS) ) fnQueryServiceStatus;
+FuncPtrType(BOOL (WINAPI *) (SC_HANDLE) ) fnCloseServiceHandle;
+FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, DWORD, LPCTSTR*) ) fnStartService;
+FuncPtrType(SC_LOCK (WINAPI *) (SC_HANDLE) ) fnLockServiceDatabase;
+FuncPtrType(BOOL (WINAPI *) (SC_LOCK) ) fnUnlockServiceDatabase;
+FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, DWORD, DWORD, DWORD, LPCTSTR, LPCTSTR, LPDWORD, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR) ) fnChangeServiceConfig;
+
+#define CreateHook( hService, szName, Function ) \
+ _ASSERT( hService == NULL ); \
+ hService = HookEvent( szName, Function ); \
+ _ASSERT ( hService != NULL ); \
+ if ( ! hService ) return 1;
+
+#define DestroyHook( hService ) \
+ if ( hService ) { \
+ UnhookEvent( hService ); \
+ hService = NULL; \
+ }
+
+#define CreateServiceFn( hService, szName, Function ) \
+ _ASSERT( hService == NULL ); \
+ hService = CreateServiceFunction( modname szName, Function ); \
+ _ASSERT( hService != NULL ); \
+ if ( ! hService ) return 1;
+
+#ifdef _DEBUG
+ #define DestroyServiceFn( hService ) \
+ if( hService ) { \
+ _ASSERT( DestroyServiceFunction( hService ) == 0 ); \
+ hService = NULL; \
+ }
+#else // _DEBUG
+ #define DestroyServiceFn( hService ) \
+ if( hService ) { \
+ DestroyServiceFunction( hService ); \
+ hService = NULL; \
+ }
+#endif // _DEBUG
+
+
+INT_PTR Explore(WPARAM wParam, LPARAM /* lParam */)
+{
+ CString sNick = GetNick( (HANDLE)wParam );
+ if ( pluginInstalled && ! sNick.IsEmpty() )
+ {
+ // Вызов UNC имени: \\Nick
+ CString cmd( _T("\\\\") );
+ cmd += sNick;
+ ShellExecute( NULL, NULL, cmd, NULL, NULL, SW_SHOWDEFAULT );
+ }
+ return 0;
+}
+
+INT_PTR AddDialog (WPARAM /* wParam */, LPARAM /* lParam */)
+{
+ AddDialog( NULL );
+ return 0;
+}
+
+INT_PTR GetCaps (WPARAM flagNum, LPARAM /* lParam */)
+{
+ INT_PTR nReturn = 0;
+ switch ( flagNum )
+ {
+ case PFLAGNUM_1:
+ nReturn = PF1_IM | PF1_BASICSEARCH | PF1_MODEMSG | PF1_PEER2PEER;
+ break;
+
+ case PFLAGNUM_2:
+ case PFLAGNUM_3:
+ nReturn = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY |
+ PF2_LIGHTDND | PF2_HEAVYDND | PF2_FREECHAT |
+ PF2_ONTHEPHONE | PF2_OUTTOLUNCH;
+ break;
+
+ case PFLAGNUM_4:
+ nReturn = PF4_AVATARS;
+ break;
+
+ case PFLAGNUM_5:
+ nReturn = 0;
+ break;
+
+ case PFLAG_UNIQUEIDTEXT:
+ nReturn = (INT_PTR)Translate ("Computer,User,Group");
+ break;
+
+ case PFLAG_UNIQUEIDSETTING:
+ nReturn = (INT_PTR)"Nick";
+ break;
+
+ case PFLAG_MAXLENOFMESSAGE:
+ // HANDLE hContact = (HANDLE) lParam;
+ nReturn = 0x7fffffff;
+ break;
+ }
+ return nReturn;
+}
+
+INT_PTR GetName(WPARAM cchName, LPARAM szName)
+{
+ lstrcpynA( (char*) szName, modtitle, static_cast <int>( cchName ) );
+ return 0;
+}
+
+INT_PTR LoadIcon(WPARAM whichIcon, LPARAM /* lParam */)
+{
+ UINT id;
+ switch ( whichIcon & 0xFFFF )
+ {
+ case PLI_PROTOCOL:
+ id = IDI_WINPOPUP;
+ break;
+
+ case PLI_ONLINE:
+ id = IDI_ONLINE;
+ break;
+
+ case PLI_OFFLINE:
+ id = IDI_OFFLINE;
+ break;
+
+ default:
+ return 0;
+ }
+ return (INT_PTR)LoadImage (pluginModule, MAKEINTRESOURCE (id), IMAGE_ICON,
+ (whichIcon & PLIF_SMALL ? 16 : 32), (whichIcon & PLIF_SMALL ? 16 : 32), 0);
+}
+
+INT_PTR SetStatus (WPARAM newStatus, LPARAM /* lParam */)
+{
+ if ( pluginInstalled && pluginCurrentStatus != (int)newStatus )
+ {
+ LOG ("SetStatus from \"%s\" to \"%s\"",
+ STATUS2TEXT(pluginCurrentStatus), STATUS2TEXT(newStatus));
+
+ pluginRequestedStatus = newStatus;
+ switch (newStatus) {
+ case ID_STATUS_OFFLINE:
+ GotoOffline ();
+ break;
+ case ID_STATUS_FREECHAT:
+ case ID_STATUS_INVISIBLE:
+ newStatus = ID_STATUS_ONLINE;
+ case ID_STATUS_ONLINE:
+ case ID_STATUS_AWAY:
+ case ID_STATUS_DND:
+ case ID_STATUS_NA:
+ case ID_STATUS_OCCUPIED:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ default:
+ GotoOnline ();
+ break;
+ }
+ }
+ return 0;
+}
+
+INT_PTR SetAwayMsg (WPARAM status_mode, LPARAM szMessage)
+{
+ pluginStatusMessage[ status_mode ] = ( szMessage ? (LPCSTR) szMessage : "" );
+ return 0;
+}
+
+INT_PTR GetAwayMsg(WPARAM /* wParam */, LPARAM lParam)
+{
+ CCSDATA *ccs = (CCSDATA*)lParam;
+ if ( pluginInstalled && ccs && ccs->hContact )
+ {
+ if ( ContactData* data = new ContactData )
+ {
+ data->hContact = ccs->hContact;
+ HANDLE cookie = data->cookie = GenerateCookie();
+ mir_forkthread( GetAwayMsgThread, data );
+ return (INT_PTR)cookie;
+ }
+ }
+ return 0;
+}
+
+INT_PTR GetStatus (WPARAM /* wParam */, LPARAM /* lParam */)
+{
+ return (INT_PTR)pluginCurrentStatus;
+}
+
+INT_PTR GetInfo (WPARAM /* flags */, LPARAM lParam)
+{
+ CCSDATA* ccs = (CCSDATA*)lParam;
+ if ( pluginInstalled && ccs && ccs->hContact )
+ {
+ mir_forkthread( GetInfoThread, ccs->hContact );
+ return 0;
+ }
+ return 1;
+}
+
+INT_PTR RecvMessage (WPARAM /* flags */, LPARAM lParam)
+{
+ CCSDATA* ccs = (CCSDATA*) lParam;
+ if ( pluginInstalled && ccs && ccs->hContact && ccs->lParam)
+ {
+ PROTORECVEVENT *pre = (PROTORECVEVENT*) ccs->lParam;
+
+ // Добавление сообщения
+ DBDeleteContactSetting (ccs->hContact, "CList", "Hidden");
+ DBEVENTINFO ei = { 0 };
+ ei.cbSize = sizeof (DBEVENTINFO);
+ ei.szModule = modname;
+ ei.timestamp = pre->timestamp;
+ ei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0u;
+ ei.eventType = EVENTTYPE_MESSAGE;
+ ei.cbBlob = (DWORD)( lstrlenA (pre->szMessage) + 1 );
+ ei.pBlob = (PBYTE) pre->szMessage;
+ CallService (MS_DB_EVENT_ADD, (WPARAM) ccs->hContact, (LPARAM) &ei);
+ }
+ return 0;
+}
+
+INT_PTR SendMsg(WPARAM /* flags */, LPARAM lParam)
+{
+ CCSDATA* ccs = (CCSDATA*) lParam;
+ if ( pluginInstalled && ccs && ccs->hContact && ccs->lParam )
+ {
+ if ( SendMsgData* data = new SendMsgData )
+ {
+ data->hContact = ccs->hContact;
+ data->text = (LPCSTR)ccs->lParam;
+ HANDLE cookie = data->cookie = GenerateCookie();
+ mir_forkthread( SendMsgThread, data );
+ return (INT_PTR)cookie;
+ }
+ }
+ return 0;
+}
+
+INT_PTR BasicSearch(WPARAM /* wParam */, LPARAM szId)
+{
+ if ( pluginInstalled && szId )
+ {
+ return pluginSearch.StartSearch( CA2T( (LPCSTR)szId ) );
+ }
+ return 0;
+}
+
+INT_PTR AddToList(WPARAM flags, LPARAM lParam)
+{
+ PROTOSEARCHRESULT* psr = (PROTOSEARCHRESULT*) lParam;
+ if ( psr && psr->cbSize >= sizeof (PROTOSEARCHRESULT) && psr->nick && *psr->nick )
+ {
+ CString sName( psr->nick );
+ CString sNotes( psr->firstName );
+ return (INT_PTR)AddToListByName( sName, flags, sNotes, false, false );
+ }
+ return 0;
+}
+
+INT_PTR GetAvatarCaps(WPARAM wParam, LPARAM lParam)
+{
+ switch ( wParam )
+ {
+ case AF_MAXSIZE:
+ if ( POINT* size = (POINT*)lParam )
+ {
+ size->x = 300; // -1 - не работает из-за несоответствия avs.dll стандарту
+ size->y = 300; // -1 - не работает из-за несоответствия avs.dll стандарту
+ }
+ break;
+
+ case AF_PROPORTION:
+ return PIP_NONE;
+
+ case AF_FORMATSUPPORTED:
+ switch ( lParam )
+ {
+ case PA_FORMAT_UNKNOWN:
+ case PA_FORMAT_PNG:
+ case PA_FORMAT_JPEG:
+ case PA_FORMAT_ICON:
+ case PA_FORMAT_BMP:
+ case PA_FORMAT_GIF:
+ case PA_FORMAT_SWF:
+ case PA_FORMAT_XML:
+ return 1;
+ }
+ break;
+
+ case AF_ENABLED:
+ return 1;
+
+ case AF_DONTNEEDDELAYS:
+ return 1;
+
+ case AF_MAXFILESIZE:
+ return MAX_AVATAR_SIZE;
+
+ case AF_DELAYAFTERFAIL:
+ return 10 * 60 * 1000; // 10 минут
+ }
+
+ return 0;
+}
+
+INT_PTR GetAvatarInfo(WPARAM /*flags*/, LPARAM lParam)
+{
+ PROTO_AVATAR_INFORMATION* pai = (PROTO_AVATAR_INFORMATION*)lParam;
+ if ( pai && pai->cbSize >= sizeof( PROTO_AVATAR_INFORMATION ) )
+ {
+ if ( ContactData* data = new ContactData )
+ {
+ data->hContact = pai->hContact;
+ /*HANDLE cookie =*/ data->cookie = GenerateCookie();
+ mir_forkthread( GetAvatarInfoThread, data );
+ return GAIR_WAITFOR;
+ }
+ }
+ return GAIR_NOAVATAR;
+}
+
+INT_PTR SetMyAvatar(WPARAM /*wParam*/, LPARAM lParam)
+{
+ CA2T szFilename( (LPCSTR)lParam );
+
+ TCHAR szPath[ MAX_PATH ];
+ GetAvatarCache( szPath );
+
+ if ( szFilename )
+ {
+ // Сохранение аватара
+ lstrcat( szPath, _T("MyAvatar") );
+ lstrcat( szPath, _tcsrchr( szFilename, _T('.') ) );
+
+ if ( lstrcmpi( szPath, szFilename ) == 0 )
+ {
+ // Тот же самый файл - ничего не делаем
+ }
+ else
+ {
+ // Другой файл - копирование аватара к себе
+ SHFILEOPSTRUCT sfo = {};
+ sfo.hwnd = GetDesktopWindow();
+ sfo.wFunc = FO_COPY;
+ TCHAR szFrom[ MAX_PATH ] = {};
+ lstrcpy( szFrom, szFilename );
+ sfo.pFrom = szFrom;
+ sfo.pTo = szPath;
+ sfo.fFlags = FOF_ALLOWUNDO | FOF_FILESONLY | FOF_NORECURSION |
+ FOF_NOCONFIRMATION;
+ SHFileOperation( &sfo );
+ }
+
+ DBWriteContactSettingTString( NULL, modname, "AvatarFile",
+ _tcsrchr( szPath, _T('\\') ) + 1 );
+ }
+ else
+ {
+ // Удаление аватара в корзину
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( NULL, modname, "AvatarFile", &dbv ) )
+ {
+ lstrcat( szPath, dbv.ptszVal );
+
+ SHFILEOPSTRUCT sfo = {};
+ sfo.hwnd = GetDesktopWindow();
+ sfo.wFunc = FO_DELETE;
+ sfo.pFrom = szPath;
+ sfo.fFlags = FOF_ALLOWUNDO | FOF_FILESONLY | FOF_NORECURSION |
+ FOF_NOCONFIRMATION;
+ SHFileOperation( &sfo );
+
+ DBFreeVariant( &dbv );
+
+ DBDeleteContactSetting( NULL, modname, "AvatarFile" );
+ }
+ }
+
+ return 0;
+}
+
+INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam)
+{
+ LPSTR szFilename = (LPSTR)wParam;
+ int nLength = (int)lParam;
+ bool ret = false;
+
+ if ( szFilename == NULL || nLength < MAX_PATH )
+ return -1;
+
+ TCHAR szPath[ MAX_PATH ];
+ GetAvatarCache( szPath );
+
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( NULL, modname, "AvatarFile", &dbv ) )
+ {
+ lstrcat( szPath, dbv.ptszVal );
+
+ ret = ( GetFileAttributes( szPath ) != INVALID_FILE_ATTRIBUTES );
+
+ DBFreeVariant( &dbv );
+ }
+
+ if ( ! ret )
+ return -1;
+
+ lstrcpyA( szFilename, CT2A( szPath ) );
+
+ return 0;
+}
+
+static INT_PTR CALLBACK DlgProcCreateAccMgrUI(HWND hwndDlg, UINT Msg,
+ WPARAM /*wParam*/, LPARAM /*lParam*/)
+{
+ switch ( Msg )
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault( hwndDlg );
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+INT_PTR CreateAccMgrUI(WPARAM /*wParam*/, LPARAM lParam)
+{
+ return (INT_PTR)CreateDialogParam( pluginModule, MAKEINTRESOURCE( IDD_CREATE ),
+ (HWND)lParam, DlgProcCreateAccMgrUI, 0 );
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Интерфейсы плагина
+
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfoEx;
+}
+
+extern "C" int __declspec(dllexport) Load(void)
+{
+ mir_getLP(&pluginInfoEx);
+ GetVersionEx( &pluginOS );
+
+ if (pluginNetapi32.LoadLibrary (pluginModule))
+ {
+ pluginNetapi32.LoadFunc (fnNetWkstaGetInfo, "NetWkstaGetInfo");
+ pluginNetapi32.LoadFunc (fnNetApiBufferFree, "NetApiBufferFree");
+ pluginNetapi32.LoadFunc (fnNetMessageBufferSend, "NetMessageBufferSend");
+ }
+
+ if (pluginAdvapi32.LoadLibrary (pluginModule))
+ {
+ pluginAdvapi32.LoadFunc (fnOpenSCManager, "OpenSCManagerW");
+ pluginAdvapi32.LoadFunc (fnOpenService, "OpenServiceW");
+ pluginAdvapi32.LoadFunc (fnStartService, "StartServiceW");
+ pluginAdvapi32.LoadFunc (fnChangeServiceConfig, "ChangeServiceConfigW");
+ pluginAdvapi32.LoadFunc (fnControlService, "ControlService");
+ pluginAdvapi32.LoadFunc (fnQueryServiceStatus, "QueryServiceStatus");
+ pluginAdvapi32.LoadFunc (fnCloseServiceHandle, "CloseServiceHandle");
+ pluginAdvapi32.LoadFunc (fnLockServiceDatabase, "LockServiceDatabase");
+ pluginAdvapi32.LoadFunc (fnUnlockServiceDatabase, "UnlockServiceDatabase");
+ }
+
+ _ASSERT( pluginInternalState == NULL );
+ pluginInternalState = CreateEvent( NULL, TRUE, TRUE, NULL );
+ _ASSERT( pluginInternalState != NULL );
+
+ INT_PTR retCallService = CallService (MS_PROTO_REGISTERMODULE, 0, (LPARAM) &pluginPD);
+ _ASSERT (retCallService != CALLSERVICE_NOTFOUND);
+ if ( retCallService == CALLSERVICE_NOTFOUND ) return 1;
+
+ CreateServiceFn( plugin_FnGetCaps, PS_GETCAPS, GetCaps );
+ CreateServiceFn( plugin_FnGetName, PS_GETNAME, GetName );
+ CreateServiceFn( plugin_FnLoadIcon, PS_LOADICON, LoadIcon );
+ CreateServiceFn( plugin_FnSetStatus, PS_SETSTATUS, SetStatus );
+ CreateServiceFn( plugin_FnGetStatus, PS_GETSTATUS, GetStatus );
+ CreateServiceFn( plugin_FnSetAwayMsg, PS_SETAWAYMSG, SetAwayMsg );
+ CreateServiceFn( plugin_FnGetAwayMsg, PSS_GETAWAYMSG, GetAwayMsg );
+ CreateServiceFn( plugin_FnGetInfo, PSS_GETINFO, GetInfo );
+ CreateServiceFn( plugin_FnSendMsg, PSS_MESSAGE, SendMsg );
+ CreateServiceFn( plugin_FnRecvMessage, PSR_MESSAGE, RecvMessage );
+ CreateServiceFn( plugin_FnBasicSearch, PS_BASICSEARCH, BasicSearch );
+ CreateServiceFn( plugin_FnAddToList, PS_ADDTOLIST, AddToList );
+ CreateServiceFn( plugin_FnExplore, PS_EXPLORE, Explore );
+ CreateServiceFn( plugin_FnAddDialog, PS_ADDDIALOG, AddDialog);
+ CreateServiceFn( plugin_FnGetAvatarCaps, PS_GETAVATARCAPS, GetAvatarCaps );
+ CreateServiceFn( plugin_FnGetAvatarInfo, PS_GETAVATARINFO, GetAvatarInfo );
+ CreateServiceFn( plugin_FnSetMyAvatar, PS_SETMYAVATAR, SetMyAvatar );
+ CreateServiceFn( plugin_FnGetMyAvatar, PS_GETMYAVATAR, GetMyAvatar );
+ CreateServiceFn( plugin_FnCreateAccMgrUI, PS_CREATEACCMGRUI, CreateAccMgrUI );
+
+ CreateHook( plugin_SYSTEM_MODULESLOADED, ME_SYSTEM_MODULESLOADED, SYSTEM_MODULESLOADED );
+ CreateHook( plugin_SYSTEM_PRESHUTDOWN, ME_SYSTEM_PRESHUTDOWN, SYSTEM_PRESHUTDOWN );
+
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload()
+{
+#ifdef CHAT_ENABLED
+ ChatUnregister();
+#endif // CHAT_ENABLED
+
+ DestroyHook( plugin_USERINFO_INITIALISE );
+ DestroyHook( plugin_OPT_INITIALISE );
+ DestroyHook( plugin_SYSTEM_PRESHUTDOWN );
+ DestroyHook( plugin_SYSTEM_MODULESLOADED );
+
+ DestroyServiceFn( plugin_FnExplore );
+ DestroyServiceFn( plugin_FnAddDialog );
+ DestroyServiceFn( plugin_FnGetCaps );
+ DestroyServiceFn( plugin_FnGetName );
+ DestroyServiceFn( plugin_FnLoadIcon );
+ DestroyServiceFn( plugin_FnSetStatus );
+ DestroyServiceFn( plugin_FnGetStatus );
+ DestroyServiceFn( plugin_FnSetAwayMsg );
+ DestroyServiceFn( plugin_FnGetAwayMsg );
+ DestroyServiceFn( plugin_FnGetInfo );
+ DestroyServiceFn( plugin_FnSendMsg );
+ DestroyServiceFn( plugin_FnRecvMessage );
+ DestroyServiceFn( plugin_FnBasicSearch );
+ DestroyServiceFn( plugin_FnAddToList );
+ DestroyServiceFn( plugin_FnGetAvatarCaps );
+ DestroyServiceFn( plugin_FnGetAvatarInfo );
+ DestroyServiceFn( plugin_FnSetMyAvatar );
+ DestroyServiceFn( plugin_FnGetMyAvatar );
+ DestroyServiceFn( plugin_FnCreateAccMgrUI );
+
+ if ( pluginNetLibUser != NULL )
+ Netlib_CloseHandle( pluginNetLibUser );
+ pluginNetLibUser = NULL;
+
+ if ( pluginInternalState != NULL )
+ CloseHandle( pluginInternalState );
+ pluginInternalState = NULL;
+
+ pluginNetapi32.FreeLibrary();
+ pluginAdvapi32.FreeLibrary();
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Перехватчики
+
+int __cdecl SYSTEM_MODULESLOADED (WPARAM /* wParam */, LPARAM /* lParam */)
+{
+ _ASSERT (pluginInstalled == true);
+ _ASSERT (pluginInitialized == false);
+
+ _ASSERT (pluginNetLibUser == NULL);
+ const NETLIBUSER nlu =
+ {
+ sizeof( NETLIBUSER ),
+ modname,
+ modname,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0
+ };
+ pluginNetLibUser = (HANDLE)CallService( MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu );
+ _ASSERT (pluginNetLibUser);
+
+ CreateHook( plugin_OPT_INITIALISE, ME_OPT_INITIALISE, OPT_INITIALISE );
+ CreateHook( plugin_USERINFO_INITIALISE, ME_USERINFO_INITIALISE, USERINFO_INITIALISE );
+
+ // Установка иконок статусов
+ struct
+ {
+ int icon_id;
+ int status;
+ }
+ const StatusIcons [] =
+ {
+ { IDI_OFFLINE, ID_STATUS_OFFLINE },
+ { IDI_ONLINE, ID_STATUS_ONLINE },
+ { IDI_AWAY, ID_STATUS_AWAY },
+ { IDI_DND, ID_STATUS_DND },
+ { IDI_NA, ID_STATUS_NA },
+ { IDI_OCCUPIED, ID_STATUS_OCCUPIED },
+ { IDI_FREECHAT, ID_STATUS_FREECHAT },
+ { IDI_INVISIBLE, ID_STATUS_INVISIBLE },
+ { IDI_ONTHEPHONE, ID_STATUS_ONTHEPHONE },
+ { IDI_OUTTOLUNCH, ID_STATUS_OUTTOLUNCH },
+ { 0, 0 }
+ };
+ TCHAR path [ MAX_PATH * 2 ] = { 0 };
+ TCHAR icon [ 32 ] = { 0 };
+ DWORD len = GetModuleFileName (pluginModule, path, MAX_PATH);
+ lstrcpy( path + len, _T(",-") );
+ len += 2;
+ for (int i = 0; StatusIcons[i].icon_id ; i++)
+ {
+ wsprintf( path + len, _T("%d"), StatusIcons[i].icon_id );
+ wsprintf( icon, modname_t _T("%d"), StatusIcons[i].status );
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSetting( NULL, "Icons", CT2A( icon ), &dbv ) )
+ DBFreeVariant( &dbv );
+ else
+ DBWriteContactSettingTString( NULL, "Icons", CT2A( icon ), path );
+ }
+
+ // Определение имени компьютера
+ DWORD iMachineNameLength = MAX_COMPUTERNAME_LENGTH + 2;
+ GetComputerName(
+ pluginMachineName.GetBuffer( (int)iMachineNameLength ), &iMachineNameLength );
+ pluginMachineName.ReleaseBuffer();
+ SetNick( NULL, pluginMachineName );
+
+ // Определение имени пользователя
+ DWORD iUserNameLength = UNLEN + 2;
+ GetUserName(
+ pluginUserName.GetBuffer( (int)iUserNameLength ), &iUserNameLength );
+ pluginUserName.ReleaseBuffer();
+ DBWriteContactSettingTString( NULL, modname, "User", pluginUserName );
+
+ // Определение имени рабочей группы
+ if ( pluginOS.dwPlatformId == VER_PLATFORM_WIN32_NT )
+ {
+ WKSTA_INFO_100* info = NULL;
+ NET_API_STATUS err = fnNetWkstaGetInfo (NULL, 100, (LPBYTE*) &info);
+ if (err == NERR_Success && info)
+ {
+ LPTSTR langroup = mir_u2t( info->wki100_langroup );
+ pluginDomainName = langroup;
+ fnNetApiBufferFree (info);
+ mir_free( langroup );
+ }
+ // Альтернатива?
+ // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
+ // DefaultDomainName
+ }
+ else
+ {
+ HKEY hKey = NULL;
+ if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_LOCAL_MACHINE,
+ _T("System\\CurrentControlSet\\Services\\VxD\\VNETSUP"), 0, KEY_READ, &hKey))
+ {
+ DWORD type, size = MAX_PATH;
+ RegQueryValueEx (hKey, _T("Workgroup"), 0, &type,
+ (LPBYTE) pluginDomainName.GetBuffer (MAX_PATH + 1), &size);
+ pluginDomainName.ReleaseBuffer( (int)size );
+ RegCloseKey (hKey);
+ }
+ }
+ DBWriteContactSettingTString (NULL, modname, "Workgroup", pluginDomainName);
+
+ // Регистрация в Chat
+#ifdef CHAT_ENABLED
+ pluginChatEnabled = ChatRegister();
+#endif // CHAT_ENABLED
+
+ // Добавление меню
+ CLISTMENUITEM miExplore =
+ {
+ sizeof( CLISTMENUITEM ),
+ (LPSTR)TranslateT( "Explore" ),
+ CMIF_TCHAR,
+ 0,
+ (HICON)LoadImage( pluginModule, MAKEINTRESOURCE( IDI_EXPLORE ),
+ IMAGE_ICON, 16, 16, LR_SHARED ),
+ modname PS_EXPLORE,
+ NULL,
+ 0,
+ 0,
+ modname
+ };
+ Menu_AddContactMenuItem(&miExplore);
+
+ CLISTMENUITEM miAddContact =
+ {
+ sizeof( CLISTMENUITEM ),
+ NULL,
+ CMIF_TCHAR,
+ 500090000,
+ (HICON)LoadImage( pluginModule, MAKEINTRESOURCE( IDI_WINPOPUP ),
+ IMAGE_ICON, 16, 16, LR_SHARED ),
+ modname PS_ADDDIALOG,
+ NULL,
+ 0,
+ 0,
+ NULL
+ };
+ miAddContact.ptszName = (LPTSTR)TranslateT( "Add contact..." );
+ miAddContact.ptszPopupName = (LPTSTR)modtitle_t;
+
+ HANDLE hMenuItem = Menu_AddMainMenuItem(&miAddContact);
+ // Замена иконки
+ miAddContact.flags = CMIM_ICON;
+ miAddContact.hIcon = (HICON)LoadImage( pluginModule,
+ MAKEINTRESOURCE( IDI_ADD_COMPUTER ), IMAGE_ICON, 16, 16, LR_SHARED ),
+ CallService( MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItem, (LPARAM)&miAddContact );
+
+ // Разрешение на активные действия (например, переход в онлайн)
+ pluginInitialized = true;
+
+ // Восстановление статуса который мог быть проигнорирован
+ SetStatus( pluginRequestedStatus, 0 );
+
+ return 0;
+}
+
+int __cdecl SYSTEM_PRESHUTDOWN (WPARAM /* wParam */, LPARAM /* lParam */)
+{
+ // Запрет на активные действия (например, переход в онлайн)
+ pluginInstalled = false;
+
+ // Ожидание подтверждения останова, т.к. переход в оффлайн выполняется асинхронно
+ // Вызов из потока миранды, где было создано окно
+ do
+ {
+ MSG msg;
+ while ( PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE ) )
+ {
+ if ( IsDialogMessage( msg.hwnd, &msg ) ) continue;
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+ }
+ while ( MsgWaitForMultipleObjects( 1, &pluginInternalState, FALSE,
+ INFINITE, QS_ALLINPUT ) == WAIT_OBJECT_0 + 1 );
+
+ return 0;
+}
diff --git a/protocols/WinPopup/src/services.h b/protocols/WinPopup/src/services.h
new file mode 100644
index 0000000000..505f7fe409
--- /dev/null
+++ b/protocols/WinPopup/src/services.h
@@ -0,0 +1,34 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// Динамические функции загружаемые DllLoaderом
+extern FuncPtrType(NET_API_STATUS (NET_API_FUNCTION *) (LMSTR, DWORD, LPBYTE*) ) fnNetWkstaGetInfo;
+extern FuncPtrType(DWORD (NET_API_FUNCTION *) (LPVOID) ) fnNetApiBufferFree;
+extern FuncPtrType(DWORD (NET_API_FUNCTION *) (LPCWSTR, LPCWSTR, LPCWSTR, LPBYTE, DWORD) ) fnNetMessageBufferSend;
+extern FuncPtrType(SC_HANDLE (WINAPI *) (LPCTSTR, LPCTSTR, DWORD) ) fnOpenSCManager;
+extern FuncPtrType(SC_HANDLE (WINAPI *) (SC_HANDLE, LPCTSTR, DWORD) ) fnOpenService;
+extern FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, DWORD, LPSERVICE_STATUS) ) fnControlService;
+extern FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, LPSERVICE_STATUS) ) fnQueryServiceStatus;
+extern FuncPtrType(BOOL (WINAPI *) (SC_HANDLE) ) fnCloseServiceHandle;
+extern FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, DWORD, LPCTSTR*) ) fnStartService;
+extern FuncPtrType(SC_LOCK (WINAPI *) (SC_HANDLE) ) fnLockServiceDatabase;
+extern FuncPtrType(BOOL (WINAPI *) (SC_LOCK) ) fnUnlockServiceDatabase;
+extern FuncPtrType(BOOL (WINAPI *) (SC_HANDLE, DWORD, DWORD, DWORD, LPCTSTR, LPCTSTR, LPDWORD, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR) ) fnChangeServiceConfig;
diff --git a/protocols/WinPopup/src/smbconst.h b/protocols/WinPopup/src/smbconst.h
new file mode 100644
index 0000000000..32f6dd2851
--- /dev/null
+++ b/protocols/WinPopup/src/smbconst.h
@@ -0,0 +1,106 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public 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 SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtconX 0x75 /* tree connect and X*/
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread 0x13 /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* Unlock a range then write */
+#define SMBreadbraw 0x1a /* read a block of data with no smb header */
+#define SMBwritebraw 0x1d /* write a block of data with no smb header */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Extended 2.0 protocol */
+#define SMBtrans2 0x32 /* TRANS2 protocol set */
+#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */
+#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX 0x74 /* user logoff */
+
+/* NT SMB extensions. */
+#define SMBnttrans 0xA0 /* NT transact */
+#define SMBnttranss 0xA1 /* NT transact secondary */
+#define SMBntcreateX 0xA2 /* NT create and X */
+#define SMBntcancel 0xA4 /* NT cancel */
diff --git a/protocols/WinPopup/src/stdafx.cpp b/protocols/WinPopup/src/stdafx.cpp
new file mode 100644
index 0000000000..d994fedc70
--- /dev/null
+++ b/protocols/WinPopup/src/stdafx.cpp
@@ -0,0 +1,22 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2009 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
diff --git a/protocols/WinPopup/src/stdafx.h b/protocols/WinPopup/src/stdafx.h
new file mode 100644
index 0000000000..4c86a5164f
--- /dev/null
+++ b/protocols/WinPopup/src/stdafx.h
@@ -0,0 +1,122 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public 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 WINVER 0x0500 // Windows 2000 compatible
+#define _WIN32_WINNT 0x0500 // Windows 2000 compatible
+#define _WIN32_WINDOWS 0x0500 // Windows 2000 compatible
+#define _WIN32_IE 0x0500 // IE 5 compatible
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#define NOCOMM
+#define NOSERVICE
+#define NOHELP
+#define NOSOUND
+#define NOPRINT
+
+//#define _ATL_NO_COM_SUPPORT
+//#define _ATL_NO_EXCEPTIONS
+//#define _ATL_NO_AUTOMATIC_NAMESPACE
+//#define _ATL_CSTRING_NO_CRT
+//#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
+
+#define _CRT_SECURE_NO_WARNINGS
+
+// Common headers
+#include <atlbase.h>
+#include <atlstr.h>
+#include <atlcoll.h>
+#include <atlfile.h>
+
+#include <prsht.h>
+#include <winsock.h>
+#include <lm.h>
+#include <nb30.h>
+#include <process.h>
+#include <commctrl.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include <shellapi.h>
+
+// Miranda SDK headers
+#include "statusmodes.h"
+#include "newpluginapi.h" // uses m_plugins.h
+#include "m_system.h"
+#include "m_clist.h"
+#include "m_database.h"
+#include "m_langpack.h"
+#include "m_netlib.h" // uses m_utils.h
+#include "m_options.h"
+#include "m_popup.h"
+#include "m_protocols.h"
+#include "m_protomod.h"
+#include "m_protosvc.h"
+#include "m_userinfo.h"
+#include "m_chat.h"
+#include "m_avatars.h"
+
+inline LPSTR lstrnchr(LPSTR s, const CHAR c, int n) throw ()
+{
+ for (; n; --n, ++s)
+ if (c == *s)
+ return s;
+ return NULL;
+}
+
+inline LPTSTR lstrnrchr(LPTSTR s, const TCHAR c, int n) throw ()
+{
+ s += n;
+ for (; n; --n, --s)
+ if (c == *s)
+ return s;
+ return NULL;
+}
+
+#include "resource.h"
+#include "dllLoaderMinimal.h"
+#include "network.h"
+#include "winpopup_proto.h"
+#include "services.h"
+#include "mailslot.h"
+#include "netbios_name.h"
+#include "netbios.h"
+#include "scanner.h"
+#include "messenger.h"
+#include "messagebox.h"
+#include "search.h"
+#include "chat.h"
+#include "md5.h"
+#include "options.h"
+#include "user_info.h"
+#include "add_dialog.h"
+#include "processapi.h"
+#include "smbconst.h"
+
+using namespace ATL;
+
+typedef class CComCritSecLock< CComAutoCriticalSection > CLock;
+
+#ifdef _DEBUG
+ #define ALMOST_INFINITE (INFINITE) // бесконечность
+#else
+ #define ALMOST_INFINITE (20000) // 20 секунд
+#endif
diff --git a/protocols/WinPopup/src/user_info.cpp b/protocols/WinPopup/src/user_info.cpp
new file mode 100644
index 0000000000..3f47e8cb80
--- /dev/null
+++ b/protocols/WinPopup/src/user_info.cpp
@@ -0,0 +1,325 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 WM_FILLTREE (WM_USER+75)
+
+typedef struct _FillTreeThreadData
+{
+ HMODULE hContact;
+ HWND hwndDlg;
+} FillTreeThreadData;
+
+typedef struct _FillTreeData
+{
+ netbios_name_list nns;
+ CString host;
+ CString about;
+} FillTreeData;
+
+typedef struct _DlgDataUserInfo
+{
+ HMODULE hContact;
+ HIMAGELIST hTreeImages;
+ bool bWorking;
+} DlgDataUserInfo;
+
+static void FillTreeThread (LPVOID param)
+{
+ FillTreeThreadData* fttd = (FillTreeThreadData*)param;
+
+ // Получение имени контакта
+ CString sNick = GetNick( fttd->hContact );
+ if ( ! sNick.IsEmpty() )
+ {
+ bool bGroup = IsGroup( fttd->hContact );
+
+ // Опрос хоста
+ if ( FillTreeData* ftd = new FillTreeData )
+ {
+ ftd->host = sNick;
+
+ if ( ! bGroup )
+ // Запрос NetBIOS-имён
+ pluginNetBIOS.GetNames( ftd->nns, ftd->host, false );
+
+ // Запрос комментария
+ DWORD buf_size = 4096;
+ if ( NETRESOURCE* buf = (NETRESOURCE*)mir_alloc( buf_size ) )
+ {
+ CString remote( _T("\\\\") );
+ if ( bGroup )
+ remote = (LPCTSTR)ftd->host;
+ else
+ remote += (LPCTSTR)ftd->host;
+
+ NETRESOURCE nr = {};
+ nr.dwScope = RESOURCE_GLOBALNET;
+ nr.lpRemoteName = const_cast <LPTSTR>(static_cast <LPCTSTR>(remote));
+ LPTSTR sys = NULL;
+ if ( WNetGetResourceInformation( &nr, buf, &buf_size, &sys ) == NO_ERROR )
+ {
+ ftd->about = buf->lpComment;
+ DBWriteContactSettingTString( fttd->hContact, modname,
+ "About", ftd->about );
+ }
+
+ mir_free( buf );
+ }
+
+ // ...и уведомление о готовности данных
+ if ( ! IsWindow( fttd->hwndDlg ) ||
+ ! PostMessage( fttd->hwndDlg, WM_FILLTREE, 0, reinterpret_cast< LPARAM >( ftd ) ) )
+ delete ftd;
+ }
+ }
+
+ mir_free( fttd );
+}
+
+static INT_PTR CALLBACK DlgProcUserInfo (HWND hwndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+ DlgDataUserInfo* data = reinterpret_cast <DlgDataUserInfo*> (GetWindowLongPtr(hwndDlg, DWLP_USER));
+
+ switch (Msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault (hwndDlg);
+
+ data = (DlgDataUserInfo*)mir_alloc( sizeof( DlgDataUserInfo ) );
+ if ( ! data )
+ return FALSE;
+
+ SetWindowLongPtr (hwndDlg, DWLP_USER, reinterpret_cast <LONG> (data));
+ data->hTreeImages = ImageList_Create (16, 16, ILC_COLOR8 | ILC_MASK, 5, 0);
+ data->bWorking = false;
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 0
+ pluginModule, MAKEINTRESOURCE (IDI_COMPUTER), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 1
+ pluginModule, MAKEINTRESOURCE (IDI_GOOD_NAME), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 2
+ pluginModule, MAKEINTRESOURCE (IDI_GOOD_NAMES), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 3
+ pluginModule, MAKEINTRESOURCE (IDI_LANA), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ ImageList_AddIcon (data->hTreeImages, reinterpret_cast <HICON> (LoadImage ( // 4
+ pluginModule, MAKEINTRESOURCE (IDI_COMPUTER_ERROR), IMAGE_ICON, 16, 16, LR_SHARED )));
+
+ TreeView_SetImageList (GetDlgItem (hwndDlg, IDC_TREE), data->hTreeImages, TVSIL_NORMAL);
+
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, NULL);
+ if (data) {
+ ImageList_Destroy (data->hTreeImages);
+ mir_free( data );
+ }
+ break;
+ }
+
+ case WM_FILLTREE:
+ // Заполнение дерева имён...
+ if ( FillTreeData* ftd = reinterpret_cast <FillTreeData*> (lParam) )
+ {
+ if ( data )
+ {
+ // Заполнение дерева имён
+ HWND hTree = GetDlgItem (hwndDlg, IDC_TREE);
+ TreeView_DeleteAllItems (hTree);
+ TVINSERTSTRUCT tvis = { 0 };
+ tvis.hParent = TVI_ROOT;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = const_cast <LPTSTR>(static_cast<LPCTSTR>(ftd->host));
+ tvis.item.iImage = tvis.item.iSelectedImage =
+ IsGroup( data->hContact ) ? 2 :
+ ( ftd->nns.GetCount() ? 0 : 4 );
+ tvis.hParent = TreeView_InsertItem (hTree, &tvis);
+ if ( ftd->nns.GetCount() )
+ {
+ for (POSITION pos = ftd->nns.GetHeadPosition (); pos;)
+ {
+ netbios_name& nname = ftd->nns.GetNext (pos);
+ CA2T textT( nname.GetANSIFullName() );
+ tvis.item.pszText = (LPTSTR) (LPCTSTR) textT;
+ tvis.item.iImage = tvis.item.iSelectedImage = (nname.IsGroupName () ? 2 : 1);
+ TreeView_InsertItem (hTree, &tvis);
+ }
+ }
+ TreeView_Expand (hTree, tvis.hParent, TVE_EXPAND);
+
+ SetDlgItemText (hwndDlg, IDC_ABOUT, ftd->about);
+
+ data->bWorking = false;
+ }
+ delete ftd;
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ {
+ LPPSHNOTIFY lpHdr = reinterpret_cast <LPPSHNOTIFY> (lParam);
+ if (lpHdr->hdr.idFrom == 0)
+ {
+ // Сохранение контакта на будущее
+ data->hContact = reinterpret_cast <HMODULE> (lpHdr->lParam);
+ switch (lpHdr->hdr.code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ // Флаг "Always Online"
+ BOOL b = DBGetContactSettingByte ( data->hContact, modname,
+ "AlwaysOnline", FALSE );
+ CheckDlgButton (hwndDlg, IDC_ONLINE_CHECK,
+ (UINT)( b ? BST_CHECKED : BST_UNCHECKED ) );
+ EnableWindow (GetDlgItem (hwndDlg, IDC_ONLINE_CHECK),
+ data->hContact != NULL );
+
+ // Флаг "Legacy online status detection"
+ CheckDlgButton( hwndDlg, IDC_CHECK00FORONLINE,(UINT)(
+ IsLegacyOnline( data->hContact ) ? BST_CHECKED : BST_UNCHECKED ) );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_CHECK00FORONLINE ),
+ data->hContact != NULL );
+
+ // Флаг "Group Contact"
+ CheckDlgButton (hwndDlg, IDC_GROUP, (UINT)(
+ IsGroup( data->hContact ) ? BST_CHECKED : BST_UNCHECKED ) );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_GROUP ),
+ data->hContact != NULL );
+
+ // Запуск опроса NetBIOS-имён хоста
+ if ( data && ! data->bWorking )
+ {
+ HWND hTree = GetDlgItem (hwndDlg, IDC_TREE);
+ data->bWorking = true;
+ TreeView_DeleteAllItems (hTree);
+ TVINSERTSTRUCT tvis = { 0 };
+ tvis.hParent = TVI_ROOT;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT;
+ tvis.item.pszText = TranslateT ("Retrieving...");
+ TreeView_InsertItem (hTree, &tvis);
+
+ SetDlgItemText (hwndDlg, IDC_ABOUT, _T(""));
+
+ if ( FillTreeThreadData* fttd = (FillTreeThreadData*)mir_alloc( sizeof( FillTreeThreadData ) ) )
+ {
+ fttd->hContact = data->hContact;
+ fttd->hwndDlg = hwndDlg;
+ mir_forkthread( FillTreeThread, fttd );
+ }
+ }
+ break;
+ }
+
+ case PSN_KILLACTIVE:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE);
+ return TRUE;
+
+ case PSN_APPLY:
+ {
+ BOOL f_now = (IsDlgButtonChecked (hwndDlg, IDC_ONLINE_CHECK) ==
+ BST_CHECKED) ? TRUE : FALSE;
+ BOOL f_old = DBGetContactSettingByte ( data->hContact, modname,
+ "AlwaysOnline", FALSE );
+ DBWriteContactSettingByte (data->hContact, modname,
+ "AlwaysOnline", (BYTE)( f_now ? TRUE : FALSE ) );
+ if ( ! f_old && f_now )
+ SetContactStatus( data->hContact, ID_STATUS_ONLINE, true );
+ else if ( f_old && ! f_now )
+ SetContactStatus( data->hContact, ID_STATUS_OFFLINE, true );
+
+ SetLegacyOnline( data->hContact, ( IsDlgButtonChecked( hwndDlg,
+ IDC_CHECK00FORONLINE ) == BST_CHECKED ) );
+
+ SetGroup( data->hContact,
+ IsDlgButtonChecked( hwndDlg, IDC_GROUP ) == BST_CHECKED );
+
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR );
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_ONLINE_CHECK:
+ {
+ BOOL f_now = (IsDlgButtonChecked (hwndDlg, IDC_ONLINE_CHECK) ==
+ BST_CHECKED) ? TRUE : FALSE;
+ BOOL f_old = DBGetContactSettingByte ( data->hContact, modname,
+ "AlwaysOnline", FALSE );
+
+ if ( f_old != f_now )
+ PropSheet_Changed (GetParent (hwndDlg), hwndDlg);
+ else
+ PropSheet_UnChanged (GetParent (hwndDlg), hwndDlg);
+
+ return TRUE;
+ }
+
+ case IDC_CHECK00FORONLINE:
+ {
+ PropSheet_Changed (GetParent (hwndDlg), hwndDlg);
+ return TRUE;
+ }
+
+ case IDC_GROUP:
+ {
+ PropSheet_Changed (GetParent (hwndDlg), hwndDlg);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+int __cdecl USERINFO_INITIALISE (WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)lParam;
+ if ( ! hContact || ( IsMyContact( hContact ) /*&& ! IsChatRoom( hContact )*/ ) )
+ {
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = pluginModule;
+ odp.pszTemplate = MAKEINTRESOURCEA( IDD_USERINFO );
+ odp.ptszTitle = LPGENT(modtitle);
+ odp.flags = ODPF_TCHAR;
+ odp.pfnDlgProc = DlgProcUserInfo;
+ Options_AddPage(wParam, &odp);
+ }
+ return 0;
+}
diff --git a/protocols/WinPopup/src/user_info.h b/protocols/WinPopup/src/user_info.h
new file mode 100644
index 0000000000..8b7b0b124e
--- /dev/null
+++ b/protocols/WinPopup/src/user_info.h
@@ -0,0 +1,22 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2006 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+int __cdecl USERINFO_INITIALISE (WPARAM wParam, LPARAM lParam);
diff --git a/protocols/WinPopup/src/winpopup_proto.cpp b/protocols/WinPopup/src/winpopup_proto.cpp
new file mode 100644
index 0000000000..53e8245ec6
--- /dev/null
+++ b/protocols/WinPopup/src/winpopup_proto.cpp
@@ -0,0 +1,1193 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2011 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+volatile WPARAM pluginRequestedStatus = ID_STATUS_OFFLINE;
+volatile WPARAM pluginCurrentStatus = ID_STATUS_OFFLINE;
+CIntStrMap pluginStatusMessage;
+CString pluginMachineName;
+CString pluginUserName;
+CString pluginDomainName;
+HMODULE pluginModule = NULL;
+volatile bool pluginBusy = false;
+volatile bool pluginInstalled = false;
+volatile bool pluginInitialized = false;
+HANDLE pluginNetLibUser = NULL;
+HANDLE pluginInternalState = NULL;
+bool pluginChatEnabled = false;
+OSVERSIONINFO pluginOS = { sizeof( OSVERSIONINFO ) };
+
+CComAutoCriticalSection pluginGuard; // Защита доступа к картам:
+CThreadContactMap pluginAwayThreadMap; // Карта асинхронных запросов эвей-сообщений
+CThreadContactMap pluginAvatarThreadMap; // Карта асинхронных запросов аватаров
+
+CString GetNick(HANDLE hContact)
+{
+ CString sNick;
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( hContact, modname, "Nick", &dbv ) )
+ {
+ sNick = dbv.ptszVal;
+ DBFreeVariant( &dbv );
+ }
+ return sNick;
+}
+
+void SetNick(HANDLE hContact, LPCTSTR szNick)
+{
+ DBWriteContactSettingTString( hContact, modname, "Nick", szNick );
+}
+
+CComAutoCriticalSection _LOG_SECTION;
+
+int LOG(const char *fmt,...)
+{
+ CComCritSecLock< CComAutoCriticalSection > _Lock( _LOG_SECTION );
+
+ int ret = 0;
+ const int size = 512;
+ if ( char* szText = (char*)mir_alloc( size ) )
+ {
+ *szText = 0;
+ va_list va;
+ va_start( va, fmt );
+ mir_vsnprintf( szText, size, fmt, va );
+ va_end( va );
+ ret = CallService( MS_NETLIB_LOG, (WPARAM)pluginNetLibUser, (LPARAM)szText );
+ mir_free( szText );
+ }
+ return ret;
+}
+
+void GetAvatarCache(LPTSTR szPath)
+{
+ // Получить путь новым способом
+ if ( ServiceExists( MS_UTILS_REPLACEVARS ) )
+ {
+ LPTSTR szAvatarCache = Utils_ReplaceVarsT(
+ _T("%miranda_avatarcache%\\") modname_t _T("\\") );
+ if ( szAvatarCache && szAvatarCache != (LPTSTR)0x80000000 )
+ {
+ lstrcpyn( szPath, szAvatarCache, MAX_PATH );
+
+ // Создание пути до будущего файла аватара
+ CallService( MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szPath );
+ return;
+ }
+ }
+
+ // Получить путь старым способом
+ char szProfilePath[ MAX_PATH ], szProfileName[ MAX_PATH ];
+ CallService( MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)szProfilePath );
+ CallService( MS_DB_GETPROFILENAME, MAX_PATH, (LPARAM)szProfileName );
+ char *pos = strrchr( szProfileName, '.' );
+ if ( lstrcmpA( pos, ".dat" ) == 0 )
+ *pos = 0;
+ lstrcpy( szPath, CA2T( szProfilePath ) );
+ lstrcat( szPath, _T("\\") );
+ lstrcat( szPath, CA2T( szProfileName ) );
+ lstrcat( szPath, _T("\\AvatarCache\\") modname_t _T("\\") );
+
+ // Создание пути до будущего файла аватара
+ CallService( MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szPath );
+ return;
+}
+
+static LONG cookie = (LONG)GetTickCount();
+
+HANDLE GenerateCookie()
+{
+ return (HANDLE)InterlockedIncrement( &cookie );
+}
+
+DWORD time()
+{
+ LARGE_INTEGER lft = {};
+ GetSystemTimeAsFileTime ((FILETIME*) &lft);
+ return (DWORD) ((lft.QuadPart - 116444736000000000) / 10000000);
+}
+
+bool IsMyContact(HANDLE hContact)
+{
+ if ( ! hContact )
+ // Это вообще не контакт
+ return false;
+
+ char* proto = (char*)CallService( MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0 );
+ if ( ! proto )
+ // Это ничейный контакт
+ return false;
+
+ if ( lstrcmpA( proto, modname ) )
+ // Это не наш контакт
+ return false;
+
+ // Это наш контакт
+ return true;
+}
+
+void SetPluginStatus(WPARAM status)
+{
+ WPARAM old = pluginCurrentStatus;
+ pluginCurrentStatus = status;
+ if (pluginInstalled)
+ {
+ ProtoBroadcastAck (modname, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS,
+ (HANDLE) old, (LPARAM) pluginCurrentStatus);
+
+ if ( old != pluginCurrentStatus &&
+ pluginCurrentStatus >= ID_STATUS_OFFLINE &&
+ pluginCurrentStatus <= ID_STATUS_OUTTOLUNCH )
+ {
+ pluginNetBIOS.BroadcastStatus();
+ }
+ }
+}
+
+bool InternalStartup()
+{
+ _ASSERT( pluginInstalled == true );
+
+ LOG ("Startup begin");
+
+ ResetEvent( pluginInternalState );
+
+ bool err = false;
+
+ BYTE method = (BYTE) DBGetContactSettingByte (NULL, modname, "SendMethod", 0);
+ if ( method == 2 )
+ {
+ // Инициализация "Службы Сообщений" с запуском
+ if (pluginMessenger.Create (TRUE))
+ err = true;
+ LOG ("Startup : Messenger");
+
+ // Инициализация NetBIOS
+ if (!pluginNetBIOS.Create (FALSE))
+ err = true;
+ LOG ("Startup : NetBIOS");
+ }
+ else
+ {
+ // Инициализация "Службы Сообщений" с остановом
+ if (pluginMessenger.Create (FALSE))
+ err = true;
+
+ // Запуск мониторинга входящих сообщений через мейлслот
+ if ( ! pluginMailslot.Create( MESSENGER_MAIL ) )
+ err = true;
+ LOG ("Startup : Mailslot");
+
+ // Инициализация и запуск мониторинга входящих сообщений через NetBIOS
+ if (!pluginNetBIOS.Create (TRUE))
+ err = true;
+ LOG ("Startup : NetBIOS");
+ }
+
+ // Запуск мониторинга контактов
+ if (!pluginScanner.Create ())
+ err = true;
+ LOG ("Startup : Scanner");
+
+ LOG ("Startup end");
+
+ return !err;
+}
+
+void InternalShutdown ()
+{
+ LOG ("Shutdown begin");
+
+ // Предварительные запросы на закрытие
+ pluginSearch.AskForDestroy();
+ pluginMailslot.AskForDestroy();
+ pluginScanner.AskForDestroy();
+ pluginNetBIOS.AskForDestroy();
+ pluginMessenger.AskForDestroy();
+
+ // Закрытие мейлслота (быстро)
+ pluginMailslot.Destroy ();
+ LOG ("Shutdown : Mailslot");
+
+ // Завершение поиска (медленно, если идёт поиск)
+ pluginSearch.Destroy();
+ LOG ("Shutdown : Search");
+
+ // Дерегистрация NetBIOS имён (медленно)
+ pluginNetBIOS.Destroy ();
+ LOG ("Shutdown : NetBIOS");
+
+ // Останов мониторинга контактов (медленно, если идёт опрос)
+ pluginScanner.Destroy ();
+ LOG ("Shutdown : Scanner");
+
+ // Освобождение "Службы Сообщений" (медленно, если нужно запускать сервис)
+ pluginMessenger.Destroy ();
+ LOG ("Shutdown : Messenger");
+
+ LOG ("Shutdown end");
+ SetEvent( pluginInternalState );
+}
+
+void GotoOnline ()
+{
+ if ( pluginCurrentStatus != ID_STATUS_OFFLINE )
+ {
+ // Уже в онлайне
+ if ( pluginCurrentStatus != ID_STATUS_CONNECTING )
+ {
+ // Просто установка нового статуса
+ SetPluginStatus (pluginRequestedStatus);
+ return;
+ }
+ }
+
+ SetPluginStatus (ID_STATUS_CONNECTING);
+
+ if (!pluginInstalled || !pluginInitialized || pluginBusy)
+ // Отложенное подключение (или запрет)
+ return;
+ pluginBusy = true;
+
+ // Начало соединения
+ mir_forkthread( GotoOnlineTread, NULL );
+}
+
+void GotoOffline()
+{
+ // Подтверждение текущего статуса
+ SetPluginStatus (ID_STATUS_OFFLINE);
+
+ if (pluginBusy)
+ // Двойной вызов
+ return;
+ pluginBusy = true;
+
+ // Перевод всех контактов в оффлайн
+ FOR_EACH_CONTACT( hContact ) {
+ SetContactStatus (hContact, ID_STATUS_OFFLINE, true);
+ }
+
+ // Начало отсоединения
+ mir_forkthread( GotoOfflineTread, NULL );
+}
+
+void GotoOnlineTread(LPVOID /* status */)
+{
+ // Запуск
+ InternalStartup();
+ pluginBusy = false;
+ Sleep( 1000 );
+
+ // Перепроверка статуса
+ if ( ! pluginInstalled || pluginRequestedStatus == ID_STATUS_OFFLINE )
+ // Черт, нужно обратно в оффлайн
+ GotoOffline ();
+ else
+ // Все в порядке, установка статуса
+ SetPluginStatus (pluginRequestedStatus);
+}
+
+void GotoOfflineTread(LPVOID /* status */)
+{
+ // Выключение всего
+ InternalShutdown ();
+ pluginBusy = false;
+ Sleep( 1000 );
+
+ // Перепроверка статуса
+ if ( pluginInstalled && pluginRequestedStatus != ID_STATUS_OFFLINE )
+ // Черт, нужно обратно в онлайн
+ GotoOnline ();
+ else
+ // Подтверждение текущего статуса
+ SetPluginStatus (ID_STATUS_OFFLINE);
+}
+
+void GetAwayMsgThread(LPVOID param)
+{
+ // Задержка чтобы миранда успела "переварить" ответ
+ Sleep( 250 );
+
+ ContactData* data = (ContactData*)param;
+
+ bool ret = false;
+
+ bool bGroup = IsGroup( data->hContact );
+ CString sNick = GetNick( data->hContact );
+ if ( ! bGroup && ! sNick.IsEmpty() )
+ {
+ ThreadEvent te = { CreateEvent( NULL, TRUE, FALSE, NULL ), data->cookie };
+
+ // Отмечаемся в карте ожидающих нитей
+ {
+ CLock oLock( pluginGuard );
+ pluginAwayThreadMap.SetAt( data->hContact, te );
+ }
+
+ // Асинхронный запрос эвей-сообщения
+ ret = pluginNetBIOS.AskAway( netbios_name( sNick, 0x03, false ) );
+
+ // Ожидание завершения (3 секунды)
+ ret = ret && ( WaitForSingleObject( te.evt, 3000 ) == WAIT_OBJECT_0 );
+
+ // Очистка карты ожидающих нитей
+ {
+ CLock oLock( pluginGuard );
+ pluginAwayThreadMap.Lookup( data->hContact, te );
+ pluginAwayThreadMap.RemoveKey( data->hContact );
+ }
+
+ CloseHandle( te.evt );
+ }
+
+ if ( ! ret )
+ {
+ // Уведомление о неудаче
+ ProtoBroadcastAck (modname, data->hContact, ACKTYPE_AWAYMSG,
+ ACKRESULT_SUCCESS /* ACKRESULT_FAILED */, data->cookie, (LPARAM)"" );
+ LOG( "Get away message failed" );
+ }
+
+ delete data;
+}
+
+void SetContactAway(HANDLE hContact, LPCSTR away)
+{
+ if ( ! pluginInstalled )
+ return;
+
+ // Определение ожидающей нити по контакту
+ bool ret = false;
+ ThreadEvent te = {};
+ {
+ CLock oLock( pluginGuard );
+ if ( pluginAwayThreadMap.Lookup( hContact, te ) )
+ {
+ SetEvent( te.evt );
+ ret = true;
+ }
+ }
+
+ if ( ret )
+ {
+ // Уведомление об эвей-сообщении
+ ProtoBroadcastAck( modname, hContact, ACKTYPE_AWAYMSG,
+ ACKRESULT_SUCCESS, te.cookie, (LPARAM)away );
+ }
+ else
+ {
+ // Принудительная установка эфей-сообщения для контакта
+ }
+}
+
+void GetInfoThread(LPVOID param)
+{
+ // Задержка чтобы миранда успела "переварить" ответ
+ Sleep( 500 );
+
+ HANDLE hContact = (HANDLE)param;
+
+ ProtoBroadcastAck( modname, hContact, ACKTYPE_GETINFO,
+ ACKRESULT_SUCCESS, (HANDLE)0, 0 );
+}
+
+void Autoanswer(HANDLE hContact)
+{
+ switch ( pluginCurrentStatus )
+ {
+ case ID_STATUS_AWAY:
+ case ID_STATUS_DND:
+ case ID_STATUS_NA:
+ case ID_STATUS_OCCUPIED:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ {
+ CString msg;
+ if ( pluginStatusMessage.Lookup( pluginCurrentStatus, msg ) )
+ {
+ // Отсылка сообщения отправителю
+ CString answer (TranslateT ("Auto-reply"));
+ answer += _T(":\r\n");
+ answer += msg;
+ DWORD foo;
+ SendContactMessage( hContact, answer, foo );
+
+ // Добавление сообщения
+ DBEVENTINFO ei = {};
+ ei.cbSize = sizeof (DBEVENTINFO);
+ ei.szModule = modname;
+ ei.timestamp = time();
+ ei.flags = DBEF_SENT;
+ ei.eventType = EVENTTYPE_MESSAGE;
+ ei.cbBlob = (DWORD)answer.GetLength () + 1;
+ ei.pBlob = (PBYTE) (LPCTSTR) answer;
+ CallServiceSync( MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&ei );
+ }
+ }
+ break;
+ }
+}
+
+void ReceiveContactMessage(LPCTSTR msg_from, LPCTSTR msg_to, LPCTSTR msg_text, int msg_len)
+{
+ if ( ! pluginInstalled )
+ return;
+
+ CString from( msg_from );
+ CString to( msg_to );
+ CString text( msg_text, msg_len );
+
+ from.MakeUpper();
+ to.MakeUpper();
+
+ // Свое собственное сообщение?
+ if ( IsItMe( from ) )
+ {
+ LOG ( "Ignoring my message." );
+ return;
+ }
+
+ // Нормализация разбиения сообщения на строки
+ Normalize( text );
+
+ // Дубликат?
+ if (DBGetContactSettingByte (NULL, modname, "Filter-dups", TRUE))
+ {
+ // Вычисление прошедшего времени с последнего сообщения
+ static FILETIME last_time = { 0, 0 };
+ FILETIME current_time;
+ GetSystemTimeAsFileTime (&current_time);
+ ULONGLONG elapsed =
+ ((ULONGLONG) current_time.dwLowDateTime |
+ (ULONGLONG) current_time.dwHighDateTime << 32) -
+ ((ULONGLONG) last_time.dwLowDateTime |
+ (ULONGLONG) last_time.dwHighDateTime << 32);
+
+ // Вычисление MD5-хэшей отправителя
+ MD5Context ctx;
+ md5init (&ctx);
+ md5update (&ctx, (const unsigned char*)(LPCTSTR)from,
+ from.GetLength() * sizeof (TCHAR));
+ unsigned char digest_from_current [16] = {0};
+ static unsigned char digest_from_last [16] = {0};
+ md5final (digest_from_current, &ctx);
+
+ // Вычисление MD5-хэшей сообщения
+ md5init (&ctx);
+ md5update (&ctx, (const unsigned char*)(LPCTSTR)text,
+ text.GetLength() * sizeof (TCHAR));
+ unsigned char digest_text_current [16] = {0};
+ static unsigned char digest_text_last [16] = {0};
+ md5final (digest_text_current, &ctx);
+
+ // Если прошло менее 2 секунд между сообщениями
+ if (elapsed < 20000000)
+ {
+ // И отправители совпадают
+ if (memcmp (digest_from_current, digest_from_last, 16) == 0)
+ {
+ // И сообщение совпадает
+ if (memcmp (digest_text_current, digest_text_last, 16) == 0)
+ {
+ // то пропускаем такое сообщение
+ LOG ("Duplicate message detected");
+ return;
+ }
+ }
+ }
+ last_time = current_time;
+ CopyMemory (digest_from_last, digest_from_current, 16);
+ CopyMemory (digest_text_last, digest_text_current, 16);
+ }
+
+#ifdef CHAT_ENABLED
+ if ( ! IsItMe( to ) && pluginChatEnabled ) // Групповой адрес?
+ {
+ // Формирование группового сообщения
+ if ( ChatNewSession( to ) )
+ {
+ // Добавить группу
+ ATLVERIFY( ChatAddGroup( to, _T("Normal") ) );
+
+ // Добавить себя
+ ATLVERIFY( ChatJoinMe( to, _T("Normal") ) );
+
+ // Добавить "От Кого"
+ ATLVERIFY( ChatJoinUser( to, from, _T("Normal") ) );
+
+ // Завершение создания чата
+ ATLVERIFY( ChatInitDone( to ) );
+
+ // Перевод чат-контакта в онлайн
+ ATLVERIFY( ChatOnline( to ) );
+
+ // Сообщение
+ ATLVERIFY( ChatMessage( to, from, text ) );
+ }
+ }
+ else
+#endif // CHAT_ENABLED
+ {
+ // Формирование приватного сообщения
+ HANDLE hContact = AddToListByName( from, 0, NULL, false, false );
+ if ( hContact )
+ {
+ PROTORECVEVENT pre = { 0 };
+ pre.timestamp = time ();
+ CT2A textA( text );
+ pre.szMessage = (LPSTR)(LPCSTR)textA;
+ CCSDATA ccs = { 0 };
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.hContact = hContact;
+ DBDeleteContactSetting (ccs.hContact, "CList", "Hidden");
+ ccs.lParam = (LPARAM) &pre;
+ CallServiceSync (MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs);
+
+ // Переводим контакт в онлайн
+ SetContactStatus( hContact, contact_scanner::ScanContact( hContact ), true );
+
+ // Авто-ответчик
+ if ( DBGetContactSettingByte( NULL, modname, "Auto-answer", FALSE ) )
+ Autoanswer( hContact );
+ }
+ }
+}
+
+void Normalize(CString& msg)
+{
+ enum { CR, LF, NOP };
+ int line_break = NOP;
+ int i = 0;
+ for (; i < msg.GetLength (); ++i)
+ {
+ switch (msg [i])
+ {
+ case _T('\r'):
+ case _T('\xb6'):
+ switch (line_break)
+ {
+ case CR: // CRCR
+ case LF: // LFCR
+ msg.Delete (i);
+ msg.Delete (i - 1);
+ msg.Insert (i - 1, _T('\r'));
+ msg.Insert (i, _T('\n'));
+ line_break = NOP;
+ break;
+ default: // xxCR
+ line_break = CR;
+ }
+ break;
+ case _T('\n'):
+ switch (line_break)
+ {
+ case CR: // CRLF
+ line_break = NOP;
+ break;
+ case LF: // LFLF
+ msg.Delete (i);
+ msg.Delete (i - 1);
+ msg.Insert (i - 1, _T('\r'));
+ msg.Insert (i, _T('\n'));
+ line_break = NOP;
+ break;
+ default: // xxLF
+ line_break = LF;
+ }
+ break;
+ default:
+ switch (line_break)
+ {
+ case CR: // CR без LF
+ case LF: // LF без CR
+ msg.Delete (i - 1);
+ msg.Insert (i - 1, _T('\r'));
+ msg.Insert (i, _T('\n'));
+ ++i;
+ break;
+ }
+ line_break = NOP;
+ }
+ }
+ switch (line_break)
+ {
+ case CR: // CR без LF
+ case LF: // LF без CR
+ msg.Delete (i - 1);
+ msg.Insert (i - 1, _T('\r'));
+ msg.Insert (i, _T('\n'));
+ break;
+ }
+}
+
+HANDLE AddToListByName(const CString& sName, WPARAM flags, LPCTSTR about, bool bInteractive, bool bGroup)
+{
+ ip addr = INADDR_NONE;
+ CString sShortName( sName );
+
+ if ( ! bGroup )
+ {
+ // Попытка получить IP из имени
+ if ( addr == INADDR_NONE )
+ addr = ResolveToIP( sShortName );
+
+ // Поиск NetBIOS-имени
+ if ( addr == INADDR_NONE )
+ addr = pluginNetBIOS.FindNameIP( sName );
+
+ // Неизвестный контакт
+ if ( addr == INADDR_NONE && bInteractive )
+ {
+ if ( MessageBox( NULL,
+ TranslateT("Cannot resolve contacts IP-address. Add it anyway?"),
+ modname_t, MB_YESNO | MB_ICONQUESTION ) != IDYES )
+ {
+ return NULL;
+ }
+ }
+ }
+
+ // Поиск существующего контакта
+ HANDLE hContact = GetContact( sShortName );
+ if ( ! hContact )
+ {
+ // Добавление контакта
+ hContact = (HANDLE)CallService( MS_DB_CONTACT_ADD, 0, 0 );
+ if ( hContact )
+ {
+ CallService( MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)modname );
+ SetNick( hContact, sShortName );
+ SetGroup( hContact, bGroup );
+ DBWriteContactSettingTString( hContact, "CList", "MyHandle", sShortName );
+ DBWriteContactSettingByte( hContact, "CList", "NotOnList", 1 );
+ DBWriteContactSettingByte( hContact, "CList", "Hidden", 1 );
+ SetContactIP( hContact, addr );
+ SetElapsed( hContact, "IPTime" );
+ if ( about )
+ DBWriteContactSettingTString( hContact, modname, "About", about );
+
+ contact_scanner::ScanContact( hContact );
+ }
+ }
+ if ( hContact && ! ( flags & PALF_TEMPORARY ) &&
+ DBGetContactSettingByte( hContact, "CList", "NotOnList", 1 ) )
+ {
+ // Оставляем контакт
+ DBDeleteContactSetting( hContact, "CList", "NotOnList" );
+ DBDeleteContactSetting( hContact, "CList", "Hidden" );
+ }
+ return hContact;
+}
+
+DWORD GetElapsed (HANDLE hContact, const char* name)
+{
+ FILETIME current = {};
+ GetSystemTimeAsFileTime (&current);
+ LARGE_INTEGER currentL = {};
+ currentL.LowPart = current.dwLowDateTime;
+ currentL.HighPart = (LONG)current.dwHighDateTime;
+ LARGE_INTEGER lastseenL = {};
+ lastseenL.LowPart = DBGetContactSettingDword (hContact, modname,
+ CStringA (name) + "L", 0);
+ lastseenL.HighPart = (LONG)DBGetContactSettingDword (hContact, modname,
+ CStringA (name) + "H", 0);
+ return (DWORD) (((currentL.QuadPart - lastseenL.QuadPart) / 10000000L) & 0xffffffff);
+}
+
+void SetElapsed (HANDLE hContact, const char* name)
+{
+ FILETIME current = {};
+ GetSystemTimeAsFileTime (&current);
+ DBWriteContactSettingDword (hContact, modname,
+ CStringA (name) + "L", current.dwLowDateTime);
+ DBWriteContactSettingDword (hContact, modname,
+ CStringA (name) + "H", current.dwHighDateTime);
+}
+
+HANDLE GetContact (ip addr)
+{
+ // Перебор списка контактов
+ FOR_EACH_CONTACT( hContact )
+ {
+ // Получение имени контакта
+ ip contact_addr = DBGetContactSettingDword (hContact, modname, "IP", 0);
+ if (contact_addr && (contact_addr == addr))
+ // Найдено совпадение
+ break;
+ }
+ return hContact;
+}
+
+HANDLE GetContact (LPCTSTR name)
+{
+ // Перебор списка контактов
+ FOR_EACH_CONTACT( hContact )
+ {
+ // Получение имени контакта
+ CString sNick = GetNick( hContact );
+ if ( ! sNick.IsEmpty() )
+ {
+ // Сличение имен
+ if ( sNick.CompareNoCase( name ) == 0 )
+ // Найдено совпадение
+ break;
+ }
+ }
+ return hContact;
+}
+
+void SetContactStatus (HANDLE hContact, int status, bool simple)
+{
+ if ( ! pluginInstalled )
+ return;
+
+ SetElapsed (hContact, "LastSeen");
+
+#ifdef CHAT_ENABLED
+ if ( IsChatRoom( hContact ) )
+ {
+ CString sSession = GetChatSession( hContact );
+ if ( pluginChatEnabled && ! sSession.IsEmpty() )
+ {
+ if ( status != ID_STATUS_OFFLINE )
+ ChatOnline( sSession );
+ else
+ ChatOffline( sSession );
+ }
+ }
+ else
+#endif // CHAT_ENABLED
+ {
+ int ns = DBGetContactSettingWord (hContact, modname, "Status", -1);
+ if ( ns != status )
+ {
+ // Изменение статуса
+ if ( ! simple )
+ // Точная установка статуса
+ DBWriteContactSettingWord (hContact, modname, "Status", (WORD) status);
+ else if ( ns == -1 || ns == ID_STATUS_OFFLINE || status != ID_STATUS_ONLINE )
+ // Примерная установка статуса
+ DBWriteContactSettingWord (hContact, modname, "Status", (WORD) status);
+ }
+ }
+}
+
+void GetAvatarInfoThread(LPVOID param)
+{
+ // Задержка чтобы миранда успела "переварить" ответ
+ Sleep( 500 );
+
+ ContactData* data = (ContactData*)param;
+
+ bool ret = false;
+ ThreadEvent te = { CreateEvent( NULL, TRUE, FALSE, NULL ), data->cookie };
+ PROTO_AVATAR_INFORMATION pai = { sizeof( PROTO_AVATAR_INFORMATION ), data->hContact };
+
+ TCHAR szPath[ MAX_PATH ];
+ GetAvatarCache( szPath );
+
+ // Получение кешированного аватара
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( data->hContact, modname, "AvatarFile", &dbv ) )
+ {
+ lstrcat( szPath, dbv.ptszVal );
+
+ if ( GetFileAttributes( szPath ) != INVALID_FILE_ATTRIBUTES )
+ {
+ ret = true;
+
+ lstrcpyA( pai.filename, CT2A( szPath ) );
+
+ // Определение формата
+ LPCTSTR szExt = _tcsrchr( dbv.ptszVal, _T('.') );
+ if ( ! szExt )
+ {
+ pai.format = PA_FORMAT_UNKNOWN;
+ }
+ else if ( lstrcmpi( szExt, _T(".png") ) == 0 || lstrcmpi( szExt, _T(".dat") ) == 0 )
+ {
+ pai.format = PA_FORMAT_PNG;
+ }
+ else if ( lstrcmpi( szExt, _T(".jpg") ) == 0 )
+ {
+ pai.format = PA_FORMAT_JPEG;
+ }
+ else if ( lstrcmpi( szExt, _T(".ico") ) == 0 )
+ {
+ pai.format = PA_FORMAT_ICON;
+ }
+ else if ( lstrcmpi( szExt, _T(".bmp") ) == 0 )
+ {
+ pai.format = PA_FORMAT_BMP;
+ }
+ else if ( lstrcmpi( szExt, _T(".gif") ) == 0 )
+ {
+ pai.format = PA_FORMAT_GIF;
+ }
+ else if ( lstrcmpi( szExt, _T(".swf") ) == 0 )
+ {
+ pai.format = PA_FORMAT_SWF;
+ }
+ else if ( lstrcmpi( szExt, _T(".xml") ) == 0 )
+ {
+ pai.format = PA_FORMAT_XML;
+ }
+ else
+ {
+ pai.format = PA_FORMAT_UNKNOWN;
+ }
+ }
+ DBFreeVariant( &dbv );
+ }
+ if ( ret )
+ {
+ ProtoBroadcastAck( modname, data->hContact, ACKTYPE_AVATAR,
+ ACKRESULT_SUCCESS, &pai, 0 );
+ LOG( "Returned cached avatar." );
+ }
+ else
+ {
+ bool bGroup = IsGroup( data->hContact );
+ CString sNick = GetNick( data->hContact );
+ if ( ! bGroup && ! sNick.IsEmpty() )
+ {
+ // Асинхронный сетевой запрос аватара
+ ProtoBroadcastAck( modname, data->hContact, ACKTYPE_AVATAR,
+ ACKRESULT_SENTREQUEST, &pai, 0 );
+
+ // Отмечаемся в карте ожидающих нитей
+ {
+ CLock oLock( pluginGuard );
+ pluginAvatarThreadMap.SetAt( data->hContact, te );
+ }
+
+ ret = pluginNetBIOS.AskAvatar( netbios_name( sNick, 0x03 ) );
+
+ // Ожидание завершения (3 секунды)
+ ret = ret && ( WaitForSingleObject( te.evt, 3000 ) == WAIT_OBJECT_0 );
+
+ // Очистка карты ожидающих нитей
+ {
+ CLock oLock( pluginGuard );
+ pluginAvatarThreadMap.Lookup( data->hContact, te );
+ pluginAvatarThreadMap.RemoveKey( data->hContact );
+ }
+ }
+ if ( ! ret )
+ {
+ ProtoBroadcastAck( modname, data->hContact, ACKTYPE_AVATAR,
+ ACKRESULT_FAILED, &pai, 0 );
+ LOG( "Get avatar failed" );
+ }
+ }
+
+ if ( te.evt )
+ CloseHandle( te.evt );
+
+ delete data;
+}
+
+void SetContactAvatar(HANDLE hContact, LPCVOID pBuffer, DWORD nLength)
+{
+ if ( ! pluginInstalled )
+ return;
+
+ PROTO_AVATAR_INFORMATION pai = { sizeof( PROTO_AVATAR_INFORMATION ), hContact };
+
+ CString sFilename, sNick = GetNick( hContact );
+ if ( sNick.IsEmpty() || sNick.FindOneOf( _T("/\\*?:|\"<>%") ) != -1 )
+ {
+ // Получение старого имени
+ DBVARIANT dbv = {};
+ if ( ! DBGetContactSettingTString( hContact, modname, "AvatarFile", &dbv ) )
+ {
+ sFilename = dbv.ptszVal;
+ DBFreeVariant( &dbv );
+ }
+ else
+ // Генерация уникального имени
+ sFilename.Format( _T("%08x_avt"), (DWORD)hContact );
+ }
+ else
+ // Генерация уникального имени
+ sFilename.Format( _T("%s_%08x_avt"), sNick, (DWORD)hContact );
+
+ // Определение типа изображения
+ if ( ! memcmp( pBuffer, "%PNG", 4 ) )
+ {
+ pai.format = PA_FORMAT_PNG;
+ sFilename += _T(".png");
+ }
+ else if ( *(DWORD*)pBuffer == 0xE0FFD8FFul || *(DWORD*)pBuffer == 0xE1FFD8FFul )
+ {
+ pai.format = PA_FORMAT_JPEG;
+ sFilename += _T(".jpg");
+ }
+ else if ( *(DWORD*)pBuffer == 0x00010000 )
+ {
+ pai.format = PA_FORMAT_ICON;
+ sFilename += _T(".ico");
+ }
+ else if ( ! memcmp( pBuffer, "BM", 2 ) )
+ {
+ pai.format = PA_FORMAT_BMP;
+ sFilename += _T(".bmp");
+ }
+ else if ( ! memcmp( pBuffer, "GIF", 3 ) )
+ {
+ pai.format = PA_FORMAT_GIF;
+ sFilename += _T(".gif");
+ }
+ else if ( ! memcmp( pBuffer, "CWS", 3 ) )
+ {
+ pai.format = PA_FORMAT_SWF;
+ sFilename += _T(".swf");
+ }
+ else if ( ! memcmp( pBuffer, "<?xml", 5 ) )
+ {
+ pai.format = PA_FORMAT_XML;
+ sFilename += _T(".xml");
+ }
+ else
+ {
+ pai.format = PA_FORMAT_UNKNOWN;
+ sFilename += _T(".dat");
+ }
+
+ // Сборка полного пути к файлу
+ TCHAR szPath[ MAX_PATH ];
+ GetAvatarCache( szPath );
+
+ lstrcat( szPath, sFilename );
+
+ CAtlFile oAvatarFile;
+ if ( FAILED( oAvatarFile.Create( szPath, GENERIC_WRITE,
+ FILE_SHARE_READ, CREATE_ALWAYS ) ) )
+ // Файл не найден
+ return;
+
+ if ( FAILED( oAvatarFile.Write( pBuffer, nLength ) ) )
+ // Ошибка записи в файл
+ return;
+
+ oAvatarFile.Close();
+
+ DBWriteContactSettingTString( hContact, modname, "AvatarFile", sFilename );
+
+ // Определение ожидающей нити по контакту
+ bool ret = false;
+ ThreadEvent te = {};
+ {
+ CLock oLock( pluginGuard );
+ if ( pluginAvatarThreadMap.Lookup( hContact, te ) )
+ {
+ SetEvent( te.evt );
+ ret = true;
+ }
+ }
+
+ lstrcpyA( pai.filename, CT2A( szPath ) );
+
+ if ( ret )
+ {
+ // Уведомление о новом аватаре
+ ProtoBroadcastAck( modname, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &pai, 0 );
+ }
+ else
+ {
+ // Либо пропустили событие, либо его не было - ставим аватар принудительно
+ if ( ServiceExists( MS_AV_SETAVATAR ) )
+ {
+ CallService( MS_AV_SETAVATAR, (WPARAM)hContact, (LPARAM)pai.filename );
+ }
+ }
+}
+
+ip GetContactIP (HANDLE hContact)
+{
+ ip addr = INADDR_NONE;
+ bool got_nick = false, nick_invalid = false;
+ DBVARIANT dbv = {};
+ if ( hContact )
+ {
+ CString sNick = GetNick( hContact );
+ if ( ! sNick.IsEmpty() )
+ {
+ got_nick = true;
+ }
+ else if ( ! DBGetContactSettingTString( hContact, "CList", "MyHandle", &dbv ) )
+ {
+ nick_invalid = true;
+ got_nick = true;
+ }
+
+ if ( got_nick )
+ {
+ // Восстановление ника
+ if ( nick_invalid )
+ SetNick( hContact, dbv.ptszVal );
+
+ // Если не истекло время кеширования адреса, то из контакта
+ DWORD elapsed = GetElapsed (hContact, "IPTime");
+ if (elapsed <= MAX_TRUSTED_IP_TIME)
+ addr = DBGetContactSettingDword (hContact, modname, "IP", 0);
+ if (addr == INADDR_NONE) {
+ // Разбор DNS-адреса
+ CString name (dbv.pszVal);
+ addr = ResolveToIP (name);
+ if (addr != INADDR_NONE)
+ SetElapsed (hContact, "IPTime");
+ }
+ if (addr != INADDR_NONE)
+ SetContactIP (hContact, addr);
+ DBFreeVariant (&dbv);
+ }
+ }
+ return addr;
+}
+
+void SetContactIP(HANDLE hContact, ip addr)
+{
+ if ( ! pluginInstalled )
+ return;
+
+ if ( hContact )
+ {
+ DBWriteContactSettingDword (hContact, modname, "IP", addr);
+ DBWriteContactSettingDword (hContact, modname, "RealIP", addr);
+ }
+}
+
+bool IsGroup(HANDLE hContact)
+{
+ return ( DBGetContactSettingByte( hContact, modname, "Group", 0u ) != 0u );
+}
+
+void SetGroup(HANDLE hContact, bool bGroup)
+{
+ DBWriteContactSettingByte( hContact, modname, "Group", ( bGroup ? 1u : 0u ) );
+}
+
+bool IsLegacyOnline(HANDLE hContact)
+{
+ return ( DBGetContactSettingByte( hContact, modname, "Check00ForOnline", 0u ) != 0u );
+}
+
+void SetLegacyOnline(HANDLE hContact, bool bOnline)
+{
+ DBWriteContactSettingByte( hContact, modname, "Check00ForOnline", ( bOnline ? 1u : 0u ) );
+}
+
+bool SendContactMessage(HANDLE hContact, LPCTSTR msg, DWORD& err)
+{
+ // Получение метода посылки
+ BYTE method = (BYTE)DBGetContactSettingByte( NULL, modname, "SendMethod", 0 );
+ switch ( method )
+ {
+ case 0:
+ return pluginMailslot.SendMailslotMessage( hContact, msg, err );
+ case 1:
+ return pluginNetBIOS.SendNetBIOSMessage( hContact, msg, err );
+ case 2:
+ return pluginMessenger.SendMessengerMessage( hContact, msg, err );
+ default:
+ return false;
+ }
+}
+
+void SendMsgThread(LPVOID param)
+{
+ SendMsgData* data = (SendMsgData*)param;
+
+ DWORD dwLastError = 0;
+ if ( SendContactMessage( data->hContact, data->text, dwLastError ) )
+ {
+ ProtoBroadcastAck ( modname, data->hContact, ACKTYPE_MESSAGE,
+ ACKRESULT_SUCCESS, data->cookie, 0 );
+ }
+ else
+ {
+ // Уведомление об ошибке
+ CString msg, buf;
+ GetErrorMessage (dwLastError, buf);
+ msg.Format( _T("%s\r\n%s"), TranslateT ("Cannot send message"), (LPCTSTR)buf);
+ ProtoBroadcastAck (modname, data->hContact, ACKTYPE_MESSAGE,
+ ACKRESULT_FAILED, data->cookie, (LPARAM)(LPCTSTR)msg );
+
+ // И заодно попапом
+ WarningBox( data->hContact, dwLastError, TranslateT("Cannot send message") );
+ }
+
+ delete data;
+}
+
+bool IsItMe(LPCTSTR name)
+{
+ return ! pluginMachineName.CompareNoCase( name ) ||
+ ! pluginUserName.CompareNoCase( name );
+}
+
+void EnumWorkgroups(CAtlList< CString >& lst, LPNETRESOURCE hRoot)
+{
+ HANDLE hEnum = NULL;
+ DWORD res = WNetOpenEnum( RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
+ RESOURCEUSAGE_CONTAINER, hRoot, &hEnum );
+ if ( res == NO_ERROR )
+ {
+ for (;;)
+ {
+ DWORD cCount = 1;
+ DWORD BufferSize = 4096;
+ char* Buffer = (char*)mir_alloc( BufferSize );
+ if ( ! Buffer )
+ break;
+ res = WNetEnumResource( hEnum, &cCount, Buffer, &BufferSize );
+ if ( res == NO_ERROR )
+ {
+ LPNETRESOURCE lpnr = (LPNETRESOURCE)Buffer;
+ if ( lpnr->dwDisplayType == RESOURCEDISPLAYTYPE_DOMAIN )
+ {
+ CharUpper ( lpnr->lpRemoteName );
+ lst.AddTail( lpnr->lpRemoteName );
+ }
+ else if ( ( lpnr->dwUsage & 0xffff ) == RESOURCEUSAGE_CONTAINER )
+ {
+ EnumWorkgroups( lst, lpnr );
+ }
+ mir_free( Buffer );
+ }
+ else
+ {
+ mir_free( Buffer );
+ break;
+ }
+ }
+ WNetCloseEnum (hEnum);
+ }
+}
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID /*lpReserved*/)
+{
+ pluginModule = hModule;
+ return TRUE;
+}
diff --git a/protocols/WinPopup/src/winpopup_proto.h b/protocols/WinPopup/src/winpopup_proto.h
new file mode 100644
index 0000000000..6016a583f8
--- /dev/null
+++ b/protocols/WinPopup/src/winpopup_proto.h
@@ -0,0 +1,318 @@
+/*
+
+WinPopup Protocol plugin for Miranda IM.
+
+Copyright (C) 2004-2010 Nikolay Raspopov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// Данные используемые плагином в базе данных Miranda IM
+//
+// Собственные данные плагина:
+// Nick String - свое название машины (авт.обновляется)
+// User String - свое название пользователя (авт.обновляется)
+// Workgroup String - свое название домена или рабочей группы машины (авт.обновляется)
+// Auto-answer BYTE - флаг авто-ответа (по-умолчанию: 0)
+// Filter-dups BYTE - флаг фильтрации дубликатов (по-умолчанию: 1)
+// SendMethod BYTE - способ отсылки сообщений: 0 - mailslot, 1 - NetBIOS, 2 - Messenger (по-умолчанию: 0)
+// RegisterNick BYTE - флаг регистрации NetBIOS имени Nick<01> и Nick<03> (по-умолчанию: 1)
+// RegisterUser BYTE - флаг регистрации NetBIOS имени User<03> (по-умолчанию: 1)
+// RegisterStatus BYTE - флаг регистрации NetBIOS имени MNS_STATUS<ab> (по-умолчанию: 1)
+// Check00ForOnline BYTE - флаг дополнительной проверки NetBIOS имён Nick<00> на онлайн статус, для всех контактов (по умолчанию: 0) (переименовано из AlwaysCheck00ForOnline)
+// AvatarFile String - путь к файлу аватара
+//
+// Данные плагина для контактов:
+// Nick String - название машины
+// IP DWORD - адрес машины
+// RealIP DWORD - адрес машины (совпадает с IP)
+// IPTimeL DWORD - время последнего ресолвинга имени в адрес
+// IPTimeH DWORD
+// LastSeenL DWORD - время последней проверка контакта
+// LastSeenH DWORD
+// PingCounter WORD - счётчик посланных пингов без ответа (больше не используется)
+// Status WORD - статус контакта
+// About String - комментарий
+// AlwaysOnline BYTE - флаг отображения контакта в online-состоянии всегда
+// Check00ForOnline BYTE - флаг дополнительной проверки NetBIOS имени Nick<00> на онлайн статус (по умолчанию: 0)
+// AvatarFile String - путь к файлу аватара
+// Group BYTE - 1/0 - это групповой контакт
+//
+// Неиспользуемые настройки:
+// ChatRoom BYTE - 1/0 - это чат
+// ChatRoomID String - идентификатор чата, обычно имя рабочей группы
+//
+// Данные CList (главного списка контаков) для контактов:
+// MyHandle String - название контакта (ред. пользователем)
+// NotOnList BYTE - флаг временного контакта
+// Hidden BYTE - флаг скрытого контакта
+//
+// Данные Icons (иконки статуса) для плагина:
+// WinPopup Protocol40071 String - путь к иконке статуса
+// WinPopup Protocol40072 String - путь к иконке статуса
+// WinPopup Protocol40073 String - путь к иконке статуса
+// WinPopup Protocol40074 String - путь к иконке статуса
+// WinPopup Protocol40075 String - путь к иконке статуса
+// WinPopup Protocol40076 String - путь к иконке статуса
+// WinPopup Protocol40077 String - путь к иконке статуса
+// WinPopup Protocol40078 String - путь к иконке статуса
+// WinPopup Protocol40079 String - путь к иконке статуса
+// WinPopup Protocol40080 String - путь к иконке статуса
+
+// Название плагина
+#define modname "WinPopup Protocol"
+#define modname_t _T( modname )
+// Краткое название плагина
+#define modtitle "WinPopup"
+#define modtitle_t _T( modtitle )
+// "Не могу остановить Службу Сообщений"
+#define T_STOP_ERROR TranslateT("Cannot stop Messenger service")
+// "Не могу запустить Службу Сообщений"
+#define T_START_ERROR TranslateT("Cannot start Messenger service")
+// "Не могу включить Службу Сообщений"
+#define T_ENABLE_ERROR TranslateT("Cannot enable Messenger service")
+// "Не могу создать приёмный мейлслот"
+#define T_CREATE_ERROR TranslateT("Cannot create receiving mailslot")
+// "Ошибка"
+#define T_ERROR TranslateT("Error")
+// Название сервиса Messenger
+#define MESSENGER _T("Messenger")
+// Название мейлслота Messenger
+#define MESSENGER_MAIL _T("messngr")
+// Время доверия IP адресу (3 часа)
+#define MAX_TRUSTED_IP_TIME 3*60*60
+// Минимальнок время обновления контакта (раз в 30 с)
+#define MIN_PING_INTERVAL 30
+
+typedef struct _ContactData
+{
+ HANDLE cookie; // Идентификатор запроса ядра миранды для ответа
+ HANDLE hContact; // Хэндлер контакта
+} ContactData;
+
+typedef struct _SendMsgData
+{
+ HANDLE cookie; // Идентификатор запроса ядра миранды для ответа
+ HANDLE hContact; // Хэндлер контакта
+ CString text;
+} SendMsgData;
+
+typedef struct _ThreadEvent
+{
+ HANDLE evt; // Хэндлер события
+ HANDLE cookie; // Идентификатор запроса ядра миранды для ответа
+} ThreadEvent;
+
+typedef CAtlMap < WPARAM, CString > CIntStrMap;
+typedef CAtlMap < HANDLE, ThreadEvent > CThreadContactMap;
+
+// Класс для конвертации ANSI строки в OEM и последующего хранения
+class COemString
+{
+public:
+ COemString(LPCTSTR ansi) :
+ len( (size_t)lstrlen( ansi ) + 1 ),
+ pos( 0 )
+ {
+ oem = (LPSTR)mir_alloc( len );
+ CharToOemBuff( ansi, oem, (DWORD)len );
+ }
+
+ ~COemString()
+ {
+ mir_free( oem );
+ }
+
+ inline operator LPCSTR() const
+ {
+ return oem + pos;
+ }
+
+ // Длина строки (без null)
+ inline int GetLength() const
+ {
+ return (int)( len - pos - 1 );
+ }
+
+ // "Отрезание" от начала строки
+ inline void CutFromStart(int n)
+ {
+ if ( GetLength() > n )
+ pos += n;
+ else
+ pos = len - 1;
+ }
+
+protected:
+ size_t len;
+ size_t pos;
+ LPSTR oem;
+};
+
+// Класс для конвертации OEM строки в ANSI и последующего хранения
+class CAnsiString
+{
+public:
+ CAnsiString(LPCSTR oem) :
+ len( lstrlenA( oem ) + 1 ),
+ pos( 0 )
+ {
+ ansi = (LPTSTR)mir_alloc( len * sizeof( TCHAR ) );
+ OemToCharBuff( oem, ansi, (DWORD)len );
+ }
+ ~CAnsiString()
+ {
+ mir_free( ansi );
+ }
+ inline operator LPCTSTR() const
+ {
+ return ansi + pos;
+ }
+ // Длина строки (без null)
+ inline int GetLength() const
+ {
+ return len - pos - 1;
+ }
+ // "Отрезание" от начала строки
+ inline void CutFromStart(int n)
+ {
+ if ( len - pos - 1 > n )
+ pos += n;
+ else
+ pos = len - 1;
+ }
+protected:
+ int len;
+ int pos;
+ LPTSTR ansi;
+};
+
+extern volatile WPARAM pluginRequestedStatus; // Требуемый статус
+extern volatile WPARAM pluginCurrentStatus; // Текущий статус
+extern CIntStrMap pluginStatusMessage; // Карта статусных сообщений
+extern CString pluginMachineName; // Имя компьютера
+extern CString pluginUserName; // Имя пользователя
+extern CString pluginDomainName; // Имя домена или группы
+extern HMODULE pluginModule; // Хэндлер на модуль
+extern volatile bool pluginInstalled; // Флаг разрешения работы плагина,
+ // сбрасывается в false при деинсталляции
+ // или при выключении
+extern volatile bool pluginInitialized; // Флаг завершения инициализации плагина
+extern HANDLE pluginNetLibUser; // Хэндлер NetLib
+extern HANDLE pluginInternalState; // Хэндлер события (сигнальное состояние - плагин остановлен)
+extern bool pluginChatEnabled; // Используем Chat-плагин?
+extern OSVERSIONINFO pluginOS; // Версия системы
+
+// Получение ника контакта
+CString GetNick(HANDLE hContact);
+// Установка ника контакта
+void SetNick(HANDLE hContact, LPCTSTR szNick);
+// Вывод в стандартный для миранды лог NetLib
+int LOG(const char *fmt,...);
+// Получение пути где хранятся аватары
+void GetAvatarCache(LPTSTR szPath);
+// Получение "уникального" числа
+HANDLE GenerateCookie();
+// Win32 API версия функции time() (для освобождения от зависимости CRT)
+DWORD time();
+// Определение принадлежности контакта плагину
+bool IsMyContact(HANDLE hContact);
+// Установка статуса плагина
+void SetPluginStatus(WPARAM status);
+// Включение всех подсистем плагина
+bool InternalStartup();
+// Выключение всех подсистем плагина
+void InternalShutdown();
+// Перевод плагина в Online
+void GotoOnline();
+// Перевод плагина в Offline
+void GotoOffline();
+// Рабочая нить асинхронного перехода плагина в Online
+void GotoOnlineTread(LPVOID status);
+// Рабочая нить асинхронного перехода плагина в Offline
+void GotoOfflineTread(LPVOID status);
+// Рабочая нить асинхронного получения эвей-сообщения контакта
+void GetAwayMsgThread(LPVOID param);
+// Рабочая нить асинхронного возвращения информации о контакте
+void GetInfoThread(LPVOID param);
+// Автоответчик статусного сообщения
+void Autoanswer(HANDLE hContact);
+// Приём сообщения, проверка на дубликаты, очистка и отправка в базу Miranda IM
+void ReceiveContactMessage(LPCTSTR msg_from, LPCTSTR msg_to, LPCTSTR msg_text, int msg_len);
+// Добавление контакта по имени (с нахождением его IP-адреса)
+HANDLE AddToListByName (const CString& sName, WPARAM flags, LPCTSTR notes, bool bInteractive, bool bGroup);
+// Получение "nameL" | ("nameH" << 32) контакта
+DWORD GetElapsed(HANDLE hContact, const char* name);
+// Установка "LastSeen" контакта равным текущему времени
+void SetElapsed(HANDLE hContact, const char* name);
+// Поиск контакта по "IP"
+HANDLE GetContact(ip addr);
+// Поиск контакта по "Nick"
+HANDLE GetContact(LPCTSTR name);
+// Установка статуса контакта (simple == true - установка только online/offline)
+void SetContactStatus(HANDLE hContact, int status, bool simple);
+// Установка эвей-текста контакта
+void SetContactAway(HANDLE hContact, LPCSTR away);
+// Установка аватара контакта
+void SetContactAvatar(HANDLE hContact, LPCVOID pBuffer, DWORD nLength);
+// Получение IP-адреса контакта (с проверкой и ресолвингом, если требуется)
+ip GetContactIP(HANDLE hContact);
+// Установка "IP" контакта
+void SetContactIP(HANDLE hContact, ip addr);
+// Это групповой контакт?
+bool IsGroup(HANDLE hContact);
+// Установка группового контакта
+void SetGroup(HANDLE hContact, bool bGroup);
+// Используется старый метод онлайн-проверки?
+bool IsLegacyOnline(HANDLE hContact);
+// Установка старого метода онлайн-проверки
+void SetLegacyOnline(HANDLE hContact, bool bOnline);
+// Посылка сообщения
+bool SendContactMessage(HANDLE hContact, LPCTSTR msg, DWORD& err);
+// Рабочая нить асинхронной посылки сообщения
+void SendMsgThread(LPVOID param);
+// Рабочая нить асинхронного запроса аватара
+void GetAvatarInfoThread(LPVOID param);
+// Моё имя?
+bool IsItMe(LPCTSTR name);
+// Нормализация разбиения сообщения на строки
+void Normalize(CString& msg);
+// Получение списка рабочих групп / доменов
+void EnumWorkgroups(CAtlList< CString >& lst, LPNETRESOURCE hRoot = NULL);
+
+// Макрос организации цикла перебора своих контактов в списке контактов
+#define FOR_EACH_CONTACT(h) \
+ HANDLE h = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); \
+ for ( ; h != NULL; \
+ h = (HANDLE)CallService( MS_DB_CONTACT_FINDNEXT, (WPARAM)h, 0 ) ) \
+ if ( IsMyContact( h ) )
+
+// Макрос для печати названия статуса (отладочный)
+#define STATUS2TEXT(s) \
+ ((((s) == ID_STATUS_OFFLINE) ? "Offline" : \
+ (((s) == ID_STATUS_ONLINE) ? "Online" : \
+ (((s) == ID_STATUS_AWAY) ? "Away" : \
+ (((s) == ID_STATUS_DND) ? "DND" : \
+ (((s) == ID_STATUS_NA) ? "NA" : \
+ (((s) == ID_STATUS_OCCUPIED) ? "Occupied" : \
+ (((s) == ID_STATUS_FREECHAT) ? "Free to chat" : \
+ (((s) == ID_STATUS_INVISIBLE) ? "Invisible" : \
+ (((s) == ID_STATUS_ONTHEPHONE) ? "On the phone" : \
+ (((s) == ID_STATUS_OUTTOLUNCH) ? "Out to lunch" : \
+ (((s) == ID_STATUS_IDLE) ? "Idle" : \
+ (((s) == (ID_STATUS_CONNECTING + 0)) ? "Connecting 1" : \
+ (((s) == (ID_STATUS_CONNECTING + 1)) ? "Connecting 2" : \
+ (((s) < (ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES)) ? "Connecting > 2" : \
+ "Unknown")))))))))))))))