aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2001-03-27 19:25:11 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2001-03-27 19:25:11 +0000
commitbd0bf459793be5616a16f3c54fe93654a836b756 (patch)
treeac1d010bc179b71e0123cdcb0a532f945fd700e4 /src
parent366fb989f301f88de6cd8516a83b86f54157a126 (diff)
downloadluasocket-bd0bf459793be5616a16f3c54fe93654a836b756.tar.gz
luasocket-bd0bf459793be5616a16f3c54fe93654a836b756.tar.bz2
luasocket-bd0bf459793be5616a16f3c54fe93654a836b756.zip
BUG: multi-line replies were not supported.
Error logic simplified.
Diffstat (limited to 'src')
-rw-r--r--src/smtp.lua653
1 files changed, 315 insertions, 338 deletions
diff --git a/src/smtp.lua b/src/smtp.lua
index f9ed64c..7f9af3d 100644
--- a/src/smtp.lua
+++ b/src/smtp.lua
@@ -1,338 +1,315 @@
1----------------------------------------------------------------------------- 1-----------------------------------------------------------------------------
2-- Simple SMTP support for the Lua language using the LuaSocket toolkit. 2-- Simple SMTP support for the Lua language using the LuaSocket toolkit.
3-- Author: Diego Nehab 3-- Author: Diego Nehab
4-- Date: 26/12/2000 4-- Date: 26/12/2000
5-- Conforming to: RFC 821 5-- Conforming to: RFC 821
6----------------------------------------------------------------------------- 6-----------------------------------------------------------------------------
7 7
8----------------------------------------------------------------------------- 8-----------------------------------------------------------------------------
9-- Program constants 9-- Program constants
10----------------------------------------------------------------------------- 10-----------------------------------------------------------------------------
11-- timeout in secconds before we give up waiting 11-- timeout in secconds before we give up waiting
12local TIMEOUT = 180 12local TIMEOUT = 180
13-- port used for connection 13-- port used for connection
14local PORT = 25 14local PORT = 25
15-- domain used in HELO command. If we are under a CGI, try to get from 15-- domain used in HELO command. If we are under a CGI, try to get from
16-- environment 16-- environment
17local DOMAIN = getenv("SERVER_NAME") 17local DOMAIN = getenv("SERVER_NAME")
18if not DOMAIN then 18if not DOMAIN then
19 DOMAIN = "localhost" 19 DOMAIN = "localhost"
20end 20end
21 21
22----------------------------------------------------------------------------- 22-----------------------------------------------------------------------------
23-- Tries to send DOS mode lines. Closes socket on error. 23-- Tries to send DOS mode lines. Closes socket on error.
24-- Input 24-- Input
25-- sock: server socket 25-- sock: server socket
26-- line: string to be sent 26-- line: string to be sent
27-- Returns 27-- Returns
28-- err: message in case of error, nil if successfull 28-- err: message in case of error, nil if successfull
29----------------------------------------------------------------------------- 29-----------------------------------------------------------------------------
30local puts = function(sock, line) 30local try_send = function(sock, line)
31 local err = sock:send(line .. "\r\n") 31 local err = sock:send(line .. "\r\n")
32 if err then sock:close() end 32print(line)
33 return err 33 if err then sock:close() end
34end 34 return err
35 35end
36----------------------------------------------------------------------------- 36
37-- Tries to receive DOS mode lines. Closes socket on error. 37-----------------------------------------------------------------------------
38-- Input 38-- Gets command reply, (accepts multiple-line replies)
39-- sock: server socket 39-- Input
40-- Returns 40-- control: control connection socket
41-- line: received string if successfull, nil in case of error 41-- Returns
42-- err: error message if any 42-- answer: whole server reply, nil if error
43----------------------------------------------------------------------------- 43-- code: reply status code or error message
44local gets = function(sock) 44-----------------------------------------------------------------------------
45 local line, err = sock:receive("*l") 45local get_answer = function(control)
46 if err then 46 local code, lastcode, sep
47 sock:close() 47 local line, err = control:receive()
48 return nil, err 48 local answer = line
49 end 49 if err then return nil, err end
50 return line 50print(line)
51end 51 _,_, code, sep = strfind(line, "^(%d%d%d)(.)")
52 52 if not code or not sep then return nil, answer end
53----------------------------------------------------------------------------- 53 if sep == "-" then -- answer is multiline
54-- Gets a reply from the server and close connection if it is wrong 54 repeat
55-- Input 55 line, err = control:receive()
56-- sock: server socket 56 if err then return nil, err end
57-- accept: acceptable errorcodes 57print(line)
58-- Returns 58 _,_, lastcode, sep = strfind(line, "^(%d%d%d)(.)")
59-- code: server reply code. nil if error 59 answer = answer .. "\n" .. line
60-- line: complete server reply message or error message 60 until code == lastcode and sep == " " -- answer ends with same code
61----------------------------------------------------------------------------- 61 end
62local get_reply = function(sock, accept) 62 return answer, tonumber(code)
63 local line, err = %gets(sock) 63end
64 if line then 64
65 if type(accept) ~= "table" then accept = {accept} end 65-----------------------------------------------------------------------------
66 local _,_, code = strfind(line, "^(%d%d%d)") 66-- Checks if a message reply code is correct. Closes control connection
67 if not code then return nil, line end 67-- if not.
68 code = tonumber(code) 68-- Input
69 for i = 1, getn(accept) do 69-- control: control connection socket
70 if code == accept[i] then return code, line end 70-- success: table with successfull reply status code
71 end 71-- Returns
72 sock:close() 72-- code: reply code or nil in case of error
73 return nil, line 73-- answer: complete server answer or system error message
74 end 74-----------------------------------------------------------------------------
75 return nil, err 75local check_answer = function(control, success)
76end 76 local answer, code = %get_answer(control)
77 77 if not answer then
78----------------------------------------------------------------------------- 78 control:close()
79-- Sends a command to the server 79 return nil, code
80-- Input 80 end
81-- sock: server socket 81 if type(success) ~= "table" then success = {success} end
82-- command: command to be sent 82 for i = 1, getn(success) do
83-- param: command parameters if any 83 if code == success[i] then
84-- Returns 84 return code, answer
85-- err: error message if any 85 end
86----------------------------------------------------------------------------- 86 end
87local send_command = function(sock, command, param) 87 control:close()
88 local line 88 return nil, answer
89 if param then line = command .. " " .. param 89end
90 else line = command end 90
91 return %puts(sock, line) 91-----------------------------------------------------------------------------
92end 92-- Sends a command to the server (closes sock on error)
93 93-- Input
94----------------------------------------------------------------------------- 94-- sock: server socket
95-- Gets the initial server greeting 95-- command: command to be sent
96-- Input 96-- param: command parameters if any
97-- sock: server socket 97-- Returns
98-- Returns 98-- err: error message if any
99-- code: server status code, nil if error 99-----------------------------------------------------------------------------
100-- answer: complete server reply 100local send_command = function(sock, command, param)
101----------------------------------------------------------------------------- 101 local line
102local get_helo = function(sock) 102 if param then line = command .. " " .. param
103 return %get_reply(sock, 220) 103 else line = command end
104end 104 return %try_send(sock, line)
105 105end
106----------------------------------------------------------------------------- 106
107-- Sends initial client greeting 107-----------------------------------------------------------------------------
108-- Input 108-- Sends initial client greeting
109-- sock: server socket 109-- Input
110-- Returns 110-- sock: server socket
111-- code: server status code, nil if error 111-- Returns
112-- answer: complete server reply 112-- code: server code if ok, nil if error
113----------------------------------------------------------------------------- 113-- answer: complete server reply
114local send_helo = function(sock) 114-----------------------------------------------------------------------------
115 local err = %send_command(sock, "HELO", %DOMAIN) 115local send_helo = function(sock)
116 if not err then 116 local err = %send_command(sock, "HELO", %DOMAIN)
117 return %get_reply(sock, 250) 117 if err then return nil, err end
118 else return nil, err end 118 return %check_answer(sock, 250)
119end 119end
120 120
121----------------------------------------------------------------------------- 121-----------------------------------------------------------------------------
122-- Sends mime headers 122-- Sends mime headers
123-- Input 123-- Input
124-- sock: server socket 124-- sock: server socket
125-- mime: table with mime headers to be sent 125-- mime: table with mime headers to be sent
126-- Returns 126-- Returns
127-- err: error message if any 127-- err: error message if any
128----------------------------------------------------------------------------- 128-----------------------------------------------------------------------------
129local send_mime = function(sock, mime) 129local send_mime = function(sock, mime)
130 local err 130 local err
131 mime = mime or {} 131 mime = mime or {}
132 -- send all headers 132 -- send all headers
133 for name,value in mime do 133 for name,value in mime do
134 err = sock:send(name .. ": " .. value .. "\r\n") 134 err = sock:send(name .. ": " .. value .. "\r\n")
135 if err then 135 if err then
136 sock:close() 136 sock:close()
137 return err 137 return err
138 end 138 end
139 end 139 end
140 -- end mime part 140 -- end mime part
141 err = sock:send("\r\n") 141 err = sock:send("\r\n")
142 if err then sock:close() end 142 if err then sock:close() end
143 return err 143 return err
144end 144end
145 145
146----------------------------------------------------------------------------- 146-----------------------------------------------------------------------------
147-- Sends connection termination command 147-- Sends connection termination command
148-- Input 148-- Input
149-- sock: server socket 149-- sock: server socket
150-- Returns 150-- Returns
151-- code: server status code, nil if error 151-- code: server status code, nil if error
152-- answer: complete server reply 152-- answer: complete server reply or error message
153----------------------------------------------------------------------------- 153-----------------------------------------------------------------------------
154local send_quit = function(sock) 154local send_quit = function(sock)
155 local code, answer 155 local err = %send_command(sock, "QUIT")
156 local err = %send_command(sock, "QUIT") 156 if err then return nil, err end
157 if not err then 157 local code, answer = %check_answer(sock, 221)
158 code, answer = %get_reply(sock, 221) 158 sock:close()
159 sock:close() 159 return code, answer
160 return code, answer 160end
161 else return nil, err end 161
162end 162-----------------------------------------------------------------------------
163 163-- Sends sender command
164----------------------------------------------------------------------------- 164-- Input
165-- Sends sender command 165-- sock: server socket
166-- Input 166-- sender: e-mail of sender
167-- sock: server socket 167-- Returns
168-- sender: e-mail of sender 168-- code: server status code, nil if error
169-- Returns 169-- answer: complete server reply or error message
170-- code: server status code, nil if error 170-----------------------------------------------------------------------------
171-- answer: complete server reply 171local send_mail = function(sock, sender)
172----------------------------------------------------------------------------- 172 local param = format("FROM:<%s>", sender)
173local send_mail = function(sock, sender) 173 local err = %send_command(sock, "MAIL", param)
174 local param = format("FROM:<%s>", sender) 174 if err then return nil, err end
175 local err = %send_command(sock, "MAIL", param) 175 return %check_answer(sock, 250)
176 if not err then 176end
177 return %get_reply(sock, 250) 177
178 else return nil, err end 178-----------------------------------------------------------------------------
179end 179-- Sends message mime headers and body
180 180-- Input
181----------------------------------------------------------------------------- 181-- sock: server socket
182-- Sends message mime headers and body 182-- mime: table containing all mime headers to be sent
183-- Input 183-- body: message body
184-- sock: server socket 184-- Returns
185-- mime: table containing all mime headers to be sent 185-- code: server status code, nil if error
186-- body: message body 186-- answer: complete server reply or error message
187-- Returns 187-----------------------------------------------------------------------------
188-- code: server status code, nil if error 188local send_data = function (sock, mime, body)
189-- answer: complete server reply 189 local err = %send_command(sock, "DATA")
190----------------------------------------------------------------------------- 190 if err then return nil, err end
191local send_data = function (sock, mime, body) 191 local code, answer = %check_answer(sock, 354)
192 local err = %send_command(sock, "DATA") 192 if not code then return nil, answer end
193 if not err then 193 -- avoid premature end in message body
194 local code, answer = %get_reply(sock, 354) 194 body = gsub(body or "", "\n%.", "\n%.%.")
195 if not code then return nil, answer end 195 -- mark end of message body
196 -- avoid premature end in message body 196 body = body .. "\r\n."
197 body = gsub(body or "", "\n%.", "\n%.%.") 197 err = %send_mime(sock, mime)
198 -- mark end of message body 198 if err then return nil, err end
199 body = body .. "\r\n." 199 err = %try_send(sock, body)
200 err = %send_mime(sock, mime) 200 return %check_answer(sock, 250)
201 if err then return nil, err end 201end
202 err = %puts(sock, body) 202
203 return %get_reply(sock, 250) 203-----------------------------------------------------------------------------
204 else return nil, err end 204-- Sends recipient list command
205end 205-- Input
206 206-- sock: server socket
207----------------------------------------------------------------------------- 207-- rcpt: lua table with recipient list
208-- Sends recipient list command 208-- Returns
209-- Input 209-- code: server status code, nil if error
210-- sock: server socket 210-- answer: complete server reply
211-- rcpt: lua table with recipient list 211-----------------------------------------------------------------------------
212-- Returns 212local send_rcpt = function(sock, rcpt)
213-- code: server status code, nil if error 213 local err, code, answer
214-- answer: complete server reply 214 if type(rcpt) ~= "table" then rcpt = {rcpt} end
215----------------------------------------------------------------------------- 215 for i = 1, getn(rcpt) do
216local send_rcpt = function(sock, rcpt) 216 err = %send_command(sock, "RCPT", format("TO:<%s>", rcpt[i]))
217 local err, code, answer 217 if err then return nil, err end
218 if type(rcpt) ~= "table" then rcpt = {rcpt} end 218 code, answer = %check_answer(sock, {250, 251})
219 for i = 1, getn(rcpt) do 219 if not code then return code, answer end
220 err = %send_command(sock, "RCPT", format("TO:<%s>", rcpt[i])) 220 end
221 if not err then 221 return code, answer
222 code, answer = %get_reply(sock, {250, 251}) 222end
223 if not code then return code, answer end 223
224 else return nil, err end 224-----------------------------------------------------------------------------
225 end 225-- Connection oriented mail functions
226 return code, answer 226-----------------------------------------------------------------------------
227end 227function smtp_connect(server)
228 228 local code, answer
229----------------------------------------------------------------------------- 229 -- connect to server
230-- Sends verify recipient command 230 local sock, err = connect(server, %PORT)
231-- Input 231 if not sock then return nil, err end
232-- sock: server socket 232 sock:timeout(%TIMEOUT)
233-- user: user to be verified 233 -- initial server greeting
234-- Returns 234 code, answer = %check_answer(sock, 220)
235-- code: server status code, nil if error 235 if not code then return nil, answer end
236-- answer: complete server reply 236 -- HELO
237----------------------------------------------------------------------------- 237 code, answer = %send_helo(sock)
238local send_vrfy = function (sock, user) 238 if not code then return nil, answer end
239 local err = %send_command(sock, "VRFY", format("<%s>", user)) 239 return sock
240 if not err then 240end
241 return %get_reply(sock, {250, 251}) 241
242 else return nil, err end 242function smtp_send(sock, from, rcpt, mime, body)
243end 243 local code, answer
244 244 -- MAIL
245----------------------------------------------------------------------------- 245 code, answer = %send_mail(sock, from)
246-- Connection oriented mail functions 246 if not code then return nil, answer end
247----------------------------------------------------------------------------- 247 -- RCPT
248function smtp_connect(server) 248 code, answer = %send_rcpt(sock, rcpt)
249 local code, answer 249 if not code then return nil, answer end
250 -- connect to server 250 -- DATA
251 local sock, err = connect(server, %PORT) 251 return %send_data(sock, mime, body)
252 if not sock then return nil, err end 252end
253 sock:timeout(%TIMEOUT) 253
254 -- initial server greeting 254function smtp_close(sock)
255 code, answer = %get_helo(sock) 255 -- QUIT
256 if not code then return nil, answer end 256 return %send_quit(sock)
257 -- HELO 257end
258 code, answer = %send_helo(sock) 258
259 if not code then return nil, answer end 259-----------------------------------------------------------------------------
260 return sock 260-- Main mail function
261end 261-- Input
262 262-- from: message sender
263function smtp_send(sock, from, rcpt, mime, body) 263-- rcpt: table containing message recipients
264 local code, answer 264-- mime: table containing mime headers
265 -- MAIL 265-- body: message body
266 code, answer = %send_mail(sock, from) 266-- server: smtp server to be used
267 if not code then return nil, answer end 267-- Returns
268 -- RCPT 268-- nil if successfull, error message in case of error
269 code, answer = %send_rcpt(sock, rcpt) 269-----------------------------------------------------------------------------
270 if not code then return nil, answer end 270function smtp_mail(from, rcpt, mime, body, server)
271 -- DATA 271 local sock, err = smtp_connect(server)
272 return %send_data(sock, mime, body) 272 if not sock then return err end
273end 273 local code, answer = smtp_send(sock, from, rcpt, mime, body)
274 274 if not code then return answer end
275function smtp_close(sock) 275 code, answer = smtp_close(sock)
276 -- QUIT 276 if code then return nil end
277 return %send_quit(sock) 277 return answer
278end 278end
279 279
280----------------------------------------------------------------------------- 280--===========================================================================
281-- Main mail function 281-- Compatibility functions
282-- Input 282--===========================================================================
283-- from: message sender 283-----------------------------------------------------------------------------
284-- rcpt: table containing message recipients 284-- Converts a comma separated list into a Lua table with one entry for each
285-- mime: table containing mime headers 285-- list element.
286-- body: message body 286-- Input
287-- server: smtp server to be used 287-- str: string containing the list to be converted
288-- Returns 288-- tab: table to be filled with entries
289-- nil if successfull, error message in case of error 289-- Returns
290----------------------------------------------------------------------------- 290-- a table t, where t.n is the number of elements with an entry t[i]
291function smtp_mail(from, rcpt, mime, body, server) 291-- for each element
292 local sock, err = smtp_connect(server) 292-----------------------------------------------------------------------------
293 if not sock then return err end 293local fill = function(str, tab)
294 local code, answer = smtp_send(sock, from, rcpt, mime, body) 294 gsub(str, "([^%s,]+)", function (w) tinsert(%tab, w) end)
295 if not code then return answer end 295 return tab
296 code, answer = smtp_close(sock) 296end
297 if not code then return answer 297
298 else return nil end 298-----------------------------------------------------------------------------
299end 299-- Client mail function, implementing CGILUA 3.2 interface
300 300-----------------------------------------------------------------------------
301--=========================================================================== 301function mail(msg)
302-- Compatibility functions 302 local rcpt = {}
303--=========================================================================== 303 local mime = {}
304----------------------------------------------------------------------------- 304 mime["Subject"] = msg.subject
305-- Converts a comma separated list into a Lua table with one entry for each 305 mime["To"] = msg.to
306-- list element. 306 mime["From"] = msg.from
307-- Input 307 %fill(msg.to, rcpt)
308-- str: string containing the list to be converted 308 if msg.cc then
309-- tab: table to be filled with entries 309 %fill(msg.cc, rcpt)
310-- Returns 310 mime["Cc"] = msg.cc
311-- a table t, where t.n is the number of elements with an entry t[i] 311 end
312-- for each element 312 if msg.bcc then %fill(msg.bcc, rcpt) end
313----------------------------------------------------------------------------- 313 rcpt.n = nil
314local fill = function(str, tab) 314 return %smtp_mail(msg.from, rcpt, mime, msg.message, msg.mailserver)
315 gsub(str, "([^%s,]+)", function (w) tinsert(%tab, w) end) 315end
316 return tab
317end
318
319-----------------------------------------------------------------------------
320-- Client mail function, implementing CGILUA 3.2 interface
321-----------------------------------------------------------------------------
322function mail(msg)
323 local rcpt = {}
324 local mime = {}
325 mime["Subject"] = msg.subject
326 mime["To"] = msg.to
327 mime["From"] = msg.from
328 %fill(msg.to, rcpt)
329 if msg.cc then
330 %fill(msg.cc, rcpt)
331 mime["Cc"] = msg.cc
332 end
333 if msg.bcc then
334 %fill(msg.bcc, rcpt)
335 end
336 rcpt.n = nil
337 return %smtp_mail(msg.from, rcpt, mime, msg.message, msg.mailserver)
338end