diff options
40 files changed, 2031 insertions, 811 deletions
@@ -2,6 +2,15 @@ ajeitar os README.* | |||
2 | ajeitar select. upvalue nao tem nada a ver... | 2 | ajeitar select. upvalue nao tem nada a ver... |
3 | make sure filter.chain fails gracefully. | 3 | make sure filter.chain fails gracefully. |
4 | ajeitar o manual sobre select, mais liberal agora | 4 | ajeitar o manual sobre select, mais liberal agora |
5 | falar sobre o novo esquema de namespace | ||
6 | tirar socket.url socket.ftp etc do manual. agora os namespaces estao | ||
7 | liberados. | ||
8 | ajeitar as referencias a RFCS e LTNS em todos os arquivos. | ||
9 | proxy no ftp | ||
10 | ajeitar < e-mail > no smtp? | ||
11 | ajeitar referencias a LTN12 nos manuais | ||
12 | |||
13 | make sure sockets are closed when exceptions are raised | ||
5 | 14 | ||
6 | falar sobre encodet/wrapt/decodet no manual sobre mime | 15 | falar sobre encodet/wrapt/decodet no manual sobre mime |
7 | 16 | ||
@@ -14,8 +23,6 @@ expose encode/decode tables to provide extensibility for mime module | |||
14 | use coroutines instead of fancy filters | 23 | use coroutines instead of fancy filters |
15 | 24 | ||
16 | check garbage collection in test*.lua | 25 | check garbage collection in test*.lua |
17 | pop3??? | ||
18 | |||
19 | 26 | ||
20 | add socket.TIMEOUT to be default timeout? | 27 | add 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> |
37 | FTP (File Transfer Protocol) is a protocol used to transfer files | 37 | FTP (File Transfer Protocol) is a protocol used to transfer files |
38 | between hosts. The module <tt>ftp.lua</tt> offers simple FTP support, | 38 | between hosts. The module <tt>ftp.lua</tt> offers simple FTP support. |
39 | allowing applications to download and upload files, and list directory | 39 | Applications can easily download and upload files. |
40 | contents. The implementation conforms to | 40 | The 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://][<user>[:<password>]@]<host>[:<port>][/<path>][<i>type</i>=a|i|d]</tt> | 52 | [ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][<i>type</i>=a|i]</tt> |
53 | </blockquote> | 53 | </blockquote> |
54 | 54 | ||
55 | <p> | ||
56 | High level functions are provided supporting the most common operations. | ||
57 | These high level functions are implemented on top of a lower level | ||
58 | interface. By using the low-level interface, users can easily create their | ||
59 | own functions to access <em>any</em> operation supported by the FTP | ||
60 | protocol. For that, check the implementation. | ||
61 | </p> | ||
62 | |||
63 | <p> | ||
64 | To use some of the functions in this module, a good understanding of | ||
65 | <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> | ||
66 | LTN012, Filters sources and sinks</a> is necessary. | ||
67 | </p> | ||
68 | |||
69 | <p> | ||
70 | The following constants can be set to control the default behaviour of | ||
71 | the 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> |
58 | socket.ftp.<b>get(</b>url<b>)</b><br> | 84 | ftp.<b>get(</b>url<b>)</b><br> |
59 | socket.ftp.<b>get{</b><br> | 85 | ftp.<b>get{</b><br> |
60 | url = <i>string</i>,<br> | 86 | host = <i>string</i>,<br> |
61 | type = <i>string</i>,<br> | 87 | sink = <i>LTN12 sink</i>,<br> |
62 | user = <i>string</i>,<br> | 88 | argument <i>or</i> path = <i>string</i>,<br> |
63 | password = <i>string</i><br> | 89 | [user = <i>string</i>,]<br> |
90 | [password = <i>string</i>]<br> | ||
91 | [command = <i>string</i>,]<br> | ||
92 | [port = <i>number</i>,]<br> | ||
93 | [type = <i>string</i>,]<br> | ||
94 | [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> |
68 | Downloads an URL from a FTP server. | 99 | The <tt>get</tt> function has two forms. The simple form has fixed |
100 | functionality: it downloads the contents of a URL and returns it as a | ||
101 | string. The generic form allows a <em>lot</em> more control, as explained | ||
102 | below. | ||
69 | </p> | 103 | </p> |
70 | 104 | ||
71 | <p class=parameters> | 105 | <p class=parameters> |
72 | The function can be called either directly with a <tt>url</tt> | 106 | If the argument of the <tt>get</tt> function is a table, the function |
73 | or with a <em>request table</em>. | 107 | expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of |
74 | Fields passed explicitly in the request table override those | 108 | <tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes |
75 | present in the <tt>url</tt>. | 109 | precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is |
76 | </p> | 110 | the 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> | 112 | optional arguments are the following: |
79 | The parameter <tt>type</tt> accepts values '<tt>a</tt>' (ASCII, the | ||
80 | default), '<tt>i</tt>' (binary) or '<tt>d</tt>' (directory listing) and | ||
81 | determines the transfer type. If <tt><path></tt> ends with a | ||
82 | '<tt>/</tt>' or <tt>type</tt> is '<tt>d</tt>', a directory listing of | ||
83 | <tt><path></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 | ||
116 | authentication. 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 | ||
123 | server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function. | ||
124 | </ul> | ||
87 | 125 | ||
88 | <p class=return> | 126 | <p class=return> |
89 | If successful, the function returns | 127 | If successful, the simple version returns the URL contents as a |
90 | the file content as a string. In case of error, the function returns | 128 | string, and the generic function returns 1. In case of error, both |
91 | <b><tt>nil</tt></b> and an error message describing the error. | 129 | functions return <b><tt>nil</tt></b> and an error message describing the |
130 | error. | ||
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. | 135 | local ftp = require("ftp") |
97 | f, 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. |
101 | f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua;type=d") | 139 | f, 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) | ||
106 | f, 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> | 144 | local ftp = require("ftp") |
118 | socket.ftp.<b>get_cb{</b><br> | 145 | local ltn12 = require("ltn12") |
119 | url = <i>string</i>,<br> | 146 | local url = require("url") |
120 | type = <i>string</i>,<br> | 147 | |
121 | content_cb = <i>receive-callback</i>,<br> | 148 | -- a function that returns a directory listing |
122 | user = <i>string</i>,<br> | 149 | function ls(u) |
123 | 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) |
128 | Same as <a href="#get"><tt>get</tt></a>, but the library returns | 155 | return r and table.concat(t), e |
129 | the content of the downloaded file to the receive callback | 156 | end |
130 | <tt>content_cb</tt>. | 157 | </pre> |
131 | </p> | ||
132 | |||
133 | <p class=note> | ||
134 | Note: 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> |
141 | socket.ftp.<b>put(</b>url, content<b>)</b><br> | 162 | ftp.<b>put(</b>url, content<b>)</b><br> |
142 | socket.ftp.<b>put{</b><br> | 163 | ftp.<b>put{</b><br> |
143 | url = <i>string</i>,<br> | 164 | host = <i>string</i>,<br> |
144 | content = <i>string</i>,<br> | 165 | source = <i>LTN12 sink</i>,<br> |
145 | type = <i>string</i>,<br> | 166 | argument <i>or</i> path = <i>string</i>,<br> |
146 | user = <i>string</i>,<br> | 167 | [user = <i>string</i>,]<br> |
147 | password = <i>string</i><br> | 168 | [password = <i>string</i>]<br> |
169 | [command = <i>string</i>,]<br> | ||
170 | [port = <i>number</i>,]<br> | ||
171 | [type = <i>string</i>,]<br> | ||
172 | [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> |
152 | Upload a file to a FTP server. | 177 | The <tt>put</tt> function has two forms. The simple form has fixed |
178 | functionality: it uploads a string of content into a URL. The generic form | ||
179 | allows a <em>lot</em> more control, as explained below. | ||
153 | </p> | 180 | </p> |
154 | 181 | ||
155 | <p class=parameters> | 182 | <p class=parameters> |
156 | The function can be called directly with a | 183 | If 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 | 184 | expects 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 |
159 | Values passed explicitly in the request table override those present in | 186 | precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is |
160 | the <tt>url</tt>. The parameter <tt>type</tt> accept values | 187 | the 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 |
162 | determines 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 |
163 | function tries to log in as '<tt>anonymous</tt>'. | 190 | optional arguments are the following: |
164 | </p> | 191 | </p> |
192 | <ul> | ||
193 | <li><tt>user</tt>, <tt>password</tt>: User name and password used for | ||
194 | authentication. 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 | ||
201 | server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function. | ||
202 | </ul> | ||
165 | 203 | ||
166 | <p class=return> | 204 | <p class=return> |
167 | If successful, the function returns 1. In case of error, the | 205 | Both functions return 1 if successful, or <b><tt>nil</tt></b> and an error |
168 | function returns <b><tt>nil</tt></b> followed by a string describing the error. | 206 | message 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 | 211 | local ftp = require("ftp") |
174 | r, 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 | ||
177 | r, 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> | ||
190 | socket.ftp.<b>put_cb{</b><br> | ||
191 | url = <i>string</i>,<br> | ||
192 | type = <i>string</i>,<br> | ||
193 | content_cb = <i>send-callback</i>,<br> | ||
194 | user = <i>string</i>,<br> | ||
195 | password = <i>string</i><br> | ||
196 | <b>}</b> | ||
197 | </p> | ||
198 | |||
199 | <p class=description> | ||
200 | Same as <a href="#put"><tt>put</tt></a>, but the | ||
201 | library obtains the contents of the file to be uploaded using the send | ||
202 | callback <tt>content_cb</tt>. | ||
203 | </p> | ||
204 | 212 | ||
205 | <p class=note> | 213 | -- Log as user "diego" on server "ftp.tecgraf.puc-rio.br", |
206 | Note: 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> | 216 | f, 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, | 221 | local ftp = require("ftp") |
213 | -- using binary mode for the transfer | 222 | local ltn12 = require("ltn12") |
214 | r, 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 |
227 | f, 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> · | ||
26 | <a href="home.html#download">download</a> · | ||
27 | <a href="introduction.html">introduction</a> · | ||
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"> | ||
40 | LTN012, Filters sources and sinks</a>. This manual simply describe the | ||
41 | functions. Please refer to the LTN for a deeper explanation of the | ||
42 | functionality 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"> | ||
52 | ltn12.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> | ||
57 | Returns a filter that passes all data it receives through each of a | ||
58 | series 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 | ||
63 | filters. | ||
64 | </p> | ||
65 | |||
66 | <p class=return> | ||
67 | The function returns the chained filter. | ||
68 | </p> | ||
69 | |||
70 | <p class=note> | ||
71 | The nesting of filters can be arbritrary. For instance, the useless filter | ||
72 | below doesn't do anything but return the data that was passed to it, | ||
73 | unaltered. | ||
74 | </p> | ||
75 | |||
76 | <pre class=example> | ||
77 | -- load required modules | ||
78 | ltn12 = require("ltn12") | ||
79 | mime = require("mime") | ||
80 | |||
81 | -- create a silly identity filter | ||
82 | id = 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"> | ||
93 | ltn12.filter.<b>cycle(</b>low [, ctx, extra]<b>)</b> | ||
94 | </p> | ||
95 | |||
96 | <p class=description> | ||
97 | Returns a high-level filter that cycles though a low-level filter by | ||
98 | passing 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 | ||
104 | argument the low-level filter might take. | ||
105 | </p> | ||
106 | |||
107 | <p class=return> | ||
108 | The function returns the high-level filter. | ||
109 | </p> | ||
110 | |||
111 | <pre class=example> | ||
112 | -- load the ltn12 module | ||
113 | local ltn12 = require("ltn12") | ||
114 | |||
115 | -- the base64 mime filter factory | ||
116 | encodet['base64'] = function() | ||
117 | return ltn12.filter.cycle(b64, "") | ||
118 | end | ||
119 | </pre> | ||
120 | |||
121 | <!-- pumps ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
122 | |||
123 | <h3 id="pump">Pumps</h3> | ||
124 | |||
125 | <!-- all ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
126 | |||
127 | <p class=name id="pump.all"> | ||
128 | ltn12.pump.<b>all(</b>source, sink<b>)</b> | ||
129 | </p> | ||
130 | |||
131 | <p class=description> | ||
132 | Pumps <em>all</em> data from a <tt>source</tt> to a <tt>sink</tt>. | ||
133 | </p> | ||
134 | |||
135 | <p class=return> | ||
136 | If successful, the function returns a value that evaluates to | ||
137 | <b><tt>true</tt></b>. In case | ||
138 | of 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"> | ||
144 | ltn12.pump.<b>step(</b>source, sink<b>)</b> | ||
145 | </p> | ||
146 | |||
147 | <p class=description> | ||
148 | Pumps <em>one</em> chunk of data from a <tt>source</tt> to a <tt>sink</tt>. | ||
149 | </p> | ||
150 | |||
151 | <p class=return> | ||
152 | If successful, the function returns a value that evaluates to | ||
153 | <b><tt>true</tt></b>. In case | ||
154 | of 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"> | ||
164 | ltn12.sink.<b>chain(</b>filter, sink<b>)</b> | ||
165 | </p> | ||
166 | |||
167 | <p class=description> | ||
168 | Creates a new sink that passes data through a <tt>filter</tt> before sending | ||
169 | it to a given <tt>sink</tt>. | ||
170 | </p> | ||
171 | |||
172 | <p class=return> | ||
173 | The function returns the new sink. | ||
174 | </p> | ||
175 | |||
176 | <!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
177 | |||
178 | <p class=name id="sink.error"> | ||
179 | ltn12.sink.<b>error(</b>message<b>)</b> | ||
180 | </p> | ||
181 | |||
182 | <p class=description> | ||
183 | Creates 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"> | ||
190 | ltn12.sink.<b>file(</b>handle, message<b>)</b> | ||
191 | </p> | ||
192 | |||
193 | <p class=description> | ||
194 | Creates 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> | ||
203 | The function returns a sink that sends all data to the given <tt>handle</tt> | ||
204 | and closes the file when done, or a sink that aborts the transmission with | ||
205 | an error <tt>message</tt> | ||
206 | </p> | ||
207 | |||
208 | <p class=note> | ||
209 | In the following example, notice how the prototype is designed to | ||
210 | fit nicely with the <tt>io.open</tt> function. | ||
211 | </p> | ||
212 | |||
213 | <pre class=example> | ||
214 | -- load the ltn12 module | ||
215 | local ltn12 = require("ltn12") | ||
216 | |||
217 | -- copy a file | ||
218 | ltn12.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"> | ||
227 | ltn12.sink.<b>null()</b> | ||
228 | </p> | ||
229 | |||
230 | <p class=description> | ||
231 | Returns a sink that ignores all data it receives. | ||
232 | </p> | ||
233 | |||
234 | <!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
235 | |||
236 | <p class=name id="sink.simplify"> | ||
237 | ltn12.sink.<b>simplify(</b>sink<b>)</b> | ||
238 | </p> | ||
239 | |||
240 | <p class=description> | ||
241 | Creates and returns a simple sink given a fancy <tt>sink</tt>. | ||
242 | </p> | ||
243 | |||
244 | <!-- table ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
245 | |||
246 | <p class=name id="sink.table"> | ||
247 | ltn12.sink.<b>table(</b>[table]<b>)</b> | ||
248 | </p> | ||
249 | |||
250 | <p class=description> | ||
251 | Creates a sink that stores all chunks in a table. The chunks can later be | ||
252 | efficiently 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> | ||
261 | The function returns the sink and the table. | ||
262 | </p> | ||
263 | |||
264 | <pre class=example> | ||
265 | -- load needed modules | ||
266 | local http = require("http") | ||
267 | local ltn12 = require("ltn12") | ||
268 | |||
269 | -- the http.get function | ||
270 | function 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 | ||
277 | end | ||
278 | </pre> | ||
279 | |||
280 | <!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
281 | |||
282 | <h3 id="source">Sources</h3> | ||
283 | |||
284 | <!-- cat ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
285 | |||
286 | <p class=name id="source.cat"> | ||
287 | ltn12.source.<b>cat(</b>source<sub>1</sub> [, source<sub>2</sub>, ..., | ||
288 | source<sub>N</sub>]<b>)</b> | ||
289 | </p> | ||
290 | |||
291 | <p class=description> | ||
292 | Creates a new source that produces the concatenation of the data produced | ||
293 | by 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 | ||
298 | sources. | ||
299 | </p> | ||
300 | |||
301 | <p class=return> | ||
302 | The function returns the new source. | ||
303 | </p> | ||
304 | |||
305 | <!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
306 | |||
307 | <p class=name id="source.chain"> | ||
308 | ltn12.source.<b>chain(</b>source, filter<b>)</b> | ||
309 | </p> | ||
310 | |||
311 | <p class=description> | ||
312 | Creates a new <tt>source</tt> that passes data through a <tt>filter</tt> | ||
313 | before returning it. | ||
314 | </p> | ||
315 | |||
316 | <p class=return> | ||
317 | The function returns the new source. | ||
318 | </p> | ||
319 | |||
320 | <!-- empty ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
321 | |||
322 | <p class=name id="source.empty"> | ||
323 | ltn12.source.<b>empty()</b> | ||
324 | </p> | ||
325 | |||
326 | <p class=description> | ||
327 | Creates and returns an empty source. | ||
328 | </p> | ||
329 | |||
330 | <!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
331 | |||
332 | <p class=name id="source.error"> | ||
333 | ltn12.source.<b>error(</b>message<b>)</b> | ||
334 | </p> | ||
335 | |||
336 | <p class=description> | ||
337 | Creates 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"> | ||
344 | ltn12.source.<b>file(</b>handle, message<b>)</b> | ||
345 | </p> | ||
346 | |||
347 | <p class=description> | ||
348 | Creates 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> | ||
357 | The function returns a source that reads chunks of data from | ||
358 | given <tt>handle</tt> and returns it to the user, | ||
359 | closing the file when done, or a source that aborts the transmission with | ||
360 | an error <tt>message</tt> | ||
361 | </p> | ||
362 | |||
363 | <p class=note> | ||
364 | In the following example, notice how the prototype is designed to | ||
365 | fit nicely with the <tt>io.open</tt> function. | ||
366 | </p> | ||
367 | |||
368 | <pre class=example> | ||
369 | -- load the ltn12 module | ||
370 | local ltn12 = require("ltn12") | ||
371 | |||
372 | -- copy a file | ||
373 | ltn12.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"> | ||
382 | ltn12.source.<b>simplify(</b>source<b>)</b> | ||
383 | </p> | ||
384 | |||
385 | <p class=description> | ||
386 | Creates and returns a simple source given a fancy <tt>source</tt>. | ||
387 | </p> | ||
388 | |||
389 | <!-- string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
390 | |||
391 | <p class=name id="source.string"> | ||
392 | ltn12.source.<b>string(</b>string<b>)</b> | ||
393 | </p> | ||
394 | |||
395 | <p class=description> | ||
396 | Creates 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> · | ||
407 | <a href="home.html#down">download</a> · | ||
408 | <a href="introduction.html">introduction</a> · | ||
409 | <a href="reference.html">reference</a> | ||
410 | </p> | ||
411 | <p> | ||
412 | <small> | ||
413 | Last modified by Diego Nehab on <br> | ||
414 | Sat 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> · | ||
26 | <a href="home.html#download">download</a> · | ||
27 | <a href="introduction.html">introduction</a> · | ||
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> | ||
39 | The MIME module offers filters that apply and remove common | ||
40 | content transfer encodings, such as Base64 and Quoted-Printable. | ||
41 | It also provides functions to break text into lines and change | ||
42 | the end-of-line convention. | ||
43 | MIME 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> | ||
52 | All functionality provided by the MIME module | ||
53 | follows the ideas presented in | ||
54 | <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> | ||
55 | LTN012, 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"> | ||
65 | mime.<b>normalize(</b>[marker]<b>)</b> | ||
66 | </p> | ||
67 | |||
68 | <p class=description> | ||
69 | Converts 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 | ||
74 | end-of-line marker defined by the MIME standard. | ||
75 | </p> | ||
76 | |||
77 | <p class=return> | ||
78 | The function returns a filter that performs the conversion. | ||
79 | </p> | ||
80 | |||
81 | <p class=note> | ||
82 | Note: There is no perfect solution to this problem. Different end-of-line | ||
83 | markers are an evil that will probably plague developers forever. | ||
84 | This function, however, will work perfectly for text created with any of | ||
85 | the most common end-of-line markers, i.e. the MacOS (CR), the Unix (LF), | ||
86 | or the DOS (CRLF) conventions. Even if the data has mixed end-of-line | ||
87 | markers, the function will still work well, although it doesn't | ||
88 | guarantee that the number of empty lines will be correct. | ||
89 | </p> | ||
90 | |||
91 | <!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
92 | |||
93 | <p class=name id="decode"> | ||
94 | mime.<b>decode(</b>"base64"<b>)</b><br> | ||
95 | mime.<b>decode(</b>"quoted-printable"<b>)</b> | ||
96 | </p> | ||
97 | |||
98 | <p class=description> | ||
99 | Returns a filter that decodes data from a given transfer content | ||
100 | encoding. | ||
101 | </p> | ||
102 | |||
103 | <p class=return> | ||
104 | The function returns the created filter. | ||
105 | </p> | ||
106 | |||
107 | <!-- encode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
108 | |||
109 | <p class=name id="encode"> | ||
110 | mime.<b>encode(</b>"base64"<b>)</b><br> | ||
111 | mime.<b>encode(</b>"quoted-printable" [, mode])</b> | ||
112 | </p> | ||
113 | |||
114 | <p class=description> | ||
115 | Returns a filter that encodes data according to a given transfer content | ||
116 | encoding. | ||
117 | </p> | ||
118 | |||
119 | <p class=parameters> | ||
120 | In the Quoted-Printable case, the user can specify whether the data is | ||
121 | textual 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> | ||
126 | The function returns the created filter. | ||
127 | </p> | ||
128 | |||
129 | <p class=note> | ||
130 | Although both transfer content encodings specify a limit for the line | ||
131 | length, the encoding filters do <em>not</em> break text into lines (for | ||
132 | added flexibility). | ||
133 | Below is a filter that converts binary data to the Base64 transfer content | ||
134 | encoding and breaks it into lines of the correct size. | ||
135 | </p> | ||
136 | |||
137 | <pre class=example> | ||
138 | base64 = ltn12.filter.chain( | ||
139 | mime.encode("base64"), | ||
140 | mime.wrap("base64") | ||
141 | ) | ||
142 | </pre> | ||
143 | |||
144 | <p class=note> | ||
145 | Note: Text data <em>has</em> to be converted to canonic form | ||
146 | <em>before</em> being encoded. | ||
147 | </p> | ||
148 | |||
149 | <pre class=example> | ||
150 | base64 = 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"> | ||
160 | mime.<b>wrap(</b>"text" [, length]<b>)</b><br> | ||
161 | mime.<b>wrap(</b>"base64"<b>)</b><br> | ||
162 | mime.<b>wrap(</b>"quoted-printable"<b>)</b> | ||
163 | </p> | ||
164 | |||
165 | <p class=description> | ||
166 | Returns a filter that breaks data into lines. | ||
167 | </p> | ||
168 | |||
169 | <p class=parameters> | ||
170 | The "<tt>text</tt>" line-wrap filter simply breaks text into lines by | ||
171 | inserting CRLF end-of-line markers at appropriate positions. | ||
172 | <tt>Length</tt> defaults 76. | ||
173 | The "<tt>base64</tt>" line-wrap filter works just like the default | ||
174 | "<tt>text</tt>" line-wrap filter with default length. | ||
175 | The function can also wrap "<tt>quoted-printable</tt>" lines, taking care | ||
176 | not to break lines in the middle of an escaped character. In that case, the | ||
177 | line length is fixed at 76. | ||
178 | </p> | ||
179 | |||
180 | <p class=return> | ||
181 | The function returns the created filter. | ||
182 | </p> | ||
183 | |||
184 | <p class=note> | ||
185 | For 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> | ||
189 | qp = ltn12.filter.chain( | ||
190 | mime.normalize(), | ||
191 | mime.encode("quoted-printable"), | ||
192 | mime.wrap("quoted-printable") | ||
193 | ) | ||
194 | </pre> | ||
195 | |||
196 | <p class=note> | ||
197 | Note: To break into lines with a different end-of-line convention, apply | ||
198 | a 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"> | ||
208 | A, B = mime.<b>b64(</b>C [, D]<b>)</b> | ||
209 | </p> | ||
210 | |||
211 | <p class=description> | ||
212 | Low-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> | ||
218 | that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of | ||
219 | <tt>C..D</tt>, <em>before</em> encoding. | ||
220 | If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with | ||
221 | the encoding of the remaining bytes of <tt>C</tt>. | ||
222 | </p> | ||
223 | |||
224 | <p class=note> | ||
225 | Note: The simplest use of this function is to encode a string into it's | ||
226 | Base64 transfer content encoding. Notice the extra parenthesis around the | ||
227 | call to <tt>mime.b64</tt>, to discard the second return value. | ||
228 | </p> | ||
229 | |||
230 | <pre class=example> | ||
231 | print((mime.b64("diego:password"))) | ||
232 | --> ZGllZ286cGFzc3dvcmQ= | ||
233 | </pre> | ||
234 | |||
235 | <!-- eol ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
236 | |||
237 | <p class=name id="eol"> | ||
238 | A, B = mime.<b>eol(</b>C [, D, marker]<b>)</b> | ||
239 | </p> | ||
240 | |||
241 | <p class=description> | ||
242 | Low-level filter to perform end-of-line marker translation. | ||
243 | For each chunk, the function needs to know if the last character of the | ||
244 | previous chunk could be part of an end-of-line marker or not. This is the | ||
245 | context the function receives besides the chunk. An updated version of | ||
246 | the 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 | ||
251 | ASCII value of the last character of the previous chunk, if it was a | ||
252 | candidate for line break, or 0 otherwise. | ||
253 | <tt>B</tt> is the same as <tt>C</tt>, but for the current | ||
254 | chunk. If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> includes a | ||
255 | new 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 | ||
261 | unix = mime.eol(0, dos, "\n") | ||
262 | </pre> | ||
263 | |||
264 | <!-- qp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
265 | |||
266 | <p class=name id="qp"> | ||
267 | A, B = mime.<b>qp(</b>C [, D, marker]<b>)</b> | ||
268 | </p> | ||
269 | |||
270 | <p class=description> | ||
271 | Low-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> | ||
277 | that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of | ||
278 | <tt>C..D</tt>, <em>before</em> encoding. | ||
279 | If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with | ||
280 | the encoding of the remaining bytes of <tt>C</tt>. | ||
281 | Throughout encoding, occurences of CRLF are replaced by the | ||
282 | <tt>marker</tt>, which itself defaults to CRLF. | ||
283 | </p> | ||
284 | |||
285 | <p class=note> | ||
286 | Note: The simplest use of this function is to encode a string into it's | ||
287 | Quoted-Printable transfer content encoding. | ||
288 | Notice the extra parenthesis around the call to <tt>mime.qp</tt>, to discard the second return value. | ||
289 | </p> | ||
290 | |||
291 | <pre class=example> | ||
292 | print((mime.qp("maçã"))) | ||
293 | --> ma=E7=E3= | ||
294 | </pre> | ||
295 | |||
296 | <!-- qpwrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
297 | |||
298 | <p class=name id="qpwrp"> | ||
299 | A, m = mime.<b>qpwrp(</b>n [, B, length]<b>)</b> | ||
300 | </p> | ||
301 | |||
302 | <p class=description> | ||
303 | Low-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 | ||
310 | line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes | ||
311 | left in the last line of <tt>A</tt>. | ||
312 | </p> | ||
313 | |||
314 | <p class=note> | ||
315 | Note: Besides breaking text into lines, this function makes sure the line | ||
316 | breaks don't fall in the middle of an escaped character combination. Also, | ||
317 | this function only breaks lines that are bigger than <tt>length</tt> bytes. | ||
318 | </p> | ||
319 | |||
320 | <!-- unb64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
321 | |||
322 | <p class=name id="unb64"> | ||
323 | A, B = mime.<b>unb64(</b>C [, D]<b>)</b> | ||
324 | </p> | ||
325 | |||
326 | <p class=description> | ||
327 | Low-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> | ||
333 | that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of | ||
334 | <tt>C..D</tt>, <em>before</em> decoding. | ||
335 | If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is the empty string | ||
336 | and <tt>B</tt> returns whatever couldn't be decoded. | ||
337 | </p> | ||
338 | |||
339 | <p class=note> | ||
340 | Note: The simplest use of this function is to decode a string from it's | ||
341 | Base64 transfer content encoding. | ||
342 | Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value. | ||
343 | </p> | ||
344 | |||
345 | <pre class=example> | ||
346 | print((mime.unb64("ZGllZ286cGFzc3dvcmQ="))) | ||
347 | --> diego:password | ||
348 | </pre> | ||
349 | |||
350 | <!-- unqp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
351 | |||
352 | <p class=name id="unqp"> | ||
353 | A, B = mime.<b>unqp(</b>C [, D]<b>)</b> | ||
354 | </p> | ||
355 | |||
356 | <p class=description> | ||
357 | Low-level filter to remove the Quoted-Printable transfer content encoding | ||
358 | from 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> | ||
364 | that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of | ||
365 | <tt>C..D</tt>, <em>before</em> decoding. | ||
366 | If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is augmented with | ||
367 | the encoding of the remaining bytes of <tt>C</tt>. | ||
368 | </p> | ||
369 | |||
370 | <p class=note> | ||
371 | Note: The simplest use of this function is to decode a string from it's | ||
372 | Quoted-Printable transfer content encoding. | ||
373 | Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value. | ||
374 | </p> | ||
375 | |||
376 | <pre class=example> | ||
377 | print((mime.qp("ma=E7=E3="))) | ||
378 | --> maçã | ||
379 | </pre> | ||
380 | |||
381 | <!-- wrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
382 | |||
383 | <p class=name id="wrp"> | ||
384 | A, m = mime.<b>wrp(</b>n [, B, length]<b>)</b> | ||
385 | </p> | ||
386 | |||
387 | <p class=description> | ||
388 | Low-level filter to break text into lines with CRLF marker. | ||
389 | Text 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 | ||
396 | line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes | ||
397 | left in the last line of <tt>A</tt>. | ||
398 | </p> | ||
399 | |||
400 | <p class=note> | ||
401 | Note: This function only breaks lines that are bigger than | ||
402 | <tt>length</tt> bytes. The resulting line length does not include the CRLF | ||
403 | marker. | ||
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> · | ||
414 | <a href="home.html#down">download</a> · | ||
415 | <a href="introduction.html">introduction</a> · | ||
416 | <a href="reference.html">reference</a> | ||
417 | </p> | ||
418 | <p> | ||
419 | <small> | ||
420 | Last modified by Diego Nehab on <br> | ||
421 | Sat 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 | ||
11 | h1, h2, h3, h4 { margin-left: 0em; } | 11 | h1, h2, h3, h4 { margin-left: 0em; } |
12 | 12 | ||
13 | |||
14 | h3 { padding-top: 1em; } | ||
15 | |||
13 | p { margin-left: 1em; } | 16 | p { margin-left: 1em; } |
14 | 17 | ||
15 | p.name { | 18 | p.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 |
39 | The <tt>smtp.lua</tt> module provides functionality to send e-mail | ||
40 | messages. The implementation conforms to the Simple Mail Transfer Protocol, | 39 | messages. 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>. |
42 | The other RFC of interest in this implementation is | 41 | Another RFC of interest is <a |
43 | <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>, | 42 | href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>, |
44 | which governs the Internet Message Format. | 43 | which governs the Internet Message Format. |
44 | Multipart messages (those that contain attatchments) are part | ||
45 | of the MIME standard, but described mainly | ||
46 | in <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">RFC | ||
47 | 2046</a> | ||
45 | 48 | ||
46 | </p> | 49 | <p> In the description below, good understanding of <a |
50 | href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters | ||
51 | sources and sinks</a> and the <a href=mime.html>MIME</a> module is | ||
52 | assumed. In fact, the SMTP module was the main reason for their | ||
53 | creation. </p> | ||
47 | 54 | ||
48 | <p> | 55 | <p> |
49 | MIME headers are represented as a Lua table in the form: | 56 | MIME headers are represented as a Lua table in the form: |
@@ -78,29 +85,56 @@ Note: MIME headers are independent of order. Therefore, there is no problem | |||
78 | in representing them in a Lua table. | 85 | in representing them in a Lua table. |
79 | </p> | 86 | </p> |
80 | 87 | ||
81 | <!-- mail +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 88 | <p> |
89 | The following constants can be set to control the default behaviour of | ||
90 | the SMTP module: | ||
91 | </p> | ||
82 | 92 | ||
83 | <p class=name id=mail> | 93 | <ul> |
84 | socket.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> | ||
104 | smtp.<b>send{</b><br> | ||
85 | from = <i>string</i>,<br> | 105 | from = <i>string</i>,<br> |
86 | rcpt = <i>string</i> or <i>string-table</i>,<br> | 106 | rcpt = <i>string</i> or <i>string-table</i>,<br> |
87 | body = <i>string</i>,<br> | 107 | source = <i>LTN12 source</i>,<br> |
88 | headers = <i>headers-table</i>,<br> | 108 | [server = <i>string</i>],<br> |
89 | server = <i>string</i><br> | 109 | [port = <i>string</i>]<br> |
110 | [domain = <i>string</i>],<br> | ||
111 | [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> |
94 | Sends a message to a recipient list. | 116 | Sends a message to a recipient list. Since sending messages is not as |
117 | simple as downloading an URL from a FTP or HTTP server, this function | ||
118 | doesn't have a simple interface. However, see the | ||
119 | <a href=#message><tt>message</tt></a> source factory for | ||
120 | a 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 | 124 | The 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 | ||
126 | address, or a string | ||
99 | in case there is just one recipient. | 127 | in case there is just one recipient. |
100 | The sender is given by the e-mail address <tt>from</tt>. | 128 | The contents of the message are given by a LTN12 <tt>source</tt>. Several |
101 | The message is composed by the optional MIME Headers <tt>headers</tt> | 129 | arguments are optional: |
102 | and 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 | ||
134 | local machine host name; | ||
135 | <li> <tt>step</tt>: LTN12 pump step function used to pass data from the | ||
136 | source 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> |
146 | Note: SMTP servers are can be very picky with the format of e-mail | ||
147 | addresses. To be safe, use only addresses of the form | ||
148 | "<tt><fulano@tecgraf.puc-rio.br></tt>" in the <tt>from</tt> and | ||
149 | <tt>rcpt</tt> arguments to the <tt>send</tt> function. In headers, e-mail | ||
150 | addresses can take whatever form you like. </p> | ||
151 | |||
152 | <p class=note> | ||
112 | Big note: There is a good deal of misconception with the use of the | 153 | Big note: There is a good deal of misconception with the use of the |
113 | destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>', | 154 | destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>', |
114 | and, more importantly, the '<tt>Bcc</tt>' headers. Do <em>not</em> add a | 155 | and, 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> |
120 | Only recipients specified in the recipient list will receive a copy of the | 161 | Only recipients specified in the <tt>rcpt</tt> list will receive a copy of the |
121 | message. Each recipient of an SMTP mail message receives a copy of the | 162 | message. Each recipient of an SMTP mail message receives a copy of the |
122 | message body along with the headers, and nothing more. The headers are | 163 | message body along with the headers, and nothing more. The headers |
123 | considered 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 |
124 | part of the message. | 165 | <tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em> |
166 | part 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> |
146 | The LuaSocket <tt>mail</tt> function does not interpret the headers you | 188 | The LuaSocket <tt>send</tt> function does not care or interpret the |
147 | pass to, but it gives you full control over what is sent and to whom | 189 | headers you send, but it gives you full control over what is sent and |
148 | it is sent: | 190 | to 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 | ||
217 | local 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. |
180 | headers = { | 225 | from = "<luasocket@tecgraf.puc-rio.br>" |
181 | to = "fulano@tecgraf.puc-rio.br", | ||
182 | cc = "beltrano@tecgraf.puc-rio.br", | ||
183 | subject = "LuaSocket test message" | ||
184 | } | ||
185 | |||
186 | from = "luasocket@tecgraf.puc-rio.br" | ||
187 | 226 | ||
188 | rcpt = { | 227 | rcpt = { |
189 | "fulano@tecgraf.puc-rio.br", | 228 | "<fulano@tecgraf.puc-rio.br>", |
190 | "beltrano@tecgraf.puc-rio.br", | 229 | "<beltrano@tecgraf.puc-rio.br>", |
191 | "sicrano@tecgraf.puc-rio.br" | 230 | "<sicrano@tecgraf.puc-rio.br>" |
192 | } | 231 | } |
193 | 232 | ||
194 | body = "This is a test message. Please ignore." | 233 | mesgt = { |
195 | 234 | headers = { | |
196 | server = "localhost" | 235 | to = "Fulano da Silva <fulano@tecgraf.puc-rio.br>", |
236 | cc = '"Beltrano F. Nunes" <beltrano@tecgraf.puc-rio.br>', | ||
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 | ||
198 | r, e = socket.smtp.mail{ | 242 | r, 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> | ||
252 | smtp.<b>message(</b>mesgt<b>)</b> | ||
253 | </p> | ||
254 | |||
255 | <p class=description> | ||
256 | Returns a LTN12 source that sends an SMTP message body, possibly multipart | ||
257 | (arbitrarily deep). | ||
258 | </p> | ||
259 | |||
260 | <p class=parameters> | ||
261 | The 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> | ||
268 | mesgt = {<br> | ||
269 | headers = <i>header-table</i>,<br> | ||
270 | body = <i>LTN12 source</i> or <i>string</i> or | ||
271 | <i>multipart-mesgt</i><br> | ||
272 | }<br> | ||
273 | <br> | ||
274 | multipart-mesgt = {<br> | ||
275 | preamble = <i>string</i><br> | ||
276 | [1] = <i>mesgt</i>,<br> | ||
277 | [2] = <i>mesgt</i>,<br> | ||
278 | ...<br> | ||
279 | [<i>n</i>] = <i>mesgt</i>,<br> | ||
280 | epilogue = <i>string</i>,<br> | ||
281 | }<br> | ||
282 | </tt></td></tr> | ||
283 | </table> | ||
284 | </blockquote> | ||
285 | |||
286 | <p class=parameters> | ||
287 | For a simple message, all that is needed is a set of <tt>headers</tt> | ||
288 | and the <tt>body</tt>. The message <tt>body</tt> can be given as a string | ||
289 | or as a LTN12 source. For multipart messages, the body is a table that | ||
290 | recursively defines each part as an independent message, plus a preamble | ||
291 | and an epilogue. | ||
292 | </p> | ||
293 | |||
294 | <p class=return> | ||
295 | The function returns an LTN12 source that produces the message contents as | ||
296 | defined by <tt>mesgt</tt>. Hopefuly, the following example will make | ||
297 | things clear. When in doubt, refer to the appropriate RFC as listed in the | ||
298 | introduction. </p> | ||
299 | |||
300 | <pre class=example> | ||
301 | -- load the smtp support and its friends | ||
302 | local smtp = require("smtp") | ||
303 | local mime = require("mime") | ||
304 | local 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. | ||
308 | source = smtp.message{ | ||
309 | headers = { | ||
310 | -- Remember that headers are *ignored* by smtp.send. | ||
311 | from = "Sicrano de Oliveira <sicrano@tecgraf.puc-rio.br>", | ||
312 | to = "Fulano da Silva <fulano@tecgraf.puc-rio.br>", | ||
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 | ||
353 | r, e = smtp.send{ | ||
354 | from = "<sicrano@tecgraf.puc-rio.br>", | ||
355 | rcpt = "<fulano@tecgraf.puc-rio.br>", | ||
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> |
39 | The <tt>socket</tt> namespace contains the namespace tables for all | 39 | The <tt>socket</tt> namespace contains the core functionality of LuaSocket. |
40 | LuaSocket modules as well as function that didn't belong in any specific | ||
41 | module, functions that are so commonly used that deserve a shortcut and a | ||
42 | few 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> |
48 | socket.<b>debug</b> | 45 | socket.<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> |
60 | socket.<b>protect(</b>function<b>)</b> | 57 | socket.<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> |
73 | The function an equivalent function that instead of throwing exceptoins, | 70 | Returns an equivalent function that instead of throwing exceptions, |
74 | returns <tt><b>nil</b></tt> followed by an error message. | 71 | returns <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 |
107 | on non-blocking TCP sockets. The function may return a socket as | 104 | on non-blocking TCP sockets. The function may return a socket as |
108 | writable even though the socket is <em>not</em> ready for sending. | 105 | writable 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 |
113 | parameter before a call to accept does <em>not</em> guarantee | 110 | parameter 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. |
115 | Use the <a href=tcp.html#timeout><tt>timeout</tt></a> | 112 | Use the <a href=tcp.html#settimeout><tt>settimeout</tt></a> |
116 | method or <tt>accept</tt> might block forever. | 113 | method 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> |
133 | Creates an | 130 | Creates an |
134 | <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a> | 131 | <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> |
135 | sink from a stream socket object. | 132 | sink 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> |
165 | Creates an | 162 | Creates an |
166 | <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a> | 163 | <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> |
167 | source from a stream socket object. | 164 | source 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> |
220 | socket.<b>version</b> | 217 | socket.<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> |
244 | client:<b>receive(</b>[pattern<sub>1</sub>, pattern<sub>2</sub>, | 244 | client:<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> |
249 | Reads data from a client object, according to the specified <em>read | 248 | Reads data from a client object, according to the specified <em>read |
250 | patterns</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. | 249 | pattern</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> |
254 | The 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> |
270 | The method returns one value for each pattern, followed by a single | 268 | If successful, the method returns the received pattern. In case of error, |
271 | error code that can be <b><tt>nil</tt></b> in case of success, the string | 269 | the 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 | 270 | can be the string '<tt>closed</tt>' in case the connection was |
273 | transmission was completed or the string '<tt>timeout</tt>' in case | 271 | closed before the transmission was completed or the string |
274 | there was a timeout during the operation. | 272 | '<tt>timeout</tt>' in case there was a timeout during the operation. |
273 | Also, after the error message, the function returns the partial result of | ||
274 | the transmission. | ||
275 | </p> | 275 | </p> |
276 | 276 | ||
277 | <p class=note> | 277 | <p class=note> |
278 | Note: 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 |
279 | to download before the error condition was met. | 279 | to support multiple patterns (but I have never seen this feature used) and |
280 | partial results used to be returned in the same way as successful results. | ||
281 | This 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> |
431 | Shuts down part of a full duplex connection. | 434 | Shuts 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> |
62 | socket.url.<b>absolute(</b>base, relative<b>)</b> | 62 | url.<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. | |||
79 | Note: The rules that | 79 | Note: The rules that |
80 | govern the composition are fairly complex, and are described in detail in | 80 | govern 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>. |
82 | The example bellow should give an idea of what are the rules. | 82 | The 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> |
117 | socket.url.<b>build(</b>parsed_url<b>)</b> | 117 | url.<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> |
138 | socket.url.<b>build_path(</b>segments, unsafe<b>)</b> | 138 | url.<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 | |||
157 | built <tt><path></tt> component. | 157 | built <tt><path></tt> component. |
158 | </p> | 158 | </p> |
159 | 159 | ||
160 | <!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
161 | |||
162 | <p class=name id="escape"> | ||
163 | url.<b>escape(</b>content<b>)</b> | ||
164 | </p> | ||
165 | |||
166 | <p class=description> | ||
167 | Applies the URL escaping content coding to a string | ||
168 | Each byte is encoded as a percent character followed | ||
169 | by the two byte hexadecimal representation of its integer | ||
170 | value. | ||
171 | </p> | ||
172 | |||
173 | <p class=parameters> | ||
174 | <tt>Content</tt> is the string to be encoded. | ||
175 | </p> | ||
176 | |||
177 | <p class=result> | ||
178 | The function returns the encoded string. | ||
179 | </p> | ||
180 | |||
181 | <pre class=example> | ||
182 | -- load url module | ||
183 | url = require("url") | ||
184 | |||
185 | code = 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> |
163 | socket.url.<b>parse(</b>url, default<b>)</b> | 192 | url.<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> |
199 | parsed_url = socket.url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there") | 228 | -- load url module |
229 | url = require("url") | ||
230 | |||
231 | parsed_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 | ||
209 | parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i") | 241 | parsed_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> |
225 | socket.url.<b>parse_path(</b>path<b>)</b> | 257 | url.<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 | |||
241 | of them. | 273 | of them. |
242 | </p> | 274 | </p> |
243 | 275 | ||
244 | <!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
245 | |||
246 | <p class=name id="escape"> | ||
247 | socket.url.<b>escape(</b>content<b>)</b> | ||
248 | </p> | ||
249 | |||
250 | <p class=description> | ||
251 | Applies the URL escaping content coding to a string | ||
252 | Each byte is encoded as a percent character followed | ||
253 | by the two byte hexadecimal representation of its integer | ||
254 | value. | ||
255 | </p> | ||
256 | |||
257 | <p class=parameters> | ||
258 | <tt>Content</tt> is the string to be encoded. | ||
259 | </p> | ||
260 | |||
261 | <p class=result> | ||
262 | The function returns the encoded string. | ||
263 | </p> | ||
264 | |||
265 | <pre class=example> | ||
266 | code = 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"> |
273 | socket.url.<b>unescape(</b>content<b>)</b> | 279 | url.<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 @@ | |||
1 | function 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") | ||
8 | end | ||
9 | |||
10 | load("socket") | ||
11 | load("url") | ||
12 | load("ltn12") | ||
13 | load("mime") | ||
14 | load("tp") | ||
15 | load("smtp") | ||
16 | load("http") | ||
17 | load("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") | |||
8 | http = require("http") | 8 | http = require("http") |
9 | ftp = require("ftp") | 9 | ftp = require("ftp") |
10 | url = require("url") | 10 | url = require("url") |
11 | ltn12 = require("ltn12") | ||
11 | 12 | ||
12 | -- formats a number of seconds into human readable form | 13 | -- formats a number of seconds into human readable form |
13 | function nicetime(s) | 14 | function 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 | ----------------------------------------------------------------------------- |
7 | require("socket") | 7 | socket = require("socket") |
8 | host = host or "*" | 8 | host = host or "*" |
9 | port1 = port1 or 8080 | 9 | port1 = port1 or 8080 |
10 | port2 = port2 or 8181 | 10 | port2 = 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 | \*-------------------------------------------------------------------------*/ |
19 | int aux_open(lua_State *L) | 18 | int 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 | \*-------------------------------------------------------------------------*/ |
27 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) | 26 | void 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 | \*-------------------------------------------------------------------------*/ | ||
49 | int 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; | ||
61 | error: | ||
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 | \*-------------------------------------------------------------------------*/ |
51 | void aux_add2group(lua_State *L, const char *classname, const char *groupname) | 70 | void 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 | \*-------------------------------------------------------------------------*/ |
63 | int aux_checkboolean(lua_State *L, int objidx) | 81 | int 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 | \*-------------------------------------------------------------------------*/ |
74 | void *aux_checkclass(lua_State *L, const char *classname, int objidx) | 91 | void *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 | \*-------------------------------------------------------------------------*/ |
89 | void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) | 105 | void *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 | \*-------------------------------------------------------------------------*/ |
103 | void aux_setclass(lua_State *L, const char *classname, int objidx) | 118 | void 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 | \*-------------------------------------------------------------------------*/ |
114 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) | 128 | void *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 | \*-------------------------------------------------------------------------*/ |
133 | void *aux_getclassudata(lua_State *L, const char *classname, int objidx) | 146 | void *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 | |||
43 | int aux_open(lua_State *L); | 37 | int aux_open(lua_State *L); |
44 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); | 38 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); |
45 | void aux_add2group(lua_State *L, const char *classname, const char *group); | 39 | void aux_add2group(lua_State *L, const char *classname, const char *group); |
@@ -49,5 +43,6 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx); | |||
49 | void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); | 43 | void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); |
50 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); | 44 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); |
51 | int aux_checkboolean(lua_State *L, int objidx); | 45 | int aux_checkboolean(lua_State *L, int objidx); |
46 | int 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); | |||
20 | static void buf_skip(p_buf buf, size_t count); | 19 | static void buf_skip(p_buf buf, size_t count); |
21 | static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent); | 20 | static 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 | |||
6 | static int global_try(lua_State *L); | ||
7 | static int global_protect(lua_State *L); | ||
8 | static int protected(lua_State *L); | ||
9 | |||
10 | static luaL_reg func[] = { | ||
11 | {"try", global_try}, | ||
12 | {"protect", global_protect}, | ||
13 | {NULL, NULL} | ||
14 | }; | ||
15 | |||
16 | /*-------------------------------------------------------------------------*\ | ||
17 | * Exception handling: try method | ||
18 | \*-------------------------------------------------------------------------*/ | ||
19 | static 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 | \*-------------------------------------------------------------------------*/ | ||
30 | static 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 | |||
40 | static 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 | \*-------------------------------------------------------------------------*/ | ||
49 | int 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 | |||
33 | int 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 | ----------------------------------------------------------------------------- |
12 | local socket = require("socket") | 12 | local socket = require("socket") |
13 | local ltn12 = require("ltn12") | 13 | local ltn12 = require("ltn12") |
@@ -17,10 +17,7 @@ local tp = require("tp") | |||
17 | ----------------------------------------------------------------------------- | 17 | ----------------------------------------------------------------------------- |
18 | -- Setup namespace | 18 | -- Setup namespace |
19 | ----------------------------------------------------------------------------- | 19 | ----------------------------------------------------------------------------- |
20 | local ftp = {} | 20 | _LOADED["ftp"] = getfenv(1) |
21 | -- make all module globals fall into namespace | ||
22 | setmetatable(ftp, { __index = _G }) | ||
23 | setfenv(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. |
34 | USER = "ftp" | 31 | USER = "ftp" |
35 | EMAIL = "anonymous@anonymous.org" | 32 | PASSWORD = "anonymous@anonymous.org" |
36 | -- block size used in transfers | ||
37 | BLOCKSIZE = 2048 | ||
38 | 33 | ||
39 | ----------------------------------------------------------------------------- | 34 | ----------------------------------------------------------------------------- |
40 | -- Low level FTP API | 35 | -- Low level FTP API |
@@ -42,7 +37,7 @@ BLOCKSIZE = 2048 | |||
42 | local metat = { __index = {} } | 37 | local metat = { __index = {} } |
43 | 38 | ||
44 | function open(server, port) | 39 | function 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) |
47 | end | 42 | end |
48 | 43 | ||
@@ -51,14 +46,17 @@ local function port(portt) | |||
51 | end | 46 | end |
52 | 47 | ||
53 | local function pasv(pasvt) | 48 | local 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 | ||
55 | end | 53 | end |
56 | 54 | ||
57 | function metat.__index:login(user, password) | 55 | function 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 | ----------------------------------------------------------------------------- |
184 | local function tput(putt) | 184 | local 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() |
193 | end | 193 | end |
194 | 194 | ||
195 | local default = { | 195 | local default = { |
@@ -198,15 +198,16 @@ local default = { | |||
198 | } | 198 | } |
199 | 199 | ||
200 | local function parse(u) | 200 | local 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 |
210 | end | 211 | end |
211 | 212 | ||
212 | local function sput(u, body) | 213 | local function sput(u, body) |
@@ -221,17 +222,17 @@ put = socket.protect(function(putt, body) | |||
221 | end) | 222 | end) |
222 | 223 | ||
223 | local function tget(gett) | 224 | local 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() |
232 | end | 233 | end |
233 | 234 | ||
234 | local function sget(u, body) | 235 | local 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) | |||
240 | end | 241 | end |
241 | 242 | ||
242 | get = socket.protect(function(gett) | 243 | get = 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 |
245 | end) | 246 | end) |
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 | ------------------------------------------------------------------------------- |
12 | local socket = require("socket") | 12 | local socket = require("socket") |
13 | local ltn12 = require("ltn12") | 13 | local ltn12 = require("ltn12") |
@@ -17,42 +17,68 @@ local url = require("url") | |||
17 | ----------------------------------------------------------------------------- | 17 | ----------------------------------------------------------------------------- |
18 | -- Setup namespace | 18 | -- Setup namespace |
19 | ------------------------------------------------------------------------------- | 19 | ------------------------------------------------------------------------------- |
20 | http = {} | 20 | _LOADED["http"] = getfenv(1) |
21 | -- make all module globals fall into namespace | ||
22 | setmetatable(http, { __index = _G }) | ||
23 | setfenv(1, http) | ||
24 | 21 | ||
25 | ----------------------------------------------------------------------------- | 22 | ----------------------------------------------------------------------------- |
26 | -- Program constants | 23 | -- Program constants |
27 | ----------------------------------------------------------------------------- | 24 | ----------------------------------------------------------------------------- |
28 | -- connection timeout in seconds | 25 | -- connection timeout in seconds |
29 | TIMEOUT = 60 | 26 | TIMEOUT = 4 |
30 | -- default port for document retrieval | 27 | -- default port for document retrieval |
31 | PORT = 80 | 28 | PORT = 80 |
32 | -- user agent field sent in request | 29 | -- user agent field sent in request |
33 | USERAGENT = socket.version | 30 | USERAGENT = socket.VERSION |
34 | -- block size used in transfers | 31 | -- block size used in transfers |
35 | BLOCKSIZE = 2048 | 32 | BLOCKSIZE = 2048 |
36 | 33 | ||
37 | ----------------------------------------------------------------------------- | 34 | ----------------------------------------------------------------------------- |
38 | -- Function return value selectors | 35 | -- Low level HTTP API |
39 | ----------------------------------------------------------------------------- | 36 | ----------------------------------------------------------------------------- |
40 | local function second(a, b) | 37 | local metat = { __index = {} } |
41 | return b | 38 | |
39 | function 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) | ||
45 | end | ||
46 | |||
47 | function 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)) | ||
42 | end | 50 | end |
43 | 51 | ||
44 | local function third(a, b, c) | 52 | function 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 | ||
46 | end | 59 | end |
47 | 60 | ||
48 | local function receive_headers(reqt, respt, tmp) | 61 | function 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)) | ||
69 | end | ||
70 | |||
71 | function 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) | ||
75 | end | ||
76 | |||
77 | function 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 | ||
73 | end | 100 | end |
74 | 101 | ||
75 | local function receive_body(reqt, respt, tmp) | 102 | function 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)) | ||
92 | end | 113 | end |
93 | 114 | ||
94 | local function send_headers(sock, headers) | 115 | function 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")) | ||
101 | end | 117 | end |
102 | 118 | ||
103 | local 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 | 122 | local function uri(reqt) |
107 | return 1 | 123 | local u = reqt |
108 | end | 124 | if not reqt.proxy and not PROXY then |
109 | 125 | u = { | |
110 | local 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 | ||
115 | end | ||
116 | |||
117 | local 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) |
129 | end | 133 | end |
130 | 134 | ||
131 | local function send_request(reqt, respt, tmp) | 135 | local 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 | ||
151 | end | ||
152 | |||
153 | local 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)) | ||
167 | end | ||
168 | |||
169 | local 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 | ||
181 | end | 145 | end |
182 | 146 | ||
183 | local function parse_url(reqt, respt, tmp) | 147 | local 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 | ||
199 | end | 162 | end |
200 | 163 | ||
201 | -- forward declaration | 164 | local function shouldredirect(reqt, respt) |
202 | local 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) | ||
169 | end | ||
203 | 170 | ||
204 | local function should_authorize(reqt, respt, tmp) | 171 | local 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 |
210 | end | 177 | end |
211 | 178 | ||
212 | local function clone(headers) | 179 | local 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 | ||
219 | end | 185 | end |
220 | 186 | ||
221 | local function authorize(reqt, respt, tmp) | 187 | local requestp, authorizep, redirectp |
222 | local headers = clone(reqt.headers) or {} | 188 | |
223 | headers["authorization"] = "Basic " .. | 189 | function 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 | ||
235 | end | 209 | end |
236 | 210 | ||
237 | local function should_redirect(reqt, respt, tmp) | 211 | function 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) | ||
242 | end | 215 | end |
243 | 216 | ||
244 | local function redirect(reqt, respt, tmp) | 217 | function 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 |
260 | end | ||
261 | |||
262 | local function skip_continue(reqt, respt, tmp) | ||
263 | if respt.code == 100 then | ||
264 | receive_status(reqt, respt, tmp) | ||
265 | end | ||
266 | end | ||
267 | |||
268 | -- execute a request of through an exception | ||
269 | function 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 | ||
286 | end | ||
287 | |||
288 | function 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 |
294 | end | 235 | end |
295 | 236 | ||
296 | function get(u) | 237 | request = socket.protect(requestp) |
238 | |||
239 | get = 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 |
304 | end | 247 | end) |
305 | 248 | ||
306 | function post(u, body) | 249 | post = 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 |
317 | end | 260 | end) |
318 | 261 | ||
319 | return http | 262 | return http |
@@ -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 | ----------------------------------------------------------------------------- |
11 | local ltn12 = {} | 11 | _LOADED["ltn12"] = getfenv(1) |
12 | setmetatable(ltn12, { __index = _G }) | 12 | |
13 | setfenv(1, ltn12) | ||
14 | filter = {} | 13 | filter = {} |
15 | source = {} | 14 | source = {} |
16 | sink = {} | 15 | sink = {} |
@@ -19,10 +18,6 @@ pump = {} | |||
19 | -- 2048 seems to be better in windows... | 18 | -- 2048 seems to be better in windows... |
20 | BLOCKSIZE = 2048 | 19 | BLOCKSIZE = 2048 |
21 | 20 | ||
22 | local function shift(a, b, c) | ||
23 | return b, c | ||
24 | end | ||
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 |
58 | end | 55 | end |
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 |
154 | end | 153 | end |
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 |
171 | end | 172 | end |
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 | \*-------------------------------------------------------------------------*/ | ||
40 | static int global_skip(lua_State *L); | ||
41 | static int global_unload(lua_State *L); | ||
42 | static int base_open(lua_State *L); | ||
43 | |||
44 | /*-------------------------------------------------------------------------*\ | ||
45 | * Modules and functions | ||
39 | \*-------------------------------------------------------------------------*/ | 46 | \*-------------------------------------------------------------------------*/ |
40 | static const luaL_reg mod[] = { | 47 | static 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 | ||
59 | static luaL_reg func[] = { | ||
60 | {"skip", global_skip}, | ||
61 | {"__unload", global_unload}, | ||
62 | {NULL, NULL} | ||
63 | }; | ||
64 | |||
65 | /*-------------------------------------------------------------------------*\ | ||
66 | * Skip a few arguments | ||
67 | \*-------------------------------------------------------------------------*/ | ||
68 | static 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 | \*-------------------------------------------------------------------------*/ | ||
77 | static int global_unload(lua_State *L) { | ||
78 | sock_close(); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /*-------------------------------------------------------------------------*\ | ||
83 | * Setup basic stuff. | ||
84 | \*-------------------------------------------------------------------------*/ | ||
85 | static 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 | \*-------------------------------------------------------------------------*/ |
55 | LUASOCKET_API int luaopen_socket(lua_State *L) { | 119 | LUASOCKET_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" | ||
28 | LUASOCKET_API int luaopen_socket(lua_State *L); | 29 | LUASOCKET_API int luaopen_socket(lua_State *L); |
29 | 30 | ||
30 | #endif /* LUASOCKET_H */ | 31 | #endif /* LUASOCKET_H */ |
@@ -76,7 +76,17 @@ static UC b64unbase[256]; | |||
76 | \*-------------------------------------------------------------------------*/ | 76 | \*-------------------------------------------------------------------------*/ |
77 | MIME_API int luaopen_mime(lua_State *L) | 77 | MIME_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); |
@@ -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" | ||
22 | MIME_API int luaopen_mime(lua_State *L); | 23 | MIME_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 | ----------------------------------------------------------------------------- | ||
12 | local open = assert(loadlib("mime", "luaopen_mime")) | ||
13 | local mime = assert(open()) | ||
14 | |||
15 | ----------------------------------------------------------------------------- | ||
16 | -- Load other required modules | 9 | -- Load other required modules |
17 | ----------------------------------------------------------------------------- | 10 | ----------------------------------------------------------------------------- |
11 | local mime = requirelib("mime", "luaopen_mime", getfenv(1)) | ||
18 | local ltn12 = require("ltn12") | 12 | local 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 |
24 | setmetatable(mime, { __index = _G }) | ||
25 | setfenv(1, mime) | ||
26 | 18 | ||
27 | -- encode, decode and wrap algorithm tables | 19 | -- encode, decode and wrap algorithm tables |
28 | encodet = {} | 20 | encodet = {} |
@@ -48,7 +40,7 @@ end | |||
48 | 40 | ||
49 | encodet['quoted-printable'] = function(mode) | 41 | encodet['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") |
52 | end | 44 | end |
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 | \*=========================================================================*/ |
20 | static int meth_set(lua_State *L); | 18 | static int getfd(lua_State *L); |
21 | static int meth_isset(lua_State *L); | 19 | static int dirty(lua_State *L); |
22 | static int c_select(lua_State *L); | 20 | static int collect_fd(lua_State *L, int tab, int max_fd, int itab, fd_set *set); |
21 | static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); | ||
22 | static void return_fd(lua_State *L, fd_set *set, int max_fd, | ||
23 | int itab, int tab, int start); | ||
24 | static void make_assoc(lua_State *L, int tab); | ||
23 | static int global_select(lua_State *L); | 25 | static int global_select(lua_State *L); |
24 | 26 | ||
25 | /* fd_set object methods */ | ||
26 | static 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 */ |
33 | static luaL_reg func[] = { | 28 | static 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 | \*-------------------------------------------------------------------------*/ |
44 | int select_open(lua_State *L) | 39 | int 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 | \*-------------------------------------------------------------------------*/ |
64 | static int global_select(lua_State *L) | 50 | static 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 | \*=========================================================================*/ |
92 | static int meth_set(lua_State *L) | 84 | static 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 | ||
100 | static int meth_isset(lua_State *L) | 98 | static 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 | /*=========================================================================*\ | 111 | static 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; |
112 | static 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 | |||
138 | static 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 | |||
163 | static 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 | |||
176 | static 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 | ----------------------------------------------------------------------------- | ||
13 | local open = assert(loadlib("smtp", "luaopen_smtp")) | ||
14 | local smtp = assert(open()) | ||
15 | |||
16 | ----------------------------------------------------------------------------- | ||
17 | -- Load other required modules | ||
18 | ----------------------------------------------------------------------------- | 11 | ----------------------------------------------------------------------------- |
12 | local smtp = requirelib("smtp") | ||
19 | local socket = require("socket") | 13 | local socket = require("socket") |
20 | local ltn12 = require("ltn12") | 14 | local ltn12 = require("ltn12") |
21 | local tp = require("tp") | 15 | local 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 |
27 | setmetatable(smtp, { __index = _G }) | ||
28 | setfenv(1, smtp) | ||
29 | 21 | ||
22 | -- timeout for connection | ||
23 | TIMEOUT = 60 | ||
30 | -- default server used to send e-mails | 24 | -- default server used to send e-mails |
31 | SERVER = "localhost" | 25 | SERVER = "localhost" |
32 | -- default port | 26 | -- default port |
@@ -94,9 +88,7 @@ function metat.__index:send(mailt) | |||
94 | end | 88 | end |
95 | 89 | ||
96 | function open(server, port) | 90 | function 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) |
101 | end | 93 | end |
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 | ||
134 | end | 132 | end |
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 |
184 | local function adjust_headers(mesgt) | 182 | local 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 | ||
202 | end | 204 | end |
203 | 205 | ||
204 | --------------------------------------------------------------------------- | 206 | --------------------------------------------------------------------------- |
205 | -- High level SMTP API | 207 | -- High level SMTP API |
206 | ----------------------------------------------------------------------------- | 208 | ----------------------------------------------------------------------------- |
207 | send = socket.protect(function(mailt) | 209 | send = 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() |
213 | end) | 215 | end) |
214 | 216 | ||
215 | return smtp | 217 | return 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 | ----------------------------------------------------------------------------- |
10 | local open = assert(loadlib("luasocket", "luaopen_socket")) | 10 | local socket = requirelib("luasocket", "luaopen_socket", getfenv(1)) |
11 | local 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) | |||
116 | end | 116 | end |
117 | 117 | ||
118 | socket.sourcet["until-closed"] = function(sock) | 118 | socket.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 | }) |
132 | end | 135 | end |
133 | 136 | ||
@@ -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 */ |
42 | static luaL_reg tcp[] = { | 39 | static 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 | \*=========================================================================*/ |
@@ -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 | ----------------------------------------------------------------------------- |
12 | local socket = require("socket") | 11 | local socket = require("socket") |
12 | local ltn12 = require("ltn12") | ||
13 | 13 | ||
14 | ----------------------------------------------------------------------------- | 14 | ----------------------------------------------------------------------------- |
15 | -- Setup namespace | 15 | -- Setup namespace |
16 | ----------------------------------------------------------------------------- | 16 | ----------------------------------------------------------------------------- |
17 | tp = {} | 17 | _LOADED["tp"] = getfenv(1) |
18 | setmetatable(tp, { __index = _G }) | ||
19 | setfenv(1, tp) | ||
20 | 18 | ||
19 | ----------------------------------------------------------------------------- | ||
20 | -- Program constants | ||
21 | ----------------------------------------------------------------------------- | ||
21 | TIMEOUT = 60 | 22 | TIMEOUT = 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) |
24 | local function get_reply(control) | 28 | local 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 |
40 | print(reply) | ||
41 | return code, reply | 44 | return code, reply |
42 | end | 45 | end |
43 | 46 | ||
@@ -46,6 +49,7 @@ local metat = { __index = {} } | |||
46 | 49 | ||
47 | function metat.__index:check(ok) | 50 | function metat.__index:check(ok) |
48 | local code, reply = get_reply(self.control) | 51 | local code, reply = get_reply(self.control) |
52 | print(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() | |||
103 | end | 107 | end |
104 | 108 | ||
105 | -- connect with server and return control object | 109 | -- connect with server and return control object |
106 | function connect(host, port) | 110 | connect = 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) |
111 | end | 115 | end) |
112 | 116 | ||
113 | return tp | 117 | return tp |
@@ -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 | ----------------------------------------------------------------------------- |
12 | local url = {} | 12 | _LOADED["url"] = getfenv(1) |
13 | setmetatable(url, { __index = _G }) | ||
14 | setfenv(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 | ||
7 | require("http") | 7 | socket = require("socket") |
8 | http = require("http") | ||
9 | mime = require("mime") | ||
10 | url = require("url") | ||
11 | ltn12 = require("ltn12") | ||
8 | 12 | ||
9 | dofile("testsupport.lua") | 13 | dofile("testsupport.lua") |
10 | 14 | ||
11 | local host, proxy, request, response, index_file | 15 | local host, proxy, request, response, index_file |
12 | local ignore, expect, index, prefix, cgiprefix, index_crlf | 16 | local ignore, expect, index, prefix, cgiprefix, index_crlf |
13 | 17 | ||
14 | socket.http.TIMEOUT = 10 | 18 | http.TIMEOUT = 10 |
15 | 19 | ||
16 | local t = socket.time() | 20 | local 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) |
62 | end | 66 | end |
@@ -64,16 +68,16 @@ end | |||
64 | ------------------------------------------------------------------------ | 68 | ------------------------------------------------------------------------ |
65 | io.write("testing request uri correctness: ") | 69 | io.write("testing request uri correctness: ") |
66 | local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" | 70 | local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" |
67 | local back, h, c, e = socket.http.get("http://" .. host .. forth) | 71 | local back, h, c, e = http.get("http://" .. host .. forth) |
68 | if not back then fail(e) end | 72 | if not back then fail(e) end |
69 | back = socket.url.parse(back) | 73 | back = url.parse(back) |
70 | if similar(back.query, "this+is+the+query+string") then print("ok") | 74 | if similar(back.query, "this+is+the+query+string") then print("ok") |
71 | else fail(back.query) end | 75 | else fail(back.query) end |
72 | 76 | ||
73 | ------------------------------------------------------------------------ | 77 | ------------------------------------------------------------------------ |
74 | io.write("testing query string correctness: ") | 78 | io.write("testing query string correctness: ") |
75 | forth = "this+is+the+query+string" | 79 | forth = "this+is+the+query+string" |
76 | back = socket.http.get("http://" .. host .. cgiprefix .. | 80 | back = http.get("http://" .. host .. cgiprefix .. |
77 | "/query-string?" .. forth) | 81 | "/query-string?" .. forth) |
78 | if similar(back, forth) then print("ok") | 82 | if similar(back, forth) then print("ok") |
79 | else fail("failed!") end | 83 | else fail("failed!") end |
@@ -149,7 +153,7 @@ check_request(request, expect, ignore) | |||
149 | 153 | ||
150 | ------------------------------------------------------------------------ | 154 | ------------------------------------------------------------------------ |
151 | io.write("testing simple post function: ") | 155 | io.write("testing simple post function: ") |
152 | back = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index) | 156 | back = http.post("http://" .. host .. cgiprefix .. "/cat", index) |
153 | assert(back == index) | 157 | assert(back == index) |
154 | 158 | ||
155 | ------------------------------------------------------------------------ | 159 | ------------------------------------------------------------------------ |
@@ -278,30 +282,6 @@ ignore = { | |||
278 | check_request(request, expect, ignore) | 282 | check_request(request, expect, ignore) |
279 | 283 | ||
280 | ------------------------------------------------------------------------ | 284 | ------------------------------------------------------------------------ |
281 | io.write("testing host not found: ") | ||
282 | request = { | ||
283 | url = "http://wronghost/does/not/exist" | ||
284 | } | ||
285 | local c, e = socket.connect("wronghost", 80) | ||
286 | expect = { | ||
287 | error = e | ||
288 | } | ||
289 | ignore = {} | ||
290 | check_request(request, expect, ignore) | ||
291 | |||
292 | ------------------------------------------------------------------------ | ||
293 | io.write("testing invalid url: ") | ||
294 | request = { | ||
295 | url = host .. prefix | ||
296 | } | ||
297 | local c, e = socket.connect("", 80) | ||
298 | expect = { | ||
299 | error = e | ||
300 | } | ||
301 | ignore = {} | ||
302 | check_request(request, expect, ignore) | ||
303 | |||
304 | ------------------------------------------------------------------------ | ||
305 | io.write("testing document not found: ") | 285 | io.write("testing document not found: ") |
306 | request = { | 286 | request = { |
307 | url = "http://" .. host .. "/wrongdocument.html" | 287 | url = "http://" .. host .. "/wrongdocument.html" |
@@ -397,29 +377,16 @@ ignore = { | |||
397 | check_request(request, expect, ignore) | 377 | check_request(request, expect, ignore) |
398 | 378 | ||
399 | ------------------------------------------------------------------------ | 379 | ------------------------------------------------------------------------ |
400 | io.write("testing wrong scheme: ") | ||
401 | request = { | ||
402 | url = "wrong://" .. host .. cgiprefix .. "/cat", | ||
403 | method = "GET" | ||
404 | } | ||
405 | expect = { | ||
406 | error = "unknown scheme 'wrong'" | ||
407 | } | ||
408 | ignore = { | ||
409 | } | ||
410 | check_request(request, expect, ignore) | ||
411 | |||
412 | ------------------------------------------------------------------------ | ||
413 | local body | 380 | local body |
414 | io.write("testing simple get function: ") | 381 | io.write("testing simple get function: ") |
415 | body = socket.http.get("http://" .. host .. prefix .. "/index.html") | 382 | body = http.get("http://" .. host .. prefix .. "/index.html") |
416 | assert(body == index) | 383 | assert(body == index) |
417 | print("ok") | 384 | print("ok") |
418 | 385 | ||
419 | ------------------------------------------------------------------------ | 386 | ------------------------------------------------------------------------ |
420 | io.write("testing HEAD method: ") | 387 | io.write("testing HEAD method: ") |
421 | socket.http.TIMEOUT = 1 | 388 | http.TIMEOUT = 1 |
422 | response = socket.http.request { | 389 | response = 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) | |||
427 | print("ok") | 394 | print("ok") |
428 | 395 | ||
429 | ------------------------------------------------------------------------ | 396 | ------------------------------------------------------------------------ |
397 | io.write("testing host not found: ") | ||
398 | local c, e = socket.connect("wronghost", 80) | ||
399 | local r, re = http.request{url = "http://wronghost/does/not/exist"} | ||
400 | assert(r == nil and e == re) | ||
401 | r, re = http.get("http://wronghost/does/not/exist") | ||
402 | assert(r == nil and e == re) | ||
403 | print("ok") | ||
404 | |||
405 | ------------------------------------------------------------------------ | ||
406 | io.write("testing invalid url: ") | ||
407 | local c, e = socket.connect("", 80) | ||
408 | local r, re = http.request{url = host .. prefix} | ||
409 | assert(r == nil and e == re) | ||
410 | r, re = http.get(host .. prefix) | ||
411 | assert(r == nil and e == re) | ||
412 | print("ok") | ||
413 | |||
414 | ------------------------------------------------------------------------ | ||
430 | print("passed all tests") | 415 | print("passed all tests") |
416 | os.remove("err") | ||
431 | 417 | ||
432 | print(string.format("done in %.2fs", socket.time() - t)) | 418 | print(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 |
70 | end | 70 | end |
71 | 71 | ||
72 | if not socket.debug then | 72 | if not socket.DEBUG then |
73 | fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") | 73 | fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") |
74 | end | 74 | end |
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 @@ | |||
1 | require("smtp") | 1 | -- load the smtp support and its friends |
2 | require("mime") | 2 | local smtp = require("smtp") |
3 | local mime = require("mime") | ||
4 | local ltn12 = require("ltn12") | ||
3 | 5 | ||
4 | mesgt = { | 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>", | 8 | source = 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 | ||
47 | print(socket.smtp.send { | 51 | -- finally send it |
52 | r, 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 @@ | |||
1 | require"url" | 1 | socket = require("socket") |
2 | socket.url = require("url") | ||
2 | dofile("testsupport.lua") | 3 | dofile("testsupport.lua") |
3 | 4 | ||
4 | local check_build_url = function(parsed) | 5 | local check_build_url = function(parsed) |