[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / input / windows / IRServerSuite.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "IRServerSuite.h"
23 #include "IrssMessage.h"
24 #include "input/ButtonTranslator.h"
25 #include "utils/log.h"
26 #include "settings/AdvancedSettings.h"
27 #include "utils/TimeUtils.h"
28 #include <Ws2tcpip.h>
29
30 #define IRSS_PORT 24000
31
32 CRemoteControl g_RemoteControl;
33
34 CRemoteControl::CRemoteControl() : CThread("CRemoteControl")
35 {
36   m_socket = INVALID_SOCKET;
37   m_bInitialized = false;
38   m_isConnecting = false;
39   m_iAttempt     = 0;
40   Reset();
41 }
42
43 CRemoteControl::~CRemoteControl()
44 {
45   Close();
46 }
47
48 void CRemoteControl::Disconnect()
49 {
50   StopThread();
51   Close();
52 }
53
54 void CRemoteControl::Close()
55 {
56   m_isConnecting = false;
57   if (m_socket != INVALID_SOCKET)
58   {
59     if (m_bInitialized)
60     {
61       m_bInitialized = false;
62       CIrssMessage message(IRSSMT_UnregisterClient, IRSSMF_Request | IRSSMF_ForceNotRespond);
63       SendPacket(message);
64     }
65     shutdown(m_socket, SD_BOTH);
66     closesocket(m_socket);
67     m_socket = INVALID_SOCKET;
68   }
69 }
70
71 void CRemoteControl::Reset()
72 {
73   m_button = 0;
74 }
75
76 void CRemoteControl::Initialize()
77 {
78   if (m_isConnecting || m_bInitialized) return;
79   //trying to connect when there is nothing to connect to is kinda slow so kick it off in a thread.
80   Create();
81 }
82
83 void CRemoteControl::Process()
84 {
85   unsigned int iMsRetryDelay = 5000;
86   unsigned int time = XbmcThreads::SystemClockMillis() - iMsRetryDelay;
87   // try to connect 60 times @ a 5 second interval (5 minutes)
88   // multiple tries because irss service might be up and running a little later then xbmc on boot.
89   while (!m_bStop && m_iAttempt <= 60)
90   {
91     if (XbmcThreads::SystemClockMillis() - time >= iMsRetryDelay)
92     {
93       time = XbmcThreads::SystemClockMillis();
94       if (Connect())
95         break;
96
97       if(m_iAttempt == 0)
98         CLog::Log(LOGINFO, "CRemoteControl::Process - failed to connect to irss, will keep retrying every %d seconds", iMsRetryDelay / 1000);
99
100       m_iAttempt++;
101     }
102     Sleep(1000);
103   }
104   m_iAttempt     = 0;
105 }
106
107 bool CRemoteControl::Connect()
108 {
109   char     namebuf[NI_MAXHOST], portbuf[NI_MAXSERV];
110   struct   addrinfo hints = {};
111   struct   addrinfo *result, *addr;
112   char     service[33];
113   int      res;
114
115   hints.ai_family   = AF_UNSPEC;
116   hints.ai_socktype = SOCK_STREAM;
117   hints.ai_protocol = IPPROTO_TCP;
118   sprintf(service, "%d", IRSS_PORT);
119
120   res = getaddrinfo("localhost", service, &hints, &result);
121   if(res)
122   {
123     if(m_iAttempt == 0)
124       CLog::Log(LOGDEBUG, "CRemoteControl::Connect - getaddrinfo failed: %s", gai_strerror(res));
125     return false;
126   }
127
128   for(addr = result; addr; addr = addr->ai_next)
129   {
130     if(getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf),NI_NUMERICHOST))
131     {
132       strcpy(namebuf, "[unknown]");
133       strcpy(portbuf, "[unknown]");
134     }
135
136     if(m_iAttempt == 0)
137       CLog::Log(LOGDEBUG, "CRemoteControl::Connect - connecting to: %s:%s ...", namebuf, portbuf);
138
139     m_socket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
140     if(m_socket == INVALID_SOCKET)
141       continue;
142
143     if(connect(m_socket, addr->ai_addr, addr->ai_addrlen) != SOCKET_ERROR)
144       break;
145
146     closesocket(m_socket);
147     m_socket = INVALID_SOCKET;
148   }
149
150   freeaddrinfo(result);
151   if(m_socket == INVALID_SOCKET)
152   {
153     if(m_iAttempt == 0)
154       CLog::Log(LOGDEBUG, "CRemoteControl::Connect - failed to connect");
155     Close();
156     return false;
157   }
158
159   u_long iMode = 1; //non-blocking
160   if (ioctlsocket(m_socket, FIONBIO, &iMode) == SOCKET_ERROR)
161   {
162     if(m_iAttempt == 0)
163       CLog::Log(LOGERROR, "IRServerSuite: failed to set socket to non-blocking.");
164     Close();
165     return false;
166   }
167
168   //register
169   CIrssMessage mess(IRSSMT_RegisterClient, IRSSMF_Request);
170   if (!SendPacket(mess))
171   {
172     if(m_iAttempt == 0)
173       CLog::Log(LOGERROR, "IRServerSuite: failed to send RegisterClient packet.");
174     return false;
175   }
176   m_isConnecting = true;
177   return true;
178 }
179
180 bool CRemoteControl::SendPacket(CIrssMessage& message)
181 {
182   int iSize = 0;
183   char* bytes = message.ToBytes(iSize);
184   char buffer[4];
185   uint32_t len = htonl(iSize);
186   memcpy(&buffer[0], &len, 4);
187   bool bResult = WriteN(&buffer[0], 4);
188   if (bResult)
189   {
190     bResult = WriteN(bytes, iSize);
191   }
192   delete[] bytes;
193   if (!bResult)
194   {
195     Close();
196     return false;
197   }
198   return true;
199 }
200
201
202 void CRemoteControl::Update()
203 {
204   if ((!m_bInitialized && !m_isConnecting) || (m_socket == INVALID_SOCKET))
205   {
206     return;
207   }
208
209   CIrssMessage mess;
210   if (!ReadPacket(mess))
211   {
212     return;
213   }
214   switch (mess.GetType())
215   {
216   case IRSSMT_RegisterClient:
217     m_isConnecting = false;
218     if ((mess.GetFlags() & IRSSMF_Success) != IRSSMF_Success)
219     {
220       //uh oh, it failed to register
221       Close();
222       CLog::Log(LOGERROR, "IRServerSuite: failed to register XBMC as a client.");
223     }
224     else
225     {
226       m_bInitialized = true;
227       //request info about receivers
228       CIrssMessage mess(IRSSMT_DetectedReceivers, IRSSMF_Request);
229       if (!SendPacket(mess))
230       {
231         CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet.");
232       }
233       mess.SetType(IRSSMT_AvailableReceivers);
234       if (!SendPacket(mess))
235       {
236         CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet.");
237       }
238       mess.SetType(IRSSMT_ActiveReceivers);
239       if (!SendPacket(mess))
240       {
241         CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet.");
242       }
243     }
244     break;
245   case IRSSMT_RemoteEvent:
246     HandleRemoteEvent(mess);
247     break;
248   case IRSSMT_Error:
249     //I suppose the errormessage is in the packet somewhere...
250     CLog::Log(LOGERROR, "IRServerSuite: we got an error message.");
251     break;
252   case IRSSMT_ServerShutdown:
253     Close();
254     break;
255   case IRSSMT_ServerSuspend:
256     //should we do something?
257     break;
258   case IRSSMT_ServerResume:
259     //should we do something?
260     break;
261   case IRSSMT_AvailableReceivers:
262     {
263       uint32_t size = mess.GetDataSize();
264       if (size > 0)
265       {
266         char* data = mess.GetData();
267         char* availablereceivers = new char[size + 1];
268         memcpy(availablereceivers, data, size);
269         availablereceivers[size] = '\0';
270         CLog::Log(LOGINFO, "IRServerSuite: Available receivers: %s", availablereceivers);
271         delete[] availablereceivers;
272       }
273     }
274     break;
275   case IRSSMT_DetectedReceivers:
276     {
277       uint32_t size = mess.GetDataSize();
278       if (size > 0)
279       {
280         char* data = mess.GetData();
281         char* detectedreceivers = new char[size + 1];
282         memcpy(detectedreceivers, data, size);
283         detectedreceivers[size] = '\0';
284         CLog::Log(LOGINFO, "IRServerSuite: Detected receivers: %s", detectedreceivers);
285         delete[] detectedreceivers;
286       }
287     }
288     break;
289   case IRSSMT_ActiveReceivers:
290     {
291       uint32_t size = mess.GetDataSize();
292       if (size > 0)
293       {
294         char* data = mess.GetData();
295         char* activereceivers = new char[size + 1];
296         memcpy(activereceivers, data, size);
297         activereceivers[size] = '\0';
298         CLog::Log(LOGINFO, "IRServerSuite: Active receivers: %s", activereceivers);
299         delete[] activereceivers;
300       }
301     }
302     break;
303   }
304 }
305
306 bool CRemoteControl::HandleRemoteEvent(CIrssMessage& message)
307 {
308   try
309   {
310     //flag should be notify, maybe check it?
311     char* data = message.GetData();
312     uint32_t datalen = message.GetDataSize();
313     char* deviceName;
314     char* keycode;
315     uint32_t devicenamelength;
316     uint32_t keycodelength;
317     if (datalen == 0)
318     {
319       CLog::Log(LOGERROR, "IRServerSuite: no data in remote message.");
320       return false;
321     }
322     if (datalen <= 8)
323     {
324       //seems to be version 1.0.4.1, only keycode is sent, use Microsoft MCE mapping??
325       devicenamelength = 13;
326       deviceName = new char[devicenamelength + 1];
327       sprintf(deviceName, "Microsoft MCE");
328       keycodelength = datalen;
329       keycode = new char[keycodelength + 1];
330       memcpy(keycode, data, keycodelength);
331     }
332     else
333     {
334       //first 4 bytes is devicename length
335       memcpy(&devicenamelength, data, 4);
336       //devicename itself
337       if (datalen < 4 + devicenamelength)
338       {
339         CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen);
340         return false;
341       }
342       deviceName = new char[devicenamelength + 1];
343       memcpy(deviceName, data + 4, devicenamelength);
344       if (datalen < 8 + devicenamelength)
345       {
346         CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen);
347         delete[] deviceName;
348         return false;
349       }
350       //next 4 bytes is keycode length
351       memcpy(&keycodelength, data + 4 + devicenamelength, 4);
352       //keycode itself
353       if (datalen < 8 + devicenamelength + keycodelength)
354       {
355         CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen);
356         delete[] deviceName;
357         return false;
358       }
359       keycode = new char[keycodelength + 1];
360       memcpy(keycode, data + 8 + devicenamelength, keycodelength);
361     }
362     deviceName[devicenamelength] = '\0';
363     keycode[keycodelength] = '\0';
364     //translate to a buttoncode xbmc understands
365     m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, keycode);
366     if (g_advancedSettings.m_logLevel == LOG_LEVEL_DEBUG_FREEMEM)
367     {
368       CLog::Log(LOGINFO, "IRServerSuite, RemoteEvent: %s %s", deviceName, keycode);
369     }
370     delete[] deviceName;
371     delete[] keycode;
372     return true;
373   }
374   catch(...)
375   {
376     CLog::Log(LOGERROR, "IRServerSuite: exception while processing RemoteEvent.");
377     return false;
378   }
379 }
380
381 int CRemoteControl::ReadN(char *buffer, int n)
382 {
383   int nOriginalSize = n;
384   memset(buffer, 0, n);
385   char *ptr = buffer;
386   while (n > 0)
387   {
388     int nBytes = 0;
389     nBytes = recv(m_socket, ptr, n, 0);
390
391     if (WSAGetLastError() == WSAEWOULDBLOCK)
392     {
393       return nOriginalSize - n;
394     }
395     if (nBytes < 0)
396     {
397       if (!m_isConnecting)
398       {
399         CLog::Log(LOGERROR, "%s, IRServerSuite recv error %d", __FUNCTION__, GetLastError());
400       }
401       Close();
402       return -1;
403     }
404
405     if (nBytes == 0)
406     {
407       CLog::Log(LOGDEBUG,"%s, IRServerSuite socket closed by server", __FUNCTION__);
408       Close();
409       break;
410     }
411
412     n -= nBytes;
413     ptr += nBytes;
414   }
415
416   return nOriginalSize - n;
417 }
418
419 bool CRemoteControl::WriteN(const char *buffer, int n)
420 {
421   const char *ptr = buffer;
422   while (n > 0)
423   {
424     int nBytes = send(m_socket, ptr, n, 0);
425     if (nBytes < 0)
426     {
427       CLog::Log(LOGERROR, "%s, IRServerSuite send error %d (%d bytes)", __FUNCTION__, GetLastError(), n);
428       Close();
429       return false;
430     }
431
432     if (nBytes == 0)
433       break;
434
435     n -= nBytes;
436     ptr += nBytes;
437   }
438
439   return n == 0;
440 }
441
442 bool CRemoteControl::ReadPacket(CIrssMessage &message)
443 {
444   try
445   {
446     char sizebuf[4];
447     int iRead = ReadN(&sizebuf[0], 4);
448     if (iRead <= 0) return false; //nothing to read
449     if (iRead != 4)
450     {
451       CLog::Log(LOGERROR, "IRServerSuite: failed to read packetsize.");
452       return false;
453     }
454     uint32_t size = 0;
455     memcpy(&size, &sizebuf[0], 4);
456     size = ntohl(size);
457     char* messagebytes = new char[size];
458     if (ReadN(messagebytes, size) != size)
459     {
460       CLog::Log(LOGERROR, "IRServerSuite: failed to read packet.");
461       return false;
462     }
463     if (!CIrssMessage::FromBytes(messagebytes, size, message))
464     {
465       CLog::Log(LOGERROR, "IRServerSuite: invalid packet received (size: %u).", size);
466       return false;
467     }
468     delete[] messagebytes;
469     return true;
470   }
471   catch(...)
472   {
473     CLog::Log(LOGERROR, "IRServerSuite: exception while processing packet.");
474     return false;
475   }
476 }
477
478 WORD CRemoteControl::GetButton()
479 {
480   return m_button;
481 }
482
483 unsigned int CRemoteControl::GetHoldTime() const
484 {
485   return 0;
486 }
487
488 void CRemoteControl::setUsed(bool value)
489 {
490 }