aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-03-19 05:04:03 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-03-19 05:04:03 +0000
commite896454e6c7fb574f405729e1ce94c223578e078 (patch)
treeb9cbcb092ee8bc16fe21a9f6ae6bf07ed052fd50
parent2c160627e51650f98d6ef01ae36bb86d6e91045f (diff)
downloadluasocket-e896454e6c7fb574f405729e1ce94c223578e078.tar.gz
luasocket-e896454e6c7fb574f405729e1ce94c223578e078.tar.bz2
luasocket-e896454e6c7fb574f405729e1ce94c223578e078.zip
Seems to be working.
-rw-r--r--src/ltn12.lua10
-rw-r--r--src/mime.lua28
-rw-r--r--src/smtp.lua138
-rw-r--r--src/tp.lua4
4 files changed, 111 insertions, 69 deletions
diff --git a/src/ltn12.lua b/src/ltn12.lua
index f43e975..ef6247d 100644
--- a/src/ltn12.lua
+++ b/src/ltn12.lua
@@ -16,7 +16,7 @@ local function second(a, b)
16 return b 16 return b
17end 17end
18 18
19local function skip(a, b, c) 19local function shift(a, b, c)
20 return b, c 20 return b, c
21end 21end
22 22
@@ -69,7 +69,7 @@ function source.file(handle, io_err)
69 if not chunk then handle:close() end 69 if not chunk then handle:close() end
70 return chunk 70 return chunk
71 end 71 end
72 else source.error(io_err or "unable to open file") end 72 else return source.error(io_err or "unable to open file") end
73end 73end
74 74
75-- turns a fancy source into a simple source 75-- turns a fancy source into a simple source
@@ -114,6 +114,7 @@ function source.chain(src, f)
114 local co = coroutine.create(function() 114 local co = coroutine.create(function()
115 while true do 115 while true do
116 local chunk, err = src() 116 local chunk, err = src()
117 if err then return nil, err end
117 local filtered = f(chunk) 118 local filtered = f(chunk)
118 local done = chunk and "" 119 local done = chunk and ""
119 while true do 120 while true do
@@ -121,11 +122,10 @@ function source.chain(src, f)
121 if filtered == done then break end 122 if filtered == done then break end
122 filtered = f(done) 123 filtered = f(done)
123 end 124 end
124 if not chunk then return nil, err end
125 end 125 end
126 end) 126 end)
127 return function() 127 return function()
128 return skip(coroutine.resume(co)) 128 return shift(coroutine.resume(co))
129 end 129 end
130end 130end
131 131
@@ -141,7 +141,7 @@ function source.cat(...)
141 end 141 end
142 end) 142 end)
143 return source.simplify(function() 143 return source.simplify(function()
144 return second(coroutine.resume(co)) 144 return shift(coroutine.resume(co))
145 end) 145 end)
146end 146end
147 147
diff --git a/src/mime.lua b/src/mime.lua
index 6db832d..8c2a5c0 100644
--- a/src/mime.lua
+++ b/src/mime.lua
@@ -6,9 +6,9 @@ setmetatable(mime, { __index = _G })
6setfenv(1, mime) 6setfenv(1, mime)
7 7
8-- encode, decode and wrap algorithm tables 8-- encode, decode and wrap algorithm tables
9local et = {} 9encodet = {}
10local dt = {} 10decodet = {}
11local wt = {} 11wrapt = {}
12 12
13-- creates a function that chooses a filter by name from a given table 13-- creates a function that chooses a filter by name from a given table
14local function choose(table) 14local function choose(table)
@@ -20,40 +20,40 @@ local function choose(table)
20end 20end
21 21
22-- define the encoding filters 22-- define the encoding filters
23et['base64'] = function() 23encodet['base64'] = function()
24 return ltn12.filter.cycle(b64, "") 24 return ltn12.filter.cycle(b64, "")
25end 25end
26 26
27et['quoted-printable'] = function(mode) 27encodet['quoted-printable'] = function(mode)
28 return ltn12.filter.cycle(qp, "", 28 return ltn12.filter.cycle(qp, "",
29 (mode == "binary") and "=0D=0A" or "\13\10") 29 (mode == "binary") and "=0D=0A" or "\13\10")
30end 30end
31 31
32-- define the decoding filters 32-- define the decoding filters
33dt['base64'] = function() 33decodet['base64'] = function()
34 return ltn12.filter.cycle(unb64, "") 34 return ltn12.filter.cycle(unb64, "")
35end 35end
36 36
37dt['quoted-printable'] = function() 37decodet['quoted-printable'] = function()
38 return ltn12.filter.cycle(unqp, "") 38 return ltn12.filter.cycle(unqp, "")
39end 39end
40 40
41-- define the line-wrap filters 41-- define the line-wrap filters
42wt['text'] = function(length) 42wrapt['text'] = function(length)
43 length = length or 76 43 length = length or 76
44 return ltn12.filter.cycle(wrp, length, length) 44 return ltn12.filter.cycle(wrp, length, length)
45end 45end
46wt['base64'] = wt['text'] 46wrapt['base64'] = wrapt['text']
47 47
48wt['quoted-printable'] = function() 48wrapt['quoted-printable'] = function()
49 return ltn12.filter.cycle(qpwrp, 76, 76) 49 return ltn12.filter.cycle(qpwrp, 76, 76)
50end 50end
51 51
52-- function that choose the encoding, decoding or wrap algorithm 52-- function that choose the encoding, decoding or wrap algorithm
53encode = choose(et) 53encode = choose(encodet)
54decode = choose(dt) 54decode = choose(decodet)
55-- there is different because there is a default wrap filter 55-- it's different because there is a default wrap filter
56local cwt = choose(wt) 56local cwt = choose(wrapt)
57function wrap(mode_or_length, length) 57function wrap(mode_or_length, length)
58 if type(mode_or_length) ~= "string" then 58 if type(mode_or_length) ~= "string" then
59 length = mode_or_length 59 length = mode_or_length
diff --git a/src/smtp.lua b/src/smtp.lua
index 0bebce3..d256388 100644
--- a/src/smtp.lua
+++ b/src/smtp.lua
@@ -10,23 +10,27 @@ socket.smtp = smtp
10setmetatable(smtp, { __index = _G }) 10setmetatable(smtp, { __index = _G })
11setfenv(1, smtp) 11setfenv(1, smtp)
12 12
13-- default server used to send e-mails
14SERVER = "localhost"
13-- default port 15-- default port
14PORT = 25 16PORT = 25
15-- domain used in HELO command and default sendmail 17-- domain used in HELO command and default sendmail
16-- If we are under a CGI, try to get from environment 18-- If we are under a CGI, try to get from environment
17DOMAIN = os.getenv("SERVER_NAME") or "localhost" 19DOMAIN = os.getenv("SERVER_NAME") or "localhost"
18-- default server used to send e-mails 20-- default time zone (means we don't know)
19SERVER = "localhost" 21ZONE = "-0000"
20 22
21function stuff() 23function stuff()
22 return ltn12.filter.cycle(dot, 2) 24 return ltn12.filter.cycle(dot, 2)
23end 25end
24 26
25local function skip(a, b, c) 27local function shift(a, b, c)
26 return b, c 28 return b, c
27end 29end
28 30
31-- send message or throw an exception
29function psend(control, mailt) 32function psend(control, mailt)
33 socket.try(control:check("2.."))
30 socket.try(control:command("EHLO", mailt.domain or DOMAIN)) 34 socket.try(control:command("EHLO", mailt.domain or DOMAIN))
31 socket.try(control:check("2..")) 35 socket.try(control:check("2.."))
32 socket.try(control:command("MAIL", "FROM:" .. mailt.from)) 36 socket.try(control:command("MAIL", "FROM:" .. mailt.from))
@@ -34,11 +38,12 @@ function psend(control, mailt)
34 if type(mailt.rcpt) == "table" then 38 if type(mailt.rcpt) == "table" then
35 for i,v in ipairs(mailt.rcpt) do 39 for i,v in ipairs(mailt.rcpt) do
36 socket.try(control:command("RCPT", "TO:" .. v)) 40 socket.try(control:command("RCPT", "TO:" .. v))
41 socket.try(control:check("2.."))
37 end 42 end
38 else 43 else
39 socket.try(control:command("RCPT", "TO:" .. mailt.rcpt)) 44 socket.try(control:command("RCPT", "TO:" .. mailt.rcpt))
45 socket.try(control:check("2.."))
40 end 46 end
41 socket.try(control:check("2.."))
42 socket.try(control:command("DATA")) 47 socket.try(control:command("DATA"))
43 socket.try(control:check("3..")) 48 socket.try(control:check("3.."))
44 socket.try(control:source(ltn12.source.chain(mailt.source, stuff()))) 49 socket.try(control:source(ltn12.source.chain(mailt.source, stuff())))
@@ -48,6 +53,7 @@ function psend(control, mailt)
48 socket.try(control:check("2..")) 53 socket.try(control:check("2.."))
49end 54end
50 55
56-- returns a hopefully unique mime boundary
51local seqno = 0 57local seqno = 0
52local function newboundary() 58local function newboundary()
53 seqno = seqno + 1 59 seqno = seqno + 1
@@ -55,62 +61,98 @@ local function newboundary()
55 math.random(0, 99999), seqno) 61 math.random(0, 99999), seqno)
56end 62end
57 63
58local function sendmessage(mesgt) 64-- sendmessage forward declaration
59 -- send headers 65local sendmessage
66
67-- yield multipart message body from a multipart message table
68local function sendmultipart(mesgt)
69 local bd = newboundary()
70 -- define boundary and finish headers
71 coroutine.yield('content-type: multipart/mixed; boundary="' ..
72 bd .. '"\r\n\r\n')
73 -- send preamble
74 if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) end
75 -- send each part separated by a boundary
76 for i, m in ipairs(mesgt.body) do
77 coroutine.yield("\r\n--" .. bd .. "\r\n")
78 sendmessage(m)
79 end
80 -- send last boundary
81 coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
82 -- send epilogue
83 if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) end
84end
85
86-- yield message body from a source
87local function sendsource(mesgt)
88 -- set content-type if user didn't override
89 if not mesgt.headers or not mesgt.headers["content-type"] then
90 coroutine.yield('content-type: text/plain; charset="iso-88591"\r\n')
91 end
92 -- finish headers
93 coroutine.yield("\r\n")
94 -- send body from source
95 while true do
96 local chunk, err = mesgt.body()
97 if err then coroutine.yield(nil, err)
98 elseif chunk then coroutine.yield(chunk)
99 else break end
100 end
101end
102
103-- yield message body from a string
104local function sendstring(mesgt)
105 -- set content-type if user didn't override
106 if not mesgt.headers or not mesgt.headers["content-type"] then
107 coroutine.yield('content-type: text/plain; charset="iso-88591"\r\n')
108 end
109 -- finish headers
110 coroutine.yield("\r\n")
111 -- send body from string
112 coroutine.yield(mesgt.body)
113
114end
115
116-- yield the headers one by one
117local function sendheaders(mesgt)
60 if mesgt.headers then 118 if mesgt.headers then
61 for i,v in pairs(mesgt.headers) do 119 for i,v in pairs(mesgt.headers) do
62 coroutine.yield(i .. ':' .. v .. "\r\n") 120 coroutine.yield(i .. ':' .. v .. "\r\n")
63 end 121 end
64 end 122 end
65 -- deal with multipart 123end
66 if type(mesgt.body) == "table" then 124
67 local bd = newboundary() 125-- message source
68 -- define boundary and finish headers 126function sendmessage(mesgt)
69 coroutine.yield('mime-version: 1.0\r\n') 127 sendheaders(mesgt)
70 coroutine.yield('content-type: multipart/mixed; boundary="' .. 128 if type(mesgt.body) == "table" then sendmultipart(mesgt)
71 bd .. '"\r\n\r\n') 129 elseif type(mesgt.body) == "function" then sendsource(mesgt)
72 -- send preamble 130 else sendstring(mesgt) end
73 if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) end 131end
74 -- send each part separated by a boundary 132
75 for i, m in ipairs(mesgt.body) do 133-- set defaul headers
76 coroutine.yield("\r\n--" .. bd .. "\r\n") 134local function adjustheaders(mesgt)
77 sendmessage(m) 135 mesgt.headers = mesgt.headers or {}
78 end 136 mesgt.headers["mime-version"] = "1.0"
79 -- send last boundary 137 mesgt.headers["date"] = mesgt.headers["date"] or
80 coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") 138 os.date("%a, %d %b %Y %H:%M:%S") .. (mesgt.zone or ZONE)
81 -- send epilogue 139 mesgt.headers["x-mailer"] = mesgt.headers["x-mailer"] or socket.version
82 if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) end
83 -- deal with a source
84 elseif type(mesgt.body) == "function" then
85 -- finish headers
86 coroutine.yield("\r\n")
87 while true do
88 local chunk, err = mesgt.body()
89 if err then return nil, err
90 elseif chunk then coroutine.yield(chunk)
91 else break end
92 end
93 -- deal with a simple string
94 else
95 -- finish headers
96 coroutine.yield("\r\n")
97 coroutine.yield(mesgt.body)
98 end
99end 140end
100 141
101function message(mesgt) 142function message(mesgt)
143 adjustheaders(mesgt)
144 -- create and return message source
102 local co = coroutine.create(function() sendmessage(mesgt) end) 145 local co = coroutine.create(function() sendmessage(mesgt) end)
103 return function() return skip(coroutine.resume(co)) end 146 return function() return shift(coroutine.resume(co)) end
104end 147end
105 148
106function send(mailt) 149function send(mailt)
107 local control, err = socket.tp.connect(mailt.server or SERVER, 150 local c, e = socket.tp.connect(mailt.server or SERVER, mailt.port or PORT)
108 mailt.port or PORT) 151 if not c then return nil, e end
109 if not control then return nil, err end 152 local s, e = pcall(psend, c, mailt)
110 local status, err = pcall(psend, control, mailt) 153 c:close()
111 control:close() 154 if s then return true
112 if status then return true 155 else return nil, e end
113 else return nil, err end
114end 156end
115 157
116return smtp 158return smtp
diff --git a/src/tp.lua b/src/tp.lua
index 3912fab..9365255 100644
--- a/src/tp.lua
+++ b/src/tp.lua
@@ -82,7 +82,7 @@ function metatable.__index:source(src, instr)
82 while true do 82 while true do
83 local chunk, err = src() 83 local chunk, err = src()
84 if not chunk then return not err, err end 84 if not chunk then return not err, err end
85 local ret, err = try_sending(self.sock, chunk) 85 local ret, err = self.sock:send(chunk)
86 if not ret then return nil, err end 86 if not ret then return nil, err end
87 end 87 end
88end 88end
@@ -95,7 +95,7 @@ end
95-- connect with server and return sock object 95-- connect with server and return sock object
96function connect(host, port) 96function connect(host, port)
97 local sock, err = socket.connect(host, port) 97 local sock, err = socket.connect(host, port)
98 if not sock then return nil, message end 98 if not sock then return nil, err end
99 sock:settimeout(TIMEOUT) 99 sock:settimeout(TIMEOUT)
100 return setmetatable({sock = sock}, metatable) 100 return setmetatable({sock = sock}, metatable)
101end 101end