Merge pull request #4857 from t-nelson/Gotham_13.2_backports
[vuplus_xbmc] / xbmc / filesystem / HTSPSession.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 "HTSPSession.h"
22 #include "URL.h"
23 #include "video/VideoInfoTag.h"
24 #include "FileItem.h"
25 #include "utils/log.h"
26 #include "utils/StringUtils.h"
27 #include "settings/AdvancedSettings.h"
28
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31 #include <sys/socket.h>
32
33 extern "C" {
34 #include "libhts/net.h"
35 #include "libhts/htsmsg.h"
36 #include "libhts/htsmsg_binary.h"
37 #include "libhts/sha1.h"
38 }
39
40
41 struct SContentType
42 {
43   unsigned    id;
44   const char* genre; 
45 };
46
47 static const SContentType g_dvb_content_group[] =
48 { { 0x1, "Movie/Drama" }
49 , { 0x2, "News/Current Affairs" }
50 , { 0x3, "Show/Game show" }
51 , { 0x4, "Sports" }
52 , { 0x5, "Children's/Youth" }
53 , { 0x6, "Music/Ballet/Dance" }
54 , { 0x7, "Arts/Culture (without music)" }
55 , { 0x8, "Social/Political issues/Economics" }
56 , { 0x9, "Childrens/Youth Education/Science/Factual" }
57 , { 0xa, "Leisure hobbies" }
58 , { 0xb, "Misc" }
59 , { 0xf, "Unknown" }
60 };
61
62 static const SContentType g_dvb_content_type[] =
63 {
64 // movie/drama
65   { 0x11, "Detective/Thriller" }
66 , { 0x12, "Adventure/Western/War" }
67 , { 0x13, "Science Fiction/Fantasy/Horror" }
68 , { 0x14, "Comedy" }
69 , { 0x15, "Soap/Melodrama/Folkloric" }
70 , { 0x16, "Romance" }
71 , { 0x17, "Serious/ClassicalReligion/Historical" }
72 , { 0x18, "Adult Movie/Drama" }
73
74 // news/current affairs
75 , { 0x21, "News/Weather Report" }
76 , { 0x22, "Magazine" }
77 , { 0x23, "Documentary" }
78 , { 0x24, "Discussion/Interview/Debate" }
79
80 // show/game show
81 , { 0x31, "Game show/Quiz/Contest" }
82 , { 0x32, "Variety" }
83 , { 0x33, "Talk" }
84
85 // sports
86 , { 0x41, "Special Event (Olympics/World cup/...)" }
87 , { 0x42, "Magazine" }
88 , { 0x43, "Football/Soccer" }
89 , { 0x44, "Tennis/Squash" }
90 , { 0x45, "Team sports (excluding football)" }
91 , { 0x46, "Athletics" }
92 , { 0x47, "Motor Sport" }
93 , { 0x48, "Water Sport" }
94 , { 0x49, "Winter Sports" }
95 , { 0x4a, "Equestrian" }
96 , { 0x4b, "Martial sports" }
97
98 // childrens/youth
99 , { 0x51, "Pre-school" }
100 , { 0x52, "Entertainment (6 to 14 year-olds)" }
101 , { 0x53, "Entertainment (10 to 16 year-olds)" }
102 , { 0x54, "Informational/Educational/Schools" }
103 , { 0x55, "Cartoons/Puppets" }
104
105 // music/ballet/dance
106 , { 0x61, "Rock/Pop" }
107 , { 0x62, "Serious music/Classical Music" }
108 , { 0x63, "Folk/Traditional music" }
109 , { 0x64, "Jazz" }
110 , { 0x65, "Musical/Opera" }
111 , { 0x66, "Ballet" }
112
113 // arts/culture
114 , { 0x71, "Performing Arts" }
115 , { 0x72, "Fine Arts" }
116 , { 0x73, "Religion" }
117 , { 0x74, "Popular Culture/Tradital Arts" }
118 , { 0x75, "Literature" }
119 , { 0x76, "Film/Cinema" }
120 , { 0x77, "Experimental Film/Video" }
121 , { 0x78, "Broadcasting/Press" }
122 , { 0x79, "New Media" }
123 , { 0x7a, "Magazine" }
124 , { 0x7b, "Fashion" }
125
126 // social/political/economic
127 , { 0x81, "Magazine/Report/Domentary" }
128 , { 0x82, "Economics/Social Advisory" }
129 , { 0x83, "Remarkable People" }
130
131 // children's youth: educational/science/factual
132 , { 0x91, "Nature/Animals/Environment" }
133 , { 0x92, "Technology/Natural sciences" }
134 , { 0x93, "Medicine/Physiology/Psychology" }
135 , { 0x94, "Foreign Countries/Expeditions" }
136 , { 0x95, "Social/Spiritual Sciences" }
137 , { 0x96, "Further Education" }
138 , { 0x97, "Languages" }
139
140 // leisure hobbies
141 , { 0xa1, "Tourism/Travel" }
142 , { 0xa2, "Handicraft" }
143 , { 0xa3, "Motoring" }
144 , { 0xa4, "Fitness & Health" }
145 , { 0xa5, "Cooking" }
146 , { 0xa6, "Advertisement/Shopping" }
147 , { 0xa7, "Gardening" }
148
149 // misc
150 , { 0xb0, "Original Language" }
151 , { 0xb1, "Black and White" }
152 , { 0xb2, "Unpublished" }
153 , { 0xb3, "Live Broadcast" }
154 };
155
156 using namespace std;
157 using namespace HTSP;
158
159 string CHTSPSession::GetGenre(unsigned type)
160 {
161   // look for full content
162   for(unsigned int i = 0; i < sizeof(g_dvb_content_type) / sizeof(g_dvb_content_type[0]); i++)
163   {
164     if(g_dvb_content_type[i].id == type)
165       return g_dvb_content_type[i].genre;
166   }
167
168   // look for group
169   type = (type >> 4) & 0xf;
170   for(unsigned int i = 0; i < sizeof(g_dvb_content_group) / sizeof(g_dvb_content_group[0]); i++)
171   {
172     if(g_dvb_content_group[i].id == type)
173       return g_dvb_content_group[i].genre;
174   }
175
176   return "";
177 }
178
179 CHTSPSession::CHTSPSession()
180   : m_fd(INVALID_SOCKET)
181   , m_seq(0)
182   , m_challenge(NULL)
183   , m_challenge_len(0)
184   , m_protocol(0)
185   , m_queue_size(1000)
186 {
187 }
188
189 CHTSPSession::~CHTSPSession()
190 {
191   Close();
192 }
193
194 void CHTSPSession::Abort()
195 {
196   shutdown(m_fd, SHUT_RDWR);
197 }
198
199 void CHTSPSession::Close()
200 {
201   if(m_fd != INVALID_SOCKET)
202   {
203     closesocket(m_fd);
204     m_fd = INVALID_SOCKET;
205   }
206
207   if(m_challenge)
208   {
209     free(m_challenge);
210     m_challenge     = NULL;
211     m_challenge_len = 0;
212   }
213 }
214
215 bool CHTSPSession::Connect(const std::string& hostname, int port)
216 {
217   char errbuf[1024];
218   int  errlen = sizeof(errbuf);
219   htsmsg_t *m;
220 //  const char *method;
221   const char *server, *version;
222   const void * chall = NULL;
223   size_t chall_len = 0;
224   int32_t proto = 0;
225
226   if(port == 0)
227     port = 9982;
228
229   m_fd = htsp_tcp_connect(hostname.c_str()
230                         , port
231                         , errbuf, errlen, 3000);
232   if(m_fd == INVALID_SOCKET)
233   {
234     CLog::Log(LOGERROR, "CHTSPSession::Open - failed to connect to server (%s)\n", errbuf);
235     return false;
236   }
237
238   // send hello
239   m = htsmsg_create_map();
240   htsmsg_add_str(m, "method", "hello");
241   htsmsg_add_str(m, "clientname", "XBMC Media Center");
242   htsmsg_add_u32(m, "htspversion", 1);
243
244   // read welcome
245   if((m = ReadResult(m)) == NULL)
246   {
247     CLog::Log(LOGERROR, "CHTSPSession::Open - failed to read greeting from server");
248     return false;
249   }
250 //  method  = htsmsg_get_str(m, "method");
251             htsmsg_get_s32(m, "htspversion", &proto);
252   server  = htsmsg_get_str(m, "servername");
253   version = htsmsg_get_str(m, "serverversion");
254             htsmsg_get_bin(m, "challenge", &chall, &chall_len);
255
256   CLog::Log(LOGDEBUG, "CHTSPSession::Open - connected to server: [%s], version: [%s], proto: %d"
257                     , server ? server : "", version ? version : "", proto);
258
259   m_protocol = proto;
260
261   if(chall && chall_len)
262   {
263     m_challenge     = malloc(chall_len);
264     m_challenge_len = chall_len;
265     memcpy(m_challenge, chall, chall_len);
266   }
267
268   htsmsg_destroy(m);
269   return true;
270 }
271
272 bool CHTSPSession::Auth(const std::string& username, const std::string& password)
273 {
274   htsmsg_t *m = htsmsg_create_map();
275   htsmsg_add_str(m, "method"  , "authenticate");
276   htsmsg_add_str(m, "username", username.c_str());
277
278   if(password != "" && m_challenge)
279   {
280     struct HTSSHA1* shactx = (struct HTSSHA1*) malloc(hts_sha1_size);
281     uint8_t d[20];
282     hts_sha1_init(shactx);
283     hts_sha1_update(shactx
284                  , (const uint8_t *)password.c_str()
285                  , password.length());
286     hts_sha1_update(shactx
287                  , (const uint8_t *)m_challenge
288                  , m_challenge_len);
289     hts_sha1_final(shactx, d);
290     htsmsg_add_bin(m, "digest", d, 20);
291     free(shactx);
292   }
293
294   return ReadSuccess(m, false, "get reply from authentication with server");
295 }
296
297 htsmsg_t* CHTSPSession::ReadMessage(int timeout)
298 {
299   void*    buf;
300   uint32_t l;
301   int      x;
302
303   if(m_queue.size())
304   {
305     htsmsg_t* m = m_queue.front();
306     m_queue.pop_front();
307     return m;
308   }
309
310   x = htsp_tcp_read_timeout(m_fd, &l, 4, timeout);
311   if(x == ETIMEDOUT)
312     return htsmsg_create_map();
313
314   if(x)
315   {
316     CLog::Log(LOGERROR, "CHTSPSession::ReadMessage - Failed to read packet size (%d)\n", x);
317     return NULL;
318   }
319
320   l   = ntohl(l);
321   if(l == 0)
322     return htsmsg_create_map();
323
324   buf = malloc(l);
325
326   x = htsp_tcp_read(m_fd, buf, l);
327   if(x)
328   {
329     CLog::Log(LOGERROR, "CHTSPSession::ReadMessage - Failed to read packet (%d)\n", x);
330     free(buf);
331     return NULL;
332   }
333
334   return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */
335 }
336
337 bool CHTSPSession::SendMessage(htsmsg_t* m)
338 {
339   void*  buf;
340   size_t len;
341
342   if(htsmsg_binary_serialize(m, &buf, &len, -1) < 0)
343   {
344     htsmsg_destroy(m);
345     return false;
346   }
347   htsmsg_destroy(m);
348
349   if(send(m_fd, (char*)buf, len, 0) < 0)
350   {
351     free(buf);
352     return false;
353   }
354   free(buf);
355   return true;
356 }
357
358 htsmsg_t* CHTSPSession::ReadResult(htsmsg_t* m, bool sequence)
359 {
360   if(sequence)
361     htsmsg_add_u32(m, "seq", ++m_seq);
362
363   if(!SendMessage(m))
364     return NULL;
365
366   std::deque<htsmsg_t*> queue;
367   m_queue.swap(queue);
368
369   while((m = ReadMessage()))
370   {
371     uint32_t seq;
372     if(!sequence)
373       break;
374     if(!htsmsg_get_u32(m, "seq", &seq) && seq == m_seq)
375       break;
376
377     queue.push_back(m);
378     if(queue.size() >= m_queue_size)
379     {
380       CLog::Log(LOGERROR, "CDVDInputStreamHTSP::ReadResult - maximum queue size (%u) reached", m_queue_size);
381       m_queue.swap(queue);
382       return NULL;
383     }
384   }
385
386   m_queue.swap(queue);
387
388   const char* error;
389   if(m && (error = htsmsg_get_str(m, "error")))
390   {
391     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::ReadResult - error (%s)", error);
392     htsmsg_destroy(m);
393     return NULL;
394   }
395   uint32_t noaccess;
396   if(m && !htsmsg_get_u32(m, "noaccess", &noaccess) && noaccess)
397   {
398     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::ReadResult - access denied (%d)", noaccess);
399     htsmsg_destroy(m);
400     return NULL;
401   }
402
403   return m;
404 }
405
406 bool CHTSPSession::ReadSuccess(htsmsg_t* m, bool sequence, std::string action)
407 {
408   if((m = ReadResult(m, sequence)) == NULL)
409   {
410     CLog::Log(LOGDEBUG, "CDVDInputStreamHTSP::ReadSuccess - failed to %s", action.c_str());
411     return false;
412   }
413   htsmsg_destroy(m);
414   return true;
415 }
416
417 bool CHTSPSession::SendSubscribe(int subscription, int channel)
418 {
419   htsmsg_t *m = htsmsg_create_map();
420   htsmsg_add_str(m, "method"        , "subscribe");
421   htsmsg_add_s32(m, "channelId"     , channel);
422   htsmsg_add_s32(m, "subscriptionId", subscription);
423   return ReadSuccess(m, true, "subscribe to channel");
424 }
425
426 bool CHTSPSession::SendUnsubscribe(int subscription)
427 {
428   htsmsg_t *m = htsmsg_create_map();
429   htsmsg_add_str(m, "method"        , "unsubscribe");
430   htsmsg_add_s32(m, "subscriptionId", subscription);
431   return ReadSuccess(m, true, "unsubscribe from channel");
432 }
433
434 bool CHTSPSession::SendEnableAsync()
435 {
436   htsmsg_t *m = htsmsg_create_map();
437   htsmsg_add_str(m, "method", "enableAsyncMetadata");
438   return ReadSuccess(m, true, "enableAsyncMetadata failed");
439 }
440
441 bool CHTSPSession::GetEvent(SEvent& event, uint32_t id)
442 {
443   if(id == 0)
444   {
445     event.Clear();
446     return false;
447   }
448
449   htsmsg_t *msg = htsmsg_create_map();
450   htsmsg_add_str(msg, "method", "getEvent");
451   htsmsg_add_u32(msg, "eventId", id);
452   if((msg = ReadResult(msg, true)) == NULL)
453   {
454     CLog::Log(LOGDEBUG, "CHTSPSession::GetEvent - failed to get event %d", id);
455     return false;
456   }
457   return ParseEvent(msg, id, event);
458 }
459
460 bool CHTSPSession::ParseEvent(htsmsg_t* msg, uint32_t id, SEvent &event)
461 {
462   uint32_t start, stop, next, content;
463   const char *title, *desc;
464   if(         htsmsg_get_u32(msg, "start", &start)
465   ||          htsmsg_get_u32(msg, "stop" , &stop)
466   || (title = htsmsg_get_str(msg, "title")) == NULL)
467   {
468     CLog::Log(LOGDEBUG, "CHTSPSession::ParseEvent - malformed event");
469     htsmsg_print(msg);
470     htsmsg_destroy(msg);
471     return false;
472   }
473   event.Clear();
474   event.id    = id;
475   event.start = start;
476   event.stop  = stop;
477   event.title = title;
478   if((desc = htsmsg_get_str(msg, "description")))
479     event.descs = desc;
480   if(htsmsg_get_u32(msg, "nextEventId", &next))
481     event.next = 0;
482   else
483     event.next = next;
484
485   if(htsmsg_get_u32(msg, "contentType", &content) == 0)
486     event.content = content;
487
488   CLog::Log(LOGDEBUG, "CHTSPSession::ParseEvent - id:%u, title:'%s', desc:'%s', start:%u, stop:%u, next:%u, content:%u"
489                     , event.id
490                     , event.title.c_str()
491                     , event.descs.c_str()
492                     , event.start
493                     , event.stop
494                     , event.next
495                     , event.content);
496
497   return true;
498 }
499
500 void CHTSPSession::ParseChannelUpdate(htsmsg_t* msg, SChannels &channels)
501 {
502   uint32_t id, event = 0, num = 0;
503   const char *name, *icon;
504   if(htsmsg_get_u32(msg, "channelId", &id))
505   {
506     CLog::Log(LOGERROR, "CHTSPSession::ParseChannelUpdate - malformed message received");
507     htsmsg_print(msg);
508     return;
509   }
510
511   SChannel &channel = channels[id];
512   channel.id = id;
513
514   if(htsmsg_get_u32(msg, "eventId", &event) == 0)
515     channel.event = event;
516
517   if((name = htsmsg_get_str(msg, "channelName")))
518     channel.name = name;
519
520   if((icon = htsmsg_get_str(msg, "channelIcon")))
521     channel.icon = icon;
522
523   if(htsmsg_get_u32(msg, "channelNumber", &num) == 0)
524   {
525     if(num == 0)
526       channel.num = id + 1000;
527     else
528       channel.num = num;
529   }
530   else
531     channel.num = id; // fallback older servers
532
533   htsmsg_t *tags;
534
535   if((tags = htsmsg_get_list(msg, "tags")))
536   {
537     channel.tags.clear();
538
539     htsmsg_field_t *f;
540     HTSMSG_FOREACH(f, tags)
541     {
542       if(f->hmf_type != HMF_S64)
543         continue;
544       channel.tags.push_back((int)f->hmf_s64);
545     }
546   }
547
548
549   CLog::Log(LOGDEBUG, "CHTSPSession::ParseChannelUpdate - id:%u, name:'%s', icon:'%s', event:%u"
550                     , id, name ? name : "(null)", icon ? icon : "(null)", event);
551 }
552
553 void CHTSPSession::ParseChannelRemove(htsmsg_t* msg, SChannels &channels)
554 {
555   uint32_t id;
556   if(htsmsg_get_u32(msg, "channelId", &id))
557   {
558     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::ParseChannelRemove - malformed message received");
559     htsmsg_print(msg);
560     return;
561   }
562   CLog::Log(LOGDEBUG, "CHTSPSession::ParseChannelRemove - id:%u", id);
563
564   channels.erase(id);
565 }
566
567 void CHTSPSession::ParseTagUpdate(htsmsg_t* msg, STags &tags)
568 {
569   uint32_t id;
570   const char *name, *icon;
571   if(htsmsg_get_u32(msg, "tagId", &id))
572   {
573     CLog::Log(LOGERROR, "CHTSPSession::ParseTagUpdate - malformed message received");
574     htsmsg_print(msg);
575     return;
576   }
577   STag &tag = tags[id];
578   tag.id = id;
579
580   if((icon = htsmsg_get_str(msg, "tagIcon")))
581     tag.icon  = icon;
582
583   if((name = htsmsg_get_str(msg, "tagName")))
584     tag.name  = name;
585
586   htsmsg_t *channels;
587
588   if((channels = htsmsg_get_list(msg, "members")))
589   {
590     tag.channels.clear();
591
592     htsmsg_field_t *f;
593     HTSMSG_FOREACH(f, channels)
594     {
595       if(f->hmf_type != HMF_S64)
596         continue;
597       tag.channels.push_back((int)f->hmf_s64);
598     }
599   }
600
601   CLog::Log(LOGDEBUG, "CHTSPSession::ParseTagUpdate - id:%u, name:'%s', icon:'%s'"
602                     , id, name ? name : "(null)", icon ? icon : "(null)");
603
604 }
605
606 void CHTSPSession::ParseTagRemove(htsmsg_t* msg, STags &tags)
607 {
608   uint32_t id;
609   if(htsmsg_get_u32(msg, "tagId", &id))
610   {
611     CLog::Log(LOGERROR, "CHTSPSession::ParseTagRemove - malformed message received");
612     htsmsg_print(msg);
613     return;
614   }
615   CLog::Log(LOGDEBUG, "CHTSPSession::ParseTagRemove - id:%u", id);
616
617   tags.erase(id);
618 }
619
620 bool CHTSPSession::ParseItem(const SChannel& channel, int tagid, const SEvent& event, CFileItem& item)
621 {
622   CVideoInfoTag* tag = item.GetVideoInfoTag();
623
624   CURL url(item.GetPath());
625   CStdString temp = StringUtils::Format("tags/%d/%d.ts", tagid, channel.id);
626   url.SetFileName(temp);
627
628   tag->m_iSeason  = 0;
629   tag->m_iEpisode = 0;
630   tag->m_iTrack       = channel.num;
631   tag->m_strAlbum     = channel.name;
632   tag->m_strShowTitle = event.title;
633   tag->m_strPlot      = event.descs;
634   tag->m_strStatus    = "livetv";
635   tag->m_genre        = StringUtils::Split(GetGenre(event.content), g_advancedSettings.m_videoItemSeparator);
636
637   tag->m_strTitle = tag->m_strAlbum;
638   if(tag->m_strShowTitle.length() > 0)
639     tag->m_strTitle += " : " + tag->m_strShowTitle;
640
641   item.SetPath(url.Get());
642   item.m_strTitle = tag->m_strTitle;
643   item.SetArt("thumb", channel.icon);
644   item.SetMimeType("video/X-htsp");
645   return true;
646 }
647
648 bool CHTSPSession::ParseQueueStatus (htsmsg_t* msg, SQueueStatus &queue)
649 {
650   if(htsmsg_get_u32(msg, "packets", &queue.packets)
651   || htsmsg_get_u32(msg, "bytes",   &queue.bytes)
652   || htsmsg_get_u32(msg, "Bdrops",  &queue.bdrops)
653   || htsmsg_get_u32(msg, "Pdrops",  &queue.pdrops)
654   || htsmsg_get_u32(msg, "Idrops",  &queue.idrops))
655   {
656     CLog::Log(LOGERROR, "CHTSPSession::ParseQueueStatus - malformed message received");
657     htsmsg_print(msg);
658     return false;
659   }
660
661   /* delay isn't always transmitted */
662   if(htsmsg_get_u32(msg, "delay", &queue.delay))
663     queue.delay = 0;
664
665   return true;
666 }