Skip to content

Commit 22d0038

Browse files
committed
Merge pull request #36 from moteus/master
Update pop3 example [ci skip]
2 parents 82e99b7 + f3631cd commit 22d0038

1 file changed

Lines changed: 223 additions & 42 deletions

File tree

examples/cURLv3/pop3.lua

Lines changed: 223 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
-- Simple pop3 wrapper
22
--
33
-- @usage
4-
-- local mbox = pop3:new('pop3://pop3.yandex.ru')
4+
-- local mbox = pop3.new('pop3s://pop3.yandex.ru')
55
--
66
-- -- Yandex works only with tls
77
-- print('Open: ', mbox:open_tls('***', '***'))
88
-- print('NOOP: ', mbox:noop())
99
-- print('RETR: ', mbox:retr(1))
1010
--
11-
-- list = mbox:list()
12-
-- for no, size in ipairs(list)do
13-
-- ...
11+
-- -- use message class from lua-pop3
12+
-- -- https://github.com/moteus/lua-pop3
13+
-- for k, msg in mbox:messages() do
14+
-- print"----------------------------------------------"
15+
-- print("subject: ", msg:subject())
16+
-- print("from: ", msg:from())
17+
-- print("to: ", msg:to())
18+
-- for i,v in ipairs(msg:full_content()) do
19+
-- if v.text then print(" ", i , "TEXT: ", v.type, #v.text)
20+
-- else print(" ", i , "FILE: ", v.type, v.file_name or v.name, #v.data) end
21+
-- end
1422
-- end
1523
--
1624
-- mbox:close()
1725
--
1826

19-
local cURL = require "cURL.safe"
20-
27+
local cURL = require "cURL.safe"
2128
local find_ca_bundle = require "cURL.utils".find_ca_bundle
29+
local message do local ok
30+
ok, message = pcall(require, "pop3.message")
31+
if not ok then message = nil end
32+
end
2233

2334
local function split(str, sep, plain)
2435
local b, res = 1, {}
@@ -35,16 +46,39 @@ local function split(str, sep, plain)
3546
return res
3647
end
3748

49+
local function pars_response(resp)
50+
local code, info = string.match(resp,"%s*(%S*)(.*)%s*")
51+
-- SASL GET ONLY "+"/"-"
52+
if code == '+OK' or code == '+' then
53+
return true, info
54+
elseif code == '-ERR' or code == '-' then
55+
return false, info
56+
end
57+
return nil, resp
58+
end
59+
60+
local function split_2_numbers(data)
61+
local n1, n2 = string.match(data, "%s*(%S+)%s+(%S+)")
62+
return tonumber(n1), tonumber(n2)
63+
end
64+
65+
local function split_1_number(data)
66+
local n1,s= string.match(data, "%s*(%S+)%s*(%S*)")
67+
return tonumber(n1),s
68+
end
69+
3870
local crln = '\r\n'
3971
local function writer(cb, ctx)
4072
local tail
4173
return function(str)
74+
-- @check Is libcurl guarantee thant data will be arraived line by line?
75+
-- if so we can just call `cb`
4276
if str then
4377
local t = split(tail and (tail .. str) or str, crln, true)
4478
if str:sub(-2) == crln then tail = nil
4579
else tail = table.remove(t) end
46-
for _, s in ipairs(t) do cb(ctx, s) end
47-
elseif tail then cb(ctx, tail) end
80+
for _, s in ipairs(t) do cb(s, ctx) end
81+
elseif tail then cb(tail, ctx) end
4882
end
4983
end
5084

@@ -59,11 +93,12 @@ end
5993

6094
local function open(self, user, password, ssl)
6195
self._easy, err = cURL.easy{
62-
url = self._url,
63-
username = user,
64-
password = password,
65-
customrequest = 'NOOP',
66-
nobody = true,
96+
url = self._url,
97+
username = user,
98+
password = password,
99+
customrequest = 'NOOP',
100+
nobody = true,
101+
headerfunction = function(h) self._response = h end
67102
}
68103
if not self._easy then return nil, err end
69104

@@ -95,56 +130,202 @@ local function open(self, user, password, ssl)
95130
return self
96131
end
97132

98-
function pop3:open(user, password)
99-
return open(self, user, password, false)
100-
end
101-
102-
function pop3:open_tls(user, password)
103-
return open(self, user, password, true)
133+
local function exec(self)
134+
self._response = nil
135+
local ok, err = self._easy:perform()
136+
if not ok then return nil, err end
137+
assert(self._response)
138+
self._response = self._response:gsub("%s+$", "")
139+
return self:response()
104140
end
105141

106-
function pop3:noop()
142+
local function exec_no_body(self, cmd, url)
107143
local ok, err = self._easy:setopt{
108-
url = self._url,
109-
customrequest = 'NOOP',
144+
url = self._url .. (url and ("/" .. url) or ""),
145+
customrequest = cmd or '',
110146
nobody = true,
111147
}
112148
if not ok then return nil, err end
113-
ok, err = self._easy:perform()
114-
if not ok then return nil, err end
115-
return self
149+
return exec(self)
116150
end
117151

118-
function pop3:list()
119-
local t = {}
152+
local function exec_body_cb(self, cb, cmd, url)
120153
local ok, err = self._easy:setopt{
121-
url = self._url,
122-
customrequest = '',
154+
url = self._url .. (url and ("/" .. url) or ""),
155+
customrequest = cmd or '',
123156
nobody = false,
124-
writefunction = writer(function(t, s) t[#t+1] = s end, t)
157+
writefunction = writer(assert(cb))
125158
}
126159
if not ok then return nil, err end
160+
ok, err = exec(self)
161+
if not ok then return nil, err end
162+
return true
163+
end
127164

128-
ok, err = self._easy:perform()
129-
165+
local function exec_body(self, cmd, url)
166+
local t = {}
167+
local ok, err = exec_body_cb(self,
168+
function(s) t[#t+1] = s end,
169+
cmd, url
170+
)
130171
if not ok then return nil, err end
131172
return t
132173
end
133174

134-
function pop3:retr(n)
135-
local t = {}
136-
local ok, err = self._easy:setopt{
137-
url = self._url .. '/' .. n,
138-
customrequest = '',
139-
nobody = false,
140-
writefunction = writer(function(t, s) t[#t+1] = s end, t)
141-
}
175+
local function exec_no_body_2_numbers(...)
176+
local ok, data = exec_no_body(...)
177+
if not ok then return ok, data end
178+
return split_2_numbers(data)
179+
end
180+
181+
function pop3:open(user, password)
182+
return open(self, user, password, false)
183+
end
184+
185+
function pop3:response()
186+
if self._response then
187+
return pars_response(self._response)
188+
end
189+
end
190+
191+
function pop3:verbose(...)
192+
local ok, err
193+
if select("#", ...) == 0 then
194+
ok, err = self._easy:setopt_verbose(true)
195+
else
196+
ok, err = self._easy:setopt_verbose(not not ...)
197+
end
198+
if not ok then return nil, ok end
199+
return self
200+
end
201+
202+
function pop3:open_tls(user, password)
203+
return open(self, user, password, true)
204+
end
205+
206+
function pop3:noop()
207+
return exec_no_body(self, 'NOOP')
208+
end
209+
210+
function pop3:stat()
211+
return exec_no_body_2_numbers(self, 'STAT')
212+
end
213+
214+
function pop3:dele(id)
215+
return exec_no_body(self, 'DELE ' .. id)
216+
end
217+
218+
function pop3:rset()
219+
return exec_no_body_2_numbers(self, 'RSET')
220+
end
221+
222+
function pop3:list(id)
223+
if id then
224+
return exec_no_body_2_numbers(self, 'LIST ' .. id)
225+
end
226+
227+
local t,i = {},0
228+
local fn = function(data)
229+
local no, size = split_2_numbers(data)
230+
if not (no and size) then
231+
return nil, "Wrong Response: `" .. data .. "`"
232+
end
233+
t[no] = size
234+
i = i + 1
235+
end
236+
237+
local ok, err = exec_body_cb(self, fn, '')
238+
if not ok then return nil, err end
142239
if not ok then return nil, err end
240+
return t, i
241+
end
143242

144-
ok, err = self._easy:perform()
243+
function pop3:uidl(id)
244+
if id then
245+
local ok, data = exec_no_body(self, 'UIDL ' .. id)
246+
if not ok then return ok, data end
247+
local no, id = split_1_number(data)
248+
if not (no and id) then
249+
return nil, "Wrong Response:" .. data
250+
end
251+
return no,id
252+
end
145253

254+
local t,i = {},0
255+
local fn = function(data)
256+
local no, id = split_1_number(data)
257+
if not (no and id) then
258+
return nil, "Wrong Response:" .. data
259+
end
260+
t[no]=id
261+
i = i + 1
262+
return true
263+
end
264+
265+
local ok, err = exec_body_cb(self, fn, 'UIDL')
146266
if not ok then return nil, err end
147-
return t
267+
if not ok then return nil, err end
268+
return t, i
269+
end
270+
271+
function pop3:retr(id)
272+
assert(id)
273+
return exec_body(self, '', tostring(id))
274+
end
275+
276+
function pop3:top(id, n)
277+
assert(id)
278+
assert(type(n) == "number")
279+
return exec_body(self, 'TOP ' .. id .. ' ' .. n)
280+
end
281+
282+
function pop3:make_iter(fn)
283+
local lst, err = self:list()
284+
if not lst then error(err) end
285+
local k = nil
286+
287+
local iter
288+
iter = function ()
289+
k = next(lst, k)
290+
if not k then return nil end
291+
292+
-- skip deleted messages ?
293+
local no, size = self:list(k)
294+
if no == false then return iter() end -- next message
295+
if not no then return error(size) end
296+
assert(no == k)
297+
298+
local data, err = fn(self, k, size)
299+
if not data then error(err) end
300+
301+
return k, data
302+
end
303+
304+
return iter
305+
end
306+
307+
if message then
308+
309+
function pop3:message(msgid)
310+
local msg, err = self:retr(msgid)
311+
if not msg then return nil, err end
312+
return message(msg)
313+
end
314+
315+
end
316+
317+
function pop3:retrs()
318+
return self:make_iter(self.retr)
319+
end
320+
321+
function pop3:tops(n)
322+
return self:make_iter(function(self, msgid)
323+
return self:top(msgid, n)
324+
end)
325+
end
326+
327+
function pop3:messages()
328+
return self:make_iter(self.message)
148329
end
149330

150331
function pop3:closed()

0 commit comments

Comments
 (0)