aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-15 06:24:00 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-15 06:24:00 +0000
commit58096449c6044b7aade5cd41cfd71c6bec1d273d (patch)
tree1814ffebe89c4c2556d84f97f66db37a7e8b4554
parent9ed7f955e5fc69af9bf1794fa2c8cd227981ba24 (diff)
downloadluasocket-58096449c6044b7aade5cd41cfd71c6bec1d273d.tar.gz
luasocket-58096449c6044b7aade5cd41cfd71c6bec1d273d.tar.bz2
luasocket-58096449c6044b7aade5cd41cfd71c6bec1d273d.zip
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.
-rw-r--r--TODO11
-rw-r--r--doc/ftp.html272
-rw-r--r--doc/ltn12.html421
-rw-r--r--doc/mime.html428
-rw-r--r--doc/reference.css3
-rw-r--r--doc/reference.html89
-rw-r--r--doc/smtp.html235
-rw-r--r--doc/socket.html25
-rw-r--r--doc/tcp.html29
-rw-r--r--doc/udp.html5
-rw-r--r--doc/url.html76
-rw-r--r--etc/check-memory.lua17
-rw-r--r--etc/get.lua1
-rw-r--r--samples/tinyirc.lua2
-rw-r--r--src/auxiliar.c50
-rw-r--r--src/auxiliar.h25
-rw-r--r--src/buffer.c9
-rw-r--r--src/except.c52
-rw-r--r--src/except.h35
-rw-r--r--src/ftp.lua73
-rw-r--r--src/http.lua351
-rw-r--r--src/inet.c1
-rw-r--r--src/ltn12.lua21
-rw-r--r--src/luasocket.c71
-rw-r--r--src/luasocket.h1
-rw-r--r--src/mime.c12
-rw-r--r--src/mime.h1
-rw-r--r--src/mime.lua14
-rw-r--r--src/select.c221
-rw-r--r--src/smtp.lua50
-rw-r--r--src/socket.lua13
-rw-r--r--src/tcp.c5
-rw-r--r--src/timeout.c8
-rw-r--r--src/tp.lua26
-rw-r--r--src/udp.c5
-rw-r--r--src/url.lua4
-rw-r--r--test/httptest.lua80
-rw-r--r--test/testclnt.lua2
-rw-r--r--test/testmesg.lua95
-rw-r--r--test/urltest.lua3
40 files changed, 2031 insertions, 811 deletions
diff --git a/TODO b/TODO
index bc307d5..69cfc6b 100644
--- a/TODO
+++ b/TODO
@@ -2,6 +2,15 @@ ajeitar os README.*
2ajeitar select. upvalue nao tem nada a ver... 2ajeitar select. upvalue nao tem nada a ver...
3make sure filter.chain fails gracefully. 3make sure filter.chain fails gracefully.
4ajeitar o manual sobre select, mais liberal agora 4ajeitar o manual sobre select, mais liberal agora
5falar sobre o novo esquema de namespace
6tirar socket.url socket.ftp etc do manual. agora os namespaces estao
7liberados.
8ajeitar as referencias a RFCS e LTNS em todos os arquivos.
9proxy no ftp
10ajeitar < e-mail > no smtp?
11ajeitar referencias a LTN12 nos manuais
12
13make sure sockets are closed when exceptions are raised
5 14
6falar sobre encodet/wrapt/decodet no manual sobre mime 15falar sobre encodet/wrapt/decodet no manual sobre mime
7 16
@@ -14,8 +23,6 @@ expose encode/decode tables to provide extensibility for mime module
14use coroutines instead of fancy filters 23use coroutines instead of fancy filters
15 24
16check garbage collection in test*.lua 25check garbage collection in test*.lua
17pop3???
18
19 26
20add socket.TIMEOUT to be default timeout? 27add socket.TIMEOUT to be default timeout?
21 28
diff --git a/doc/ftp.html b/doc/ftp.html
index 6776a17..a0c9268 100644
--- a/doc/ftp.html
+++ b/doc/ftp.html
@@ -35,9 +35,9 @@
35 35
36<p> 36<p>
37FTP (File Transfer Protocol) is a protocol used to transfer files 37FTP (File Transfer Protocol) is a protocol used to transfer files
38between hosts. The module <tt>ftp.lua</tt> offers simple FTP support, 38between hosts. The module <tt>ftp.lua</tt> offers simple FTP support.
39allowing applications to download and upload files, and list directory 39Applications can easily download and upload files.
40contents. The implementation conforms to 40The implementation conforms to
41<a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>. 41<a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>.
42</p> 42</p>
43 43
@@ -49,175 +49,191 @@ URLs MUST conform to
49 49
50<blockquote> 50<blockquote>
51<tt> 51<tt>
52[ftp://][&lt;user&gt;[:&lt;password&gt;]@]&lt;host&gt;[:&lt;port&gt;][/&lt;path&gt;][<i>type</i>=a|i|d]</tt> 52[ftp://][&lt;user&gt;[:&lt;password&gt;]@]&lt;host&gt;[:&lt;port&gt;][/&lt;path&gt;][<i>type</i>=a|i]</tt>
53</blockquote> 53</blockquote>
54 54
55<p>
56High level functions are provided supporting the most common operations.
57These high level functions are implemented on top of a lower level
58interface. By using the low-level interface, users can easily create their
59own functions to access <em>any</em> operation supported by the FTP
60protocol. For that, check the implementation.
61</p>
62
63<p>
64To use some of the functions in this module, a good understanding of
65<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
66LTN012, Filters sources and sinks</a> is necessary.
67</p>
68
69<p>
70The following constants can be set to control the default behaviour of
71the FTP module:
72</p>
73
74<ul>
75<li> <tt>PASSWORD</tt>: default anonymous password.
76<li> <tt>PORT</tt>: default port used for the control connection;
77<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations;
78<li> <tt>USER</tt>: default anonymous user;
79</ul>
80
55<!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 81<!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
56 82
57<p class=name id=get> 83<p class=name id=get>
58socket.ftp.<b>get(</b>url<b>)</b><br> 84ftp.<b>get(</b>url<b>)</b><br>
59socket.ftp.<b>get{</b><br> 85ftp.<b>get{</b><br>
60&nbsp;&nbsp;url = <i>string</i>,<br> 86&nbsp;&nbsp;host = <i>string</i>,<br>
61&nbsp;&nbsp;type = <i>string</i>,<br> 87&nbsp;&nbsp;sink = <i>LTN12 sink</i>,<br>
62&nbsp;&nbsp;user = <i>string</i>,<br> 88&nbsp;&nbsp;argument <i>or</i> path = <i>string</i>,<br>
63&nbsp;&nbsp;password = <i>string</i><br> 89&nbsp;&nbsp;[user = <i>string</i>,]<br>
90&nbsp;&nbsp;[password = <i>string</i>]<br>
91&nbsp;&nbsp;[command = <i>string</i>,]<br>
92&nbsp;&nbsp;[port = <i>number</i>,]<br>
93&nbsp;&nbsp;[type = <i>string</i>,]<br>
94&nbsp;&nbsp;[step = <i>LTN12 pump step</i>],<br>
64<b>}</b> 95<b>}</b>
65</p> 96</p>
66 97
67<p class=description> 98<p class=description>
68Downloads an URL from a FTP server. 99The <tt>get</tt> function has two forms. The simple form has fixed
100functionality: it downloads the contents of a URL and returns it as a
101string. The generic form allows a <em>lot</em> more control, as explained
102below.
69</p> 103</p>
70 104
71<p class=parameters> 105<p class=parameters>
72The function can be called either directly with a <tt>url</tt> 106If the argument of the <tt>get</tt> function is a table, the function
73or with a <em>request table</em>. 107expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of
74Fields passed explicitly in the request table override those 108<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
75present in the <tt>url</tt>. 109precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is
76</p> 110the LTN12 sink that will receive the downloaded data. <tt>Argument</tt> or
77 111<tt>path</tt> give the target path to the resource in the server. The
78<p class=parameters> 112optional arguments are the following:
79The parameter <tt>type</tt> accepts values '<tt>a</tt>' (ASCII, the
80default), '<tt>i</tt>' (binary) or '<tt>d</tt>' (directory listing) and
81determines the transfer type. If <tt>&lt;path&gt;</tt> ends with a
82'<tt>/</tt>' or <tt>type</tt> is '<tt>d</tt>', a directory listing of
83<tt>&lt;path&gt;</tt> is returned. If no <tt>user</tt> is provided in the
84<tt>url</tt> or explicitly, the function tries to log in as user
85'<tt>anonymous</tt>'.
86</p> 113</p>
114<ul>
115<li><tt>user</tt>, <tt>password</tt>: User name and password used for
116authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
117<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
118"<tt>retr</tt>", but see example below;
119<li><tt>port</tt>: The port to contacct the server at. Defaults to 21;
120<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
121"<tt>a</tt>". Defaults to whatever is the server default;
122<li><tt>step</tt>: LTN12 pump step function used to pass data from the
123server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function.
124</ul>
87 125
88<p class=return> 126<p class=return>
89If successful, the function returns 127If successful, the simple version returns the URL contents as a
90the file content as a string. In case of error, the function returns 128string, and the generic function returns 1. In case of error, both
91<b><tt>nil</tt></b> and an error message describing the error. 129functions return <b><tt>nil</tt></b> and an error message describing the
130error.
92</p> 131</p>
93 132
94<pre class=example> 133<pre class=example>
95-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br", 134-- load the ftp support
96-- go to directory "pub/lua" and get file "lua.tar.gz" as binary. 135local ftp = require("ftp")
97f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
98 136
99-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br", 137-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br",
100-- go to director "pub" and retrieve directory listing of directory "lua" 138-- and get file "lua.tar.gz" from directory "pub/lua" as binary.
101f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua;type=d") 139f, e = ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
102
103-- Log as user "diego", password "nehab", on server "ftp.tecgraf.puc-rio.br",
104-- go to directory "tec/luasocket/bin" and retrieve file "luasocket.exe"
105-- (actually, fails because of wrong password, of course)
106f, e = socket.ftp.get{
107 url = "ftp://ftp.tecgraf.puc-rio.br/tec/luasocket/bin/luasocket.exe",
108 user = "diego",
109 password = "nehab",
110 type = "i"
111}
112-- f returns nil, and e returns an appropriate error message
113</pre> 140</pre>
114 141
115<!-- get_cb +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 142<pre class=example>
116 143-- load needed modules
117<p class=name id=get_cb> 144local ftp = require("ftp")
118socket.ftp.<b>get_cb{</b><br> 145local ltn12 = require("ltn12")
119&nbsp;&nbsp;url = <i>string</i>,<br> 146local url = require("url")
120&nbsp;&nbsp;type = <i>string</i>,<br> 147
121&nbsp;&nbsp;content_cb = <i>receive-callback</i>,<br> 148-- a function that returns a directory listing
122&nbsp;&nbsp;user = <i>string</i>,<br> 149function ls(u)
123&nbsp;&nbsp;password = <i>string</i><br> 150 local t = {}
124<b>}</b> 151 local p = url.parse(u)
125</p> 152 p.command = "nlst"
126 153 p.sink = ltn12.sink.table(t)
127<p class=description> 154 local r, e = ftp.get(p)
128Same as <a href="#get"><tt>get</tt></a>, but the library returns 155 return r and table.concat(t), e
129the content of the downloaded file to the receive callback 156end
130<tt>content_cb</tt>. 157</pre>
131</p>
132
133<p class=note>
134Note: for more information on callbacks, refer to
135<a href="stream.html#stream">Streaming with callbacks</a>.
136</p>
137 158
138<!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 159<!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
139 160
140<p class=name id=put> 161<p class=name id=put>
141socket.ftp.<b>put(</b>url, content<b>)</b><br> 162ftp.<b>put(</b>url, content<b>)</b><br>
142socket.ftp.<b>put{</b><br> 163ftp.<b>put{</b><br>
143&nbsp;&nbsp;url = <i>string</i>,<br> 164&nbsp;&nbsp;host = <i>string</i>,<br>
144&nbsp;&nbsp;content = <i>string</i>,<br> 165&nbsp;&nbsp;source = <i>LTN12 sink</i>,<br>
145&nbsp;&nbsp;type = <i>string</i>,<br> 166&nbsp;&nbsp;argument <i>or</i> path = <i>string</i>,<br>
146&nbsp;&nbsp;user = <i>string</i>,<br> 167&nbsp;&nbsp;[user = <i>string</i>,]<br>
147&nbsp;&nbsp;password = <i>string</i><br> 168&nbsp;&nbsp;[password = <i>string</i>]<br>
169&nbsp;&nbsp;[command = <i>string</i>,]<br>
170&nbsp;&nbsp;[port = <i>number</i>,]<br>
171&nbsp;&nbsp;[type = <i>string</i>,]<br>
172&nbsp;&nbsp;[step = <i>LTN12 pump step</i>],<br>
148<b>}</b> 173<b>}</b>
149</p> 174</p>
150 175
151<p class=description> 176<p class=description>
152Upload a file to a FTP server. 177The <tt>put</tt> function has two forms. The simple form has fixed
178functionality: it uploads a string of content into a URL. The generic form
179allows a <em>lot</em> more control, as explained below.
153</p> 180</p>
154 181
155<p class=parameters> 182<p class=parameters>
156The function can be called directly with a 183If the argument of the <tt>put</tt> function is a table, the function
157<tt>url</tt> and <tt>content</tt> parameters, or with a 184expects at least the fields <tt>host</tt>, <tt>source</tt>, and one of
158<em>request table</em>. 185<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
159Values passed explicitly in the request table override those present in 186precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is
160the <tt>url</tt>. The parameter <tt>type</tt> accept values 187the LTN12 source that will provide the contents to be uploaded.
161'<tt>a</tt>' (ASCII, the default) or '<tt>i</tt>' (binary) and 188<tt>Argument</tt> or
162determines the transfer type. If no <tt>user</tt> is provided, the 189<tt>path</tt> give the target path to the resource in the server. The
163function tries to log in as '<tt>anonymous</tt>'. 190optional arguments are the following:
164</p> 191</p>
192<ul>
193<li><tt>user</tt>, <tt>password</tt>: User name and password used for
194authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
195<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
196"<tt>retr</tt>", but see example below;
197<li><tt>port</tt>: The port to contacct the server at. Defaults to 21;
198<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
199"<tt>a</tt>". Defaults to whatever is the server default;
200<li><tt>step</tt>: LTN12 pump step function used to pass data from the
201server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function.
202</ul>
165 203
166<p class=return> 204<p class=return>
167If successful, the function returns 1. In case of error, the 205Both functions return 1 if successful, or <b><tt>nil</tt></b> and an error
168function returns <b><tt>nil</tt></b> followed by a string describing the error. 206message describing the reason for failure.
169</p> 207</p>
170 208
171<pre class=example> 209<pre class=example>
172-- Log as user "anonymous" on server "ftp.free.org" and store file 210-- load the ftp support
173-- "hello" with contents "hello world!", using binary mode for the transfer 211local ftp = require("ftp")
174r, e = socket.ftp.put("ftp://ftp.free.org/hello;type=i", "hello world!\n")
175
176-- Does exactly the same, but logging in as diego
177r, e = socket.ftp.put{
178 url = "ftp://ftp.free.org/hello",
179 type = "i",
180 user = "diego",
181 password = "nehab",
182 content = "hello world\n"
183}
184</pre>
185</blockquote>
186
187<!-- put_cb +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
188
189<p class=name id=put_cb>
190socket.ftp.<b>put_cb{</b><br>
191&nbsp;&nbsp;url = <i>string</i>,<br>
192&nbsp;&nbsp;type = <i>string</i>,<br>
193&nbsp;&nbsp;content_cb = <i>send-callback</i>,<br>
194&nbsp;&nbsp;user = <i>string</i>,<br>
195&nbsp;&nbsp;password = <i>string</i><br>
196<b>}</b>
197</p>
198
199<p class=description>
200Same as <a href="#put"><tt>put</tt></a>, but the
201library obtains the contents of the file to be uploaded using the send
202callback <tt>content_cb</tt>.
203</p>
204 212
205<p class=note> 213-- Log as user "diego" on server "ftp.tecgraf.puc-rio.br",
206Note: for more information on callbacks, refer to 214-- using password "nehab", and store a file "README" with contents
207<a href="stream.html#stream">Streaming with callbacks</a>. 215-- "wrong password, of course"
208</p> 216f, e = ftp.put("ftp://diego:nehab@ftp.tecgraf.puc-rio.br/README", "wrong password, of course")
217</pre>
209 218
210<pre class=example> 219<pre class=example>
211-- Log as user "anonymous" on server "ftp.free.org" and store file 220-- load the ftp support
212-- "hello" with contents of the same file in the current directory, 221local ftp = require("ftp")
213-- using binary mode for the transfer 222local ltn12 = require("ltn12")
214r, e = socket.ftp.put_cb{ 223
215 url = "ftp://ftp.free.org/hello", 224-- Log as user "diego" on server "ftp.tecgraf.puc-rio.br",
216 type = "i", 225-- using password "nehab", and append to the file "LOG", sending the
217 content_cb = socket.callback.send_file(io.open("hello", "r")) 226-- contents of a local file
227f, e = ftp.put{
228 host = "ftp.tecgraf.puc-rio.br",
229 user = "diego",
230 password = "nehab",
231 command = "appe",
232 argument = "LOG",
233 source = ltn12.source.file(io.open("LOCAL-LOG", "r"))
218} 234}
219</pre> 235</pre>
220</blockquote> 236
221 237
222<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 238<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
223 239
diff --git a/doc/ltn12.html b/doc/ltn12.html
new file mode 100644
index 0000000..363ce43
--- /dev/null
+++ b/doc/ltn12.html
@@ -0,0 +1,421 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2 "http://www.w3.org/TR/html4/strict.dtd">
3<html>
4
5<head>
6<title>LuaSocket: Network support for the Lua language</title>
7<link rel="stylesheet" href="reference.css" type="text/css">
8</head>
9
10<body>
11
12<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
13
14<div class=header>
15<hr>
16<center>
17<table summary="LuaSocket logo">
18<tr><td align=center><a href="http://www.lua.org">
19<img border=0 alt="LuaSocket" src="luasocket.png">
20</a></td></tr>
21<tr><td align=center valign=top>Network support for the Lua language
22</td></tr>
23</table>
24<p class=bar>
25<a href="home.html">home</a> &middot;
26<a href="home.html#download">download</a> &middot;
27<a href="introduction.html">introduction</a> &middot;
28<a href="reference.html">reference</a>
29</p>
30</center>
31<hr>
32</div>
33
34<!-- ltn12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
35
36<h2 id=ltn12>LTN12</h2>
37
38<p> The LTN12 module implements the ideas described in
39<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
40LTN012, Filters sources and sinks</a>. This manual simply describe the
41functions. Please refer to the LTN for a deeper explanation of the
42functionality provided by this module.
43</p>
44
45<!-- filters ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
46
47<h3 id="filter">Filters</h3>
48
49<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
50
51<p class=name id="filter.chain">
52ltn12.filter.<b>chain(</b>filter<sub>1</sub>, filter<sub>2</sub>
53[, ... filter<sub>N</sub>]<b>)</b>
54</p>
55
56<p class=description>
57Returns a filter that passes all data it receives through each of a
58series of given filters.
59</p>
60
61<p class=parameters>
62<tt>Filter<sub>1</sub></tt> to <tt>filter<sub>N</sub></tt> are simple
63filters.
64</p>
65
66<p class=return>
67The function returns the chained filter.
68</p>
69
70<p class=note>
71The nesting of filters can be arbritrary. For instance, the useless filter
72below doesn't do anything but return the data that was passed to it,
73unaltered.
74</p>
75
76<pre class=example>
77-- load required modules
78ltn12 = require("ltn12")
79mime = require("mime")
80
81-- create a silly identity filter
82id = ltn12.filter.chain(
83 mime.encode("quoted-printable"),
84 mime.encode("base64"),
85 mime.decode("base64"),
86 mime.decode("quoted-printable")
87)
88</pre>
89
90<!-- cycle ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
91
92<p class=name id="filter.cycle">
93ltn12.filter.<b>cycle(</b>low [, ctx, extra]<b>)</b>
94</p>
95
96<p class=description>
97Returns a high-level filter that cycles though a low-level filter by
98passing it each chunk and updating a context between calls.
99</p>
100
101<p class=parameters>
102<tt>Low</tt> is the low-level filter to be cycled,
103<tt>ctx</tt> is the initial context and <tt>extra</tt> is any extra
104argument the low-level filter might take.
105</p>
106
107<p class=return>
108The function returns the high-level filter.
109</p>
110
111<pre class=example>
112-- load the ltn12 module
113local ltn12 = require("ltn12")
114
115-- the base64 mime filter factory
116encodet['base64'] = function()
117 return ltn12.filter.cycle(b64, "")
118end
119</pre>
120
121<!-- pumps ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
122
123<h3 id="pump">Pumps</h3>
124
125<!-- all ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
126
127<p class=name id="pump.all">
128ltn12.pump.<b>all(</b>source, sink<b>)</b>
129</p>
130
131<p class=description>
132Pumps <em>all</em> data from a <tt>source</tt> to a <tt>sink</tt>.
133</p>
134
135<p class=return>
136If successful, the function returns a value that evaluates to
137<b><tt>true</tt></b>. In case
138of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
139</p>
140
141<!-- step +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
142
143<p class=name id="pump.step">
144ltn12.pump.<b>step(</b>source, sink<b>)</b>
145</p>
146
147<p class=description>
148Pumps <em>one</em> chunk of data from a <tt>source</tt> to a <tt>sink</tt>.
149</p>
150
151<p class=return>
152If successful, the function returns a value that evaluates to
153<b><tt>true</tt></b>. In case
154of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
155</p>
156
157<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
158
159<h3 id="sink">Sinks</h3>
160
161<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
162
163<p class=name id="sink.chain">
164ltn12.sink.<b>chain(</b>filter, sink<b>)</b>
165</p>
166
167<p class=description>
168Creates a new sink that passes data through a <tt>filter</tt> before sending
169it to a given <tt>sink</tt>.
170</p>
171
172<p class=return>
173The function returns the new sink.
174</p>
175
176<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
177
178<p class=name id="sink.error">
179ltn12.sink.<b>error(</b>message<b>)</b>
180</p>
181
182<p class=description>
183Creates and returns a sink that aborts transmission with an error
184<tt>message</tt>.
185</p>
186
187<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
188
189<p class=name id="sink.file">
190ltn12.sink.<b>file(</b>handle, message<b>)</b>
191</p>
192
193<p class=description>
194Creates a sink that sends data to a file.
195</p>
196
197<p class=parameters>
198<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
199<tt>message</tt> should give the reason for failure.
200</p>
201
202<p class=return>
203The function returns a sink that sends all data to the given <tt>handle</tt>
204and closes the file when done, or a sink that aborts the transmission with
205an error <tt>message</tt>
206</p>
207
208<p class=note>
209In the following example, notice how the prototype is designed to
210fit nicely with the <tt>io.open</tt> function.
211</p>
212
213<pre class=example>
214-- load the ltn12 module
215local ltn12 = require("ltn12")
216
217-- copy a file
218ltn12.pump.all(
219 ltn12.source.file(io.open("original.png")),
220 ltn12.sink.file(io.open("copy.png"))
221)
222</pre>
223
224<!-- null +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
225
226<p class=name id="sink.null">
227ltn12.sink.<b>null()</b>
228</p>
229
230<p class=description>
231Returns a sink that ignores all data it receives.
232</p>
233
234<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
235
236<p class=name id="sink.simplify">
237ltn12.sink.<b>simplify(</b>sink<b>)</b>
238</p>
239
240<p class=description>
241Creates and returns a simple sink given a fancy <tt>sink</tt>.
242</p>
243
244<!-- table ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
245
246<p class=name id="sink.table">
247ltn12.sink.<b>table(</b>[table]<b>)</b>
248</p>
249
250<p class=description>
251Creates a sink that stores all chunks in a table. The chunks can later be
252efficiently concatenated into a single string.
253</p>
254
255<p class=parameters>
256<tt>Table</tt> is used to hold the chunks. If
257<tt><b>nil</b></tt>, the function creates its own table.
258</p>
259
260<p class=return>
261The function returns the sink and the table.
262</p>
263
264<pre class=example>
265-- load needed modules
266local http = require("http")
267local ltn12 = require("ltn12")
268
269-- the http.get function
270function get(u)
271 local t = {}
272 local respt = request{
273 url = u,
274 sink = ltn12.sink.table(t)
275 }
276 return table.concat(t), respt.headers, respt.code, respt.error
277end
278</pre>
279
280<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
281
282<h3 id="source">Sources</h3>
283
284<!-- cat ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
285
286<p class=name id="source.cat">
287ltn12.source.<b>cat(</b>source<sub>1</sub> [, source<sub>2</sub>, ...,
288source<sub>N</sub>]<b>)</b>
289</p>
290
291<p class=description>
292Creates a new source that produces the concatenation of the data produced
293by a number of sources.
294</p>
295
296<p class=parameters>
297<tt>Source<sub>1</sub></tt> to <tt>source<sub>N</sub></tt> are the original
298sources.
299</p>
300
301<p class=return>
302The function returns the new source.
303</p>
304
305<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
306
307<p class=name id="source.chain">
308ltn12.source.<b>chain(</b>source, filter<b>)</b>
309</p>
310
311<p class=description>
312Creates a new <tt>source</tt> that passes data through a <tt>filter</tt>
313before returning it.
314</p>
315
316<p class=return>
317The function returns the new source.
318</p>
319
320<!-- empty ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
321
322<p class=name id="source.empty">
323ltn12.source.<b>empty()</b>
324</p>
325
326<p class=description>
327Creates and returns an empty source.
328</p>
329
330<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
331
332<p class=name id="source.error">
333ltn12.source.<b>error(</b>message<b>)</b>
334</p>
335
336<p class=description>
337Creates and returns a source that aborts transmission with an error
338<tt>message</tt>.
339</p>
340
341<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
342
343<p class=name id="source.file">
344ltn12.source.<b>file(</b>handle, message<b>)</b>
345</p>
346
347<p class=description>
348Creates a source that produces the contents of a file.
349</p>
350
351<p class=parameters>
352<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
353<tt>message</tt> should give the reason for failure.
354</p>
355
356<p class=return>
357The function returns a source that reads chunks of data from
358given <tt>handle</tt> and returns it to the user,
359closing the file when done, or a source that aborts the transmission with
360an error <tt>message</tt>
361</p>
362
363<p class=note>
364In the following example, notice how the prototype is designed to
365fit nicely with the <tt>io.open</tt> function.
366</p>
367
368<pre class=example>
369-- load the ltn12 module
370local ltn12 = require("ltn12")
371
372-- copy a file
373ltn12.pump.all(
374 ltn12.source.file(io.open("original.png")),
375 ltn12.sink.file(io.open("copy.png"))
376)
377</pre>
378
379<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
380
381<p class=name id="source.simplify">
382ltn12.source.<b>simplify(</b>source<b>)</b>
383</p>
384
385<p class=description>
386Creates and returns a simple source given a fancy <tt>source</tt>.
387</p>
388
389<!-- string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
390
391<p class=name id="source.string">
392ltn12.source.<b>string(</b>string<b>)</b>
393</p>
394
395<p class=description>
396Creates and returns a source that produces the contents of a
397<tt>string</tt>, chunk by chunk.
398</p>
399
400<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
401
402<div class=footer>
403<hr>
404<center>
405<p class=bar>
406<a href="home.html">home</a> &middot;
407<a href="home.html#down">download</a> &middot;
408<a href="introduction.html">introduction</a> &middot;
409<a href="reference.html">reference</a>
410</p>
411<p>
412<small>
413Last modified by Diego Nehab on <br>
414Sat Aug 9 01:00:41 PDT 2003
415</small>
416</p>
417</center>
418</div>
419
420</body>
421</html>
diff --git a/doc/mime.html b/doc/mime.html
new file mode 100644
index 0000000..d2fcc3c
--- /dev/null
+++ b/doc/mime.html
@@ -0,0 +1,428 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2 "http://www.w3.org/TR/html4/strict.dtd">
3<html>
4
5<head>
6<title>LuaSocket: Network support for the Lua language</title>
7<link rel="stylesheet" href="reference.css" type="text/css">
8</head>
9
10<body>
11
12<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
13
14<div class=header>
15<hr>
16<center>
17<table summary="LuaSocket logo">
18<tr><td align=center><a href="http://www.lua.org">
19<img border=0 alt="LuaSocket" src="luasocket.png">
20</a></td></tr>
21<tr><td align=center valign=top>Network support for the Lua language
22</td></tr>
23</table>
24<p class=bar>
25<a href="home.html">home</a> &middot;
26<a href="home.html#download">download</a> &middot;
27<a href="introduction.html">introduction</a> &middot;
28<a href="reference.html">reference</a>
29</p>
30</center>
31<hr>
32</div>
33
34<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
35
36<h2 id=mime>MIME</h2>
37
38<p>
39The MIME module offers filters that apply and remove common
40content transfer encodings, such as Base64 and Quoted-Printable.
41It also provides functions to break text into lines and change
42the end-of-line convention.
43MIME is described mainly in
44<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2045.txt">RFC 2045</a>,
45<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">2046</a>,
46<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2047</a>,
47<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2048</a> and
48<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2048.txt">2049</a>.
49</p>
50
51<p>
52All functionality provided by the MIME module
53follows the ideas presented in
54<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
55LTN012, Filters sources and sinks</a>.
56</p>
57
58<!-- High-level +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
59
60<h3 id=high>High-level filters</h3>
61
62<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
63
64<p class=name id="normalize">
65mime.<b>normalize(</b>[marker]<b>)</b>
66</p>
67
68<p class=description>
69Converts most common end-of-line markers to a specific given marker.
70</p>
71
72<p class=parameters>
73<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic
74end-of-line marker defined by the MIME standard.
75</p>
76
77<p class=return>
78The function returns a filter that performs the conversion.
79</p>
80
81<p class=note>
82Note: There is no perfect solution to this problem. Different end-of-line
83markers are an evil that will probably plague developers forever.
84This function, however, will work perfectly for text created with any of
85the most common end-of-line markers, i.e. the MacOS (CR), the Unix (LF),
86or the DOS (CRLF) conventions. Even if the data has mixed end-of-line
87markers, the function will still work well, although it doesn't
88guarantee that the number of empty lines will be correct.
89</p>
90
91<!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
92
93<p class=name id="decode">
94mime.<b>decode(</b>"base64"<b>)</b><br>
95mime.<b>decode(</b>"quoted-printable"<b>)</b>
96</p>
97
98<p class=description>
99Returns a filter that decodes data from a given transfer content
100encoding.
101</p>
102
103<p class=return>
104The function returns the created filter.
105</p>
106
107<!-- encode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
108
109<p class=name id="encode">
110mime.<b>encode(</b>"base64"<b>)</b><br>
111mime.<b>encode(</b>"quoted-printable" [, mode])</b>
112</p>
113
114<p class=description>
115Returns a filter that encodes data according to a given transfer content
116encoding.
117</p>
118
119<p class=parameters>
120In the Quoted-Printable case, the user can specify whether the data is
121textual or binary, by passing the <tt>mode</tt> strings "<tt>text</tt>" or
122"<tt>binary</tt>". <tt>Mode</tt> defaults to "<tt>text</tt>".
123</p>
124
125<p class=return>
126The function returns the created filter.
127</p>
128
129<p class=note>
130Although both transfer content encodings specify a limit for the line
131length, the encoding filters do <em>not</em> break text into lines (for
132added flexibility).
133Below is a filter that converts binary data to the Base64 transfer content
134encoding and breaks it into lines of the correct size.
135</p>
136
137<pre class=example>
138base64 = ltn12.filter.chain(
139 mime.encode("base64"),
140 mime.wrap("base64")
141)
142</pre>
143
144<p class=note>
145Note: Text data <em>has</em> to be converted to canonic form
146<em>before</em> being encoded.
147</p>
148
149<pre class=example>
150base64 = ltn12.filter.chain(
151 mime.normalize(),
152 mime.encode("base64"),
153 mime.wrap("base64")
154)
155</pre>
156
157<!-- wrap +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
158
159<p class=name id="wrap">
160mime.<b>wrap(</b>"text" [, length]<b>)</b><br>
161mime.<b>wrap(</b>"base64"<b>)</b><br>
162mime.<b>wrap(</b>"quoted-printable"<b>)</b>
163</p>
164
165<p class=description>
166Returns a filter that breaks data into lines.
167</p>
168
169<p class=parameters>
170The "<tt>text</tt>" line-wrap filter simply breaks text into lines by
171inserting CRLF end-of-line markers at appropriate positions.
172<tt>Length</tt> defaults 76.
173The "<tt>base64</tt>" line-wrap filter works just like the default
174"<tt>text</tt>" line-wrap filter with default length.
175The function can also wrap "<tt>quoted-printable</tt>" lines, taking care
176not to break lines in the middle of an escaped character. In that case, the
177line length is fixed at 76.
178</p>
179
180<p class=return>
181The function returns the created filter.
182</p>
183
184<p class=note>
185For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following:
186</p>
187
188<pre class=example>
189qp = ltn12.filter.chain(
190 mime.normalize(),
191 mime.encode("quoted-printable"),
192 mime.wrap("quoted-printable")
193)
194</pre>
195
196<p class=note>
197Note: To break into lines with a different end-of-line convention, apply
198a normalization filter after the line break filter.
199</p>
200
201<!-- Low-level ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
202
203<h3 id=low>Low-level filters</h3>
204
205<!-- b64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
206
207<p class=name id="b64">
208A, B = mime.<b>b64(</b>C [, D]<b>)</b>
209</p>
210
211<p class=description>
212Low-level filter to perform Base64 encoding.
213</p>
214
215<p class=description>
216<tt>A</tt> is the encoded version of the largest prefix of
217<tt>C..D</tt>
218that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
219<tt>C..D</tt>, <em>before</em> encoding.
220If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
221the encoding of the remaining bytes of <tt>C</tt>.
222</p>
223
224<p class=note>
225Note: The simplest use of this function is to encode a string into it's
226Base64 transfer content encoding. Notice the extra parenthesis around the
227call to <tt>mime.b64</tt>, to discard the second return value.
228</p>
229
230<pre class=example>
231print((mime.b64("diego:password")))
232--&gt; ZGllZ286cGFzc3dvcmQ=
233</pre>
234
235<!-- eol ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
236
237<p class=name id="eol">
238A, B = mime.<b>eol(</b>C [, D, marker]<b>)</b>
239</p>
240
241<p class=description>
242Low-level filter to perform end-of-line marker translation.
243For each chunk, the function needs to know if the last character of the
244previous chunk could be part of an end-of-line marker or not. This is the
245context the function receives besides the chunk. An updated version of
246the context is returned after each new chunk.
247</p>
248
249<p class=description>
250<tt>A</tt> is the translated version of <tt>D</tt>. <tt>C</tt> is the
251ASCII value of the last character of the previous chunk, if it was a
252candidate for line break, or 0 otherwise.
253<tt>B</tt> is the same as <tt>C</tt>, but for the current
254chunk. If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> includes a
255new end-of-line marker, depending on <tt>C</tt>.
256<tt>Marker</tt> gives the new end-of-line marker and defaults to CRLF.
257</p>
258
259<pre class=example>
260-- translates the end-of-line marker to UNIX
261unix = mime.eol(0, dos, "\n")
262</pre>
263
264<!-- qp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
265
266<p class=name id="qp">
267A, B = mime.<b>qp(</b>C [, D, marker]<b>)</b>
268</p>
269
270<p class=description>
271Low-level filter to perform Quoted-Printable encoding.
272</p>
273
274<p class=description>
275<tt>A</tt> is the encoded version of the largest prefix of
276<tt>C..D</tt>
277that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
278<tt>C..D</tt>, <em>before</em> encoding.
279If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
280the encoding of the remaining bytes of <tt>C</tt>.
281Throughout encoding, occurences of CRLF are replaced by the
282<tt>marker</tt>, which itself defaults to CRLF.
283</p>
284
285<p class=note>
286Note: The simplest use of this function is to encode a string into it's
287Quoted-Printable transfer content encoding.
288Notice the extra parenthesis around the call to <tt>mime.qp</tt>, to discard the second return value.
289</p>
290
291<pre class=example>
292print((mime.qp("maçã")))
293--&gt; ma=E7=E3=
294</pre>
295
296<!-- qpwrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
297
298<p class=name id="qpwrp">
299A, m = mime.<b>qpwrp(</b>n [, B, length]<b>)</b>
300</p>
301
302<p class=description>
303Low-level filter to break Quoted-Printable text into lines.
304</p>
305
306<p class=description>
307<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
308<tt>length</tt> bytes (defaults to 76).
309'<tt>n</tt>' should tell how many bytes are left for the first
310line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
311left in the last line of <tt>A</tt>.
312</p>
313
314<p class=note>
315Note: Besides breaking text into lines, this function makes sure the line
316breaks don't fall in the middle of an escaped character combination. Also,
317this function only breaks lines that are bigger than <tt>length</tt> bytes.
318</p>
319
320<!-- unb64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
321
322<p class=name id="unb64">
323A, B = mime.<b>unb64(</b>C [, D]<b>)</b>
324</p>
325
326<p class=description>
327Low-level filter to perform Base64 decoding.
328</p>
329
330<p class=description>
331<tt>A</tt> is the decoded version of the largest prefix of
332<tt>C..D</tt>
333that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
334<tt>C..D</tt>, <em>before</em> decoding.
335If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is the empty string
336and <tt>B</tt> returns whatever couldn't be decoded.
337</p>
338
339<p class=note>
340Note: The simplest use of this function is to decode a string from it's
341Base64 transfer content encoding.
342Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value.
343</p>
344
345<pre class=example>
346print((mime.unb64("ZGllZ286cGFzc3dvcmQ=")))
347--&gt; diego:password
348</pre>
349
350<!-- unqp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
351
352<p class=name id="unqp">
353A, B = mime.<b>unqp(</b>C [, D]<b>)</b>
354</p>
355
356<p class=description>
357Low-level filter to remove the Quoted-Printable transfer content encoding
358from data.
359</p>
360
361<p class=description>
362<tt>A</tt> is the decoded version of the largest prefix of
363<tt>C..D</tt>
364that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
365<tt>C..D</tt>, <em>before</em> decoding.
366If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is augmented with
367the encoding of the remaining bytes of <tt>C</tt>.
368</p>
369
370<p class=note>
371Note: The simplest use of this function is to decode a string from it's
372Quoted-Printable transfer content encoding.
373Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value.
374</p>
375
376<pre class=example>
377print((mime.qp("ma=E7=E3=")))
378--&gt; maçã
379</pre>
380
381<!-- wrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
382
383<p class=name id="wrp">
384A, m = mime.<b>wrp(</b>n [, B, length]<b>)</b>
385</p>
386
387<p class=description>
388Low-level filter to break text into lines with CRLF marker.
389Text is assumed to be in the <a href=#normalize><tt>normalize</tt></a> form.
390</p>
391
392<p class=description>
393<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
394<tt>length</tt> bytes (defaults to 76).
395'<tt>n</tt>' should tell how many bytes are left for the first
396line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
397left in the last line of <tt>A</tt>.
398</p>
399
400<p class=note>
401Note: This function only breaks lines that are bigger than
402<tt>length</tt> bytes. The resulting line length does not include the CRLF
403marker.
404</p>
405
406
407<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
408
409<div class=footer>
410<hr>
411<center>
412<p class=bar>
413<a href="home.html">home</a> &middot;
414<a href="home.html#down">download</a> &middot;
415<a href="introduction.html">introduction</a> &middot;
416<a href="reference.html">reference</a>
417</p>
418<p>
419<small>
420Last modified by Diego Nehab on <br>
421Sat Aug 9 01:00:41 PDT 2003
422</small>
423</p>
424</center>
425</div>
426
427</body>
428</html>
diff --git a/doc/reference.css b/doc/reference.css
index 607ac4f..7c8148d 100644
--- a/doc/reference.css
+++ b/doc/reference.css
@@ -10,6 +10,9 @@ tt {
10 10
11h1, h2, h3, h4 { margin-left: 0em; } 11h1, h2, h3, h4 { margin-left: 0em; }
12 12
13
14h3 { padding-top: 1em; }
15
13p { margin-left: 1em; } 16p { margin-left: 1em; }
14 17
15p.name { 18p.name {
diff --git a/doc/reference.html b/doc/reference.html
index ba519c0..ebcfb5b 100644
--- a/doc/reference.html
+++ b/doc/reference.html
@@ -36,7 +36,7 @@
36<h2>Reference</h2> 36<h2>Reference</h2>
37 37
38<blockquote> 38<blockquote>
39<a href="dns.html">DNS services (socket.dns)</a> 39<a href="dns.html">DNS (in socket)</a>
40<blockquote> 40<blockquote>
41<a href="dns.html#toip">toip</a>, 41<a href="dns.html#toip">toip</a>,
42<a href="dns.html#tohostname">tohostname</a>, 42<a href="dns.html#tohostname">tohostname</a>,
@@ -47,31 +47,17 @@
47<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 47<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
48 48
49<blockquote> 49<blockquote>
50<a href="ftp.html">FTP (socket.ftp)</a> 50<a href="ftp.html">FTP</a>
51<blockquote> 51<blockquote>
52<a href="ftp.html#get">get</a>, 52<a href="ftp.html#get">get</a>,
53<a href="ftp.html#put">put</a>, 53<a href="ftp.html#put">put</a>
54<a href="ftp.html#open">open</a>.
55</blockquote> 54</blockquote>
56</blockquote> 55</blockquote>
57 56
58<!-- global +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
59
60<blockquote>
61<a href="global.html">Global symbols</a>
62<blockquote>
63<a href="global.html#LUASOCKET_LIBNAME">LUASOCKET_LIBNAME</a>,
64<a href="global.html#mime">mime</a>,
65<a href="global.html#ltn12">ltn12</a>,
66<a href="global.html#socket">socket</a>.
67</blockquote>
68</blockquote>
69</table>
70
71<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 57<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
72 58
73<blockquote> 59<blockquote>
74<a href="http.html">HTTP (socket.http)</a> 60<a href="http.html">HTTP</a>
75<blockquote> 61<blockquote>
76<a href="http.html#get">get</a>, 62<a href="http.html#get">get</a>,
77<a href="http.html#post">post</a>, 63<a href="http.html#post">post</a>,
@@ -82,46 +68,45 @@
82<!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 68<!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
83 69
84<blockquote> 70<blockquote>
85<a href="ltn12.html">LTN012 (ltn12)</a> 71<a href="ltn12.html">LTN12</a>
86<blockquote> 72<blockquote>
87<a href="ltn12.html#filter">filter</a>: 73<a href="ltn12.html#filter">filter</a>:
88<a href="ltn12.html#chain">chain</a>, 74<a href="ltn12.html#filter.chain">chain</a>,
89<a href="ltn12.html#cycle">cycle</a>. 75<a href="ltn12.html#filter.cycle">cycle</a>.
90</blockquote> 76</blockquote>
91<blockquote> 77<blockquote>
92<a href="ltn12.html#pump">pump</a>: 78<a href="ltn12.html#pump">pump</a>:
93<a href="ltn12.html#all">all</a>, 79<a href="ltn12.html#pump.all">all</a>,
94<a href="ltn12.html#step">step</a>. 80<a href="ltn12.html#pump.step">step</a>.
95</blockquote> 81</blockquote>
96<blockquote> 82<blockquote>
97<a href="ltn12.html#sink">sink</a>: 83<a href="ltn12.html#sink">sink</a>:
98<a href="ltn12.html#chain">chain</a>, 84<a href="ltn12.html#sink.chain">chain</a>,
99<a href="ltn12.html#error">error</a>, 85<a href="ltn12.html#sink.error">error</a>,
100<a href="ltn12.html#chain">file</a>, 86<a href="ltn12.html#sink.file">file</a>,
101<a href="ltn12.html#file">null</a>, 87<a href="ltn12.html#sink.null">null</a>,
102<a href="ltn12.html#simplify">simplify</a>, 88<a href="ltn12.html#sink.simplify">simplify</a>,
103<a href="ltn12.html#table">table</a>. 89<a href="ltn12.html#sink.table">table</a>.
104</blockquote> 90</blockquote>
105<blockquote> 91<blockquote>
106<a href="ltn12.html#source">source</a>: 92<a href="ltn12.html#source">source</a>:
107<a href="ltn12.html#cat">cat</a>, 93<a href="ltn12.html#source.cat">cat</a>,
108<a href="ltn12.html#chain">chain</a>, 94<a href="ltn12.html#source.chain">chain</a>,
109<a href="ltn12.html#empty">empty</a>, 95<a href="ltn12.html#source.empty">empty</a>,
110<a href="ltn12.html#file">file</a>, 96<a href="ltn12.html#source.error">error</a>,
111<a href="ltn12.html#simplify">simplify</a>, 97<a href="ltn12.html#source.file">file</a>,
112<a href="ltn12.html#simplify">rewind</a>, 98<a href="ltn12.html#source.simplify">simplify</a>,
113<a href="ltn12.html#simplify">string</a>. 99<a href="ltn12.html#source.string">string</a>.
114</blockquote> 100</blockquote>
115</blockquote> 101</blockquote>
116 102
117<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 103<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
118 104
119<blockquote> 105<blockquote>
120<a href="mime.html">MIME (mime) </a> 106<a href="mime.html">MIME</a>
121<blockquote> 107<blockquote>
122<a href="mime.html#high">high-level</a>: 108<a href="mime.html#high">high-level</a>:
123<a href="mime.html#normalize">normalize</a>, 109<a href="mime.html#normalize">normalize</a>,
124<a href="mime.html#chain">chain</a>,
125<a href="mime.html#decode">decode</a>, 110<a href="mime.html#decode">decode</a>,
126<a href="mime.html#encode">encode</a>, 111<a href="mime.html#encode">encode</a>,
127<a href="mime.html#wrap">wrap</a>. 112<a href="mime.html#wrap">wrap</a>.
@@ -141,9 +126,8 @@
141<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 126<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
142 127
143<blockquote> 128<blockquote>
144<a href="smtp.html">SMTP (socket.smtp)</a> 129<a href="smtp.html">SMTP</a>
145<blockquote> 130<blockquote>
146<a href="smtp.html#mail">open</a>,
147<a href="smtp.html#message">message</a>, 131<a href="smtp.html#message">message</a>,
148<a href="smtp.html#send">send</a>. 132<a href="smtp.html#send">send</a>.
149</blockquote> 133</blockquote>
@@ -152,26 +136,20 @@
152<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 136<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
153 137
154<blockquote> 138<blockquote>
155<a href="socket.html">The socket namespace (socket)</a> 139<a href="socket.html">Socket</a>
156<blockquote> 140<blockquote>
157<a href="tcp.html#bind">bind</a>, 141<a href="socket.html#debug">DEBUG</a>,
158<a href="tcp.html#connect">connect</a>,
159<a href="socket.html#debug">debug</a>,
160<a href="dns.html#dns">dns</a>, 142<a href="dns.html#dns">dns</a>,
161<a href="ftp.html#ftp">ftp</a>,
162<a href="http.html#http">http</a>,
163<a href="socket.html#protect">protect</a>, 143<a href="socket.html#protect">protect</a>,
164<a href="socket.html#select">select</a>, 144<a href="socket.html#select">select</a>,
165<a href="socket.html#sink">sink</a>, 145<a href="socket.html#sink">sink</a>,
166<a href="socket.html#source">source</a>, 146<a href="socket.html#source">source</a>,
167<a href="socket.html#sleep">sleep</a>, 147<a href="socket.html#sleep">sleep</a>,
168<a href="smtp.html#smtp">smtp</a>,
169<a href="socket.html#time">time</a>, 148<a href="socket.html#time">time</a>,
170<a href="tcp.html#tcp">tcp</a>, 149<a href="tcp.html#tcp">tcp</a>,
171<a href="socket.html#try">try</a>, 150<a href="socket.html#try">try</a>,
172<a href="udp.html#udp">udp</a>, 151<a href="udp.html#udp">udp</a>,
173<a href="url.html#url">url</a>, 152<a href="socket.html#version">VERSION</a>.
174<a href="socket.html#version">version</a>.
175</blockquote> 153</blockquote>
176</blockquote> 154</blockquote>
177</table> 155</table>
@@ -179,7 +157,7 @@
179<!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 157<!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
180 158
181<blockquote> 159<blockquote>
182<a href="tcp.html">TCP (socket.tcp)</a> 160<a href="tcp.html">TCP (in socket)</a>
183<blockquote> 161<blockquote>
184<a href="tcp.html#accept">accept</a>, 162<a href="tcp.html#accept">accept</a>,
185<a href="tcp.html#bind">bind</a>, 163<a href="tcp.html#bind">bind</a>,
@@ -198,7 +176,7 @@
198<!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 176<!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
199 177
200<blockquote> 178<blockquote>
201<a href="udp.html">UDP (socket.udp)</a> 179<a href="udp.html">UDP (in socket)</a>
202<blockquote> 180<blockquote>
203<a href="udp.html#close">close</a>, 181<a href="udp.html#close">close</a>,
204<a href="udp.html#getpeername">getpeername</a>, 182<a href="udp.html#getpeername">getpeername</a>,
@@ -210,23 +188,22 @@
210<a href="udp.html#setpeername">setpeername</a>, 188<a href="udp.html#setpeername">setpeername</a>,
211<a href="udp.html#setsockname">setsockname</a>, 189<a href="udp.html#setsockname">setsockname</a>,
212<a href="udp.html#setoption">setoption</a>, 190<a href="udp.html#setoption">setoption</a>,
213<a href="udp.html#settimeout">settimeout</a>, 191<a href="udp.html#settimeout">settimeout</a>.
214<a href="udp.html#settimeout">shutdown</a>.
215</blockquote> 192</blockquote>
216</blockquote> 193</blockquote>
217 194
218<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 195<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
219 196
220<blockquote> 197<blockquote>
221<a href="url.html">URL (socket.url) </a> 198<a href="url.html">URL</a>
222<blockquote> 199<blockquote>
223<a href="url.html#absolute">absolute</a>, 200<a href="url.html#absolute">absolute</a>,
224<a href="url.html#build">build</a>, 201<a href="url.html#build">build</a>,
225<a href="url.html#build_path">build_path</a>, 202<a href="url.html#build_path">build_path</a>,
226<a href="url.html#quote">quote</a>, 203<a href="url.html#escape">escape</a>,
227<a href="url.html#parse">parse</a>, 204<a href="url.html#parse">parse</a>,
228<a href="url.html#parse_path">parse_path</a>, 205<a href="url.html#parse_path">parse_path</a>,
229<a href="url.html#quote">unquote</a>. 206<a href="url.html#unescape">unescape</a>.
230</blockquote> 207</blockquote>
231</blockquote> 208</blockquote>
232 209
diff --git a/doc/smtp.html b/doc/smtp.html
index 0862de0..b0ae634 100644
--- a/doc/smtp.html
+++ b/doc/smtp.html
@@ -35,15 +35,22 @@
35 35
36<h2 id=smtp>SMTP</h2> 36<h2 id=smtp>SMTP</h2>
37 37
38<p> 38<p> The <tt>smtp.lua</tt> module provides functionality to send e-mail
39The <tt>smtp.lua</tt> module provides functionality to send e-mail
40messages. The implementation conforms to the Simple Mail Transfer Protocol, 39messages. The implementation conforms to the Simple Mail Transfer Protocol,
41<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>. 40<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>.
42The other RFC of interest in this implementation is 41Another RFC of interest is <a
43<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>, 42href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>,
44which governs the Internet Message Format. 43which governs the Internet Message Format.
44Multipart messages (those that contain attatchments) are part
45of the MIME standard, but described mainly
46in <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">RFC
472046</a>
45 48
46</p> 49<p> In the description below, good understanding of <a
50href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters
51sources and sinks</a> and the <a href=mime.html>MIME</a> module is
52assumed. In fact, the SMTP module was the main reason for their
53creation. </p>
47 54
48<p> 55<p>
49MIME headers are represented as a Lua table in the form: 56MIME 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
78in representing them in a Lua table. 85in representing them in a Lua table.
79</p> 86</p>
80 87
81<!-- mail +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 88<p>
89The following constants can be set to control the default behaviour of
90the SMTP module:
91</p>
82 92
83<p class=name id=mail> 93<ul>
84socket.smtp.<b>mail{</b><br> 94<li> <tt>DOMAIN</tt>: domain used to greet the server;
95<li> <tt>PORT</tt>: default port used for the connection;
96<li> <tt>SERVER</tt>: default server used for the connection;
97<li> <tt>TIMEOUT</tt>: default timeout for all I/O operations;
98<li> <tt>ZONE</tt>: default time zone.
99</ul>
100
101<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
102
103<p class=name id=send>
104smtp.<b>send{</b><br>
85&nbsp;&nbsp;from = <i>string</i>,<br> 105&nbsp;&nbsp;from = <i>string</i>,<br>
86&nbsp;&nbsp;rcpt = <i>string</i> or <i>string-table</i>,<br> 106&nbsp;&nbsp;rcpt = <i>string</i> or <i>string-table</i>,<br>
87&nbsp;&nbsp;body = <i>string</i>,<br> 107&nbsp;&nbsp;source = <i>LTN12 source</i>,<br>
88&nbsp;&nbsp;headers = <i>headers-table</i>,<br> 108&nbsp;&nbsp;[server = <i>string</i>],<br>
89&nbsp;&nbsp;server = <i>string</i><br> 109&nbsp;&nbsp;[port = <i>string</i>]<br>
110&nbsp;&nbsp;[domain = <i>string</i>],<br>
111&nbsp;&nbsp;[step = <i>LTN12 pump step</i>],<br>
90<b>}</b> 112<b>}</b>
91</p> 113</p>
92 114
93<p class=description> 115<p class=description>
94Sends a message to a recipient list. 116Sends a message to a recipient list. Since sending messages is not as
117simple as downloading an URL from a FTP or HTTP server, this function
118doesn't have a simple interface. However, see the
119<a href=#message><tt>message</tt></a> source factory for
120a very powerful way to define the message contents.
95</p> 121</p>
96 122
97<p class=parameters> 123<p class=parameters>
98<tt>Rcpt</tt> is a Lua table with one entry for each recipient, or a string 124The sender is given by the e-mail address in the <tt>from</tt> field.
125<tt>Rcpt</tt> is a Lua table with one entry for each recipient e-mail
126address, or a string
99in case there is just one recipient. 127in case there is just one recipient.
100The sender is given by the e-mail address <tt>from</tt>. 128The contents of the message are given by a LTN12 <tt>source</tt>. Several
101The message is composed by the optional MIME Headers <tt>headers</tt> 129arguments are optional:
102and text <tt>body</tt>. The message is sent using the server 130<ul>
103<tt>server</tt>. 131<li> <tt>server</tt>: Server to connect to. Defaults to "localhost";
132<li> <tt>port</tt>: Port to connect to. Defaults to 25;
133<li> <tt>domain</tt>: Domain name used to greet the server; Defaults to the
134local machine host name;
135<li> <tt>step</tt>: LTN12 pump step function used to pass data from the
136source to the server. Defaults to the LTN12 <tt>pump.step</tt> function.
137</ul>
104</p> 138</p>
105 139
106<p class=return> 140<p class=return>
@@ -109,6 +143,13 @@ If successful, the function returns 1. Otherwise, the function returns
109</p> 143</p>
110 144
111<p class=note> 145<p class=note>
146Note: SMTP servers are can be very picky with the format of e-mail
147addresses. To be safe, use only addresses of the form
148"<tt>&lt;fulano@tecgraf.puc-rio.br&gt;</tt>" in the <tt>from</tt> and
149<tt>rcpt</tt> arguments to the <tt>send</tt> function. In headers, e-mail
150addresses can take whatever form you like. </p>
151
152<p class=note>
112Big note: There is a good deal of misconception with the use of the 153Big note: There is a good deal of misconception with the use of the
113destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>', 154destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>',
114and, more importantly, the '<tt>Bcc</tt>' headers. Do <em>not</em> add a 155and, more importantly, the '<tt>Bcc</tt>' headers. Do <em>not</em> add a
@@ -117,11 +158,12 @@ exact opposite of what you expect.
117</p> 158</p>
118 159
119<p class=note> 160<p class=note>
120Only recipients specified in the recipient list will receive a copy of the 161Only recipients specified in the <tt>rcpt</tt> list will receive a copy of the
121message. Each recipient of an SMTP mail message receives a copy of the 162message. Each recipient of an SMTP mail message receives a copy of the
122message body along with the headers, and nothing more. The headers are 163message body along with the headers, and nothing more. The headers
123considered as part of the message. The list of recipients is <em>not</em> 164<em>are</em> part of the message and should be produced by the LTN12
124part of the message. 165<tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em>
166part of the message and will not be sent to anyone.
125</p> 167</p>
126 168
127<p class=note> 169<p class=note>
@@ -143,9 +185,9 @@ Copy") contains addresses of recipients of the message whose addresses are not t
143</ul> 185</ul>
144 186
145<p class=note> 187<p class=note>
146The LuaSocket <tt>mail</tt> function does not interpret the headers you 188The LuaSocket <tt>send</tt> function does not care or interpret the
147pass to, but it gives you full control over what is sent and to whom 189headers you send, but it gives you full control over what is sent and
148it is sent: 190to whom it is sent:
149</p> 191</p>
150<ul> 192<ul>
151<li> If someone is to receive the message, the e-mail address <em>has</em> 193<li> If someone is to receive the message, the e-mail address <em>has</em>
@@ -171,36 +213,147 @@ and
171</p> 213</p>
172 214
173<pre class=example> 215<pre class=example>
216-- load the smtp support
217local smtp = require("smtp")
218
174-- Connects to server "localhost" and sends a message to users 219-- Connects to server "localhost" and sends a message to users
175-- "fulano@tecgraf.puc-rio.br", "beltrano@tecgraf.puc-rio.br", 220-- "fulano@tecgraf.puc-rio.br", "beltrano@tecgraf.puc-rio.br",
176-- and "sicrano@tecgraf.puc-rio.br". 221-- and "sicrano@tecgraf.puc-rio.br".
177-- Note that "fulano" is the primary recipient, "beltrano" receives a 222-- Note that "fulano" is the primary recipient, "beltrano" receives a
178-- carbon copy and neither of them knows that "sicrano" received a blind 223-- carbon copy and neither of them knows that "sicrano" received a blind
179-- carbon copy of the message. 224-- carbon copy of the message.
180headers = { 225from = "&lt;luasocket@tecgraf.puc-rio.br&gt;"
181 to = "fulano@tecgraf.puc-rio.br",
182 cc = "beltrano@tecgraf.puc-rio.br",
183 subject = "LuaSocket test message"
184}
185
186from = "luasocket@tecgraf.puc-rio.br"
187 226
188rcpt = { 227rcpt = {
189 "fulano@tecgraf.puc-rio.br", 228 "&lt;fulano@tecgraf.puc-rio.br&gt;",
190 "beltrano@tecgraf.puc-rio.br", 229 "&lt;beltrano@tecgraf.puc-rio.br&gt;",
191 "sicrano@tecgraf.puc-rio.br" 230 "&lt;sicrano@tecgraf.puc-rio.br&gt;"
192} 231}
193 232
194body = "This is a test message. Please ignore." 233mesgt = {
195 234 headers = {
196server = "localhost" 235 to = "Fulano da Silva &lt;fulano@tecgraf.puc-rio.br&gt;",
236 cc = '"Beltrano F. Nunes" &lt;beltrano@tecgraf.puc-rio.br&gt;',
237 subject = "My first message"
238 }
239 body = "I hope this works. If it does, I can send you another 1000 copies."
240}
197 241
198r, e = socket.smtp.mail{ 242r, e = smtp.send{
199 from = from, 243 from = from,
200 rcpt = rcpt, 244 rcpt = rcpt,
201 headers = headers, 245 source = smtp.message(mesgt)
202 body = body, 246}
203 server = server 247</pre>
248
249<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
250
251<p class=name id=message>
252smtp.<b>message(</b>mesgt<b>)</b>
253</p>
254
255<p class=description>
256Returns a LTN12 source that sends an SMTP message body, possibly multipart
257(arbitrarily deep).
258</p>
259
260<p class=parameters>
261The only parameter of the function is a table describing the message.
262<tt>Mesgt</tt> has the following form (notice the recursive structure):
263</p>
264
265<blockquote>
266<table summary="Mesgt table structure">
267<tr><td><tt>
268mesgt = {<br>
269&nbsp;&nbsp;headers = <i>header-table</i>,<br>
270&nbsp;&nbsp;body = <i>LTN12 source</i> or <i>string</i> or
271<i>multipart-mesgt</i><br>
272}<br>
273&nbsp;<br>
274multipart-mesgt = {<br>
275&nbsp;&nbsp;preamble = <i>string</i><br>
276&nbsp;&nbsp;[1] = <i>mesgt</i>,<br>
277&nbsp;&nbsp;[2] = <i>mesgt</i>,<br>
278&nbsp;&nbsp;...<br>
279&nbsp;&nbsp;[<i>n</i>] = <i>mesgt</i>,<br>
280&nbsp;&nbsp;epilogue = <i>string</i>,<br>
281}<br>
282</tt></td></tr>
283</table>
284</blockquote>
285
286<p class=parameters>
287For a simple message, all that is needed is a set of <tt>headers</tt>
288and the <tt>body</tt>. The message <tt>body</tt> can be given as a string
289or as a LTN12 source. For multipart messages, the body is a table that
290recursively defines each part as an independent message, plus a preamble
291and an epilogue.
292</p>
293
294<p class=return>
295The function returns an LTN12 source that produces the message contents as
296defined by <tt>mesgt</tt>. Hopefuly, the following example will make
297things clear. When in doubt, refer to the appropriate RFC as listed in the
298introduction. </p>
299
300<pre class=example>
301-- load the smtp support and its friends
302local smtp = require("smtp")
303local mime = require("mime")
304local ltn12 = require("ltn12")
305
306-- creates a source to send a message with two parts. The first part is
307-- plain text, the second part is a PNG image, encoded as base64.
308source = smtp.message{
309 headers = {
310 -- Remember that headers are *ignored* by smtp.send.
311 from = "Sicrano de Oliveira &lt;sicrano@tecgraf.puc-rio.br&gt;",
312 to = "Fulano da Silva &lt;fulano@tecgraf.puc-rio.br&gt;",
313 subject = "Here is a message with attachments"
314 },
315 body = {
316 preamble = "If your client doesn't understand attachments, \r\n" ..
317 "it will still display the preamble and the epilogue.\r\n",
318 "Preamble might show up even in a MIME enabled client.",
319 -- first part: no headers means plain text, us-ascii.
320 -- The mime.eol low-level filter normalizes end-of-line markers.
321 [1] = {
322 body = mime.eol(0, [[
323 Lines in a message body should always end with CRLF.
324 The smtp module will *NOT* perform translation. It will
325 perform necessary stuffing or '.' characters, though.
326 ]])
327 },
328 -- second part: headers describe content to be a png image,
329 -- sent under the base64 transfer content encoding.
330 -- notice that nothing happens until the message is actually sent.
331 -- small chunks are loaded into memory right before transmission and
332 -- translation happens on the fly.
333 [2] = {
334 headers = {
335 ["content-type"] = 'image/png; name="image.png"',
336 ["content-disposition"] = 'attachment; filename="image.png"',
337 ["content-description"] = 'a beautiful image',
338 ["content-transfer-encoding"] = "BASE64"
339 },
340 body = ltn12.source.chain(
341 ltn12.source.file(io.open("image.png", "rb")),
342 ltn12.filter.chain(
343 mime.encode("base64"),
344 mime.wrap()
345 )
346 )
347 },
348 epilogue = "This might also show up, but after the attachments"
349 }
350}
351
352-- finally send it
353r, e = smtp.send{
354 from = "&lt;sicrano@tecgraf.puc-rio.br&gt;",
355 rcpt = "&lt;fulano@tecgraf.puc-rio.br&gt;",
356 source = source,
204} 357}
205</pre> 358</pre>
206 359
diff --git a/doc/socket.html b/doc/socket.html
index bde882b..eccc676 100644
--- a/doc/socket.html
+++ b/doc/socket.html
@@ -36,16 +36,13 @@
36<h2 id=socket>The socket namespace</h2> 36<h2 id=socket>The socket namespace</h2>
37 37
38<p> 38<p>
39The <tt>socket</tt> namespace contains the namespace tables for all 39The <tt>socket</tt> namespace contains the core functionality of LuaSocket.
40LuaSocket modules as well as function that didn't belong in any specific
41module, functions that are so commonly used that deserve a shortcut and a
42few constants.
43</p> 40</p>
44 41
45<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 42<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
46 43
47<p class=name id=debug> 44<p class=name id=debug>
48socket.<b>debug</b> 45socket.<b>DEBUG</b>
49</p> 46</p>
50 47
51<p class=description> 48<p class=description>
@@ -57,7 +54,7 @@ with debug support.
57<!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 54<!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
58 55
59<p class=name id=protect> 56<p class=name id=protect>
60socket.<b>protect(</b>function<b>)</b> 57socket.<b>protect(</b>func<b>)</b>
61</p> 58</p>
62 59
63<p class=description> 60<p class=description>
@@ -65,12 +62,12 @@ Converts a function that throws exceptions into a safe function.
65</p> 62</p>
66 63
67<p class=parameters> 64<p class=parameters>
68<tt>Function</tt> is a function that calls 65<tt>Funct</tt> is a function that calls
69<a href=#try><tt>try</tt></a> to throw exceptions. 66<a href=#try><tt>try</tt></a> to throw exceptions.
70</p> 67</p>
71 68
72<p class=return> 69<p class=return>
73The function an equivalent function that instead of throwing exceptoins, 70Returns an equivalent function that instead of throwing exceptions,
74returns <tt><b>nil</b></tt> followed by an error message. 71returns <tt><b>nil</b></tt> followed by an error message.
75</p> 72</p>
76 73
@@ -103,16 +100,16 @@ simplify the test if a specific socket has changed status.
103</p> 100</p>
104 101
105<p class=note> 102<p class=note>
106<b>Important Note</b>: a known bug in WinSock causes <tt>select</tt> to fail 103<b>Important note</b>: a known bug in WinSock causes <tt>select</tt> to fail
107on non-blocking TCP sockets. The function may return a socket as 104on non-blocking TCP sockets. The function may return a socket as
108writable even though the socket is <em>not</em> ready for sending. 105writable even though the socket is <em>not</em> ready for sending.
109</p> 106</p>
110 107
111<p class=note> 108<p class=note>
112<b>Important note</b>: calling select with a server socket in the receive 109<b>Another important note</b>: calling select with a server socket in the receive
113parameter before a call to accept does <em>not</em> guarantee 110parameter before a call to accept does <em>not</em> guarantee
114<a href=tcp.html#accept><tt>accept</tt></a> will return immediately. 111<a href=tcp.html#accept><tt>accept</tt></a> will return immediately.
115Use the <a href=tcp.html#timeout><tt>timeout</tt></a> 112Use the <a href=tcp.html#settimeout><tt>settimeout</tt></a>
116method or <tt>accept</tt> might block forever. 113method or <tt>accept</tt> might block forever.
117</p> 114</p>
118 115
@@ -131,7 +128,7 @@ socket.<b>sink(</b>mode, socket<b>)</b>
131 128
132<p class=description> 129<p class=description>
133Creates an 130Creates an
134<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a> 131<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
135sink from a stream socket object. 132sink from a stream socket object.
136</p> 133</p>
137 134
@@ -163,7 +160,7 @@ socket.<b>source(</b>mode, socket [, length]<b>)</b>
163 160
164<p class=description> 161<p class=description>
165Creates an 162Creates an
166<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a> 163<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
167source from a stream socket object. 164source from a stream socket object.
168</p> 165</p>
169 166
@@ -217,7 +214,7 @@ c = socket.try(socket.connect("localhost", 80))
217<!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 214<!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
218 215
219<p class=name id=version> 216<p class=name id=version>
220socket.<b>version</b> 217socket.<b>VERSION</b>
221</p> 218</p>
222 219
223<p class=description> 220<p class=description>
diff --git a/doc/tcp.html b/doc/tcp.html
index 34d6c6e..7a49660 100644
--- a/doc/tcp.html
+++ b/doc/tcp.html
@@ -241,18 +241,16 @@ method returns <b><tt>nil</tt></b> followed by an error message.
241<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 241<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
242 242
243<p class=name id=receive> 243<p class=name id=receive>
244client:<b>receive(</b>[pattern<sub>1</sub>, pattern<sub>2</sub>, 244client:<b>receive(</b>[pattern]<b>)</b>
245... pattern<sub>N</sub>]<b>)</b>
246</p> 245</p>
247 246
248<p class=description> 247<p class=description>
249Reads data from a client object, according to the specified <em>read 248Reads data from a client object, according to the specified <em>read
250patterns</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. 249pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
251</p> 250</p>
252 251
253<p class=parameters> 252<p class=parameters>
254The parameters <tt>pattern</tt><sub>1</sub>, <tt>pattern</tt><sub>2</sub>, ... 253<tt>Pattern</tt> can be any of the following:
255<tt>pattern</tt><sub>N</sub> can be any of the following:
256</p> 254</p>
257 255
258<ul> 256<ul>
@@ -267,16 +265,21 @@ of bytes from the socket.
267</ul> 265</ul>
268 266
269<p class=return> 267<p class=return>
270The method returns one value for each pattern, followed by a single 268If successful, the method returns the received pattern. In case of error,
271error code that can be <b><tt>nil</tt></b> in case of success, the string 269the method returns <tt><b>nil</b></tt> followed by an error message which
272'<tt>closed</tt>' in case the connection was closed before the 270can be the string '<tt>closed</tt>' in case the connection was
273transmission was completed or the string '<tt>timeout</tt>' in case 271closed before the transmission was completed or the string
274there was a timeout during the operation. 272'<tt>timeout</tt>' in case there was a timeout during the operation.
273Also, after the error message, the function returns the partial result of
274the transmission.
275</p> 275</p>
276 276
277<p class=note> 277<p class=note>
278Note: In case of error, the method always return everything it managed 278<b>Important note</b>: This function was changed <em>severely</em>. It used
279to download before the error condition was met. 279to support multiple patterns (but I have never seen this feature used) and
280partial results used to be returned in the same way as successful results.
281This last feature violated the idea that all functions should return
282<tt><b>nil</b></tt> on error. Thus the change.
280</p> 283</p>
281 284
282<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 285<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@@ -428,7 +431,7 @@ client:<b>shutdown(</b>mode<b>)</b><br>
428</p> 431</p>
429 432
430<p class=description> 433<p class=description>
431Shuts down part of a full duplex connection. 434Shuts down part of a full-duplex connection.
432</p> 435</p>
433 436
434<p class=parameters> 437<p class=parameters>
diff --git a/doc/udp.html b/doc/udp.html
index 9f5cf8f..5ae0a89 100644
--- a/doc/udp.html
+++ b/doc/udp.html
@@ -29,6 +29,11 @@
29<hr> 29<hr>
30</div> 30</div>
31 31
32
33<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
34
35<h2 id=udp>UDP</h2>
36
32<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 37<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
33 38
34<p class="name" id="socket.udp"> 39<p class="name" id="socket.udp">
diff --git a/doc/url.html b/doc/url.html
index f3a7cb7..cd699a2 100644
--- a/doc/url.html
+++ b/doc/url.html
@@ -59,7 +59,7 @@ An URL is defined by the following grammar:
59<!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 59<!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
60 60
61<p class=name id=absolute> 61<p class=name id=absolute>
62socket.url.<b>absolute(</b>base, relative<b>)</b> 62url.<b>absolute(</b>base, relative<b>)</b>
63</p> 63</p>
64 64
65<p class=description> 65<p class=description>
@@ -79,7 +79,7 @@ The function returns a string with the absolute URL.
79Note: The rules that 79Note: The rules that
80govern the composition are fairly complex, and are described in detail in 80govern the composition are fairly complex, and are described in detail in
81<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>. 81<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>.
82The example bellow should give an idea of what are the rules. 82The example bellow should give an idea of what the rules are.
83</p> 83</p>
84 84
85<pre class=example> 85<pre class=example>
@@ -114,7 +114,7 @@ g;x?y#s = http://a/b/c/g;x?y#s
114<!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 114<!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
115 115
116<p class=name id=build> 116<p class=name id=build>
117socket.url.<b>build(</b>parsed_url<b>)</b> 117url.<b>build(</b>parsed_url<b>)</b>
118</p> 118</p>
119 119
120<p class=description> 120<p class=description>
@@ -135,7 +135,7 @@ The function returns a string with the built URL.
135<!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 135<!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
136 136
137<p class=name id=build_path> 137<p class=name id=build_path>
138socket.url.<b>build_path(</b>segments, unsafe<b>)</b> 138url.<b>build_path(</b>segments, unsafe<b>)</b>
139</p> 139</p>
140 140
141<p class=description> 141<p class=description>
@@ -157,10 +157,39 @@ The function returns a string with the
157built <tt>&lt;path&gt;</tt> component. 157built <tt>&lt;path&gt;</tt> component.
158</p> 158</p>
159 159
160<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
161
162<p class=name id="escape">
163url.<b>escape(</b>content<b>)</b>
164</p>
165
166<p class=description>
167Applies the URL escaping content coding to a string
168Each byte is encoded as a percent character followed
169by the two byte hexadecimal representation of its integer
170value.
171</p>
172
173<p class=parameters>
174<tt>Content</tt> is the string to be encoded.
175</p>
176
177<p class=result>
178The function returns the encoded string.
179</p>
180
181<pre class=example>
182-- load url module
183url = require("url")
184
185code = url.escape("/#?;")
186-- code = "%2f%23%3f%3b"
187</pre>
188
160<!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 189<!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
161 190
162<p class=name id=parse> 191<p class=name id=parse>
163socket.url.<b>parse(</b>url, default<b>)</b> 192url.<b>parse(</b>url, default<b>)</b>
164</p> 193</p>
165 194
166<p class=description> 195<p class=description>
@@ -196,7 +225,10 @@ parsed_url = {<br>
196</tt></blockquote> 225</tt></blockquote>
197 226
198<pre class=example> 227<pre class=example>
199parsed_url = socket.url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there") 228-- load url module
229url = require("url")
230
231parsed_url = url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there")
200-- parsed_url = { 232-- parsed_url = {
201-- scheme = "http", 233-- scheme = "http",
202-- authority = "www.puc-rio.br", 234-- 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"
206-- host = "www.puc-rio.br", 238-- host = "www.puc-rio.br",
207-- } 239-- }
208 240
209parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i") 241parsed_url = url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i")
210-- parsed_url = { 242-- parsed_url = {
211-- scheme = "ftp", 243-- scheme = "ftp",
212-- authority = "root:passwd@unsafe.org", 244-- authority = "root:passwd@unsafe.org",
@@ -222,7 +254,7 @@ parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i
222<!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 254<!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
223 255
224<p class=name id=parse_path> 256<p class=name id=parse_path>
225socket.url.<b>parse_path(</b>path<b>)</b> 257url.<b>parse_path(</b>path<b>)</b>
226</p> 258</p>
227 259
228<p class=description> 260<p class=description>
@@ -241,36 +273,10 @@ returning a list with all the parsed segments, the function unescapes all
241of them. 273of them.
242</p> 274</p>
243 275
244<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
245
246<p class=name id="escape">
247socket.url.<b>escape(</b>content<b>)</b>
248</p>
249
250<p class=description>
251Applies the URL escaping content coding to a string
252Each byte is encoded as a percent character followed
253by the two byte hexadecimal representation of its integer
254value.
255</p>
256
257<p class=parameters>
258<tt>Content</tt> is the string to be encoded.
259</p>
260
261<p class=result>
262The function returns the encoded string.
263</p>
264
265<pre class=example>
266code = socket.url.escape("/#?;")
267-- code = "%2f%23%3f%3b"
268</pre>
269
270<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 276<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
271 277
272<p class=name id="unescape"> 278<p class=name id="unescape">
273socket.url.<b>unescape(</b>content<b>)</b> 279url.<b>unescape(</b>content<b>)</b>
274</p> 280</p>
275 281
276<p class=description> 282<p class=description>
diff --git a/etc/check-memory.lua b/etc/check-memory.lua
new file mode 100644
index 0000000..fdc6b9b
--- /dev/null
+++ b/etc/check-memory.lua
@@ -0,0 +1,17 @@
1function load(s)
2 collectgarbage()
3 local a = gcinfo()
4 _G[s] = require(s)
5 collectgarbage()
6 local b = gcinfo()
7 print(s .. ":\t " .. (b-a) .. "k")
8end
9
10load("socket")
11load("url")
12load("ltn12")
13load("mime")
14load("tp")
15load("smtp")
16load("http")
17load("ftp")
diff --git a/etc/get.lua b/etc/get.lua
index c1e0542..0603ce5 100644
--- a/etc/get.lua
+++ b/etc/get.lua
@@ -8,6 +8,7 @@ socket = require("socket")
8http = require("http") 8http = require("http")
9ftp = require("ftp") 9ftp = require("ftp")
10url = require("url") 10url = require("url")
11ltn12 = require("ltn12")
11 12
12-- formats a number of seconds into human readable form 13-- formats a number of seconds into human readable form
13function nicetime(s) 14function nicetime(s)
diff --git a/samples/tinyirc.lua b/samples/tinyirc.lua
index eac979d..85ebe29 100644
--- a/samples/tinyirc.lua
+++ b/samples/tinyirc.lua
@@ -4,7 +4,7 @@
4-- Author: Diego Nehab 4-- Author: Diego Nehab
5-- RCS ID: $Id$ 5-- RCS ID: $Id$
6----------------------------------------------------------------------------- 6-----------------------------------------------------------------------------
7require("socket") 7socket = require("socket")
8host = host or "*" 8host = host or "*"
9port1 = port1 or 8080 9port1 = port1 or 8080
10port2 = port2 or 8181 10port2 = port2 or 8181
diff --git a/src/auxiliar.c b/src/auxiliar.c
index b1f9203..9a37e10 100644
--- a/src/auxiliar.c
+++ b/src/auxiliar.c
@@ -7,7 +7,6 @@
7#include <string.h> 7#include <string.h>
8#include <stdio.h> 8#include <stdio.h>
9 9
10#include "luasocket.h"
11#include "auxiliar.h" 10#include "auxiliar.h"
12 11
13/*=========================================================================*\ 12/*=========================================================================*\
@@ -16,16 +15,15 @@
16/*-------------------------------------------------------------------------*\ 15/*-------------------------------------------------------------------------*\
17* Initializes the module 16* Initializes the module
18\*-------------------------------------------------------------------------*/ 17\*-------------------------------------------------------------------------*/
19int aux_open(lua_State *L) 18int aux_open(lua_State *L) {
20{
21 return 0; 19 return 0;
22} 20}
23 21
24/*-------------------------------------------------------------------------*\ 22/*-------------------------------------------------------------------------*\
25* Creates a new class with given methods 23* Creates a new class with given methods
24* Methods whose names start with __ are passed directly to the metatable.
26\*-------------------------------------------------------------------------*/ 25\*-------------------------------------------------------------------------*/
27void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) 26void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) {
28{
29 luaL_newmetatable(L, classname); /* mt */ 27 luaL_newmetatable(L, classname); /* mt */
30 /* create __index table to place methods */ 28 /* create __index table to place methods */
31 lua_pushstring(L, "__index"); /* mt,"__index" */ 29 lua_pushstring(L, "__index"); /* mt,"__index" */
@@ -46,10 +44,30 @@ void aux_newclass(lua_State *L, const char *classname, luaL_reg *func)
46} 44}
47 45
48/*-------------------------------------------------------------------------*\ 46/*-------------------------------------------------------------------------*\
47* Prints the value of a class in a nice way
48\*-------------------------------------------------------------------------*/
49int aux_tostring(lua_State *L) {
50 char buf[32];
51 if (!lua_getmetatable(L, 1)) goto error;
52 lua_pushstring(L, "__index");
53 lua_gettable(L, -2);
54 if (!lua_istable(L, -1)) goto error;
55 lua_pushstring(L, "class");
56 lua_gettable(L, -2);
57 if (!lua_isstring(L, -1)) goto error;
58 sprintf(buf, "%p", lua_touserdata(L, 1));
59 lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf);
60 return 1;
61error:
62 lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'");
63 lua_error(L);
64 return 1;
65}
66
67/*-------------------------------------------------------------------------*\
49* Insert class into group 68* Insert class into group
50\*-------------------------------------------------------------------------*/ 69\*-------------------------------------------------------------------------*/
51void aux_add2group(lua_State *L, const char *classname, const char *groupname) 70void aux_add2group(lua_State *L, const char *classname, const char *groupname) {
52{
53 luaL_getmetatable(L, classname); 71 luaL_getmetatable(L, classname);
54 lua_pushstring(L, groupname); 72 lua_pushstring(L, groupname);
55 lua_pushboolean(L, 1); 73 lua_pushboolean(L, 1);
@@ -60,8 +78,7 @@ void aux_add2group(lua_State *L, const char *classname, const char *groupname)
60/*-------------------------------------------------------------------------*\ 78/*-------------------------------------------------------------------------*\
61* Make sure argument is a boolean 79* Make sure argument is a boolean
62\*-------------------------------------------------------------------------*/ 80\*-------------------------------------------------------------------------*/
63int aux_checkboolean(lua_State *L, int objidx) 81int aux_checkboolean(lua_State *L, int objidx) {
64{
65 if (!lua_isboolean(L, objidx)) 82 if (!lua_isboolean(L, objidx))
66 luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); 83 luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN));
67 return lua_toboolean(L, objidx); 84 return lua_toboolean(L, objidx);
@@ -71,8 +88,7 @@ int aux_checkboolean(lua_State *L, int objidx)
71* Return userdata pointer if object belongs to a given class, abort with 88* Return userdata pointer if object belongs to a given class, abort with
72* error otherwise 89* error otherwise
73\*-------------------------------------------------------------------------*/ 90\*-------------------------------------------------------------------------*/
74void *aux_checkclass(lua_State *L, const char *classname, int objidx) 91void *aux_checkclass(lua_State *L, const char *classname, int objidx) {
75{
76 void *data = aux_getclassudata(L, classname, objidx); 92 void *data = aux_getclassudata(L, classname, objidx);
77 if (!data) { 93 if (!data) {
78 char msg[45]; 94 char msg[45];
@@ -86,8 +102,7 @@ void *aux_checkclass(lua_State *L, const char *classname, int objidx)
86* Return userdata pointer if object belongs to a given group, abort with 102* Return userdata pointer if object belongs to a given group, abort with
87* error otherwise 103* error otherwise
88\*-------------------------------------------------------------------------*/ 104\*-------------------------------------------------------------------------*/
89void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) 105void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) {
90{
91 void *data = aux_getgroupudata(L, groupname, objidx); 106 void *data = aux_getgroupudata(L, groupname, objidx);
92 if (!data) { 107 if (!data) {
93 char msg[45]; 108 char msg[45];
@@ -100,8 +115,7 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx)
100/*-------------------------------------------------------------------------*\ 115/*-------------------------------------------------------------------------*\
101* Set object class 116* Set object class
102\*-------------------------------------------------------------------------*/ 117\*-------------------------------------------------------------------------*/
103void aux_setclass(lua_State *L, const char *classname, int objidx) 118void aux_setclass(lua_State *L, const char *classname, int objidx) {
104{
105 luaL_getmetatable(L, classname); 119 luaL_getmetatable(L, classname);
106 if (objidx < 0) objidx--; 120 if (objidx < 0) objidx--;
107 lua_setmetatable(L, objidx); 121 lua_setmetatable(L, objidx);
@@ -111,8 +125,7 @@ void aux_setclass(lua_State *L, const char *classname, int objidx)
111* Get a userdata pointer if object belongs to a given group. Return NULL 125* Get a userdata pointer if object belongs to a given group. Return NULL
112* otherwise 126* otherwise
113\*-------------------------------------------------------------------------*/ 127\*-------------------------------------------------------------------------*/
114void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) 128void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) {
115{
116 if (!lua_getmetatable(L, objidx)) 129 if (!lua_getmetatable(L, objidx))
117 return NULL; 130 return NULL;
118 lua_pushstring(L, groupname); 131 lua_pushstring(L, groupname);
@@ -130,7 +143,6 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx)
130* Get a userdata pointer if object belongs to a given class. Return NULL 143* Get a userdata pointer if object belongs to a given class. Return NULL
131* otherwise 144* otherwise
132\*-------------------------------------------------------------------------*/ 145\*-------------------------------------------------------------------------*/
133void *aux_getclassudata(lua_State *L, const char *classname, int objidx) 146void *aux_getclassudata(lua_State *L, const char *classname, int objidx) {
134{
135 return luaL_checkudata(L, objidx, classname); 147 return luaL_checkudata(L, objidx, classname);
136} 148}
diff --git a/src/auxiliar.h b/src/auxiliar.h
index bc45182..70f4704 100644
--- a/src/auxiliar.h
+++ b/src/auxiliar.h
@@ -2,26 +2,28 @@
2#define AUX_H 2#define AUX_H
3/*=========================================================================*\ 3/*=========================================================================*\
4* Auxiliar routines for class hierarchy manipulation 4* Auxiliar routines for class hierarchy manipulation
5* LuaSocket toolkit 5* LuaSocket toolkit (but completely independent of other LuaSocket modules)
6* 6*
7* A LuaSocket class is a name associated with Lua metatables. A LuaSocket 7* A LuaSocket class is a name associated with Lua metatables. A LuaSocket
8* group is a name associated to a class. A class can belong to any number 8* group is a name associated with a class. A class can belong to any number
9* of groups. This module provides the functionality to: 9* of groups. This module provides the functionality to:
10* 10*
11* - create new classes 11* - create new classes
12* - add classes to groups 12* - add classes to groups
13* - set the class of object 13* - set the class of objects
14* - check if an object belongs to a given class or group 14* - check if an object belongs to a given class or group
15* - get the userdata associated to objects
16* - print objects in a pretty way
15* 17*
16* LuaSocket class names follow the convention <module>{<class>}. Modules 18* LuaSocket class names follow the convention <module>{<class>}. Modules
17* can define any number of classes and groups. The module tcp.c, for 19* can define any number of classes and groups. The module tcp.c, for
18* example, defines the classes tcp{master}, tcp{client} and tcp{server} and 20* example, defines the classes tcp{master}, tcp{client} and tcp{server} and
19* the groups tcp{client, server} and tcp{any}. Module functions can then 21* the groups tcp{client,server} and tcp{any}. Module functions can then
20* perform type-checking on it's arguments by either class or group. 22* perform type-checking on their arguments by either class or group.
21* 23*
22* LuaSocket metatables define the __index metamethod as being a table. This 24* LuaSocket metatables define the __index metamethod as being a table. This
23* table has one field for each method supported by the class. In DEBUG 25* table has one field for each method supported by the class, and a field
24* mode, it also has one field with the class name. 26* "class" with the class name.
25* 27*
26* The mapping from class name to the corresponding metatable and the 28* The mapping from class name to the corresponding metatable and the
27* reverse mapping are done using lauxlib. 29* reverse mapping are done using lauxlib.
@@ -32,14 +34,6 @@
32#include <lua.h> 34#include <lua.h>
33#include <lauxlib.h> 35#include <lauxlib.h>
34 36
35/* min and max macros */
36#ifndef MIN
37#define MIN(x, y) ((x) < (y) ? x : y)
38#endif
39#ifndef MAX
40#define MAX(x, y) ((x) > (y) ? x : y)
41#endif
42
43int aux_open(lua_State *L); 37int aux_open(lua_State *L);
44void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); 38void aux_newclass(lua_State *L, const char *classname, luaL_reg *func);
45void aux_add2group(lua_State *L, const char *classname, const char *group); 39void 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);
49void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); 43void *aux_getclassudata(lua_State *L, const char *groupname, int objidx);
50void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); 44void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx);
51int aux_checkboolean(lua_State *L, int objidx); 45int aux_checkboolean(lua_State *L, int objidx);
46int aux_tostring(lua_State *L);
52 47
53#endif /* AUX_H */ 48#endif /* AUX_H */
diff --git a/src/buffer.c b/src/buffer.c
index b771047..fd885a2 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -7,7 +7,6 @@
7#include <lua.h> 7#include <lua.h>
8#include <lauxlib.h> 8#include <lauxlib.h>
9 9
10#include "auxiliar.h"
11#include "buffer.h" 10#include "buffer.h"
12 11
13/*=========================================================================*\ 12/*=========================================================================*\
@@ -20,6 +19,14 @@ static int buf_get(p_buf buf, const char **data, size_t *count);
20static void buf_skip(p_buf buf, size_t count); 19static void buf_skip(p_buf buf, size_t count);
21static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent); 20static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent);
22 21
22/* min and max macros */
23#ifndef MIN
24#define MIN(x, y) ((x) < (y) ? x : y)
25#endif
26#ifndef MAX
27#define MAX(x, y) ((x) > (y) ? x : y)
28#endif
29
23/*=========================================================================*\ 30/*=========================================================================*\
24* Exported functions 31* Exported functions
25\*=========================================================================*/ 32\*=========================================================================*/
diff --git a/src/except.c b/src/except.c
new file mode 100644
index 0000000..c9eb20e
--- /dev/null
+++ b/src/except.c
@@ -0,0 +1,52 @@
1#include <lauxlib.h>
2#include <stdio.h>
3
4#include "except.h"
5
6static int global_try(lua_State *L);
7static int global_protect(lua_State *L);
8static int protected(lua_State *L);
9
10static luaL_reg func[] = {
11 {"try", global_try},
12 {"protect", global_protect},
13 {NULL, NULL}
14};
15
16/*-------------------------------------------------------------------------*\
17* Exception handling: try method
18\*-------------------------------------------------------------------------*/
19static int global_try(lua_State *L) {
20 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
21 lua_settop(L, 2);
22 lua_error(L);
23 return 0;
24 } else return lua_gettop(L);
25}
26
27/*-------------------------------------------------------------------------*\
28* Exception handling: protect factory
29\*-------------------------------------------------------------------------*/
30static int protected(lua_State *L) {
31 lua_pushvalue(L, lua_upvalueindex(1));
32 lua_insert(L, 1);
33 if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) {
34 lua_pushnil(L);
35 lua_insert(L, 1);
36 return 2;
37 } else return lua_gettop(L);
38}
39
40static int global_protect(lua_State *L) {
41 lua_insert(L, 1);
42 lua_pushcclosure(L, protected, 1);
43 return 1;
44}
45
46/*-------------------------------------------------------------------------*\
47* Init module
48\*-------------------------------------------------------------------------*/
49int except_open(lua_State *L) {
50 luaL_openlib(L, NULL, func, 0);
51 return 0;
52}
diff --git a/src/except.h b/src/except.h
new file mode 100644
index 0000000..2c57b27
--- /dev/null
+++ b/src/except.h
@@ -0,0 +1,35 @@
1#ifndef EXCEPT_H
2#define EXCEPT_H
3/*=========================================================================*\
4* Exception control
5* LuaSocket toolkit (but completely independent from other modules)
6*
7* This provides support for simple exceptions in Lua. During the
8* development of the HTTP/FTP/SMTP support, it became aparent that
9* error checking was taking a substantial amount of the coding. These
10* function greatly simplify the task of checking errors.
11*
12* The main idea is that functions should return nil as its first return
13* value when it finds an error, and return an error message (or value)
14* following nil. In case of success, as long as the first value is not nil,
15* the other values don't matter.
16*
17* The idea is to nest function calls with the "try" function. This function
18* checks the first value, and calls "error" on the second if the first is
19* nil. Otherwise, it returns all values it received.
20*
21* The protect function returns a new function that behaves exactly like the
22* function it receives, but the new function doesn't throw exceptions: it
23* returns nil followed by the error message instead.
24*
25* With these two function, it's easy to write functions that throw
26* exceptions on error, but that don't interrupt the user script.
27*
28* RCS ID: $Id$
29\*=========================================================================*/
30
31#include <lua.h>
32
33int except_open(lua_State *L);
34
35#endif
diff --git a/src/ftp.lua b/src/ftp.lua
index 79772f8..c130d1a 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -7,7 +7,7 @@
7----------------------------------------------------------------------------- 7-----------------------------------------------------------------------------
8 8
9----------------------------------------------------------------------------- 9-----------------------------------------------------------------------------
10-- Load other required modules 10-- Load required modules
11----------------------------------------------------------------------------- 11-----------------------------------------------------------------------------
12local socket = require("socket") 12local socket = require("socket")
13local ltn12 = require("ltn12") 13local ltn12 = require("ltn12")
@@ -17,10 +17,7 @@ local tp = require("tp")
17----------------------------------------------------------------------------- 17-----------------------------------------------------------------------------
18-- Setup namespace 18-- Setup namespace
19----------------------------------------------------------------------------- 19-----------------------------------------------------------------------------
20local ftp = {} 20_LOADED["ftp"] = getfenv(1)
21-- make all module globals fall into namespace
22setmetatable(ftp, { __index = _G })
23setfenv(1, ftp)
24 21
25----------------------------------------------------------------------------- 22-----------------------------------------------------------------------------
26-- Program constants 23-- Program constants
@@ -32,9 +29,7 @@ PORT = 21
32-- this is the default anonymous password. used when no password is 29-- this is the default anonymous password. used when no password is
33-- provided in url. should be changed to your e-mail. 30-- provided in url. should be changed to your e-mail.
34USER = "ftp" 31USER = "ftp"
35EMAIL = "anonymous@anonymous.org" 32PASSWORD = "anonymous@anonymous.org"
36-- block size used in transfers
37BLOCKSIZE = 2048
38 33
39----------------------------------------------------------------------------- 34-----------------------------------------------------------------------------
40-- Low level FTP API 35-- Low level FTP API
@@ -42,7 +37,7 @@ BLOCKSIZE = 2048
42local metat = { __index = {} } 37local metat = { __index = {} }
43 38
44function open(server, port) 39function open(server, port)
45 local tp = socket.try(socket.tp.connect(server, port or PORT)) 40 local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT))
46 return setmetatable({tp = tp}, metat) 41 return setmetatable({tp = tp}, metat)
47end 42end
48 43
@@ -51,14 +46,17 @@ local function port(portt)
51end 46end
52 47
53local function pasv(pasvt) 48local function pasv(pasvt)
54 return socket.connect(pasvt.ip, pasvt.port) 49 local data = socket.try(socket.tcp())
50 socket.try(data:settimeout(TIMEOUT))
51 socket.try(data:connect(pasvt.ip, pasvt.port))
52 return data
55end 53end
56 54
57function metat.__index:login(user, password) 55function metat.__index:login(user, password)
58 socket.try(self.tp:command("user", user or USER)) 56 socket.try(self.tp:command("user", user or USER))
59 local code, reply = socket.try(self.tp:check{"2..", 331}) 57 local code, reply = socket.try(self.tp:check{"2..", 331})
60 if code == 331 then 58 if code == 331 then
61 socket.try(self.tp:command("pass", password or EMAIL)) 59 socket.try(self.tp:command("pass", password or PASSWORD))
62 socket.try(self.tp:check("2..")) 60 socket.try(self.tp:check("2.."))
63 end 61 end
64 return 1 62 return 1
@@ -104,6 +102,7 @@ function metat.__index:send(sendt)
104 socket.try(self.pasvt or self.portt, "need port or pasv first") 102 socket.try(self.pasvt or self.portt, "need port or pasv first")
105 if self.pasvt then data = socket.try(pasv(self.pasvt)) end 103 if self.pasvt then data = socket.try(pasv(self.pasvt)) end
106 local argument = sendt.argument or string.gsub(sendt.path, "^/", "") 104 local argument = sendt.argument or string.gsub(sendt.path, "^/", "")
105 if argument == "" then argument = nil end
107 local command = sendt.command or "stor" 106 local command = sendt.command or "stor"
108 socket.try(self.tp:command(command, argument)) 107 socket.try(self.tp:command(command, argument))
109 local code, reply = socket.try(self.tp:check{"2..", "1.."}) 108 local code, reply = socket.try(self.tp:check{"2..", "1.."})
@@ -133,6 +132,7 @@ function metat.__index:receive(recvt)
133 socket.try(self.pasvt or self.portt, "need port or pasv first") 132 socket.try(self.pasvt or self.portt, "need port or pasv first")
134 if self.pasvt then data = socket.try(pasv(self.pasvt)) end 133 if self.pasvt then data = socket.try(pasv(self.pasvt)) end
135 local argument = recvt.argument or string.gsub(recvt.path, "^/", "") 134 local argument = recvt.argument or string.gsub(recvt.path, "^/", "")
135 if argument == "" then argument = nil end
136 local command = recvt.command or "retr" 136 local command = recvt.command or "retr"
137 socket.try(self.tp:command(command, argument)) 137 socket.try(self.tp:command(command, argument))
138 local code = socket.try(self.tp:check{"1..", "2.."}) 138 local code = socket.try(self.tp:check{"1..", "2.."})
@@ -182,14 +182,14 @@ end
182-- High level FTP API 182-- High level FTP API
183----------------------------------------------------------------------------- 183-----------------------------------------------------------------------------
184local function tput(putt) 184local function tput(putt)
185 local ftp = socket.ftp.open(putt.host, putt.port) 185 local con = ftp.open(putt.host, putt.port)
186 ftp:greet() 186 con:greet()
187 ftp:login(putt.user, putt.password) 187 con:login(putt.user, putt.password)
188 if putt.type then ftp:type(putt.type) end 188 if putt.type then con:type(putt.type) end
189 ftp:pasv() 189 con:pasv()
190 ftp:send(putt) 190 con:send(putt)
191 ftp:quit() 191 con:quit()
192 return ftp:close() 192 return con:close()
193end 193end
194 194
195local default = { 195local default = {
@@ -198,15 +198,16 @@ local default = {
198} 198}
199 199
200local function parse(u) 200local function parse(u)
201 local putt = socket.try(url.parse(u, default)) 201 local t = socket.try(url.parse(u, default))
202 socket.try(putt.scheme == "ftp", "invalid scheme '" .. putt.scheme .. "'") 202 socket.try(t.scheme == "ftp", "invalid scheme '" .. t.scheme .. "'")
203 socket.try(putt.host, "invalid host") 203 socket.try(t.host, "invalid host")
204 local pat = "^type=(.)$" 204 local pat = "^type=(.)$"
205 if putt.params then 205 if t.params then
206 putt.type = socket.skip(2, string.find(putt.params, pat)) 206 t.type = socket.skip(2, string.find(t.params, pat))
207 socket.try(putt.type == "a" or putt.type == "i") 207 socket.try(t.type == "a" or t.type == "i",
208 "invalid type '" .. t.type .. "'")
208 end 209 end
209 return putt 210 return t
210end 211end
211 212
212local function sput(u, body) 213local function sput(u, body)
@@ -221,17 +222,17 @@ put = socket.protect(function(putt, body)
221end) 222end)
222 223
223local function tget(gett) 224local function tget(gett)
224 local ftp = socket.ftp.open(gett.host, gett.port) 225 local con = ftp.open(gett.host, gett.port)
225 ftp:greet() 226 con:greet()
226 ftp:login(gett.user, gett.password) 227 con:login(gett.user, gett.password)
227 if gett.type then ftp:type(gett.type) end 228 if gett.type then con:type(gett.type) end
228 ftp:pasv() 229 con:pasv()
229 ftp:receive(gett) 230 con:receive(gett)
230 ftp:quit() 231 con:quit()
231 return ftp:close() 232 return con:close()
232end 233end
233 234
234local function sget(u, body) 235local function sget(u)
235 local gett = parse(u) 236 local gett = parse(u)
236 local t = {} 237 local t = {}
237 gett.sink = ltn12.sink.table(t) 238 gett.sink = ltn12.sink.table(t)
@@ -240,7 +241,7 @@ local function sget(u, body)
240end 241end
241 242
242get = socket.protect(function(gett) 243get = socket.protect(function(gett)
243 if type(gett) == "string" then return sget(gett, body) 244 if type(gett) == "string" then return sget(gett)
244 else return tget(gett) end 245 else return tget(gett) end
245end) 246end)
246 247
diff --git a/src/http.lua b/src/http.lua
index ebe6b54..129b562 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -7,7 +7,7 @@
7----------------------------------------------------------------------------- 7-----------------------------------------------------------------------------
8 8
9----------------------------------------------------------------------------- 9-----------------------------------------------------------------------------
10-- Load other required modules 10-- Load required modules
11------------------------------------------------------------------------------- 11-------------------------------------------------------------------------------
12local socket = require("socket") 12local socket = require("socket")
13local ltn12 = require("ltn12") 13local ltn12 = require("ltn12")
@@ -17,42 +17,68 @@ local url = require("url")
17----------------------------------------------------------------------------- 17-----------------------------------------------------------------------------
18-- Setup namespace 18-- Setup namespace
19------------------------------------------------------------------------------- 19-------------------------------------------------------------------------------
20http = {} 20_LOADED["http"] = getfenv(1)
21-- make all module globals fall into namespace
22setmetatable(http, { __index = _G })
23setfenv(1, http)
24 21
25----------------------------------------------------------------------------- 22-----------------------------------------------------------------------------
26-- Program constants 23-- Program constants
27----------------------------------------------------------------------------- 24-----------------------------------------------------------------------------
28-- connection timeout in seconds 25-- connection timeout in seconds
29TIMEOUT = 60 26TIMEOUT = 4
30-- default port for document retrieval 27-- default port for document retrieval
31PORT = 80 28PORT = 80
32-- user agent field sent in request 29-- user agent field sent in request
33USERAGENT = socket.version 30USERAGENT = socket.VERSION
34-- block size used in transfers 31-- block size used in transfers
35BLOCKSIZE = 2048 32BLOCKSIZE = 2048
36 33
37----------------------------------------------------------------------------- 34-----------------------------------------------------------------------------
38-- Function return value selectors 35-- Low level HTTP API
39----------------------------------------------------------------------------- 36-----------------------------------------------------------------------------
40local function second(a, b) 37local metat = { __index = {} }
41 return b 38
39function open(host, port)
40 local con = socket.try(socket.tcp())
41 socket.try(con:settimeout(TIMEOUT))
42 port = port or PORT
43 socket.try(con:connect(host, port))
44 return setmetatable({ con = con }, metat)
45end
46
47function metat.__index:sendrequestline(method, uri)
48 local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
49 return socket.try(self.con:send(reqline))
42end 50end
43 51
44local function third(a, b, c) 52function metat.__index:sendheaders(headers)
45 return c 53 for i, v in pairs(headers) do
54 socket.try(self.con:send(i .. ": " .. v .. "\r\n"))
55 end
56 -- mark end of request headers
57 socket.try(self.con:send("\r\n"))
58 return 1
46end 59end
47 60
48local function receive_headers(reqt, respt, tmp) 61function metat.__index:sendbody(headers, source, step)
49 local sock = tmp.sock 62 source = source or ltn12.source.empty()
63 step = step or ltn12.pump.step
64 -- if we don't know the size in advance, send chunked and hope for the best
65 local mode
66 if headers["content-length"] then mode = "keep-open"
67 else mode = "http-chunked" end
68 return socket.try(ltn12.pump.all(source, socket.sink(mode, self.con), step))
69end
70
71function metat.__index:receivestatusline()
72 local status = socket.try(self.con:receive())
73 local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
74 return socket.try(tonumber(code), status)
75end
76
77function metat.__index:receiveheaders()
50 local line, name, value 78 local line, name, value
51 local headers = {} 79 local headers = {}
52 -- store results
53 respt.headers = headers
54 -- get first line 80 -- get first line
55 line = socket.try(sock:receive()) 81 line = socket.try(self.con:receive())
56 -- headers go until a blank line is found 82 -- headers go until a blank line is found
57 while line ~= "" do 83 while line ~= "" do
58 -- get field-name and value 84 -- get field-name and value
@@ -60,189 +86,137 @@ local function receive_headers(reqt, respt, tmp)
60 socket.try(name and value, "malformed reponse headers") 86 socket.try(name and value, "malformed reponse headers")
61 name = string.lower(name) 87 name = string.lower(name)
62 -- get next line (value might be folded) 88 -- get next line (value might be folded)
63 line = socket.try(sock:receive()) 89 line = socket.try(self.con:receive())
64 -- unfold any folded values 90 -- unfold any folded values
65 while string.find(line, "^%s") do 91 while string.find(line, "^%s") do
66 value = value .. line 92 value = value .. line
67 line = socket.try(sock:receive()) 93 line = socket.try(self.con:receive())
68 end 94 end
69 -- save pair in table 95 -- save pair in table
70 if headers[name] then headers[name] = headers[name] .. ", " .. value 96 if headers[name] then headers[name] = headers[name] .. ", " .. value
71 else headers[name] = value end 97 else headers[name] = value end
72 end 98 end
99 return headers
73end 100end
74 101
75local function receive_body(reqt, respt, tmp) 102function metat.__index:receivebody(headers, sink, step)
76 local sink = reqt.sink or ltn12.sink.null() 103 sink = sink or ltn12.sink.null()
77 local step = reqt.step or ltn12.pump.step 104 step = step or ltn12.pump.step
78 local source 105 local length = tonumber(headers["content-length"])
79 local te = respt.headers["transfer-encoding"] 106 local TE = headers["transfer-encoding"]
80 if te and te ~= "identity" then 107 local mode
81 -- get by chunked transfer-coding of message body 108 if TE and TE ~= "identity" then mode = "http-chunked"
82 source = socket.source("http-chunked", tmp.sock) 109 elseif tonumber(headers["content-length"]) then mode = "by-length"
83 elseif tonumber(respt.headers["content-length"]) then 110 else mode = "default" end
84 -- get by content-length 111 return socket.try(ltn12.pump.all(socket.source(mode, self.con, length),
85 local length = tonumber(respt.headers["content-length"]) 112 sink, step))
86 source = socket.source("by-length", tmp.sock, length)
87 else
88 -- get it all until connection closes
89 source = socket.source(tmp.sock)
90 end
91 socket.try(ltn12.pump.all(source, sink, step))
92end 113end
93 114
94local function send_headers(sock, headers) 115function metat.__index:close()
95 -- send request headers 116 return self.con:close()
96 for i, v in pairs(headers) do
97 socket.try(sock:send(i .. ": " .. v .. "\r\n"))
98 end
99 -- mark end of request headers
100 socket.try(sock:send("\r\n"))
101end 117end
102 118
103local function should_receive_body(reqt, respt, tmp) 119-----------------------------------------------------------------------------
104 if reqt.method == "HEAD" then return nil end 120-- High level HTTP API
105 if respt.code == 204 or respt.code == 304 then return nil end 121-----------------------------------------------------------------------------
106 if respt.code >= 100 and respt.code < 200 then return nil end 122local function uri(reqt)
107 return 1 123 local u = reqt
108end 124 if not reqt.proxy and not PROXY then
109 125 u = {
110local function receive_status(reqt, respt, tmp) 126 path = reqt.path,
111 local status = socket.try(tmp.sock:receive()) 127 params = reqt.params,
112 local code = third(string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) 128 query = reqt.query,
113 -- store results 129 fragment = reqt.fragment
114 respt.code, respt.status = tonumber(code), status
115end
116
117local function request_uri(reqt, respt, tmp)
118 local u = tmp.parsed
119 if not reqt.proxy then
120 local parsed = tmp.parsed
121 u = {
122 path = parsed.path,
123 params = parsed.params,
124 query = parsed.query,
125 fragment = parsed.fragment
126 } 130 }
127 end 131 end
128 return url.build(u) 132 return url.build(u)
129end 133end
130 134
131local function send_request(reqt, respt, tmp) 135local function adjustheaders(headers, host)
132 local uri = request_uri(reqt, respt, tmp)
133 local headers = tmp.headers
134 local step = reqt.step or ltn12.pump.step
135 -- send request line
136 socket.try(tmp.sock:send((reqt.method or "GET")
137 .. " " .. uri .. " HTTP/1.1\r\n"))
138 if reqt.source and not headers["content-length"] then
139 headers["transfer-encoding"] = "chunked"
140 end
141 send_headers(tmp.sock, headers)
142 -- send request message body, if any
143 if not reqt.source then return end
144 if headers["content-length"] then
145 socket.try(ltn12.pump.all(reqt.source,
146 socket.sink(tmp.sock), step))
147 else
148 socket.try(ltn12.pump.all(reqt.source,
149 socket.sink("http-chunked", tmp.sock), step))
150 end
151end
152
153local function open(reqt, respt, tmp)
154 local proxy = reqt.proxy or PROXY
155 local host, port
156 if proxy then
157 local pproxy = url.parse(proxy)
158 socket.try(pproxy.port and pproxy.host, "invalid proxy")
159 host, port = pproxy.host, pproxy.port
160 else
161 host, port = tmp.parsed.host, tmp.parsed.port
162 end
163 -- store results
164 tmp.sock = socket.try(socket.tcp())
165 socket.try(tmp.sock:settimeout(reqt.timeout or TIMEOUT))
166 socket.try(tmp.sock:connect(host, port))
167end
168
169local function adjust_headers(reqt, respt, tmp)
170 local lower = {} 136 local lower = {}
171 -- override with user values 137 -- override with user values
172 for i,v in (reqt.headers or lower) do 138 for i,v in (headers or lower) do
173 lower[string.lower(i)] = v 139 lower[string.lower(i)] = v
174 end 140 end
175 lower["user-agent"] = lower["user-agent"] or USERAGENT 141 lower["user-agent"] = lower["user-agent"] or USERAGENT
176 -- these cannot be overriden 142 -- these cannot be overriden
177 lower["host"] = tmp.parsed.host 143 lower["host"] = host
178 lower["connection"] = "close" 144 return lower
179 -- store results
180 tmp.headers = lower
181end 145end
182 146
183local function parse_url(reqt, respt, tmp) 147local function adjustrequest(reqt)
184 -- parse url with default fields 148 -- parse url with default fields
185 local parsed = url.parse(reqt.url, { 149 local parsed = url.parse(reqt.url, {
186 host = "", 150 host = "",
187 port = PORT, 151 port = PORT,
188 path ="/", 152 path ="/",
189 scheme = "http" 153 scheme = "http"
190 }) 154 })
191 -- scheme has to be http 155 -- explicit info in reqt overrides that given by the URL
192 socket.try(parsed.scheme == "http", 156 for i,v in reqt do parsed[i] = v end
193 string.format("unknown scheme '%s'", parsed.scheme)) 157 -- compute uri if user hasn't overriden
194 -- explicit authentication info overrides that given by the URL 158 parsed.uri = parsed.uri or uri(parsed)
195 parsed.user = reqt.user or parsed.user 159 -- adjust headers in request
196 parsed.password = reqt.password or parsed.password 160 parsed.headers = adjustheaders(parsed.headers, parsed.host)
197 -- store results 161 return parsed
198 tmp.parsed = parsed
199end 162end
200 163
201-- forward declaration 164local function shouldredirect(reqt, respt)
202local request_p 165 return (reqt.redirect ~= false) and
166 (respt.code == 301 or respt.code == 302) and
167 (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
168 and (not reqt.nredirects or reqt.nredirects < 5)
169end
203 170
204local function should_authorize(reqt, respt, tmp) 171local function shouldauthorize(reqt, respt)
205 -- if there has been an authorization attempt, it must have failed 172 -- if there has been an authorization attempt, it must have failed
206 if reqt.headers and reqt.headers["authorization"] then return nil end 173 if reqt.headers and reqt.headers["authorization"] then return nil end
207 -- if last attempt didn't fail due to lack of authentication, 174 -- if last attempt didn't fail due to lack of authentication,
208 -- or we don't have authorization information, we can't retry 175 -- or we don't have authorization information, we can't retry
209 return respt.code == 401 and tmp.parsed.user and tmp.parsed.password 176 return respt.code == 401 and reqt.user and reqt.password
210end 177end
211 178
212local function clone(headers) 179local function shouldreceivebody(reqt, respt)
213 if not headers then return nil end 180 if reqt.method == "HEAD" then return nil end
214 local copy = {} 181 local code = respt.code
215 for i,v in pairs(headers) do 182 if code == 204 or code == 304 then return nil end
216 copy[i] = v 183 if code >= 100 and code < 200 then return nil end
217 end 184 return 1
218 return copy
219end 185end
220 186
221local function authorize(reqt, respt, tmp) 187local requestp, authorizep, redirectp
222 local headers = clone(reqt.headers) or {} 188
223 headers["authorization"] = "Basic " .. 189function requestp(reqt)
224 (mime.b64(tmp.parsed.user .. ":" .. tmp.parsed.password)) 190 local reqt = adjustrequest(reqt)
225 local autht = { 191 local respt = {}
226 method = reqt.method, 192 local con = open(reqt.host, reqt.port)
227 url = reqt.url, 193 con:sendrequestline(reqt.method, reqt.uri)
228 source = reqt.source, 194 con:sendheaders(reqt.headers)
229 sink = reqt.sink, 195 con:sendbody(reqt.headers, reqt.source, reqt.step)
230 headers = headers, 196 respt.code, respt.status = con:receivestatusline()
231 timeout = reqt.timeout, 197 respt.headers = con:receiveheaders()
232 proxy = reqt.proxy, 198 if shouldredirect(reqt, respt) then
233 } 199 con:close()
234 request_p(autht, respt, tmp) 200 return redirectp(reqt, respt)
201 elseif shouldauthorize(reqt, respt) then
202 con:close()
203 return authorizep(reqt, respt)
204 elseif shouldreceivebody(reqt, respt) then
205 con:receivebody(respt.headers, reqt.sink, reqt.step)
206 end
207 con:close()
208 return respt
235end 209end
236 210
237local function should_redirect(reqt, respt, tmp) 211function authorizep(reqt, respt)
238 return (reqt.redirect ~= false) and 212 local auth = "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
239 (respt.code == 301 or respt.code == 302) and 213 reqt.headers["authorization"] = auth
240 (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") 214 return requestp(reqt)
241 and (not tmp.nredirects or tmp.nredirects < 5)
242end 215end
243 216
244local function redirect(reqt, respt, tmp) 217function redirectp(reqt, respt)
245 tmp.nredirects = (tmp.nredirects or 0) + 1 218 -- we create a new table to get rid of anything we don't
219 -- absolutely need, including authentication info
246 local redirt = { 220 local redirt = {
247 method = reqt.method, 221 method = reqt.method,
248 -- the RFC says the redirect URL has to be absolute, but some 222 -- the RFC says the redirect URL has to be absolute, but some
@@ -251,69 +225,38 @@ local function redirect(reqt, respt, tmp)
251 source = reqt.source, 225 source = reqt.source,
252 sink = reqt.sink, 226 sink = reqt.sink,
253 headers = reqt.headers, 227 headers = reqt.headers,
254 timeout = reqt.timeout, 228 proxy = reqt.proxy,
255 proxy = reqt.proxy 229 nredirects = (reqt.nredirects or 0) + 1
256 } 230 }
257 request_p(redirt, respt, tmp) 231 respt = requestp(redirt)
258 -- we pass the location header as a clue we redirected 232 -- we pass the location header as a clue we redirected
259 if respt.headers then respt.headers.location = redirt.url end 233 if respt.headers then respt.headers.location = redirt.url end
260end
261
262local function skip_continue(reqt, respt, tmp)
263 if respt.code == 100 then
264 receive_status(reqt, respt, tmp)
265 end
266end
267
268-- execute a request of through an exception
269function request_p(reqt, respt, tmp)
270 parse_url(reqt, respt, tmp)
271 adjust_headers(reqt, respt, tmp)
272 open(reqt, respt, tmp)
273 send_request(reqt, respt, tmp)
274 receive_status(reqt, respt, tmp)
275 skip_continue(reqt, respt, tmp)
276 receive_headers(reqt, respt, tmp)
277 if should_redirect(reqt, respt, tmp) then
278 tmp.sock:close()
279 redirect(reqt, respt, tmp)
280 elseif should_authorize(reqt, respt, tmp) then
281 tmp.sock:close()
282 authorize(reqt, respt, tmp)
283 elseif should_receive_body(reqt, respt, tmp) then
284 receive_body(reqt, respt, tmp)
285 end
286end
287
288function request(reqt)
289 local respt, tmp = {}, {}
290 local s, e = pcall(request_p, reqt, respt, tmp)
291 if not s then respt.error = e end
292 if tmp.sock then tmp.sock:close() end
293 return respt 234 return respt
294end 235end
295 236
296function get(u) 237request = socket.protect(requestp)
238
239get = socket.protect(function(u)
297 local t = {} 240 local t = {}
298 respt = request { 241 local respt = requestp {
299 url = u, 242 url = u,
300 sink = ltn12.sink.table(t) 243 sink = ltn12.sink.table(t)
301 } 244 }
302 return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers, 245 return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers,
303 respt.code, respt.error 246 respt.code
304end 247end)
305 248
306function post(u, body) 249post = socket.protect(function(u, body)
307 local t = {} 250 local t = {}
308 respt = request { 251 local respt = requestp {
309 url = u, 252 url = u,
310 method = "POST", 253 method = "POST",
311 source = ltn12.source.string(body), 254 source = ltn12.source.string(body),
312 sink = ltn12.sink.table(t), 255 sink = ltn12.sink.table(t),
313 headers = { ["content-length"] = string.len(body) } 256 headers = { ["content-length"] = string.len(body) }
314 } 257 }
315 return (table.getn(t) > 0 or nil) and table.concat(t), 258 return (table.getn(t) > 0 or nil) and table.concat(t),
316 respt.headers, respt.code, respt.error 259 respt.headers, respt.code
317end 260end)
318 261
319return http 262return http
diff --git a/src/inet.c b/src/inet.c
index 3a57441..62c67f1 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -10,7 +10,6 @@
10#include <lua.h> 10#include <lua.h>
11#include <lauxlib.h> 11#include <lauxlib.h>
12 12
13#include "luasocket.h"
14#include "inet.h" 13#include "inet.h"
15 14
16/*=========================================================================*\ 15/*=========================================================================*\
diff --git a/src/ltn12.lua b/src/ltn12.lua
index 41855f0..6228247 100644
--- a/src/ltn12.lua
+++ b/src/ltn12.lua
@@ -8,9 +8,8 @@
8----------------------------------------------------------------------------- 8-----------------------------------------------------------------------------
9-- Setup namespace 9-- Setup namespace
10----------------------------------------------------------------------------- 10-----------------------------------------------------------------------------
11local ltn12 = {} 11_LOADED["ltn12"] = getfenv(1)
12setmetatable(ltn12, { __index = _G }) 12
13setfenv(1, ltn12)
14filter = {} 13filter = {}
15source = {} 14source = {}
16sink = {} 15sink = {}
@@ -19,10 +18,6 @@ pump = {}
19-- 2048 seems to be better in windows... 18-- 2048 seems to be better in windows...
20BLOCKSIZE = 2048 19BLOCKSIZE = 2048
21 20
22local function shift(a, b, c)
23 return b, c
24end
25
26----------------------------------------------------------------------------- 21-----------------------------------------------------------------------------
27-- Filter stuff 22-- Filter stuff
28----------------------------------------------------------------------------- 23-----------------------------------------------------------------------------
@@ -53,7 +48,9 @@ local function chain2(f1, f2)
53 end 48 end
54 end) 49 end)
55 return function(chunk) 50 return function(chunk)
56 return shift(coroutine.resume(co, chunk)) 51 local ret, a, b = coroutine.resume(co, chunk)
52 if ret then return a, b
53 else return nil, a end
57 end 54 end
58end 55end
59 56
@@ -149,7 +146,9 @@ function source.chain(src, f)
149 end 146 end
150 end) 147 end)
151 return function() 148 return function()
152 return shift(coroutine.resume(co)) 149 local ret, a, b = coroutine.resume(co)
150 if ret then return a, b
151 else return nil, a end
153 end 152 end
154end 153end
155 154
@@ -166,7 +165,9 @@ function source.cat(...)
166 end 165 end
167 end) 166 end)
168 return function() 167 return function()
169 return shift(coroutine.resume(co)) 168 local ret, a, b = coroutine.resume(co)
169 if ret then return a, b
170 else return nil, a end
170 end 171 end
171end 172end
172 173
diff --git a/src/luasocket.c b/src/luasocket.c
index ca3a52c..2b0a1fa 100644
--- a/src/luasocket.c
+++ b/src/luasocket.c
@@ -26,7 +26,7 @@
26#include "luasocket.h" 26#include "luasocket.h"
27 27
28#include "auxiliar.h" 28#include "auxiliar.h"
29#include "base.h" 29#include "except.h"
30#include "timeout.h" 30#include "timeout.h"
31#include "buffer.h" 31#include "buffer.h"
32#include "inet.h" 32#include "inet.h"
@@ -35,11 +35,18 @@
35#include "select.h" 35#include "select.h"
36 36
37/*-------------------------------------------------------------------------*\ 37/*-------------------------------------------------------------------------*\
38* Modules 38* Internal function prototypes
39\*-------------------------------------------------------------------------*/
40static int global_skip(lua_State *L);
41static int global_unload(lua_State *L);
42static int base_open(lua_State *L);
43
44/*-------------------------------------------------------------------------*\
45* Modules and functions
39\*-------------------------------------------------------------------------*/ 46\*-------------------------------------------------------------------------*/
40static const luaL_reg mod[] = { 47static const luaL_reg mod[] = {
41 {"auxiliar", aux_open}, 48 {"auxiliar", aux_open},
42 {"base", base_open}, 49 {"except", except_open},
43 {"timeout", tm_open}, 50 {"timeout", tm_open},
44 {"buffer", buf_open}, 51 {"buffer", buf_open},
45 {"inet", inet_open}, 52 {"inet", inet_open},
@@ -49,11 +56,69 @@ static const luaL_reg mod[] = {
49 {NULL, NULL} 56 {NULL, NULL}
50}; 57};
51 58
59static luaL_reg func[] = {
60 {"skip", global_skip},
61 {"__unload", global_unload},
62 {NULL, NULL}
63};
64
65/*-------------------------------------------------------------------------*\
66* Skip a few arguments
67\*-------------------------------------------------------------------------*/
68static int global_skip(lua_State *L) {
69 int amount = luaL_checkint(L, 1);
70 int ret = lua_gettop(L) - amount - 1;
71 return ret >= 0 ? ret : 0;
72}
73
74/*-------------------------------------------------------------------------*\
75* Unloads the library
76\*-------------------------------------------------------------------------*/
77static int global_unload(lua_State *L) {
78 sock_close();
79 return 0;
80}
81
82/*-------------------------------------------------------------------------*\
83* Setup basic stuff.
84\*-------------------------------------------------------------------------*/
85static int base_open(lua_State *L) {
86 if (sock_open()) {
87 /* whoever is loading the library replaced the global environment
88 * with the namespace table */
89 lua_pushvalue(L, LUA_GLOBALSINDEX);
90 /* make sure library is still "requirable" if initialized staticaly */
91 lua_pushstring(L, "_LOADEDLIB");
92 lua_gettable(L, -2);
93 lua_pushstring(L, LUASOCKET_LIBNAME);
94 lua_pushcfunction(L, (lua_CFunction) luaopen_socket);
95 lua_settable(L, -3);
96 lua_pop(L, 1);
97#ifdef LUASOCKET_DEBUG
98 lua_pushstring(L, "DEBUG");
99 lua_pushboolean(L, 1);
100 lua_rawset(L, -3);
101#endif
102 /* make version string available to scripts */
103 lua_pushstring(L, "VERSION");
104 lua_pushstring(L, LUASOCKET_VERSION);
105 lua_rawset(L, -3);
106 /* export other functions */
107 luaL_openlib(L, NULL, func, 0);
108 return 1;
109 } else {
110 lua_pushstring(L, "unable to initialize library");
111 lua_error(L);
112 return 0;
113 }
114}
115
52/*-------------------------------------------------------------------------*\ 116/*-------------------------------------------------------------------------*\
53* Initializes all library modules. 117* Initializes all library modules.
54\*-------------------------------------------------------------------------*/ 118\*-------------------------------------------------------------------------*/
55LUASOCKET_API int luaopen_socket(lua_State *L) { 119LUASOCKET_API int luaopen_socket(lua_State *L) {
56 int i; 120 int i;
121 base_open(L);
57 for (i = 0; mod[i].name; i++) mod[i].func(L); 122 for (i = 0; mod[i].name; i++) mod[i].func(L);
58 return 1; 123 return 1;
59} 124}
diff --git a/src/luasocket.h b/src/luasocket.h
index 716b7ff..6d30605 100644
--- a/src/luasocket.h
+++ b/src/luasocket.h
@@ -25,6 +25,7 @@
25/*-------------------------------------------------------------------------*\ 25/*-------------------------------------------------------------------------*\
26* Initializes the library. 26* Initializes the library.
27\*-------------------------------------------------------------------------*/ 27\*-------------------------------------------------------------------------*/
28#define LUASOCKET_LIBNAME "socket"
28LUASOCKET_API int luaopen_socket(lua_State *L); 29LUASOCKET_API int luaopen_socket(lua_State *L);
29 30
30#endif /* LUASOCKET_H */ 31#endif /* LUASOCKET_H */
diff --git a/src/mime.c b/src/mime.c
index f42528c..5750714 100644
--- a/src/mime.c
+++ b/src/mime.c
@@ -76,7 +76,17 @@ static UC b64unbase[256];
76\*-------------------------------------------------------------------------*/ 76\*-------------------------------------------------------------------------*/
77MIME_API int luaopen_mime(lua_State *L) 77MIME_API int luaopen_mime(lua_State *L)
78{ 78{
79 lua_newtable(L); 79 /* whoever is loading the library replaced the global environment
80 * with the namespace table */
81 lua_pushvalue(L, LUA_GLOBALSINDEX);
82 /* make sure library is still "requirable" if initialized staticaly */
83 lua_pushstring(L, "_LOADEDLIB");
84 lua_gettable(L, -2);
85 lua_pushstring(L, MIME_LIBNAME);
86 lua_pushcfunction(L, (lua_CFunction) luaopen_mime);
87 lua_settable(L, -3);
88 lua_pop(L, 1);
89 /* export functions */
80 luaL_openlib(L, NULL, func, 0); 90 luaL_openlib(L, NULL, func, 0);
81 /* initialize lookup tables */ 91 /* initialize lookup tables */
82 qpsetup(qpclass, qpunbase); 92 qpsetup(qpclass, qpunbase);
diff --git a/src/mime.h b/src/mime.h
index 6febedf..b82d61a 100644
--- a/src/mime.h
+++ b/src/mime.h
@@ -19,6 +19,7 @@
19#define MIME_API extern 19#define MIME_API extern
20#endif 20#endif
21 21
22#define MIME_LIBNAME "mime"
22MIME_API int luaopen_mime(lua_State *L); 23MIME_API int luaopen_mime(lua_State *L);
23 24
24#endif /* MIME_H */ 25#endif /* MIME_H */
diff --git a/src/mime.lua b/src/mime.lua
index ecf310d..000404f 100644
--- a/src/mime.lua
+++ b/src/mime.lua
@@ -6,23 +6,15 @@
6----------------------------------------------------------------------------- 6-----------------------------------------------------------------------------
7 7
8----------------------------------------------------------------------------- 8-----------------------------------------------------------------------------
9-- Load MIME from dynamic library
10-- Comment these lines if you are loading static
11-----------------------------------------------------------------------------
12local open = assert(loadlib("mime", "luaopen_mime"))
13local mime = assert(open())
14
15-----------------------------------------------------------------------------
16-- Load other required modules 9-- Load other required modules
17----------------------------------------------------------------------------- 10-----------------------------------------------------------------------------
11local mime = requirelib("mime", "luaopen_mime", getfenv(1))
18local ltn12 = require("ltn12") 12local ltn12 = require("ltn12")
19 13
20----------------------------------------------------------------------------- 14-----------------------------------------------------------------------------
21-- Setup namespace 15-- Setup namespace
22----------------------------------------------------------------------------- 16-----------------------------------------------------------------------------
23-- make all module globals fall into mime namespace 17_LOADED["mime"] = mime
24setmetatable(mime, { __index = _G })
25setfenv(1, mime)
26 18
27-- encode, decode and wrap algorithm tables 19-- encode, decode and wrap algorithm tables
28encodet = {} 20encodet = {}
@@ -48,7 +40,7 @@ end
48 40
49encodet['quoted-printable'] = function(mode) 41encodet['quoted-printable'] = function(mode)
50 return ltn12.filter.cycle(qp, "", 42 return ltn12.filter.cycle(qp, "",
51 (mode == "binary") and "=0D=0A" or "\13\10") 43 (mode == "binary") and "=0D=0A" or "\r\n")
52end 44end
53 45
54-- define the decoding filters 46-- define the decoding filters
diff --git a/src/select.c b/src/select.c
index 1ebd82c..13f9d6e 100644
--- a/src/select.c
+++ b/src/select.c
@@ -9,26 +9,21 @@
9#include <lua.h> 9#include <lua.h>
10#include <lauxlib.h> 10#include <lauxlib.h>
11 11
12#include "luasocket.h"
13#include "socket.h" 12#include "socket.h"
14#include "auxiliar.h"
15#include "select.h" 13#include "select.h"
16 14
17/*=========================================================================*\ 15/*=========================================================================*\
18* Internal function prototypes. 16* Internal function prototypes.
19\*=========================================================================*/ 17\*=========================================================================*/
20static int meth_set(lua_State *L); 18static int getfd(lua_State *L);
21static int meth_isset(lua_State *L); 19static int dirty(lua_State *L);
22static int c_select(lua_State *L); 20static int collect_fd(lua_State *L, int tab, int max_fd, int itab, fd_set *set);
21static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set);
22static void return_fd(lua_State *L, fd_set *set, int max_fd,
23 int itab, int tab, int start);
24static void make_assoc(lua_State *L, int tab);
23static int global_select(lua_State *L); 25static int global_select(lua_State *L);
24 26
25/* fd_set object methods */
26static luaL_reg set[] = {
27 {"set", meth_set},
28 {"isset", meth_isset},
29 {NULL, NULL}
30};
31
32/* functions in library namespace */ 27/* functions in library namespace */
33static luaL_reg func[] = { 28static luaL_reg func[] = {
34 {"select", global_select}, 29 {"select", global_select},
@@ -36,22 +31,13 @@ static luaL_reg func[] = {
36}; 31};
37 32
38/*=========================================================================*\ 33/*=========================================================================*\
39* Internal function prototypes. 34* Exported functions
40\*=========================================================================*/ 35\*=========================================================================*/
41/*-------------------------------------------------------------------------*\ 36/*-------------------------------------------------------------------------*\
42* Initializes module 37* Initializes module
43\*-------------------------------------------------------------------------*/ 38\*-------------------------------------------------------------------------*/
44int select_open(lua_State *L) 39int select_open(lua_State *L) {
45{ 40 luaL_openlib(L, NULL, func, 0);
46 /* get select auxiliar lua function from lua code and register
47 * pass it as an upvalue to global_select */
48#ifdef LUASOCKET_COMPILED
49#include "select.lch"
50#else
51 lua_dofile(L, "select.lua");
52#endif
53 luaL_openlib(L, NULL, func, 1);
54 aux_newclass(L, "select{fd_set}", set);
55 return 0; 41 return 0;
56} 42}
57 43
@@ -61,64 +47,149 @@ int select_open(lua_State *L)
61/*-------------------------------------------------------------------------*\ 47/*-------------------------------------------------------------------------*\
62* Waits for a set of sockets until a condition is met or timeout. 48* Waits for a set of sockets until a condition is met or timeout.
63\*-------------------------------------------------------------------------*/ 49\*-------------------------------------------------------------------------*/
64static int global_select(lua_State *L) 50static int global_select(lua_State *L) {
65{ 51 int timeout, rtab, wtab, itab, max_fd, ret, ndirty;
66 fd_set *read_fd_set, *write_fd_set; 52 fd_set rset, wset;
67 /* make sure we have enough arguments (nil is the default) */ 53 FD_ZERO(&rset); FD_ZERO(&wset);
68 lua_settop(L, 3); 54 lua_settop(L, 3);
69 /* check timeout */ 55 timeout = lua_isnil(L, 3) ? -1 : (int)(luaL_checknumber(L, 3) * 1000);
70 if (!lua_isnil(L, 3) && !lua_isnumber(L, 3)) 56 lua_newtable(L); itab = lua_gettop(L);
71 luaL_argerror(L, 3, "number or nil expected"); 57 lua_newtable(L); rtab = lua_gettop(L);
72 /* select auxiliar lua function to be called comes first */ 58 lua_newtable(L); wtab = lua_gettop(L);
73 lua_pushvalue(L, lua_upvalueindex(1)); 59 max_fd = collect_fd(L, 1, -1, itab, &rset);
74 lua_insert(L, 1); 60 ndirty = check_dirty(L, 1, rtab, &rset);
75 /* pass fd_set objects */ 61 timeout = ndirty > 0? 0: timeout;
76 read_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); 62 max_fd = collect_fd(L, 2, max_fd, itab, &wset);
77 FD_ZERO(read_fd_set); 63 ret = sock_select(max_fd+1, &rset, &wset, NULL, timeout);
78 aux_setclass(L, "select{fd_set}", -1); 64 if (ret > 0 || (ret == 0 && ndirty > 0)) {
79 write_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); 65 return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
80 FD_ZERO(write_fd_set); 66 return_fd(L, &wset, max_fd+1, itab, wtab, 0);
81 aux_setclass(L, "select{fd_set}", -1); 67 make_assoc(L, rtab);
82 /* pass select auxiliar C function */ 68 make_assoc(L, wtab);
83 lua_pushcfunction(L, c_select); 69 return 2;
84 /* call select auxiliar lua function */ 70 } else if (ret == 0) {
85 lua_call(L, 6, 3); 71 lua_pushstring(L, "timeout");
86 return 3; 72 return 3;
73 } else {
74 lua_pushnil(L);
75 lua_pushnil(L);
76 lua_pushstring(L, "error");
77 return 3;
78 }
87} 79}
88 80
89/*=========================================================================*\ 81/*=========================================================================*\
90* Lua methods 82* Internal functions
91\*=========================================================================*/ 83\*=========================================================================*/
92static int meth_set(lua_State *L) 84static int getfd(lua_State *L) {
93{ 85 int fd = -1;
94 fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1); 86 lua_pushstring(L, "getfd");
95 t_sock fd = (t_sock) lua_tonumber(L, 2); 87 lua_gettable(L, -2);
96 if (fd >= 0) FD_SET(fd, set); 88 if (!lua_isnil(L, -1)) {
97 return 0; 89 lua_pushvalue(L, -2);
90 lua_call(L, 1, 1);
91 if (lua_isnumber(L, -1))
92 fd = lua_tonumber(L, -1);
93 }
94 lua_pop(L, 1);
95 return fd;
98} 96}
99 97
100static int meth_isset(lua_State *L) 98static int dirty(lua_State *L) {
101{ 99 int is = 0;
102 fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1); 100 lua_pushstring(L, "dirty");
103 t_sock fd = (t_sock) lua_tonumber(L, 2); 101 lua_gettable(L, -2);
104 if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1); 102 if (!lua_isnil(L, -1)) {
105 else lua_pushnil(L); 103 lua_pushvalue(L, -2);
106 return 1; 104 lua_call(L, 1, 1);
105 is = lua_toboolean(L, -1);
106 }
107 lua_pop(L, 1);
108 return is;
107} 109}
108 110
109/*=========================================================================*\ 111static int collect_fd(lua_State *L, int tab, int max_fd,
110* Internal functions 112 int itab, fd_set *set) {
111\*=========================================================================*/ 113 int i = 1;
112static int c_select(lua_State *L) 114 if (lua_isnil(L, tab))
113{ 115 return max_fd;
114 int max_fd = (int) lua_tonumber(L, 1); 116 while (1) {
115 fd_set *read_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 2); 117 int fd;
116 fd_set *write_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 3); 118 lua_pushnumber(L, i);
117 int timeout = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000); 119 lua_gettable(L, tab);
118 struct timeval tv; 120 if (lua_isnil(L, -1)) {
119 tv.tv_sec = timeout / 1000; 121 lua_pop(L, 1);
120 tv.tv_usec = (timeout % 1000) * 1000; 122 break;
121 lua_pushnumber(L, select(max_fd, read_fd_set, write_fd_set, NULL, 123 }
122 timeout < 0 ? NULL : &tv)); 124 fd = getfd(L);
123 return 1; 125 if (fd > 0) {
126 FD_SET(fd, set);
127 if (max_fd < fd) max_fd = fd;
128 lua_pushnumber(L, fd);
129 lua_pushvalue(L, -2);
130 lua_settable(L, itab);
131 }
132 lua_pop(L, 1);
133 i = i + 1;
134 }
135 return max_fd;
136}
137
138static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) {
139 int ndirty = 0, i = 1;
140 if (lua_isnil(L, tab))
141 return 0;
142 while (1) {
143 int fd;
144 lua_pushnumber(L, i);
145 lua_gettable(L, tab);
146 if (lua_isnil(L, -1)) {
147 lua_pop(L, 1);
148 break;
149 }
150 fd = getfd(L);
151 if (fd > 0 && dirty(L)) {
152 lua_pushnumber(L, ++ndirty);
153 lua_pushvalue(L, -2);
154 lua_settable(L, dtab);
155 FD_CLR(fd, set);
156 }
157 lua_pop(L, 1);
158 i = i + 1;
159 }
160 return ndirty;
161}
162
163static void return_fd(lua_State *L, fd_set *set, int max_fd,
164 int itab, int tab, int start) {
165 int fd;
166 for (fd = 0; fd < max_fd; fd++) {
167 if (FD_ISSET(fd, set)) {
168 lua_pushnumber(L, ++start);
169 lua_pushnumber(L, fd);
170 lua_gettable(L, itab);
171 lua_settable(L, tab);
172 }
173 }
174}
175
176static void make_assoc(lua_State *L, int tab) {
177 int i = 1, atab;
178 lua_newtable(L); atab = lua_gettop(L);
179 while (1) {
180 lua_pushnumber(L, i);
181 lua_gettable(L, tab);
182 if (!lua_isnil(L, -1)) {
183 lua_pushnumber(L, i);
184 lua_pushvalue(L, -2);
185 lua_settable(L, atab);
186 lua_pushnumber(L, i);
187 lua_settable(L, atab);
188 } else {
189 lua_pop(L, 1);
190 break;
191 }
192 i = i+1;
193 }
124} 194}
195
diff --git a/src/smtp.lua b/src/smtp.lua
index 7ae99a5..dc80c35 100644
--- a/src/smtp.lua
+++ b/src/smtp.lua
@@ -7,15 +7,9 @@
7----------------------------------------------------------------------------- 7-----------------------------------------------------------------------------
8 8
9----------------------------------------------------------------------------- 9-----------------------------------------------------------------------------
10-- Load SMTP from dynamic library 10-- Load required modules
11-- Comment these lines if you are loading static
12-----------------------------------------------------------------------------
13local open = assert(loadlib("smtp", "luaopen_smtp"))
14local smtp = assert(open())
15
16-----------------------------------------------------------------------------
17-- Load other required modules
18----------------------------------------------------------------------------- 11-----------------------------------------------------------------------------
12local smtp = requirelib("smtp")
19local socket = require("socket") 13local socket = require("socket")
20local ltn12 = require("ltn12") 14local ltn12 = require("ltn12")
21local tp = require("tp") 15local tp = require("tp")
@@ -23,10 +17,10 @@ local tp = require("tp")
23----------------------------------------------------------------------------- 17-----------------------------------------------------------------------------
24-- Setup namespace 18-- Setup namespace
25----------------------------------------------------------------------------- 19-----------------------------------------------------------------------------
26-- make all module globals fall into smtp namespace 20_LOADED["smtp"] = smtp
27setmetatable(smtp, { __index = _G })
28setfenv(1, smtp)
29 21
22-- timeout for connection
23TIMEOUT = 60
30-- default server used to send e-mails 24-- default server used to send e-mails
31SERVER = "localhost" 25SERVER = "localhost"
32-- default port 26-- default port
@@ -94,9 +88,7 @@ function metat.__index:send(mailt)
94end 88end
95 89
96function open(server, port) 90function open(server, port)
97 print(server or SERVER, port or PORT) 91 local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT))
98 local tp, error = tp.connect(server or SERVER, port or PORT)
99 if not tp then return nil, error end
100 return setmetatable({tp = tp}, metat) 92 return setmetatable({tp = tp}, metat)
101end 93end
102 94
@@ -121,7 +113,10 @@ local function send_multipart(mesgt)
121 coroutine.yield('content-type: multipart/mixed; boundary="' .. 113 coroutine.yield('content-type: multipart/mixed; boundary="' ..
122 bd .. '"\r\n\r\n') 114 bd .. '"\r\n\r\n')
123 -- send preamble 115 -- send preamble
124 if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) end 116 if mesgt.body.preamble then
117 coroutine.yield(mesgt.body.preamble)
118 coroutine.yield("\r\n")
119 end
125 -- send each part separated by a boundary 120 -- send each part separated by a boundary
126 for i, m in ipairs(mesgt.body) do 121 for i, m in ipairs(mesgt.body) do
127 coroutine.yield("\r\n--" .. bd .. "\r\n") 122 coroutine.yield("\r\n--" .. bd .. "\r\n")
@@ -130,7 +125,10 @@ local function send_multipart(mesgt)
130 -- send last boundary 125 -- send last boundary
131 coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") 126 coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
132 -- send epilogue 127 -- send epilogue
133 if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) end 128 if mesgt.body.epilogue then
129 coroutine.yield(mesgt.body.epilogue)
130 coroutine.yield("\r\n")
131 end
134end 132end
135 133
136-- yield message body from a source 134-- yield message body from a source
@@ -183,12 +181,12 @@ end
183-- set defaul headers 181-- set defaul headers
184local function adjust_headers(mesgt) 182local function adjust_headers(mesgt)
185 local lower = {} 183 local lower = {}
186 for i,v in (mesgt or lower) do 184 for i,v in (mesgt.headers or lower) do
187 lower[string.lower(i)] = v 185 lower[string.lower(i)] = v
188 end 186 end
189 lower["date"] = lower["date"] or 187 lower["date"] = lower["date"] or
190 os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) 188 os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
191 lower["x-mailer"] = lower["x-mailer"] or socket.version 189 lower["x-mailer"] = lower["x-mailer"] or socket.VERSION
192 -- this can't be overriden 190 -- this can't be overriden
193 lower["mime-version"] = "1.0" 191 lower["mime-version"] = "1.0"
194 mesgt.headers = lower 192 mesgt.headers = lower
@@ -198,18 +196,22 @@ function message(mesgt)
198 adjust_headers(mesgt) 196 adjust_headers(mesgt)
199 -- create and return message source 197 -- create and return message source
200 local co = coroutine.create(function() send_message(mesgt) end) 198 local co = coroutine.create(function() send_message(mesgt) end)
201 return function() return socket.skip(1, coroutine.resume(co)) end 199 return function()
200 local ret, a, b = coroutine.resume(co)
201 if ret then return a, b
202 else return nil, a end
203 end
202end 204end
203 205
204--------------------------------------------------------------------------- 206---------------------------------------------------------------------------
205-- High level SMTP API 207-- High level SMTP API
206----------------------------------------------------------------------------- 208-----------------------------------------------------------------------------
207send = socket.protect(function(mailt) 209send = socket.protect(function(mailt)
208 local smtp = socket.try(open(mailt.server, mailt.port)) 210 local con = open(mailt.server, mailt.port)
209 smtp:greet(mailt.domain) 211 con:greet(mailt.domain)
210 smtp:send(mailt) 212 con:send(mailt)
211 smtp:quit() 213 con:quit()
212 return smtp:close() 214 return con:close()
213end) 215end)
214 216
215return smtp 217return smtp
diff --git a/src/socket.lua b/src/socket.lua
index 418cd1b..9aa6437 100644
--- a/src/socket.lua
+++ b/src/socket.lua
@@ -7,8 +7,8 @@
7----------------------------------------------------------------------------- 7-----------------------------------------------------------------------------
8-- Load LuaSocket from dynamic library 8-- Load LuaSocket from dynamic library
9----------------------------------------------------------------------------- 9-----------------------------------------------------------------------------
10local open = assert(loadlib("luasocket", "luaopen_socket")) 10local socket = requirelib("luasocket", "luaopen_socket", getfenv(1))
11local socket = assert(open()) 11_LOADED["socket"] = socket
12 12
13----------------------------------------------------------------------------- 13-----------------------------------------------------------------------------
14-- Auxiliar functions 14-- Auxiliar functions
@@ -116,18 +116,21 @@ socket.sourcet["by-length"] = function(sock, length)
116end 116end
117 117
118socket.sourcet["until-closed"] = function(sock) 118socket.sourcet["until-closed"] = function(sock)
119 local done
119 return setmetatable({ 120 return setmetatable({
120 getfd = function() return sock:getfd() end, 121 getfd = function() return sock:getfd() end,
121 dirty = function() return sock:dirty() end 122 dirty = function() return sock:dirty() end
122 }, { 123 }, {
123 __call = ltn12.source.simplify(function() 124 __call = function()
125 if done then return nil end
124 local chunk, err, partial = sock:receive(socket.BLOCKSIZE) 126 local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
125 if not err then return chunk 127 if not err then return chunk
126 elseif err == "closed" then 128 elseif err == "closed" then
127 sock:close() 129 sock:close()
128 return partial, ltn12.source.empty() 130 done = 1
131 return partial
129 else return nil, err end 132 else return nil, err end
130 end) 133 end
131 }) 134 })
132end 135end
133 136
diff --git a/src/tcp.c b/src/tcp.c
index 90cfcde..845e0a3 100644
--- a/src/tcp.c
+++ b/src/tcp.c
@@ -9,13 +9,10 @@
9#include <lua.h> 9#include <lua.h>
10#include <lauxlib.h> 10#include <lauxlib.h>
11 11
12#include "luasocket.h"
13
14#include "auxiliar.h" 12#include "auxiliar.h"
15#include "socket.h" 13#include "socket.h"
16#include "inet.h" 14#include "inet.h"
17#include "options.h" 15#include "options.h"
18#include "base.h"
19#include "tcp.h" 16#include "tcp.h"
20 17
21/*=========================================================================*\ 18/*=========================================================================*\
@@ -41,7 +38,7 @@ static int meth_dirty(lua_State *L);
41/* tcp object methods */ 38/* tcp object methods */
42static luaL_reg tcp[] = { 39static luaL_reg tcp[] = {
43 {"__gc", meth_close}, 40 {"__gc", meth_close},
44 {"__tostring", base_meth_tostring}, 41 {"__tostring", aux_tostring},
45 {"accept", meth_accept}, 42 {"accept", meth_accept},
46 {"bind", meth_bind}, 43 {"bind", meth_bind},
47 {"close", meth_close}, 44 {"close", meth_close},
diff --git a/src/timeout.c b/src/timeout.c
index bd6c3b4..4f9a315 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -26,6 +26,14 @@
26#endif 26#endif
27#endif 27#endif
28 28
29/* min and max macros */
30#ifndef MIN
31#define MIN(x, y) ((x) < (y) ? x : y)
32#endif
33#ifndef MAX
34#define MAX(x, y) ((x) > (y) ? x : y)
35#endif
36
29/*=========================================================================*\ 37/*=========================================================================*\
30* Internal function prototypes 38* Internal function prototypes
31\*=========================================================================*/ 39\*=========================================================================*/
diff --git a/src/tp.lua b/src/tp.lua
index 3e9dba6..56dd8bc 100644
--- a/src/tp.lua
+++ b/src/tp.lua
@@ -2,24 +2,28 @@
2-- Unified SMTP/FTP subsystem 2-- Unified SMTP/FTP subsystem
3-- LuaSocket toolkit. 3-- LuaSocket toolkit.
4-- Author: Diego Nehab 4-- Author: Diego Nehab
5-- Conforming to: RFC 2616, LTN7
6-- RCS ID: $Id$ 5-- RCS ID: $Id$
7----------------------------------------------------------------------------- 6-----------------------------------------------------------------------------
8 7
9----------------------------------------------------------------------------- 8-----------------------------------------------------------------------------
10-- Load other required modules 9-- Load required modules
11----------------------------------------------------------------------------- 10-----------------------------------------------------------------------------
12local socket = require("socket") 11local socket = require("socket")
12local ltn12 = require("ltn12")
13 13
14----------------------------------------------------------------------------- 14-----------------------------------------------------------------------------
15-- Setup namespace 15-- Setup namespace
16----------------------------------------------------------------------------- 16-----------------------------------------------------------------------------
17tp = {} 17_LOADED["tp"] = getfenv(1)
18setmetatable(tp, { __index = _G })
19setfenv(1, tp)
20 18
19-----------------------------------------------------------------------------
20-- Program constants
21-----------------------------------------------------------------------------
21TIMEOUT = 60 22TIMEOUT = 60
22 23
24-----------------------------------------------------------------------------
25-- Implementation
26-----------------------------------------------------------------------------
23-- gets server reply (works for SMTP and FTP) 27-- gets server reply (works for SMTP and FTP)
24local function get_reply(control) 28local function get_reply(control)
25 local code, current, sep 29 local code, current, sep
@@ -37,7 +41,6 @@ local function get_reply(control)
37 -- reply ends with same code 41 -- reply ends with same code
38 until code == current and sep == " " 42 until code == current and sep == " "
39 end 43 end
40print(reply)
41 return code, reply 44 return code, reply
42end 45end
43 46
@@ -46,6 +49,7 @@ local metat = { __index = {} }
46 49
47function metat.__index:check(ok) 50function metat.__index:check(ok)
48 local code, reply = get_reply(self.control) 51 local code, reply = get_reply(self.control)
52print(reply)
49 if not code then return nil, reply end 53 if not code then return nil, reply end
50 if type(ok) ~= "function" then 54 if type(ok) ~= "function" then
51 if type(ok) == "table" then 55 if type(ok) == "table" then
@@ -103,11 +107,11 @@ function metat.__index:close()
103end 107end
104 108
105-- connect with server and return control object 109-- connect with server and return control object
106function connect(host, port) 110connect = socket.protect(function(host, port, timeout)
107 local control, err = socket.connect(host, port) 111 local control = socket.try(socket.tcp())
108 if not control then return nil, err end 112 socket.try(control:settimeout(timeout or TIMEOUT))
109 control:settimeout(TIMEOUT) 113 socket.try(control:connect(host, port))
110 return setmetatable({control = control}, metat) 114 return setmetatable({control = control}, metat)
111end 115end)
112 116
113return tp 117return tp
diff --git a/src/udp.c b/src/udp.c
index 4770a2e..51d6402 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -9,13 +9,10 @@
9#include <lua.h> 9#include <lua.h>
10#include <lauxlib.h> 10#include <lauxlib.h>
11 11
12#include "luasocket.h"
13
14#include "auxiliar.h" 12#include "auxiliar.h"
15#include "socket.h" 13#include "socket.h"
16#include "inet.h" 14#include "inet.h"
17#include "options.h" 15#include "options.h"
18#include "base.h"
19#include "udp.h" 16#include "udp.h"
20 17
21/*=========================================================================*\ 18/*=========================================================================*\
@@ -51,7 +48,7 @@ static luaL_reg udp[] = {
51 {"close", meth_close}, 48 {"close", meth_close},
52 {"setoption", meth_setoption}, 49 {"setoption", meth_setoption},
53 {"__gc", meth_close}, 50 {"__gc", meth_close},
54 {"__tostring", base_meth_tostring}, 51 {"__tostring", aux_tostring},
55 {"getfd", meth_getfd}, 52 {"getfd", meth_getfd},
56 {"setfd", meth_setfd}, 53 {"setfd", meth_setfd},
57 {"dirty", meth_dirty}, 54 {"dirty", meth_dirty},
diff --git a/src/url.lua b/src/url.lua
index 2441268..aac2a47 100644
--- a/src/url.lua
+++ b/src/url.lua
@@ -9,9 +9,7 @@
9----------------------------------------------------------------------------- 9-----------------------------------------------------------------------------
10-- Setup namespace 10-- Setup namespace
11----------------------------------------------------------------------------- 11-----------------------------------------------------------------------------
12local url = {} 12_LOADED["url"] = getfenv(1)
13setmetatable(url, { __index = _G })
14setfenv(1, url)
15 13
16----------------------------------------------------------------------------- 14-----------------------------------------------------------------------------
17-- Encodes a string into its escaped hexadecimal representation 15-- Encodes a string into its escaped hexadecimal representation
diff --git a/test/httptest.lua b/test/httptest.lua
index a70aa87..61dc60a 100644
--- a/test/httptest.lua
+++ b/test/httptest.lua
@@ -4,14 +4,18 @@
4-- to "/luasocket-test-cgi" and "/luasocket-test-cgi/" 4-- to "/luasocket-test-cgi" and "/luasocket-test-cgi/"
5-- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth 5-- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth
6 6
7require("http") 7socket = require("socket")
8http = require("http")
9mime = require("mime")
10url = require("url")
11ltn12 = require("ltn12")
8 12
9dofile("testsupport.lua") 13dofile("testsupport.lua")
10 14
11local host, proxy, request, response, index_file 15local host, proxy, request, response, index_file
12local ignore, expect, index, prefix, cgiprefix, index_crlf 16local ignore, expect, index, prefix, cgiprefix, index_crlf
13 17
14socket.http.TIMEOUT = 10 18http.TIMEOUT = 10
15 19
16local t = socket.time() 20local t = socket.time()
17 21
@@ -56,7 +60,7 @@ local check_request = function(request, expect, ignore)
56 end 60 end
57 request.source = request.source or 61 request.source = request.source or
58 (request.body and ltn12.source.string(request.body)) 62 (request.body and ltn12.source.string(request.body))
59 local response = socket.http.request(request) 63 local response = http.request(request)
60 if t and table.getn(t) > 0 then response.body = table.concat(t) end 64 if t and table.getn(t) > 0 then response.body = table.concat(t) end
61 check_result(response, expect, ignore) 65 check_result(response, expect, ignore)
62end 66end
@@ -64,16 +68,16 @@ end
64------------------------------------------------------------------------ 68------------------------------------------------------------------------
65io.write("testing request uri correctness: ") 69io.write("testing request uri correctness: ")
66local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" 70local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string"
67local back, h, c, e = socket.http.get("http://" .. host .. forth) 71local back, h, c, e = http.get("http://" .. host .. forth)
68if not back then fail(e) end 72if not back then fail(e) end
69back = socket.url.parse(back) 73back = url.parse(back)
70if similar(back.query, "this+is+the+query+string") then print("ok") 74if similar(back.query, "this+is+the+query+string") then print("ok")
71else fail(back.query) end 75else fail(back.query) end
72 76
73------------------------------------------------------------------------ 77------------------------------------------------------------------------
74io.write("testing query string correctness: ") 78io.write("testing query string correctness: ")
75forth = "this+is+the+query+string" 79forth = "this+is+the+query+string"
76back = socket.http.get("http://" .. host .. cgiprefix .. 80back = http.get("http://" .. host .. cgiprefix ..
77 "/query-string?" .. forth) 81 "/query-string?" .. forth)
78if similar(back, forth) then print("ok") 82if similar(back, forth) then print("ok")
79else fail("failed!") end 83else fail("failed!") end
@@ -149,7 +153,7 @@ check_request(request, expect, ignore)
149 153
150------------------------------------------------------------------------ 154------------------------------------------------------------------------
151io.write("testing simple post function: ") 155io.write("testing simple post function: ")
152back = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index) 156back = http.post("http://" .. host .. cgiprefix .. "/cat", index)
153assert(back == index) 157assert(back == index)
154 158
155------------------------------------------------------------------------ 159------------------------------------------------------------------------
@@ -278,30 +282,6 @@ ignore = {
278check_request(request, expect, ignore) 282check_request(request, expect, ignore)
279 283
280------------------------------------------------------------------------ 284------------------------------------------------------------------------
281io.write("testing host not found: ")
282request = {
283 url = "http://wronghost/does/not/exist"
284}
285local c, e = socket.connect("wronghost", 80)
286expect = {
287 error = e
288}
289ignore = {}
290check_request(request, expect, ignore)
291
292------------------------------------------------------------------------
293io.write("testing invalid url: ")
294request = {
295 url = host .. prefix
296}
297local c, e = socket.connect("", 80)
298expect = {
299 error = e
300}
301ignore = {}
302check_request(request, expect, ignore)
303
304------------------------------------------------------------------------
305io.write("testing document not found: ") 285io.write("testing document not found: ")
306request = { 286request = {
307 url = "http://" .. host .. "/wrongdocument.html" 287 url = "http://" .. host .. "/wrongdocument.html"
@@ -397,29 +377,16 @@ ignore = {
397check_request(request, expect, ignore) 377check_request(request, expect, ignore)
398 378
399------------------------------------------------------------------------ 379------------------------------------------------------------------------
400io.write("testing wrong scheme: ")
401request = {
402 url = "wrong://" .. host .. cgiprefix .. "/cat",
403 method = "GET"
404}
405expect = {
406 error = "unknown scheme 'wrong'"
407}
408ignore = {
409}
410check_request(request, expect, ignore)
411
412------------------------------------------------------------------------
413local body 380local body
414io.write("testing simple get function: ") 381io.write("testing simple get function: ")
415body = socket.http.get("http://" .. host .. prefix .. "/index.html") 382body = http.get("http://" .. host .. prefix .. "/index.html")
416assert(body == index) 383assert(body == index)
417print("ok") 384print("ok")
418 385
419------------------------------------------------------------------------ 386------------------------------------------------------------------------
420io.write("testing HEAD method: ") 387io.write("testing HEAD method: ")
421socket.http.TIMEOUT = 1 388http.TIMEOUT = 1
422response = socket.http.request { 389response = http.request {
423 method = "HEAD", 390 method = "HEAD",
424 url = "http://www.cs.princeton.edu/~diego/" 391 url = "http://www.cs.princeton.edu/~diego/"
425} 392}
@@ -427,6 +394,25 @@ assert(response and response.headers)
427print("ok") 394print("ok")
428 395
429------------------------------------------------------------------------ 396------------------------------------------------------------------------
397io.write("testing host not found: ")
398local c, e = socket.connect("wronghost", 80)
399local r, re = http.request{url = "http://wronghost/does/not/exist"}
400assert(r == nil and e == re)
401r, re = http.get("http://wronghost/does/not/exist")
402assert(r == nil and e == re)
403print("ok")
404
405------------------------------------------------------------------------
406io.write("testing invalid url: ")
407local c, e = socket.connect("", 80)
408local r, re = http.request{url = host .. prefix}
409assert(r == nil and e == re)
410r, re = http.get(host .. prefix)
411assert(r == nil and e == re)
412print("ok")
413
414------------------------------------------------------------------------
430print("passed all tests") 415print("passed all tests")
416os.remove("err")
431 417
432print(string.format("done in %.2fs", socket.time() - t)) 418print(string.format("done in %.2fs", socket.time() - t))
diff --git a/test/testclnt.lua b/test/testclnt.lua
index 1825007..14d3484 100644
--- a/test/testclnt.lua
+++ b/test/testclnt.lua
@@ -69,7 +69,7 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone)
69 end 69 end
70end 70end
71 71
72if not socket.debug then 72if not socket.DEBUG then
73 fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") 73 fail("Please define LUASOCKET_DEBUG and recompile LuaSocket")
74end 74end
75 75
diff --git a/test/testmesg.lua b/test/testmesg.lua
index 15b0eb7..449f4c2 100644
--- a/test/testmesg.lua
+++ b/test/testmesg.lua
@@ -1,52 +1,57 @@
1require("smtp") 1-- load the smtp support and its friends
2require("mime") 2local smtp = require("smtp")
3local mime = require("mime")
4local ltn12 = require("ltn12")
3 5
4mesgt = { 6-- creates a source to send a message with two parts. The first part is
5 headers = { 7-- plain text, the second part is a PNG image, encoded as base64.
6 to = "D Burgess <db@werx4.com>", 8source = smtp.message{
7 subject = "Looking good! (please check headers)" 9 headers = {
10 -- Remember that headers are *ignored* by smtp.send.
11 from = "Sicrano <sicrano@tecgraf.puc-rio.br>",
12 to = "Fulano <fulano@tecgraf.puc-rio.br>",
13 subject = "Here is a message with attachments"
14 },
15 body = {
16 preamble = "If your client doesn't understand attachments, \r\n" ..
17 "it will still display the preamble and the epilogue.\r\n",
18 "Preamble might show up even in a MIME enabled client.",
19 -- first part: No headers means plain text, us-ascii.
20 -- The mime.eol low-level filter normalizes end-of-line markers.
21 [1] = {
22 body = mime.eol(0, [[
23 Lines in a message body should always end with CRLF.
24 The smtp module will *NOT* perform translation. It will
25 perform necessary stuffing, though.
26 ]])
8 }, 27 },
9 body = { 28 -- second part: Headers describe content the to be an image,
10 preamble = "Some attatched stuff", 29 -- sent under the base64 transfer content encoding.
11 [1] = { 30 -- Notice that nothing happens until the message is sent. Small
12 body = mime.eol(0, "Testing stuffing.\n.\nGot you.\n.Hehehe.\n") 31 -- chunks are loaded into memory and translation happens on the fly.
13 }, 32 [2] = {
14 [2] = { 33 headers = {
15 headers = { 34 ["content-type"] = 'image/png; name="image.png"',
16 ["content-type"] = 'application/octet-stream; name="testmesg.lua"', 35 ["content-disposition"] = 'attachment; filename="image.png"',
17 ["content-disposition"] = 'attachment; filename="testmesg.lua"', 36 ["content-description"] = 'a beautiful image',
18 ["content-transfer-encoding"] = "BASE64" 37 ["content-transfer-encoding"] = "BASE64"
19 }, 38 },
20 body = ltn12.source.chain( 39 body = ltn12.source.chain(
21 ltn12.source.file(io.open("testmesg.lua", "rb")), 40 ltn12.source.file(io.open("image.png", "rb")),
22 ltn12.filter.chain( 41 ltn12.filter.chain(
23 mime.encode("base64"), 42 mime.encode("base64"),
24 mime.wrap() 43 mime.wrap()
25 ) 44 )
26 ) 45 )
27 }, 46 },
28 [3] = { 47 epilogue = "This might also show up, but after the attachments"
29 headers = { 48 }
30 ["content-type"] = 'text/plain; name="testmesg.lua"',
31 ["content-disposition"] = 'attachment; filename="testmesg.lua"',
32 ["content-transfer-encoding"] = "QUOTED-PRINTABLE"
33 },
34 body = ltn12.source.chain(
35 ltn12.source.file(io.open("testmesg.lua", "rb")),
36 ltn12.filter.chain(
37 mime.normalize(),
38 mime.encode("quoted-printable"),
39 mime.wrap("quoted-printable")
40 )
41 )
42 },
43 epilogue = "Done attaching stuff",
44 }
45} 49}
46 50
47print(socket.smtp.send { 51-- finally send it
52r, e = smtp.send{
48 rcpt = "<diego@cs.princeton.edu>", 53 rcpt = "<diego@cs.princeton.edu>",
49 from = "<diego@cs.princeton.edu>", 54 from = "<diego@cs.princeton.edu>",
50 source = socket.smtp.message(mesgt), 55 source = source,
51 server = "mail.cs.princeton.edu" 56 server = "mail.cs.princeton.edu"
52}) 57}
diff --git a/test/urltest.lua b/test/urltest.lua
index 8c2ee88..92f2fae 100644
--- a/test/urltest.lua
+++ b/test/urltest.lua
@@ -1,4 +1,5 @@
1require"url" 1socket = require("socket")
2socket.url = require("url")
2dofile("testsupport.lua") 3dofile("testsupport.lua")
3 4
4local check_build_url = function(parsed) 5local check_build_url = function(parsed)