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