LogicMachine Forum
https digest call to philips tv (jointSPACE) - Printable Version

+- LogicMachine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: https digest call to philips tv (jointSPACE) (/showthread.php?tid=2266)



https digest call to philips tv (jointSPACE) - ralwet - 05.10.2019

Hi

I own a Philips TV wich is supporting jointSPACE Rest-Calls. And I can do rest-calls to my Philips-TV with Curl without problems:
The folowing Curl-Example is successfully returning  the "Powerstate" of my TV ("tv on" or "tv off").
  • The Call is using digest authorization
  • The Call is using https
  • Insecure server connections has to be allowed ("-k" - because the TV is using a self signed certificate)
  • Beside SSL, it connects to Port 1926 where the jointSPACE API is answering the requests.

Code:
curl -XGET -u <user>:<password> https://192.168.3.123:1926/powerstate -k --digest -v

Now I try to do this curl-call on my SpaceLynk with Lua. So far it is not working. It returns code 404.
I also tried to use the Erwin's digest-example:  https://forum.logicmachine.net/showthread.php?tid=237&highlight=mpm_digest 3
But no luck so far Sad
Can somebody help me by providing a valid lua code which is doing this curl-call?

Thanks!


RE: https digest call to philips tv (jointSPACE) - Habib - 05.10.2019

Hi,

I've no experiance with your problem, but what I've seen is, that you need an HTTPS connection and Erwin Solution use only http?
You've seen that?


RE: https digest call to philips tv (jointSPACE) - ralwet - 05.10.2019

(05.10.2019, 12:21)Habib Wrote: Hi,

I've no experiance with your problem, but what I've seen is, that you need an HTTPS connection and Erwin Solution use only http?
You've seen that?

Hi Habib

Oh, yes. I forget to mention, that i adaptet Erwin's Solution to https (line 366). But this is not working Sad
Code:
local s_http = require 'ssl.https' --local s_http = require "socket.http"



RE: https digest call to philips tv (jointSPACE) - admin - 08.10.2019

Try digest code from this post:
https://forum.logicmachine.net/showthread.php?tid=1061&pid=9364#pid9364

Replace
Code:
local skthttp = require('socket.http')
with
Code:
local skthttp = require('ssl.https')



RE: https digest call to philips tv (jointSPACE) - ralwet - 08.10.2019

Thanks for you help! Unfortunately using this script I get again a 404.

Code:
local skthttp = require('ssl.https') --local skthttp = require('socket.http') local skturl = require('socket.url') local ltn12 = require('ltn12') local md5sum = require('encdec').md5 local hash = function(...)   return md5sum(table.concat({...}, ':')) end local parse_header = function(header)   local result = {}   for key, value in (header .. ','):gmatch('(%w+)=(.-),') do     if value:sub(1, 1) == '"' then -- strip quotes       result[ key:lower() ] = value:sub(2, -2)     else       result[ key:lower() ] = value     end   end   return result end local make_digest_header = function(headers)   local digest = {}   for _, header in ipairs(headers) do     if not header.unquote then       header[ 2 ] = '"' .. header[ 2 ] .. '"'     end     digest[ #digest + 1 ] = header[ 1 ] .. '=' .. header[ 2 ]   end   return 'Digest ' .. table.concat(digest, ', ') end local _request = function(req)   if not req.url then     return nil, 'missing url'   end   local url = skturl.parse(req.url)   local user, password = url.user, url.password   if not user or not password then     return nil, 'missing credentials in url'   end   url.user, url.password, url.authority, url.userinfo = nil, nil, nil, nil   req.url = skturl.build(url)   local source   if req.source then     local chunks = {}     local capture = function(chunk)       if chunk then         chunks[ #chunks + 1 ] = chunk       end       return chunk     end     local chunk_id = 0     source = function()       chunk_id = chunk_id + 1       return chunks[ chunk_id ]     end     req.source = ltn12.source.chain(req.source, capture)   end   local body, code, hdrs = skthttp.request(req)   if code == 401 and hdrs['www-authenticate'] then     local ht = parse_header(hdrs['www-authenticate'])     if not ht.realm or not ht.nonce then       return nil, 'missing realm/nonce from response'     end     if ht.qop ~= 'auth' then       return nil, 'unsupported qop ' .. tostring(ht.qop)     end     if ht.algorithm and ht.algorithm:lower() ~= 'md5' then       return nil, 'unsupported algo ' .. tostring(ht.algorithm)     end     local nc = '00000001'     local cnonce = string.format('%08x', os.time())     local uri = skturl.build({ path = url.path, query = url.query })     local method = req.method or 'GET'     local response = hash(       hash(user, ht.realm, password),       ht.nonce,       nc,       cnonce,       'auth',       hash(method, uri)     )     req.headers = req.headers or {}     local auth = {       { 'username', user },       { 'realm', ht.realm },       { 'nonce', ht.nonce },       { 'uri', uri },       { 'cnonce', cnonce },       { 'nc', nc, unquote = true },       { 'qop', 'auth' },       { 'algorithm', 'MD5' },       { 'response', response },     }     if ht.opaque then       table.insert(auth, { 'opaque', ht.opaque })     end     req.headers.authorization = make_digest_header(auth)     if not req.headers.cookie and hdrs['set-cookie'] then       -- not really correct but enough for httpbin       local cookie = (hdrs['set-cookie'] .. ';'):match('(.-=.-)[;,]')       if cookie then         req.headers.cookie = '$Version: 0; ' .. cookie .. ';'       end     end     if req.source then       req.source = source     end     body, code, hdrs = skthttp.request(req)   end   return body, code, hdrs end local request = function(url)   local t = type(url)   if t == 'table' then     return _request(table.clone(url))   elseif t == 'string' then     local req = {}     local _, code, headers = _request({ url = url, sink = ltn12.sink.table(req) })     return table.concat(req), code, headers   end end url = 'https://<USER>:<PASSWORD>@192.168.3.123:1926/powerstate' image, err, hdrs = request(url) log (image) log (err) log (hdrs)

The url works in a Web-Browser. But not in this Script Sad

This Curl-Call does the Job too (-k Param is necessery. This allows self signed certificate):
Code:
curl -XGET -u <user>:<password> https://192.168.3.123:1926/powerstate -k --digest -v

Any further Ideas?


RE: https digest call to philips tv (jointSPACE) - admin - 08.10.2019

Can you run curl in verbose mode (-v) and post the output?


RE: https digest call to philips tv (jointSPACE) - ralwet - 08.10.2019

Curl-Request with verbose:
Code:
curl -XGET -u <user>:<password> https://192.168.3.151:1926/powerstate -k --digest -v

Response:
Code:
Note: Unnecessary use of -X or --request, GET is already inferred. *   Trying 192.168.3.151... * TCP_NODELAY set * Connected to 192.168.3.151 (192.168.3.151) port 1926 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: *   CAfile: /etc/ssl/certs/ca-certificates.crt   CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Client hello (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305 * ALPN, server did not agree to a protocol * Server certificate: *  subject: C=IN; ST=Karnataka; L=Bengaluru; O=TP VISION India Pvt. Ltd.; OU=Smart TV; CN=restfultv.tpvision.com *  start date: May 19 10:51:53 2015 GMT *  expire date: Oct  4 10:51:53 2042 GMT *  issuer: C=IN; ST=Karnataka; L=Bengaluru; O=TP VISION India Pvt. Ltd.; OU=Smart TV; CN=ca.tpvision.com *  SSL certificate verify result: self signed certificate in certificate chain (19), continuing anyway. * Server auth using Digest with user '<user>' > GET /powerstate HTTP/1.1 > Host: 192.168.3.151:1926 > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 401 Unauthorized < Date: Tue, 08 Oct 2019 16:11:17 GMT+00:00 < Accept-Ranges: bytes < Server: Restlet-Framework/2.3.12 < WWW-Authenticate: Digest realm="XTV", domain="/", nonce="MTU3MDU1MTA3NzcyNTplZGFiYzY4NWNmMDAzNTM2YjRiMzJjYmQxMGM1YzY0OA==", algorithm=MD5, qop="auth" < Content-Length: 424 < Content-Type: text/html; charset=UTF-8 < * Ignoring the response-body * Connection #0 to host 192.168.3.151 left intact * Issue another request to this URL: 'https://192.168.3.151:1926/powerstate' * Found bundle for host 192.168.3.151: 0x55f0b0f70a50 [can pipeline] * Re-using existing connection! (#0) with host 192.168.3.151 * Connected to 192.168.3.151 (192.168.3.151) port 1926 (#0) * Server auth using Digest with user '<user>' > GET /powerstate HTTP/1.1 > Host: 192.168.3.151:1926 > Authorization: Digest username="<user>", realm="XTV", nonce="MTU3MDU1MTA3NzcyNTplZGFiYzY4NWNmMDAzNTM2YjRiMzJjYmQxMGM1YzY0OA==", uri="/powerstate", cnonce="NzVmNDdjYzYwMDBiYjU0Y2FmMjFjNmM5ZjA4MWE1NDE=", nc=00000001, qop=auth, response="ad84deaff1b8f9cf07c651a689543991", algorithm="MD5" > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 08 Oct 2019 16:11:17 GMT+00:00 < Accept-Ranges: bytes < Server: Restlet-Framework/2.3.12 < Access-Control-Allow-Origin: * < Content-Length: 19 < Content-Type: application/json; charset=UTF-8 < * Connection #0 to host 192.168.3.151 left intact {"powerstate":"On"}



RE: https digest call to philips tv (jointSPACE) - admin - 09.10.2019

Looks correct. Do you get error 401 with your script if you specify incorrect password? Can you provide remote access to your device?


RE: https digest call to philips tv (jointSPACE) - ralwet - 09.10.2019

I get e 404

Script:
Code:
local skthttp = require('ssl.https') local skturl = require('socket.url') local ltn12 = require('ltn12') local md5sum = require('encdec').md5 local hash = function(...)   return md5sum(table.concat({...}, ':')) end local parse_header = function(header)   local result = {}   for key, value in (header .. ','):gmatch('(%w+)=(.-),') do     if value:sub(1, 1) == '"' then -- strip quotes       result[ key:lower() ] = value:sub(2, -2)     else       result[ key:lower() ] = value     end   end   return result end local make_digest_header = function(headers)   local digest = {}   for _, header in ipairs(headers) do     if not header.unquote then       header[ 2 ] = '"' .. header[ 2 ] .. '"'     end     digest[ #digest + 1 ] = header[ 1 ] .. '=' .. header[ 2 ]   end   return 'Digest ' .. table.concat(digest, ', ') end local _request = function(req)   if not req.url then     return nil, 'missing url'   end   local url = skturl.parse(req.url)   local user, password = url.user, url.password   if not user or not password then     return nil, 'missing credentials in url'   end   url.user, url.password, url.authority, url.userinfo = nil, nil, nil, nil   req.url = skturl.build(url)   local source   if req.source then     local chunks = {}     local capture = function(chunk)       if chunk then         chunks[ #chunks + 1 ] = chunk       end       return chunk     end     local chunk_id = 0     source = function()       chunk_id = chunk_id + 1       return chunks[ chunk_id ]     end     req.source = ltn12.source.chain(req.source, capture)   end   local body, code, hdrs = skthttp.request(req)   if code == 401 and hdrs['www-authenticate'] then     local ht = parse_header(hdrs['www-authenticate'])     if not ht.realm or not ht.nonce then       return nil, 'missing realm/nonce from response'     end     if ht.qop ~= 'auth' then       return nil, 'unsupported qop ' .. tostring(ht.qop)     end     if ht.algorithm and ht.algorithm:lower() ~= 'md5' then       return nil, 'unsupported algo ' .. tostring(ht.algorithm)     end     local nc = '00000001'     local cnonce = string.format('%08x', os.time())     local uri = skturl.build({ path = url.path, query = url.query })     local method = req.method or 'GET'     local response = hash(       hash(user, ht.realm, password),       ht.nonce,       nc,       cnonce,       'auth',       hash(method, uri)     )     req.headers = req.headers or {}     local auth = {       { 'username', user },       { 'realm', ht.realm },       { 'nonce', ht.nonce },       { 'uri', uri },       { 'cnonce', cnonce },       { 'nc', nc, unquote = true },       { 'qop', 'auth' },       { 'algorithm', 'MD5' },       { 'response', response },     }     if ht.opaque then       table.insert(auth, { 'opaque', ht.opaque })     end     req.headers.authorization = make_digest_header(auth)     if not req.headers.cookie and hdrs['set-cookie'] then       -- not really correct but enough for httpbin       local cookie = (hdrs['set-cookie'] .. ';'):match('(.-=.-)[;,]')       if cookie then         req.headers.cookie = '$Version: 0; ' .. cookie .. ';'       end     end     if req.source then       req.source = source     end     body, code, hdrs = skthttp.request(req)   end   return body, code, hdrs end local request = function(url)   local t = type(url)   if t == 'table' then     return _request(table.clone(url))   elseif t == 'string' then     local req = {}     local _, code, headers = _request({ url = url, sink = ltn12.sink.table(req) })     return table.concat(req), code, headers   end end url = 'https://<user>:<password>@192.168.3.151:1926/powerstate' image, err, hdrs = request(url) log (image) log (err) log (hdrs)



SpaceLynk Log:
Code:
GetStatePhilipsTV 09.10.2019 11:12:37 * string: <html> <head>    <title>Status page</title> </head> <body style="font-family: sans-serif;"> <p style="font-size: 1.2em;font-weight: bold;margin: 1em 0px;">Not Found</p> <p>The server has not found anything matching the request URI</p> <p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">here</a>.<br> Please continue your visit at our <a href="/">home page</a>. </p> </body> </html> GetStatePhilipsTV 09.10.2019 11:12:37 * number: 404 GetStatePhilipsTV 09.10.2019 11:12:37 * table: ["date"]   * string: Wed, 09 Oct 2019 09:12:37 GMT+00:00 ["content-length"]   * string: 439 ["content-type"]   * string: text/html; charset=UTF-8 ["server"]   * string: Restlet-Framework/2.3.12

Thank you very much for your Support Smile But a Remote-Session is not necessary. If we don't find a quick solution on the SpaceLynk I will go for a workaraound (using a Linux-Device for calling the TV).


RE: https digest call to philips tv (jointSPACE) - admin - 09.10.2019

This issue might appear due to missing port part in HTTP host header.

Try replacing line 125:
Code:
local _, code, headers = _request({ url = url, sink = ltn12.sink.table(req), headers = { host = '192.168.3.151:1926' } })



RE: https digest call to philips tv (jointSPACE) - ralwet - 09.10.2019

Yes, now it works! Thank you very much!! Smile