From 58096449c6044b7aade5cd41cfd71c6bec1d273d Mon Sep 17 00:00:00 2001
From: Diego Nehab
FTP (File Transfer Protocol) is a protocol used to transfer files
-between hosts. The module ftp.lua offers simple FTP support,
-allowing applications to download and upload files, and list directory
-contents. The implementation conforms to
+between hosts. The module ftp.lua offers simple FTP support.
+Applications can easily download and upload files.
+The implementation conforms to
RFC 959.
+High level functions are provided supporting the most common operations.
+These high level functions are implemented on top of a lower level
+interface. By using the low-level interface, users can easily create their
+own functions to access any operation supported by the FTP
+protocol. For that, check the implementation.
+
+To use some of the functions in this module, a good understanding of
+
+LTN012, Filters sources and sinks is necessary.
+
+The following constants can be set to control the default behaviour of
+the FTP module:
+
-socket.ftp.get(url)
-Downloads an URL from a FTP server.
+The get function has two forms. The simple form has fixed
+functionality: it downloads the contents of a URL and returns it as a
+string. The generic form allows a lot more control, as explained
+below.
-The function can be called either directly with a url
-or with a request table.
-Fields passed explicitly in the request table override those
-present in the url.
-
-The parameter type accepts values 'a' (ASCII, the
-default), 'i' (binary) or 'd' (directory listing) and
-determines the transfer type. If <path> ends with a
-'/' or type is 'd', a directory listing of
-<path> is returned. If no user is provided in the
-url or explicitly, the function tries to log in as user
-'anonymous'.
+If the argument of the get function is a table, the function
+expects at least the fields host, sink, and one of
+argument or path (argument takes
+precedence). Host is the server to connect to. Sink is
+the LTN12 sink that will receive the downloaded data. Argument or
+path give the target path to the resource in the server. The
+optional arguments are the following:
-If successful, the function returns
-the file content as a string. In case of error, the function returns
-nil and an error message describing the error.
+If successful, the simple version returns the URL contents as a
+string, and the generic function returns 1. In case of error, both
+functions return nil and an error message describing the
+error.
-socket.ftp.get_cb{
-Same as get, but the library returns
-the content of the downloaded file to the receive callback
-content_cb.
-
-Note: for more information on callbacks, refer to
-Streaming with callbacks.
-
-socket.ftp.put(url, content)
-Upload a file to a FTP server.
+The put function has two forms. The simple form has fixed
+functionality: it uploads a string of content into a URL. The generic form
+allows a lot more control, as explained below.
-The function can be called directly with a
-url and content parameters, or with a
-request table.
-Values passed explicitly in the request table override those present in
-the url. The parameter type accept values
-'a' (ASCII, the default) or 'i' (binary) and
-determines the transfer type. If no user is provided, the
-function tries to log in as 'anonymous'.
+If the argument of the put function is a table, the function
+expects at least the fields host, source, and one of
+argument or path (argument takes
+precedence). Host is the server to connect to. Source is
+the LTN12 source that will provide the contents to be uploaded.
+Argument or
+path give the target path to the resource in the server. The
+optional arguments are the following:
-If successful, the function returns 1. In case of error, the
-function returns nil followed by a string describing the error.
+Both functions return 1 if successful, or nil and an error
+message describing the reason for failure.
-socket.ftp.put_cb{
-Same as put, but the
-library obtains the contents of the file to be uploaded using the send
-callback content_cb.
-
-Note: for more information on callbacks, refer to
-Streaming with callbacks.
- The LTN12 module implements the ideas described in
+
+LTN012, Filters sources and sinks. This manual simply describe the
+functions. Please refer to the LTN for a deeper explanation of the
+functionality provided by this module.
+
+ltn12.filter.chain(filter1, filter2
+[, ... filterN])
+
+Returns a filter that passes all data it receives through each of a
+series of given filters.
+
+Filter1 to filterN are simple
+filters.
+
+The function returns the chained filter.
+
+The nesting of filters can be arbritrary. For instance, the useless filter
+below doesn't do anything but return the data that was passed to it,
+unaltered.
+
+ltn12.filter.cycle(low [, ctx, extra])
+
+Returns a high-level filter that cycles though a low-level filter by
+passing it each chunk and updating a context between calls.
+
+Low is the low-level filter to be cycled,
+ctx is the initial context and extra is any extra
+argument the low-level filter might take.
+
+The function returns the high-level filter.
+
+ltn12.pump.all(source, sink)
+
+Pumps all data from a source to a sink.
+
+If successful, the function returns a value that evaluates to
+true. In case
+of error, the function returns a false value, followed by an error message.
+
+ltn12.pump.step(source, sink)
+
+Pumps one chunk of data from a source to a sink.
+
+If successful, the function returns a value that evaluates to
+true. In case
+of error, the function returns a false value, followed by an error message.
+
+ltn12.sink.chain(filter, sink)
+
+Creates a new sink that passes data through a filter before sending
+it to a given sink.
+
+The function returns the new sink.
+
+ltn12.sink.error(message)
+
+Creates and returns a sink that aborts transmission with an error
+message.
+
+ltn12.sink.file(handle, message)
+
+Creates a sink that sends data to a file.
+
+Handle is a file handle. If handle is nil,
+message should give the reason for failure.
+
+The function returns a sink that sends all data to the given handle
+and closes the file when done, or a sink that aborts the transmission with
+an error message
+
+In the following example, notice how the prototype is designed to
+fit nicely with the io.open function.
+
+ltn12.sink.null()
+
+Returns a sink that ignores all data it receives.
+
+ltn12.sink.simplify(sink)
+
+Creates and returns a simple sink given a fancy sink.
+
+ltn12.sink.table([table])
+
+Creates a sink that stores all chunks in a table. The chunks can later be
+efficiently concatenated into a single string.
+
+Table is used to hold the chunks. If
+nil, the function creates its own table.
+
+The function returns the sink and the table.
+
+ltn12.source.cat(source1 [, source2, ...,
+sourceN])
+
+Creates a new source that produces the concatenation of the data produced
+by a number of sources.
+
+Source1 to sourceN are the original
+sources.
+
+The function returns the new source.
+
+ltn12.source.chain(source, filter)
+
+Creates a new source that passes data through a filter
+before returning it.
+
+The function returns the new source.
+
+ltn12.source.empty()
+
+Creates and returns an empty source.
+
+ltn12.source.error(message)
+
+Creates and returns a source that aborts transmission with an error
+message.
+
+ltn12.source.file(handle, message)
+
+Creates a source that produces the contents of a file.
+
+Handle is a file handle. If handle is nil,
+message should give the reason for failure.
+
+The function returns a source that reads chunks of data from
+given handle and returns it to the user,
+closing the file when done, or a source that aborts the transmission with
+an error message
+
+In the following example, notice how the prototype is designed to
+fit nicely with the io.open function.
+
+ltn12.source.simplify(source)
+
+Creates and returns a simple source given a fancy source.
+
+ltn12.source.string(string)
+
+Creates and returns a source that produces the contents of a
+string, chunk by chunk.
+
+The MIME module offers filters that apply and remove common
+content transfer encodings, such as Base64 and Quoted-Printable.
+It also provides functions to break text into lines and change
+the end-of-line convention.
+MIME is described mainly in
+RFC 2045,
+2046,
+2047,
+2048 and
+2049.
+
+All functionality provided by the MIME module
+follows the ideas presented in
+
+LTN012, Filters sources and sinks.
+
+mime.normalize([marker])
+
+Converts most common end-of-line markers to a specific given marker.
+
+Marker is the new marker. It defaults to CRLF, the canonic
+end-of-line marker defined by the MIME standard.
+
+The function returns a filter that performs the conversion.
+
+Note: There is no perfect solution to this problem. Different end-of-line
+markers are an evil that will probably plague developers forever.
+This function, however, will work perfectly for text created with any of
+the most common end-of-line markers, i.e. the MacOS (CR), the Unix (LF),
+or the DOS (CRLF) conventions. Even if the data has mixed end-of-line
+markers, the function will still work well, although it doesn't
+guarantee that the number of empty lines will be correct.
+
+mime.decode("base64")
+Returns a filter that decodes data from a given transfer content
+encoding.
+
+The function returns the created filter.
+
+mime.encode("base64")
+Returns a filter that encodes data according to a given transfer content
+encoding.
+
+In the Quoted-Printable case, the user can specify whether the data is
+textual or binary, by passing the mode strings "text" or
+"binary". Mode defaults to "text".
+
+The function returns the created filter.
+
+Although both transfer content encodings specify a limit for the line
+length, the encoding filters do not break text into lines (for
+added flexibility).
+Below is a filter that converts binary data to the Base64 transfer content
+encoding and breaks it into lines of the correct size.
+
+Note: Text data has to be converted to canonic form
+before being encoded.
+
+mime.wrap("text" [, length])
+Returns a filter that breaks data into lines.
+
+The "text" line-wrap filter simply breaks text into lines by
+inserting CRLF end-of-line markers at appropriate positions.
+Length defaults 76.
+The "base64" line-wrap filter works just like the default
+"text" line-wrap filter with default length.
+The function can also wrap "quoted-printable" lines, taking care
+not to break lines in the middle of an escaped character. In that case, the
+line length is fixed at 76.
+
+The function returns the created filter.
+
+For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following:
+
+Note: To break into lines with a different end-of-line convention, apply
+a normalization filter after the line break filter.
+
+A, B = mime.b64(C [, D])
+
+Low-level filter to perform Base64 encoding.
+
+A is the encoded version of the largest prefix of
+C..D
+that can be encoded unambiguously. B has the remaining bytes of
+C..D, before encoding.
+If D is nil, A is padded with
+the encoding of the remaining bytes of C.
+
+Note: The simplest use of this function is to encode a string into it's
+Base64 transfer content encoding. Notice the extra parenthesis around the
+call to mime.b64, to discard the second return value.
+
+A, B = mime.eol(C [, D, marker])
+
+Low-level filter to perform end-of-line marker translation.
+For each chunk, the function needs to know if the last character of the
+previous chunk could be part of an end-of-line marker or not. This is the
+context the function receives besides the chunk. An updated version of
+the context is returned after each new chunk.
+
+A is the translated version of D. C is the
+ASCII value of the last character of the previous chunk, if it was a
+candidate for line break, or 0 otherwise.
+B is the same as C, but for the current
+chunk. If D is nil, A includes a
+new end-of-line marker, depending on C.
+Marker gives the new end-of-line marker and defaults to CRLF.
+
+A, B = mime.qp(C [, D, marker])
+
+Low-level filter to perform Quoted-Printable encoding.
+
+A is the encoded version of the largest prefix of
+C..D
+that can be encoded unambiguously. B has the remaining bytes of
+C..D, before encoding.
+If D is nil, A is padded with
+the encoding of the remaining bytes of C.
+Throughout encoding, occurences of CRLF are replaced by the
+marker, which itself defaults to CRLF.
+
+Note: The simplest use of this function is to encode a string into it's
+Quoted-Printable transfer content encoding.
+Notice the extra parenthesis around the call to mime.qp, to discard the second return value.
+
+A, m = mime.qpwrp(n [, B, length])
+
+Low-level filter to break Quoted-Printable text into lines.
+
+A is a copy of B, broken into lines of at most
+length bytes (defaults to 76).
+'n' should tell how many bytes are left for the first
+line of B and 'm' returns the number of bytes
+left in the last line of A.
+
+Note: Besides breaking text into lines, this function makes sure the line
+breaks don't fall in the middle of an escaped character combination. Also,
+this function only breaks lines that are bigger than length bytes.
+
+A, B = mime.unb64(C [, D])
+
+Low-level filter to perform Base64 decoding.
+
+A is the decoded version of the largest prefix of
+C..D
+that can be decoded unambiguously. B has the remaining bytes of
+C..D, before decoding.
+If D is nil, A is the empty string
+and B returns whatever couldn't be decoded.
+
+Note: The simplest use of this function is to decode a string from it's
+Base64 transfer content encoding.
+Notice the extra parenthesis around the call to mime.unqp, to discard the second return value.
+
+A, B = mime.unqp(C [, D])
+
+Low-level filter to remove the Quoted-Printable transfer content encoding
+from data.
+
+A is the decoded version of the largest prefix of
+C..D
+that can be decoded unambiguously. B has the remaining bytes of
+C..D, before decoding.
+If D is nil, A is augmented with
+the encoding of the remaining bytes of C.
+
+Note: The simplest use of this function is to decode a string from it's
+Quoted-Printable transfer content encoding.
+Notice the extra parenthesis around the call to mime.unqp, to discard the second return value.
+
+A, m = mime.wrp(n [, B, length])
+
+Low-level filter to break text into lines with CRLF marker.
+Text is assumed to be in the normalize form.
+
+A is a copy of B, broken into lines of at most
+length bytes (defaults to 76).
+'n' should tell how many bytes are left for the first
+line of B and 'm' returns the number of bytes
+left in the last line of A.
+
+Note: This function only breaks lines that are bigger than
+length bytes. The resulting line length does not include the CRLF
+marker.
+
-The smtp.lua module provides functionality to send e-mail
+ The smtp.lua module provides functionality to send e-mail
messages. The implementation conforms to the Simple Mail Transfer Protocol,
RFC 2821.
-The other RFC of interest in this implementation is
-RFC 2822,
+Another RFC of interest is RFC 2822,
which governs the Internet Message Format.
+Multipart messages (those that contain attatchments) are part
+of the MIME standard, but described mainly
+in RFC
+2046
- In the description below, good understanding of LTN012, Filters
+sources and sinks and the MIME module is
+assumed. In fact, the SMTP module was the main reason for their
+creation.
MIME headers are represented as a Lua table in the form:
@@ -78,29 +85,56 @@ Note: MIME headers are independent of order. Therefore, there is no problem
in representing them in a Lua table.
+The following constants can be set to control the default behaviour of
+the SMTP module:
+
-socket.smtp.mail{
+smtp.send{
-Sends a message to a recipient list.
+Sends a message to a recipient list. Since sending messages is not as
+simple as downloading an URL from a FTP or HTTP server, this function
+doesn't have a simple interface. However, see the
+message source factory for
+a very powerful way to define the message contents.
-Rcpt is a Lua table with one entry for each recipient, or a string
+The sender is given by the e-mail address in the from field.
+Rcpt is a Lua table with one entry for each recipient e-mail
+address, or a string
in case there is just one recipient.
-The sender is given by the e-mail address from.
-The message is composed by the optional MIME Headers headers
-and text body. The message is sent using the server
-server.
+The contents of the message are given by a LTN12 source. Several
+arguments are optional:
+
-[ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][type=a|i|d]
+[ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][type=a|i]
+
+
+
-socket.ftp.get{
- url = string,
- type = string,
- user = string,
- password = string
+ftp.get(url)
+ftp.get{
+ host = string,
+ sink = LTN12 sink,
+ argument or path = string,
+ [user = string,]
+ [password = string]
+ [command = string,]
+ [port = number,]
+ [type = string,]
+ [step = LTN12 pump step],
}
+
--- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br",
--- go to directory "pub/lua" and get file "lua.tar.gz" as binary.
-f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
+-- load the ftp support
+local ftp = require("ftp")
-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br",
--- go to director "pub" and retrieve directory listing of directory "lua"
-f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua;type=d")
-
--- Log as user "diego", password "nehab", on server "ftp.tecgraf.puc-rio.br",
--- go to directory "tec/luasocket/bin" and retrieve file "luasocket.exe"
--- (actually, fails because of wrong password, of course)
-f, e = socket.ftp.get{
- url = "ftp://ftp.tecgraf.puc-rio.br/tec/luasocket/bin/luasocket.exe",
- user = "diego",
- password = "nehab",
- type = "i"
-}
--- f returns nil, and e returns an appropriate error message
+-- and get file "lua.tar.gz" from directory "pub/lua" as binary.
+f, e = ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
-
-
-
- url = string,
- type = string,
- content_cb = receive-callback,
- user = string,
- password = string
-}
-
+-- load needed modules
+local ftp = require("ftp")
+local ltn12 = require("ltn12")
+local url = require("url")
+
+-- a function that returns a directory listing
+function ls(u)
+ local t = {}
+ local p = url.parse(u)
+ p.command = "nlst"
+ p.sink = ltn12.sink.table(t)
+ local r, e = ftp.get(p)
+ return r and table.concat(t), e
+end
+
-socket.ftp.put{
- url = string,
- content = string,
- type = string,
- user = string,
- password = string
+ftp.put(url, content)
+ftp.put{
+ host = string,
+ source = LTN12 sink,
+ argument or path = string,
+ [user = string,]
+ [password = string]
+ [command = string,]
+ [port = number,]
+ [type = string,]
+ [step = LTN12 pump step],
}
+
--- Log as user "anonymous" on server "ftp.free.org" and store file
--- "hello" with contents "hello world!", using binary mode for the transfer
-r, e = socket.ftp.put("ftp://ftp.free.org/hello;type=i", "hello world!\n")
-
--- Does exactly the same, but logging in as diego
-r, e = socket.ftp.put{
- url = "ftp://ftp.free.org/hello",
- type = "i",
- user = "diego",
- password = "nehab",
- content = "hello world\n"
-}
-
-
-
-
-
-
- url = string,
- type = string,
- content_cb = send-callback,
- user = string,
- password = string
-}
-
--- Log as user "anonymous" on server "ftp.free.org" and store file
--- "hello" with contents of the same file in the current directory,
--- using binary mode for the transfer
-r, e = socket.ftp.put_cb{
- url = "ftp://ftp.free.org/hello",
- type = "i",
- content_cb = socket.callback.send_file(io.open("hello", "r"))
+-- load the ftp support
+local ftp = require("ftp")
+local ltn12 = require("ltn12")
+
+-- Log as user "diego" on server "ftp.tecgraf.puc-rio.br",
+-- using password "nehab", and append to the file "LOG", sending the
+-- contents of a local file
+f, e = ftp.put{
+ host = "ftp.tecgraf.puc-rio.br",
+ user = "diego",
+ password = "nehab",
+ command = "appe",
+ argument = "LOG",
+ source = ltn12.source.file(io.open("LOCAL-LOG", "r"))
}
-
+
diff --git a/doc/ltn12.html b/doc/ltn12.html
new file mode 100644
index 0000000..363ce43
--- /dev/null
+++ b/doc/ltn12.html
@@ -0,0 +1,421 @@
+
+
+
+
+LTN12
+
+Filters
+
+
+
+
+-- load required modules
+ltn12 = require("ltn12")
+mime = require("mime")
+
+-- create a silly identity filter
+id = ltn12.filter.chain(
+ mime.encode("quoted-printable"),
+ mime.encode("base64"),
+ mime.decode("base64"),
+ mime.decode("quoted-printable")
+)
+
+
+
+
+
+-- load the ltn12 module
+local ltn12 = require("ltn12")
+
+-- the base64 mime filter factory
+encodet['base64'] = function()
+ return ltn12.filter.cycle(b64, "")
+end
+
+
+
+
+Pumps
+
+
+
+Sinks
+
+
+
+
+-- load the ltn12 module
+local ltn12 = require("ltn12")
+
+-- copy a file
+ltn12.pump.all(
+ ltn12.source.file(io.open("original.png")),
+ ltn12.sink.file(io.open("copy.png"))
+)
+
+
+
+
+
+-- load needed modules
+local http = require("http")
+local ltn12 = require("ltn12")
+
+-- the http.get function
+function get(u)
+ local t = {}
+ local respt = request{
+ url = u,
+ sink = ltn12.sink.table(t)
+ }
+ return table.concat(t), respt.headers, respt.code, respt.error
+end
+
+
+
+
+Sources
+
+
+
+
+-- load the ltn12 module
+local ltn12 = require("ltn12")
+
+-- copy a file
+ltn12.pump.all(
+ ltn12.source.file(io.open("original.png")),
+ ltn12.sink.file(io.open("copy.png"))
+)
+
+
+
+
+MIME
+
+High-level filters
+
+
+
+
+mime.decode("quoted-printable")
+
+mime.encode("quoted-printable" [, mode])
+
+base64 = ltn12.filter.chain(
+ mime.encode("base64"),
+ mime.wrap("base64")
+)
+
+
+
+base64 = ltn12.filter.chain(
+ mime.normalize(),
+ mime.encode("base64"),
+ mime.wrap("base64")
+)
+
+
+
+
+
+mime.wrap("base64")
+mime.wrap("quoted-printable")
+
+qp = ltn12.filter.chain(
+ mime.normalize(),
+ mime.encode("quoted-printable"),
+ mime.wrap("quoted-printable")
+)
+
+
+Low-level filters
+
+
+
+
+print((mime.b64("diego:password")))
+--> ZGllZ286cGFzc3dvcmQ=
+
+
+
+
+
+-- translates the end-of-line marker to UNIX
+unix = mime.eol(0, dos, "\n")
+
+
+
+
+
+print((mime.qp("maçã")))
+--> ma=E7=E3=
+
+
+
+
+
+print((mime.unb64("ZGllZ286cGFzc3dvcmQ=")))
+--> diego:password
+
+
+
+
+
+print((mime.qp("ma=E7=E3=")))
+--> maçã
+
+
+
+
+Reference
-DNS services (socket.dns)
+DNS (in socket)
toip,
tohostname,
@@ -47,31 +47,17 @@
-FTP (socket.ftp)
+FTP
-
-
-
get,
-put,
-open.
+put
-Global symbols
-
-
-
-LUASOCKET_LIBNAME,
-mime,
-ltn12,
-socket.
-
-
-HTTP (socket.http)
+HTTP
get,
post,
@@ -82,46 +68,45 @@
-LTN012 (ltn12)
+LTN12
filter:
-chain,
-cycle.
+chain,
+cycle.
pump:
-all,
-step.
+all,
+step.
sink:
-chain,
-error,
-file,
-null,
-simplify,
-table.
+chain,
+error,
+file,
+null,
+simplify,
+table.
source:
-cat,
-chain,
-empty,
-file,
-simplify,
-rewind,
-string.
+cat,
+chain,
+empty,
+error,
+file,
+simplify,
+string.
-MIME (mime)
+MIME
high-level:
normalize,
-chain,
decode,
encode,
wrap.
@@ -141,9 +126,8 @@
-SMTP (socket.smtp)
+SMTP
-open,
message,
send.
@@ -152,26 +136,20 @@
-The socket namespace (socket)
+Socket
@@ -179,7 +157,7 @@
-bind,
-connect,
-debug,
+DEBUG,
dns,
-ftp,
-http,
protect,
select,
sink,
source,
sleep,
-smtp,
time,
tcp,
try,
udp,
-url,
-version.
+VERSION.
-TCP (socket.tcp)
+TCP (in socket)
accept,
bind,
@@ -198,7 +176,7 @@
-UDP (socket.udp)
+UDP (in socket)
close,
getpeername,
@@ -210,23 +188,22 @@
setpeername,
setsockname,
setoption,
-settimeout,
-shutdown.
+settimeout.
-URL (socket.url)
+URL
diff --git a/doc/smtp.html b/doc/smtp.html
index 0862de0..b0ae634 100644
--- a/doc/smtp.html
+++ b/doc/smtp.html
@@ -35,15 +35,22 @@
absolute,
build,
build_path,
-quote,
+escape,
parse,
parse_path,
-unquote.
+unescape.
SMTP
-
+
+
+
+
+
+
from = string,
rcpt = string or string-table,
- body = string,
- headers = headers-table,
- server = string
+ source = LTN12 source,
+ [server = string],
+ [port = string]
+ [domain = string],
+ [step = LTN12 pump step],
}
+
@@ -108,6 +142,13 @@ If successful, the function returns 1. Otherwise, the function returns nil followed by an error message.
++Note: SMTP servers are can be very picky with the format of e-mail +addresses. To be safe, use only addresses of the form +"<fulano@tecgraf.puc-rio.br>" in the from and +rcpt arguments to the send function. In headers, e-mail +addresses can take whatever form you like.
+Big note: There is a good deal of misconception with the use of the destination address field headers, i.e., the 'To', 'Cc', @@ -117,11 +158,12 @@ exact opposite of what you expect.
-Only recipients specified in the recipient list will receive a copy of the +Only recipients specified in the rcpt list will receive a copy of the message. Each recipient of an SMTP mail message receives a copy of the -message body along with the headers, and nothing more. The headers are -considered as part of the message. The list of recipients is not -part of the message. +message body along with the headers, and nothing more. The headers +are part of the message and should be produced by the LTN12 +source function. The rcpt list is not +part of the message and will not be sent to anyone.
@@ -143,9 +185,9 @@ Copy") contains addresses of recipients of the message whose addresses are not t
-The LuaSocket mail function does not interpret the headers you -pass to, but it gives you full control over what is sent and to whom -it is sent: +The LuaSocket send function does not care or interpret the +headers you send, but it gives you full control over what is sent and +to whom it is sent:
+-- load the smtp support
+local smtp = require("smtp")
+
-- Connects to server "localhost" and sends a message to users
-- "fulano@tecgraf.puc-rio.br", "beltrano@tecgraf.puc-rio.br",
-- and "sicrano@tecgraf.puc-rio.br".
-- Note that "fulano" is the primary recipient, "beltrano" receives a
-- carbon copy and neither of them knows that "sicrano" received a blind
-- carbon copy of the message.
-headers = {
- to = "fulano@tecgraf.puc-rio.br",
- cc = "beltrano@tecgraf.puc-rio.br",
- subject = "LuaSocket test message"
-}
-
-from = "luasocket@tecgraf.puc-rio.br"
+from = "<luasocket@tecgraf.puc-rio.br>"
rcpt = {
- "fulano@tecgraf.puc-rio.br",
- "beltrano@tecgraf.puc-rio.br",
- "sicrano@tecgraf.puc-rio.br"
+ "<fulano@tecgraf.puc-rio.br>",
+ "<beltrano@tecgraf.puc-rio.br>",
+ "<sicrano@tecgraf.puc-rio.br>"
}
-body = "This is a test message. Please ignore."
-
-server = "localhost"
+mesgt = {
+ headers = {
+ to = "Fulano da Silva <fulano@tecgraf.puc-rio.br>",
+ cc = '"Beltrano F. Nunes" <beltrano@tecgraf.puc-rio.br>',
+ subject = "My first message"
+ }
+ body = "I hope this works. If it does, I can send you another 1000 copies."
+}
-r, e = socket.smtp.mail{
+r, e = smtp.send{
from = from,
rcpt = rcpt,
- headers = headers,
- body = body,
- server = server
+ source = smtp.message(mesgt)
+}
+
+
+
+
++smtp.message(mesgt) +
+ ++Returns a LTN12 source that sends an SMTP message body, possibly multipart +(arbitrarily deep). +
+ ++The only parameter of the function is a table describing the message. +Mesgt has the following form (notice the recursive structure): +
+ +++ ++
++ +mesgt = {
+ headers = header-table,
+ body = LTN12 source or string or +multipart-mesgt
+}
+
+multipart-mesgt = {
+ preamble = string
+ [1] = mesgt,
+ [2] = mesgt,
+ ...
+ [n] = mesgt,
+ epilogue = string,
+}
+
+For a simple message, all that is needed is a set of headers +and the body. The message body can be given as a string +or as a LTN12 source. For multipart messages, the body is a table that +recursively defines each part as an independent message, plus a preamble +and an epilogue. +
+ ++The function returns an LTN12 source that produces the message contents as +defined by mesgt. Hopefuly, the following example will make +things clear. When in doubt, refer to the appropriate RFC as listed in the +introduction.
+ +
+-- load the smtp support and its friends
+local smtp = require("smtp")
+local mime = require("mime")
+local ltn12 = require("ltn12")
+
+-- creates a source to send a message with two parts. The first part is
+-- plain text, the second part is a PNG image, encoded as base64.
+source = smtp.message{
+ headers = {
+ -- Remember that headers are *ignored* by smtp.send.
+ from = "Sicrano de Oliveira <sicrano@tecgraf.puc-rio.br>",
+ to = "Fulano da Silva <fulano@tecgraf.puc-rio.br>",
+ subject = "Here is a message with attachments"
+ },
+ body = {
+ preamble = "If your client doesn't understand attachments, \r\n" ..
+ "it will still display the preamble and the epilogue.\r\n",
+ "Preamble might show up even in a MIME enabled client.",
+ -- first part: no headers means plain text, us-ascii.
+ -- The mime.eol low-level filter normalizes end-of-line markers.
+ [1] = {
+ body = mime.eol(0, [[
+ Lines in a message body should always end with CRLF.
+ The smtp module will *NOT* perform translation. It will
+ perform necessary stuffing or '.' characters, though.
+ ]])
+ },
+ -- second part: headers describe content to be a png image,
+ -- sent under the base64 transfer content encoding.
+ -- notice that nothing happens until the message is actually sent.
+ -- small chunks are loaded into memory right before transmission and
+ -- translation happens on the fly.
+ [2] = {
+ headers = {
+ ["content-type"] = 'image/png; name="image.png"',
+ ["content-disposition"] = 'attachment; filename="image.png"',
+ ["content-description"] = 'a beautiful image',
+ ["content-transfer-encoding"] = "BASE64"
+ },
+ body = ltn12.source.chain(
+ ltn12.source.file(io.open("image.png", "rb")),
+ ltn12.filter.chain(
+ mime.encode("base64"),
+ mime.wrap()
+ )
+ )
+ },
+ epilogue = "This might also show up, but after the attachments"
+ }
+}
+
+-- finally send it
+r, e = smtp.send{
+ from = "<sicrano@tecgraf.puc-rio.br>",
+ rcpt = "<fulano@tecgraf.puc-rio.br>",
+ source = source,
}
diff --git a/doc/socket.html b/doc/socket.html
index bde882b..eccc676 100644
--- a/doc/socket.html
+++ b/doc/socket.html
@@ -36,16 +36,13 @@
-The socket namespace contains the namespace tables for all -LuaSocket modules as well as function that didn't belong in any specific -module, functions that are so commonly used that deserve a shortcut and a -few constants. +The socket namespace contains the core functionality of LuaSocket.
-socket.debug +socket.DEBUG
@@ -57,7 +54,7 @@ with debug support.
-socket.protect(function) +socket.protect(func)
@@ -65,12 +62,12 @@ Converts a function that throws exceptions into a safe function.
-Function is a function that calls +Funct is a function that calls try to throw exceptions.
-The function an equivalent function that instead of throwing exceptoins, +Returns an equivalent function that instead of throwing exceptions, returns nil followed by an error message.
@@ -103,16 +100,16 @@ simplify the test if a specific socket has changed status.-Important Note: a known bug in WinSock causes select to fail +Important note: a known bug in WinSock causes select to fail on non-blocking TCP sockets. The function may return a socket as writable even though the socket is not ready for sending.
-Important note: calling select with a server socket in the receive +Another important note: calling select with a server socket in the receive parameter before a call to accept does not guarantee accept will return immediately. -Use the timeout +Use the settimeout method or accept might block forever.
@@ -131,7 +128,7 @@ socket.sink(mode, socket)Creates an -LTN012 +LTN12 sink from a stream socket object.
@@ -163,7 +160,7 @@ socket.source(mode, socket [, length])Creates an -LTN012 +LTN12 source from a stream socket object.
@@ -217,7 +214,7 @@ c = socket.try(socket.connect("localhost", 80))-socket.version +socket.VERSION
diff --git a/doc/tcp.html b/doc/tcp.html index 34d6c6e..7a49660 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -241,18 +241,16 @@ method returns nil followed by an error message.
-client:receive([pattern1, pattern2, -... patternN]) +client:receive([pattern])
Reads data from a client object, according to the specified read -patterns. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. +pattern. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
-The parameters pattern1, pattern2, ... -patternN can be any of the following: +Pattern can be any of the following:
-The method returns one value for each pattern, followed by a single -error code that can be nil in case of success, the string -'closed' in case the connection was closed before the -transmission was completed or the string 'timeout' in case -there was a timeout during the operation. +If successful, the method returns the received pattern. In case of error, +the method returns nil followed by an error message which +can be the string 'closed' in case the connection was +closed before the transmission was completed or the string +'timeout' in case there was a timeout during the operation. +Also, after the error message, the function returns the partial result of +the transmission.
-Note: In case of error, the method always return everything it managed -to download before the error condition was met. +Important note: This function was changed severely. It used +to support multiple patterns (but I have never seen this feature used) and +partial results used to be returned in the same way as successful results. +This last feature violated the idea that all functions should return +nil on error. Thus the change.
@@ -428,7 +431,7 @@ client:shutdown(mode)-Shuts down part of a full duplex connection. +Shuts down part of a full-duplex connection.
diff --git a/doc/udp.html b/doc/udp.html index 9f5cf8f..5ae0a89 100644 --- a/doc/udp.html +++ b/doc/udp.html @@ -29,6 +29,11 @@
diff --git a/doc/url.html b/doc/url.html index f3a7cb7..cd699a2 100644 --- a/doc/url.html +++ b/doc/url.html @@ -59,7 +59,7 @@ An URL is defined by the following grammar:
-socket.url.absolute(base, relative) +url.absolute(base, relative)
@@ -79,7 +79,7 @@ The function returns a string with the absolute URL. Note: The rules that govern the composition are fairly complex, and are described in detail in RFC 2396. -The example bellow should give an idea of what are the rules. +The example bellow should give an idea of what the rules are.
@@ -114,7 +114,7 @@ g;x?y#s = http://a/b/c/g;x?y#s-socket.url.build(parsed_url) +url.build(parsed_url)
@@ -135,7 +135,7 @@ The function returns a string with the built URL.
-socket.url.build_path(segments, unsafe) +url.build_path(segments, unsafe)
@@ -157,10 +157,39 @@ The function returns a string with the built <path> component.
+ + ++url.escape(content) +
+ ++Applies the URL escaping content coding to a string +Each byte is encoded as a percent character followed +by the two byte hexadecimal representation of its integer +value. +
+ ++Content is the string to be encoded. +
+ ++The function returns the encoded string. +
+ ++-- load url module +url = require("url") + +code = url.escape("/#?;") +-- code = "%2f%23%3f%3b" ++-socket.url.parse(url, default) +url.parse(url, default)
@@ -196,7 +225,10 @@ parsed_url = {
-parsed_url = socket.url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there") +-- load url module +url = require("url") + +parsed_url = url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there") -- parsed_url = { -- scheme = "http", -- authority = "www.puc-rio.br", @@ -206,7 +238,7 @@ parsed_url = socket.url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there" -- host = "www.puc-rio.br", -- } -parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i") +parsed_url = url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i") -- parsed_url = { -- scheme = "ftp", -- authority = "root:passwd@unsafe.org", @@ -222,7 +254,7 @@ parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i-socket.url.parse_path(path) +url.parse_path(path)
@@ -241,36 +273,10 @@ returning a list with all the parsed segments, the function unescapes all of them.
- - --socket.url.escape(content) -
- --Applies the URL escaping content coding to a string -Each byte is encoded as a percent character followed -by the two byte hexadecimal representation of its integer -value. -
- --Content is the string to be encoded. -
- --The function returns the encoded string. -
- --code = socket.url.escape("/#?;") --- code = "%2f%23%3f%3b" ---socket.url.unescape(content) +url.unescape(content)
-- cgit v1.2.3-55-g6feb