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