- Add support of ORF P4 Irdeto mode
[oscam.git] / module-ghttp.c
blob80e6fde0a8e1bde66fa4949ef5f0d6533116df50
1 #define MODULE_LOG_PREFIX "ghttp"
3 #include "globals.h"
4 #ifdef MODULE_GHTTP
5 #include "oscam-client.h"
6 #include "oscam-net.h"
7 #include "oscam-string.h"
8 #include "oscam-reader.h"
9 #include "oscam-work.h"
10 #include "module-dvbapi.h"
11 #ifdef WITH_SSL
12 #include <openssl/crypto.h>
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
15 #endif
17 typedef struct
19 uchar *session_id;
20 uchar *host_id;
21 uchar *fallback_id;
22 pthread_mutex_t conn_mutex;
23 LLIST *post_contexts;
24 LLIST *ecm_q;
25 #ifdef WITH_SSL
26 SSL *ssl_handle;
27 #endif
28 } s_ghttp;
30 typedef struct
32 uint16_t onid;
33 uint16_t tsid;
34 uint16_t sid;
35 uint16_t pid;
36 } s_ca_context;
38 static LLIST *ghttp_ignored_contexts;
39 #ifdef WITH_SSL
40 static SSL_CTX *ghttp_ssl_context;
41 #endif
43 static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST *er);
45 #ifdef WITH_SSL
46 static bool _ssl_connect(struct s_client *client, int32_t fd)
48 s_ghttp *context = (s_ghttp *)client->ghttp;
50 if(context->ssl_handle) // cleanup previous
52 SSL_shutdown(context->ssl_handle);
53 SSL_free(context->ssl_handle);
56 cs_log_dbg(D_CLIENT, "%s: trying ssl...", client->reader->label);
58 context->ssl_handle = SSL_new(ghttp_ssl_context);
59 if(context->ssl_handle == NULL)
61 ERR_print_errors_fp(stderr);
62 ERR_remove_state(0);
63 return false;
65 if(!SSL_set_fd(context->ssl_handle, fd))
67 ERR_print_errors_fp(stderr);
68 ERR_remove_state(0);
69 return false;
71 if(SSL_connect(context->ssl_handle) != 1)
73 ERR_print_errors_fp(stderr);
74 ERR_remove_state(0);
77 if(context->ssl_handle)
79 cs_log_dbg(D_CLIENT, "%s: ssl established", client->reader->label);
80 return true;
83 return false;
85 #endif
87 int32_t ghttp_client_init(struct s_client *cl)
89 int32_t handle;
90 char *str = NULL;
92 ghttp_ignored_contexts = ll_create("ignored contexts");
93 #ifdef WITH_SSL
94 ghttp_ssl_context = SSL_CTX_new(SSLv23_client_method());
95 if(ghttp_ssl_context == NULL)
97 ERR_print_errors_fp(stderr);
98 ERR_remove_state(0);
100 #endif
102 if(cl->reader->r_port == 0)
103 { cl->reader->r_port = cl->reader->ghttp_use_ssl ? 443 : 80; }
105 str = strstr(cl->reader->device, ".");
106 if(!str)
108 char host[128];
109 cs_strncpy(host, cl->reader->device, sizeof(cl->reader->device));
110 snprintf(cl->reader->device, sizeof(cl->reader->device), "%s.appspot.com", host);
113 cs_log("%s: init google cache client %s:%d (fd=%d)", cl->reader->label, cl->reader->device, cl->reader->r_port, cl->udp_fd);
115 if(cl->udp_fd) { network_tcp_connection_close(cl->reader, "re-init"); }
117 handle = network_tcp_connection_open(cl->reader);
118 if(handle < 0) { return -1; }
120 cl->reader->tcp_connected = 2;
121 cl->reader->card_status = CARD_INSERTED;
122 cl->reader->last_g = cl->reader->last_s = time((time_t *)0);
124 cl->pfd = cl->udp_fd;
126 if(!cl->ghttp)
128 if(!cs_malloc(&(cl->ghttp), sizeof(s_ghttp))) { return -1; }
129 memset(cl->ghttp, 0, sizeof(s_ghttp));
130 ((s_ghttp *)cl->ghttp)->post_contexts = ll_create("post contexts");
131 ((s_ghttp *)cl->ghttp)->ecm_q = ll_create("ecm queue");
133 else
135 ll_clear(((s_ghttp *)cl->ghttp)->ecm_q);
138 if(cl->reader->ghttp_use_ssl)
140 #ifndef WITH_SSL
141 cs_log("%s: use_ssl set but no ssl support available, aborting...", cl->reader->label);
142 return -1;
143 #endif
144 #ifdef WITH_SSL
145 if(ghttp_ssl_context == NULL) { return -1; }
147 if(_ssl_connect(cl, handle))
149 cl->crypted = 1;
151 else
153 network_tcp_connection_close(cl->reader, "ssl failed");
154 return -1;
156 #endif
159 return 0;
162 static uint32_t javastring_hashcode(uchar *input, int32_t len)
164 uint32_t h = 0;
165 while(/**input &&*/ len--)
167 h = 31 * h + *input++;
169 return h;
172 static int32_t ghttp_send_int(struct s_client *client, uchar *buf, int32_t l)
174 cs_log_dbg(D_CLIENT, "%s: sending %d bytes", client->reader->label, l);
175 if(!client->pfd)
177 // disconnected? try reinit.
178 cs_log_dbg(D_CLIENT, "%s: disconnected?", client->reader->label);
179 ghttp_client_init(client);
182 #ifdef WITH_SSL
183 s_ghttp *context = (s_ghttp *)client->ghttp;
184 if(client->reader->ghttp_use_ssl)
185 { return SSL_write(context->ssl_handle, buf, l); }
186 #endif
187 return send(client->pfd, buf, l, 0);
190 static int32_t ghttp_send(struct s_client *client, uchar *buf, int32_t l)
192 s_ghttp *context = (s_ghttp *)client->ghttp;
193 SAFE_MUTEX_LOCK(&context->conn_mutex);
194 int32_t ret = ghttp_send_int(client, buf, l);
195 SAFE_MUTEX_UNLOCK(&context->conn_mutex);
196 return ret;
199 static int32_t ghttp_recv_int(struct s_client *client, uchar *buf, int32_t l)
201 int32_t n = -1;
202 s_ghttp *context = (s_ghttp *)client->ghttp;
204 if(!client->pfd)
206 ll_clear(context->ecm_q);
207 return -1;
210 if(client->reader->ghttp_use_ssl)
212 #ifdef WITH_SSL
213 n = SSL_read(context->ssl_handle, buf, l);
214 #endif
216 else { n = cs_recv(client->pfd, buf, l, 0); }
218 if(n > 0)
220 cs_log_dbg(D_CLIENT, "%s: received %d bytes from %s", client->reader->label, n, remote_txt());
221 client->last = time((time_t *)0);
223 if(n > 400)
225 buf[n] = '\0';
226 cs_log_dbg(D_CLIENT, "%s: unexpected reply size %d - %s", client->reader->label, n, buf);
227 return -1; // assumes google error, disconnects
230 if(n < 5)
232 cs_log_dbg(D_CLIENT, "%s: read %d bytes, disconnecting", client->reader->label, n);
233 n = -1;
235 return n;
238 static int32_t ghttp_recv(struct s_client *client, uchar *buf, int32_t l)
240 s_ghttp *context = (s_ghttp *)client->ghttp;
241 SAFE_MUTEX_LOCK(&context->conn_mutex);
242 int32_t ret = ghttp_recv_int(client, buf, l);
243 SAFE_MUTEX_UNLOCK(&context->conn_mutex);
244 return ret;
247 static bool _is_post_context(LLIST *ca_contexts, ECM_REQUEST *er, bool remove_data)
249 s_ca_context *ctx;
250 s_ca_context *existing = NULL;
251 if(cs_malloc(&ctx, sizeof(s_ca_context)))
253 ctx->onid = er->onid;
254 ctx->tsid = er->tsid;
255 ctx->sid = er->srvid;
256 ctx->pid = 0;
258 existing = (s_ca_context *)ll_contains_data(ca_contexts, ctx, sizeof(s_ca_context));
259 if(remove_data)
260 { ll_remove_data(ca_contexts, existing); }
261 NULLFREE(ctx);
263 return existing != NULL;
266 static void _add_context(LLIST *ca_contexts, s_ca_context *context)
268 if(!ll_contains_data(ca_contexts, context, sizeof(s_ca_context)))
270 ll_append(ca_contexts, context);
272 else { NULLFREE(context); }
274 while(ll_count(ca_contexts) > 64)
275 { ll_remove_first_data(ca_contexts); }
277 cs_log_dbg(D_CLIENT, "ca contexts size %d", ll_count(ca_contexts));
280 static void _set_pid_status(LLIST *ca_contexts, uint16_t onid, uint16_t tsid, uint16_t sid, uint16_t pid)
282 s_ca_context *ctx;
283 if(cs_malloc(&ctx, sizeof(s_ca_context)))
285 ctx->onid = onid;
286 ctx->tsid = tsid;
287 ctx->sid = sid;
288 ctx->pid = pid;
289 _add_context(ca_contexts, ctx);
293 static void _set_pids_status(LLIST *ca_contexts, uint16_t onid, uint16_t tsid, uint16_t sid, uchar *buf, int len)
295 int8_t offs = 0;
296 uint16_t pid = 0;
298 while(offs < len)
300 pid = b2i(2, buf + offs);
301 offs += 2;
302 _set_pid_status(ca_contexts, onid, tsid, sid, pid);
306 static bool _swap_hosts(s_ghttp *context)
308 if(!context->fallback_id) { return false; }
309 uchar *tmp = context->host_id;
310 context->host_id = context->fallback_id;
311 context->fallback_id = tmp;
312 NULLFREE(context->session_id);
313 ll_clear(context->ecm_q);
314 ll_clear_data(ghttp_ignored_contexts);
315 return true;
318 static char *_get_header_substr(uchar *buf, const char *start, const char *end)
320 char *data = strstr((char *)buf, start);
321 if(!data) { return NULL; }
322 data += strlen(start);
323 int len = strstr(data, end) - data;
324 if(len <= 0) { return NULL; }
325 char tmp = data[len];
326 data[len] = '\0';
327 char *value = cs_strdup(data);
328 data[len] = tmp;
329 return value;
332 static int _get_int_header(uchar *buf, const char *start)
334 char *data = strstr((char *)buf, start);
335 if(!data) { return -1; }
336 data += strlen(start);
337 return atoi(data);
340 static char *_get_header(uchar *buf, const char *start)
342 return _get_header_substr(buf, start, "\r\n");
345 static int32_t ghttp_recv_chk(struct s_client *client, uchar *dcw, int32_t *rc, uchar *buf, int32_t n)
347 char *data;
348 char *hdrstr;
349 uchar *content;
350 int rcode, len, clen = 0;
351 s_ghttp *context = (s_ghttp *)client->ghttp;
352 ECM_REQUEST *er = NULL;
354 if(n < 5) { return -1; }
356 data = strstr((char *)buf, "HTTP/1.1 ");
357 if(!data || ll_count(context->ecm_q) > 6)
359 cs_log_dbg(D_CLIENT, "%s: non http or otherwise corrupt response: %s", client->reader->label, buf);
360 cs_log_dump_dbg(D_CLIENT, buf, n, "%s: ", client->reader->label);
361 network_tcp_connection_close(client->reader, "receive error");
362 NULLFREE(context->session_id);
363 ll_clear(context->ecm_q);
364 return -1;
367 LL_ITER itr = ll_iter_create(context->ecm_q);
368 er = (ECM_REQUEST *)ll_iter_next(&itr);
370 rcode = _get_int_header(buf, "HTTP/1.1 ");
371 clen = _get_int_header(buf, "Content-Length: ");
373 content = (uchar *)(strstr(data, "\r\n\r\n") + 4);
375 hdrstr = _get_header_substr(buf, "ETag: \"", "\"\r\n");
376 if(hdrstr)
378 NULLFREE(context->host_id);
379 context->host_id = (uchar *)hdrstr;
380 cs_log_dbg(D_CLIENT, "%s: new name: %s", client->reader->label, context->host_id);
381 len = b64decode(context->host_id);
382 if(len == 0 || len >= 64)
384 NULLFREE(context->host_id);
386 else
388 cs_log_dbg(D_CLIENT, "%s: redirected...", client->reader->label);
389 NULLFREE(context->session_id);
390 ll_clear_data(ghttp_ignored_contexts);
391 ll_clear(context->ecm_q);
392 return -1;
396 hdrstr = _get_header_substr(buf, "ETag: W/\"", "\"\r\n");
397 if(hdrstr)
399 NULLFREE(context->fallback_id);
400 context->fallback_id = (uchar *)hdrstr;
401 cs_log_dbg(D_CLIENT, "%s: new fallback name: %s", client->reader->label, context->fallback_id);
402 len = b64decode(context->fallback_id);
403 if(len == 0 || len >= 64)
405 NULLFREE(context->fallback_id);
409 hdrstr = _get_header(buf, "Set-Cookie: GSSID=");
410 if(hdrstr)
412 NULLFREE(context->session_id);
413 context->session_id = (uchar *)hdrstr;
414 cs_log_dbg(D_CLIENT, "%s: set session_id to: %s", client->reader->label, context->session_id);
417 // buf[n] = '\0';
418 // cs_log_dump_dbg(D_TRACE, content, clen, "%s: reply\n%s", client->reader->label, buf);
420 if(rcode < 200 || rcode > 204)
422 cs_log_dbg(D_CLIENT, "%s: http error code %d", client->reader->label, rcode);
423 data = strstr((char *)buf, "Content-Type: application/octet-stream"); // if not octet-stream, google error. need reconnect?
424 if(data) // we have error info string in the post content
426 if(clen > 0)
428 content[clen] = '\0';
429 cs_log_dbg(D_CLIENT, "%s: http error message: %s", client->reader->label, content);
432 if(rcode == 503)
434 if(er && _is_post_context(context->post_contexts, er, false))
436 if(_swap_hosts(context))
438 cs_log_dbg(D_CLIENT, "%s: switching to fallback", client->reader->label);
440 else
442 cs_log_dbg(D_CLIENT, "%s: recv_chk got 503 despite post, trying reconnect", client->reader->label);
443 network_tcp_connection_close(client->reader, "reconnect");
444 ll_clear(context->ecm_q);
447 else
449 // on 503 cache timeout, retry with POST immediately (and switch to POST for subsequent)
450 if(er)
452 _set_pid_status(context->post_contexts, er->onid, er->tsid, er->srvid, 0);
453 cs_log_dbg(D_CLIENT, "%s: recv_chk got 503, trying direct post", client->reader->label);
454 _ghttp_post_ecmdata(client, er);
458 else if(rcode == 401)
460 NULLFREE(context->session_id);
461 if(er)
463 cs_log_dbg(D_CLIENT, "%s: session expired, trying direct post", client->reader->label);
464 _ghttp_post_ecmdata(client, er);
467 else if(rcode == 403)
469 client->reader->enable = 0;
470 network_tcp_connection_close(client->reader, "login failure");
471 ll_clear(context->ecm_q);
472 cs_log("%s: invalid username/password, disabling reader.", client->reader->label);
475 // not sure if this is needed on failure, copied from newcamd
476 *rc = 0;
477 memset(dcw, 0, 16);
479 return -1;
482 // successful http reply (200 ok or 204 no content)
484 hdrstr = _get_header(buf, "Pragma: context-ignore=");
485 if(hdrstr)
487 if(clen > 1)
489 cs_log_dump_dbg(D_CLIENT, content, clen, "%s: pmt ignore reply - %s (%d pids)", client->reader->label, hdrstr, clen / 2);
490 uint32_t onid = 0, tsid = 0, sid = 0;
491 if(sscanf(hdrstr, "%4x-%4x-%4x", &onid, &tsid, &sid) == 3)
492 { _set_pids_status(ghttp_ignored_contexts, onid, tsid, sid, content, clen); }
493 NULLFREE(hdrstr);
494 return -1;
496 NULLFREE(hdrstr);
499 data = strstr((char *)buf, "Pragma: context-ignore-clear");
500 if(data)
502 cs_log_dbg(D_CLIENT, "%s: clearing local ignore list (size %d)", client->reader->label, ll_count(ghttp_ignored_contexts));
503 ll_clear_data(ghttp_ignored_contexts);
506 // switch back to cache get after rapid ecm response (arbitrary atm), only effect is a slight bw save for client
507 if(!er || _is_post_context(context->post_contexts, er, false))
509 data = strstr((char *)buf, "Pragma: cached");
510 if(data || (client->cwlastresptime > 0 && client->cwlastresptime < 640))
512 cs_log_dbg(D_CLIENT, "%s: probably cached cw (%d ms), switching back to cache get for next req", client->reader->label, client->cwlastresptime);
513 if(er) { _is_post_context(context->post_contexts, er, true); }
517 if(clen == 16) // cw in content
519 memcpy(dcw, content, 16);
520 *rc = 1;
521 er = ll_remove_first(context->ecm_q);
522 if(!er) { return -1; }
523 cs_log_dump_dbg(D_TRACE, dcw, 16, "%s: cw recv chk for idx %d", client->reader->label, er->idx);
524 return er->idx;
526 else
528 if(clen != 0) { cs_log_dump_dbg(D_CLIENT, content, clen, "%s: recv_chk fail, clen = %d", client->reader->label, clen); }
530 return -1;
533 static char *_ghttp_basic_auth(struct s_client *client)
535 uchar auth[64];
536 char *encauth = NULL;
537 int32_t ret;
538 s_ghttp *context = (s_ghttp *)client->ghttp;
540 if(!context->session_id && strlen(client->reader->r_usr) > 0)
542 cs_log_dbg(D_CLIENT, "%s: username specified and no existing session, adding basic auth", client->reader->label);
543 ret = snprintf((char *)auth, sizeof(auth), "%s:%s", client->reader->r_usr, client->reader->r_pwd);
544 ret = b64encode((char *)auth, ret, &encauth);
546 return encauth;
549 static int32_t _ghttp_http_get(struct s_client *client, uint32_t hash, int odd)
551 uchar req[128];
552 char *encauth = NULL;
553 int32_t ret;
554 s_ghttp *context = (s_ghttp *)client->ghttp;
556 encauth = _ghttp_basic_auth(client);
558 if(encauth) // basic auth login
560 ret = snprintf((char *)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s\r\n\r\n", odd ? 81 : 80, hash, context->host_id, encauth);
561 NULLFREE(encauth);
563 else
565 if(context->session_id) // session exists
567 ret = snprintf((char *)req, sizeof(req), "GET /api/c/%s/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n", context->session_id, odd ? 81 : 80, hash, context->host_id);
569 else // no credentials configured, assume no session required
571 ret = snprintf((char *)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n", odd ? 81 : 80, hash, context->host_id);
575 ret = ghttp_send(client, req, ret);
577 return ret;
580 static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST *er)
582 uchar req[640];
583 uchar *end;
584 char *encauth = NULL;
585 int32_t ret;
586 s_ghttp *context = (s_ghttp *)client->ghttp;
588 encauth = _ghttp_basic_auth(client);
590 if(encauth) // basic auth login
592 ret = snprintf((char *)req, sizeof(req), "POST /api/e/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s\r\nContent-Length: %d\r\n\r\n", er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, encauth, er->ecmlen);
593 NULLFREE(encauth);
595 else
597 if(context->session_id) // session exists
599 ret = snprintf((char *)req, sizeof(req), "POST /api/e/%s/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n", context->session_id, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, er->ecmlen);
601 else // no credentials configured, assume no session required
603 ret = snprintf((char *)req, sizeof(req), "POST /api/e/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n", er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, er->ecmlen);
606 end = req + ret;
607 memcpy(end, er->ecm, er->ecmlen);
609 cs_log_dbg(D_CLIENT, "%s: sending full ecm - /api/e/%x/%x/%x/%x/%x/%x", client->reader->label, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid);
611 ret = ghttp_send(client, req, ret + er->ecmlen);
613 return ret;
616 static bool _is_pid_ignored(ECM_REQUEST *er)
618 s_ca_context *ignore;
619 if(cs_malloc(&ignore, sizeof(s_ca_context)))
621 ignore->onid = er->onid;
622 ignore->tsid = er->tsid;
623 ignore->sid = er->srvid;
624 ignore->pid = er->pid;
625 if(ll_contains_data(ghttp_ignored_contexts, ignore, sizeof(s_ca_context)))
627 NULLFREE(ignore);
628 return true;
630 else { NULLFREE(ignore); }
632 return false;
635 static int32_t ghttp_send_ecm(struct s_client *client, ECM_REQUEST *er)
637 uint32_t hash;
638 s_ghttp *context = (s_ghttp *)client->ghttp;
640 if(_is_pid_ignored(er))
642 cs_log_dbg(D_CLIENT, "%s: ca context found in ignore list, ecm blocked: %x-%x-%x pid %x", client->reader->label, er->onid, er->tsid, er->srvid, er->pid);
643 return -1;
646 if(!context->host_id) { context->host_id = (uchar *)cs_strdup(client->reader->device); }
648 ll_append(context->ecm_q, er);
649 if(ll_count(context->ecm_q) > 1)
650 { cs_log_dbg(D_CLIENT, "%s: %d simultaneous ecms...", client->reader->label, ll_count(context->ecm_q)); }
652 if(_is_post_context(context->post_contexts, er, false))
654 _ghttp_post_ecmdata(client, er);
656 else
658 hash = javastring_hashcode(er->ecm + 3, er->ecmlen - 3);
659 _ghttp_http_get(client, hash, er->ecm[0] == 0x81);
662 return 0;
665 static void ghttp_cleanup(struct s_client *client)
667 s_ghttp *context = (s_ghttp *)client->ghttp;
669 ll_destroy_data(&ghttp_ignored_contexts);
671 if(context)
673 NULLFREE(context->session_id);
674 NULLFREE(context->host_id);
675 NULLFREE(context->fallback_id);
676 ll_destroy(&context->ecm_q);
677 ll_destroy_data(&context->post_contexts);
678 #ifdef WITH_SSL
679 if(context->ssl_handle)
681 SSL_shutdown(context->ssl_handle);
682 SSL_free(context->ssl_handle);
684 SSL_CTX_free(ghttp_ssl_context);
685 #endif
686 NULLFREE(context);
690 #ifdef HAVE_DVBAPI
691 static int32_t ghttp_capmt_notify(struct s_client *client, struct demux_s *demux)
693 uchar req[640], lenhdr[64] = "";
694 uchar *pids = NULL;
695 uchar *end;
696 char *encauth = NULL;
697 int32_t ret;
698 int8_t i, pids_len = 0, offs = 0;
699 s_ghttp *context = (s_ghttp *)client->ghttp;
701 if(!context) { return -1; }
703 cs_log_dbg(D_CLIENT, "%s: capmt %x-%x-%x %d pids on adapter %d mask %x dmx index %d", client->reader->label, demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount, demux->adapter_index, demux->ca_mask, demux->demux_index);
705 if(demux->ECMpidcount > 0)
707 if(cs_malloc(&pids, demux->ECMpidcount * 8))
709 pids_len = demux->ECMpidcount * 8;
710 for(i = 0; i < demux->ECMpidcount; i++)
712 i2b_buf(2, demux->ECMpids[i].ECM_PID, pids + offs);
713 i2b_buf(2, demux->ECMpids[i].CAID, pids + (offs += 2));
714 i2b_buf(4, demux->ECMpids[i].PROVID, pids + (offs += 2));
715 offs += 4;
717 snprintf((char *)lenhdr, sizeof(lenhdr), "\r\nContent-Length: %d", pids_len);
719 else { return -1; }
722 if(!context->host_id) { context->host_id = (uchar *)cs_strdup(client->reader->device); }
724 encauth = _ghttp_basic_auth(client);
726 if(encauth) // basic auth login
728 ret = snprintf((char *)req, sizeof(req), "%s /api/p/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s%s\r\n\r\n", ((pids_len > 0) ? "POST" : "GET"), demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount, demux->enigma_namespace, context->host_id, encauth, lenhdr);
729 NULLFREE(encauth);
731 else
733 if(context->session_id) // session exists
735 ret = snprintf((char *)req, sizeof(req), "%s /api/p/%s/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s%s\r\n\r\n", ((pids_len > 0) ? "POST" : "GET"), context->session_id, demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount, demux->enigma_namespace, context->host_id, lenhdr);
737 else // no credentials configured, assume no session required
739 ret = snprintf((char *)req, sizeof(req), "%s /api/p/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s%s\r\n\r\n", ((pids_len > 0) ? "POST" : "GET"), demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount, demux->enigma_namespace, context->host_id, lenhdr);
742 end = req + ret;
743 if(pids_len > 0)
745 memcpy(end, pids, pids_len);
746 cs_log_dbg(D_CLIENT, "%s: new unscrambling detected, switching to post", client->reader->label);
747 _set_pid_status(context->post_contexts, demux->onid, demux->tsid, demux->program_number, 0);
749 cs_log_dump_dbg(D_CLIENT, pids, pids_len, "%s: sending capmt ecm pids - %s /api/p/%x/%x/%x/%x/%x", client->reader->label, (pids_len > 0) ? "POST" : "GET", demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount, demux->enigma_namespace);
751 ret = ghttp_send(client, req, ret + pids_len);
753 if(pids_len > 0) { NULLFREE(pids); }
755 return 0;
757 #endif
759 void module_ghttp(struct s_module *ph)
761 ph->ptab.nports = 0;
762 // ph->ptab.ports[0].s_port = cfg.ghttp_port;
763 ph->desc = "ghttp";
764 ph->type = MOD_CONN_TCP;
765 // ph->listenertype = LIS_GHTTP;
766 ph->large_ecm_support = 1;
767 ph->recv = ghttp_recv;
768 ph->c_init = ghttp_client_init;
769 ph->c_recv_chk = ghttp_recv_chk;
770 ph->c_send_ecm = ghttp_send_ecm;
771 ph->cleanup = ghttp_cleanup;
772 #ifdef HAVE_DVBAPI
773 ph->c_capmt = ghttp_capmt_notify;
774 #endif
775 ph->num = R_GHTTP;
777 #endif