Logic Machine Forum
email with attachement gives image size 0 - Printable Version

+- Logic Machine 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: email with attachement gives image size 0 (/showthread.php?tid=1061)

Pages: 1 2


email with attachement gives image size 0 - gjniewenhuijse - 26.10.2017

Hello,

I have a script that sends an email with attached foscam camera snaphots when my doorbell is pushed.

That works great for months, but after some time it crashed my lm4 as following:
- when my doorbell is pushed i receive an email with size 0 images. every doorbell push i receive that mail with size 0 image
- so i try to find the problem and test my code manual with a resident script (sleepinterval 60), the same code as my event-based script

Code:
require('user.nit_email')
mail({'test@test.nl'}, 'Test', 'Test message.', 'std_camera_2')
-I receive every 60 seconds an email, thats correct. But when i stop/deactivate this script i also receive every 60 seconds the email, why?
-So i need to restart my lm4 to solve this issue.

Nit_email

Code:
local from_name = 'HomeLynk'

local settings = {
   -- "from" field, only e-mail must be specified here
   from = 'test@test.nl',
   -- smtp username
   --user = '',
   -- smtp password
   --password = '',
   -- smtp server
   server = '1.2.3.4',
   -- smtp server port
   port = 25,
   -- enable ssl, required for gmail smtp
   --secure = 'sslv23',
 }

local escape = function(v)
 return '<' .. tostring(v) .. '>'
end

-- send an e-mail,
function mail(to, subject, message, att)
 -- make sure these settings are correct
 local smtp = require('socket.smtp')
 if att then
   local mime = require('mime')
        local ltn12 = require('ltn12')
   -- generate attachements
   bodyAtt = handleAtt(att, 'start')
 else
   bodyAtt = {"",""}
 end
 
 if type(to) ~= 'table' then
   to = { to }
 end
 
 for index, email in ipairs(to) do
   to[ index ] = escape(email)
 end
    
 settings.source = smtp.message({
   headers = {
     from = from_name..escape(settings.from),
     to = table.concat(to, ', '),
     subject = subject,
   },
   body = {
       --preamble = "Hello Recipient, \r\n" .. "This is your e-mail with a picture attached.",
       {
         header = {
           ['Content-type'] = 'text/html; charset=utf-8',
         },
         body = mime.eol(0, message),
       },
       bodyAtt[1],bodyAtt[2], -- get attachements if exists (max 2 for now)  <---todo:make it dynamic
       --epilogue = "That's it folks! :)"
   }
 })
 bodyAtt = nil
 
 -- fixup from field
 settings.from = escape(settings.from)
 settings.rcpt = to

 smtp.send(settings)
 if att then
   handleAtt(att, 'end')
 end
end

-- std_camera: snapshot camera voordeur
-- std_camera_b: snapshot camera voordeur en achterdeur
function handleAtt(att, handle)
 if handle == "start" then
   if att == "std_camera" then
     require('user.nit_camera')
     dst = '/tmp/capture.jpg'
     local url = string.format(url_cmdFoscam, cam_voordeur, cam_port, 'snapPicture2', '', cam_usr2, cam_pwd2)
            local cmd = string.format('wget -O %q %q', dst, url)
            os.execute(cmd)
     local attachment = ltn12.source.chain( ltn12.source.file(io.open(dst, 'r')), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
     return
     {
       {
         headers = {
           ['Content-type'] = 'image/jpeg; name="image.jpg"',
           ['Content-disposition'] = 'attachment; filename="image.jpg"',
           ['Content-transfer-encoding'] = 'BASE64',
         },
         body = attachment
       }
            }
   
   elseif att == "std_camera_b" then
     require('user.nit_camera')
     dst1 = '/tmp/capture1.jpg'
     dst2 = '/tmp/capture2.jpg'
     local url1 = string.format(url_cmdFoscam, cam_voordeur, cam_port, 'snapPicture2', '', cam_usr2, cam_pwd2)
     local url2 = string.format(url_cmdFoscam, cam_achterdeur, cam_port, 'snapPicture2', '', cam_usr2, cam_pwd2)
            local cmd1 = string.format('wget -O %q %q', dst1, url1)
            local cmd2 = string.format('wget -O %q %q', dst2, url2)
     os.execute(cmd1)
            os.execute(cmd2)
     local attachment1 = ltn12.source.chain( ltn12.source.file(io.open(dst1, 'r')), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
     local attachment2 = ltn12.source.chain( ltn12.source.file(io.open(dst2, 'r')), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
     return
     {
       {
         headers = {
           ['Content-type'] = 'image/jpeg; name="image1.jpg"',
           ['Content-disposition'] = 'attachment; filename="image1.jpg"',
           ['Content-transfer-encoding'] = 'BASE64',
         },
         body = attachment1
       },
       {
         headers = {
           ['Content-type'] = 'image/jpeg; name="image2.jpg"',
           ['Content-disposition'] = 'attachment; filename="image2.jpg"',
           ['Content-transfer-encoding'] = 'BASE64',
         },
         body = attachment2
       }
     }
   
   elseif att == "std_camera_2" then
     require('user.nit_camera')
     dst1 = '/tmp/capture1.jpg'
     dst2 = '/tmp/capture2.jpg'
     local url1 = string.format(url_cmdFoscam, cam_voordeur, cam_port, 'snapPicture2', '', cam_usr2, cam_pwd2)
            local cmd1 = string.format('wget -O %q %q', dst1, url1)
            local cmd2 = string.format('wget -O %q %q', dst2, url1)
     os.execute(cmd1) -- direct snapshot
     cmdFoscam(cam_voordeur, cam_port, 'ptzGotoPresetPoint', '&name=Deur') -- move to frontdoor
            os.sleep(20) -- wait for 20 seconds
     os.execute(cmd2) -- new snapshot
     local attachment1 = ltn12.source.chain( ltn12.source.file(io.open(dst1, 'r')), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
     local attachment2 = ltn12.source.chain( ltn12.source.file(io.open(dst2, 'r')), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
     return
     {
       {
         headers = {
           ['Content-type'] = 'image/jpeg; name="image1.jpg"',
           ['Content-disposition'] = 'attachment; filename="image1.jpg"',
           ['Content-transfer-encoding'] = 'BASE64',
         },
         body = attachment1
       },
       {
         headers = {
           ['Content-type'] = 'image/jpeg; name="image2.jpg"',
           ['Content-disposition'] = 'attachment; filename="image2.jpg"',
           ['Content-transfer-encoding'] = 'BASE64',
         },
         body = attachment2
       }
     }
   end
 
 elseif handle == "end" then
   if att == "std_camera" then
     os.remove(dst)
   elseif att == "std_camera_b" then
     os.remove(dst1)
     os.remove(dst2)
   elseif att == "std_camera_2" then
     os.remove(dst1)
     os.remove(dst2)
   end
 end
 
end

Nit_camera
Code:
-- snapshot urls
cam_voordeur = '2.2.2.2'
cam_achterdeur = '2.2.2.3'
cam_port = 80
cam_usr = 'user'
cam_pwd = 'password'
cam_usr2 = 'user2'
cam_pwd2 = 'password2'

-- start command urls
url_cmdFoscam = 'http://%s:%s/cgi-bin/CGIProxy.fcgi?cmd=%s%s&usr=%s&pwd=%s'

-- Foscam camera command
function cmdFoscam(iCamera, iPort, iCmd, iParam)
 local url, response
 url = string.format(url_cmdFoscam, iCamera, iPort, iCmd, iParam, cam_usr, cam_pwd)
      
 if (url) then
     require('socket.http')
   socket.http.TIMEOUT = 5
   response = socket.http.request( url_encode(url) )
 end
 return response
end



RE: email with attachement gives image size 0 - admin - 26.10.2017

You should use LuaSocket to get a snapshot instead of wget. wget will fail if output file already exist so this might cause a problem with 0 sized attachments. There's no need to save snapshot to /tmp, you can use ltn12.source.string.


RE: email with attachement gives image size 0 - gjniewenhuijse - 26.10.2017

(26.10.2017, 09:06)admin Wrote: You should use LuaSocket to get a snapshot instead of wget. wget will fail if output file already exist so this might cause a problem with 0 sized attachments. There's no need to save snapshot to /tmp, you can use ltn12.source.string.

can you please give a little example?


RE: email with attachement gives image size 0 - admin - 26.10.2017

You already have a function for HTTP requests, just get your snapshot by calling cmdFoscam and then pass the result like this:
Code:
local snapshot = cmdFoscam(...)
local attachment = ltn12.source.chain( ltn12.source.string(snapshot), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )



RE: email with attachement gives image size 0 - DGrandes - 11.07.2018

Hello,

I've tried to use this script but I can´t get it to work.

******************************************************************************
function handleAtt(att)
if att == "snapshoot" then
local snapshoot = 'http://IPCAMERA/cgi-bin/snapshot.cgi?'
local attachment = ltn12.source.chain( ltn12.source.string(snapshot), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
return
{
{
headers = {
['Content-type'] = 'image/jpeg; name="image.jpg"',
['Content-disposition'] = 'attachment; filename="image.jpg"',
['Content-transfer-encoding'] = 'BASE64',
},
body = attachment
}
}
end
end

The mail function send me a emai with jpg attachment but i can´t see anything.
When I use the link 'http://IPCAMERA/cgi-bin/snapshot.cgi?' i have to log in. How can I automatize with code?

Thanks


RE: email with attachement gives image size 0 - Daniel - 11.07.2018

Try like that

'http://user:password@IPCAMERA/cgi-bin/snapshot.cgi?'


RE: email with attachement gives image size 0 - DGrandes - 11.07.2018

(11.07.2018, 11:02)Daniel. Wrote: Try like that

'http://user:password@IPCAMERA/cgi-bin/snapshot.cgi?'

It doesn´t work.
I´ve tried diferent options and every time ask me user and pass.

https://www.ispyconnect.com/man.aspx?n=Dahua


RE: email with attachement gives image size 0 - buuuudzik - 11.07.2018

(11.07.2018, 14:12)DGrandes Wrote:
(11.07.2018, 11:02)Daniel. Wrote: Try like that

'http://user:password@IPCAMERA/cgi-bin/snapshot.cgi?'

It doesn´t work.
I´ve tried diferent options and every time ask me user and pass.

https://www.ispyconnect.com/man.aspx?n=Dahua

You can try like here:
https://forum.logicmachine.net/showthread.php?tid=1073&pid=6338#pid6338

Maybe your camera support such params:
http://IP_address/cgi-bin/snapshot.cgi?chn=1&u=admin&p=password


RE: email with attachement gives image size 0 - DGrandes - 11.07.2018

(11.07.2018, 14:20)buuuudzik Wrote:
(11.07.2018, 14:12)DGrandes Wrote:
(11.07.2018, 11:02)Daniel. Wrote: Try like that

'http://user:password@IPCAMERA/cgi-bin/snapshot.cgi?'

It doesn´t work.
I´ve tried diferent options and every time ask me user and pass.

https://www.ispyconnect.com/man.aspx?n=Dahua

You can try like here:
https://forum.logicmachine.net/showthread.php?tid=1073&pid=6338#pid6338

Maybe your camera support such params:
http://IP_address/cgi-bin/snapshot.cgi?chn=1&u=admin&p=password

Don´t work..

My camera is IPC-HDBW1320E of Dahua and I try with both url of 'https://www.ispyconnect.com/man.aspx?n=Dahua':

It do something strange.. the first time I´ve tried the url it work and don´t ask me the user and pass, but once i use this url it doesn´t work..

I´ve tried with:

http://user:pass@CAMERAIP/cgi-bin/snapshot.cgi?1

http://admin:user@CAMERAIP/cgi-bin/snapshot.cgi?loginuse=user&loginpas=pass
http://admin:user@CAMERAIP/cgi-bin/snapshot.cgi?user=user&pass=pass
http://admin:user@CAMERAIP/cgi-bin/snapshot.cgi?user=user&pwd=pass
http://admin:user@CAMERAIP/cgi-bin/snapshot.cgi?u=user&p=pass


RE: email with attachement gives image size 0 - mlaudren - 11.07.2018

Can you try to add a fake parameter in the url like timestamp.
http://user:pass@CAMERAIP/cgi-bin/snapshot.cgi?1&ts=randomnumber

If it worked 1 time, and then fail, maybe there is a caching issue. By adding a parameter that change in the url, it is consider as a new request and will not look in the cache if any.


RE: email with attachement gives image size 0 - DGrandes - 12.07.2018

(11.07.2018, 18:21)mlaudren Wrote: Can you try to add a fake parameter in the url like timestamp.
http://user:pass@CAMERAIP/cgi-bin/snapshot.cgi?1&ts=randomnumber

If it worked 1 time, and then fail, maybe there is a caching issue. By adding a parameter that change in the url, it is consider as a new request and will not look in the cache if any.

Thanks!! this random number works perfectly. But with the script don´t attach the image correctly.. can you help me with the script?

Event-Based Script:

mail('MyEmail', 'ALERTA', '¡¡¡Alguien en casa!!!', 'captura') 


Comon Functions:

function mail (to, subject, message, att)
  -- make sure these settings are correct
  local settings = {
    -- "from" field, only e-mail must be specified here
    from = '......',
    -- smtp username
    user = '.....',
    -- smtp password
    password = '......',
    -- smtp server
    server = 'smtp.gmail.com',
    -- smtp server port
    port = 465,
    -- enable tls, required for gmail smtp
    secure = 'tlsv1_2',
  }
  -- make sure these settings are correct
  local smtp = require('socket.smtp')
  
  local escape = function(v)
  return '<' .. tostring(v) .. '>'
end

  local from_name = '.......'
  if att then
    local mime = require('mime')
        local ltn12 = require('ltn12')
    -- generate attachements
    bodyAtt = handleAtt(att) --bodyAtt = handleAtt(att, 'start')
  else
    bodyAtt = {"",""}
  end
  
  if type(to) ~= 'table' then
    to = { to }
  end
  
  for index, email in ipairs(to) do
    to[ index ] = escape(email)
  end
    
  settings.source = smtp.message({
    headers = {
      from = from_name..escape(settings.from),
      to = table.concat(to, ', '),
      subject = subject,
    },
    body = {
        --preamble = "Hello Recipient, \r\n" .. "This is your e-mail with a picture attached.",
        {
          header = {
            ['Content-type'] = 'text/html; charset=utf-8',
          },
          body = mime.eol(0, message),
        },
        bodyAtt[1],bodyAtt[2], -- get attachements if exists (max 2 for now)  <---todo:make it dynamic
        --epilogue = "That's it folks! Smile"
    }
  })
  bodyAtt = nil
   
  -- fixup from field
  settings.from = escape(settings.from)
  settings.rcpt = to

  smtp.send(settings)
end


function handleAtt(att)
    if att == "captura" then
    local snapshot = 'CAMERAIP'--..math.random(0, 1000000000000)
    
    log (snapshot)
    
    local attachment = ltn12.source.chain( ltn12.source.string(snapshot), ltn12.filter.chain(mime.encode('base64'), mime.wrap()) )
    return 
      {
        {
          headers = {
            ['Content-type'] = 'image/jpeg; name="Captura.jpg"',
            ['Content-disposition'] = 'attachment; filename="Captura.jpg"',
            ['Content-transfer-encoding'] = 'BASE64',
          },
          body = attachment
        }
            }
  
end
end
  

Thanks!!!


RE: email with attachement gives image size 0 - DGrandes - 16.08.2018

Hi,

I still don't know how to make a snapshoot from my ip camera and save or send it.

I have created a DNS to share the url in case you want to test it and help me. The camera is covered by privacy reasons...

IP : http://dnspruebacamara.myqnapcloud.com:5858/
user: admin
pass: 123456789

I have test it from web explorers (chrome or firefox) and it works perfectly from Them:

http://admin:123456789@dnspruebacamara.myqnapcloud.com:5858/cgi-bin/snapshot.cgi?1&ts=4561248/snapshot.jpeg

where ts=... is a random number that i must change by code, for the navigator not to ask me again the security credencials

.... But I can not get it work from the following code:

image_type = 'jpeg'
image_name_attachment = 'snapshot'
source_image_url = 'http://admin:123456789@dnspruebacamara.myqnapcloud.com:5858/cgi-bin/snapshot.cgi?1&ts=4561248/snapshot.jpeg'

where ts=... is a random number that i must change by code, for the navigator not to ask me again the security credencials

--Get remote (from HTTP) image and put image file to HL (will be deleted when end of script)
os.execute('wget -O /www/scada/vis/' .. image_name_attachment .. '.' .. image_type .. ' ' .. source_image_url .. '')

or I have also tried this other code, and it did not work either:

image = socket.http.request(source_image_url)

I know that I am doing something wrong, but I dont know what I am missing.

Can anyone help me please???

Best regards


RE: email with attachement gives image size 0 - admin - 16.08.2018

Your camera uses digest auth mechanism which is not supported by socket.http library.
Use this code to get your snapshot:

Code:
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
  local sink = req.sink

  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
  req.sink = nil
  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
    local qop = ht.qop
    if qop and qop ~= 'auth' then
      return nil, 'unsupported qop ' .. tostring(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
    req.sink = sink
    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 = 'http://admin:123456789@dnspruebacamara.myqnapcloud.com:5858/cgi-bin/snapshot.cgi?1&ts=123/snapshot.jpeg'
image, err, hdrs = request(url)



RE: email with attachement gives image size 0 - DGrandes - 17.08.2018

Thanks you!

It works perfectly

Best Regards


RE: email with attachement gives image size 0 - Igori - 25.02.2019

Hello!
I tried to adapt the script from post 13 to my task. But as a result, I get a file with an image, before which there is a piece of HTML code. In the logs we can see:
Code:
stest 25.02.2019 13:44:51
* string: <!DOCTYPE html>
<html><head><title>Document Error: Unauthorized</title></head>
<body><h2>Access Error: 401 -- Unauthorized</h2>
<p>Authentication Error</p>
</body>
</html>
����

As we can see, the authorization on the camera does not pass, but I still get the image. In order to view it, I only need to remove the html header.
So, I have to edit the image before sending it by mail. The solution is simple, but I think it is not quite right Wink

Code:
a1, a2 = string.find(image,"</html>\r\n")
image = string.sub(image, a2+1)

How to solve the problem correctly?
Thanks!


RE: email with attachement gives image size 0 - admin - 25.02.2019

Your solution is correct, there's nothing else that can be done since the camera response is clearly wrong Smile


RE: email with attachement gives image size 0 - Igori - 25.02.2019

OK. Thank you!


RE: email with attachement gives image size 0 - hollywoodperth - 14.08.2019

(16.08.2018, 12:50)I tried to use this code, hopefully I put it in right. Didn\t flag any errors in the log, so that's hopeful.I can't find the url of where the snapshot is sent to... any help??admin Wrote: Your camera uses digest auth mechanism which is not supported by socket.http library.
Use this code to get your snapshot:

Code:
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 = 'http://admin:123456789@dnspruebacamara.myqnapcloud.com:5858/cgi-bin/snapshot.cgi?1&ts=123/snapshot.jpeg'
image, err, hdrs = request(url)



RE: email with attachement gives image size 0 - admin - 15.08.2019

Log what output the server produces. Add this to the end of the script:
Code:
log(image, err, hdrs)



RE: email with attachement gives image size 0 - puntukas - 02.10.2019

In my case I have a file with a silly header:

<?xml version="1.0" encoding="UTF-8" ?>
<userCheck>
<statusValue>401</statusValue>
<statusString>Unauthorized</statusString>
</userCheck>

so I need to add:
a1, a2 = string.find(image,"</userCheck>\n")
image = string.sub(image, a2+1)

to remove them.

Thanks.