mirror of
				https://github.com/lunarmodules/luasocket.git
				synced 2025-10-31 10:25:55 +01:00 
			
		
		
		
	BUG: multi-line replies were not supported.
Error logic simplified.
This commit is contained in:
		
							
								
								
									
										255
									
								
								src/smtp.lua
									
									
									
									
									
								
							
							
						
						
									
										255
									
								
								src/smtp.lua
									
									
									
									
									
								
							| @@ -27,56 +27,69 @@ end | |||||||
| -- Returns | -- Returns | ||||||
| --   err: message in case of error, nil if successfull | --   err: message in case of error, nil if successfull | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local puts = function(sock, line) | local try_send = function(sock, line) | ||||||
|     local err = sock:send(line .. "\r\n") |     local err = sock:send(line .. "\r\n") | ||||||
| 	if err then sock:close() end | print(line) | ||||||
| 	return err |     if err then sock:close() end | ||||||
|  |     return err | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| -- Tries to receive DOS mode lines. Closes socket on error. | -- Gets command reply, (accepts multiple-line replies) | ||||||
| -- Input | -- Input | ||||||
| --   sock: server socket | --   control: control connection socket | ||||||
| -- Returns | -- Returns | ||||||
| --   line: received string if successfull, nil in case of error | --   answer: whole server reply, nil if error | ||||||
| --   err: error message if any | --   code: reply status code or error message | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local gets = function(sock) | local get_answer = function(control) | ||||||
| 	local line, err = sock:receive("*l") |     local code, lastcode, sep | ||||||
| 	if err then  |     local line, err = control:receive() | ||||||
| 		sock:close()  |     local answer = line | ||||||
| 		return nil, err |     if err then return nil, err end | ||||||
| 	end | print(line) | ||||||
| 	return line |     _,_, code, sep = strfind(line, "^(%d%d%d)(.)") | ||||||
|  |     if not code or not sep then return nil, answer end | ||||||
|  |     if sep == "-" then -- answer is multiline | ||||||
|  |         repeat  | ||||||
|  |             line, err = control:receive() | ||||||
|  |             if err then return nil, err end | ||||||
|  | print(line) | ||||||
|  |             _,_, lastcode, sep = strfind(line, "^(%d%d%d)(.)") | ||||||
|  |             answer = answer .. "\n" .. line | ||||||
|  |         until code == lastcode and sep == " " -- answer ends with same code | ||||||
|  |     end | ||||||
|  |     return answer, tonumber(code) | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| -- Gets a reply from the server and close connection if it is wrong | -- Checks if a message reply code is correct. Closes control connection  | ||||||
|  | -- if not. | ||||||
| -- Input | -- Input | ||||||
| --   sock: server socket | --   control: control connection socket | ||||||
| --   accept: acceptable errorcodes | --   success: table with successfull reply status code | ||||||
| -- Returns | -- Returns | ||||||
| --   code: server reply code. nil if error | --   code: reply code or nil in case of error | ||||||
| --   line: complete server reply message or error message | --   answer: complete server answer or system error message | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local get_reply = function(sock, accept) | local check_answer = function(control, success) | ||||||
|     local line, err = %gets(sock)  |     local answer, code = %get_answer(control) | ||||||
|     if line then |     if not answer then  | ||||||
| 		if type(accept) ~= "table" then accept = {accept} end |         control:close() | ||||||
|         local _,_, code = strfind(line, "^(%d%d%d)") |         return nil, code | ||||||
| 		if not code then return nil, line end |     end | ||||||
| 		code = tonumber(code) |     if type(success) ~= "table" then success = {success} end | ||||||
| 		for i = 1, getn(accept) do |     for i = 1, getn(success) do | ||||||
| 			if code == accept[i] then return code, line end |         if code == success[i] then | ||||||
| 		end |             return code, answer | ||||||
| 		sock:close() |         end | ||||||
| 		return nil, line |     end | ||||||
| 	end |     control:close() | ||||||
|     return nil, err |     return nil, answer | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| -- Sends a command to the server | -- Sends a command to the server (closes sock on error) | ||||||
| -- Input | -- Input | ||||||
| --   sock: server socket | --   sock: server socket | ||||||
| --   command: command to be sent | --   command: command to be sent | ||||||
| @@ -88,19 +101,7 @@ local send_command = function(sock, command, param) | |||||||
|     local line |     local line | ||||||
|     if param then line = command .. " " .. param |     if param then line = command .. " " .. param | ||||||
|     else line = command end |     else line = command end | ||||||
|     return %puts(sock, line) |     return %try_send(sock, line) | ||||||
| end |  | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- |  | ||||||
| -- Gets the initial server greeting |  | ||||||
| -- Input |  | ||||||
| --   sock: server socket |  | ||||||
| -- Returns |  | ||||||
| --   code: server status code, nil if error |  | ||||||
| --   answer: complete server reply |  | ||||||
| ----------------------------------------------------------------------------- |  | ||||||
| local get_helo = function(sock) |  | ||||||
|     return %get_reply(sock, 220) |  | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| @@ -108,14 +109,13 @@ end | |||||||
| -- Input | -- Input | ||||||
| --   sock: server socket | --   sock: server socket | ||||||
| -- Returns | -- Returns | ||||||
| --   code: server status code, nil if error | --   code: server code if ok, nil if error | ||||||
| --   answer: complete server reply | --   answer: complete server reply | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local send_helo = function(sock) | local send_helo = function(sock) | ||||||
|     local err = %send_command(sock, "HELO", %DOMAIN) |     local err = %send_command(sock, "HELO", %DOMAIN) | ||||||
|     if not err then |     if err then return nil, err end | ||||||
|         return %get_reply(sock, 250) |     return %check_answer(sock, 250) | ||||||
| 	else return nil, err end |  | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| @@ -127,20 +127,20 @@ end | |||||||
| --   err: error message if any | --   err: error message if any | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local send_mime = function(sock, mime) | local send_mime = function(sock, mime) | ||||||
| 	local err |     local err | ||||||
| 	mime = mime or {} |     mime = mime or {} | ||||||
| 	-- send all headers |     -- send all headers | ||||||
| 	for name,value in mime do |     for name,value in mime do | ||||||
| 		err = sock:send(name .. ": " .. value .. "\r\n") |         err = sock:send(name .. ": " .. value .. "\r\n") | ||||||
| 		if err then  |         if err then  | ||||||
| 			sock:close() |             sock:close() | ||||||
| 			return err  |             return err  | ||||||
| 		end |         end | ||||||
| 	end |     end | ||||||
| 	-- end mime part |     -- end mime part | ||||||
| 	err = sock:send("\r\n") |     err = sock:send("\r\n") | ||||||
| 	if err then sock:close() end |     if err then sock:close() end | ||||||
| 	return err |     return err | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| @@ -149,16 +149,14 @@ end | |||||||
| --   sock: server socket | --   sock: server socket | ||||||
| -- Returns | -- Returns | ||||||
| --   code: server status code, nil if error | --   code: server status code, nil if error | ||||||
| --   answer: complete server reply | --   answer: complete server reply or error message | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local send_quit = function(sock) | local send_quit = function(sock) | ||||||
| 	local code, answer |  | ||||||
|     local err = %send_command(sock, "QUIT") |     local err = %send_command(sock, "QUIT") | ||||||
|     if not err then |     if err then return nil, err end | ||||||
|         code, answer = %get_reply(sock, 221) |     local code, answer = %check_answer(sock, 221) | ||||||
| 		sock:close() |     sock:close() | ||||||
| 		return code, answer |     return code, answer | ||||||
|     else return nil, err end |  | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| @@ -168,14 +166,13 @@ end | |||||||
| --   sender: e-mail of sender | --   sender: e-mail of sender | ||||||
| -- Returns | -- Returns | ||||||
| --   code: server status code, nil if error | --   code: server status code, nil if error | ||||||
| --   answer: complete server reply | --   answer: complete server reply or error message | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local send_mail = function(sock, sender) | local send_mail = function(sock, sender) | ||||||
|     local param = format("FROM:<%s>", sender) |     local param = format("FROM:<%s>", sender) | ||||||
|     local err = %send_command(sock, "MAIL", param) |     local err = %send_command(sock, "MAIL", param) | ||||||
|     if not err then |     if err then return nil, err end | ||||||
|         return %get_reply(sock, 250) |     return %check_answer(sock, 250) | ||||||
| 	else return nil, err end |  | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| @@ -186,22 +183,21 @@ end | |||||||
| --   body: message body | --   body: message body | ||||||
| -- Returns | -- Returns | ||||||
| --   code: server status code, nil if error | --   code: server status code, nil if error | ||||||
| --   answer: complete server reply | --   answer: complete server reply or error message | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local send_data = function (sock, mime, body) | local send_data = function (sock, mime, body) | ||||||
| 	local err = %send_command(sock, "DATA") |     local err = %send_command(sock, "DATA") | ||||||
|     if not err then |     if err then return nil, err end | ||||||
| 		local code, answer = %get_reply(sock, 354) |     local code, answer = %check_answer(sock, 354) | ||||||
| 		if not code then return nil, answer end |     if not code then return nil, answer end | ||||||
| 		-- avoid premature end in message body |     -- avoid premature end in message body | ||||||
|     	body = gsub(body or "", "\n%.", "\n%.%.") |     body = gsub(body or "", "\n%.", "\n%.%.") | ||||||
| 		-- mark end of message body |     -- mark end of message body | ||||||
|     	body = body .. "\r\n." |     body = body .. "\r\n." | ||||||
|        	err = %send_mime(sock, mime) |     err = %send_mime(sock, mime) | ||||||
| 		if err then return nil, err end |     if err then return nil, err end | ||||||
|         err = %puts(sock, body) |     err = %try_send(sock, body) | ||||||
|         return %get_reply(sock, 250) |     return %check_answer(sock, 250) | ||||||
|     else return nil, err end |  | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| @@ -215,53 +211,36 @@ end | |||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local send_rcpt = function(sock, rcpt) | local send_rcpt = function(sock, rcpt) | ||||||
|     local err, code, answer |     local err, code, answer | ||||||
| 	if type(rcpt) ~= "table" then rcpt = {rcpt} end |     if type(rcpt) ~= "table" then rcpt = {rcpt} end | ||||||
|     for i = 1, getn(rcpt) do |     for i = 1, getn(rcpt) do | ||||||
|         err = %send_command(sock, "RCPT", format("TO:<%s>", rcpt[i])) |         err = %send_command(sock, "RCPT", format("TO:<%s>", rcpt[i])) | ||||||
|         if not err then |         if err then return nil, err end | ||||||
|             code, answer = %get_reply(sock, {250, 251}) |         code, answer = %check_answer(sock, {250, 251}) | ||||||
| 			if not code then return code, answer end |         if not code then return code, answer end | ||||||
|         else return nil, err end |  | ||||||
|     end |     end | ||||||
|     return code, answer |     return code, answer | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- |  | ||||||
| -- Sends verify recipient command |  | ||||||
| -- Input |  | ||||||
| --   sock: server socket |  | ||||||
| --   user: user to be verified |  | ||||||
| -- Returns |  | ||||||
| --   code: server status code, nil if error |  | ||||||
| --   answer: complete server reply |  | ||||||
| ----------------------------------------------------------------------------- |  | ||||||
| local send_vrfy = function (sock, user) |  | ||||||
|     local err = %send_command(sock, "VRFY", format("<%s>", user)) |  | ||||||
|     if not err then |  | ||||||
|         return %get_reply(sock, {250, 251}) |  | ||||||
| 	else return nil, err end |  | ||||||
| end |  | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| -- Connection oriented mail functions | -- Connection oriented mail functions | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| function smtp_connect(server) | function smtp_connect(server) | ||||||
| 	local code, answer |     local code, answer | ||||||
|     -- connect to server |     -- connect to server | ||||||
|     local sock, err = connect(server, %PORT) |     local sock, err = connect(server, %PORT) | ||||||
|     if not sock then return nil, err end |     if not sock then return nil, err end | ||||||
|     sock:timeout(%TIMEOUT) |     sock:timeout(%TIMEOUT) | ||||||
|     -- initial server greeting |     -- initial server greeting | ||||||
|     code, answer = %get_helo(sock) |     code, answer = %check_answer(sock, 220) | ||||||
|     if not code then return nil, answer end |     if not code then return nil, answer end | ||||||
|     -- HELO |     -- HELO | ||||||
|     code, answer = %send_helo(sock) |     code, answer = %send_helo(sock) | ||||||
|     if not code then return nil, answer end |     if not code then return nil, answer end | ||||||
| 	return sock |     return sock | ||||||
| end | end | ||||||
|  |  | ||||||
| function smtp_send(sock, from, rcpt, mime, body) | function smtp_send(sock, from, rcpt, mime, body) | ||||||
| 	local code, answer |     local code, answer | ||||||
|     -- MAIL |     -- MAIL | ||||||
|     code, answer = %send_mail(sock, from) |     code, answer = %send_mail(sock, from) | ||||||
|     if not code then return nil, answer end |     if not code then return nil, answer end | ||||||
| @@ -289,13 +268,13 @@ end | |||||||
| --   nil if successfull, error message in case of error | --   nil if successfull, error message in case of error | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| function smtp_mail(from, rcpt, mime, body, server) | function smtp_mail(from, rcpt, mime, body, server) | ||||||
| 	local sock, err = smtp_connect(server) |     local sock, err = smtp_connect(server) | ||||||
| 	if not sock then return err end |     if not sock then return err end | ||||||
| 	local code, answer = smtp_send(sock, from, rcpt, mime, body) |     local code, answer = smtp_send(sock, from, rcpt, mime, body) | ||||||
| 	if not code then return answer end |     if not code then return answer end | ||||||
| 	code, answer = smtp_close(sock) |     code, answer = smtp_close(sock) | ||||||
| 	if not code then return answer |     if code then return nil end | ||||||
| 	else return nil end |     return answer | ||||||
| end | end | ||||||
|  |  | ||||||
| --=========================================================================== | --=========================================================================== | ||||||
| @@ -312,27 +291,25 @@ end | |||||||
| --   for each element | --   for each element | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| local fill = function(str, tab) | local fill = function(str, tab) | ||||||
| 	gsub(str, "([^%s,]+)", function (w) tinsert(%tab, w) end) |     gsub(str, "([^%s,]+)", function (w) tinsert(%tab, w) end) | ||||||
| 	return tab |     return tab | ||||||
| end | end | ||||||
|  |  | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| -- Client mail function, implementing CGILUA 3.2 interface | -- Client mail function, implementing CGILUA 3.2 interface | ||||||
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ||||||
| function mail(msg) | function mail(msg) | ||||||
|   local rcpt = {} |     local rcpt = {} | ||||||
|   local mime = {} |     local mime = {} | ||||||
|   mime["Subject"] = msg.subject |     mime["Subject"] = msg.subject | ||||||
|   mime["To"] = msg.to |     mime["To"] = msg.to | ||||||
|   mime["From"] = msg.from |     mime["From"] = msg.from | ||||||
|   %fill(msg.to, rcpt) |     %fill(msg.to, rcpt) | ||||||
|   if msg.cc then  |     if msg.cc then  | ||||||
|     %fill(msg.cc, rcpt)  |         %fill(msg.cc, rcpt)  | ||||||
|     mime["Cc"] = msg.cc |         mime["Cc"] = msg.cc | ||||||
|   end |     end | ||||||
|   if msg.bcc then |     if msg.bcc then %fill(msg.bcc, rcpt) end | ||||||
|     %fill(msg.bcc, rcpt) |     rcpt.n = nil | ||||||
|   end |     return %smtp_mail(msg.from, rcpt, mime, msg.message, msg.mailserver) | ||||||
|   rcpt.n = nil |  | ||||||
|   return %smtp_mail(msg.from, rcpt, mime, msg.message, msg.mailserver) |  | ||||||
| end | end | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user