aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/except.c32
-rw-r--r--src/ftp.lua182
-rw-r--r--src/http.lua55
-rw-r--r--src/smtp.lua64
-rw-r--r--src/socket.lua4
5 files changed, 185 insertions, 152 deletions
diff --git a/src/except.c b/src/except.c
index 0482e1c..53a65ac 100644
--- a/src/except.c
+++ b/src/except.c
@@ -14,17 +14,20 @@
14\*=========================================================================*/ 14\*=========================================================================*/
15static int global_try(lua_State *L); 15static int global_try(lua_State *L);
16static int global_protect(lua_State *L); 16static int global_protect(lua_State *L);
17static int global_newtry(lua_State *L);
17static int protected(lua_State *L); 18static int protected(lua_State *L);
19static int finalize(lua_State *L);
18 20
19/* except functions */ 21/* except functions */
20static luaL_reg func[] = { 22static luaL_reg func[] = {
21 {"try", global_try}, 23 {"try", global_try},
22 {"protect", global_protect}, 24 {"newtry", global_newtry},
23 {NULL, NULL} 25 {"protect", global_protect},
26 {NULL, NULL}
24}; 27};
25 28
26/*-------------------------------------------------------------------------*\ 29/*-------------------------------------------------------------------------*\
27* Exception handling: try method 30* Try method
28\*-------------------------------------------------------------------------*/ 31\*-------------------------------------------------------------------------*/
29static int global_try(lua_State *L) { 32static int global_try(lua_State *L) {
30 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { 33 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
@@ -35,7 +38,25 @@ static int global_try(lua_State *L) {
35} 38}
36 39
37/*-------------------------------------------------------------------------*\ 40/*-------------------------------------------------------------------------*\
38* Exception handling: protect factory 41* Finalizer factory
42\*-------------------------------------------------------------------------*/
43static int finalize(lua_State *L) {
44 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
45 lua_pushvalue(L, lua_upvalueindex(1));
46 lua_pcall(L, 0, 0, 0);
47 lua_settop(L, 2);
48 lua_error(L);
49 return 0;
50 } else return lua_gettop(L);
51}
52
53static int global_newtry(lua_State *L) {
54 lua_pushcclosure(L, finalize, 1);
55 return 1;
56}
57
58/*-------------------------------------------------------------------------*\
59* Protect factory
39\*-------------------------------------------------------------------------*/ 60\*-------------------------------------------------------------------------*/
40static int protected(lua_State *L) { 61static int protected(lua_State *L) {
41 lua_pushvalue(L, lua_upvalueindex(1)); 62 lua_pushvalue(L, lua_upvalueindex(1));
@@ -48,7 +69,6 @@ static int protected(lua_State *L) {
48} 69}
49 70
50static int global_protect(lua_State *L) { 71static int global_protect(lua_State *L) {
51 lua_insert(L, 1);
52 lua_pushcclosure(L, protected, 1); 72 lua_pushcclosure(L, protected, 1);
53 return 1; 73 return 1;
54} 74}
diff --git a/src/ftp.lua b/src/ftp.lua
index 3868158..ad02c72 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -32,143 +32,153 @@ local metat = { __index = {} }
32 32
33function open(server, port) 33function open(server, port)
34 local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) 34 local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT))
35 return setmetatable({tp = tp}, metat) 35 local f = { tp = tp }
36 -- make sure everything gets closed in an exception
37 f.try = socket.newtry(function()
38 tp:close()
39 if f.data then f.data:close() end
40 if f.server then f.server:close() end
41 end)
42 return setmetatable(f, metat)
36end 43end
37 44
38local function port(portt) 45function metat.__index:portconnect()
39 return portt.server:accept() 46 self.try(self.server:settimeout(TIMEOUT))
47 self.data = self.try(self.server:accept())
48 self.try(self.data:settimeout(TIMEOUT))
40end 49end
41 50
42local function pasv(pasvt) 51function metat.__index:pasvconnect()
43 local data = socket.try(socket.tcp()) 52 self.data = self.try(socket.tcp())
44 socket.try(data:settimeout(TIMEOUT)) 53 self.try(self.data:settimeout(TIMEOUT))
45 socket.try(data:connect(pasvt.ip, pasvt.port)) 54 self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
46 return data
47end 55end
48 56
49function metat.__index:login(user, password) 57function metat.__index:login(user, password)
50 socket.try(self.tp:command("user", user or USER)) 58 self.try(self.tp:command("user", user or USER))
51 local code, reply = socket.try(self.tp:check{"2..", 331}) 59 local code, reply = self.try(self.tp:check{"2..", 331})
52 if code == 331 then 60 if code == 331 then
53 socket.try(self.tp:command("pass", password or PASSWORD)) 61 self.try(self.tp:command("pass", password or PASSWORD))
54 socket.try(self.tp:check("2..")) 62 self.try(self.tp:check("2.."))
55 end 63 end
56 return 1 64 return 1
57end 65end
58 66
59function metat.__index:pasv() 67function metat.__index:pasv()
60 socket.try(self.tp:command("pasv")) 68 self.try(self.tp:command("pasv"))
61 local code, reply = socket.try(self.tp:check("2..")) 69 local code, reply = self.try(self.tp:check("2.."))
62 local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" 70 local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
63 local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) 71 local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
64 socket.try(a and b and c and d and p1 and p2, reply) 72 self.try(a and b and c and d and p1 and p2, reply)
65 self.pasvt = { 73 self.pasvt = {
66 ip = string.format("%d.%d.%d.%d", a, b, c, d), 74 ip = string.format("%d.%d.%d.%d", a, b, c, d),
67 port = p1*256 + p2 75 port = p1*256 + p2
68 } 76 }
69 if self.portt then 77 if self.server then
70 self.portt.server:close() 78 self.server:close()
71 self.portt = nil 79 self.server = nil
72 end 80 end
73 return self.pasvt.ip, self.pasvt.port 81 return self.pasvt.ip, self.pasvt.port
74end 82end
75 83
76function metat.__index:port(ip, port) 84function metat.__index:port(ip, port)
77 self.pasvt = nil 85 self.pasvt = nil
78 local server
79 if not ip then 86 if not ip then
80 ip, port = socket.try(self.tp:getcontrol():getsockname()) 87 ip, port = self.try(self.tp:getcontrol():getsockname())
81 server = socket.try(socket.bind(ip, 0)) 88 self.server = self.try(socket.bind(ip, 0))
82 ip, port = socket.try(server:getsockname()) 89 ip, port = self.try(self.server:getsockname())
83 socket.try(server:settimeout(TIMEOUT)) 90 self.try(server:settimeout(TIMEOUT))
84 end 91 end
85 local pl = math.mod(port, 256) 92 local pl = math.mod(port, 256)
86 local ph = (port - pl)/256 93 local ph = (port - pl)/256
87 local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") 94 local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",")
88 socket.try(self.tp:command("port", arg)) 95 self.try(self.tp:command("port", arg))
89 socket.try(self.tp:check("2..")) 96 self.try(self.tp:check("2.."))
90 self.portt = server and {ip = ip, port = port, server = server}
91 return 1 97 return 1
92end 98end
93 99
94function metat.__index:send(sendt) 100function metat.__index:send(sendt)
95 local data 101 self.try(self.pasvt or self.server, "need port or pasv first")
96 socket.try(self.pasvt or self.portt, "need port or pasv first") 102 -- if there is a pasvt table, we already sent a PASV command
97 if self.pasvt then data = socket.try(pasv(self.pasvt)) end 103 -- we just get the data connection into self.data
104 if self.pasvt then self:pasvconnect() end
105 -- get the transfer argument and command
98 local argument = sendt.argument or string.gsub(sendt.path, "^/", "") 106 local argument = sendt.argument or string.gsub(sendt.path, "^/", "")
99 if argument == "" then argument = nil end 107 if argument == "" then argument = nil end
100 local command = sendt.command or "stor" 108 local command = sendt.command or "stor"
101 socket.try(self.tp:command(command, argument)) 109 -- send the transfer command and check the reply
102 local code, reply = socket.try(self.tp:check{"2..", "1.."}) 110 self.try(self.tp:command(command, argument))
103 if self.portt then data = socket.try(port(self.portt)) end 111 local code, reply = self.try(self.tp:check{"2..", "1.."})
112 -- if there is not a a pasvt table, then there is a server
113 -- and we already sent a PORT command
114 if not self.pasvt then self:portconnect() end
115 -- get the sink, source and step for the transfer
104 local step = sendt.step or ltn12.pump.step 116 local step = sendt.step or ltn12.pump.step
105 local checkstep = function(src, snk) 117 local checkstep = function(src, snk)
118 -- check status in control connection while downloading
106 local readyt = socket.select(readt, nil, 0) 119 local readyt = socket.select(readt, nil, 0)
107 if readyt[tp] then 120 if readyt[tp] then self.try(self.tp:check("2..")) end
108 code, reply = self.tp:check("2..") 121 return step(src, snk)
109 if not code then
110 data:close()
111 return nil, reply
112 end
113 end
114 local ret, err = step(src, snk)
115 if err then data:close() end
116 return ret, err
117 end 122 end
118 local sink = socket.sink("close-when-done", data) 123 local sink = socket.sink("close-when-done", self.data)
119 socket.try(ltn12.pump.all(sendt.source, sink, checkstep)) 124 -- transfer all data and check error
120 if string.find(code, "1..") then socket.try(self.tp:check("2..")) end 125 self.try(ltn12.pump.all(sendt.source, sink, checkstep))
126 if string.find(code, "1..") then self.try(self.tp:check("2..")) end
127 -- done with data connection
128 self.data:close()
129 self.data = nil
121 return 1 130 return 1
122end 131end
123 132
124function metat.__index:receive(recvt) 133function metat.__index:receive(recvt)
125 local data 134 self.try(self.pasvt or self.server, "need port or pasv first")
126 socket.try(self.pasvt or self.portt, "need port or pasv first") 135 if self.pasvt then self:pasvconnect() end
127 if self.pasvt then data = socket.try(pasv(self.pasvt)) end
128 local argument = recvt.argument or string.gsub(recvt.path, "^/", "") 136 local argument = recvt.argument or string.gsub(recvt.path, "^/", "")
129 if argument == "" then argument = nil end 137 if argument == "" then argument = nil end
130 local command = recvt.command or "retr" 138 local command = recvt.command or "retr"
131 socket.try(self.tp:command(command, argument)) 139 self.try(self.tp:command(command, argument))
132 local code = socket.try(self.tp:check{"1..", "2.."}) 140 local code = self.try(self.tp:check{"1..", "2.."})
133 if self.portt then data = socket.try(port(self.portt)) end 141 if not self.pasvt then self:portconnect() end
134 local source = socket.source("until-closed", data) 142 local source = socket.source("until-closed", self.data)
135 local step = recvt.step or ltn12.pump.step 143 local step = recvt.step or ltn12.pump.step
136 local checkstep = function(src, snk) 144 self.try(ltn12.pump.all(source, recvt.sink, step))
137 local ret, err = step(src, snk) 145 if string.find(code, "1..") then self.try(self.tp:check("2..")) end
138 if err then data:close() end 146 self.data:close()
139 return ret, err 147 self.data = nil
140 end
141 socket.try(ltn12.pump.all(source, recvt.sink, checkstep))
142 if string.find(code, "1..") then socket.try(self.tp:check("2..")) end
143 return 1 148 return 1
144end 149end
145 150
146function metat.__index:cwd(dir) 151function metat.__index:cwd(dir)
147 socket.try(self.tp:command("cwd", dir)) 152 self.try(self.tp:command("cwd", dir))
148 socket.try(self.tp:check(250)) 153 self.try(self.tp:check(250))
149 return 1 154 return 1
150end 155end
151 156
152function metat.__index:type(type) 157function metat.__index:type(type)
153 socket.try(self.tp:command("type", type)) 158 self.try(self.tp:command("type", type))
154 socket.try(self.tp:check(200)) 159 self.try(self.tp:check(200))
155 return 1 160 return 1
156end 161end
157 162
158function metat.__index:greet() 163function metat.__index:greet()
159 local code = socket.try(self.tp:check{"1..", "2.."}) 164 local code = self.try(self.tp:check{"1..", "2.."})
160 if string.find(code, "1..") then socket.try(self.tp:check("2..")) end 165 if string.find(code, "1..") then self.try(self.tp:check("2..")) end
161 return 1 166 return 1
162end 167end
163 168
164function metat.__index:quit() 169function metat.__index:quit()
165 socket.try(self.tp:command("quit")) 170 self.try(self.tp:command("quit"))
166 socket.try(self.tp:check("2..")) 171 self.try(self.tp:check("2.."))
167 return 1 172 return 1
168end 173end
169 174
170function metat.__index:close() 175function metat.__index:close()
171 socket.try(self.tp:close()) 176 self.tp:close()
177 if self.data then self.data:close() end
178 if self.server then self.server:close() end
179 self.tp = nil
180 self.data = nil
181 self.server = nil
172 return 1 182 return 1
173end 183end
174 184
@@ -176,14 +186,14 @@ end
176-- High level FTP API 186-- High level FTP API
177----------------------------------------------------------------------------- 187-----------------------------------------------------------------------------
178local function tput(putt) 188local function tput(putt)
179 local con = open(putt.host, putt.port) 189 local f = open(putt.host, putt.port)
180 con:greet() 190 f:greet()
181 con:login(putt.user, putt.password) 191 f:login(putt.user, putt.password)
182 if putt.type then con:type(putt.type) end 192 if putt.type then f:type(putt.type) end
183 con:pasv() 193 f:pasv()
184 con:send(putt) 194 f:send(putt)
185 con:quit() 195 f:quit()
186 return con:close() 196 return f:close()
187end 197end
188 198
189local default = { 199local default = {
@@ -216,14 +226,14 @@ put = socket.protect(function(putt, body)
216end) 226end)
217 227
218local function tget(gett) 228local function tget(gett)
219 local con = open(gett.host, gett.port) 229 local f = open(gett.host, gett.port)
220 con:greet() 230 f:greet()
221 con:login(gett.user, gett.password) 231 f:login(gett.user, gett.password)
222 if gett.type then con:type(gett.type) end 232 if gett.type then f:type(gett.type) end
223 con:pasv() 233 f:pasv()
224 con:receive(gett) 234 f:receive(gett)
225 con:quit() 235 f:quit()
226 return con:close() 236 return f:close()
227end 237end
228 238
229local function sget(u) 239local function sget(u)
diff --git a/src/http.lua b/src/http.lua
index 3bd4d6a..d8889e1 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -31,24 +31,25 @@ BLOCKSIZE = 2048
31local metat = { __index = {} } 31local metat = { __index = {} }
32 32
33function open(host, port) 33function open(host, port)
34 local con = socket.try(socket.tcp()) 34 local c = socket.try(socket.tcp())
35 socket.try(con:settimeout(TIMEOUT)) 35 -- make sure the connection gets closed on exception
36 port = port or PORT 36 local try = socket.newtry(function() c:close() end)
37 socket.try(con:connect(host, port)) 37 try(c:settimeout(TIMEOUT))
38 return setmetatable({ con = con }, metat) 38 try(c:connect(host, port or PORT))
39 return setmetatable({ c = c, try = try }, metat)
39end 40end
40 41
41function metat.__index:sendrequestline(method, uri) 42function metat.__index:sendrequestline(method, uri)
42 local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) 43 local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
43 return socket.try(self.con:send(reqline)) 44 return self.try(self.c:send(reqline))
44end 45end
45 46
46function metat.__index:sendheaders(headers) 47function metat.__index:sendheaders(headers)
47 for i, v in pairs(headers) do 48 for i, v in pairs(headers) do
48 socket.try(self.con:send(i .. ": " .. v .. "\r\n")) 49 self.try(self.c:send(i .. ": " .. v .. "\r\n"))
49 end 50 end
50 -- mark end of request headers 51 -- mark end of request headers
51 socket.try(self.con:send("\r\n")) 52 self.try(self.c:send("\r\n"))
52 return 1 53 return 1
53end 54end
54 55
@@ -59,32 +60,32 @@ function metat.__index:sendbody(headers, source, step)
59 local mode 60 local mode
60 if headers["content-length"] then mode = "keep-open" 61 if headers["content-length"] then mode = "keep-open"
61 else mode = "http-chunked" end 62 else mode = "http-chunked" end
62 return socket.try(ltn12.pump.all(source, socket.sink(mode, self.con), step)) 63 return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
63end 64end
64 65
65function metat.__index:receivestatusline() 66function metat.__index:receivestatusline()
66 local status = socket.try(self.con:receive()) 67 local status = self.try(self.c:receive())
67 local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) 68 local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
68 return socket.try(tonumber(code), status) 69 return self.try(tonumber(code), status)
69end 70end
70 71
71function metat.__index:receiveheaders() 72function metat.__index:receiveheaders()
72 local line, name, value 73 local line, name, value
73 local headers = {} 74 local headers = {}
74 -- get first line 75 -- get first line
75 line = socket.try(self.con:receive()) 76 line = self.try(self.c:receive())
76 -- headers go until a blank line is found 77 -- headers go until a blank line is found
77 while line ~= "" do 78 while line ~= "" do
78 -- get field-name and value 79 -- get field-name and value
79 name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) 80 name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
80 socket.try(name and value, "malformed reponse headers") 81 self.try(name and value, "malformed reponse headers")
81 name = string.lower(name) 82 name = string.lower(name)
82 -- get next line (value might be folded) 83 -- get next line (value might be folded)
83 line = socket.try(self.con:receive()) 84 line = self.try(self.c:receive())
84 -- unfold any folded values 85 -- unfold any folded values
85 while string.find(line, "^%s") do 86 while string.find(line, "^%s") do
86 value = value .. line 87 value = value .. line
87 line = socket.try(self.con:receive()) 88 line = self.try(self.c:receive())
88 end 89 end
89 -- save pair in table 90 -- save pair in table
90 if headers[name] then headers[name] = headers[name] .. ", " .. value 91 if headers[name] then headers[name] = headers[name] .. ", " .. value
@@ -102,12 +103,12 @@ function metat.__index:receivebody(headers, sink, step)
102 if TE and TE ~= "identity" then mode = "http-chunked" 103 if TE and TE ~= "identity" then mode = "http-chunked"
103 elseif tonumber(headers["content-length"]) then mode = "by-length" 104 elseif tonumber(headers["content-length"]) then mode = "by-length"
104 else mode = "default" end 105 else mode = "default" end
105 return socket.try(ltn12.pump.all(socket.source(mode, self.con, length), 106 return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
106 sink, step)) 107 sink, step))
107end 108end
108 109
109function metat.__index:close() 110function metat.__index:close()
110 return self.con:close() 111 return self.c:close()
111end 112end
112 113
113----------------------------------------------------------------------------- 114-----------------------------------------------------------------------------
@@ -204,23 +205,23 @@ end
204 205
205function trequest(reqt) 206function trequest(reqt)
206 reqt = adjustrequest(reqt) 207 reqt = adjustrequest(reqt)
207 local con = open(reqt.host, reqt.port) 208 local h = open(reqt.host, reqt.port)
208 con:sendrequestline(reqt.method, reqt.uri) 209 h:sendrequestline(reqt.method, reqt.uri)
209 con:sendheaders(reqt.headers) 210 h:sendheaders(reqt.headers)
210 con:sendbody(reqt.headers, reqt.source, reqt.step) 211 h:sendbody(reqt.headers, reqt.source, reqt.step)
211 local code, headers, status 212 local code, headers, status
212 code, status = con:receivestatusline() 213 code, status = h:receivestatusline()
213 headers = con:receiveheaders() 214 headers = h:receiveheaders()
214 if shouldredirect(reqt, code) then 215 if shouldredirect(reqt, code) then
215 con:close() 216 h:close()
216 return tredirect(reqt, headers) 217 return tredirect(reqt, headers)
217 elseif shouldauthorize(reqt, code) then 218 elseif shouldauthorize(reqt, code) then
218 con:close() 219 h:close()
219 return tauthorize(reqt) 220 return tauthorize(reqt)
220 elseif shouldreceivebody(reqt, code) then 221 elseif shouldreceivebody(reqt, code) then
221 con:receivebody(headers, reqt.sink, reqt.step) 222 h:receivebody(headers, reqt.sink, reqt.step)
222 end 223 end
223 con:close() 224 h:close()
224 return 1, code, headers, status 225 return 1, code, headers, status
225end 226end
226 227
diff --git a/src/smtp.lua b/src/smtp.lua
index 1708053..d6357d2 100644
--- a/src/smtp.lua
+++ b/src/smtp.lua
@@ -31,51 +31,51 @@ ZONE = "-0000"
31local metat = { __index = {} } 31local metat = { __index = {} }
32 32
33function metat.__index:greet(domain) 33function metat.__index:greet(domain)
34 socket.try(self.tp:check("2..")) 34 self.try(self.tp:check("2.."))
35 socket.try(self.tp:command("EHLO", domain or DOMAIN)) 35 self.try(self.tp:command("EHLO", domain or DOMAIN))
36 return socket.skip(1, socket.try(self.tp:check("2.."))) 36 return socket.skip(1, self.try(self.tp:check("2..")))
37end 37end
38 38
39function metat.__index:mail(from) 39function metat.__index:mail(from)
40 socket.try(self.tp:command("MAIL", "FROM:" .. from)) 40 self.try(self.tp:command("MAIL", "FROM:" .. from))
41 return socket.try(self.tp:check("2..")) 41 return self.try(self.tp:check("2.."))
42end 42end
43 43
44function metat.__index:rcpt(to) 44function metat.__index:rcpt(to)
45 socket.try(self.tp:command("RCPT", "TO:" .. to)) 45 self.try(self.tp:command("RCPT", "TO:" .. to))
46 return socket.try(self.tp:check("2..")) 46 return self.try(self.tp:check("2.."))
47end 47end
48 48
49function metat.__index:data(src, step) 49function metat.__index:data(src, step)
50 socket.try(self.tp:command("DATA")) 50 self.try(self.tp:command("DATA"))
51 socket.try(self.tp:check("3..")) 51 self.try(self.tp:check("3.."))
52 socket.try(self.tp:source(src, step)) 52 self.try(self.tp:source(src, step))
53 socket.try(self.tp:send("\r\n.\r\n")) 53 self.try(self.tp:send("\r\n.\r\n"))
54 return socket.try(self.tp:check("2..")) 54 return self.try(self.tp:check("2.."))
55end 55end
56 56
57function metat.__index:quit() 57function metat.__index:quit()
58 socket.try(self.tp:command("QUIT")) 58 self.try(self.tp:command("QUIT"))
59 return socket.try(self.tp:check("2..")) 59 return self.try(self.tp:check("2.."))
60end 60end
61 61
62function metat.__index:close() 62function metat.__index:close()
63 return socket.try(self.tp:close()) 63 return self.try(self.tp:close())
64end 64end
65 65
66function metat.__index:login(user, password) 66function metat.__index:login(user, password)
67 socket.try(self.tp:command("AUTH", "LOGIN")) 67 self.try(self.tp:command("AUTH", "LOGIN"))
68 socket.try(self.tp:check("3..")) 68 self.try(self.tp:check("3.."))
69 socket.try(self.tp:command(mime.b64(user))) 69 self.try(self.tp:command(mime.b64(user)))
70 socket.try(self.tp:check("3..")) 70 self.try(self.tp:check("3.."))
71 socket.try(self.tp:command(mime.b64(password))) 71 self.try(self.tp:command(mime.b64(password)))
72 return socket.try(self.tp:check("2..")) 72 return self.try(self.tp:check("2.."))
73end 73end
74 74
75function metat.__index:plain(user, password) 75function metat.__index:plain(user, password)
76 local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) 76 local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
77 socket.try(self.tp:command("AUTH", auth)) 77 self.try(self.tp:command("AUTH", auth))
78 return socket.try(self.tp:check("2..")) 78 return self.try(self.tp:check("2.."))
79end 79end
80 80
81function metat.__index:auth(user, password, ext) 81function metat.__index:auth(user, password, ext)
@@ -85,7 +85,7 @@ function metat.__index:auth(user, password, ext)
85 elseif string.find(ext, "AUTH[^\n]+PLAIN") then 85 elseif string.find(ext, "AUTH[^\n]+PLAIN") then
86 return self:plain(user, password) 86 return self:plain(user, password)
87 else 87 else
88 socket.try(nil, "authentication not supported") 88 self.try(nil, "authentication not supported")
89 end 89 end
90end 90end
91 91
@@ -104,7 +104,9 @@ end
104 104
105function open(server, port) 105function open(server, port)
106 local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) 106 local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT))
107 return setmetatable({tp = tp}, metat) 107 -- make sure tp is closed if we get an exception
108 local try = socket.newtry(function() tp:close() end)
109 return setmetatable({ tp = tp, try = try}, metat)
108end 110end
109 111
110--------------------------------------------------------------------------- 112---------------------------------------------------------------------------
@@ -222,10 +224,10 @@ end
222-- High level SMTP API 224-- High level SMTP API
223----------------------------------------------------------------------------- 225-----------------------------------------------------------------------------
224send = socket.protect(function(mailt) 226send = socket.protect(function(mailt)
225 local con = open(mailt.server, mailt.port) 227 local s = open(mailt.server, mailt.port)
226 local ext = con:greet(mailt.domain) 228 local ext = s:greet(mailt.domain)
227 con:auth(mailt.user, mailt.password, ext) 229 s:auth(mailt.user, mailt.password, ext)
228 con:send(mailt) 230 s:send(mailt)
229 con:quit() 231 s:quit()
230 return con:close() 232 return s:close()
231end) 233end)
diff --git a/src/socket.lua b/src/socket.lua
index f73d167..0a681bf 100644
--- a/src/socket.lua
+++ b/src/socket.lua
@@ -156,8 +156,8 @@ socket.sourcet["http-chunked"] = function(sock)
156 else 156 else
157 -- get chunk and skip terminating CRLF 157 -- get chunk and skip terminating CRLF
158 local chunk, err = sock:receive(size) 158 local chunk, err = sock:receive(size)
159 if err or socket.skip(2, sock:receive()) then return nil, err 159 if chunk then sock:receive() end
160 else return chunk end 160 return chunk, err
161 end 161 end
162 end 162 end
163 }) 163 })