1 #define MODULE_LOG_PREFIX "ghttp"
5 #include "oscam-client.h"
7 #include "oscam-string.h"
8 #include "oscam-reader.h"
9 #include "oscam-work.h"
10 #include "module-dvbapi.h"
12 #include <openssl/crypto.h>
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
22 pthread_mutex_t conn_mutex
;
38 static LLIST
*ghttp_ignored_contexts
;
40 static SSL_CTX
*ghttp_ssl_context
;
43 static int32_t _ghttp_post_ecmdata(struct s_client
*client
, ECM_REQUEST
*er
);
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 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
67 if(!SSL_set_fd(context
->ssl_handle
, fd
))
69 ERR_print_errors_fp(stderr
);
70 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
75 if(SSL_connect(context
->ssl_handle
) != 1)
77 ERR_print_errors_fp(stderr
);
78 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
83 if(context
->ssl_handle
)
85 cs_log_dbg(D_CLIENT
, "%s: ssl established", client
->reader
->label
);
93 int32_t ghttp_client_init(struct s_client
*cl
)
98 ghttp_ignored_contexts
= ll_create("ignored contexts");
100 ghttp_ssl_context
= SSL_CTX_new(SSLv23_client_method());
101 if(ghttp_ssl_context
== NULL
)
103 ERR_print_errors_fp(stderr
);
104 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
110 if(cl
->reader
->r_port
== 0)
112 cl
->reader
->r_port
= cl
->reader
->ghttp_use_ssl
? 443 : 80;
115 str
= strstr(cl
->reader
->device
, ".");
119 cs_strncpy(host
, cl
->reader
->device
, sizeof(host
));
120 snprintf(cl
->reader
->device
, sizeof(cl
->reader
->device
), "%.115s.appspot.com", host
);
123 cs_log("%s: init google cache client %s:%d (fd=%d)", cl
->reader
->label
, cl
->reader
->device
, cl
->reader
->r_port
, cl
->udp_fd
);
127 network_tcp_connection_close(cl
->reader
, "re-init");
130 handle
= network_tcp_connection_open(cl
->reader
);
136 cl
->reader
->tcp_connected
= 2;
137 cl
->reader
->card_status
= CARD_INSERTED
;
138 cl
->reader
->last_g
= cl
->reader
->last_s
= time((time_t *)0);
140 cl
->pfd
= cl
->udp_fd
;
143 if(!cs_malloc(&(cl
->ghttp
), sizeof(s_ghttp
)))
147 memset(cl
->ghttp
, 0, sizeof(s_ghttp
));
148 ((s_ghttp
*)cl
->ghttp
)->post_contexts
= ll_create("post contexts");
149 ((s_ghttp
*)cl
->ghttp
)->ecm_q
= ll_create("ecm queue");
153 ll_clear(((s_ghttp
*)cl
->ghttp
)->ecm_q
);
156 if(cl
->reader
->ghttp_use_ssl
)
159 cs_log("%s: use_ssl set but no ssl support available, aborting...", cl
->reader
->label
);
163 if(ghttp_ssl_context
== NULL
)
168 if(_ssl_connect(cl
, handle
))
174 network_tcp_connection_close(cl
->reader
, "ssl failed");
183 static uint32_t javastring_hashcode(uint8_t *input
, int32_t len
)
186 while(/**input &&*/ len
--)
188 h
= 31 * h
+ *input
++;
193 static int32_t ghttp_send_int(struct s_client
*client
, uint8_t *buf
, int32_t l
)
195 cs_log_dbg(D_CLIENT
, "%s: sending %d bytes", client
->reader
->label
, l
);
198 // disconnected? try reinit.
199 cs_log_dbg(D_CLIENT
, "%s: disconnected?", client
->reader
->label
);
200 ghttp_client_init(client
);
204 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
205 if(client
->reader
->ghttp_use_ssl
)
207 return SSL_write(context
->ssl_handle
, buf
, l
);
210 return send(client
->pfd
, buf
, l
, 0);
213 static int32_t ghttp_send(struct s_client
*client
, uint8_t *buf
, int32_t l
)
215 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
216 SAFE_MUTEX_LOCK(&context
->conn_mutex
);
217 int32_t ret
= ghttp_send_int(client
, buf
, l
);
218 SAFE_MUTEX_UNLOCK(&context
->conn_mutex
);
222 static int32_t ghttp_recv_int(struct s_client
*client
, uint8_t *buf
, int32_t l
)
225 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
229 ll_clear(context
->ecm_q
);
233 if(client
->reader
->ghttp_use_ssl
)
236 n
= SSL_read(context
->ssl_handle
, buf
, l
);
241 n
= cs_recv(client
->pfd
, buf
, l
, 0);
246 cs_log_dbg(D_CLIENT
, "%s: received %d bytes from %s", client
->reader
->label
, n
, remote_txt());
247 client
->last
= time((time_t *)0);
252 cs_log_dbg(D_CLIENT
, "%s: unexpected reply size %d - %s", client
->reader
->label
, n
, buf
);
253 return -1; // assumes google error, disconnects
259 cs_log_dbg(D_CLIENT
, "%s: read %d bytes, disconnecting", client
->reader
->label
, n
);
265 static int32_t ghttp_recv(struct s_client
*client
, uint8_t *buf
, int32_t l
)
267 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
268 SAFE_MUTEX_LOCK(&context
->conn_mutex
);
269 int32_t ret
= ghttp_recv_int(client
, buf
, l
);
270 SAFE_MUTEX_UNLOCK(&context
->conn_mutex
);
274 static bool _is_post_context(LLIST
*ca_contexts
, ECM_REQUEST
*er
, bool remove_data
)
277 s_ca_context
*existing
= NULL
;
279 if(cs_malloc(&ctx
, sizeof(s_ca_context
)))
281 ctx
->onid
= er
->onid
;
282 ctx
->tsid
= er
->tsid
;
283 ctx
->sid
= er
->srvid
;
286 existing
= (s_ca_context
*)ll_contains_data(ca_contexts
, ctx
, sizeof(s_ca_context
));
290 ll_remove_data(ca_contexts
, existing
);
294 return existing
!= NULL
;
297 static void _add_context(LLIST
*ca_contexts
, s_ca_context
*context
)
299 if(!ll_contains_data(ca_contexts
, context
, sizeof(s_ca_context
)))
301 ll_append(ca_contexts
, context
);
308 while(ll_count(ca_contexts
) > 64)
310 ll_remove_first_data(ca_contexts
);
313 cs_log_dbg(D_CLIENT
, "ca contexts size %d", ll_count(ca_contexts
));
316 static void _set_pid_status(LLIST
*ca_contexts
, uint16_t onid
, uint16_t tsid
, uint16_t sid
, uint16_t pid
)
319 if(cs_malloc(&ctx
, sizeof(s_ca_context
)))
325 _add_context(ca_contexts
, ctx
);
329 static void _set_pids_status(LLIST
*ca_contexts
, uint16_t onid
, uint16_t tsid
, uint16_t sid
, uint8_t *buf
, int len
)
336 pid
= b2i(2, buf
+ offs
);
338 _set_pid_status(ca_contexts
, onid
, tsid
, sid
, pid
);
342 static bool _swap_hosts(s_ghttp
*context
)
344 if(!context
->fallback_id
)
349 uint8_t *tmp
= context
->host_id
;
350 context
->host_id
= context
->fallback_id
;
351 context
->fallback_id
= tmp
;
352 NULLFREE(context
->session_id
);
353 ll_clear(context
->ecm_q
);
354 ll_clear_data(ghttp_ignored_contexts
);
358 static char *_get_header_substr(uint8_t *buf
, const char *start
, const char *end
)
360 char *data
= strstr((char *)buf
, start
);
366 data
+= cs_strlen(start
);
367 int len
= strstr(data
, end
) - data
;
373 char tmp
= data
[len
];
375 char *value
= cs_strdup(data
);
380 static int _get_int_header(uint8_t *buf
, const char *start
)
382 char *data
= strstr((char *)buf
, start
);
383 if(!data
) { return -1; }
384 data
+= cs_strlen(start
);
388 static char *_get_header(uint8_t *buf
, const char *start
)
390 return _get_header_substr(buf
, start
, "\r\n");
393 static int32_t ghttp_recv_chk(struct s_client
*client
, uint8_t *dcw
, int32_t *rc
, uint8_t *buf
, int32_t n
)
398 int rcode
, len
, clen
= 0;
399 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
400 ECM_REQUEST
*er
= NULL
;
407 data
= strstr((char *)buf
, "HTTP/1.1 ");
408 if(!data
|| ll_count(context
->ecm_q
) > 6)
410 cs_log_dbg(D_CLIENT
, "%s: non http or otherwise corrupt response: %s", client
->reader
->label
, buf
);
411 cs_log_dump_dbg(D_CLIENT
, buf
, n
, "%s: ", client
->reader
->label
);
412 network_tcp_connection_close(client
->reader
, "receive error");
413 NULLFREE(context
->session_id
);
414 ll_clear(context
->ecm_q
);
418 LL_ITER itr
= ll_iter_create(context
->ecm_q
);
419 er
= (ECM_REQUEST
*)ll_iter_next(&itr
);
421 rcode
= _get_int_header(buf
, "HTTP/1.1 ");
422 clen
= _get_int_header(buf
, "Content-Length: ");
424 content
= (uint8_t *)(strstr(data
, "\r\n\r\n") + 4);
426 hdrstr
= _get_header_substr(buf
, "ETag: \"", "\"\r\n");
429 NULLFREE(context
->host_id
);
430 context
->host_id
= (uint8_t *)hdrstr
;
431 cs_log_dbg(D_CLIENT
, "%s: new name: %s", client
->reader
->label
, context
->host_id
);
433 len
= b64decode(context
->host_id
);
434 if(len
== 0 || len
>= 64)
436 NULLFREE(context
->host_id
);
440 cs_log_dbg(D_CLIENT
, "%s: redirected...", client
->reader
->label
);
441 NULLFREE(context
->session_id
);
442 ll_clear_data(ghttp_ignored_contexts
);
443 ll_clear(context
->ecm_q
);
448 hdrstr
= _get_header_substr(buf
, "ETag: W/\"", "\"\r\n");
451 NULLFREE(context
->fallback_id
);
452 context
->fallback_id
= (uint8_t *)hdrstr
;
453 cs_log_dbg(D_CLIENT
, "%s: new fallback name: %s", client
->reader
->label
, context
->fallback_id
);
455 len
= b64decode(context
->fallback_id
);
456 if(len
== 0 || len
>= 64)
458 NULLFREE(context
->fallback_id
);
462 hdrstr
= _get_header(buf
, "Set-Cookie: GSSID=");
465 NULLFREE(context
->session_id
);
466 context
->session_id
= (uint8_t *)hdrstr
;
467 cs_log_dbg(D_CLIENT
, "%s: set session_id to: %s", client
->reader
->label
, context
->session_id
);
471 // cs_log_dump_dbg(D_TRACE, content, clen, "%s: reply\n%s", client->reader->label, buf);
473 if(rcode
< 200 || rcode
> 204)
475 cs_log_dbg(D_CLIENT
, "%s: http error code %d", client
->reader
->label
, rcode
);
476 data
= strstr((char *)buf
, "Content-Type: application/octet-stream"); // if not octet-stream, google error. need reconnect?
477 if(data
) // we have error info string in the post content
481 content
[clen
] = '\0';
482 cs_log_dbg(D_CLIENT
, "%s: http error message: %s", client
->reader
->label
, content
);
488 if(er
&& _is_post_context(context
->post_contexts
, er
, false))
490 if(_swap_hosts(context
))
492 cs_log_dbg(D_CLIENT
, "%s: switching to fallback", client
->reader
->label
);
496 cs_log_dbg(D_CLIENT
, "%s: recv_chk got 503 despite post, trying reconnect", client
->reader
->label
);
497 network_tcp_connection_close(client
->reader
, "reconnect");
498 ll_clear(context
->ecm_q
);
503 // on 503 cache timeout, retry with POST immediately (and switch to POST for subsequent)
506 _set_pid_status(context
->post_contexts
, er
->onid
, er
->tsid
, er
->srvid
, 0);
507 cs_log_dbg(D_CLIENT
, "%s: recv_chk got 503, trying direct post", client
->reader
->label
);
508 _ghttp_post_ecmdata(client
, er
);
512 else if(rcode
== 401)
514 NULLFREE(context
->session_id
);
517 cs_log_dbg(D_CLIENT
, "%s: session expired, trying direct post", client
->reader
->label
);
518 _ghttp_post_ecmdata(client
, er
);
521 else if(rcode
== 403)
523 client
->reader
->enable
= 0;
524 network_tcp_connection_close(client
->reader
, "login failure");
525 ll_clear(context
->ecm_q
);
526 cs_log("%s: invalid username/password, disabling reader.", client
->reader
->label
);
529 // not sure if this is needed on failure, copied from newcamd
536 // successful http reply (200 ok or 204 no content)
538 hdrstr
= _get_header(buf
, "Pragma: context-ignore=");
543 cs_log_dump_dbg(D_CLIENT
, content
, clen
, "%s: pmt ignore reply - %s (%d pids)",
544 client
->reader
->label
, hdrstr
, clen
/ 2);
546 uint32_t onid
= 0, tsid
= 0, sid
= 0;
548 if(sscanf(hdrstr
, "%4x-%4x-%4x", &onid
, &tsid
, &sid
) == 3)
550 _set_pids_status(ghttp_ignored_contexts
, onid
, tsid
, sid
, content
, clen
);
558 data
= strstr((char *)buf
, "Pragma: context-ignore-clear");
561 cs_log_dbg(D_CLIENT
, "%s: clearing local ignore list (size %d)",
562 client
->reader
->label
, ll_count(ghttp_ignored_contexts
));
564 ll_clear_data(ghttp_ignored_contexts
);
567 // switch back to cache get after rapid ecm response (arbitrary atm), only effect is a slight bw save for client
568 if(!er
|| _is_post_context(context
->post_contexts
, er
, false))
570 data
= strstr((char *)buf
, "Pragma: cached");
571 if(data
|| (client
->cwlastresptime
> 0 && client
->cwlastresptime
< 640))
573 cs_log_dbg(D_CLIENT
, "%s: probably cached cw (%d ms), switching back to cache get for next req",
574 client
->reader
->label
, client
->cwlastresptime
);
578 _is_post_context(context
->post_contexts
, er
, true);
583 if(clen
== 16) // cw in content
585 memcpy(dcw
, content
, 16);
587 er
= ll_remove_first(context
->ecm_q
);
593 cs_log_dump_dbg(D_TRACE
, dcw
, 16, "%s: cw recv chk for idx %d", client
->reader
->label
, er
->idx
);
600 cs_log_dump_dbg(D_CLIENT
, content
, clen
, "%s: recv_chk fail, clen = %d", client
->reader
->label
, clen
);
606 static char *_ghttp_basic_auth(struct s_client
*client
)
609 char *encauth
= NULL
;
611 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
613 if(!context
->session_id
&& cs_strlen(client
->reader
->r_usr
) > 0)
615 cs_log_dbg(D_CLIENT
, "%s: username specified and no existing session, adding basic auth", client
->reader
->label
);
616 ret
= snprintf((char *)auth
, sizeof(auth
), "%s:%s", client
->reader
->r_usr
, client
->reader
->r_pwd
);
617 b64encode((char *)auth
, ret
, &encauth
);
622 static int32_t _ghttp_http_get(struct s_client
*client
, uint32_t hash
, int odd
)
625 char *encauth
= NULL
;
627 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
629 encauth
= _ghttp_basic_auth(client
);
631 if(encauth
) // basic auth login
633 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",
634 odd
? 81 : 80, hash
, context
->host_id
, encauth
);
640 if(context
->session_id
) // session exists
642 ret
= snprintf((char *)req
, sizeof(req
), "GET /api/c/%s/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n",
643 context
->session_id
, odd
? 81 : 80, hash
, context
->host_id
);
645 else // no credentials configured, assume no session required
647 ret
= snprintf((char *)req
, sizeof(req
), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n",
648 odd
? 81 : 80, hash
, context
->host_id
);
652 ret
= ghttp_send(client
, req
, ret
);
657 static int32_t _ghttp_post_ecmdata(struct s_client
*client
, ECM_REQUEST
*er
)
661 char *encauth
= NULL
;
663 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
665 encauth
= _ghttp_basic_auth(client
);
667 if(encauth
) // basic auth login
669 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",
670 er
->onid
, er
->tsid
, er
->pid
, er
->srvid
, er
->caid
, er
->prid
, context
->host_id
, encauth
, er
->ecmlen
);
676 if(context
->session_id
) // session exists
678 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",
679 context
->session_id
, er
->onid
, er
->tsid
, er
->pid
, er
->srvid
, er
->caid
, er
->prid
, context
->host_id
, er
->ecmlen
);
681 else // no credentials configured, assume no session required
683 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",
684 er
->onid
, er
->tsid
, er
->pid
, er
->srvid
, er
->caid
, er
->prid
, context
->host_id
, er
->ecmlen
);
688 memcpy(end
, er
->ecm
, er
->ecmlen
);
690 cs_log_dbg(D_CLIENT
, "%s: sending full ecm - /api/e/%x/%x/%x/%x/%x/%x",
691 client
->reader
->label
, er
->onid
, er
->tsid
, er
->pid
, er
->srvid
, er
->caid
, er
->prid
);
693 ret
= ghttp_send(client
, req
, ret
+ er
->ecmlen
);
698 static bool _is_pid_ignored(ECM_REQUEST
*er
)
700 s_ca_context
*ignore
;
701 if(cs_malloc(&ignore
, sizeof(s_ca_context
)))
703 ignore
->onid
= er
->onid
;
704 ignore
->tsid
= er
->tsid
;
705 ignore
->sid
= er
->srvid
;
706 ignore
->pid
= er
->pid
;
708 if(ll_contains_data(ghttp_ignored_contexts
, ignore
, sizeof(s_ca_context
)))
721 static int32_t ghttp_send_ecm(struct s_client
*client
, ECM_REQUEST
*er
)
724 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
726 if(_is_pid_ignored(er
))
728 cs_log_dbg(D_CLIENT
, "%s: ca context found in ignore list, ecm blocked: %x-%x-%x pid %x",
729 client
->reader
->label
, er
->onid
, er
->tsid
, er
->srvid
, er
->pid
);
733 if(!context
->host_id
) { context
->host_id
= (uint8_t *)cs_strdup(client
->reader
->device
); }
735 ll_append(context
->ecm_q
, er
);
736 if(ll_count(context
->ecm_q
) > 1)
738 cs_log_dbg(D_CLIENT
, "%s: %d simultaneous ecms...", client
->reader
->label
, ll_count(context
->ecm_q
));
741 if(_is_post_context(context
->post_contexts
, er
, false))
743 _ghttp_post_ecmdata(client
, er
);
747 hash
= javastring_hashcode(er
->ecm
+ 3, er
->ecmlen
- 3);
748 _ghttp_http_get(client
, hash
, er
->ecm
[0] == 0x81);
754 static void ghttp_cleanup(struct s_client
*client
)
756 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
758 ll_destroy_data(&ghttp_ignored_contexts
);
762 NULLFREE(context
->session_id
);
763 NULLFREE(context
->host_id
);
764 NULLFREE(context
->fallback_id
);
765 ll_destroy(&context
->ecm_q
);
766 ll_destroy_data(&context
->post_contexts
);
769 if(context
->ssl_handle
)
771 SSL_shutdown(context
->ssl_handle
);
772 SSL_free(context
->ssl_handle
);
774 SSL_CTX_free(ghttp_ssl_context
);
781 static int32_t ghttp_capmt_notify(struct s_client
*client
, struct demux_s
*demux
)
783 uint8_t req
[640], lenhdr
[64] = "";
784 uint8_t *pids
= NULL
;
786 char *encauth
= NULL
;
788 int8_t i
, pids_len
= 0, offs
= 0;
789 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
796 cs_log_dbg(D_CLIENT
, "%s: capmt %x-%x-%x %d pids on adapter %d mask %x dmx index %d",
797 client
->reader
->label
, demux
->onid
, demux
->tsid
, demux
->program_number
, demux
->ECMpidcount
,
798 demux
->adapter_index
, demux
->ca_mask
, demux
->demux_index
);
800 if(demux
->ECMpidcount
> 0)
802 if(cs_malloc(&pids
, demux
->ECMpidcount
* 8))
804 pids_len
= demux
->ECMpidcount
* 8;
806 for(i
= 0; i
< demux
->ECMpidcount
; i
++)
808 i2b_buf(2, demux
->ECMpids
[i
].ECM_PID
, pids
+ offs
);
809 i2b_buf(2, demux
->ECMpids
[i
].CAID
, pids
+ (offs
+= 2));
810 i2b_buf(4, demux
->ECMpids
[i
].PROVID
, pids
+ (offs
+= 2));
813 snprintf((char *)lenhdr
, sizeof(lenhdr
), "\r\nContent-Length: %d", pids_len
);
821 if(!context
->host_id
)
823 context
->host_id
= (uint8_t *)cs_strdup(client
->reader
->device
);
826 encauth
= _ghttp_basic_auth(client
);
828 if(encauth
) // basic auth login
830 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",
831 ((pids_len
> 0) ? "POST" : "GET"), demux
->onid
, demux
->tsid
, demux
->program_number
,
832 demux
->ECMpidcount
, demux
->ens
, context
->host_id
, encauth
, lenhdr
);
838 if(context
->session_id
) // session exists
840 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",
841 ((pids_len
> 0) ? "POST" : "GET"), context
->session_id
, demux
->onid
, demux
->tsid
,
842 demux
->program_number
, demux
->ECMpidcount
, demux
->ens
, context
->host_id
, lenhdr
);
844 else // no credentials configured, assume no session required
846 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",
847 ((pids_len
> 0) ? "POST" : "GET"), demux
->onid
, demux
->tsid
, demux
->program_number
,
848 demux
->ECMpidcount
, demux
->ens
, context
->host_id
, lenhdr
);
854 memcpy(end
, pids
, pids_len
);
855 cs_log_dbg(D_CLIENT
, "%s: new unscrambling detected, switching to post", client
->reader
->label
);
856 _set_pid_status(context
->post_contexts
, demux
->onid
, demux
->tsid
, demux
->program_number
, 0);
858 cs_log_dump_dbg(D_CLIENT
, pids
, pids_len
, "%s: sending capmt ecm pids - %s /api/p/%x/%x/%x/%x/%x",
859 client
->reader
->label
, (pids_len
> 0) ? "POST" : "GET", demux
->onid
, demux
->tsid
,
860 demux
->program_number
, demux
->ECMpidcount
, demux
->ens
);
862 ghttp_send(client
, req
, ret
+ pids_len
);
873 void module_ghttp(struct s_module
*ph
)
876 // ph->ptab.ports[0].s_port = cfg.ghttp_port;
878 ph
->type
= MOD_CONN_TCP
;
879 // ph->listenertype = LIS_GHTTP;
880 ph
->large_ecm_support
= 1;
881 ph
->recv
= ghttp_recv
;
882 ph
->c_init
= ghttp_client_init
;
883 ph
->c_recv_chk
= ghttp_recv_chk
;
884 ph
->c_send_ecm
= ghttp_send_ecm
;
885 ph
->cleanup
= ghttp_cleanup
;
887 ph
->c_capmt
= ghttp_capmt_notify
;