diff options
| author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2000-12-29 22:15:09 +0000 |
|---|---|---|
| committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2000-12-29 22:15:09 +0000 |
| commit | 17c4d1c30544f0ed638879835f179ada96249868 (patch) | |
| tree | 46ca8042ba8fda147f56af61e26ec2ceec41f614 /src/smtp.lua | |
| parent | 6f9d15b66027cef58441549f8ac0603ca42da0ac (diff) | |
| download | luasocket-17c4d1c30544f0ed638879835f179ada96249868.tar.gz luasocket-17c4d1c30544f0ed638879835f179ada96249868.tar.bz2 luasocket-17c4d1c30544f0ed638879835f179ada96249868.zip | |
Initial revision
Diffstat (limited to 'src/smtp.lua')
| -rw-r--r-- | src/smtp.lua | 338 |
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 | ||
| 12 | local TIMEOUT = 180 | ||
| 13 | -- port used for connection | ||
| 14 | local PORT = 25 | ||
| 15 | -- domain used in HELO command. If we are under a CGI, try to get from | ||
| 16 | -- environment | ||
| 17 | local DOMAIN = getenv("SERVER_NAME") | ||
| 18 | if not DOMAIN then | ||
| 19 | DOMAIN = "localhost" | ||
| 20 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 30 | local puts = function(sock, line) | ||
| 31 | local err = sock:send(line .. "\r\n") | ||
| 32 | if err then sock:close() end | ||
| 33 | return err | ||
| 34 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 44 | local 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 | ||
| 51 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 62 | local 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 | ||
| 76 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 87 | local 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) | ||
| 92 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 102 | local get_helo = function(sock) | ||
| 103 | return %get_reply(sock, 220) | ||
| 104 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 114 | local 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 | ||
| 119 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 129 | local 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 | ||
| 144 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 154 | local 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 | ||
| 162 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 173 | local 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 | ||
| 179 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 191 | local 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 | ||
| 205 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 216 | local 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 | ||
| 227 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 238 | local 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 | ||
| 243 | end | ||
| 244 | |||
| 245 | ----------------------------------------------------------------------------- | ||
| 246 | -- Connection oriented mail functions | ||
| 247 | ----------------------------------------------------------------------------- | ||
| 248 | function 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 | ||
| 261 | end | ||
| 262 | |||
| 263 | function smtp_send(sock, from, rcpt, mime, body) | ||
| 264 | local code, answer | ||
| 265 | |||
| 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) | ||
| 273 | end | ||
| 274 | |||
| 275 | function smtp_close(sock) | ||
| 276 | -- QUIT | ||
| 277 | return %send_quit(sock) | ||
| 278 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 291 | function 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 | ||
| 299 | end | ||
| 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 | ----------------------------------------------------------------------------- | ||
| 314 | local fill = function(str, tab) | ||
| 315 | gsub(str, "([^%s,]+)", function (w) tinsert(%tab, w) end) | ||
| 316 | return tab | ||
| 317 | end | ||
| 318 | |||
| 319 | ----------------------------------------------------------------------------- | ||
| 320 | -- Client mail function, implementing CGILUA 3.2 interface | ||
| 321 | ----------------------------------------------------------------------------- | ||
| 322 | function 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) | ||
| 338 | end | ||
