diff --git a/FIX b/FIX index 3d0b3de..84504f0 100644 --- a/FIX +++ b/FIX @@ -1,3 +1,14 @@ +fix smtp.send hang on source error +add create field to FTP and SMTP and fix HTTP ugliness +clean timeout argument to open functions in SMTP, HTTP and FTP +eliminate globals from namespaces created by module(). + + + + + + + url.absolute was not working when base_url was already parsed http.request was redirecting even when the location header was empty tcp{client}:shutdown() was checking for group instead of class. diff --git a/TODO b/TODO index cb5ac32..b2a167e 100644 --- a/TODO +++ b/TODO @@ -1,22 +1,13 @@ +new instalation scheme??? +test empty socket.select no windows. +bug by mathew percival? + +arranjar um jeito de fazer multipart/alternative + what the hell does __unload do? -clean timeout argument to open functions in SMTP, HTTP and FTP -add create field to FTP and SMTP -talk about new create field in HTTP, FTP and SMTP -talk about the non-blocking connect in the manual + test it on Windows!!! -think about a dispatcher. - - it creates a server and receives a function that will do the work on - received connections - - this function is invoked with the client socket - - it calls special send and receive functions that yield on timeout - -think about how to extend http, ftp, smtp to use special send and receive -functions for non-blocking so they can be used in the context of the -dispatcher! - -adjust manual for new sock:send returns. - leave code for losers that don't have nanosleep ftp.send/recv return bytes transfered? @@ -41,7 +32,16 @@ testar os options! - proteger ou atomizar o conjunto (timedout, receive), (timedout, send) - inet_ntoa também é uma merda. -eliminate globals from namespaces created by module(). - * BUG NO SET DO TINYIRC!!! SINISTRO. * _VERSION, _DEBUG, etc. +* talk about new create field in HTTP, FTP and SMTP +* talk about the non-blocking connect in the manual +* think about how to extend http, ftp, smtp to use special send and receive +* functions for non-blocking so they can be used in the context of the +* dispatcher! +* adjust manual for new sock:send returns. +* think about a dispatcher. + * - it creates a server and receives a function that will do the work on + * received connections + * - this function is invoked with the client socket + * - it calls special send and receive functions that yield on timeout diff --git a/config b/config index dcc3955..da9fdf4 100644 --- a/config +++ b/config @@ -21,7 +21,7 @@ LUALIB= #------ # Compat-5.1 directory # -COMPAT=compat-5.1r3 +COMPAT=compat-5.1r4 #------ # Top of your Lua installation diff --git a/doc/ftp.html b/doc/ftp.html index 7860c27..8b7ed96 100644 --- a/doc/ftp.html +++ b/doc/ftp.html @@ -106,6 +106,7 @@ ftp.get{
  [port = number,]
  [type = string,]
  [step = LTN12 pump step,]
+  [create = function]
}

@@ -138,7 +139,9 @@ authentication. Defaults to "ftp:anonymous@anonymous.org";
  • step: LTN12 pump step function used to pass data from the -server to the sink. Defaults to the LTN12 pump.step function. +server to the sink. Defaults to the LTN12 pump.step function; +
  • accept: An optional function to be used instead of +socket.tcp when the communications socket is created.

    @@ -188,6 +191,7 @@ ftp.put{
      [port = number,]
      [type = string,]
      [step = LTN12 pump step,]
    +  [create = function]
    }

    @@ -220,7 +224,9 @@ authentication. Defaults to "ftp:anonymous@anonymous.org";
  • step: LTN12 pump step function used to pass data from the -server to the sink. Defaults to the LTN12 pump.step function. +server to the sink. Defaults to the LTN12 pump.step function; +
  • accept: An optional function to be used instead of +socket.tcp when the communications socket is created.

    diff --git a/doc/http.html b/doc/http.html index af58571..27942b1 100644 --- a/doc/http.html +++ b/doc/http.html @@ -131,7 +131,8 @@ http.request{
      [source = LTN12 source],
      [step = LTN12 pump step,]
      [proxy = string,]
    -  [redirect = boolean]
    +  [redirect = boolean,]
    +  [create = function]
    }

    @@ -178,7 +179,9 @@ pump step function used to move data. Defaults to the LTN12 pump.step function.
  • proxy: The URL of a proxy server to use. Defaults to no proxy;
  • redirect: Set to false to prevent the -function from automatically following 301 or 302 server redirect messages. +function from automatically following 301 or 302 server redirect messages; +
  • accept: An optional function to be used instead of +socket.tcp when the communications socket is created.

    diff --git a/doc/index.html b/doc/index.html index 933fa5f..e596f8e 100644 --- a/doc/index.html +++ b/doc/index.html @@ -168,6 +168,7 @@ support.

  • Improved: tcp:send(data, i, j) to return (i+sent-1). This is great for non-blocking I/O, but might break some code;
  • Improved: HTTP, SMTP, and FTP functions to accept a new field create that overrides the function used to create socket objects; +
  • Fixed: smtp.send was hanging on errors returned by LTN12 sources;
  • Fixed: url.absolute() to work when base_url is in parsed form;
  • Fixed: http.request() not to redirect when the location @@ -194,7 +195,8 @@ with descriptor 0 to be ignored (Renato Maia);
  • Fixed: "Bug" that caused gethostbyname to crash under VMS (Renato Maia);
  • Fixed: tcp:send("") to return 0 bytes sent (Alexander Marinov); -
  • Improved: socket.DEBUG and socket.VERSION became socket._DEBUGs and socket._VERSION for uniformity with other libraries. +
  • Improved: socket.DEBUG and socket.VERSION became socket._DEBUGs and socket._VERSION for uniformity with other libraries; +
  • Improved: socket.select now works on empty sets on Windows. diff --git a/doc/installation.html b/doc/installation.html index 59815fa..62604ed 100644 --- a/doc/installation.html +++ b/doc/installation.html @@ -71,11 +71,11 @@ Here is the standard LuaSocket distribution directory structure:

     <ROOT>/compat-5.1.lua
    -<ROOT>/socket.lua
    -<ROOT>/lsocket.dll
    -<ROOT>/mime.lua
    -<ROOT>/lmime.dll
     <ROOT>/ltn12.lua
    +<ROOT>/mime/init.lua
    +<ROOT>/mime/core.dll
    +<ROOT>/socket/init.lua
    +<ROOT>/socket/core.dll
     <ROOT>/socket/http.lua
     <ROOT>/socket/tp.lua
     <ROOT>/socket/ftp.lua
    @@ -83,10 +83,8 @@ Here is the standard LuaSocket distribution directory structure:

    <ROOT>/socket/url.lua
    -

    Naturally, on Unix systems, lsocket.dll and lmime.dll -would be replaced by lsocket.so and lmime.so. In Mac OS -X, they would be replaced by lsocket.dylib and -lmime.dylib.

    +

    Naturally, on Unix systems, core.dll +would be replaced by core.so.

    In order for the interpreter to find all LuaSocket components, three environment variables need to be set. The first environment variable tells diff --git a/doc/reference.html b/doc/reference.html index fd5140b..b4b2f1d 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -142,7 +142,7 @@ Support, Manual">

    Socket
    -_DEBUG, +_DEBUG, dns, gettime, newtry, @@ -155,7 +155,7 @@ Support, Manual"> tcp, try, udp, -_VERSION. +_VERSION.
    diff --git a/doc/smtp.html b/doc/smtp.html index bd18bfa..1e1523b 100644 --- a/doc/smtp.html +++ b/doc/smtp.html @@ -127,6 +127,7 @@ smtp.send{
      [port = number,]
      [domain = string,]
      [step = LTN12 pump step,]
    +  [create = function]
    }

    @@ -138,6 +139,7 @@ doesn't have a simple interface. However, see the a very powerful way to define the message contents.

    +

    The sender is given by the e-mail address in the from field. Rcpt is a Lua table with one entry for each recipient e-mail @@ -158,7 +160,9 @@ local machine host name;

  • step: LTN12 pump step function used to pass data from the -source to the server. Defaults to the LTN12 pump.step function. +source to the server. Defaults to the LTN12 pump.step function; +
  • accept: An optional function to be used instead of +socket.tcp when the communications socket is created.

    @@ -167,7 +171,7 @@ If successful, the function returns 1. Otherwise, the function returns

    -Note: SMTP servers are can be very picky with the format of e-mail +Note: SMTP servers can be very picky with the format of e-mail addresses. To be safe, use only addresses of the form "<fulano@example.com>" in the from and rcpt arguments to the send function. In headers, e-mail diff --git a/doc/socket.html b/doc/socket.html index 5cc7157..54b12cb 100644 --- a/doc/socket.html +++ b/doc/socket.html @@ -80,12 +80,12 @@ socket.connect(address, port [, locaddr, locport]) This function is a shortcut that creates and returns a TCP client object connected to a remote host at a given port. Optionally, the user can also specify the local address and port to bind -(locaddr and locport). +(locaddr and locport).

    -

    +

    socket._DEBUG

    @@ -372,7 +372,7 @@ c = socket.try(socket.connect("localhost", 80)) -

    +

    socket._VERSION

    diff --git a/doc/tcp.html b/doc/tcp.html index ae8efd5..5c810de 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -79,7 +79,7 @@ reported by nil followed by a message describing the error.

    Note: calling socket.select with a server object in -the receive parameter before a call to accept does +the recvt parameter before a call to accept does not guarantee accept will return immediately. Use the settimeout method or accept might block until another client shows up. @@ -111,7 +111,7 @@ method returns nil followed by an error message.

    Note: The function socket.bind -is available and is a shortcut for the creation server sockets. +is available and is a shortcut for the creation of server sockets.

    @@ -173,8 +173,11 @@ is available and is a shortcut for the creation of client sockets.

    Note: Starting with LuaSocket 2.0, the settimeout -method affects the behavior of connect, causing it to return in case of -a timeout. +method affects the behavior of connect, causing it to return +with an error in case of a timeout. If that happens, you can still call socket.select with the socket in the +sendt table. The socket will be writable when the connection is +stablished.

    @@ -328,11 +331,11 @@ substring to be sent.

    -If successful, the method returns the number of bytes accepted by -the transport layer. In case of error, the method returns +If successful, the method returns the number of bytes sent. +In case of error, the method returns nil, followed by an error message, followed by the -partial number of bytes accepted by the transport layer. -The error message can be 'closed' in case +index of the first character within [i, j] that has not been sent yet +(you might want to try again from then on). The error message can be 'closed' in case the connection was closed before the transmission was completed or the string 'timeout' in case there was a timeout during the operation. @@ -433,7 +436,7 @@ of bandwidth.

    Received is a number with the new number of bytes received. Sent is a number with the new number of bytes sent. -Age is the new age in seconds +Age is the new age in seconds.

    diff --git a/etc/check-links-nb.lua b/etc/check-links-nb.lua index 7e8df1b..c379e9a 100644 --- a/etc/check-links-nb.lua +++ b/etc/check-links-nb.lua @@ -84,17 +84,22 @@ function newcreate(thread) first = (first or 1) - 1 local result, error while true do + -- tell dispatcher we want to keep sending before we + -- yield control + sending:insert(tcp) + -- return control to dispatcher + -- if upon return the dispatcher tells us we timed out, + -- return an error to whoever called us + if coroutine.yield() == "timeout" then + return nil, "timeout" + end + -- mark time we started waiting + context[tcp].last = socket.gettime() + -- try sending result, error, first = tcp:send(data, first+1, last) - if error == "timeout" then - -- tell dispatcher we want to keep sending - sending:insert(tcp) - -- mark time we started waiting - context[tcp].last = socket.gettime() - -- return control to dispatcher - if coroutine.yield() == "timeout" then - return nil, "timeout" - end - else return result, error, first end + -- if we are done, or there was an unexpected error, + -- break away from loop + if error ~= "timeout" then return result, error, first end end end, -- receive in non-blocking mode and yield on timeout @@ -102,28 +107,35 @@ function newcreate(thread) local error, partial = "timeout", "" local value while true do + -- tell dispatcher we want to keep receiving before we + -- yield control + receiving:insert(tcp) + -- return control to dispatcher + -- if upon return the dispatcher tells us we timed out, + -- return an error to whoever called us + if coroutine.yield() == "timeout" then + return nil, "timeout" + end + -- mark time we started waiting + context[tcp].last = socket.gettime() + -- try receiving value, error, partial = tcp:receive(pattern, partial) - if error == "timeout" then - -- tell dispatcher we want to keep receiving - receiving:insert(tcp) - -- mark time we started waiting - context[tcp].last = socket.gettime() - -- return control to dispatcher - if coroutine.yield() == "timeout" then - return nil, "timeout" - end - else return value, error, partial end + -- if we are done, or there was an unexpected error, + -- break away from loop + if error ~= "timeout" then return value, error, partial end end end, -- connect in non-blocking mode and yield on timeout connect = function(self, host, port) local result, error = tcp:connect(host, port) + -- mark time we started waiting + context[tcp].last = socket.gettime() if error == "timeout" then -- tell dispatcher we will be able to write uppon connection sending:insert(tcp) - -- mark time we started waiting - context[tcp].last = socket.gettime() -- return control to dispatcher + -- if upon return the dispatcher tells us we have a + -- timeout, just abort if coroutine.yield() == "timeout" then return nil, "timeout" end @@ -148,10 +160,10 @@ function newcreate(thread) end -- get the status of a URL, non-blocking -function getstatus(from, link) +function getstatus(link) local parsed = url.parse(link, {scheme = "file"}) if parsed.scheme == "http" then - local thread = coroutine.create(function(thread, from, link) + local thread = coroutine.create(function(thread, link) local r, c, h, s = http.request{ method = "HEAD", url = link, @@ -162,7 +174,7 @@ function getstatus(from, link) nthreads = nthreads - 1 end) nthreads = nthreads + 1 - assert(coroutine.resume(thread, thread, from, link)) + assert(coroutine.resume(thread, thread, link)) end end @@ -190,6 +202,8 @@ function dispatch() local now = socket.gettime() for who, data in pairs(context) do if data.last and now - data.last > TIMEOUT then + sending:remove(who) + receiving:remove(who) assert(coroutine.resume(context[who].thread, "timeout")) end end @@ -206,14 +220,15 @@ function readfile(path) else return nil, error end end -function retrieve(u) +function load(u) local parsed = url.parse(u, { scheme = "file" }) local body, headers, code, error local base = u if parsed.scheme == "http" then body, code, headers = http.request(u) if code == 200 then - base = base or headers.location + -- if there was a redirect, update base to reflect it + base = headers.location or base end if not body then error = code @@ -241,12 +256,13 @@ function getlinks(body, base) return links end -function checklinks(from) - local base, body, error = retrieve(from) +function checklinks(address) + local base, body, error = load(address) if not body then print(error) return end + print("Checking ", base) local links = getlinks(body, base) for _, link in ipairs(links) do - getstatus(from, link) + getstatus(link) end end @@ -255,8 +271,7 @@ if table.getn(arg) < 1 then print("Usage:\n luasocket check-links.lua {}") exit() end -for _, a in ipairs(arg) do - print("Checking ", a) - checklinks(url.absolute("file:", a)) +for _, address in ipairs(arg) do + checklinks(url.absolute("file:", address)) end dispatch() diff --git a/etc/check-links.lua b/etc/check-links.lua index 79e6a54..9d837e4 100644 --- a/etc/check-links.lua +++ b/etc/check-links.lua @@ -37,7 +37,7 @@ function retrieve(u) if parsed.scheme == "http" then body, code, headers = http.request(u) if code == 200 then - base = base or headers.location + base = headers.location or base end if not body then error = code diff --git a/etc/dict.lua b/etc/dict.lua index 76b254a..62d6913 100644 --- a/etc/dict.lua +++ b/etc/dict.lua @@ -16,6 +16,7 @@ local url = require("socket.url") local tp = require("socket.tp") module("socket.dict") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Globals @@ -151,4 +152,3 @@ get = socket.protect(function(gett) else return tget(gett) end end) ---getmetatable(_M).__index = nil diff --git a/etc/lp.lua b/etc/lp.lua index 2ba7954..66a9692 100644 --- a/etc/lp.lua +++ b/etc/lp.lua @@ -15,6 +15,7 @@ local string = require("string") local socket = require("socket") local ltn12 = require("ltn12") module("socket.lp") +getmetatable(_M).__index = nil -- default port PORT = 515 @@ -318,4 +319,3 @@ query = socket.protect(function(p) return data end) ---getmetatable(_M).__index = nil diff --git a/etc/tftp.lua b/etc/tftp.lua index 60658f7..b974db8 100644 --- a/etc/tftp.lua +++ b/etc/tftp.lua @@ -16,6 +16,7 @@ local socket = require("socket") local ltn12 = require("ltn12") local url = require("socket.url") module("socket.tftp") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Program constants @@ -153,4 +154,3 @@ get = socket.protect(function(gett) else return tget(gett) end end) ---getmetatable(_M).__index = nil diff --git a/makefile b/makefile new file mode 100644 index 0000000..234e162 --- /dev/null +++ b/makefile @@ -0,0 +1,52 @@ +#------ +# Load configuration +# +include config + +#------ +# Hopefully no need to change anything below this line +# +INSTALL_SOCKET=$(INSTALL_TOP)/socket +INSTALL_MIME=$(INSTALL_TOP)/mime + +all clean: + cd src; $(MAKE) $@ + +#------ +# Files to install +# +TO_SOCKET:= \ + socket.lua \ + http.lua \ + url.lua \ + tp.lua \ + ftp.lua \ + smtp.lua + +TO_TOP:= \ + ltn12.lua + +TO_MIME:= \ + $(MIME_SO) \ + mime.lua + +#------ +# Install LuaSocket according to recommendation +# +install: all + cd src; mkdir -p $(INSTALL_TOP) + cd src; $(INSTALL_DATA) $(COMPAT)/compat-5.1.lua $(INSTALL_TOP) + cd src; $(INSTALL_DATA) ltn12.lua $(INSTALL_TOP) + cd src; mkdir -p $(INSTALL_SOCKET) + cd src; $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET) + cd src; $(INSTALL_DATA) $(TO_SOCKET) $(INSTALL_SOCKET) + cd src; cd $(INSTALL_SOCKET); $(INSTALL_LINK) -s $(SOCKET_SO) core.$(EXT) + cd src; cd $(INSTALL_SOCKET); $(INSTALL_LINK) -s socket.lua init.lua + cd src; mkdir -p $(INSTALL_MIME) + cd src; $(INSTALL_DATA) $(TO_MIME) $(INSTALL_MIME) + cd src; cd $(INSTALL_MIME); $(INSTALL_LINK) -s $(MIME_SO) core.$(EXT) + cd src; cd $(INSTALL_MIME); $(INSTALL_LINK) -s mime.lua init.lua + +#------ +# End of makefile +# diff --git a/src/ftp.lua b/src/ftp.lua index 841df5f..226e04c 100644 --- a/src/ftp.lua +++ b/src/ftp.lua @@ -17,6 +17,7 @@ local url = require("socket.url") local tp = require("socket.tp") local ltn12 = require("ltn12") module("socket.ftp") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Program constants @@ -35,8 +36,8 @@ PASSWORD = "anonymous@anonymous.org" ----------------------------------------------------------------------------- local metat = { __index = {} } -function open(server, port) - local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) +function open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, create, TIMEOUT)) local f = base.setmetatable({ tp = tp }, metat) -- make sure everything gets closed in an exception f.try = socket.newtry(function() f:close() end) @@ -199,7 +200,7 @@ end local function tput(putt) putt = override(putt) socket.try(putt.host, "missing hostname") - local f = open(putt.host, putt.port) + local f = open(putt.host, putt.port, putt.create) f:greet() f:login(putt.user, putt.password) if putt.type then f:type(putt.type) end @@ -242,7 +243,7 @@ end) local function tget(gett) gett = override(gett) socket.try(gett.host, "missing hostname") - local f = open(gett.host, gett.port) + local f = open(gett.host, gett.port, gett.create) f:greet() f:login(gett.user, gett.password) if gett.type then f:type(gett.type) end @@ -264,7 +265,7 @@ command = socket.protect(function(cmdt) cmdt = override(cmdt) socket.try(cmdt.host, "missing hostname") socket.try(cmdt.command, "missing command") - local f = open(cmdt.host, cmdt.port) + local f = open(cmdt.host, cmdt.port, cmdt.create) f:greet() f:login(cmdt.user, cmdt.password) f.try(f.tp:command(cmdt.command, cmdt.argument)) @@ -278,4 +279,3 @@ get = socket.protect(function(gett) else return tget(gett) end end) ---getmetatable(_M).__index = nil diff --git a/src/http.lua b/src/http.lua index 91c52da..9434d97 100644 --- a/src/http.lua +++ b/src/http.lua @@ -16,6 +16,7 @@ local string = require("string") local base = _G local table = require("table") module("socket.http") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Program constants @@ -105,26 +106,16 @@ end ----------------------------------------------------------------------------- local metat = { __index = {} } --- default connect function, respecting the timeout -local function connect(host, port, create) - local c, e = (create or socket.tcp)() - if not c then return nil, e end - c:settimeout(TIMEOUT) - local r, e = c:connect(host, port or PORT) - if not r then - c:close() - return nil, e - end - return c -end - function open(host, port, create) -- create socket with user connect function, or with default - local c = socket.try(connect(host, port, create)) - -- create our http request object, pointing to the socket + local c = socket.try(create or socket.tcp)() local h = base.setmetatable({ c = c }, metat) - -- make sure the object close gets called on exception + -- create finalized try h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(TIMEOUT)) + h.try(c:connect(host, port or PORT)) + -- here everything worked return h end @@ -134,11 +125,11 @@ function metat.__index:sendrequestline(method, uri) end function metat.__index:sendheaders(headers) + local h = "\r\n" for i, v in base.pairs(headers) do - self.try(self.c:send(i .. ": " .. v .. "\r\n")) + h = i .. ": " .. v .. "\r\n" .. h end - -- mark end of request headers - self.try(self.c:send("\r\n")) + self.try(self.c:send(h)) return 1 end @@ -213,7 +204,7 @@ local function adjustheaders(headers, host) ["te"] = "trailers" } -- override with user headers - for i,v in pairs(headers or lower) do + for i,v in base.pairs(headers or lower) do lower[string.lower(i)] = v end return lower @@ -232,7 +223,7 @@ local function adjustrequest(reqt) local nreqt = reqt.url and url.parse(reqt.url, default) or {} local t = url.parse(reqt.url, default) -- explicit components override url - for i,v in pairs(reqt) do nreqt[i] = v end + for i,v in base.pairs(reqt) do nreqt[i] = v end socket.try(nreqt.host, "invalid host '" .. base.tostring(nreqt.host) .. "'") -- compute uri if user hasn't overriden nreqt.uri = reqt.uri or adjusturi(nreqt) @@ -276,11 +267,11 @@ function tauthorize(reqt) return trequest(reqt) end -function tredirect(reqt, headers) - return trequest { +function tredirect(reqt, location) + local result, code, headers, status = trequest { -- the RFC says the redirect URL has to be absolute, but some -- servers do not respect that - url = url.absolute(reqt, headers["location"]), + url = url.absolute(reqt, location), source = reqt.source, sink = reqt.sink, headers = reqt.headers, @@ -288,6 +279,9 @@ function tredirect(reqt, headers) nredirects = (reqt.nredirects or 0) + 1, connect = reqt.connect } + -- pass location header back as a hint we redirected + headers.location = headers.location or location + return result, code, headers, status end function trequest(reqt) @@ -301,7 +295,7 @@ function trequest(reqt) headers = h:receiveheaders() if shouldredirect(reqt, code, headers) then h:close() - return tredirect(reqt, headers) + return tredirect(reqt, headers.location) elseif shouldauthorize(reqt, code) then h:close() return tauthorize(reqt) @@ -332,4 +326,3 @@ request = socket.protect(function(reqt, body) else return trequest(reqt) end end) ---getmetatable(_M).__index = nil diff --git a/src/ltn12.lua b/src/ltn12.lua index fbc9dce..633e0d7 100644 --- a/src/ltn12.lua +++ b/src/ltn12.lua @@ -12,6 +12,7 @@ local string = require("string") local table = require("table") local base = _G module("ltn12") +getmetatable(_M).__index = nil filter = {} source = {} @@ -134,8 +135,6 @@ function source.rewind(src) end end -local print = print - -- chains a source with a filter function source.chain(src, f) base.assert(src and f) @@ -258,7 +257,8 @@ end function pump.step(src, snk) local chunk, src_err = src() local ret, snk_err = snk(chunk, src_err) - return chunk and ret and not src_err and not snk_err, src_err or snk_err + if chunk and ret then return 1 + else return nil, src_err or snk_err end end -- pumps all data from a source to a sink, using a step function @@ -267,8 +267,10 @@ function pump.all(src, snk, step) step = step or pump.step while true do local ret, err = step(src, snk) - if not ret then return not err, err end + if not ret then + if err then return nil, err + else return 1 end + end end end ---getmetatable(_M).__index = nil diff --git a/src/luasocket.c b/src/luasocket.c index ed26b1f..94ea05b 100644 --- a/src/luasocket.c +++ b/src/luasocket.c @@ -108,7 +108,7 @@ static int base_open(lua_State *L) { /*-------------------------------------------------------------------------*\ * Initializes all library modules. \*-------------------------------------------------------------------------*/ -LUASOCKET_API int luaopen_socketcore(lua_State *L) { +LUASOCKET_API int luaopen_socket_core(lua_State *L) { int i; base_open(L); for (i = 0; mod[i].name; i++) mod[i].func(L); diff --git a/src/makefile b/src/makefile new file mode 100644 index 0000000..7ef18bf --- /dev/null +++ b/src/makefile @@ -0,0 +1,87 @@ +#------ +# Load configuration +# +include ../config + +#------ +# Hopefully no need to change anything below this line +# + +#------ +# Modules belonging to socket-core +# +SOCKET_OBJS:= \ + luasocket.o \ + timeout.o \ + buffer.o \ + io.o \ + auxiliar.o \ + options.o \ + inet.o \ + tcp.o \ + udp.o \ + except.o \ + select.o \ + $(COMPAT)/compat-5.1.o \ + usocket.o + +#------ +# Modules belonging mime-core +# +MIME_OBJS:=\ + mime.o \ + $(COMPAT)/compat-5.1.o + +#------ +# Modules belonging unix (local domain sockets) +# +UNIX_OBJS:=\ + buffer.o \ + auxiliar.o \ + options.o \ + timeout.o \ + io.o \ + usocket.o \ + unix.o + +all: $(SOCKET_SO) $(MIME_SO) + +$(SOCKET_SO): $(SOCKET_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +$(MIME_SO): $(MIME_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +$(UNIX_SO): $(UNIX_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +#------ +# List of dependencies +# +auxiliar.o: auxiliar.c auxiliar.h +buffer.o: buffer.c buffer.h io.h timeout.h +except.o: except.c except.h +inet.o: inet.c inet.h socket.h io.h timeout.h usocket.h +io.o: io.c io.h timeout.h +luasocket.o: luasocket.c luasocket.h auxiliar.h except.h timeout.h \ + buffer.h io.h inet.h socket.h usocket.h tcp.h udp.h select.h +mime.o: mime.c mime.h +options.o: options.c auxiliar.h options.h socket.h io.h timeout.h \ + usocket.h inet.h +select.o: select.c socket.h io.h timeout.h usocket.h select.h +tcp.o: tcp.c auxiliar.h socket.h io.h timeout.h usocket.h inet.h \ + options.h tcp.h buffer.h +timeout.o: timeout.c auxiliar.h timeout.h +udp.o: udp.c auxiliar.h socket.h io.h timeout.h usocket.h inet.h \ + options.h udp.h +unix.o: unix.c auxiliar.h socket.h io.h timeout.h usocket.h options.h \ + unix.h buffer.h +usocket.o: usocket.c socket.h io.h timeout.h usocket.h + +clean: + rm -f $(SOCKET_SO) $(SOCKET_OBJS) + rm -f $(MIME_SO) $(UNIX_SO) $(MIME_OBJS) $(UNIX_OBJS) + +#------ +# End of makefile configuration +# diff --git a/src/mime.lua b/src/mime.lua index e112f8a..af42dcd 100644 --- a/src/mime.lua +++ b/src/mime.lua @@ -14,6 +14,7 @@ local mime = require("mime.core") local io = require("io") local string = require("string") module("mime") +getmetatable(_M).__index = nil -- encode, decode and wrap algorithm tables encodet = {} @@ -84,5 +85,3 @@ end function stuff() return ltn12.filter.cycle(dot, 2) end - ---getmetatable(_M).__index = nil diff --git a/src/smtp.lua b/src/smtp.lua index 6850f7f..46df1ab 100644 --- a/src/smtp.lua +++ b/src/smtp.lua @@ -18,6 +18,7 @@ local tp = require("socket.tp") local ltn12 = require("ltn12") local mime = require("mime") module("socket.smtp") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Program constants @@ -111,12 +112,12 @@ function metat.__index:send(mailt) self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) end -function open(server, port) - local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) +function open(server, port, create) + local tp = socket.try(tp.connect(server or SERVER, port or PORT, + create, TIMEOUT)) local s = base.setmetatable({tp = tp}, metat) -- make sure tp is closed if we get an exception s.try = socket.newtry(function() - if s.tp:command("QUIT") then s.tp:check("2..") end s:close() end) return s @@ -165,10 +166,9 @@ end local function send_source(mesgt) -- set content-type if user didn't override if not mesgt.headers or not mesgt.headers["content-type"] then - coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n') - end + coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n\r\n') + else coroutine.yield("\r\n") end -- finish headers - coroutine.yield("\r\n") -- send body from source while true do local chunk, err = mesgt.body() @@ -182,21 +182,20 @@ end local function send_string(mesgt) -- set content-type if user didn't override if not mesgt.headers or not mesgt.headers["content-type"] then - coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n') - end - -- finish headers - coroutine.yield("\r\n") + coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n\r\n') + else coroutine.yield("\r\n") end -- send body from string coroutine.yield(mesgt.body) - end --- yield the headers one by one +-- yield the headers all at once local function send_headers(mesgt) if mesgt.headers then + local h = "" for i,v in base.pairs(mesgt.headers) do - coroutine.yield(i .. ':' .. v .. "\r\n") + h = i .. ': ' .. v .. "\r\n" .. h end + coroutine.yield(h) end end @@ -237,12 +236,10 @@ end -- High level SMTP API ----------------------------------------------------------------------------- send = socket.protect(function(mailt) - local s = open(mailt.server, mailt.port) + local s = open(mailt.server, mailt.port, mailt.create) local ext = s:greet(mailt.domain) s:auth(mailt.user, mailt.password, ext) s:send(mailt) s:quit() return s:close() end) - ---getmetatable(_M).__index = nil diff --git a/src/socket.lua b/src/socket.lua index 13b474d..be01667 100644 --- a/src/socket.lua +++ b/src/socket.lua @@ -12,6 +12,7 @@ local string = require("string") local math = require("math") local socket = require("socket.core") module("socket") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Exported auxiliar functions @@ -131,5 +132,3 @@ sourcet["default"] = sourcet["until-closed"] source = choose(sourcet) --- clear globals from namespace -getmetatable(_M).__index = nil diff --git a/src/ssl.c b/src/ssl.c deleted file mode 100644 index 8e2ce00..0000000 --- a/src/ssl.c +++ /dev/null @@ -1,70 +0,0 @@ -/*=========================================================================*\ -* Simple client SSL support -* LuaSocket toolkit -* -* RCS ID: $Id$ -\*=========================================================================*/ -#include -#include - -#include "ssl.h" - -/*=========================================================================*\ -* Internal function prototypes -\*=========================================================================*/ -static int global_wrap(lua_State *L); - -/* functions in library namespace */ -static luaL_reg func[] = { - {"wrap", global_create}, - {NULL, NULL} -}; - -static luaL_reg wrap[] = { - {"__tostring", aux_tostring}, - {"__gc", meth_close}, - {"close", meth_close}, - {"receive", meth_receive}, - {"send", meth_send}, - {NULL, NULL} -}; - -static luaL_reg owned[] = { - {"__tostring", aux_tostring}, - {NULL, NULL} -}; - -/*-------------------------------------------------------------------------*\ -* Initializes module -\*-------------------------------------------------------------------------*/ -int ssl_open(lua_State *L) -{ - aux_newclass(L, "ssl{wraper}", wrap); - aux_newclass(L, "ssl{owned}", owned); - lua_pushstring(L, "ssl") - lua_newtable(L); - luaL_openlib(L, NULL, func, 0); - lua_settable(L, -3); - return 0; -} - -/*=========================================================================*\ -* Library functions -\*=========================================================================*/ -/*-------------------------------------------------------------------------*\ -* Wraps a tcp object into an SSL object -\*-------------------------------------------------------------------------*/ -static int global_wrap(lua_State *L) { - p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{client}", 1); - /* change class of tcp object */ - aux_setclass(L, "ssl{owned}", 1); - /* create wrapper */ - p_wrap wrap = (p_wrap) lua_newuserdata(L, sizeof(t_wrap)); - /* lock reference */ - lua_pushvalue(L, 1); - wrap->ref = lua_ref(L, 1); - /* initialize wrapper */ - wrap->tcp = tcp; - io_init(&tcp->io, wrap_send, wrap_recv, wrap); - return 1; -} diff --git a/src/ssl.h b/src/ssl.h deleted file mode 100644 index 13ce97b..0000000 --- a/src/ssl.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SSL_H -#define SSL_H -/*=========================================================================*\ -* Simple client SSL support -* LuaSocket toolkit -* -* This is just a simple example to show how to extend LuaSocket -* -* RCS ID: $Id$ -\*=========================================================================*/ -#include -#include - -#include "buffer.h" -#include "timeout.h" -#include "socket.h" -#include "tcp.h" - -typedef struct t_wrap_ { - p_tcp tcp; - SSL* ssl; - int ref; -} t_wrap; - -typedef t_wrap *p_wrap; - -int ssl_open(lua_State *L); - -#endif /* SSL_H */ diff --git a/src/tp.lua b/src/tp.lua index c51d123..7823699 100644 --- a/src/tp.lua +++ b/src/tp.lua @@ -13,6 +13,7 @@ local string = require("string") local socket = require("socket") local ltn12 = require("ltn12") module("socket.tp") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Program constants @@ -98,7 +99,8 @@ end function metat.__index:source(source, step) local sink = socket.sink("keep-open", self.c) - return ltn12.pump.all(source, sink, step or ltn12.pump.step) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err end -- closes the underlying c @@ -108,8 +110,8 @@ function metat.__index:close() end -- connect with server and return c object -function connect(host, port, timeout) - local c, e = socket.tcp() +function connect(host, port, create, timeout) + local c, e = (create or socket.tcp()) if not c then return nil, e end c:settimeout(timeout or TIMEOUT) local r, e = c:connect(host, port) @@ -120,4 +122,3 @@ function connect(host, port, timeout) return base.setmetatable({c = c}, metat) end ---getmetatable(_M).__index = nil diff --git a/src/url.lua b/src/url.lua index 0db111b..bd39d98 100644 --- a/src/url.lua +++ b/src/url.lua @@ -12,6 +12,7 @@ local string = require("string") local base = _G local table = require("table") module("socket.url") +getmetatable(_M).__index = nil ----------------------------------------------------------------------------- -- Encodes a string into its escaped hexadecimal representation @@ -279,4 +280,3 @@ function build_path(parsed, unsafe) return path end ---getmetatable(_M).__index = nil diff --git a/src/wsocket.c b/src/wsocket.c index 2cbd073..c4c51b4 100644 --- a/src/wsocket.c +++ b/src/wsocket.c @@ -74,7 +74,10 @@ int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) { double t = tm_get(tm); tv.tv_sec = (int) t; tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); - return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); + if (n <= 0) { + Sleep(1000*t); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); } /*-------------------------------------------------------------------------*\ diff --git a/test/testmesg.lua b/test/testmesg.lua index 5350921..37e8c11 100644 --- a/test/testmesg.lua +++ b/test/testmesg.lua @@ -48,18 +48,19 @@ source = smtp.message{ } } ---[[ -sink = ltn12.sink.file(io.stdout) -ltn12.pump.all(source, sink) -]] +function filter(s) + if s then io.write(s) end + return s +end --- finally send it r, e = smtp.send{ rcpt = {"", "" }, from = "", - source = source, - server = "mail.cs.princeton.edu" + source = ltn12.source.chain(source, filter), + --server = "mail.cs.princeton.edu" + server = "localhost", + port = 2525 } print(r, e)