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
);
65 if(!SSL_set_fd(context
->ssl_handle
, fd
))
67 ERR_print_errors_fp(stderr
);
71 if(SSL_connect(context
->ssl_handle
) != 1)
73 ERR_print_errors_fp(stderr
);
77 if(context
->ssl_handle
)
79 cs_log_dbg(D_CLIENT
, "%s: ssl established", client
->reader
->label
);
87 int32_t ghttp_client_init(struct s_client
*cl
)
92 ghttp_ignored_contexts
= ll_create("ignored contexts");
94 ghttp_ssl_context
= SSL_CTX_new(SSLv23_client_method());
95 if(ghttp_ssl_context
== NULL
)
97 ERR_print_errors_fp(stderr
);
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
, ".");
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
;
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");
135 ll_clear(((s_ghttp
*)cl
->ghttp
)->ecm_q
);
138 if(cl
->reader
->ghttp_use_ssl
)
141 cs_log("%s: use_ssl set but no ssl support available, aborting...", cl
->reader
->label
);
145 if(ghttp_ssl_context
== NULL
) { return -1; }
147 if(_ssl_connect(cl
, handle
))
153 network_tcp_connection_close(cl
->reader
, "ssl failed");
162 static uint32_t javastring_hashcode(uchar
*input
, int32_t len
)
165 while(/**input &&*/ len
--)
167 h
= 31 * h
+ *input
++;
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
);
177 // disconnected? try reinit.
178 cs_log_dbg(D_CLIENT
, "%s: disconnected?", client
->reader
->label
);
179 ghttp_client_init(client
);
183 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
184 if(client
->reader
->ghttp_use_ssl
)
185 { return SSL_write(context
->ssl_handle
, buf
, l
); }
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
);
199 static int32_t ghttp_recv_int(struct s_client
*client
, uchar
*buf
, int32_t l
)
202 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
206 ll_clear(context
->ecm_q
);
210 if(client
->reader
->ghttp_use_ssl
)
213 n
= SSL_read(context
->ssl_handle
, buf
, l
);
216 else { n
= cs_recv(client
->pfd
, buf
, l
, 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);
226 cs_log_dbg(D_CLIENT
, "%s: unexpected reply size %d - %s", client
->reader
->label
, n
, buf
);
227 return -1; // assumes google error, disconnects
232 cs_log_dbg(D_CLIENT
, "%s: read %d bytes, disconnecting", client
->reader
->label
, 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
);
247 static bool _is_post_context(LLIST
*ca_contexts
, ECM_REQUEST
*er
, bool remove_data
)
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
;
258 existing
= (s_ca_context
*)ll_contains_data(ca_contexts
, ctx
, sizeof(s_ca_context
));
260 { ll_remove_data(ca_contexts
, existing
); }
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
)
283 if(cs_malloc(&ctx
, sizeof(s_ca_context
)))
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
)
300 pid
= b2i(2, buf
+ offs
);
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
);
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
];
327 char *value
= cs_strdup(data
);
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
);
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
)
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
);
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");
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
);
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
);
396 hdrstr
= _get_header_substr(buf
, "ETag: W/\"", "\"\r\n");
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=");
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
);
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
428 content
[clen
] = '\0';
429 cs_log_dbg(D_CLIENT
, "%s: http error message: %s", client
->reader
->label
, content
);
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
);
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
);
449 // on 503 cache timeout, retry with POST immediately (and switch to POST for subsequent)
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
);
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
482 // successful http reply (200 ok or 204 no content)
484 hdrstr
= _get_header(buf
, "Pragma: context-ignore=");
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
); }
499 data
= strstr((char *)buf
, "Pragma: context-ignore-clear");
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);
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
);
528 if(clen
!= 0) { cs_log_dump_dbg(D_CLIENT
, content
, clen
, "%s: recv_chk fail, clen = %d", client
->reader
->label
, clen
); }
533 static char *_ghttp_basic_auth(struct s_client
*client
)
536 char *encauth
= NULL
;
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
);
549 static int32_t _ghttp_http_get(struct s_client
*client
, uint32_t hash
, int odd
)
552 char *encauth
= NULL
;
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
);
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
);
580 static int32_t _ghttp_post_ecmdata(struct s_client
*client
, ECM_REQUEST
*er
)
584 char *encauth
= NULL
;
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
);
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
);
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
);
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
)))
630 else { NULLFREE(ignore
); }
635 static int32_t ghttp_send_ecm(struct s_client
*client
, ECM_REQUEST
*er
)
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
);
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
);
658 hash
= javastring_hashcode(er
->ecm
+ 3, er
->ecmlen
- 3);
659 _ghttp_http_get(client
, hash
, er
->ecm
[0] == 0x81);
665 static void ghttp_cleanup(struct s_client
*client
)
667 s_ghttp
*context
= (s_ghttp
*)client
->ghttp
;
669 ll_destroy_data(&ghttp_ignored_contexts
);
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
);
679 if(context
->ssl_handle
)
681 SSL_shutdown(context
->ssl_handle
);
682 SSL_free(context
->ssl_handle
);
684 SSL_CTX_free(ghttp_ssl_context
);
691 static int32_t ghttp_capmt_notify(struct s_client
*client
, struct demux_s
*demux
)
693 uchar req
[640], lenhdr
[64] = "";
696 char *encauth
= NULL
;
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));
717 snprintf((char *)lenhdr
, sizeof(lenhdr
), "\r\nContent-Length: %d", pids_len
);
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
);
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
);
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
); }
759 void module_ghttp(struct s_module
*ph
)
762 // ph->ptab.ports[0].s_port = cfg.ghttp_port;
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
;
773 ph
->c_capmt
= ghttp_capmt_notify
;