1 #pragma once
2 
3 #include "DelayedLink.h"
4 #include "Plugins.h"
5 
6 #ifndef byte
7 typedef unsigned char byte;
8 #endif
9 
10 namespace Plugins {
11 
12 	extern std::mutex PythonMutex;			// controls access to Python
13 
14 	class CPluginMessageBase
15 	{
16 	public:
~CPluginMessageBase(void)17 		virtual ~CPluginMessageBase(void) {};
18 
19 		CPlugin*	m_pPlugin;
20 		std::string	m_Name;
21 		int			m_HwdID;
22 		int			m_Unit;
23 		bool		m_Delay;
24 		time_t		m_When;
25 
26 	protected:
CPluginMessageBase(CPlugin * pPlugin)27 		CPluginMessageBase(CPlugin* pPlugin) : m_pPlugin(pPlugin), m_HwdID(pPlugin->m_HwdID), m_Unit(-1), m_Delay(false)
28 		{
29 			m_Name = __func__;
30 			m_When = time(0);
31 		};
32 		virtual void ProcessLocked() = 0;
33 	public:
Name()34 		virtual const char* Name() { return m_Name.c_str(); };
Plugin()35 		virtual const CPlugin*	Plugin() { return m_pPlugin; };
Process()36 		virtual void Process()
37 		{
38 			std::lock_guard<std::mutex> l(PythonMutex);
39 			m_pPlugin->RestoreThread();
40 			ProcessLocked();
41 			m_pPlugin->ReleaseThread();
42 		};
43 	};
44 
45 	// Handles lifecycle management of the Python Connection object
46 	class CHasConnection
47 	{
48 	public:
CHasConnection(PyObject * Connection)49 		CHasConnection(PyObject* Connection) : m_pConnection(Connection)
50 		{
51 			Py_XINCREF(m_pConnection);
52 		};
~CHasConnection()53 		~CHasConnection()
54 		{
55 			Py_XDECREF(m_pConnection);
56 		}
57 		PyObject*	m_pConnection;
58 	};
59 
60 	class InitializeMessage : public CPluginMessageBase
61 	{
62 	public:
InitializeMessage(CPlugin * pPlugin)63 		InitializeMessage(CPlugin* pPlugin) : CPluginMessageBase(pPlugin) { m_Name = __func__; };
Process()64 		virtual void Process()
65 		{
66 			std::lock_guard<std::mutex> l(PythonMutex);
67 			m_pPlugin->Initialise();
68 		};
ProcessLocked()69 		virtual void ProcessLocked() {};
70 	};
71 
72 	// Base callback message class
73 	class CCallbackBase : public CPluginMessageBase
74 	{
75 	protected:
76 		std::string	m_Callback;
77 		virtual void ProcessLocked() = 0;
78 	public:
CCallbackBase(CPlugin * pPlugin,const std::string & Callback)79 		CCallbackBase(CPlugin* pPlugin, const std::string &Callback) : CPluginMessageBase(pPlugin), m_Callback(Callback) {};
Callback(PyObject * pParams)80 		virtual void Callback(PyObject* pParams) { if (m_Callback.length()) m_pPlugin->Callback(m_Callback, pParams); };
PythonName()81 		virtual const char* PythonName() { return m_Callback.c_str(); };
82 	};
83 
84 	class onStartCallback : public CCallbackBase
85 	{
86 	public:
onStartCallback(CPlugin * pPlugin)87 		onStartCallback(CPlugin* pPlugin) : CCallbackBase(pPlugin, "onStart") { m_Name = __func__; };
88 	protected:
ProcessLocked()89 		virtual void ProcessLocked()
90 		{
91 			m_pPlugin->Start();
92 			Callback(NULL);
93 		};
94 	};
95 
96 	class onHeartbeatCallback : public CCallbackBase
97 	{
98 	public:
onHeartbeatCallback(CPlugin * pPlugin)99 		onHeartbeatCallback(CPlugin* pPlugin) : CCallbackBase(pPlugin, "onHeartbeat") { m_Name = __func__; };
100 	protected:
ProcessLocked()101 		virtual void ProcessLocked()
102 		{
103 			Callback(NULL);
104 		};
105 	};
106 
107 #ifdef _WIN32
string_to_wstring(const std::string & str,int codepage)108 static std::wstring string_to_wstring(const std::string &str, int codepage)
109 {
110 	if (str.empty()) return std::wstring();
111 	int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
112 	std::wstring res(sz, 0);
113 	MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
114 	return res;
115 }
116 
wstring_to_string(const std::wstring & wstr,int codepage)117 static std::string wstring_to_string(const std::wstring &wstr, int codepage)
118 {
119 	if (wstr.empty()) return std::string();
120 	int sz = WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), 0, 0, 0, 0);
121 	std::string res(sz, 0);
122 	WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), &res[0], sz, 0, 0);
123 	return res;
124 }
125 
get_utf8_from_ansi(const std::string & utf8,int codepage)126 static std::string get_utf8_from_ansi(const std::string &utf8, int codepage)
127 {
128 	std::wstring utf16 = string_to_wstring(utf8, codepage);
129 	std::string ansi = wstring_to_string(utf16, CP_UTF8);
130 	return ansi;
131 }
132 #endif
133 
134 	class onConnectCallback : public CCallbackBase, public CHasConnection
135 	{
136 	public:
onConnectCallback(CPlugin * pPlugin,PyObject * Connection)137 		onConnectCallback(CPlugin* pPlugin, PyObject* Connection) : CCallbackBase(pPlugin, "onConnect"), CHasConnection(Connection) { m_Name = __func__; };
onConnectCallback(CPlugin * pPlugin,PyObject * Connection,const int Code,const std::string & Text)138 		onConnectCallback(CPlugin* pPlugin, PyObject* Connection, const int Code, const std::string &Text) : CCallbackBase(pPlugin, "onConnect"), CHasConnection(Connection), m_Status(Code), m_Text(Text) { m_Name = __func__; };
139 		int						m_Status;
140 		std::string				m_Text;
141 	protected:
ProcessLocked()142 		virtual void ProcessLocked()
143 		{
144 #ifdef _WIN32
145 			std::string textUTF8 = get_utf8_from_ansi(m_Text, GetACP());
146 #else
147 			std::string textUTF8 = m_Text; // TODO: Is it safe to assume non-Windows will always be UTF-8?
148 #endif
149 			Callback(Py_BuildValue("Ois", m_pConnection, m_Status, textUTF8.c_str()));  // 0 is success else socket failure code
150 		};
151 	};
152 
153 	class onDisconnectCallback : public CCallbackBase, public CHasConnection
154 	{
155 	public:
onDisconnectCallback(CPlugin * pPlugin,PyObject * Connection)156 		onDisconnectCallback(CPlugin* pPlugin, PyObject* Connection) : CCallbackBase(pPlugin, "onDisconnect"), CHasConnection(Connection) { m_Name = __func__; };
157 	protected:
ProcessLocked()158 		virtual void ProcessLocked()
159 		{
160 			Callback(Py_BuildValue("(O)", m_pConnection));  // 0 is success else socket failure code
161 		};
162 	};
163 
164 	class onDeviceAddedCallback : public CCallbackBase
165 	{
166 	public:
onDeviceAddedCallback(CPlugin * pPlugin,int Unit)167 		onDeviceAddedCallback(CPlugin* pPlugin, int Unit) : CCallbackBase(pPlugin, "onDeviceAdded") { m_Unit = Unit; };
168 	protected:
ProcessLocked()169 		virtual void ProcessLocked()
170 		{
171 			m_pPlugin->onDeviceAdded(m_Unit);
172 
173 			PyObject*	pParams = Py_BuildValue("(i)", m_Unit);
174 			Callback(pParams);
175 		};
176 	};
177 
178 	class onDeviceModifiedCallback : public CCallbackBase
179 	{
180 	public:
onDeviceModifiedCallback(CPlugin * pPlugin,int Unit)181 		onDeviceModifiedCallback(CPlugin* pPlugin, int Unit) : CCallbackBase(pPlugin, "onDeviceModified") { m_Unit = Unit; };
182 	protected:
ProcessLocked()183 		virtual void ProcessLocked()
184 		{
185 			m_pPlugin->onDeviceModified(m_Unit);
186 
187 			PyObject*	pParams = Py_BuildValue("(i)", m_Unit);
188 			Callback(pParams);
189 		};
190 	};
191 
192 	class onDeviceRemovedCallback : public CCallbackBase
193 	{
194 	public:
onDeviceRemovedCallback(CPlugin * pPlugin,int Unit)195 		onDeviceRemovedCallback(CPlugin* pPlugin, int Unit) : CCallbackBase(pPlugin, "onDeviceRemoved") { m_Unit = Unit; };
196 	protected:
ProcessLocked()197 		virtual void ProcessLocked()
198 		{
199 			PyObject*	pParams = Py_BuildValue("(i)", m_Unit);
200 			Callback(pParams);
201 
202 			m_pPlugin->onDeviceRemoved(m_Unit);
203 		};
204 	};
205 
206 	class onCommandCallback : public CCallbackBase
207 	{
208 	public:
onCommandCallback(CPlugin * pPlugin,int Unit,const std::string & Command,const int level,const std::string & color)209 		onCommandCallback(CPlugin* pPlugin, int Unit, const std::string& Command, const int level, const std::string &color) : CCallbackBase(pPlugin, "onCommand")
210 		{
211 			m_Name = __func__;
212 			m_Unit = Unit;
213 			m_fLevel = -273.15f;
214 			m_Command = Command;
215 			m_iLevel = level;
216 			m_iColor = color;
217 		};
onCommandCallback(CPlugin * pPlugin,int Unit,const std::string & Command,const float level)218 		onCommandCallback(CPlugin* pPlugin, int Unit, const std::string& Command, const float level) : CCallbackBase(pPlugin, "onCommand")
219 		{
220 			m_Name = __func__;
221 			m_Unit = Unit;
222 			m_fLevel = level;
223 			m_Command = Command;
224 			m_iLevel = -1;
225 			m_iColor = "";
226 		};
227 		std::string				m_Command;
228 		std::string				m_iColor;
229 		int						m_iLevel;
230 		float					m_fLevel;
231 
232 	protected:
ProcessLocked()233 		virtual void ProcessLocked()
234 		{
235 			PyObject*	pParams;
236 			if (m_fLevel != -273.15f)
237 			{
238 				pParams = Py_BuildValue("isfs", m_Unit, m_Command.c_str(), m_fLevel, "");
239 			}
240 			else
241 			{
242 				pParams = Py_BuildValue("isis", m_Unit, m_Command.c_str(), m_iLevel, m_iColor.c_str());
243 			}
244 			Callback(pParams);
245 		};
246 	};
247 
248 	class onSecurityEventCallback : public CCallbackBase
249 	{
250 	public:
onSecurityEventCallback(CPlugin * pPlugin,int Unit,const int level,const std::string & Description)251 		onSecurityEventCallback(CPlugin* pPlugin, int Unit, const int level, const std::string& Description) : CCallbackBase(pPlugin, "onSecurityEvent")
252 		{
253 			m_Name = __func__;
254 			m_Unit = Unit;
255 			m_iLevel = level;
256 			m_Description = Description;
257 		};
258 		int				m_iLevel;
259 		std::string		m_Description;
260 
261 	protected:
ProcessLocked()262 		virtual void ProcessLocked()
263 		{
264 			PyObject*	pParams = Py_BuildValue("iis", m_Unit, m_iLevel, m_Description.c_str());
265 			Callback(pParams);
266 		};
267 	};
268 
269 	class onMessageCallback : public CCallbackBase, public CHasConnection
270 	{
271 	public:
onMessageCallback(CPlugin * pPlugin,PyObject * Connection,const std::string & Buffer)272 		onMessageCallback(CPlugin* pPlugin, PyObject* Connection, const std::string& Buffer) : CCallbackBase(pPlugin, "onMessage"), CHasConnection(Connection), m_Data(NULL)
273 		{
274 			m_Name = __func__;
275 			m_Buffer.reserve(Buffer.length());
276 			m_Buffer.assign((const byte*)Buffer.c_str(), (const byte*)Buffer.c_str()+Buffer.length());
277 		};
onMessageCallback(CPlugin * pPlugin,PyObject * Connection,const std::vector<byte> & Buffer)278 		onMessageCallback(CPlugin* pPlugin, PyObject* Connection, const std::vector<byte>& Buffer) : CCallbackBase(pPlugin, "onMessage"), CHasConnection(Connection), m_Data(NULL)
279 		{
280 			m_Name = __func__;
281 			m_Buffer = Buffer;
282 		};
onMessageCallback(CPlugin * pPlugin,PyObject * Connection,PyObject * pData)283 		onMessageCallback(CPlugin* pPlugin, PyObject* Connection, PyObject*	pData) : CCallbackBase(pPlugin, "onMessage"), CHasConnection(Connection)
284 		{
285 			m_Name = __func__;
286 			m_Data = pData;
287 		};
288 		std::vector<byte>		m_Buffer;
289 		PyObject*				m_Data;
290 
291 	protected:
ProcessLocked()292 		virtual void ProcessLocked()
293 		{
294 			PyObject*	pParams = NULL;
295 
296 			// Data is stored in a single vector of bytes
297 			if (m_Buffer.size())
298 			{
299 				pParams = Py_BuildValue("Oy#", m_pConnection, &m_Buffer[0], m_Buffer.size());
300 				Callback(pParams);
301 			}
302 
303 			// Data is in a dictionary
304 			if (m_Data)
305 			{
306 				pParams = Py_BuildValue("OO", m_pConnection, m_Data);
307 				Callback(pParams);
308 				Py_XDECREF(m_Data);
309 			}
310 		}
311 	};
312 
313 	class onNotificationCallback : public CCallbackBase
314 	{
315 	public:
onNotificationCallback(CPlugin * pPlugin,const std::string & Subject,const std::string & Text,const std::string & Name,const std::string & Status,int Priority,const std::string & Sound,const std::string & ImageFile)316 		onNotificationCallback(CPlugin* pPlugin,
317 							const std::string& Subject,
318 							const std::string& Text,
319 							const std::string& Name,
320 							const std::string& Status,
321 							int Priority,
322 							const std::string& Sound,
323 							const std::string& ImageFile) : CCallbackBase(pPlugin, "onNotification")
324 		{
325 			m_Name = __func__;
326 			m_Subject = Subject;
327 			m_Text = Text;
328 			m_SuppliedName = Name;
329 			m_Status = Status;
330 			m_Priority = Priority;
331 			m_Sound = Sound;
332 			m_ImageFile = ImageFile;
333 		};
334 
335 		std::string				m_Subject;
336 		std::string				m_Text;
337 		std::string				m_SuppliedName;
338 		std::string				m_Status;
339 		int						m_Priority;
340 		std::string				m_Sound;
341 		std::string				m_ImageFile;
342 
343 	protected:
ProcessLocked()344 		virtual void ProcessLocked()
345 		{
346 			PyObject*	pParams = Py_BuildValue("ssssiss", m_SuppliedName.c_str(), m_Subject.c_str(), m_Text.c_str(), m_Status.c_str(), m_Priority, m_Sound.c_str(), m_ImageFile.c_str());
347 			Callback(pParams);
348 		};
349 	};
350 
351 	class onStopCallback : public CCallbackBase
352 	{
353 	public:
onStopCallback(CPlugin * pPlugin)354 		onStopCallback(CPlugin* pPlugin) : CCallbackBase(pPlugin, "onStop") { m_Name = __func__; };
355 	protected:
ProcessLocked()356 		virtual void ProcessLocked()
357 		{
358 			Callback(NULL);
359 			m_pPlugin->Stop();
360 		};
361 	};
362 
363 	// Base directive message class
364 	class CDirectiveBase : public CPluginMessageBase
365 	{
366 	protected:
367 		virtual void ProcessLocked() = 0;
368 	public:
CDirectiveBase(CPlugin * pPlugin)369 		CDirectiveBase(CPlugin* pPlugin) : CPluginMessageBase(pPlugin) {};
Process()370 		virtual void Process() {
371 			std::lock_guard<std::mutex> l(PythonMutex);
372 			m_pPlugin->RestoreThread();
373 			ProcessLocked();
374 			m_pPlugin->ReleaseThread();
375 		};
376 	};
377 
378 	class ProtocolDirective : public CDirectiveBase, public CHasConnection
379 	{
380 	public:
ProtocolDirective(CPlugin * pPlugin,PyObject * Connection)381 		ProtocolDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()382 		virtual void ProcessLocked() { m_pPlugin->ConnectionProtocol(this); };
383 	};
384 
385 	class ConnectDirective : public CDirectiveBase, public CHasConnection
386 	{
387 	public:
ConnectDirective(CPlugin * pPlugin,PyObject * Connection)388 		ConnectDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()389 		virtual void ProcessLocked() { m_pPlugin->ConnectionConnect(this); };
390 	};
391 
392 	class ListenDirective : public CDirectiveBase, public CHasConnection
393 	{
394 	public:
ListenDirective(CPlugin * pPlugin,PyObject * Connection)395 		ListenDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()396 		virtual void ProcessLocked() { m_pPlugin->ConnectionListen(this); };
397 	};
398 
399 	class DisconnectDirective : public CDirectiveBase, public CHasConnection
400 	{
401 	public:
DisconnectDirective(CPlugin * pPlugin,PyObject * Connection)402 		DisconnectDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()403 		virtual void ProcessLocked() { m_pPlugin->ConnectionDisconnect(this); };
404 	};
405 
406 	class WriteDirective : public CDirectiveBase, public CHasConnection
407 	{
408 	public:
409 		PyObject*		m_Object;
WriteDirective(CPlugin * pPlugin,PyObject * Connection,PyObject * pData,const int Delay)410 		WriteDirective(CPlugin* pPlugin, PyObject* Connection, PyObject* pData, const int Delay) : CDirectiveBase(pPlugin), CHasConnection(Connection)
411 		{
412 			m_Name = __func__;
413 			m_Object = pData;
414 			if (m_Object)
415 				Py_INCREF(m_Object);
416 			if (Delay)
417 			{
418 				m_When += Delay;
419 				m_Delay=true;
420 			}
421 		};
~WriteDirective()422 		~WriteDirective()
423 		{
424 			if (m_Object)
425 				Py_DECREF(m_Object);
426 		}
427 
ProcessLocked()428 		virtual void ProcessLocked() { m_pPlugin->ConnectionWrite(this); };
429 	};
430 
431 	class SettingsDirective : public CDirectiveBase
432 	{
433 	public:
SettingsDirective(CPlugin * pPlugin)434 		SettingsDirective(CPlugin* pPlugin) : CDirectiveBase(pPlugin) { m_Name = __func__; };
ProcessLocked()435 		virtual void ProcessLocked() { m_pPlugin->LoadSettings(); };
436 	};
437 
438 	class PollIntervalDirective : public CDirectiveBase
439 	{
440 	public:
PollIntervalDirective(CPlugin * pPlugin,const int PollInterval)441 		PollIntervalDirective(CPlugin* pPlugin, const int PollInterval) : CDirectiveBase(pPlugin), m_Interval(PollInterval) { m_Name = __func__; };
442 		int						m_Interval;
ProcessLocked()443 		virtual void ProcessLocked() {m_pPlugin->PollInterval(m_Interval); };
444 	};
445 
446 	class NotifierDirective : public CDirectiveBase
447 	{
448 	public:
NotifierDirective(CPlugin * pPlugin,const char * Name)449 		NotifierDirective(CPlugin* pPlugin, const char* Name) : CDirectiveBase(pPlugin), m_NotifierName(Name) { m_Name = __func__; };
450 		std::string		m_NotifierName;
ProcessLocked()451 		virtual void ProcessLocked() { m_pPlugin->Notifier(m_NotifierName); };
452 	};
453 
454 	// Base event message class
455 	class CEventBase : public CPluginMessageBase
456 	{
457 	protected:
458 		virtual void ProcessLocked() = 0;
459 	public:
CEventBase(CPlugin * pPlugin)460 		CEventBase(CPlugin* pPlugin) : CPluginMessageBase(pPlugin) {};
461 	};
462 
463 	class ReadEvent : public CEventBase, public CHasConnection
464 	{
465 	public:
CEventBase(pPlugin)466 		ReadEvent(CPlugin* pPlugin, PyObject* Connection, const int ByteCount, const unsigned char* Data, const int ElapsedMs = -1) : CEventBase(pPlugin), CHasConnection(Connection)
467 		{
468 			m_Name = __func__;
469 			m_ElapsedMs = ElapsedMs;
470 			m_Buffer.reserve(ByteCount);
471 			m_Buffer.assign(Data, Data + ByteCount);
472 		};
473 		std::vector<byte>		m_Buffer;
474 		int						m_ElapsedMs;
ProcessLocked()475 		virtual void ProcessLocked()
476 		{
477 			m_pPlugin->WriteDebugBuffer(m_Buffer, true);
478 			m_pPlugin->ConnectionRead(this);
479 		};
480 	};
481 
482 	class DisconnectedEvent : public CEventBase, public CHasConnection
483 	{
484 	public:
DisconnectedEvent(CPlugin * pPlugin,PyObject * Connection)485 		DisconnectedEvent(CPlugin* pPlugin, PyObject* Connection) : CEventBase(pPlugin), CHasConnection(Connection), bNotifyPlugin(true) { m_Name = __func__; };
DisconnectedEvent(CPlugin * pPlugin,PyObject * Connection,bool NotifyPlugin)486 		DisconnectedEvent(CPlugin* pPlugin, PyObject* Connection, bool NotifyPlugin) : CEventBase(pPlugin), CHasConnection(Connection), bNotifyPlugin(NotifyPlugin) { m_Name = __func__; };
ProcessLocked()487 		virtual void ProcessLocked() { m_pPlugin->DisconnectEvent(this); };
488 		bool	bNotifyPlugin;
489 	};
490 }
491