mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-12-25 12:08:21 +01:00
Manual is almost done. HTTP is missing.
Implemented new distribution scheme. Select is now purely C. HTTP reimplemented seems faster dunno why. LTN12 functions that coroutines fail gracefully.
This commit is contained in:
parent
9ed7f955e5
commit
58096449c6
11
TODO
11
TODO
@ -2,6 +2,15 @@ ajeitar os README.*
|
||||
ajeitar select. upvalue nao tem nada a ver...
|
||||
make sure filter.chain fails gracefully.
|
||||
ajeitar o manual sobre select, mais liberal agora
|
||||
falar sobre o novo esquema de namespace
|
||||
tirar socket.url socket.ftp etc do manual. agora os namespaces estao
|
||||
liberados.
|
||||
ajeitar as referencias a RFCS e LTNS em todos os arquivos.
|
||||
proxy no ftp
|
||||
ajeitar < e-mail > no smtp?
|
||||
ajeitar referencias a LTN12 nos manuais
|
||||
|
||||
make sure sockets are closed when exceptions are raised
|
||||
|
||||
falar sobre encodet/wrapt/decodet no manual sobre mime
|
||||
|
||||
@ -14,8 +23,6 @@ expose encode/decode tables to provide extensibility for mime module
|
||||
use coroutines instead of fancy filters
|
||||
|
||||
check garbage collection in test*.lua
|
||||
pop3???
|
||||
|
||||
|
||||
add socket.TIMEOUT to be default timeout?
|
||||
|
||||
|
268
doc/ftp.html
268
doc/ftp.html
@ -35,9 +35,9 @@
|
||||
|
||||
<p>
|
||||
FTP (File Transfer Protocol) is a protocol used to transfer files
|
||||
between hosts. The module <tt>ftp.lua</tt> offers simple FTP support,
|
||||
allowing applications to download and upload files, and list directory
|
||||
contents. The implementation conforms to
|
||||
between hosts. The module <tt>ftp.lua</tt> offers simple FTP support.
|
||||
Applications can easily download and upload files.
|
||||
The implementation conforms to
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>.
|
||||
</p>
|
||||
|
||||
@ -49,175 +49,191 @@ URLs MUST conform to
|
||||
|
||||
<blockquote>
|
||||
<tt>
|
||||
[ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][<i>type</i>=a|i|d]</tt>
|
||||
[ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][<i>type</i>=a|i]</tt>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
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 <em>any</em> operation supported by the FTP
|
||||
protocol. For that, check the implementation.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To use some of the functions in this module, a good understanding of
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
|
||||
LTN012, Filters sources and sinks</a> is necessary.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following constants can be set to control the default behaviour of
|
||||
the FTP module:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li> <tt>PASSWORD</tt>: default anonymous password.
|
||||
<li> <tt>PORT</tt>: default port used for the control connection;
|
||||
<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations;
|
||||
<li> <tt>USER</tt>: default anonymous user;
|
||||
</ul>
|
||||
|
||||
<!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=get>
|
||||
socket.ftp.<b>get(</b>url<b>)</b><br>
|
||||
socket.ftp.<b>get{</b><br>
|
||||
url = <i>string</i>,<br>
|
||||
type = <i>string</i>,<br>
|
||||
user = <i>string</i>,<br>
|
||||
password = <i>string</i><br>
|
||||
ftp.<b>get(</b>url<b>)</b><br>
|
||||
ftp.<b>get{</b><br>
|
||||
host = <i>string</i>,<br>
|
||||
sink = <i>LTN12 sink</i>,<br>
|
||||
argument <i>or</i> path = <i>string</i>,<br>
|
||||
[user = <i>string</i>,]<br>
|
||||
[password = <i>string</i>]<br>
|
||||
[command = <i>string</i>,]<br>
|
||||
[port = <i>number</i>,]<br>
|
||||
[type = <i>string</i>,]<br>
|
||||
[step = <i>LTN12 pump step</i>],<br>
|
||||
<b>}</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Downloads an URL from a FTP server.
|
||||
The <tt>get</tt> 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 <em>lot</em> more control, as explained
|
||||
below.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
The function can be called either directly with a <tt>url</tt>
|
||||
or with a <em>request table</em>.
|
||||
Fields passed explicitly in the request table override those
|
||||
present in the <tt>url</tt>.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
The parameter <tt>type</tt> accepts values '<tt>a</tt>' (ASCII, the
|
||||
default), '<tt>i</tt>' (binary) or '<tt>d</tt>' (directory listing) and
|
||||
determines the transfer type. If <tt><path></tt> ends with a
|
||||
'<tt>/</tt>' or <tt>type</tt> is '<tt>d</tt>', a directory listing of
|
||||
<tt><path></tt> is returned. If no <tt>user</tt> is provided in the
|
||||
<tt>url</tt> or explicitly, the function tries to log in as user
|
||||
'<tt>anonymous</tt>'.
|
||||
If the argument of the <tt>get</tt> function is a table, the function
|
||||
expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of
|
||||
<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
|
||||
precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is
|
||||
the LTN12 sink that will receive the downloaded data. <tt>Argument</tt> or
|
||||
<tt>path</tt> give the target path to the resource in the server. The
|
||||
optional arguments are the following:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>user</tt>, <tt>password</tt>: User name and password used for
|
||||
authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
|
||||
<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
|
||||
"<tt>retr</tt>", but see example below;
|
||||
<li><tt>port</tt>: The port to contacct the server at. Defaults to 21;
|
||||
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
|
||||
"<tt>a</tt>". Defaults to whatever is the server default;
|
||||
<li><tt>step</tt>: LTN12 pump step function used to pass data from the
|
||||
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function.
|
||||
</ul>
|
||||
|
||||
<p class=return>
|
||||
If successful, the function returns
|
||||
the file content as a string. In case of error, the function returns
|
||||
<b><tt>nil</tt></b> 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 <b><tt>nil</tt></b> and an error message describing the
|
||||
error.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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")
|
||||
</pre>
|
||||
|
||||
<!-- get_cb +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
<pre class=example>
|
||||
-- load needed modules
|
||||
local ftp = require("ftp")
|
||||
local ltn12 = require("ltn12")
|
||||
local url = require("url")
|
||||
|
||||
<p class=name id=get_cb>
|
||||
socket.ftp.<b>get_cb{</b><br>
|
||||
url = <i>string</i>,<br>
|
||||
type = <i>string</i>,<br>
|
||||
content_cb = <i>receive-callback</i>,<br>
|
||||
user = <i>string</i>,<br>
|
||||
password = <i>string</i><br>
|
||||
<b>}</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Same as <a href="#get"><tt>get</tt></a>, but the library returns
|
||||
the content of the downloaded file to the receive callback
|
||||
<tt>content_cb</tt>.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Note: for more information on callbacks, refer to
|
||||
<a href="stream.html#stream">Streaming with callbacks</a>.
|
||||
</p>
|
||||
-- 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
|
||||
</pre>
|
||||
|
||||
<!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=put>
|
||||
socket.ftp.<b>put(</b>url, content<b>)</b><br>
|
||||
socket.ftp.<b>put{</b><br>
|
||||
url = <i>string</i>,<br>
|
||||
content = <i>string</i>,<br>
|
||||
type = <i>string</i>,<br>
|
||||
user = <i>string</i>,<br>
|
||||
password = <i>string</i><br>
|
||||
ftp.<b>put(</b>url, content<b>)</b><br>
|
||||
ftp.<b>put{</b><br>
|
||||
host = <i>string</i>,<br>
|
||||
source = <i>LTN12 sink</i>,<br>
|
||||
argument <i>or</i> path = <i>string</i>,<br>
|
||||
[user = <i>string</i>,]<br>
|
||||
[password = <i>string</i>]<br>
|
||||
[command = <i>string</i>,]<br>
|
||||
[port = <i>number</i>,]<br>
|
||||
[type = <i>string</i>,]<br>
|
||||
[step = <i>LTN12 pump step</i>],<br>
|
||||
<b>}</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Upload a file to a FTP server.
|
||||
The <tt>put</tt> function has two forms. The simple form has fixed
|
||||
functionality: it uploads a string of content into a URL. The generic form
|
||||
allows a <em>lot</em> more control, as explained below.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
The function can be called directly with a
|
||||
<tt>url</tt> and <tt>content</tt> parameters, or with a
|
||||
<em>request table</em>.
|
||||
Values passed explicitly in the request table override those present in
|
||||
the <tt>url</tt>. The parameter <tt>type</tt> accept values
|
||||
'<tt>a</tt>' (ASCII, the default) or '<tt>i</tt>' (binary) and
|
||||
determines the transfer type. If no <tt>user</tt> is provided, the
|
||||
function tries to log in as '<tt>anonymous</tt>'.
|
||||
If the argument of the <tt>put</tt> function is a table, the function
|
||||
expects at least the fields <tt>host</tt>, <tt>source</tt>, and one of
|
||||
<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
|
||||
precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is
|
||||
the LTN12 source that will provide the contents to be uploaded.
|
||||
<tt>Argument</tt> or
|
||||
<tt>path</tt> give the target path to the resource in the server. The
|
||||
optional arguments are the following:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>user</tt>, <tt>password</tt>: User name and password used for
|
||||
authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
|
||||
<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
|
||||
"<tt>retr</tt>", but see example below;
|
||||
<li><tt>port</tt>: The port to contacct the server at. Defaults to 21;
|
||||
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
|
||||
"<tt>a</tt>". Defaults to whatever is the server default;
|
||||
<li><tt>step</tt>: LTN12 pump step function used to pass data from the
|
||||
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function.
|
||||
</ul>
|
||||
|
||||
<p class=return>
|
||||
If successful, the function returns 1. In case of error, the
|
||||
function returns <b><tt>nil</tt></b> followed by a string describing the error.
|
||||
Both functions return 1 if successful, or <b><tt>nil</tt></b> and an error
|
||||
message describing the reason for failure.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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")
|
||||
-- load the ftp support
|
||||
local ftp = require("ftp")
|
||||
|
||||
-- Does exactly the same, but logging in as diego
|
||||
r, e = socket.ftp.put{
|
||||
url = "ftp://ftp.free.org/hello",
|
||||
type = "i",
|
||||
-- Log as user "diego" on server "ftp.tecgraf.puc-rio.br",
|
||||
-- using password "nehab", and store a file "README" with contents
|
||||
-- "wrong password, of course"
|
||||
f, e = ftp.put("ftp://diego:nehab@ftp.tecgraf.puc-rio.br/README", "wrong password, of course")
|
||||
</pre>
|
||||
|
||||
<pre class=example>
|
||||
-- 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",
|
||||
content = "hello world\n"
|
||||
command = "appe",
|
||||
argument = "LOG",
|
||||
source = ltn12.source.file(io.open("LOCAL-LOG", "r"))
|
||||
}
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<!-- put_cb +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=put_cb>
|
||||
socket.ftp.<b>put_cb{</b><br>
|
||||
url = <i>string</i>,<br>
|
||||
type = <i>string</i>,<br>
|
||||
content_cb = <i>send-callback</i>,<br>
|
||||
user = <i>string</i>,<br>
|
||||
password = <i>string</i><br>
|
||||
<b>}</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Same as <a href="#put"><tt>put</tt></a>, but the
|
||||
library obtains the contents of the file to be uploaded using the send
|
||||
callback <tt>content_cb</tt>.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Note: for more information on callbacks, refer to
|
||||
<a href="stream.html#stream">Streaming with callbacks</a>.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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"))
|
||||
}
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
|
421
doc/ltn12.html
Normal file
421
doc/ltn12.html
Normal file
@ -0,0 +1,421 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>LuaSocket: Network support for the Lua language</title>
|
||||
<link rel="stylesheet" href="reference.css" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<div class=header>
|
||||
<hr>
|
||||
<center>
|
||||
<table summary="LuaSocket logo">
|
||||
<tr><td align=center><a href="http://www.lua.org">
|
||||
<img border=0 alt="LuaSocket" src="luasocket.png">
|
||||
</a></td></tr>
|
||||
<tr><td align=center valign=top>Network support for the Lua language
|
||||
</td></tr>
|
||||
</table>
|
||||
<p class=bar>
|
||||
<a href="home.html">home</a> ·
|
||||
<a href="home.html#download">download</a> ·
|
||||
<a href="introduction.html">introduction</a> ·
|
||||
<a href="reference.html">reference</a>
|
||||
</p>
|
||||
</center>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<!-- ltn12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h2 id=ltn12>LTN12</h2>
|
||||
|
||||
<p> The LTN12 module implements the ideas described in
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
|
||||
LTN012, Filters sources and sinks</a>. This manual simply describe the
|
||||
functions. Please refer to the LTN for a deeper explanation of the
|
||||
functionality provided by this module.
|
||||
</p>
|
||||
|
||||
<!-- filters ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h3 id="filter">Filters</h3>
|
||||
|
||||
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="filter.chain">
|
||||
ltn12.filter.<b>chain(</b>filter<sub>1</sub>, filter<sub>2</sub>
|
||||
[, ... filter<sub>N</sub>]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a filter that passes all data it receives through each of a
|
||||
series of given filters.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Filter<sub>1</sub></tt> to <tt>filter<sub>N</sub></tt> are simple
|
||||
filters.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the chained filter.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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")
|
||||
)
|
||||
</pre>
|
||||
|
||||
<!-- cycle ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="filter.cycle">
|
||||
ltn12.filter.<b>cycle(</b>low [, ctx, extra]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a high-level filter that cycles though a low-level filter by
|
||||
passing it each chunk and updating a context between calls.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Low</tt> is the low-level filter to be cycled,
|
||||
<tt>ctx</tt> is the initial context and <tt>extra</tt> is any extra
|
||||
argument the low-level filter might take.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the high-level filter.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- load the ltn12 module
|
||||
local ltn12 = require("ltn12")
|
||||
|
||||
-- the base64 mime filter factory
|
||||
encodet['base64'] = function()
|
||||
return ltn12.filter.cycle(b64, "")
|
||||
end
|
||||
</pre>
|
||||
|
||||
<!-- pumps ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h3 id="pump">Pumps</h3>
|
||||
|
||||
<!-- all ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="pump.all">
|
||||
ltn12.pump.<b>all(</b>source, sink<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Pumps <em>all</em> data from a <tt>source</tt> to a <tt>sink</tt>.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
If successful, the function returns a value that evaluates to
|
||||
<b><tt>true</tt></b>. In case
|
||||
of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
|
||||
</p>
|
||||
|
||||
<!-- step +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="pump.step">
|
||||
ltn12.pump.<b>step(</b>source, sink<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Pumps <em>one</em> chunk of data from a <tt>source</tt> to a <tt>sink</tt>.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
If successful, the function returns a value that evaluates to
|
||||
<b><tt>true</tt></b>. In case
|
||||
of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
|
||||
</p>
|
||||
|
||||
<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h3 id="sink">Sinks</h3>
|
||||
|
||||
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="sink.chain">
|
||||
ltn12.sink.<b>chain(</b>filter, sink<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates a new sink that passes data through a <tt>filter</tt> before sending
|
||||
it to a given <tt>sink</tt>.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the new sink.
|
||||
</p>
|
||||
|
||||
<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="sink.error">
|
||||
ltn12.sink.<b>error(</b>message<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates and returns a sink that aborts transmission with an error
|
||||
<tt>message</tt>.
|
||||
</p>
|
||||
|
||||
<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="sink.file">
|
||||
ltn12.sink.<b>file(</b>handle, message<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates a sink that sends data to a file.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
|
||||
<tt>message</tt> should give the reason for failure.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns a sink that sends all data to the given <tt>handle</tt>
|
||||
and closes the file when done, or a sink that aborts the transmission with
|
||||
an error <tt>message</tt>
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
In the following example, notice how the prototype is designed to
|
||||
fit nicely with the <tt>io.open</tt> function.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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"))
|
||||
)
|
||||
</pre>
|
||||
|
||||
<!-- null +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="sink.null">
|
||||
ltn12.sink.<b>null()</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a sink that ignores all data it receives.
|
||||
</p>
|
||||
|
||||
<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="sink.simplify">
|
||||
ltn12.sink.<b>simplify(</b>sink<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates and returns a simple sink given a fancy <tt>sink</tt>.
|
||||
</p>
|
||||
|
||||
<!-- table ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="sink.table">
|
||||
ltn12.sink.<b>table(</b>[table]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates a sink that stores all chunks in a table. The chunks can later be
|
||||
efficiently concatenated into a single string.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Table</tt> is used to hold the chunks. If
|
||||
<tt><b>nil</b></tt>, the function creates its own table.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the sink and the table.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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
|
||||
</pre>
|
||||
|
||||
<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h3 id="source">Sources</h3>
|
||||
|
||||
<!-- cat ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.cat">
|
||||
ltn12.source.<b>cat(</b>source<sub>1</sub> [, source<sub>2</sub>, ...,
|
||||
source<sub>N</sub>]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates a new source that produces the concatenation of the data produced
|
||||
by a number of sources.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Source<sub>1</sub></tt> to <tt>source<sub>N</sub></tt> are the original
|
||||
sources.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the new source.
|
||||
</p>
|
||||
|
||||
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.chain">
|
||||
ltn12.source.<b>chain(</b>source, filter<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates a new <tt>source</tt> that passes data through a <tt>filter</tt>
|
||||
before returning it.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the new source.
|
||||
</p>
|
||||
|
||||
<!-- empty ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.empty">
|
||||
ltn12.source.<b>empty()</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates and returns an empty source.
|
||||
</p>
|
||||
|
||||
<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.error">
|
||||
ltn12.source.<b>error(</b>message<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates and returns a source that aborts transmission with an error
|
||||
<tt>message</tt>.
|
||||
</p>
|
||||
|
||||
<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.file">
|
||||
ltn12.source.<b>file(</b>handle, message<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates a source that produces the contents of a file.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
|
||||
<tt>message</tt> should give the reason for failure.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns a source that reads chunks of data from
|
||||
given <tt>handle</tt> and returns it to the user,
|
||||
closing the file when done, or a source that aborts the transmission with
|
||||
an error <tt>message</tt>
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
In the following example, notice how the prototype is designed to
|
||||
fit nicely with the <tt>io.open</tt> function.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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"))
|
||||
)
|
||||
</pre>
|
||||
|
||||
<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.simplify">
|
||||
ltn12.source.<b>simplify(</b>source<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates and returns a simple source given a fancy <tt>source</tt>.
|
||||
</p>
|
||||
|
||||
<!-- string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="source.string">
|
||||
ltn12.source.<b>string(</b>string<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Creates and returns a source that produces the contents of a
|
||||
<tt>string</tt>, chunk by chunk.
|
||||
</p>
|
||||
|
||||
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<div class=footer>
|
||||
<hr>
|
||||
<center>
|
||||
<p class=bar>
|
||||
<a href="home.html">home</a> ·
|
||||
<a href="home.html#down">download</a> ·
|
||||
<a href="introduction.html">introduction</a> ·
|
||||
<a href="reference.html">reference</a>
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
Last modified by Diego Nehab on <br>
|
||||
Sat Aug 9 01:00:41 PDT 2003
|
||||
</small>
|
||||
</p>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
428
doc/mime.html
Normal file
428
doc/mime.html
Normal file
@ -0,0 +1,428 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>LuaSocket: Network support for the Lua language</title>
|
||||
<link rel="stylesheet" href="reference.css" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<div class=header>
|
||||
<hr>
|
||||
<center>
|
||||
<table summary="LuaSocket logo">
|
||||
<tr><td align=center><a href="http://www.lua.org">
|
||||
<img border=0 alt="LuaSocket" src="luasocket.png">
|
||||
</a></td></tr>
|
||||
<tr><td align=center valign=top>Network support for the Lua language
|
||||
</td></tr>
|
||||
</table>
|
||||
<p class=bar>
|
||||
<a href="home.html">home</a> ·
|
||||
<a href="home.html#download">download</a> ·
|
||||
<a href="introduction.html">introduction</a> ·
|
||||
<a href="reference.html">reference</a>
|
||||
</p>
|
||||
</center>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h2 id=mime>MIME</h2>
|
||||
|
||||
<p>
|
||||
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
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2045.txt">RFC 2045</a>,
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">2046</a>,
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2047</a>,
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2048</a> and
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2048.txt">2049</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
All functionality provided by the MIME module
|
||||
follows the ideas presented in
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
|
||||
LTN012, Filters sources and sinks</a>.
|
||||
</p>
|
||||
|
||||
<!-- High-level +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h3 id=high>High-level filters</h3>
|
||||
|
||||
<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="normalize">
|
||||
mime.<b>normalize(</b>[marker]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Converts most common end-of-line markers to a specific given marker.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic
|
||||
end-of-line marker defined by the MIME standard.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns a filter that performs the conversion.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="decode">
|
||||
mime.<b>decode(</b>"base64"<b>)</b><br>
|
||||
mime.<b>decode(</b>"quoted-printable"<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a filter that decodes data from a given transfer content
|
||||
encoding.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the created filter.
|
||||
</p>
|
||||
|
||||
<!-- encode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="encode">
|
||||
mime.<b>encode(</b>"base64"<b>)</b><br>
|
||||
mime.<b>encode(</b>"quoted-printable" [, mode])</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a filter that encodes data according to a given transfer content
|
||||
encoding.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
In the Quoted-Printable case, the user can specify whether the data is
|
||||
textual or binary, by passing the <tt>mode</tt> strings "<tt>text</tt>" or
|
||||
"<tt>binary</tt>". <tt>Mode</tt> defaults to "<tt>text</tt>".
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the created filter.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Although both transfer content encodings specify a limit for the line
|
||||
length, the encoding filters do <em>not</em> 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.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
base64 = ltn12.filter.chain(
|
||||
mime.encode("base64"),
|
||||
mime.wrap("base64")
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p class=note>
|
||||
Note: Text data <em>has</em> to be converted to canonic form
|
||||
<em>before</em> being encoded.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
base64 = ltn12.filter.chain(
|
||||
mime.normalize(),
|
||||
mime.encode("base64"),
|
||||
mime.wrap("base64")
|
||||
)
|
||||
</pre>
|
||||
|
||||
<!-- wrap +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="wrap">
|
||||
mime.<b>wrap(</b>"text" [, length]<b>)</b><br>
|
||||
mime.<b>wrap(</b>"base64"<b>)</b><br>
|
||||
mime.<b>wrap(</b>"quoted-printable"<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a filter that breaks data into lines.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
The "<tt>text</tt>" line-wrap filter simply breaks text into lines by
|
||||
inserting CRLF end-of-line markers at appropriate positions.
|
||||
<tt>Length</tt> defaults 76.
|
||||
The "<tt>base64</tt>" line-wrap filter works just like the default
|
||||
"<tt>text</tt>" line-wrap filter with default length.
|
||||
The function can also wrap "<tt>quoted-printable</tt>" lines, taking care
|
||||
not to break lines in the middle of an escaped character. In that case, the
|
||||
line length is fixed at 76.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns the created filter.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following:
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
qp = ltn12.filter.chain(
|
||||
mime.normalize(),
|
||||
mime.encode("quoted-printable"),
|
||||
mime.wrap("quoted-printable")
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p class=note>
|
||||
Note: To break into lines with a different end-of-line convention, apply
|
||||
a normalization filter after the line break filter.
|
||||
</p>
|
||||
|
||||
<!-- Low-level ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h3 id=low>Low-level filters</h3>
|
||||
|
||||
<!-- b64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="b64">
|
||||
A, B = mime.<b>b64(</b>C [, D]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Low-level filter to perform Base64 encoding.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is the encoded version of the largest prefix of
|
||||
<tt>C..D</tt>
|
||||
that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
|
||||
<tt>C..D</tt>, <em>before</em> encoding.
|
||||
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
|
||||
the encoding of the remaining bytes of <tt>C</tt>.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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 <tt>mime.b64</tt>, to discard the second return value.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
print((mime.b64("diego:password")))
|
||||
--> ZGllZ286cGFzc3dvcmQ=
|
||||
</pre>
|
||||
|
||||
<!-- eol ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="eol">
|
||||
A, B = mime.<b>eol(</b>C [, D, marker]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is the translated version of <tt>D</tt>. <tt>C</tt> is the
|
||||
ASCII value of the last character of the previous chunk, if it was a
|
||||
candidate for line break, or 0 otherwise.
|
||||
<tt>B</tt> is the same as <tt>C</tt>, but for the current
|
||||
chunk. If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> includes a
|
||||
new end-of-line marker, depending on <tt>C</tt>.
|
||||
<tt>Marker</tt> gives the new end-of-line marker and defaults to CRLF.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- translates the end-of-line marker to UNIX
|
||||
unix = mime.eol(0, dos, "\n")
|
||||
</pre>
|
||||
|
||||
<!-- qp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="qp">
|
||||
A, B = mime.<b>qp(</b>C [, D, marker]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Low-level filter to perform Quoted-Printable encoding.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is the encoded version of the largest prefix of
|
||||
<tt>C..D</tt>
|
||||
that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
|
||||
<tt>C..D</tt>, <em>before</em> encoding.
|
||||
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
|
||||
the encoding of the remaining bytes of <tt>C</tt>.
|
||||
Throughout encoding, occurences of CRLF are replaced by the
|
||||
<tt>marker</tt>, which itself defaults to CRLF.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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 <tt>mime.qp</tt>, to discard the second return value.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
print((mime.qp("maçã")))
|
||||
--> ma=E7=E3=
|
||||
</pre>
|
||||
|
||||
<!-- qpwrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="qpwrp">
|
||||
A, m = mime.<b>qpwrp(</b>n [, B, length]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Low-level filter to break Quoted-Printable text into lines.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
|
||||
<tt>length</tt> bytes (defaults to 76).
|
||||
'<tt>n</tt>' should tell how many bytes are left for the first
|
||||
line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
|
||||
left in the last line of <tt>A</tt>.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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 <tt>length</tt> bytes.
|
||||
</p>
|
||||
|
||||
<!-- unb64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="unb64">
|
||||
A, B = mime.<b>unb64(</b>C [, D]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Low-level filter to perform Base64 decoding.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is the decoded version of the largest prefix of
|
||||
<tt>C..D</tt>
|
||||
that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
|
||||
<tt>C..D</tt>, <em>before</em> decoding.
|
||||
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is the empty string
|
||||
and <tt>B</tt> returns whatever couldn't be decoded.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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 <tt>mime.unqp</tt>, to discard the second return value.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
print((mime.unb64("ZGllZ286cGFzc3dvcmQ=")))
|
||||
--> diego:password
|
||||
</pre>
|
||||
|
||||
<!-- unqp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="unqp">
|
||||
A, B = mime.<b>unqp(</b>C [, D]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Low-level filter to remove the Quoted-Printable transfer content encoding
|
||||
from data.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is the decoded version of the largest prefix of
|
||||
<tt>C..D</tt>
|
||||
that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
|
||||
<tt>C..D</tt>, <em>before</em> decoding.
|
||||
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is augmented with
|
||||
the encoding of the remaining bytes of <tt>C</tt>.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
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 <tt>mime.unqp</tt>, to discard the second return value.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
print((mime.qp("ma=E7=E3=")))
|
||||
--> maçã
|
||||
</pre>
|
||||
|
||||
<!-- wrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="wrp">
|
||||
A, m = mime.<b>wrp(</b>n [, B, length]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Low-level filter to break text into lines with CRLF marker.
|
||||
Text is assumed to be in the <a href=#normalize><tt>normalize</tt></a> form.
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
|
||||
<tt>length</tt> bytes (defaults to 76).
|
||||
'<tt>n</tt>' should tell how many bytes are left for the first
|
||||
line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
|
||||
left in the last line of <tt>A</tt>.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Note: This function only breaks lines that are bigger than
|
||||
<tt>length</tt> bytes. The resulting line length does not include the CRLF
|
||||
marker.
|
||||
</p>
|
||||
|
||||
|
||||
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<div class=footer>
|
||||
<hr>
|
||||
<center>
|
||||
<p class=bar>
|
||||
<a href="home.html">home</a> ·
|
||||
<a href="home.html#down">download</a> ·
|
||||
<a href="introduction.html">introduction</a> ·
|
||||
<a href="reference.html">reference</a>
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
Last modified by Diego Nehab on <br>
|
||||
Sat Aug 9 01:00:41 PDT 2003
|
||||
</small>
|
||||
</p>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -10,6 +10,9 @@ tt {
|
||||
|
||||
h1, h2, h3, h4 { margin-left: 0em; }
|
||||
|
||||
|
||||
h3 { padding-top: 1em; }
|
||||
|
||||
p { margin-left: 1em; }
|
||||
|
||||
p.name {
|
||||
|
@ -36,7 +36,7 @@
|
||||
<h2>Reference</h2>
|
||||
|
||||
<blockquote>
|
||||
<a href="dns.html">DNS services (socket.dns)</a>
|
||||
<a href="dns.html">DNS (in socket)</a>
|
||||
<blockquote>
|
||||
<a href="dns.html#toip">toip</a>,
|
||||
<a href="dns.html#tohostname">tohostname</a>,
|
||||
@ -47,31 +47,17 @@
|
||||
<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="ftp.html">FTP (socket.ftp)</a>
|
||||
<a href="ftp.html">FTP</a>
|
||||
<blockquote>
|
||||
<a href="ftp.html#get">get</a>,
|
||||
<a href="ftp.html#put">put</a>,
|
||||
<a href="ftp.html#open">open</a>.
|
||||
<a href="ftp.html#put">put</a>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<!-- global +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="global.html">Global symbols</a>
|
||||
<blockquote>
|
||||
<a href="global.html#LUASOCKET_LIBNAME">LUASOCKET_LIBNAME</a>,
|
||||
<a href="global.html#mime">mime</a>,
|
||||
<a href="global.html#ltn12">ltn12</a>,
|
||||
<a href="global.html#socket">socket</a>.
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</table>
|
||||
|
||||
<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="http.html">HTTP (socket.http)</a>
|
||||
<a href="http.html">HTTP</a>
|
||||
<blockquote>
|
||||
<a href="http.html#get">get</a>,
|
||||
<a href="http.html#post">post</a>,
|
||||
@ -82,46 +68,45 @@
|
||||
<!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="ltn12.html">LTN012 (ltn12)</a>
|
||||
<a href="ltn12.html">LTN12</a>
|
||||
<blockquote>
|
||||
<a href="ltn12.html#filter">filter</a>:
|
||||
<a href="ltn12.html#chain">chain</a>,
|
||||
<a href="ltn12.html#cycle">cycle</a>.
|
||||
<a href="ltn12.html#filter.chain">chain</a>,
|
||||
<a href="ltn12.html#filter.cycle">cycle</a>.
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<a href="ltn12.html#pump">pump</a>:
|
||||
<a href="ltn12.html#all">all</a>,
|
||||
<a href="ltn12.html#step">step</a>.
|
||||
<a href="ltn12.html#pump.all">all</a>,
|
||||
<a href="ltn12.html#pump.step">step</a>.
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<a href="ltn12.html#sink">sink</a>:
|
||||
<a href="ltn12.html#chain">chain</a>,
|
||||
<a href="ltn12.html#error">error</a>,
|
||||
<a href="ltn12.html#chain">file</a>,
|
||||
<a href="ltn12.html#file">null</a>,
|
||||
<a href="ltn12.html#simplify">simplify</a>,
|
||||
<a href="ltn12.html#table">table</a>.
|
||||
<a href="ltn12.html#sink.chain">chain</a>,
|
||||
<a href="ltn12.html#sink.error">error</a>,
|
||||
<a href="ltn12.html#sink.file">file</a>,
|
||||
<a href="ltn12.html#sink.null">null</a>,
|
||||
<a href="ltn12.html#sink.simplify">simplify</a>,
|
||||
<a href="ltn12.html#sink.table">table</a>.
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<a href="ltn12.html#source">source</a>:
|
||||
<a href="ltn12.html#cat">cat</a>,
|
||||
<a href="ltn12.html#chain">chain</a>,
|
||||
<a href="ltn12.html#empty">empty</a>,
|
||||
<a href="ltn12.html#file">file</a>,
|
||||
<a href="ltn12.html#simplify">simplify</a>,
|
||||
<a href="ltn12.html#simplify">rewind</a>,
|
||||
<a href="ltn12.html#simplify">string</a>.
|
||||
<a href="ltn12.html#source.cat">cat</a>,
|
||||
<a href="ltn12.html#source.chain">chain</a>,
|
||||
<a href="ltn12.html#source.empty">empty</a>,
|
||||
<a href="ltn12.html#source.error">error</a>,
|
||||
<a href="ltn12.html#source.file">file</a>,
|
||||
<a href="ltn12.html#source.simplify">simplify</a>,
|
||||
<a href="ltn12.html#source.string">string</a>.
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="mime.html">MIME (mime) </a>
|
||||
<a href="mime.html">MIME</a>
|
||||
<blockquote>
|
||||
<a href="mime.html#high">high-level</a>:
|
||||
<a href="mime.html#normalize">normalize</a>,
|
||||
<a href="mime.html#chain">chain</a>,
|
||||
<a href="mime.html#decode">decode</a>,
|
||||
<a href="mime.html#encode">encode</a>,
|
||||
<a href="mime.html#wrap">wrap</a>.
|
||||
@ -141,9 +126,8 @@
|
||||
<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="smtp.html">SMTP (socket.smtp)</a>
|
||||
<a href="smtp.html">SMTP</a>
|
||||
<blockquote>
|
||||
<a href="smtp.html#mail">open</a>,
|
||||
<a href="smtp.html#message">message</a>,
|
||||
<a href="smtp.html#send">send</a>.
|
||||
</blockquote>
|
||||
@ -152,26 +136,20 @@
|
||||
<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="socket.html">The socket namespace (socket)</a>
|
||||
<a href="socket.html">Socket</a>
|
||||
<blockquote>
|
||||
<a href="tcp.html#bind">bind</a>,
|
||||
<a href="tcp.html#connect">connect</a>,
|
||||
<a href="socket.html#debug">debug</a>,
|
||||
<a href="socket.html#debug">DEBUG</a>,
|
||||
<a href="dns.html#dns">dns</a>,
|
||||
<a href="ftp.html#ftp">ftp</a>,
|
||||
<a href="http.html#http">http</a>,
|
||||
<a href="socket.html#protect">protect</a>,
|
||||
<a href="socket.html#select">select</a>,
|
||||
<a href="socket.html#sink">sink</a>,
|
||||
<a href="socket.html#source">source</a>,
|
||||
<a href="socket.html#sleep">sleep</a>,
|
||||
<a href="smtp.html#smtp">smtp</a>,
|
||||
<a href="socket.html#time">time</a>,
|
||||
<a href="tcp.html#tcp">tcp</a>,
|
||||
<a href="socket.html#try">try</a>,
|
||||
<a href="udp.html#udp">udp</a>,
|
||||
<a href="url.html#url">url</a>,
|
||||
<a href="socket.html#version">version</a>.
|
||||
<a href="socket.html#version">VERSION</a>.
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</table>
|
||||
@ -179,7 +157,7 @@
|
||||
<!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="tcp.html">TCP (socket.tcp)</a>
|
||||
<a href="tcp.html">TCP (in socket)</a>
|
||||
<blockquote>
|
||||
<a href="tcp.html#accept">accept</a>,
|
||||
<a href="tcp.html#bind">bind</a>,
|
||||
@ -198,7 +176,7 @@
|
||||
<!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="udp.html">UDP (socket.udp)</a>
|
||||
<a href="udp.html">UDP (in socket)</a>
|
||||
<blockquote>
|
||||
<a href="udp.html#close">close</a>,
|
||||
<a href="udp.html#getpeername">getpeername</a>,
|
||||
@ -210,23 +188,22 @@
|
||||
<a href="udp.html#setpeername">setpeername</a>,
|
||||
<a href="udp.html#setsockname">setsockname</a>,
|
||||
<a href="udp.html#setoption">setoption</a>,
|
||||
<a href="udp.html#settimeout">settimeout</a>,
|
||||
<a href="udp.html#settimeout">shutdown</a>.
|
||||
<a href="udp.html#settimeout">settimeout</a>.
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<blockquote>
|
||||
<a href="url.html">URL (socket.url) </a>
|
||||
<a href="url.html">URL</a>
|
||||
<blockquote>
|
||||
<a href="url.html#absolute">absolute</a>,
|
||||
<a href="url.html#build">build</a>,
|
||||
<a href="url.html#build_path">build_path</a>,
|
||||
<a href="url.html#quote">quote</a>,
|
||||
<a href="url.html#escape">escape</a>,
|
||||
<a href="url.html#parse">parse</a>,
|
||||
<a href="url.html#parse_path">parse_path</a>,
|
||||
<a href="url.html#quote">unquote</a>.
|
||||
<a href="url.html#unescape">unescape</a>.
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
|
235
doc/smtp.html
235
doc/smtp.html
@ -35,15 +35,22 @@
|
||||
|
||||
<h2 id=smtp>SMTP</h2>
|
||||
|
||||
<p>
|
||||
The <tt>smtp.lua</tt> module provides functionality to send e-mail
|
||||
<p> The <tt>smtp.lua</tt> module provides functionality to send e-mail
|
||||
messages. The implementation conforms to the Simple Mail Transfer Protocol,
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>.
|
||||
The other RFC of interest in this implementation is
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>,
|
||||
Another RFC of interest is <a
|
||||
href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>,
|
||||
which governs the Internet Message Format.
|
||||
Multipart messages (those that contain attatchments) are part
|
||||
of the MIME standard, but described mainly
|
||||
in <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">RFC
|
||||
2046</a>
|
||||
|
||||
</p>
|
||||
<p> In the description below, good understanding of <a
|
||||
href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters
|
||||
sources and sinks</a> and the <a href=mime.html>MIME</a> module is
|
||||
assumed. In fact, the SMTP module was the main reason for their
|
||||
creation. </p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<!-- mail +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
<p>
|
||||
The following constants can be set to control the default behaviour of
|
||||
the SMTP module:
|
||||
</p>
|
||||
|
||||
<p class=name id=mail>
|
||||
socket.smtp.<b>mail{</b><br>
|
||||
<ul>
|
||||
<li> <tt>DOMAIN</tt>: domain used to greet the server;
|
||||
<li> <tt>PORT</tt>: default port used for the connection;
|
||||
<li> <tt>SERVER</tt>: default server used for the connection;
|
||||
<li> <tt>TIMEOUT</tt>: default timeout for all I/O operations;
|
||||
<li> <tt>ZONE</tt>: default time zone.
|
||||
</ul>
|
||||
|
||||
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=send>
|
||||
smtp.<b>send{</b><br>
|
||||
from = <i>string</i>,<br>
|
||||
rcpt = <i>string</i> or <i>string-table</i>,<br>
|
||||
body = <i>string</i>,<br>
|
||||
headers = <i>headers-table</i>,<br>
|
||||
server = <i>string</i><br>
|
||||
source = <i>LTN12 source</i>,<br>
|
||||
[server = <i>string</i>],<br>
|
||||
[port = <i>string</i>]<br>
|
||||
[domain = <i>string</i>],<br>
|
||||
[step = <i>LTN12 pump step</i>],<br>
|
||||
<b>}</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
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
|
||||
<a href=#message><tt>message</tt></a> source factory for
|
||||
a very powerful way to define the message contents.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Rcpt</tt> is a Lua table with one entry for each recipient, or a string
|
||||
The sender is given by the e-mail address in the <tt>from</tt> field.
|
||||
<tt>Rcpt</tt> 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 <tt>from</tt>.
|
||||
The message is composed by the optional MIME Headers <tt>headers</tt>
|
||||
and text <tt>body</tt>. The message is sent using the server
|
||||
<tt>server</tt>.
|
||||
The contents of the message are given by a LTN12 <tt>source</tt>. Several
|
||||
arguments are optional:
|
||||
<ul>
|
||||
<li> <tt>server</tt>: Server to connect to. Defaults to "localhost";
|
||||
<li> <tt>port</tt>: Port to connect to. Defaults to 25;
|
||||
<li> <tt>domain</tt>: Domain name used to greet the server; Defaults to the
|
||||
local machine host name;
|
||||
<li> <tt>step</tt>: LTN12 pump step function used to pass data from the
|
||||
source to the server. Defaults to the LTN12 <tt>pump.step</tt> function.
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
@ -108,6 +142,13 @@ If successful, the function returns 1. Otherwise, the function returns
|
||||
<b><tt>nil</tt></b> followed by an error message.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Note: SMTP servers are can be very picky with the format of e-mail
|
||||
addresses. To be safe, use only addresses of the form
|
||||
"<tt><fulano@tecgraf.puc-rio.br></tt>" in the <tt>from</tt> and
|
||||
<tt>rcpt</tt> arguments to the <tt>send</tt> function. In headers, e-mail
|
||||
addresses can take whatever form you like. </p>
|
||||
|
||||
<p class=note>
|
||||
Big note: There is a good deal of misconception with the use of the
|
||||
destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>',
|
||||
@ -117,11 +158,12 @@ exact opposite of what you expect.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Only recipients specified in the recipient list will receive a copy of the
|
||||
Only recipients specified in the <tt>rcpt</tt> 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 <em>not</em>
|
||||
part of the message.
|
||||
message body along with the headers, and nothing more. The headers
|
||||
<em>are</em> part of the message and should be produced by the LTN12
|
||||
<tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em>
|
||||
part of the message and will not be sent to anyone.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
@ -143,9 +185,9 @@ Copy") contains addresses of recipients of the message whose addresses are not t
|
||||
</ul>
|
||||
|
||||
<p class=note>
|
||||
The LuaSocket <tt>mail</tt> 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 <tt>send</tt> 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:
|
||||
</p>
|
||||
<ul>
|
||||
<li> If someone is to receive the message, the e-mail address <em>has</em>
|
||||
@ -171,36 +213,147 @@ and
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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."
|
||||
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."
|
||||
}
|
||||
|
||||
server = "localhost"
|
||||
|
||||
r, e = socket.smtp.mail{
|
||||
r, e = smtp.send{
|
||||
from = from,
|
||||
rcpt = rcpt,
|
||||
headers = headers,
|
||||
body = body,
|
||||
server = server
|
||||
source = smtp.message(mesgt)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=message>
|
||||
smtp.<b>message(</b>mesgt<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Returns a LTN12 source that sends an SMTP message body, possibly multipart
|
||||
(arbitrarily deep).
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
The only parameter of the function is a table describing the message.
|
||||
<tt>Mesgt</tt> has the following form (notice the recursive structure):
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<table summary="Mesgt table structure">
|
||||
<tr><td><tt>
|
||||
mesgt = {<br>
|
||||
headers = <i>header-table</i>,<br>
|
||||
body = <i>LTN12 source</i> or <i>string</i> or
|
||||
<i>multipart-mesgt</i><br>
|
||||
}<br>
|
||||
<br>
|
||||
multipart-mesgt = {<br>
|
||||
preamble = <i>string</i><br>
|
||||
[1] = <i>mesgt</i>,<br>
|
||||
[2] = <i>mesgt</i>,<br>
|
||||
...<br>
|
||||
[<i>n</i>] = <i>mesgt</i>,<br>
|
||||
epilogue = <i>string</i>,<br>
|
||||
}<br>
|
||||
</tt></td></tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
|
||||
<p class=parameters>
|
||||
For a simple message, all that is needed is a set of <tt>headers</tt>
|
||||
and the <tt>body</tt>. The message <tt>body</tt> 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.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function returns an LTN12 source that produces the message contents as
|
||||
defined by <tt>mesgt</tt>. Hopefuly, the following example will make
|
||||
things clear. When in doubt, refer to the appropriate RFC as listed in the
|
||||
introduction. </p>
|
||||
|
||||
<pre class=example>
|
||||
-- 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,
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
@ -36,16 +36,13 @@
|
||||
<h2 id=socket>The socket namespace</h2>
|
||||
|
||||
<p>
|
||||
The <tt>socket</tt> 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 <tt>socket</tt> namespace contains the core functionality of LuaSocket.
|
||||
</p>
|
||||
|
||||
<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=debug>
|
||||
socket.<b>debug</b>
|
||||
socket.<b>DEBUG</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -57,7 +54,7 @@ with debug support.
|
||||
<!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=protect>
|
||||
socket.<b>protect(</b>function<b>)</b>
|
||||
socket.<b>protect(</b>func<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -65,12 +62,12 @@ Converts a function that throws exceptions into a safe function.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Function</tt> is a function that calls
|
||||
<tt>Funct</tt> is a function that calls
|
||||
<a href=#try><tt>try</tt></a> to throw exceptions.
|
||||
</p>
|
||||
|
||||
<p class=return>
|
||||
The function an equivalent function that instead of throwing exceptoins,
|
||||
Returns an equivalent function that instead of throwing exceptions,
|
||||
returns <tt><b>nil</b></tt> followed by an error message.
|
||||
</p>
|
||||
|
||||
@ -103,16 +100,16 @@ simplify the test if a specific socket has changed status.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
<b>Important Note</b>: a known bug in WinSock causes <tt>select</tt> to fail
|
||||
<b>Important note</b>: a known bug in WinSock causes <tt>select</tt> to fail
|
||||
on non-blocking TCP sockets. The function may return a socket as
|
||||
writable even though the socket is <em>not</em> ready for sending.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
<b>Important note</b>: calling select with a server socket in the receive
|
||||
<b>Another important note</b>: calling select with a server socket in the receive
|
||||
parameter before a call to accept does <em>not</em> guarantee
|
||||
<a href=tcp.html#accept><tt>accept</tt></a> will return immediately.
|
||||
Use the <a href=tcp.html#timeout><tt>timeout</tt></a>
|
||||
Use the <a href=tcp.html#settimeout><tt>settimeout</tt></a>
|
||||
method or <tt>accept</tt> might block forever.
|
||||
</p>
|
||||
|
||||
@ -131,7 +128,7 @@ socket.<b>sink(</b>mode, socket<b>)</b>
|
||||
|
||||
<p class=description>
|
||||
Creates an
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a>
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
|
||||
sink from a stream socket object.
|
||||
</p>
|
||||
|
||||
@ -163,7 +160,7 @@ socket.<b>source(</b>mode, socket [, length]<b>)</b>
|
||||
|
||||
<p class=description>
|
||||
Creates an
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a>
|
||||
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
|
||||
source from a stream socket object.
|
||||
</p>
|
||||
|
||||
@ -217,7 +214,7 @@ c = socket.try(socket.connect("localhost", 80))
|
||||
<!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=version>
|
||||
socket.<b>version</b>
|
||||
socket.<b>VERSION</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
|
29
doc/tcp.html
29
doc/tcp.html
@ -241,18 +241,16 @@ method returns <b><tt>nil</tt></b> followed by an error message.
|
||||
<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=receive>
|
||||
client:<b>receive(</b>[pattern<sub>1</sub>, pattern<sub>2</sub>,
|
||||
... pattern<sub>N</sub>]<b>)</b>
|
||||
client:<b>receive(</b>[pattern]<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Reads data from a client object, according to the specified <em>read
|
||||
patterns</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
|
||||
pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
The parameters <tt>pattern</tt><sub>1</sub>, <tt>pattern</tt><sub>2</sub>, ...
|
||||
<tt>pattern</tt><sub>N</sub> can be any of the following:
|
||||
<tt>Pattern</tt> can be any of the following:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
@ -267,16 +265,21 @@ of bytes from the socket.
|
||||
</ul>
|
||||
|
||||
<p class=return>
|
||||
The method returns one value for each pattern, followed by a single
|
||||
error code that can be <b><tt>nil</tt></b> in case of success, the string
|
||||
'<tt>closed</tt>' in case the connection was closed before the
|
||||
transmission was completed or the string '<tt>timeout</tt>' in case
|
||||
there was a timeout during the operation.
|
||||
If successful, the method returns the received pattern. In case of error,
|
||||
the method returns <tt><b>nil</b></tt> followed by an error message which
|
||||
can be the string '<tt>closed</tt>' in case the connection was
|
||||
closed before the transmission was completed or the string
|
||||
'<tt>timeout</tt>' in case there was a timeout during the operation.
|
||||
Also, after the error message, the function returns the partial result of
|
||||
the transmission.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Note: In case of error, the method always return everything it managed
|
||||
to download before the error condition was met.
|
||||
<b>Important note</b>: This function was changed <em>severely</em>. 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
|
||||
<tt><b>nil</b></tt> on error. Thus the change.
|
||||
</p>
|
||||
|
||||
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
@ -428,7 +431,7 @@ client:<b>shutdown(</b>mode<b>)</b><br>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
Shuts down part of a full duplex connection.
|
||||
Shuts down part of a full-duplex connection.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
|
@ -29,6 +29,11 @@
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<h2 id=udp>UDP</h2>
|
||||
|
||||
<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class="name" id="socket.udp">
|
||||
|
76
doc/url.html
76
doc/url.html
@ -59,7 +59,7 @@ An URL is defined by the following grammar:
|
||||
<!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=absolute>
|
||||
socket.url.<b>absolute(</b>base, relative<b>)</b>
|
||||
url.<b>absolute(</b>base, relative<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -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
|
||||
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>.
|
||||
The example bellow should give an idea of what are the rules.
|
||||
The example bellow should give an idea of what the rules are.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
@ -114,7 +114,7 @@ g;x?y#s = http://a/b/c/g;x?y#s
|
||||
<!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=build>
|
||||
socket.url.<b>build(</b>parsed_url<b>)</b>
|
||||
url.<b>build(</b>parsed_url<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -135,7 +135,7 @@ The function returns a string with the built URL.
|
||||
<!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=build_path>
|
||||
socket.url.<b>build_path(</b>segments, unsafe<b>)</b>
|
||||
url.<b>build_path(</b>segments, unsafe<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -157,10 +157,39 @@ The function returns a string with the
|
||||
built <tt><path></tt> component.
|
||||
</p>
|
||||
|
||||
<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="escape">
|
||||
url.<b>escape(</b>content<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Content</tt> is the string to be encoded.
|
||||
</p>
|
||||
|
||||
<p class=result>
|
||||
The function returns the encoded string.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
-- load url module
|
||||
url = require("url")
|
||||
|
||||
code = url.escape("/#?;")
|
||||
-- code = "%2f%23%3f%3b"
|
||||
</pre>
|
||||
|
||||
<!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=parse>
|
||||
socket.url.<b>parse(</b>url, default<b>)</b>
|
||||
url.<b>parse(</b>url, default<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -196,7 +225,10 @@ parsed_url = {<br>
|
||||
</tt></blockquote>
|
||||
|
||||
<pre class=example>
|
||||
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
|
||||
<!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id=parse_path>
|
||||
socket.url.<b>parse_path(</b>path<b>)</b>
|
||||
url.<b>parse_path(</b>path<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
@ -241,36 +273,10 @@ returning a list with all the parsed segments, the function unescapes all
|
||||
of them.
|
||||
</p>
|
||||
|
||||
<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="escape">
|
||||
socket.url.<b>escape(</b>content<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
<tt>Content</tt> is the string to be encoded.
|
||||
</p>
|
||||
|
||||
<p class=result>
|
||||
The function returns the encoded string.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
code = socket.url.escape("/#?;")
|
||||
-- code = "%2f%23%3f%3b"
|
||||
</pre>
|
||||
|
||||
<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<p class=name id="unescape">
|
||||
socket.url.<b>unescape(</b>content<b>)</b>
|
||||
url.<b>unescape(</b>content<b>)</b>
|
||||
</p>
|
||||
|
||||
<p class=description>
|
||||
|
17
etc/check-memory.lua
Normal file
17
etc/check-memory.lua
Normal file
@ -0,0 +1,17 @@
|
||||
function load(s)
|
||||
collectgarbage()
|
||||
local a = gcinfo()
|
||||
_G[s] = require(s)
|
||||
collectgarbage()
|
||||
local b = gcinfo()
|
||||
print(s .. ":\t " .. (b-a) .. "k")
|
||||
end
|
||||
|
||||
load("socket")
|
||||
load("url")
|
||||
load("ltn12")
|
||||
load("mime")
|
||||
load("tp")
|
||||
load("smtp")
|
||||
load("http")
|
||||
load("ftp")
|
@ -8,6 +8,7 @@ socket = require("socket")
|
||||
http = require("http")
|
||||
ftp = require("ftp")
|
||||
url = require("url")
|
||||
ltn12 = require("ltn12")
|
||||
|
||||
-- formats a number of seconds into human readable form
|
||||
function nicetime(s)
|
||||
|
@ -4,7 +4,7 @@
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id$
|
||||
-----------------------------------------------------------------------------
|
||||
require("socket")
|
||||
socket = require("socket")
|
||||
host = host or "*"
|
||||
port1 = port1 or 8080
|
||||
port2 = port2 or 8181
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "luasocket.h"
|
||||
#include "auxiliar.h"
|
||||
|
||||
/*=========================================================================*\
|
||||
@ -16,16 +15,15 @@
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Initializes the module
|
||||
\*-------------------------------------------------------------------------*/
|
||||
int aux_open(lua_State *L)
|
||||
{
|
||||
int aux_open(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Creates a new class with given methods
|
||||
* Methods whose names start with __ are passed directly to the metatable.
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void aux_newclass(lua_State *L, const char *classname, luaL_reg *func)
|
||||
{
|
||||
void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) {
|
||||
luaL_newmetatable(L, classname); /* mt */
|
||||
/* create __index table to place methods */
|
||||
lua_pushstring(L, "__index"); /* mt,"__index" */
|
||||
@ -45,11 +43,31 @@ void aux_newclass(lua_State *L, const char *classname, luaL_reg *func)
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Prints the value of a class in a nice way
|
||||
\*-------------------------------------------------------------------------*/
|
||||
int aux_tostring(lua_State *L) {
|
||||
char buf[32];
|
||||
if (!lua_getmetatable(L, 1)) goto error;
|
||||
lua_pushstring(L, "__index");
|
||||
lua_gettable(L, -2);
|
||||
if (!lua_istable(L, -1)) goto error;
|
||||
lua_pushstring(L, "class");
|
||||
lua_gettable(L, -2);
|
||||
if (!lua_isstring(L, -1)) goto error;
|
||||
sprintf(buf, "%p", lua_touserdata(L, 1));
|
||||
lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf);
|
||||
return 1;
|
||||
error:
|
||||
lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'");
|
||||
lua_error(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Insert class into group
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void aux_add2group(lua_State *L, const char *classname, const char *groupname)
|
||||
{
|
||||
void aux_add2group(lua_State *L, const char *classname, const char *groupname) {
|
||||
luaL_getmetatable(L, classname);
|
||||
lua_pushstring(L, groupname);
|
||||
lua_pushboolean(L, 1);
|
||||
@ -60,8 +78,7 @@ void aux_add2group(lua_State *L, const char *classname, const char *groupname)
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Make sure argument is a boolean
|
||||
\*-------------------------------------------------------------------------*/
|
||||
int aux_checkboolean(lua_State *L, int objidx)
|
||||
{
|
||||
int aux_checkboolean(lua_State *L, int objidx) {
|
||||
if (!lua_isboolean(L, objidx))
|
||||
luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN));
|
||||
return lua_toboolean(L, objidx);
|
||||
@ -71,8 +88,7 @@ int aux_checkboolean(lua_State *L, int objidx)
|
||||
* Return userdata pointer if object belongs to a given class, abort with
|
||||
* error otherwise
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void *aux_checkclass(lua_State *L, const char *classname, int objidx)
|
||||
{
|
||||
void *aux_checkclass(lua_State *L, const char *classname, int objidx) {
|
||||
void *data = aux_getclassudata(L, classname, objidx);
|
||||
if (!data) {
|
||||
char msg[45];
|
||||
@ -86,8 +102,7 @@ void *aux_checkclass(lua_State *L, const char *classname, int objidx)
|
||||
* Return userdata pointer if object belongs to a given group, abort with
|
||||
* error otherwise
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void *aux_checkgroup(lua_State *L, const char *groupname, int objidx)
|
||||
{
|
||||
void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) {
|
||||
void *data = aux_getgroupudata(L, groupname, objidx);
|
||||
if (!data) {
|
||||
char msg[45];
|
||||
@ -100,8 +115,7 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx)
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Set object class
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void aux_setclass(lua_State *L, const char *classname, int objidx)
|
||||
{
|
||||
void aux_setclass(lua_State *L, const char *classname, int objidx) {
|
||||
luaL_getmetatable(L, classname);
|
||||
if (objidx < 0) objidx--;
|
||||
lua_setmetatable(L, objidx);
|
||||
@ -111,8 +125,7 @@ void aux_setclass(lua_State *L, const char *classname, int objidx)
|
||||
* Get a userdata pointer if object belongs to a given group. Return NULL
|
||||
* otherwise
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx)
|
||||
{
|
||||
void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) {
|
||||
if (!lua_getmetatable(L, objidx))
|
||||
return NULL;
|
||||
lua_pushstring(L, groupname);
|
||||
@ -130,7 +143,6 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx)
|
||||
* Get a userdata pointer if object belongs to a given class. Return NULL
|
||||
* otherwise
|
||||
\*-------------------------------------------------------------------------*/
|
||||
void *aux_getclassudata(lua_State *L, const char *classname, int objidx)
|
||||
{
|
||||
void *aux_getclassudata(lua_State *L, const char *classname, int objidx) {
|
||||
return luaL_checkudata(L, objidx, classname);
|
||||
}
|
||||
|
@ -2,26 +2,28 @@
|
||||
#define AUX_H
|
||||
/*=========================================================================*\
|
||||
* Auxiliar routines for class hierarchy manipulation
|
||||
* LuaSocket toolkit
|
||||
* LuaSocket toolkit (but completely independent of other LuaSocket modules)
|
||||
*
|
||||
* A LuaSocket class is a name associated with Lua metatables. A LuaSocket
|
||||
* group is a name associated to a class. A class can belong to any number
|
||||
* group is a name associated with a class. A class can belong to any number
|
||||
* of groups. This module provides the functionality to:
|
||||
*
|
||||
* - create new classes
|
||||
* - add classes to groups
|
||||
* - set the class of object
|
||||
* - set the class of objects
|
||||
* - check if an object belongs to a given class or group
|
||||
* - get the userdata associated to objects
|
||||
* - print objects in a pretty way
|
||||
*
|
||||
* LuaSocket class names follow the convention <module>{<class>}. Modules
|
||||
* can define any number of classes and groups. The module tcp.c, for
|
||||
* example, defines the classes tcp{master}, tcp{client} and tcp{server} and
|
||||
* the groups tcp{client, server} and tcp{any}. Module functions can then
|
||||
* perform type-checking on it's arguments by either class or group.
|
||||
* the groups tcp{client,server} and tcp{any}. Module functions can then
|
||||
* perform type-checking on their arguments by either class or group.
|
||||
*
|
||||
* LuaSocket metatables define the __index metamethod as being a table. This
|
||||
* table has one field for each method supported by the class. In DEBUG
|
||||
* mode, it also has one field with the class name.
|
||||
* table has one field for each method supported by the class, and a field
|
||||
* "class" with the class name.
|
||||
*
|
||||
* The mapping from class name to the corresponding metatable and the
|
||||
* reverse mapping are done using lauxlib.
|
||||
@ -32,14 +34,6 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
/* min and max macros */
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? x : y)
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? x : y)
|
||||
#endif
|
||||
|
||||
int aux_open(lua_State *L);
|
||||
void aux_newclass(lua_State *L, const char *classname, luaL_reg *func);
|
||||
void aux_add2group(lua_State *L, const char *classname, const char *group);
|
||||
@ -49,5 +43,6 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx);
|
||||
void *aux_getclassudata(lua_State *L, const char *groupname, int objidx);
|
||||
void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx);
|
||||
int aux_checkboolean(lua_State *L, int objidx);
|
||||
int aux_tostring(lua_State *L);
|
||||
|
||||
#endif /* AUX_H */
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "auxiliar.h"
|
||||
#include "buffer.h"
|
||||
|
||||
/*=========================================================================*\
|
||||
@ -20,6 +19,14 @@ static int buf_get(p_buf buf, const char **data, size_t *count);
|
||||
static void buf_skip(p_buf buf, size_t count);
|
||||
static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent);
|
||||
|
||||
/* min and max macros */
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? x : y)
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? x : y)
|
||||
#endif
|
||||
|
||||
/*=========================================================================*\
|
||||
* Exported functions
|
||||
\*=========================================================================*/
|
||||
|
52
src/except.c
Normal file
52
src/except.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include <lauxlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "except.h"
|
||||
|
||||
static int global_try(lua_State *L);
|
||||
static int global_protect(lua_State *L);
|
||||
static int protected(lua_State *L);
|
||||
|
||||
static luaL_reg func[] = {
|
||||
{"try", global_try},
|
||||
{"protect", global_protect},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Exception handling: try method
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int global_try(lua_State *L) {
|
||||
if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
|
||||
lua_settop(L, 2);
|
||||
lua_error(L);
|
||||
return 0;
|
||||
} else return lua_gettop(L);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Exception handling: protect factory
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int protected(lua_State *L) {
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) {
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, 1);
|
||||
return 2;
|
||||
} else return lua_gettop(L);
|
||||
}
|
||||
|
||||
static int global_protect(lua_State *L) {
|
||||
lua_insert(L, 1);
|
||||
lua_pushcclosure(L, protected, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Init module
|
||||
\*-------------------------------------------------------------------------*/
|
||||
int except_open(lua_State *L) {
|
||||
luaL_openlib(L, NULL, func, 0);
|
||||
return 0;
|
||||
}
|
35
src/except.h
Normal file
35
src/except.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef EXCEPT_H
|
||||
#define EXCEPT_H
|
||||
/*=========================================================================*\
|
||||
* Exception control
|
||||
* LuaSocket toolkit (but completely independent from other modules)
|
||||
*
|
||||
* This provides support for simple exceptions in Lua. During the
|
||||
* development of the HTTP/FTP/SMTP support, it became aparent that
|
||||
* error checking was taking a substantial amount of the coding. These
|
||||
* function greatly simplify the task of checking errors.
|
||||
*
|
||||
* The main idea is that functions should return nil as its first return
|
||||
* value when it finds an error, and return an error message (or value)
|
||||
* following nil. In case of success, as long as the first value is not nil,
|
||||
* the other values don't matter.
|
||||
*
|
||||
* The idea is to nest function calls with the "try" function. This function
|
||||
* checks the first value, and calls "error" on the second if the first is
|
||||
* nil. Otherwise, it returns all values it received.
|
||||
*
|
||||
* The protect function returns a new function that behaves exactly like the
|
||||
* function it receives, but the new function doesn't throw exceptions: it
|
||||
* returns nil followed by the error message instead.
|
||||
*
|
||||
* With these two function, it's easy to write functions that throw
|
||||
* exceptions on error, but that don't interrupt the user script.
|
||||
*
|
||||
* RCS ID: $Id$
|
||||
\*=========================================================================*/
|
||||
|
||||
#include <lua.h>
|
||||
|
||||
int except_open(lua_State *L);
|
||||
|
||||
#endif
|
73
src/ftp.lua
73
src/ftp.lua
@ -7,7 +7,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load other required modules
|
||||
-- Load required modules
|
||||
-----------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
local ltn12 = require("ltn12")
|
||||
@ -17,10 +17,7 @@ local tp = require("tp")
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-----------------------------------------------------------------------------
|
||||
local ftp = {}
|
||||
-- make all module globals fall into namespace
|
||||
setmetatable(ftp, { __index = _G })
|
||||
setfenv(1, ftp)
|
||||
_LOADED["ftp"] = getfenv(1)
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
@ -32,9 +29,7 @@ PORT = 21
|
||||
-- this is the default anonymous password. used when no password is
|
||||
-- provided in url. should be changed to your e-mail.
|
||||
USER = "ftp"
|
||||
EMAIL = "anonymous@anonymous.org"
|
||||
-- block size used in transfers
|
||||
BLOCKSIZE = 2048
|
||||
PASSWORD = "anonymous@anonymous.org"
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Low level FTP API
|
||||
@ -42,7 +37,7 @@ BLOCKSIZE = 2048
|
||||
local metat = { __index = {} }
|
||||
|
||||
function open(server, port)
|
||||
local tp = socket.try(socket.tp.connect(server, port or PORT))
|
||||
local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT))
|
||||
return setmetatable({tp = tp}, metat)
|
||||
end
|
||||
|
||||
@ -51,14 +46,17 @@ local function port(portt)
|
||||
end
|
||||
|
||||
local function pasv(pasvt)
|
||||
return socket.connect(pasvt.ip, pasvt.port)
|
||||
local data = socket.try(socket.tcp())
|
||||
socket.try(data:settimeout(TIMEOUT))
|
||||
socket.try(data:connect(pasvt.ip, pasvt.port))
|
||||
return data
|
||||
end
|
||||
|
||||
function metat.__index:login(user, password)
|
||||
socket.try(self.tp:command("user", user or USER))
|
||||
local code, reply = socket.try(self.tp:check{"2..", 331})
|
||||
if code == 331 then
|
||||
socket.try(self.tp:command("pass", password or EMAIL))
|
||||
socket.try(self.tp:command("pass", password or PASSWORD))
|
||||
socket.try(self.tp:check("2.."))
|
||||
end
|
||||
return 1
|
||||
@ -104,6 +102,7 @@ function metat.__index:send(sendt)
|
||||
socket.try(self.pasvt or self.portt, "need port or pasv first")
|
||||
if self.pasvt then data = socket.try(pasv(self.pasvt)) end
|
||||
local argument = sendt.argument or string.gsub(sendt.path, "^/", "")
|
||||
if argument == "" then argument = nil end
|
||||
local command = sendt.command or "stor"
|
||||
socket.try(self.tp:command(command, argument))
|
||||
local code, reply = socket.try(self.tp:check{"2..", "1.."})
|
||||
@ -133,6 +132,7 @@ function metat.__index:receive(recvt)
|
||||
socket.try(self.pasvt or self.portt, "need port or pasv first")
|
||||
if self.pasvt then data = socket.try(pasv(self.pasvt)) end
|
||||
local argument = recvt.argument or string.gsub(recvt.path, "^/", "")
|
||||
if argument == "" then argument = nil end
|
||||
local command = recvt.command or "retr"
|
||||
socket.try(self.tp:command(command, argument))
|
||||
local code = socket.try(self.tp:check{"1..", "2.."})
|
||||
@ -182,14 +182,14 @@ end
|
||||
-- High level FTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function tput(putt)
|
||||
local ftp = socket.ftp.open(putt.host, putt.port)
|
||||
ftp:greet()
|
||||
ftp:login(putt.user, putt.password)
|
||||
if putt.type then ftp:type(putt.type) end
|
||||
ftp:pasv()
|
||||
ftp:send(putt)
|
||||
ftp:quit()
|
||||
return ftp:close()
|
||||
local con = ftp.open(putt.host, putt.port)
|
||||
con:greet()
|
||||
con:login(putt.user, putt.password)
|
||||
if putt.type then con:type(putt.type) end
|
||||
con:pasv()
|
||||
con:send(putt)
|
||||
con:quit()
|
||||
return con:close()
|
||||
end
|
||||
|
||||
local default = {
|
||||
@ -198,15 +198,16 @@ local default = {
|
||||
}
|
||||
|
||||
local function parse(u)
|
||||
local putt = socket.try(url.parse(u, default))
|
||||
socket.try(putt.scheme == "ftp", "invalid scheme '" .. putt.scheme .. "'")
|
||||
socket.try(putt.host, "invalid host")
|
||||
local t = socket.try(url.parse(u, default))
|
||||
socket.try(t.scheme == "ftp", "invalid scheme '" .. t.scheme .. "'")
|
||||
socket.try(t.host, "invalid host")
|
||||
local pat = "^type=(.)$"
|
||||
if putt.params then
|
||||
putt.type = socket.skip(2, string.find(putt.params, pat))
|
||||
socket.try(putt.type == "a" or putt.type == "i")
|
||||
if t.params then
|
||||
t.type = socket.skip(2, string.find(t.params, pat))
|
||||
socket.try(t.type == "a" or t.type == "i",
|
||||
"invalid type '" .. t.type .. "'")
|
||||
end
|
||||
return putt
|
||||
return t
|
||||
end
|
||||
|
||||
local function sput(u, body)
|
||||
@ -221,17 +222,17 @@ put = socket.protect(function(putt, body)
|
||||
end)
|
||||
|
||||
local function tget(gett)
|
||||
local ftp = socket.ftp.open(gett.host, gett.port)
|
||||
ftp:greet()
|
||||
ftp:login(gett.user, gett.password)
|
||||
if gett.type then ftp:type(gett.type) end
|
||||
ftp:pasv()
|
||||
ftp:receive(gett)
|
||||
ftp:quit()
|
||||
return ftp:close()
|
||||
local con = ftp.open(gett.host, gett.port)
|
||||
con:greet()
|
||||
con:login(gett.user, gett.password)
|
||||
if gett.type then con:type(gett.type) end
|
||||
con:pasv()
|
||||
con:receive(gett)
|
||||
con:quit()
|
||||
return con:close()
|
||||
end
|
||||
|
||||
local function sget(u, body)
|
||||
local function sget(u)
|
||||
local gett = parse(u)
|
||||
local t = {}
|
||||
gett.sink = ltn12.sink.table(t)
|
||||
@ -240,7 +241,7 @@ local function sget(u, body)
|
||||
end
|
||||
|
||||
get = socket.protect(function(gett)
|
||||
if type(gett) == "string" then return sget(gett, body)
|
||||
if type(gett) == "string" then return sget(gett)
|
||||
else return tget(gett) end
|
||||
end)
|
||||
|
||||
|
357
src/http.lua
357
src/http.lua
@ -7,7 +7,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load other required modules
|
||||
-- Load required modules
|
||||
-------------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
local ltn12 = require("ltn12")
|
||||
@ -17,42 +17,68 @@ local url = require("url")
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-------------------------------------------------------------------------------
|
||||
http = {}
|
||||
-- make all module globals fall into namespace
|
||||
setmetatable(http, { __index = _G })
|
||||
setfenv(1, http)
|
||||
_LOADED["http"] = getfenv(1)
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
-- connection timeout in seconds
|
||||
TIMEOUT = 60
|
||||
TIMEOUT = 4
|
||||
-- default port for document retrieval
|
||||
PORT = 80
|
||||
-- user agent field sent in request
|
||||
USERAGENT = socket.version
|
||||
USERAGENT = socket.VERSION
|
||||
-- block size used in transfers
|
||||
BLOCKSIZE = 2048
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Function return value selectors
|
||||
-- Low level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function second(a, b)
|
||||
return b
|
||||
local metat = { __index = {} }
|
||||
|
||||
function open(host, port)
|
||||
local con = socket.try(socket.tcp())
|
||||
socket.try(con:settimeout(TIMEOUT))
|
||||
port = port or PORT
|
||||
socket.try(con:connect(host, port))
|
||||
return setmetatable({ con = con }, metat)
|
||||
end
|
||||
|
||||
local function third(a, b, c)
|
||||
return c
|
||||
function metat.__index:sendrequestline(method, uri)
|
||||
local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
|
||||
return socket.try(self.con:send(reqline))
|
||||
end
|
||||
|
||||
local function receive_headers(reqt, respt, tmp)
|
||||
local sock = tmp.sock
|
||||
function metat.__index:sendheaders(headers)
|
||||
for i, v in pairs(headers) do
|
||||
socket.try(self.con:send(i .. ": " .. v .. "\r\n"))
|
||||
end
|
||||
-- mark end of request headers
|
||||
socket.try(self.con:send("\r\n"))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:sendbody(headers, source, step)
|
||||
source = source or ltn12.source.empty()
|
||||
step = step or ltn12.pump.step
|
||||
-- if we don't know the size in advance, send chunked and hope for the best
|
||||
local mode
|
||||
if headers["content-length"] then mode = "keep-open"
|
||||
else mode = "http-chunked" end
|
||||
return socket.try(ltn12.pump.all(source, socket.sink(mode, self.con), step))
|
||||
end
|
||||
|
||||
function metat.__index:receivestatusline()
|
||||
local status = socket.try(self.con:receive())
|
||||
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
|
||||
return socket.try(tonumber(code), status)
|
||||
end
|
||||
|
||||
function metat.__index:receiveheaders()
|
||||
local line, name, value
|
||||
local headers = {}
|
||||
-- store results
|
||||
respt.headers = headers
|
||||
-- get first line
|
||||
line = socket.try(sock:receive())
|
||||
line = socket.try(self.con:receive())
|
||||
-- headers go until a blank line is found
|
||||
while line ~= "" do
|
||||
-- get field-name and value
|
||||
@ -60,189 +86,137 @@ local function receive_headers(reqt, respt, tmp)
|
||||
socket.try(name and value, "malformed reponse headers")
|
||||
name = string.lower(name)
|
||||
-- get next line (value might be folded)
|
||||
line = socket.try(sock:receive())
|
||||
line = socket.try(self.con:receive())
|
||||
-- unfold any folded values
|
||||
while string.find(line, "^%s") do
|
||||
value = value .. line
|
||||
line = socket.try(sock:receive())
|
||||
line = socket.try(self.con:receive())
|
||||
end
|
||||
-- save pair in table
|
||||
if headers[name] then headers[name] = headers[name] .. ", " .. value
|
||||
else headers[name] = value end
|
||||
end
|
||||
return headers
|
||||
end
|
||||
|
||||
local function receive_body(reqt, respt, tmp)
|
||||
local sink = reqt.sink or ltn12.sink.null()
|
||||
local step = reqt.step or ltn12.pump.step
|
||||
local source
|
||||
local te = respt.headers["transfer-encoding"]
|
||||
if te and te ~= "identity" then
|
||||
-- get by chunked transfer-coding of message body
|
||||
source = socket.source("http-chunked", tmp.sock)
|
||||
elseif tonumber(respt.headers["content-length"]) then
|
||||
-- get by content-length
|
||||
local length = tonumber(respt.headers["content-length"])
|
||||
source = socket.source("by-length", tmp.sock, length)
|
||||
else
|
||||
-- get it all until connection closes
|
||||
source = socket.source(tmp.sock)
|
||||
end
|
||||
socket.try(ltn12.pump.all(source, sink, step))
|
||||
function metat.__index:receivebody(headers, sink, step)
|
||||
sink = sink or ltn12.sink.null()
|
||||
step = step or ltn12.pump.step
|
||||
local length = tonumber(headers["content-length"])
|
||||
local TE = headers["transfer-encoding"]
|
||||
local mode
|
||||
if TE and TE ~= "identity" then mode = "http-chunked"
|
||||
elseif tonumber(headers["content-length"]) then mode = "by-length"
|
||||
else mode = "default" end
|
||||
return socket.try(ltn12.pump.all(socket.source(mode, self.con, length),
|
||||
sink, step))
|
||||
end
|
||||
|
||||
local function send_headers(sock, headers)
|
||||
-- send request headers
|
||||
for i, v in pairs(headers) do
|
||||
socket.try(sock:send(i .. ": " .. v .. "\r\n"))
|
||||
end
|
||||
-- mark end of request headers
|
||||
socket.try(sock:send("\r\n"))
|
||||
function metat.__index:close()
|
||||
return self.con:close()
|
||||
end
|
||||
|
||||
local function should_receive_body(reqt, respt, tmp)
|
||||
if reqt.method == "HEAD" then return nil end
|
||||
if respt.code == 204 or respt.code == 304 then return nil end
|
||||
if respt.code >= 100 and respt.code < 200 then return nil end
|
||||
return 1
|
||||
end
|
||||
|
||||
local function receive_status(reqt, respt, tmp)
|
||||
local status = socket.try(tmp.sock:receive())
|
||||
local code = third(string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
|
||||
-- store results
|
||||
respt.code, respt.status = tonumber(code), status
|
||||
end
|
||||
|
||||
local function request_uri(reqt, respt, tmp)
|
||||
local u = tmp.parsed
|
||||
if not reqt.proxy then
|
||||
local parsed = tmp.parsed
|
||||
u = {
|
||||
path = parsed.path,
|
||||
params = parsed.params,
|
||||
query = parsed.query,
|
||||
fragment = parsed.fragment
|
||||
-----------------------------------------------------------------------------
|
||||
-- High level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function uri(reqt)
|
||||
local u = reqt
|
||||
if not reqt.proxy and not PROXY then
|
||||
u = {
|
||||
path = reqt.path,
|
||||
params = reqt.params,
|
||||
query = reqt.query,
|
||||
fragment = reqt.fragment
|
||||
}
|
||||
end
|
||||
return url.build(u)
|
||||
end
|
||||
|
||||
local function send_request(reqt, respt, tmp)
|
||||
local uri = request_uri(reqt, respt, tmp)
|
||||
local headers = tmp.headers
|
||||
local step = reqt.step or ltn12.pump.step
|
||||
-- send request line
|
||||
socket.try(tmp.sock:send((reqt.method or "GET")
|
||||
.. " " .. uri .. " HTTP/1.1\r\n"))
|
||||
if reqt.source and not headers["content-length"] then
|
||||
headers["transfer-encoding"] = "chunked"
|
||||
end
|
||||
send_headers(tmp.sock, headers)
|
||||
-- send request message body, if any
|
||||
if not reqt.source then return end
|
||||
if headers["content-length"] then
|
||||
socket.try(ltn12.pump.all(reqt.source,
|
||||
socket.sink(tmp.sock), step))
|
||||
else
|
||||
socket.try(ltn12.pump.all(reqt.source,
|
||||
socket.sink("http-chunked", tmp.sock), step))
|
||||
end
|
||||
end
|
||||
|
||||
local function open(reqt, respt, tmp)
|
||||
local proxy = reqt.proxy or PROXY
|
||||
local host, port
|
||||
if proxy then
|
||||
local pproxy = url.parse(proxy)
|
||||
socket.try(pproxy.port and pproxy.host, "invalid proxy")
|
||||
host, port = pproxy.host, pproxy.port
|
||||
else
|
||||
host, port = tmp.parsed.host, tmp.parsed.port
|
||||
end
|
||||
-- store results
|
||||
tmp.sock = socket.try(socket.tcp())
|
||||
socket.try(tmp.sock:settimeout(reqt.timeout or TIMEOUT))
|
||||
socket.try(tmp.sock:connect(host, port))
|
||||
end
|
||||
|
||||
local function adjust_headers(reqt, respt, tmp)
|
||||
local function adjustheaders(headers, host)
|
||||
local lower = {}
|
||||
-- override with user values
|
||||
for i,v in (reqt.headers or lower) do
|
||||
for i,v in (headers or lower) do
|
||||
lower[string.lower(i)] = v
|
||||
end
|
||||
lower["user-agent"] = lower["user-agent"] or USERAGENT
|
||||
-- these cannot be overriden
|
||||
lower["host"] = tmp.parsed.host
|
||||
lower["connection"] = "close"
|
||||
-- store results
|
||||
tmp.headers = lower
|
||||
lower["host"] = host
|
||||
return lower
|
||||
end
|
||||
|
||||
local function parse_url(reqt, respt, tmp)
|
||||
local function adjustrequest(reqt)
|
||||
-- parse url with default fields
|
||||
local parsed = url.parse(reqt.url, {
|
||||
host = "",
|
||||
port = PORT,
|
||||
port = PORT,
|
||||
path ="/",
|
||||
scheme = "http"
|
||||
scheme = "http"
|
||||
})
|
||||
-- scheme has to be http
|
||||
socket.try(parsed.scheme == "http",
|
||||
string.format("unknown scheme '%s'", parsed.scheme))
|
||||
-- explicit authentication info overrides that given by the URL
|
||||
parsed.user = reqt.user or parsed.user
|
||||
parsed.password = reqt.password or parsed.password
|
||||
-- store results
|
||||
tmp.parsed = parsed
|
||||
-- explicit info in reqt overrides that given by the URL
|
||||
for i,v in reqt do parsed[i] = v end
|
||||
-- compute uri if user hasn't overriden
|
||||
parsed.uri = parsed.uri or uri(parsed)
|
||||
-- adjust headers in request
|
||||
parsed.headers = adjustheaders(parsed.headers, parsed.host)
|
||||
return parsed
|
||||
end
|
||||
|
||||
-- forward declaration
|
||||
local request_p
|
||||
local function shouldredirect(reqt, respt)
|
||||
return (reqt.redirect ~= false) and
|
||||
(respt.code == 301 or respt.code == 302) and
|
||||
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
|
||||
and (not reqt.nredirects or reqt.nredirects < 5)
|
||||
end
|
||||
|
||||
local function should_authorize(reqt, respt, tmp)
|
||||
local function shouldauthorize(reqt, respt)
|
||||
-- if there has been an authorization attempt, it must have failed
|
||||
if reqt.headers and reqt.headers["authorization"] then return nil end
|
||||
-- if last attempt didn't fail due to lack of authentication,
|
||||
-- or we don't have authorization information, we can't retry
|
||||
return respt.code == 401 and tmp.parsed.user and tmp.parsed.password
|
||||
return respt.code == 401 and reqt.user and reqt.password
|
||||
end
|
||||
|
||||
local function clone(headers)
|
||||
if not headers then return nil end
|
||||
local copy = {}
|
||||
for i,v in pairs(headers) do
|
||||
copy[i] = v
|
||||
local function shouldreceivebody(reqt, respt)
|
||||
if reqt.method == "HEAD" then return nil end
|
||||
local code = respt.code
|
||||
if code == 204 or code == 304 then return nil end
|
||||
if code >= 100 and code < 200 then return nil end
|
||||
return 1
|
||||
end
|
||||
|
||||
local requestp, authorizep, redirectp
|
||||
|
||||
function requestp(reqt)
|
||||
local reqt = adjustrequest(reqt)
|
||||
local respt = {}
|
||||
local con = open(reqt.host, reqt.port)
|
||||
con:sendrequestline(reqt.method, reqt.uri)
|
||||
con:sendheaders(reqt.headers)
|
||||
con:sendbody(reqt.headers, reqt.source, reqt.step)
|
||||
respt.code, respt.status = con:receivestatusline()
|
||||
respt.headers = con:receiveheaders()
|
||||
if shouldredirect(reqt, respt) then
|
||||
con:close()
|
||||
return redirectp(reqt, respt)
|
||||
elseif shouldauthorize(reqt, respt) then
|
||||
con:close()
|
||||
return authorizep(reqt, respt)
|
||||
elseif shouldreceivebody(reqt, respt) then
|
||||
con:receivebody(respt.headers, reqt.sink, reqt.step)
|
||||
end
|
||||
return copy
|
||||
con:close()
|
||||
return respt
|
||||
end
|
||||
|
||||
local function authorize(reqt, respt, tmp)
|
||||
local headers = clone(reqt.headers) or {}
|
||||
headers["authorization"] = "Basic " ..
|
||||
(mime.b64(tmp.parsed.user .. ":" .. tmp.parsed.password))
|
||||
local autht = {
|
||||
method = reqt.method,
|
||||
url = reqt.url,
|
||||
source = reqt.source,
|
||||
sink = reqt.sink,
|
||||
headers = headers,
|
||||
timeout = reqt.timeout,
|
||||
proxy = reqt.proxy,
|
||||
}
|
||||
request_p(autht, respt, tmp)
|
||||
function authorizep(reqt, respt)
|
||||
local auth = "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
|
||||
reqt.headers["authorization"] = auth
|
||||
return requestp(reqt)
|
||||
end
|
||||
|
||||
local function should_redirect(reqt, respt, tmp)
|
||||
return (reqt.redirect ~= false) and
|
||||
(respt.code == 301 or respt.code == 302) and
|
||||
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
|
||||
and (not tmp.nredirects or tmp.nredirects < 5)
|
||||
end
|
||||
|
||||
local function redirect(reqt, respt, tmp)
|
||||
tmp.nredirects = (tmp.nredirects or 0) + 1
|
||||
function redirectp(reqt, respt)
|
||||
-- we create a new table to get rid of anything we don't
|
||||
-- absolutely need, including authentication info
|
||||
local redirt = {
|
||||
method = reqt.method,
|
||||
-- the RFC says the redirect URL has to be absolute, but some
|
||||
@ -251,69 +225,38 @@ local function redirect(reqt, respt, tmp)
|
||||
source = reqt.source,
|
||||
sink = reqt.sink,
|
||||
headers = reqt.headers,
|
||||
timeout = reqt.timeout,
|
||||
proxy = reqt.proxy
|
||||
proxy = reqt.proxy,
|
||||
nredirects = (reqt.nredirects or 0) + 1
|
||||
}
|
||||
request_p(redirt, respt, tmp)
|
||||
respt = requestp(redirt)
|
||||
-- we pass the location header as a clue we redirected
|
||||
if respt.headers then respt.headers.location = redirt.url end
|
||||
end
|
||||
|
||||
local function skip_continue(reqt, respt, tmp)
|
||||
if respt.code == 100 then
|
||||
receive_status(reqt, respt, tmp)
|
||||
end
|
||||
end
|
||||
|
||||
-- execute a request of through an exception
|
||||
function request_p(reqt, respt, tmp)
|
||||
parse_url(reqt, respt, tmp)
|
||||
adjust_headers(reqt, respt, tmp)
|
||||
open(reqt, respt, tmp)
|
||||
send_request(reqt, respt, tmp)
|
||||
receive_status(reqt, respt, tmp)
|
||||
skip_continue(reqt, respt, tmp)
|
||||
receive_headers(reqt, respt, tmp)
|
||||
if should_redirect(reqt, respt, tmp) then
|
||||
tmp.sock:close()
|
||||
redirect(reqt, respt, tmp)
|
||||
elseif should_authorize(reqt, respt, tmp) then
|
||||
tmp.sock:close()
|
||||
authorize(reqt, respt, tmp)
|
||||
elseif should_receive_body(reqt, respt, tmp) then
|
||||
receive_body(reqt, respt, tmp)
|
||||
end
|
||||
end
|
||||
|
||||
function request(reqt)
|
||||
local respt, tmp = {}, {}
|
||||
local s, e = pcall(request_p, reqt, respt, tmp)
|
||||
if not s then respt.error = e end
|
||||
if tmp.sock then tmp.sock:close() end
|
||||
return respt
|
||||
end
|
||||
|
||||
function get(u)
|
||||
local t = {}
|
||||
respt = request {
|
||||
url = u,
|
||||
sink = ltn12.sink.table(t)
|
||||
}
|
||||
return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers,
|
||||
respt.code, respt.error
|
||||
end
|
||||
request = socket.protect(requestp)
|
||||
|
||||
function post(u, body)
|
||||
get = socket.protect(function(u)
|
||||
local t = {}
|
||||
respt = request {
|
||||
url = u,
|
||||
method = "POST",
|
||||
local respt = requestp {
|
||||
url = u,
|
||||
sink = ltn12.sink.table(t)
|
||||
}
|
||||
return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers,
|
||||
respt.code
|
||||
end)
|
||||
|
||||
post = socket.protect(function(u, body)
|
||||
local t = {}
|
||||
local respt = requestp {
|
||||
url = u,
|
||||
method = "POST",
|
||||
source = ltn12.source.string(body),
|
||||
sink = ltn12.sink.table(t),
|
||||
headers = { ["content-length"] = string.len(body) }
|
||||
headers = { ["content-length"] = string.len(body) }
|
||||
}
|
||||
return (table.getn(t) > 0 or nil) and table.concat(t),
|
||||
respt.headers, respt.code, respt.error
|
||||
end
|
||||
return (table.getn(t) > 0 or nil) and table.concat(t),
|
||||
respt.headers, respt.code
|
||||
end)
|
||||
|
||||
return http
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "luasocket.h"
|
||||
#include "inet.h"
|
||||
|
||||
/*=========================================================================*\
|
||||
|
@ -8,9 +8,8 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-----------------------------------------------------------------------------
|
||||
local ltn12 = {}
|
||||
setmetatable(ltn12, { __index = _G })
|
||||
setfenv(1, ltn12)
|
||||
_LOADED["ltn12"] = getfenv(1)
|
||||
|
||||
filter = {}
|
||||
source = {}
|
||||
sink = {}
|
||||
@ -19,10 +18,6 @@ pump = {}
|
||||
-- 2048 seems to be better in windows...
|
||||
BLOCKSIZE = 2048
|
||||
|
||||
local function shift(a, b, c)
|
||||
return b, c
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Filter stuff
|
||||
-----------------------------------------------------------------------------
|
||||
@ -53,7 +48,9 @@ local function chain2(f1, f2)
|
||||
end
|
||||
end)
|
||||
return function(chunk)
|
||||
return shift(coroutine.resume(co, chunk))
|
||||
local ret, a, b = coroutine.resume(co, chunk)
|
||||
if ret then return a, b
|
||||
else return nil, a end
|
||||
end
|
||||
end
|
||||
|
||||
@ -149,7 +146,9 @@ function source.chain(src, f)
|
||||
end
|
||||
end)
|
||||
return function()
|
||||
return shift(coroutine.resume(co))
|
||||
local ret, a, b = coroutine.resume(co)
|
||||
if ret then return a, b
|
||||
else return nil, a end
|
||||
end
|
||||
end
|
||||
|
||||
@ -166,7 +165,9 @@ function source.cat(...)
|
||||
end
|
||||
end)
|
||||
return function()
|
||||
return shift(coroutine.resume(co))
|
||||
local ret, a, b = coroutine.resume(co)
|
||||
if ret then return a, b
|
||||
else return nil, a end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "luasocket.h"
|
||||
|
||||
#include "auxiliar.h"
|
||||
#include "base.h"
|
||||
#include "except.h"
|
||||
#include "timeout.h"
|
||||
#include "buffer.h"
|
||||
#include "inet.h"
|
||||
@ -35,11 +35,18 @@
|
||||
#include "select.h"
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Modules
|
||||
* Internal function prototypes
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int global_skip(lua_State *L);
|
||||
static int global_unload(lua_State *L);
|
||||
static int base_open(lua_State *L);
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Modules and functions
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static const luaL_reg mod[] = {
|
||||
{"auxiliar", aux_open},
|
||||
{"base", base_open},
|
||||
{"except", except_open},
|
||||
{"timeout", tm_open},
|
||||
{"buffer", buf_open},
|
||||
{"inet", inet_open},
|
||||
@ -49,11 +56,69 @@ static const luaL_reg mod[] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static luaL_reg func[] = {
|
||||
{"skip", global_skip},
|
||||
{"__unload", global_unload},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Skip a few arguments
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int global_skip(lua_State *L) {
|
||||
int amount = luaL_checkint(L, 1);
|
||||
int ret = lua_gettop(L) - amount - 1;
|
||||
return ret >= 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Unloads the library
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int global_unload(lua_State *L) {
|
||||
sock_close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Setup basic stuff.
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int base_open(lua_State *L) {
|
||||
if (sock_open()) {
|
||||
/* whoever is loading the library replaced the global environment
|
||||
* with the namespace table */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
/* make sure library is still "requirable" if initialized staticaly */
|
||||
lua_pushstring(L, "_LOADEDLIB");
|
||||
lua_gettable(L, -2);
|
||||
lua_pushstring(L, LUASOCKET_LIBNAME);
|
||||
lua_pushcfunction(L, (lua_CFunction) luaopen_socket);
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1);
|
||||
#ifdef LUASOCKET_DEBUG
|
||||
lua_pushstring(L, "DEBUG");
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawset(L, -3);
|
||||
#endif
|
||||
/* make version string available to scripts */
|
||||
lua_pushstring(L, "VERSION");
|
||||
lua_pushstring(L, LUASOCKET_VERSION);
|
||||
lua_rawset(L, -3);
|
||||
/* export other functions */
|
||||
luaL_openlib(L, NULL, func, 0);
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushstring(L, "unable to initialize library");
|
||||
lua_error(L);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Initializes all library modules.
|
||||
\*-------------------------------------------------------------------------*/
|
||||
LUASOCKET_API int luaopen_socket(lua_State *L) {
|
||||
int i;
|
||||
base_open(L);
|
||||
for (i = 0; mod[i].name; i++) mod[i].func(L);
|
||||
return 1;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Initializes the library.
|
||||
\*-------------------------------------------------------------------------*/
|
||||
#define LUASOCKET_LIBNAME "socket"
|
||||
LUASOCKET_API int luaopen_socket(lua_State *L);
|
||||
|
||||
#endif /* LUASOCKET_H */
|
||||
|
12
src/mime.c
12
src/mime.c
@ -76,7 +76,17 @@ static UC b64unbase[256];
|
||||
\*-------------------------------------------------------------------------*/
|
||||
MIME_API int luaopen_mime(lua_State *L)
|
||||
{
|
||||
lua_newtable(L);
|
||||
/* whoever is loading the library replaced the global environment
|
||||
* with the namespace table */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
/* make sure library is still "requirable" if initialized staticaly */
|
||||
lua_pushstring(L, "_LOADEDLIB");
|
||||
lua_gettable(L, -2);
|
||||
lua_pushstring(L, MIME_LIBNAME);
|
||||
lua_pushcfunction(L, (lua_CFunction) luaopen_mime);
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1);
|
||||
/* export functions */
|
||||
luaL_openlib(L, NULL, func, 0);
|
||||
/* initialize lookup tables */
|
||||
qpsetup(qpclass, qpunbase);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define MIME_API extern
|
||||
#endif
|
||||
|
||||
#define MIME_LIBNAME "mime"
|
||||
MIME_API int luaopen_mime(lua_State *L);
|
||||
|
||||
#endif /* MIME_H */
|
||||
|
14
src/mime.lua
14
src/mime.lua
@ -5,24 +5,16 @@
|
||||
-- RCS ID: $Id$
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load MIME from dynamic library
|
||||
-- Comment these lines if you are loading static
|
||||
-----------------------------------------------------------------------------
|
||||
local open = assert(loadlib("mime", "luaopen_mime"))
|
||||
local mime = assert(open())
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load other required modules
|
||||
-----------------------------------------------------------------------------
|
||||
local mime = requirelib("mime", "luaopen_mime", getfenv(1))
|
||||
local ltn12 = require("ltn12")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-----------------------------------------------------------------------------
|
||||
-- make all module globals fall into mime namespace
|
||||
setmetatable(mime, { __index = _G })
|
||||
setfenv(1, mime)
|
||||
_LOADED["mime"] = mime
|
||||
|
||||
-- encode, decode and wrap algorithm tables
|
||||
encodet = {}
|
||||
@ -48,7 +40,7 @@ end
|
||||
|
||||
encodet['quoted-printable'] = function(mode)
|
||||
return ltn12.filter.cycle(qp, "",
|
||||
(mode == "binary") and "=0D=0A" or "\13\10")
|
||||
(mode == "binary") and "=0D=0A" or "\r\n")
|
||||
end
|
||||
|
||||
-- define the decoding filters
|
||||
|
227
src/select.c
227
src/select.c
@ -9,26 +9,21 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "luasocket.h"
|
||||
#include "socket.h"
|
||||
#include "auxiliar.h"
|
||||
#include "select.h"
|
||||
|
||||
/*=========================================================================*\
|
||||
* Internal function prototypes.
|
||||
\*=========================================================================*/
|
||||
static int meth_set(lua_State *L);
|
||||
static int meth_isset(lua_State *L);
|
||||
static int c_select(lua_State *L);
|
||||
static int getfd(lua_State *L);
|
||||
static int dirty(lua_State *L);
|
||||
static int collect_fd(lua_State *L, int tab, int max_fd, int itab, fd_set *set);
|
||||
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set);
|
||||
static void return_fd(lua_State *L, fd_set *set, int max_fd,
|
||||
int itab, int tab, int start);
|
||||
static void make_assoc(lua_State *L, int tab);
|
||||
static int global_select(lua_State *L);
|
||||
|
||||
/* fd_set object methods */
|
||||
static luaL_reg set[] = {
|
||||
{"set", meth_set},
|
||||
{"isset", meth_isset},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* functions in library namespace */
|
||||
static luaL_reg func[] = {
|
||||
{"select", global_select},
|
||||
@ -36,22 +31,13 @@ static luaL_reg func[] = {
|
||||
};
|
||||
|
||||
/*=========================================================================*\
|
||||
* Internal function prototypes.
|
||||
* Exported functions
|
||||
\*=========================================================================*/
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Initializes module
|
||||
\*-------------------------------------------------------------------------*/
|
||||
int select_open(lua_State *L)
|
||||
{
|
||||
/* get select auxiliar lua function from lua code and register
|
||||
* pass it as an upvalue to global_select */
|
||||
#ifdef LUASOCKET_COMPILED
|
||||
#include "select.lch"
|
||||
#else
|
||||
lua_dofile(L, "select.lua");
|
||||
#endif
|
||||
luaL_openlib(L, NULL, func, 1);
|
||||
aux_newclass(L, "select{fd_set}", set);
|
||||
int select_open(lua_State *L) {
|
||||
luaL_openlib(L, NULL, func, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -61,64 +47,149 @@ int select_open(lua_State *L)
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Waits for a set of sockets until a condition is met or timeout.
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int global_select(lua_State *L)
|
||||
{
|
||||
fd_set *read_fd_set, *write_fd_set;
|
||||
/* make sure we have enough arguments (nil is the default) */
|
||||
static int global_select(lua_State *L) {
|
||||
int timeout, rtab, wtab, itab, max_fd, ret, ndirty;
|
||||
fd_set rset, wset;
|
||||
FD_ZERO(&rset); FD_ZERO(&wset);
|
||||
lua_settop(L, 3);
|
||||
/* check timeout */
|
||||
if (!lua_isnil(L, 3) && !lua_isnumber(L, 3))
|
||||
luaL_argerror(L, 3, "number or nil expected");
|
||||
/* select auxiliar lua function to be called comes first */
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
/* pass fd_set objects */
|
||||
read_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set));
|
||||
FD_ZERO(read_fd_set);
|
||||
aux_setclass(L, "select{fd_set}", -1);
|
||||
write_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set));
|
||||
FD_ZERO(write_fd_set);
|
||||
aux_setclass(L, "select{fd_set}", -1);
|
||||
/* pass select auxiliar C function */
|
||||
lua_pushcfunction(L, c_select);
|
||||
/* call select auxiliar lua function */
|
||||
lua_call(L, 6, 3);
|
||||
return 3;
|
||||
}
|
||||
|
||||
/*=========================================================================*\
|
||||
* Lua methods
|
||||
\*=========================================================================*/
|
||||
static int meth_set(lua_State *L)
|
||||
{
|
||||
fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1);
|
||||
t_sock fd = (t_sock) lua_tonumber(L, 2);
|
||||
if (fd >= 0) FD_SET(fd, set);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meth_isset(lua_State *L)
|
||||
{
|
||||
fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1);
|
||||
t_sock fd = (t_sock) lua_tonumber(L, 2);
|
||||
if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1);
|
||||
else lua_pushnil(L);
|
||||
return 1;
|
||||
timeout = lua_isnil(L, 3) ? -1 : (int)(luaL_checknumber(L, 3) * 1000);
|
||||
lua_newtable(L); itab = lua_gettop(L);
|
||||
lua_newtable(L); rtab = lua_gettop(L);
|
||||
lua_newtable(L); wtab = lua_gettop(L);
|
||||
max_fd = collect_fd(L, 1, -1, itab, &rset);
|
||||
ndirty = check_dirty(L, 1, rtab, &rset);
|
||||
timeout = ndirty > 0? 0: timeout;
|
||||
max_fd = collect_fd(L, 2, max_fd, itab, &wset);
|
||||
ret = sock_select(max_fd+1, &rset, &wset, NULL, timeout);
|
||||
if (ret > 0 || (ret == 0 && ndirty > 0)) {
|
||||
return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
|
||||
return_fd(L, &wset, max_fd+1, itab, wtab, 0);
|
||||
make_assoc(L, rtab);
|
||||
make_assoc(L, wtab);
|
||||
return 2;
|
||||
} else if (ret == 0) {
|
||||
lua_pushstring(L, "timeout");
|
||||
return 3;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "error");
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
/*=========================================================================*\
|
||||
* Internal functions
|
||||
\*=========================================================================*/
|
||||
static int c_select(lua_State *L)
|
||||
{
|
||||
int max_fd = (int) lua_tonumber(L, 1);
|
||||
fd_set *read_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 2);
|
||||
fd_set *write_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 3);
|
||||
int timeout = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000);
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
lua_pushnumber(L, select(max_fd, read_fd_set, write_fd_set, NULL,
|
||||
timeout < 0 ? NULL : &tv));
|
||||
return 1;
|
||||
static int getfd(lua_State *L) {
|
||||
int fd = -1;
|
||||
lua_pushstring(L, "getfd");
|
||||
lua_gettable(L, -2);
|
||||
if (!lua_isnil(L, -1)) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_call(L, 1, 1);
|
||||
if (lua_isnumber(L, -1))
|
||||
fd = lua_tonumber(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int dirty(lua_State *L) {
|
||||
int is = 0;
|
||||
lua_pushstring(L, "dirty");
|
||||
lua_gettable(L, -2);
|
||||
if (!lua_isnil(L, -1)) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_call(L, 1, 1);
|
||||
is = lua_toboolean(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return is;
|
||||
}
|
||||
|
||||
static int collect_fd(lua_State *L, int tab, int max_fd,
|
||||
int itab, fd_set *set) {
|
||||
int i = 1;
|
||||
if (lua_isnil(L, tab))
|
||||
return max_fd;
|
||||
while (1) {
|
||||
int fd;
|
||||
lua_pushnumber(L, i);
|
||||
lua_gettable(L, tab);
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
fd = getfd(L);
|
||||
if (fd > 0) {
|
||||
FD_SET(fd, set);
|
||||
if (max_fd < fd) max_fd = fd;
|
||||
lua_pushnumber(L, fd);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, itab);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
i = i + 1;
|
||||
}
|
||||
return max_fd;
|
||||
}
|
||||
|
||||
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) {
|
||||
int ndirty = 0, i = 1;
|
||||
if (lua_isnil(L, tab))
|
||||
return 0;
|
||||
while (1) {
|
||||
int fd;
|
||||
lua_pushnumber(L, i);
|
||||
lua_gettable(L, tab);
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
fd = getfd(L);
|
||||
if (fd > 0 && dirty(L)) {
|
||||
lua_pushnumber(L, ++ndirty);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, dtab);
|
||||
FD_CLR(fd, set);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
i = i + 1;
|
||||
}
|
||||
return ndirty;
|
||||
}
|
||||
|
||||
static void return_fd(lua_State *L, fd_set *set, int max_fd,
|
||||
int itab, int tab, int start) {
|
||||
int fd;
|
||||
for (fd = 0; fd < max_fd; fd++) {
|
||||
if (FD_ISSET(fd, set)) {
|
||||
lua_pushnumber(L, ++start);
|
||||
lua_pushnumber(L, fd);
|
||||
lua_gettable(L, itab);
|
||||
lua_settable(L, tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void make_assoc(lua_State *L, int tab) {
|
||||
int i = 1, atab;
|
||||
lua_newtable(L); atab = lua_gettop(L);
|
||||
while (1) {
|
||||
lua_pushnumber(L, i);
|
||||
lua_gettable(L, tab);
|
||||
if (!lua_isnil(L, -1)) {
|
||||
lua_pushnumber(L, i);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, atab);
|
||||
lua_pushnumber(L, i);
|
||||
lua_settable(L, atab);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
i = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
|
50
src/smtp.lua
50
src/smtp.lua
@ -7,15 +7,9 @@
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load SMTP from dynamic library
|
||||
-- Comment these lines if you are loading static
|
||||
-----------------------------------------------------------------------------
|
||||
local open = assert(loadlib("smtp", "luaopen_smtp"))
|
||||
local smtp = assert(open())
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load other required modules
|
||||
-- Load required modules
|
||||
-----------------------------------------------------------------------------
|
||||
local smtp = requirelib("smtp")
|
||||
local socket = require("socket")
|
||||
local ltn12 = require("ltn12")
|
||||
local tp = require("tp")
|
||||
@ -23,10 +17,10 @@ local tp = require("tp")
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-----------------------------------------------------------------------------
|
||||
-- make all module globals fall into smtp namespace
|
||||
setmetatable(smtp, { __index = _G })
|
||||
setfenv(1, smtp)
|
||||
_LOADED["smtp"] = smtp
|
||||
|
||||
-- timeout for connection
|
||||
TIMEOUT = 60
|
||||
-- default server used to send e-mails
|
||||
SERVER = "localhost"
|
||||
-- default port
|
||||
@ -94,9 +88,7 @@ function metat.__index:send(mailt)
|
||||
end
|
||||
|
||||
function open(server, port)
|
||||
print(server or SERVER, port or PORT)
|
||||
local tp, error = tp.connect(server or SERVER, port or PORT)
|
||||
if not tp then return nil, error end
|
||||
local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT))
|
||||
return setmetatable({tp = tp}, metat)
|
||||
end
|
||||
|
||||
@ -121,7 +113,10 @@ local function send_multipart(mesgt)
|
||||
coroutine.yield('content-type: multipart/mixed; boundary="' ..
|
||||
bd .. '"\r\n\r\n')
|
||||
-- send preamble
|
||||
if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) end
|
||||
if mesgt.body.preamble then
|
||||
coroutine.yield(mesgt.body.preamble)
|
||||
coroutine.yield("\r\n")
|
||||
end
|
||||
-- send each part separated by a boundary
|
||||
for i, m in ipairs(mesgt.body) do
|
||||
coroutine.yield("\r\n--" .. bd .. "\r\n")
|
||||
@ -130,7 +125,10 @@ local function send_multipart(mesgt)
|
||||
-- send last boundary
|
||||
coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
|
||||
-- send epilogue
|
||||
if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) end
|
||||
if mesgt.body.epilogue then
|
||||
coroutine.yield(mesgt.body.epilogue)
|
||||
coroutine.yield("\r\n")
|
||||
end
|
||||
end
|
||||
|
||||
-- yield message body from a source
|
||||
@ -183,12 +181,12 @@ end
|
||||
-- set defaul headers
|
||||
local function adjust_headers(mesgt)
|
||||
local lower = {}
|
||||
for i,v in (mesgt or lower) do
|
||||
for i,v in (mesgt.headers or lower) do
|
||||
lower[string.lower(i)] = v
|
||||
end
|
||||
lower["date"] = lower["date"] or
|
||||
os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
|
||||
lower["x-mailer"] = lower["x-mailer"] or socket.version
|
||||
lower["x-mailer"] = lower["x-mailer"] or socket.VERSION
|
||||
-- this can't be overriden
|
||||
lower["mime-version"] = "1.0"
|
||||
mesgt.headers = lower
|
||||
@ -198,18 +196,22 @@ function message(mesgt)
|
||||
adjust_headers(mesgt)
|
||||
-- create and return message source
|
||||
local co = coroutine.create(function() send_message(mesgt) end)
|
||||
return function() return socket.skip(1, coroutine.resume(co)) end
|
||||
return function()
|
||||
local ret, a, b = coroutine.resume(co)
|
||||
if ret then return a, b
|
||||
else return nil, a end
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- High level SMTP API
|
||||
-----------------------------------------------------------------------------
|
||||
send = socket.protect(function(mailt)
|
||||
local smtp = socket.try(open(mailt.server, mailt.port))
|
||||
smtp:greet(mailt.domain)
|
||||
smtp:send(mailt)
|
||||
smtp:quit()
|
||||
return smtp:close()
|
||||
local con = open(mailt.server, mailt.port)
|
||||
con:greet(mailt.domain)
|
||||
con:send(mailt)
|
||||
con:quit()
|
||||
return con:close()
|
||||
end)
|
||||
|
||||
return smtp
|
||||
|
@ -7,8 +7,8 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load LuaSocket from dynamic library
|
||||
-----------------------------------------------------------------------------
|
||||
local open = assert(loadlib("luasocket", "luaopen_socket"))
|
||||
local socket = assert(open())
|
||||
local socket = requirelib("luasocket", "luaopen_socket", getfenv(1))
|
||||
_LOADED["socket"] = socket
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Auxiliar functions
|
||||
@ -116,18 +116,21 @@ socket.sourcet["by-length"] = function(sock, length)
|
||||
end
|
||||
|
||||
socket.sourcet["until-closed"] = function(sock)
|
||||
local done
|
||||
return setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = ltn12.source.simplify(function()
|
||||
__call = function()
|
||||
if done then return nil end
|
||||
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
|
||||
if not err then return chunk
|
||||
elseif err == "closed" then
|
||||
sock:close()
|
||||
return partial, ltn12.source.empty()
|
||||
done = 1
|
||||
return partial
|
||||
else return nil, err end
|
||||
end)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -9,13 +9,10 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "luasocket.h"
|
||||
|
||||
#include "auxiliar.h"
|
||||
#include "socket.h"
|
||||
#include "inet.h"
|
||||
#include "options.h"
|
||||
#include "base.h"
|
||||
#include "tcp.h"
|
||||
|
||||
/*=========================================================================*\
|
||||
@ -41,7 +38,7 @@ static int meth_dirty(lua_State *L);
|
||||
/* tcp object methods */
|
||||
static luaL_reg tcp[] = {
|
||||
{"__gc", meth_close},
|
||||
{"__tostring", base_meth_tostring},
|
||||
{"__tostring", aux_tostring},
|
||||
{"accept", meth_accept},
|
||||
{"bind", meth_bind},
|
||||
{"close", meth_close},
|
||||
|
@ -26,6 +26,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* min and max macros */
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? x : y)
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? x : y)
|
||||
#endif
|
||||
|
||||
/*=========================================================================*\
|
||||
* Internal function prototypes
|
||||
\*=========================================================================*/
|
||||
|
26
src/tp.lua
26
src/tp.lua
@ -2,24 +2,28 @@
|
||||
-- Unified SMTP/FTP subsystem
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- Conforming to: RFC 2616, LTN7
|
||||
-- RCS ID: $Id$
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Load other required modules
|
||||
-- Load required modules
|
||||
-----------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
local ltn12 = require("ltn12")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-----------------------------------------------------------------------------
|
||||
tp = {}
|
||||
setmetatable(tp, { __index = _G })
|
||||
setfenv(1, tp)
|
||||
_LOADED["tp"] = getfenv(1)
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
TIMEOUT = 60
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Implementation
|
||||
-----------------------------------------------------------------------------
|
||||
-- gets server reply (works for SMTP and FTP)
|
||||
local function get_reply(control)
|
||||
local code, current, sep
|
||||
@ -37,7 +41,6 @@ local function get_reply(control)
|
||||
-- reply ends with same code
|
||||
until code == current and sep == " "
|
||||
end
|
||||
print(reply)
|
||||
return code, reply
|
||||
end
|
||||
|
||||
@ -46,6 +49,7 @@ local metat = { __index = {} }
|
||||
|
||||
function metat.__index:check(ok)
|
||||
local code, reply = get_reply(self.control)
|
||||
print(reply)
|
||||
if not code then return nil, reply end
|
||||
if type(ok) ~= "function" then
|
||||
if type(ok) == "table" then
|
||||
@ -103,11 +107,11 @@ function metat.__index:close()
|
||||
end
|
||||
|
||||
-- connect with server and return control object
|
||||
function connect(host, port)
|
||||
local control, err = socket.connect(host, port)
|
||||
if not control then return nil, err end
|
||||
control:settimeout(TIMEOUT)
|
||||
connect = socket.protect(function(host, port, timeout)
|
||||
local control = socket.try(socket.tcp())
|
||||
socket.try(control:settimeout(timeout or TIMEOUT))
|
||||
socket.try(control:connect(host, port))
|
||||
return setmetatable({control = control}, metat)
|
||||
end
|
||||
end)
|
||||
|
||||
return tp
|
||||
|
@ -9,13 +9,10 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "luasocket.h"
|
||||
|
||||
#include "auxiliar.h"
|
||||
#include "socket.h"
|
||||
#include "inet.h"
|
||||
#include "options.h"
|
||||
#include "base.h"
|
||||
#include "udp.h"
|
||||
|
||||
/*=========================================================================*\
|
||||
@ -51,7 +48,7 @@ static luaL_reg udp[] = {
|
||||
{"close", meth_close},
|
||||
{"setoption", meth_setoption},
|
||||
{"__gc", meth_close},
|
||||
{"__tostring", base_meth_tostring},
|
||||
{"__tostring", aux_tostring},
|
||||
{"getfd", meth_getfd},
|
||||
{"setfd", meth_setfd},
|
||||
{"dirty", meth_dirty},
|
||||
|
@ -9,9 +9,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Setup namespace
|
||||
-----------------------------------------------------------------------------
|
||||
local url = {}
|
||||
setmetatable(url, { __index = _G })
|
||||
setfenv(1, url)
|
||||
_LOADED["url"] = getfenv(1)
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Encodes a string into its escaped hexadecimal representation
|
||||
|
@ -4,14 +4,18 @@
|
||||
-- to "/luasocket-test-cgi" and "/luasocket-test-cgi/"
|
||||
-- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth
|
||||
|
||||
require("http")
|
||||
socket = require("socket")
|
||||
http = require("http")
|
||||
mime = require("mime")
|
||||
url = require("url")
|
||||
ltn12 = require("ltn12")
|
||||
|
||||
dofile("testsupport.lua")
|
||||
|
||||
local host, proxy, request, response, index_file
|
||||
local ignore, expect, index, prefix, cgiprefix, index_crlf
|
||||
|
||||
socket.http.TIMEOUT = 10
|
||||
http.TIMEOUT = 10
|
||||
|
||||
local t = socket.time()
|
||||
|
||||
@ -56,7 +60,7 @@ local check_request = function(request, expect, ignore)
|
||||
end
|
||||
request.source = request.source or
|
||||
(request.body and ltn12.source.string(request.body))
|
||||
local response = socket.http.request(request)
|
||||
local response = http.request(request)
|
||||
if t and table.getn(t) > 0 then response.body = table.concat(t) end
|
||||
check_result(response, expect, ignore)
|
||||
end
|
||||
@ -64,16 +68,16 @@ end
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing request uri correctness: ")
|
||||
local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string"
|
||||
local back, h, c, e = socket.http.get("http://" .. host .. forth)
|
||||
local back, h, c, e = http.get("http://" .. host .. forth)
|
||||
if not back then fail(e) end
|
||||
back = socket.url.parse(back)
|
||||
back = url.parse(back)
|
||||
if similar(back.query, "this+is+the+query+string") then print("ok")
|
||||
else fail(back.query) end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing query string correctness: ")
|
||||
forth = "this+is+the+query+string"
|
||||
back = socket.http.get("http://" .. host .. cgiprefix ..
|
||||
back = http.get("http://" .. host .. cgiprefix ..
|
||||
"/query-string?" .. forth)
|
||||
if similar(back, forth) then print("ok")
|
||||
else fail("failed!") end
|
||||
@ -149,7 +153,7 @@ check_request(request, expect, ignore)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing simple post function: ")
|
||||
back = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index)
|
||||
back = http.post("http://" .. host .. cgiprefix .. "/cat", index)
|
||||
assert(back == index)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
@ -277,30 +281,6 @@ ignore = {
|
||||
}
|
||||
check_request(request, expect, ignore)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing host not found: ")
|
||||
request = {
|
||||
url = "http://wronghost/does/not/exist"
|
||||
}
|
||||
local c, e = socket.connect("wronghost", 80)
|
||||
expect = {
|
||||
error = e
|
||||
}
|
||||
ignore = {}
|
||||
check_request(request, expect, ignore)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing invalid url: ")
|
||||
request = {
|
||||
url = host .. prefix
|
||||
}
|
||||
local c, e = socket.connect("", 80)
|
||||
expect = {
|
||||
error = e
|
||||
}
|
||||
ignore = {}
|
||||
check_request(request, expect, ignore)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing document not found: ")
|
||||
request = {
|
||||
@ -396,37 +376,43 @@ ignore = {
|
||||
}
|
||||
check_request(request, expect, ignore)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing wrong scheme: ")
|
||||
request = {
|
||||
url = "wrong://" .. host .. cgiprefix .. "/cat",
|
||||
method = "GET"
|
||||
}
|
||||
expect = {
|
||||
error = "unknown scheme 'wrong'"
|
||||
}
|
||||
ignore = {
|
||||
}
|
||||
check_request(request, expect, ignore)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
local body
|
||||
io.write("testing simple get function: ")
|
||||
body = socket.http.get("http://" .. host .. prefix .. "/index.html")
|
||||
body = http.get("http://" .. host .. prefix .. "/index.html")
|
||||
assert(body == index)
|
||||
print("ok")
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing HEAD method: ")
|
||||
socket.http.TIMEOUT = 1
|
||||
response = socket.http.request {
|
||||
http.TIMEOUT = 1
|
||||
response = http.request {
|
||||
method = "HEAD",
|
||||
url = "http://www.cs.princeton.edu/~diego/"
|
||||
}
|
||||
assert(response and response.headers)
|
||||
print("ok")
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing host not found: ")
|
||||
local c, e = socket.connect("wronghost", 80)
|
||||
local r, re = http.request{url = "http://wronghost/does/not/exist"}
|
||||
assert(r == nil and e == re)
|
||||
r, re = http.get("http://wronghost/does/not/exist")
|
||||
assert(r == nil and e == re)
|
||||
print("ok")
|
||||
|
||||
------------------------------------------------------------------------
|
||||
io.write("testing invalid url: ")
|
||||
local c, e = socket.connect("", 80)
|
||||
local r, re = http.request{url = host .. prefix}
|
||||
assert(r == nil and e == re)
|
||||
r, re = http.get(host .. prefix)
|
||||
assert(r == nil and e == re)
|
||||
print("ok")
|
||||
|
||||
------------------------------------------------------------------------
|
||||
print("passed all tests")
|
||||
os.remove("err")
|
||||
|
||||
print(string.format("done in %.2fs", socket.time() - t))
|
||||
|
@ -69,7 +69,7 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone)
|
||||
end
|
||||
end
|
||||
|
||||
if not socket.debug then
|
||||
if not socket.DEBUG then
|
||||
fail("Please define LUASOCKET_DEBUG and recompile LuaSocket")
|
||||
end
|
||||
|
||||
|
@ -1,52 +1,57 @@
|
||||
require("smtp")
|
||||
require("mime")
|
||||
-- load the smtp support and its friends
|
||||
local smtp = require("smtp")
|
||||
local mime = require("mime")
|
||||
local ltn12 = require("ltn12")
|
||||
|
||||
mesgt = {
|
||||
headers = {
|
||||
to = "D Burgess <db@werx4.com>",
|
||||
subject = "Looking good! (please check headers)"
|
||||
-- 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 <sicrano@tecgraf.puc-rio.br>",
|
||||
to = "Fulano <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, though.
|
||||
]])
|
||||
},
|
||||
body = {
|
||||
preamble = "Some attatched stuff",
|
||||
[1] = {
|
||||
body = mime.eol(0, "Testing stuffing.\n.\nGot you.\n.Hehehe.\n")
|
||||
},
|
||||
[2] = {
|
||||
headers = {
|
||||
["content-type"] = 'application/octet-stream; name="testmesg.lua"',
|
||||
["content-disposition"] = 'attachment; filename="testmesg.lua"',
|
||||
["content-transfer-encoding"] = "BASE64"
|
||||
},
|
||||
body = ltn12.source.chain(
|
||||
ltn12.source.file(io.open("testmesg.lua", "rb")),
|
||||
ltn12.filter.chain(
|
||||
mime.encode("base64"),
|
||||
mime.wrap()
|
||||
)
|
||||
)
|
||||
},
|
||||
[3] = {
|
||||
headers = {
|
||||
["content-type"] = 'text/plain; name="testmesg.lua"',
|
||||
["content-disposition"] = 'attachment; filename="testmesg.lua"',
|
||||
["content-transfer-encoding"] = "QUOTED-PRINTABLE"
|
||||
},
|
||||
body = ltn12.source.chain(
|
||||
ltn12.source.file(io.open("testmesg.lua", "rb")),
|
||||
ltn12.filter.chain(
|
||||
mime.normalize(),
|
||||
mime.encode("quoted-printable"),
|
||||
mime.wrap("quoted-printable")
|
||||
)
|
||||
)
|
||||
},
|
||||
epilogue = "Done attaching stuff",
|
||||
}
|
||||
-- second part: Headers describe content the to be an image,
|
||||
-- sent under the base64 transfer content encoding.
|
||||
-- Notice that nothing happens until the message is sent. Small
|
||||
-- chunks are loaded into memory 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"
|
||||
}
|
||||
}
|
||||
|
||||
print(socket.smtp.send {
|
||||
-- finally send it
|
||||
r, e = smtp.send{
|
||||
rcpt = "<diego@cs.princeton.edu>",
|
||||
from = "<diego@cs.princeton.edu>",
|
||||
source = socket.smtp.message(mesgt),
|
||||
source = source,
|
||||
server = "mail.cs.princeton.edu"
|
||||
})
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
require"url"
|
||||
socket = require("socket")
|
||||
socket.url = require("url")
|
||||
dofile("testsupport.lua")
|
||||
|
||||
local check_build_url = function(parsed)
|
||||
|
Loading…
Reference in New Issue
Block a user