1 #include "stdafx.h"
2 #include "DomoticzTCP.h"
3 #include "../main/Logger.h"
4 #include "../main/Helper.h"
5 #include "../main/localtime_r.h"
6 #include "../main/mainworker.h"
7 #include "../main/WebServerHelper.h"
8 #include "../webserver/proxyclient.h"
9 
10 #define RETRY_DELAY 30
11 
12 extern http::server::CWebServerHelper m_webservers;
13 
DomoticzTCP(const int ID,const std::string & IPAddress,const unsigned short usIPPort,const std::string & username,const std::string & password)14 DomoticzTCP::DomoticzTCP(const int ID, const std::string &IPAddress, const unsigned short usIPPort, const std::string &username, const std::string &password) :
15 	m_username(username), m_password(password), m_szIPAddress(IPAddress)
16 {
17 	m_HwdID = ID;
18 	m_usIPPort = usIPPort;
19 	m_bIsStarted = false;
20 #ifndef NOCLOUD
21 	b_useProxy = IsValidAPIKey(m_szIPAddress);
22 	b_ProxyConnected = false;
23 #endif
24 }
25 
~DomoticzTCP(void)26 DomoticzTCP::~DomoticzTCP(void)
27 {
28 }
29 
30 #ifndef NOCLOUD
IsValidAPIKey(const std::string & IPAddress)31 bool DomoticzTCP::IsValidAPIKey(const std::string &IPAddress)
32 {
33 	if (IPAddress.find(".") != std::string::npos) {
34 		// we assume an IPv4 address or host name
35 		return false;
36 	}
37 	if (IPAddress.find(":") != std::string::npos) {
38 		// we assume an IPv6 address
39 		return false;
40 	}
41 	// just a simple check
42 	return IPAddress.length() == 15;
43 }
44 #endif
45 
StartHardware()46 bool DomoticzTCP::StartHardware()
47 {
48 	RequestStart();
49 
50 #ifndef NOCLOUD
51 	b_useProxy = IsValidAPIKey(m_szIPAddress);
52 	if (b_useProxy) {
53 		return StartHardwareProxy();
54 	}
55 #endif
56 	//Start worker thread
57 	m_thread = std::make_shared<std::thread>(&DomoticzTCP::Do_Work, this);
58 	SetThreadNameInt(m_thread->native_handle());
59 
60 	return (m_thread != nullptr);
61 }
62 
StopHardware()63 bool DomoticzTCP::StopHardware()
64 {
65 #ifndef NOCLOUD
66 	if (b_useProxy) {
67 		return StopHardwareProxy();
68 	}
69 #endif
70 
71 	if (m_thread)
72 	{
73 		RequestStop();
74 		m_thread->join();
75 		m_thread.reset();
76 	}
77 	m_bIsStarted = false;
78 	return true;
79 }
80 
OnConnect()81 void DomoticzTCP::OnConnect()
82 {
83 	Log(LOG_STATUS, "connected to: %s:%d", m_szIPAddress.c_str(), m_usIPPort);
84 	if (!m_username.empty())
85 	{
86 		char szAuth[300];
87 		snprintf(szAuth, sizeof(szAuth), "AUTH;%s;%s", m_username.c_str(), m_password.c_str());
88 		WriteToHardware((const char*)&szAuth, (const unsigned char)strlen(szAuth));
89 	}
90 	sOnConnected(this);
91 }
92 
OnDisconnect()93 void DomoticzTCP::OnDisconnect()
94 {
95 	Log(LOG_STATUS, "disconnected from: %s:%d", m_szIPAddress.c_str(), m_usIPPort);
96 }
97 
OnData(const unsigned char * pData,size_t length)98 void DomoticzTCP::OnData(const unsigned char *pData, size_t length)
99 {
100 	if (length == 6 && strstr(reinterpret_cast<const char *>(pData), "NOAUTH") != 0)
101 	{
102 		Log(LOG_ERROR, "Authentication failed for user %s on %s:%d", m_username.c_str(), m_szIPAddress.c_str(), m_usIPPort);
103 		return;
104 	}
105 	std::lock_guard<std::mutex> l(readQueueMutex);
106 	onInternalMessage((const unsigned char *)pData, length, false); // Do not check validity, this might be non RFX-message
107 }
108 
OnError(const boost::system::error_code & error)109 void DomoticzTCP::OnError(const boost::system::error_code& error)
110 {
111 	if (
112 		(error == boost::asio::error::address_in_use) ||
113 		(error == boost::asio::error::connection_refused) ||
114 		(error == boost::asio::error::access_denied) ||
115 		(error == boost::asio::error::host_unreachable) ||
116 		(error == boost::asio::error::timed_out) ||
117 		(error == boost::asio::error::host_not_found)
118 		)
119 	{
120 		Log(LOG_ERROR, "Can not connect to: %s:%d (%s)", m_szIPAddress.c_str(), m_usIPPort, error.message().c_str());
121 	}
122 	else if (error != boost::asio::error::eof)
123 	{
124 		Log(LOG_ERROR, "%s", error.message().c_str());
125 	}
126 }
127 
Do_Work()128 void DomoticzTCP::Do_Work()
129 {
130 	connect(m_szIPAddress, m_usIPPort);
131 	int sec_counter = 0;
132 	while (!IsStopRequested(1000))
133 	{
134 		sec_counter++;
135 		if (sec_counter % 12 == 0)
136 			mytime(&m_LastHeartbeat);
137 	}
138 	terminate();
139 
140 	Log(LOG_STATUS, "Worker stopped...");
141 }
142 
WriteToHardware(const char * pdata,const unsigned char length)143 bool DomoticzTCP::WriteToHardware(const char *pdata, const unsigned char length)
144 {
145 #ifndef NOCLOUD
146 	if (b_useProxy)
147 	{
148 		if (isConnectedProxy())
149 		{
150 			writeProxy(pdata, length);
151 			return true;
152 		}
153 	}
154 	else if (ASyncTCP::isConnected())
155 	{
156 		write(std::string((const char*)pdata, length));
157 		return true;
158 	}
159 #else
160 	if (ASyncTCP::isConnected())
161 	{
162 		write(std::string((const char*)pdata, length));
163 		return true;
164 	}
165 #endif
166 	return false;
167 }
168 
isConnected()169 bool DomoticzTCP::isConnected()
170 {
171 #ifndef NOCLOUD
172 	if (b_useProxy)
173 		return isConnectedProxy();
174 	else
175 		return ASyncTCP::isConnected();
176 #else
177 	return ASyncTCP::isConnected();
178 #endif
179 }
180 
181 #ifndef NOCLOUD
CompareToken(const std::string & aToken)182 bool DomoticzTCP::CompareToken(const std::string &aToken)
183 {
184 	return (aToken == token);
185 }
186 
CompareId(const std::string & instanceid)187 bool DomoticzTCP::CompareId(const std::string &instanceid)
188 {
189 	return (m_szIPAddress == instanceid);
190 }
191 
StartHardwareProxy()192 bool DomoticzTCP::StartHardwareProxy()
193 {
194 	if (m_bIsStarted) {
195 		return false; // dont start twice
196 	}
197 	m_bIsStarted = true;
198 	return ConnectInternalProxy();
199 }
200 
ConnectInternalProxy()201 bool DomoticzTCP::ConnectInternalProxy()
202 {
203 	http::server::CProxyClient *proxy;
204 	const int version = 1;
205 	// we temporarily use the instance id as an identifier for this connection, meanwhile we get a token from the proxy
206 	// this means that we connect connect twice to the same server
207 	token = m_szIPAddress;
208 	proxy = m_webservers.GetProxyForMaster(this);
209 	if (proxy) {
210 		proxy->ConnectToDomoticz(m_szIPAddress, m_username, m_password, this, version);
211 		sOnConnected(this); // we do need this?
212 	}
213 	else {
214 		Log(LOG_STATUS, "Delaying Domoticz master login");
215 	}
216 	return true;
217 }
218 
StopHardwareProxy()219 bool DomoticzTCP::StopHardwareProxy()
220 {
221 	DisconnectProxy();
222 	m_bIsStarted = false;
223 	// Avoid dangling pointer if this hardware is removed.
224 	m_webservers.RemoveMaster(this);
225 	return true;
226 }
227 
DisconnectProxy()228 void DomoticzTCP::DisconnectProxy()
229 {
230 	http::server::CProxyClient *proxy;
231 
232 	proxy = m_webservers.GetProxyForMaster(this);
233 	if (proxy) {
234 		proxy->DisconnectFromDomoticz(token, this);
235 	}
236 	b_ProxyConnected = false;
237 }
238 
isConnectedProxy()239 bool DomoticzTCP::isConnectedProxy()
240 {
241 	return b_ProxyConnected;
242 }
243 
writeProxy(const char * data,size_t size)244 void DomoticzTCP::writeProxy(const char *data, size_t size)
245 {
246 	/* send data to slave */
247 	if (isConnectedProxy()) {
248 		http::server::CProxyClient *proxy = m_webservers.GetProxyForMaster(this);
249 		if (proxy) {
250 			proxy->WriteMasterData(token, data, size);
251 		}
252 	}
253 }
254 
FromProxy(const unsigned char * data,size_t datalen)255 void DomoticzTCP::FromProxy(const unsigned char *data, size_t datalen)
256 {
257 	/* data received from slave */
258 	std::lock_guard<std::mutex> l(readQueueMutex);
259 	onInternalMessage(data, datalen);
260 }
261 
GetToken()262 std::string DomoticzTCP::GetToken()
263 {
264 	return token;
265 }
266 
Authenticated(const std::string & aToken,bool authenticated)267 void DomoticzTCP::Authenticated(const std::string &aToken, bool authenticated)
268 {
269 	b_ProxyConnected = authenticated;
270 	token = aToken;
271 	if (authenticated) {
272 		Log(LOG_STATUS, "Domoticz TCP connected via Proxy.");
273 	}
274 }
275 
SetConnected(bool connected)276 void DomoticzTCP::SetConnected(bool connected)
277 {
278 	if (connected) {
279 		ConnectInternalProxy();
280 	}
281 	else {
282 		b_ProxyConnected = false;
283 	}
284 }
285 #endif
286 
287