Update pop3 example [ci skip]

master
Alexey Melnichuk 2014-09-16 16:00:48 +05:00
parent 5fdc34a47d
commit f3631cd55e
1 changed files with 228 additions and 47 deletions

View File

@ -1,24 +1,35 @@
-- Simple pop3 wrapper
--
-- @usage
-- local mbox = pop3:new('pop3://pop3.yandex.ru')
-- local mbox = pop3.new('pop3s://pop3.yandex.ru')
--
-- -- Yandex works only with tls
-- print('Open: ', mbox:open_tls('***', '***'))
-- print('NOOP: ', mbox:noop())
-- print('RETR: ', mbox:retr(1))
--
-- list = mbox:list()
-- for no, size in ipairs(list)do
-- ...
-- -- use message class from lua-pop3
-- -- https://github.com/moteus/lua-pop3
-- for k, msg in mbox:messages() do
-- print"----------------------------------------------"
-- print("subject: ", msg:subject())
-- print("from: ", msg:from())
-- print("to: ", msg:to())
-- for i,v in ipairs(msg:full_content()) do
-- if v.text then print(" ", i , "TEXT: ", v.type, #v.text)
-- else print(" ", i , "FILE: ", v.type, v.file_name or v.name, #v.data) end
-- end
-- end
--
-- mbox:close()
--
local cURL = require "cURL.safe"
local cURL = require "cURL.safe"
local find_ca_bundle = require "cURL.utils".find_ca_bundle
local message do local ok
ok, message = pcall(require, "pop3.message")
if not ok then message = nil end
end
local function split(str, sep, plain)
local b, res = 1, {}
@ -35,16 +46,39 @@ local function split(str, sep, plain)
return res
end
local function pars_response(resp)
local code, info = string.match(resp,"%s*(%S*)(.*)%s*")
-- SASL GET ONLY "+"/"-"
if code == '+OK' or code == '+' then
return true, info
elseif code == '-ERR' or code == '-' then
return false, info
end
return nil, resp
end
local function split_2_numbers(data)
local n1, n2 = string.match(data, "%s*(%S+)%s+(%S+)")
return tonumber(n1), tonumber(n2)
end
local function split_1_number(data)
local n1,s= string.match(data, "%s*(%S+)%s*(%S*)")
return tonumber(n1),s
end
local crln = '\r\n'
local function writer(cb, ctx)
local tail
return function(str)
-- @check Is libcurl guarantee thant data will be arraived line by line?
-- if so we can just call `cb`
if str then
local t = split(tail and (tail .. str) or str, crln, true)
if str:sub(-2) == crln then tail = nil
else tail = table.remove(t) end
for _, s in ipairs(t) do cb(ctx, s) end
elseif tail then cb(ctx, tail) end
for _, s in ipairs(t) do cb(s, ctx) end
elseif tail then cb(tail, ctx) end
end
end
@ -59,11 +93,12 @@ end
local function open(self, user, password, ssl)
self._easy, err = cURL.easy{
url = self._url,
username = user,
password = password,
customrequest = 'NOOP',
nobody = true,
url = self._url,
username = user,
password = password,
customrequest = 'NOOP',
nobody = true,
headerfunction = function(h) self._response = h end
}
if not self._easy then return nil, err end
@ -95,56 +130,202 @@ local function open(self, user, password, ssl)
return self
end
local function exec(self)
self._response = nil
local ok, err = self._easy:perform()
if not ok then return nil, err end
assert(self._response)
self._response = self._response:gsub("%s+$", "")
return self:response()
end
local function exec_no_body(self, cmd, url)
local ok, err = self._easy:setopt{
url = self._url .. (url and ("/" .. url) or ""),
customrequest = cmd or '',
nobody = true,
}
if not ok then return nil, err end
return exec(self)
end
local function exec_body_cb(self, cb, cmd, url)
local ok, err = self._easy:setopt{
url = self._url .. (url and ("/" .. url) or ""),
customrequest = cmd or '',
nobody = false,
writefunction = writer(assert(cb))
}
if not ok then return nil, err end
ok, err = exec(self)
if not ok then return nil, err end
return true
end
local function exec_body(self, cmd, url)
local t = {}
local ok, err = exec_body_cb(self,
function(s) t[#t+1] = s end,
cmd, url
)
if not ok then return nil, err end
return t
end
local function exec_no_body_2_numbers(...)
local ok, data = exec_no_body(...)
if not ok then return ok, data end
return split_2_numbers(data)
end
function pop3:open(user, password)
return open(self, user, password, false)
end
function pop3:response()
if self._response then
return pars_response(self._response)
end
end
function pop3:verbose(...)
local ok, err
if select("#", ...) == 0 then
ok, err = self._easy:setopt_verbose(true)
else
ok, err = self._easy:setopt_verbose(not not ...)
end
if not ok then return nil, ok end
return self
end
function pop3:open_tls(user, password)
return open(self, user, password, true)
end
function pop3:noop()
local ok, err = self._easy:setopt{
url = self._url,
customrequest = 'NOOP',
nobody = true,
}
if not ok then return nil, err end
ok, err = self._easy:perform()
if not ok then return nil, err end
return self
return exec_no_body(self, 'NOOP')
end
function pop3:list()
local t = {}
local ok, err = self._easy:setopt{
url = self._url,
customrequest = '',
nobody = false,
writefunction = writer(function(t, s) t[#t+1] = s end, t)
}
if not ok then return nil, err end
ok, err = self._easy:perform()
if not ok then return nil, err end
return t
function pop3:stat()
return exec_no_body_2_numbers(self, 'STAT')
end
function pop3:retr(n)
local t = {}
local ok, err = self._easy:setopt{
url = self._url .. '/' .. n,
customrequest = '',
nobody = false,
writefunction = writer(function(t, s) t[#t+1] = s end, t)
}
if not ok then return nil, err end
function pop3:dele(id)
return exec_no_body(self, 'DELE ' .. id)
end
ok, err = self._easy:perform()
function pop3:rset()
return exec_no_body_2_numbers(self, 'RSET')
end
function pop3:list(id)
if id then
return exec_no_body_2_numbers(self, 'LIST ' .. id)
end
local t,i = {},0
local fn = function(data)
local no, size = split_2_numbers(data)
if not (no and size) then
return nil, "Wrong Response: `" .. data .. "`"
end
t[no] = size
i = i + 1
end
local ok, err = exec_body_cb(self, fn, '')
if not ok then return nil, err end
return t
if not ok then return nil, err end
return t, i
end
function pop3:uidl(id)
if id then
local ok, data = exec_no_body(self, 'UIDL ' .. id)
if not ok then return ok, data end
local no, id = split_1_number(data)
if not (no and id) then
return nil, "Wrong Response:" .. data
end
return no,id
end
local t,i = {},0
local fn = function(data)
local no, id = split_1_number(data)
if not (no and id) then
return nil, "Wrong Response:" .. data
end
t[no]=id
i = i + 1
return true
end
local ok, err = exec_body_cb(self, fn, 'UIDL')
if not ok then return nil, err end
if not ok then return nil, err end
return t, i
end
function pop3:retr(id)
assert(id)
return exec_body(self, '', tostring(id))
end
function pop3:top(id, n)
assert(id)
assert(type(n) == "number")
return exec_body(self, 'TOP ' .. id .. ' ' .. n)
end
function pop3:make_iter(fn)
local lst, err = self:list()
if not lst then error(err) end
local k = nil
local iter
iter = function ()
k = next(lst, k)
if not k then return nil end
-- skip deleted messages ?
local no, size = self:list(k)
if no == false then return iter() end -- next message
if not no then return error(size) end
assert(no == k)
local data, err = fn(self, k, size)
if not data then error(err) end
return k, data
end
return iter
end
if message then
function pop3:message(msgid)
local msg, err = self:retr(msgid)
if not msg then return nil, err end
return message(msg)
end
end
function pop3:retrs()
return self:make_iter(self.retr)
end
function pop3:tops(n)
return self:make_iter(function(self, msgid)
return self:top(msgid, n)
end)
end
function pop3:messages()
return self:make_iter(self.message)
end
function pop3:closed()