From fd729b32a8966291f007567f72f3dc0158bdab35 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Mon, 5 Oct 2015 11:47:51 +0800 Subject: Added support for arbitrary datagram sizes. The maximum size is still constant per UDP object, but the size can be speficied at creation time. --- src/udp.c | 58 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 21 deletions(-) (limited to 'src/udp.c') diff --git a/src/udp.c b/src/udp.c index 17d932a..9c27b60 100644 --- a/src/udp.c +++ b/src/udp.c @@ -41,6 +41,7 @@ static int meth_setpeername(lua_State *L); static int meth_close(lua_State *L); static int meth_setoption(lua_State *L); static int meth_getoption(lua_State *L); +static int meth_getbufferlength(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_getfd(lua_State *L); static int meth_setfd(lua_State *L); @@ -63,6 +64,7 @@ static luaL_Reg udp_methods[] = { {"setfd", meth_setfd}, {"setoption", meth_setoption}, {"getoption", meth_getoption}, + {"getoption", meth_getbufferlength}, {"setpeername", meth_setpeername}, {"setsockname", meth_setsockname}, {"settimeout", meth_settimeout}, @@ -202,47 +204,55 @@ static int meth_sendto(lua_State *L) { \*-------------------------------------------------------------------------*/ static int meth_receive(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); - char buffer[UDP_DATAGRAMSIZE]; - size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); + char buf[UDP_DATAGRAMSIZE]; + size_t len = MAX(udp->len, UDP_DATAGRAMSIZE); + char *dgram = len > sizeof(buf)? udp->buf: buf; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, len); int err; p_timeout tm = &udp->tm; - count = MIN(count, sizeof(buffer)); timeout_markstart(tm); - err = socket_recv(&udp->sock, buffer, count, &got, tm); + wanted = MIN(wanted, len); + err = socket_recv(&udp->sock, dgram, wanted, &got, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err == IO_CLOSED) - err = IO_DONE; - if (err != IO_DONE) { + if (err != IO_DONE && err != IO_CLOSED ) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); return 2; } - lua_pushlstring(L, buffer, got); + lua_pushlstring(L, dgram, got); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_getbufferlength(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + lua_pushinteger(L, MAX(UDP_DATAGRAMSIZE, udp->len)); return 1; } /*-------------------------------------------------------------------------*\ * Receives data and sender from a UDP socket \*-------------------------------------------------------------------------*/ -static int meth_receivefrom(lua_State *L) -{ +static int meth_receivefrom(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); - char buffer[UDP_DATAGRAMSIZE]; - size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); - int err; - p_timeout tm = &udp->tm; + char buf[UDP_DATAGRAMSIZE]; + size_t len = MAX(udp->len, UDP_DATAGRAMSIZE); + char *dgram = len > sizeof(buf)? udp->buf: buf; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, len); struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); char addrstr[INET6_ADDRSTRLEN]; char portstr[6]; + int err; + p_timeout tm = &udp->tm; timeout_markstart(tm); - count = MIN(count, sizeof(buffer)); - err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, + wanted = MIN(wanted, len); + err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, &addr_len, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err == IO_CLOSED) - err = IO_DONE; - if (err != IO_DONE) { + if (err != IO_DONE && err != IO_CLOSED) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); return 2; @@ -254,7 +264,7 @@ static int meth_receivefrom(lua_State *L) lua_pushstring(L, gai_strerror(err)); return 2; } - lua_pushlstring(L, buffer, got); + lua_pushlstring(L, dgram, got); lua_pushstring(L, addrstr); lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); return 3; @@ -409,13 +419,19 @@ static int meth_setsockname(lua_State *L) { * Creates a master udp object \*-------------------------------------------------------------------------*/ static int udp_create(lua_State *L, int family) { + p_udp udp = NULL; + /* optional length for private datagram buffer. this is useful when + * you need larger datagrams than UDP_DATAGRAMSIZE */ + size_t len = (size_t) luaL_optinteger(L, 1, 0); + if (len <= UDP_DATAGRAMSIZE) len = 0; /* allocate udp object */ - p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + udp = (p_udp) lua_newuserdata(L, sizeof(t_udp) + len - 1); auxiliar_setclass(L, "udp{unconnected}", -1); /* if family is AF_UNSPEC, we leave the socket invalid and * store AF_UNSPEC into family. This will allow it to later be * replaced with an AF_INET6 or AF_INET socket upon first use. */ udp->sock = SOCKET_INVALID; + udp->len = len; timeout_init(&udp->tm, -1, -1); udp->family = family; if (family != AF_UNSPEC) { -- cgit v1.2.3-55-g6feb From be67f63f4e11e53690bf1431a236f86b484c9bf0 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Tue, 6 Oct 2015 11:33:50 +0800 Subject: Changed buffer-per-socket to buffer-per-operation. This is a difficult tradeoff to measure. I think large datagrams won't be used very frequently. So it is better to not lock a large buffer to each socket object and instead allocate and deallocate for each operation receiving a datagram larger than UDP_DATAGRAMSIZE. --- doc/reference.html | 2 ++ doc/socket.html | 25 ++++++++++++++++++++++- doc/udp.html | 33 +++++++------------------------ src/select.c | 5 ++++- src/udp.c | 58 +++++++++++++++++++++++++----------------------------- src/udp.h | 2 -- test/testclnt.lua | 1 - 7 files changed, 64 insertions(+), 62 deletions(-) (limited to 'src/udp.c') diff --git a/doc/reference.html b/doc/reference.html index 6067ba6..878e7d2 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -147,6 +147,7 @@ Support, Manual"> connect, connect4, connect6, +_DATAGRAMSIZE, _DEBUG, dns, gettime, @@ -158,6 +159,7 @@ Support, Manual"> skip, sleep, _SETSIZE, +_SOCKETINVALID, source, tcp, tcp4, diff --git a/doc/socket.html b/doc/socket.html index e6a9bf8..8a81414 100644 --- a/doc/socket.html +++ b/doc/socket.html @@ -90,7 +90,7 @@ of connect are defined as simple helper functions that restrict the -

+

socket._DEBUG

@@ -99,6 +99,19 @@ This constant is set to true if the library was compiled with debug support.

+ + +

+socket._DATAGRAMSIZE +

+ +

+Default datagram size used by calls to +receive and +receivefrom. +(Unless changed in compile time, the value is 8192.) +

+

@@ -393,6 +406,16 @@ The maximum number of sockets that the select function can handle.

+ + +

+socket._SOCKETINVALID +

+ +

+The OS value for an invalid socket. +

+

diff --git a/doc/udp.html b/doc/udp.html index 22d7c72..9437c51 100644 --- a/doc/udp.html +++ b/doc/udp.html @@ -42,7 +42,7 @@

-socket.udp([buffersize]) +socket.udp()

@@ -62,13 +62,6 @@ The setpeername is used to connect the object.

-

-The optional buffersize parameter -specifies the size of the largest datagram that will -ever be received by the UDP object. The default value is -8192. -

-

In case of success, a new unconnected UDP object returned. In case of error, nil is returned, followed by @@ -92,7 +85,7 @@ href=#setoption>setoption will fail.

-socket.udp4([buffersize]) +socket.udp4()

@@ -112,13 +105,6 @@ The setpeername is used to connect the object.

-

-The optional buffersize parameter -specifies the size of the largest datagram that will -ever be received by the UDP object. The default value is -8192. -

-

In case of success, a new unconnected UDP object returned. In case of error, nil is returned, followed by @@ -128,7 +114,7 @@ an error message.

-socket.udp6([buffersize]) +socket.udp6()

@@ -148,13 +134,6 @@ The setpeername is used to connect the object.

-

-The optional buffersize parameter -specifies the size of the largest datagram that will -ever be received by the UDP object. The default value is -8192. -

-

In case of success, a new unconnected UDP object returned. In case of error, nil is returned, followed by @@ -261,8 +240,10 @@ the excess bytes are discarded. If there are less then size bytes available in the current datagram, the available bytes are returned. If size is omitted, the -buffersize argument at creation time is used -(which defaults to 8192 bytes). +compile-time constant socket._DATAGRAMSIZE is used +(it defaults to 8192 bytes). Larger sizes will cause a +temporary buffer to be allocated for the operation.

diff --git a/src/select.c b/src/select.c index d14c40a..9d133b7 100644 --- a/src/select.c +++ b/src/select.c @@ -39,7 +39,10 @@ static luaL_Reg func[] = { \*-------------------------------------------------------------------------*/ int select_open(lua_State *L) { lua_pushstring(L, "_SETSIZE"); - lua_pushnumber(L, FD_SETSIZE); + lua_pushinteger(L, FD_SETSIZE); + lua_rawset(L, -3); + lua_pushstring(L, "_SOCKETINVALID"); + lua_pushinteger(L, SOCKET_INVALID); lua_rawset(L, -3); luaL_setfuncs(L, func, 0); return 0; diff --git a/src/udp.c b/src/udp.c index 9c27b60..968dca8 100644 --- a/src/udp.c +++ b/src/udp.c @@ -41,7 +41,6 @@ static int meth_setpeername(lua_State *L); static int meth_close(lua_State *L); static int meth_setoption(lua_State *L); static int meth_getoption(lua_State *L); -static int meth_getbufferlength(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_getfd(lua_State *L); static int meth_setfd(lua_State *L); @@ -64,7 +63,6 @@ static luaL_Reg udp_methods[] = { {"setfd", meth_setfd}, {"setoption", meth_setoption}, {"getoption", meth_getoption}, - {"getoption", meth_getbufferlength}, {"setpeername", meth_setpeername}, {"setsockname", meth_setsockname}, {"settimeout", meth_settimeout}, @@ -118,8 +116,7 @@ static luaL_Reg func[] = { /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -int udp_open(lua_State *L) -{ +int udp_open(lua_State *L) { /* create classes */ auxiliar_newclass(L, "udp{connected}", udp_methods); auxiliar_newclass(L, "udp{unconnected}", udp_methods); @@ -130,6 +127,10 @@ int udp_open(lua_State *L) auxiliar_add2group(L, "udp{unconnected}", "select{able}"); /* define library functions */ luaL_setfuncs(L, func, 0); + /* export default UDP size */ + lua_pushliteral(L, "_DATAGRAMSIZE"); + lua_pushinteger(L, UDP_DATAGRAMSIZE); + lua_rawset(L, -3); return 0; } @@ -205,30 +206,26 @@ static int meth_sendto(lua_State *L) { static int meth_receive(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); char buf[UDP_DATAGRAMSIZE]; - size_t len = MAX(udp->len, UDP_DATAGRAMSIZE); - char *dgram = len > sizeof(buf)? udp->buf: buf; - size_t got, wanted = (size_t) luaL_optnumber(L, 2, len); + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; int err; p_timeout tm = &udp->tm; timeout_markstart(tm); - wanted = MIN(wanted, len); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } err = socket_recv(&udp->sock, dgram, wanted, &got, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err != IO_DONE && err != IO_CLOSED ) { + if (err != IO_DONE && err != IO_CLOSED) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); return 2; } lua_pushlstring(L, dgram, got); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Receives data from a UDP socket -\*-------------------------------------------------------------------------*/ -static int meth_getbufferlength(lua_State *L) { - p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); - lua_pushinteger(L, MAX(UDP_DATAGRAMSIZE, udp->len)); + if (wanted > sizeof(buf)) free(dgram); return 1; } @@ -238,9 +235,8 @@ static int meth_getbufferlength(lua_State *L) { static int meth_receivefrom(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); char buf[UDP_DATAGRAMSIZE]; - size_t len = MAX(udp->len, UDP_DATAGRAMSIZE); - char *dgram = len > sizeof(buf)? udp->buf: buf; - size_t got, wanted = (size_t) luaL_optnumber(L, 2, len); + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); char addrstr[INET6_ADDRSTRLEN]; @@ -248,13 +244,18 @@ static int meth_receivefrom(lua_State *L) { int err; p_timeout tm = &udp->tm; timeout_markstart(tm); - wanted = MIN(wanted, len); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, &addr_len, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ if (err != IO_DONE && err != IO_CLOSED) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); return 2; } err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, @@ -262,19 +263,20 @@ static int meth_receivefrom(lua_State *L) { if (err) { lua_pushnil(L); lua_pushstring(L, gai_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); return 2; } lua_pushlstring(L, dgram, got); lua_pushstring(L, addrstr); lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); + if (wanted > sizeof(buf)) free(dgram); return 3; } /*-------------------------------------------------------------------------*\ * Returns family as string \*-------------------------------------------------------------------------*/ -static int meth_getfamily(lua_State *L) -{ +static int meth_getfamily(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); if (udp->family == AF_INET6) { lua_pushliteral(L, "inet6"); @@ -419,19 +421,13 @@ static int meth_setsockname(lua_State *L) { * Creates a master udp object \*-------------------------------------------------------------------------*/ static int udp_create(lua_State *L, int family) { - p_udp udp = NULL; - /* optional length for private datagram buffer. this is useful when - * you need larger datagrams than UDP_DATAGRAMSIZE */ - size_t len = (size_t) luaL_optinteger(L, 1, 0); - if (len <= UDP_DATAGRAMSIZE) len = 0; /* allocate udp object */ - udp = (p_udp) lua_newuserdata(L, sizeof(t_udp) + len - 1); + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); auxiliar_setclass(L, "udp{unconnected}", -1); /* if family is AF_UNSPEC, we leave the socket invalid and * store AF_UNSPEC into family. This will allow it to later be * replaced with an AF_INET6 or AF_INET socket upon first use. */ udp->sock = SOCKET_INVALID; - udp->len = len; timeout_init(&udp->tm, -1, -1); udp->family = family; if (family != AF_UNSPEC) { diff --git a/src/udp.h b/src/udp.h index da27a7a..be9b6a5 100644 --- a/src/udp.h +++ b/src/udp.h @@ -23,8 +23,6 @@ typedef struct t_udp_ { t_socket sock; t_timeout tm; int family; - size_t len; /* length of datagram buffer below */ - char buf[1]; /* allocate larger structure to hold actual buffer */ } t_udp; typedef t_udp *p_udp; diff --git a/test/testclnt.lua b/test/testclnt.lua index ee1201f..170e187 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -669,7 +669,6 @@ local udp_methods = { "settimeout" } - ------------------------------------------------------------------------ test_methods(socket.udp(), udp_methods) do local sock = socket.tcp6() -- cgit v1.2.3-55-g6feb From 944305dc21350fd2ec32a9552d893da86894fd62 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Fri, 4 Mar 2016 15:36:32 -0300 Subject: Added gettimeout for completeness. Also documented. Rordered manuals so order is alphabetical. --- doc/mime.html | 59 ++++---- doc/reference.html | 2 + doc/smtp.html | 235 ++++++++++++++--------------- doc/socket.html | 70 ++++----- doc/tcp.html | 283 +++++++++++++++++++---------------- doc/udp.html | 431 +++++++++++++++++++++++++++-------------------------- src/tcp.c | 8 + src/timeout.c | 10 ++ src/timeout.h | 1 + src/udp.c | 7 + 10 files changed, 587 insertions(+), 519 deletions(-) (limited to 'src/udp.c') diff --git a/doc/mime.html b/doc/mime.html index ae136fd..8cb3507 100644 --- a/doc/mime.html +++ b/doc/mime.html @@ -72,34 +72,6 @@ local mime = require("mime")

High-level filters

- - -

-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 Mac OS (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. -

@@ -159,6 +131,35 @@ base64 = ltn12.filter.chain( ) + + +

+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 Mac OS (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. +

+

@@ -466,7 +467,7 @@ marker.

Last modified by Diego Nehab on
-Thu Apr 20 00:25:44 EDT 2006 +Fri Mar 4 15:19:17 BRT 2016

diff --git a/doc/reference.html b/doc/reference.html index 878e7d2..287dc19 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -187,6 +187,7 @@ Support, Manual"> getpeername, getsockname, getstats, +gettimeout, listen, receive, send, @@ -207,6 +208,7 @@ Support, Manual"> getoption, getpeername, getsockname, +gettimeout, receive, receivefrom, send, diff --git a/doc/smtp.html b/doc/smtp.html index bbbff80..600ec37 100644 --- a/doc/smtp.html +++ b/doc/smtp.html @@ -114,6 +114,124 @@ the SMTP module:
  • ZONE: default time zone. + + +

    +smtp.message(mesgt) +

    + +

    +Returns a simple +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 simple +LTN12 +source. For multipart messages, the body is a table that +recursively defines each part as an independent message, plus an optional +preamble and epilogue. +

    + +

    +The function returns a simple +LTN12 +source that produces the +message contents as defined by mesgt, chunk by chunk. +Hopefully, 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("socket.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@example.com>",
    +     to = "Fulano da Silva <fulano@example.com>",
    +     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 will probably appear 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. However, the 
    +        send function *DOES* perform SMTP stuffing, whereas the message
    +        function does *NOT*.
    +      ]])
    +    },
    +    -- 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@example.com>",
    +    rcpt = "<fulano@example.com>",
    +    source = source,
    +}
    +
    + +

    @@ -275,123 +393,6 @@ r, e = smtp.send{ } - - -

    -smtp.message(mesgt) -

    - -

    -Returns a simple -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 simple -LTN12 -source. For multipart messages, the body is a table that -recursively defines each part as an independent message, plus an optional -preamble and epilogue. -

    - -

    -The function returns a simple -LTN12 -source that produces the -message contents as defined by mesgt, chunk by chunk. -Hopefully, 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("socket.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@example.com>",
    -     to = "Fulano da Silva <fulano@example.com>",
    -     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 will probably appear 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. However, the 
    -        send function *DOES* perform SMTP stuffing, whereas the message
    -        function does *NOT*.
    -      ]])
    -    },
    -    -- 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@example.com>",
    -    rcpt = "<fulano@example.com>",
    -    source = source,
    -}
    -
    -