Project

General

Profile

Feature #2076 » biss2.diff.txt

Petar Ivanov, 2014-04-23 03:45

 
1
diff --git a/src/ccw.c b/src/ccw.c
2
new file mode 100644
3
index 0000000..6316772
4
--- /dev/null
5
+++ b/src/ccw.c
6
@@ -0,0 +1,544 @@
7
+/*
8
+ *  tvheadend, CCW
9
+ *  Copyright (C) 2012
10
+ *
11
+ *  This program is free software: you can redistribute it and/or modify
12
+ *  it under the terms of the GNU General Public License as published by
13
+ *  the Free Software Foundation, either version 3 of the License, or
14
+ *  (at your option) any later version.
15
+ *
16
+ *  This program is distributed in the hope that it will be useful,
17
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
+ *  GNU General Public License for more details.
20
+ *
21
+ *  You should have received a copy of the GNU General Public License
22
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
+ */
24
+
25
+#include <pthread.h>
26
+#include <assert.h>
27
+#include <string.h>
28
+#include <stdio.h>
29
+#include <poll.h>
30
+#include <unistd.h>
31
+#include <stdlib.h>
32
+#include <stdarg.h>
33
+#include <errno.h>
34
+#include <ctype.h>
35
+#include <sys/types.h>
36
+#include <sys/un.h>
37
+
38
+#include "tvheadend.h"
39
+#include "dvb/dvb.h"
40
+#include "tcp.h"
41
+#include "psi.h"
42
+#include "tsdemux.h"
43
+#include "ffdecsa/FFdecsa.h"
44
+#include "ccw.h"
45
+#include "notify.h"
46
+#include "subscriptions.h"
47
+#include "dtable.h"
48
+
49
+
50
+/**
51
+ *
52
+ */
53
+TAILQ_HEAD(ccw_queue, ccw);
54
+LIST_HEAD(ccw_service_list, ccw_service);
55
+static struct ccw_queue ccws;
56
+static pthread_cond_t ccw_config_changed;
57
+
58
+
59
+
60
+/**
61
+ *
62
+ */
63
+typedef struct ccw_service {
64
+  th_descrambler_t ct_head;
65
+
66
+  service_t *ct_service;
67
+
68
+  struct ccw *ct_ccw;
69
+
70
+  LIST_ENTRY(ccw_service) ct_link;
71
+
72
+  /**
73
+   * Status of the key(s) in ct_keys
74
+   */
75
+  enum {
76
+    CT_UNKNOWN,
77
+    CT_RESOLVED,
78
+    CT_FORBIDDEN
79
+  } ct_keystate;
80
+
81
+  /* buffer for keystruct */
82
+  void    *ct_keys;
83
+
84
+  /* CSA */
85
+  int      ct_cluster_size;
86
+  uint8_t *ct_tsbcluster;
87
+  int      ct_fill;
88
+
89
+  uint8_t ccw_evenkey[8];
90
+  uint8_t ccw_oddkey[8];
91
+
92
+} ccw_service_t;
93
+
94
+
95
+/**
96
+ *
97
+ */
98
+typedef struct ccw {
99
+  pthread_cond_t ccw_cond;
100
+
101
+  TAILQ_ENTRY(ccw) ccw_link; /* Linkage protected via global_lock */
102
+
103
+  struct ccw_service_list ccw_services;
104
+
105
+  uint16_t ccw_caid;  // CAID
106
+  uint16_t ccw_tid;   // Transponder ID
107
+  uint16_t ccw_sid;   // Channel ID
108
+  uint8_t ccw_confedkey[8];  // Key
109
+  char *ccw_comment;
110
+  char *ccw_id;
111
+
112
+  int   ccw_enabled;
113
+  int   ccw_running;
114
+  int   ccw_reconfigure;
115
+
116
+} ccw_t;
117
+
118
+
119
+/**
120
+ * global_lock is held
121
+ * s_stream_mutex is held
122
+ */
123
+static void 
124
+ccw_service_destroy(th_descrambler_t *td)
125
+{
126
+  tvhlog(LOG_INFO, "ccw", "Removing CCW key from service");
127
+
128
+  ccw_service_t *ct = (ccw_service_t *)td;
129
+
130
+  LIST_REMOVE(td, td_service_link);
131
+  LIST_REMOVE(ct, ct_link);
132
+
133
+  free_key_struct(ct->ct_keys);
134
+  free(ct->ct_tsbcluster);
135
+  free(ct);
136
+}
137
+
138
+
139
+/**
140
+ *
141
+ */
142
+static void
143
+ccw_table_input(struct th_descrambler *td, struct service *t,
144
+    struct elementary_stream *st, const uint8_t *data, int len)
145
+{
146
+
147
+// CCW работает без ECM сюда мы попасть не должны никогда
148
+
149
+  tvhlog(LOG_INFO, "ccw",
150
+            "ECM incoming for service \"%s\"",t->s_svcname);
151
+}
152
+
153
+
154
+/**
155
+ *
156
+ */
157
+static int
158
+ccw_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st,
159
+     const uint8_t *tsb)
160
+{
161
+  ccw_service_t *ct = (ccw_service_t *)td;
162
+  int r, i;
163
+  unsigned char *vec[3];
164
+  uint8_t *t0;
165
+
166
+  if(ct->ct_keystate == CT_FORBIDDEN)
167
+    return 1;
168
+
169
+  if(ct->ct_keystate != CT_RESOLVED)
170
+    return -1;
171
+
172
+  memcpy(ct->ct_tsbcluster + ct->ct_fill * 188, tsb, 188);
173
+  ct->ct_fill++;
174
+
175
+  if(ct->ct_fill != ct->ct_cluster_size)
176
+    return 0;
177
+
178
+  ct->ct_fill = 0;
179
+
180
+  vec[0] = ct->ct_tsbcluster;
181
+  vec[1] = ct->ct_tsbcluster + ct->ct_cluster_size * 188;
182
+  vec[2] = NULL;
183
+
184
+  while(1) {
185
+    t0 = vec[0];
186
+    r = decrypt_packets(ct->ct_keys, vec);
187
+    if(r == 0)
188
+      break;
189
+    for(i = 0; i < r; i++) {
190
+      ts_recv_packet2(t, t0);
191
+      t0 += 188;
192
+    }
193
+  }
194
+  return 0;
195
+}
196
+
197
+
198
+/**
199
+ *
200
+ */
201
+static inline elementary_stream_t *
202
+ccw_find_stream_by_caid(service_t *t, int caid)
203
+{
204
+  elementary_stream_t *st;
205
+  caid_t *c;
206
+
207
+  TAILQ_FOREACH(st, &t->s_components, es_link) {
208
+    LIST_FOREACH(c, &st->es_caids, link) {
209
+      if(c->caid == caid)
210
+	return st;
211
+    }
212
+  }
213
+  return NULL;
214
+}
215
+
216
+
217
+
218
+/**
219
+ * Check if our CAID's matches, and if so, link
220
+ *
221
+ * global_lock is held
222
+ */
223
+void
224
+ccw_service_start(service_t *t)
225
+{
226
+  ccw_t *ccw;
227
+  ccw_service_t *ct;
228
+  th_descrambler_t *td;
229
+
230
+  lock_assert(&global_lock);
231
+
232
+  TAILQ_FOREACH(ccw, &ccws, ccw_link) {
233
+    if(ccw->ccw_caid == 0)
234
+      continue;
235
+
236
+    if(ccw_find_stream_by_caid(t, ccw->ccw_caid) == NULL)
237
+      continue;
238
+
239
+    if(t->s_dvb_service_id != ccw->ccw_sid)
240
+      continue;
241
+
242
+    if(t->s_dvb_mux_instance->tdmi_transport_stream_id != ccw->ccw_tid)
243
+      continue;
244
+
245
+    tvhlog(LOG_INFO, "ccw",
246
+      "Starting ccw key for service \"%s\" on tuner %d", 
247
+      t->s_svcname,
248
+      t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num);
249
+
250
+
251
+    /* create new ccw service */
252
+    ct                  = calloc(1, sizeof(ccw_service_t));
253
+    ct->ct_cluster_size = get_suggested_cluster_size();
254
+    ct->ct_tsbcluster   = malloc(ct->ct_cluster_size * 188);
255
+
256
+    ct->ct_keys       = get_key_struct();
257
+    ct->ct_ccw      = ccw;
258
+    ct->ct_service  = t;
259
+
260
+    memcpy(ct->ccw_evenkey,ccw->ccw_confedkey,8);
261
+    memcpy(ct->ccw_oddkey,ccw->ccw_confedkey,8);
262
+
263
+    set_even_control_word(ct->ct_keys, ct->ccw_evenkey);
264
+    set_odd_control_word(ct->ct_keys, ct->ccw_oddkey);
265
+    if(ct->ct_keystate != CT_RESOLVED)
266
+        tvhlog(LOG_INFO, "ccw", "Obtained key for service \"%s\"",t->s_svcname);
267
+
268
+    tvhlog(LOG_DEBUG, "ccw",
269
+	   "Key for service \"%s\" "
270
+	   "even: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x"
271
+	   " odd: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
272
+	   t->s_svcname,
273
+	   ct->ccw_evenkey[0], ct->ccw_evenkey[1], ct->ccw_evenkey[2], ct->ccw_evenkey[3],
274
+           ct->ccw_evenkey[4], ct->ccw_evenkey[5], ct->ccw_evenkey[6], ct->ccw_evenkey[7],
275
+           ct->ccw_oddkey[0], ct->ccw_oddkey[1], ct->ccw_oddkey[2], ct->ccw_oddkey[3],
276
+           ct->ccw_oddkey[4], ct->ccw_oddkey[5], ct->ccw_oddkey[6],  ct->ccw_oddkey[7]);
277
+    ct->ct_keystate = CT_RESOLVED;
278
+
279
+    td = &ct->ct_head;
280
+    td->td_stop       = ccw_service_destroy;
281
+    td->td_table      = ccw_table_input;
282
+    td->td_descramble = ccw_descramble;
283
+    LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
284
+
285
+    LIST_INSERT_HEAD(&ccw->ccw_services, ct, ct_link);
286
+  }
287
+}
288
+
289
+
290
+/**
291
+ *
292
+ */
293
+static void
294
+ccw_destroy(ccw_t *ccw)
295
+{
296
+  lock_assert(&global_lock);
297
+  TAILQ_REMOVE(&ccws, ccw, ccw_link);
298
+  ccw->ccw_running = 0;
299
+  pthread_cond_signal(&ccw->ccw_cond);
300
+}
301
+
302
+/**
303
+ *
304
+ */
305
+static ccw_t *
306
+ccw_entry_find(const char *id, int create)
307
+{
308
+//  pthread_attr_t attr;
309
+//  pthread_t ptid;
310
+  char buf[20];
311
+  ccw_t *ccw;
312
+  static int tally;
313
+
314
+  if(id != NULL) {
315
+    TAILQ_FOREACH(ccw, &ccws, ccw_link)
316
+      if(!strcmp(ccw->ccw_id, id))
317
+  return ccw;
318
+  }
319
+  if(create == 0)
320
+    return NULL;
321
+
322
+  if(id == NULL) {
323
+    tally++;
324
+    snprintf(buf, sizeof(buf), "%d", tally);
325
+    id = buf;
326
+  } else {
327
+    tally = MAX(atoi(id), tally);
328
+  }
329
+
330
+  ccw = calloc(1, sizeof(ccw_t));
331
+  pthread_cond_init(&ccw->ccw_cond, NULL);
332
+  ccw->ccw_id      = strdup(id);
333
+  ccw->ccw_running = 1;
334
+
335
+  TAILQ_INSERT_TAIL(&ccws, ccw, ccw_link);
336
+
337
+//  pthread_attr_init(&attr);
338
+//  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
339
+//  pthread_create(&ptid, &attr, ccw_thread, ccw);
340
+//  pthread_attr_destroy(&attr);
341
+
342
+  return ccw;
343
+}
344
+
345
+
346
+/**
347
+ *
348
+ */
349
+static htsmsg_t *
350
+ccw_record_build(ccw_t *ccw)
351
+{
352
+  htsmsg_t *e = htsmsg_create_map();
353
+  char buf[100];
354
+
355
+  htsmsg_add_str(e, "id", ccw->ccw_id);
356
+  htsmsg_add_u32(e, "enabled",  !!ccw->ccw_enabled);
357
+
358
+  htsmsg_add_u32(e, "caid", ccw->ccw_caid);
359
+  htsmsg_add_u32(e, "tid", ccw->ccw_tid);
360
+  htsmsg_add_u32(e, "sid", ccw->ccw_sid);
361
+
362
+  snprintf(buf, sizeof(buf),
363
+	   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
364
+	   ccw->ccw_confedkey[0],
365
+	   ccw->ccw_confedkey[1],
366
+	   ccw->ccw_confedkey[2],
367
+	   ccw->ccw_confedkey[3],
368
+	   ccw->ccw_confedkey[4],
369
+	   ccw->ccw_confedkey[5],
370
+	   ccw->ccw_confedkey[6],
371
+	   ccw->ccw_confedkey[7]);
372
+
373
+  htsmsg_add_str(e, "key", buf);
374
+  htsmsg_add_str(e, "comment", ccw->ccw_comment ?: "");
375
+
376
+  return e;
377
+}
378
+
379
+
380
+/**
381
+ *
382
+ */
383
+static int
384
+nibble(char c)
385
+{
386
+  switch(c) {
387
+  case '0' ... '9':
388
+    return c - '0';
389
+  case 'a' ... 'f':
390
+    return c - 'a' + 10;
391
+  case 'A' ... 'F':
392
+    return c - 'A' + 10;
393
+  default:
394
+    return 0;
395
+  }
396
+}
397
+
398
+/**
399
+ *
400
+ */
401
+static htsmsg_t *
402
+ccw_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
403
+{
404
+  ccw_t *ccw;
405
+  const char *s;
406
+  uint32_t u32;
407
+  uint8_t key[8];
408
+  int u, l, i;
409
+
410
+  if((ccw = ccw_entry_find(id, maycreate)) == NULL)
411
+    return NULL;
412
+
413
+  lock_assert(&global_lock);
414
+
415
+  if(!htsmsg_get_u32(values, "caid", &u32))
416
+    ccw->ccw_caid = u32;
417
+  if(!htsmsg_get_u32(values, "tid", &u32))
418
+    ccw->ccw_tid = u32;
419
+  if(!htsmsg_get_u32(values, "sid", &u32))
420
+    ccw->ccw_sid = u32;
421
+
422
+  if((s = htsmsg_get_str(values, "key")) != NULL) {
423
+    for(i = 0; i < 8; i++) {
424
+      while(*s != 0 && !isxdigit(*s)) s++;
425
+      if(*s == 0)
426
+	break;
427
+      u = nibble(*s++);
428
+      while(*s != 0 && !isxdigit(*s)) s++;
429
+      if(*s == 0)
430
+	break;
431
+      l = nibble(*s++);
432
+      key[i] = (u << 4) | l;
433
+    }
434
+    memcpy(ccw->ccw_confedkey, key, 8);
435
+  }
436
+
437
+
438
+  if((s = htsmsg_get_str(values, "comment")) != NULL) {
439
+    free(ccw->ccw_comment);
440
+    ccw->ccw_comment = strdup(s);
441
+  }
442
+
443
+  if(!htsmsg_get_u32(values, "enabled", &u32)) 
444
+    ccw->ccw_enabled = u32;
445
+
446
+
447
+  ccw->ccw_reconfigure = 1;
448
+
449
+  pthread_cond_signal(&ccw->ccw_cond);
450
+
451
+  pthread_cond_broadcast(&ccw_config_changed);
452
+
453
+  return ccw_record_build(ccw);
454
+}
455
+
456
+
457
+
458
+
459
+/**
460
+ *
461
+ */
462
+static int
463
+ccw_entry_delete(void *opaque, const char *id)
464
+{
465
+  ccw_t *ccw;
466
+
467
+  pthread_cond_broadcast(&ccw_config_changed);
468
+
469
+  if((ccw = ccw_entry_find(id, 0)) == NULL)
470
+    return -1;
471
+  ccw_destroy(ccw);
472
+  return 0;
473
+}
474
+
475
+
476
+
477
+/**
478
+ *
479
+ */
480
+static htsmsg_t *
481
+ccw_entry_get_all(void *opaque)
482
+{
483
+  htsmsg_t *r = htsmsg_create_list();
484
+  ccw_t *ccw;
485
+
486
+  TAILQ_FOREACH(ccw, &ccws, ccw_link)
487
+    htsmsg_add_msg(r, NULL, ccw_record_build(ccw));
488
+
489
+  return r;
490
+}
491
+
492
+
493
+/**
494
+ *
495
+ */
496
+static htsmsg_t *
497
+ccw_entry_get(void *opaque, const char *id)
498
+{
499
+  ccw_t *ccw;
500
+
501
+
502
+  if((ccw = ccw_entry_find(id, 0)) == NULL)
503
+    return NULL;
504
+  return ccw_record_build(ccw);
505
+}
506
+
507
+
508
+/**
509
+ *
510
+ */
511
+static htsmsg_t *
512
+ccw_entry_create(void *opaque)
513
+{
514
+  pthread_cond_broadcast(&ccw_config_changed);
515
+  return ccw_record_build(ccw_entry_find(NULL, 1));
516
+}
517
+
518
+
519
+/**
520
+ *
521
+ */
522
+static const dtable_class_t ccw_dtc = {
523
+  .dtc_record_get     = ccw_entry_get,
524
+  .dtc_record_get_all = ccw_entry_get_all,
525
+  .dtc_record_create  = ccw_entry_create,
526
+  .dtc_record_update  = ccw_entry_update,
527
+  .dtc_record_delete  = ccw_entry_delete,
528
+  .dtc_read_access = ACCESS_ADMIN,
529
+  .dtc_write_access = ACCESS_ADMIN,
530
+  .dtc_mutex = &global_lock,
531
+};
532
+
533
+
534
+/**
535
+ *
536
+ */
537
+void
538
+ccw_init(void)
539
+{
540
+  dtable_t *dt;
541
+
542
+  TAILQ_INIT(&ccws);
543
+
544
+  pthread_cond_init(&ccw_config_changed, NULL);
545
+
546
+  dt = dtable_create(&ccw_dtc, "ccw", NULL);
547
+  dtable_load(dt);
548
+
549
+}
550
+
551
diff --git a/src/ccw.h b/src/ccw.h
552
new file mode 100644
553
index 0000000..ff263c7
554
--- /dev/null
555
+++ b/src/ccw.h
556
@@ -0,0 +1,26 @@
557
+/*
558
+ *  tvheadend, CCW
559
+ *  Copyright (C) 2012
560
+ *
561
+ *  This program is free software: you can redistribute it and/or modify
562
+ *  it under the terms of the GNU General Public License as published by
563
+ *  the Free Software Foundation, either version 3 of the License, or
564
+ *  (at your option) any later version.
565
+ *
566
+ *  This program is distributed in the hope that it will be useful,
567
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
568
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
569
+ *  GNU General Public License for more details.
570
+ *
571
+ *  You should have received a copy of the GNU General Public License
572
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
573
+ */
574
+
575
+#ifndef CCW_H_
576
+#define CCW_H_
577
+
578
+void ccw_init(void);
579
+
580
+void ccw_service_start(struct service *t);
581
+
582
+#endif /* CCW_H_ */
583
diff --git a/src/webui/static/app/ccweditor.js b/src/webui/static/app/ccweditor.js
584
new file mode 100644
585
index 0000000..5736289
586
--- /dev/null
587
+++ b/src/webui/static/app/ccweditor.js
588
@@ -0,0 +1,92 @@
589
+
590
+tvheadend.ccweditor = function() {
591
+    var fm = Ext.form;
592
+    
593
+    var enabledColumn = new Ext.grid.CheckColumn({
594
+       header: "Enabled",
595
+       dataIndex: 'enabled',
596
+       width: 60
597
+    });
598
+
599
+    function setMetaAttr(meta, record){
600
+        var enabled = record.get('enabled');
601
+        if(!enabled) return;
602
+
603
+        if(enabled == 1){
604
+            meta.attr = 'style="color:green;"';
605
+        } else {
606
+            meta.attr = 'style="color:red;"';
607
+        }
608
+    }
609
+
610
+    var cm = new Ext.grid.ColumnModel([
611
+	enabledColumn,
612
+	{
613
+	    header: "CAID",
614
+	    dataIndex: 'caid',
615
+            renderer: function(value, metadata, record, row, col, store) {
616
+		setMetaAttr(metadata, record);
617
+		return value;
618
+            },
619
+	    editor: new fm.TextField({allowBlank: false})
620
+	},{
621
+	    header: "Mux ID",
622
+	    dataIndex: 'tid',
623
+            renderer: function(value, metadata, record, row, col, store) {
624
+		setMetaAttr(metadata, record);
625
+		return value;
626
+            },
627
+	    editor: new fm.TextField({allowBlank: false})
628
+	},{
629
+	    header: "SID",
630
+	    dataIndex: 'sid',
631
+            renderer: function(value, metadata, record, row, col, store) {
632
+		setMetaAttr(metadata, record);
633
+		return value;
634
+            },
635
+	    editor: new fm.TextField({allowBlank: false})
636
+	},{
637
+	    header: "Key",
638
+	    dataIndex: 'key',
639
+	    width: 200,
640
+            renderer: function(value, metadata, record, row, col, store) {
641
+		setMetaAttr(metadata, record);
642
+		return value;
643
+            },
644
+	    editor: new fm.TextField({allowBlank: false})
645
+	},{
646
+	    header: "Comment",
647
+	    dataIndex: 'comment',
648
+	    width: 400,
649
+            renderer: function(value, metadata, record, row, col, store) {
650
+                setMetaAttr(metadata, record);
651
+                return value;
652
+            },
653
+	    editor: new fm.TextField()
654
+	}
655
+    ]);
656
+
657
+    var rec = Ext.data.Record.create([
658
+	'enabled','caid','tid','sid','key','comment'
659
+    ]);
660
+
661
+    store = new Ext.data.JsonStore({
662
+       root: 'entries',
663
+       fields: rec,
664
+       url: "tablemgr",
665
+       autoLoad: true,
666
+       id: 'id',
667
+       baseParams: {table: 'ccw', op: "get"}
668
+    });
669
+
670
+    tvheadend.comet.on('ccwStatus', function(server) {
671
+        var rec = store.getById(server.id);
672
+        if(rec){
673
+            rec.set('connected', server.connected);
674
+        }
675
+    });
676
+
677
+    return new tvheadend.tableEditor('CCW Keys', 'ccw', cm, rec,
678
+				     [enabledColumn], store,
679
+				     'config_ccw.html', 'key');
680
+}
(2-2/6)