From b1f352bf67aa1110742c548452f20fd220ac4b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Sat, 17 Mar 2018 14:47:40 +0000 Subject: [PATCH] Embed luasocket as a static library Add mobdebug lua script to allow debugging premake with ZeroBrane Studio Add --debugger command line option to enable debugging --- contrib/luasocket/.gitignore | 16 + contrib/luasocket/.travis.yml | 54 + contrib/luasocket/FIX | 28 + contrib/luasocket/LICENSE | 20 + contrib/luasocket/Lua51.props | 28 + contrib/luasocket/Lua52.props | 28 + contrib/luasocket/NEW | 44 + contrib/luasocket/README | 11 + contrib/luasocket/TODO | 81 + contrib/luasocket/WISH | 22 + contrib/luasocket/doc/dns.html | 183 + contrib/luasocket/doc/ftp.html | 288 ++ contrib/luasocket/doc/http.html | 335 ++ contrib/luasocket/doc/index.html | 215 ++ contrib/luasocket/doc/installation.html | 127 + contrib/luasocket/doc/introduction.html | 333 ++ contrib/luasocket/doc/ltn12.html | 430 +++ contrib/luasocket/doc/lua05.ppt | Bin 0 -> 304128 bytes contrib/luasocket/doc/luasocket.png | Bin 0 -> 11732 bytes contrib/luasocket/doc/mime.html | 477 +++ contrib/luasocket/doc/reference.css | 55 + contrib/luasocket/doc/reference.html | 260 ++ contrib/luasocket/doc/smtp.html | 418 +++ contrib/luasocket/doc/socket.html | 479 +++ contrib/luasocket/doc/tcp.html | 721 ++++ contrib/luasocket/doc/udp.html | 596 ++++ contrib/luasocket/doc/url.html | 328 ++ contrib/luasocket/etc/README | 89 + contrib/luasocket/etc/b64.lua | 19 + contrib/luasocket/etc/check-links.lua | 111 + contrib/luasocket/etc/check-memory.lua | 17 + contrib/luasocket/etc/cookie.lua | 88 + contrib/luasocket/etc/dict.lua | 151 + contrib/luasocket/etc/dispatch.lua | 307 ++ contrib/luasocket/etc/eol.lua | 13 + contrib/luasocket/etc/forward.lua | 65 + contrib/luasocket/etc/get.lua | 141 + contrib/luasocket/etc/links | 17 + contrib/luasocket/etc/lp.lua | 323 ++ contrib/luasocket/etc/qp.lua | 23 + contrib/luasocket/etc/tftp.lua | 154 + contrib/luasocket/gem/ex1.lua | 4 + contrib/luasocket/gem/ex10.lua | 17 + contrib/luasocket/gem/ex11.lua | 7 + contrib/luasocket/gem/ex12.lua | 34 + contrib/luasocket/gem/ex2.lua | 11 + contrib/luasocket/gem/ex3.lua | 15 + contrib/luasocket/gem/ex4.lua | 5 + contrib/luasocket/gem/ex5.lua | 15 + contrib/luasocket/gem/ex6.lua | 14 + contrib/luasocket/gem/ex7.lua | 16 + contrib/luasocket/gem/ex8.lua | 5 + contrib/luasocket/gem/ex9.lua | 3 + contrib/luasocket/gem/gem.c | 54 + contrib/luasocket/gem/gt.b64 | 206 ++ contrib/luasocket/gem/input.bin | Bin 0 -> 11732 bytes contrib/luasocket/gem/ltn012.tex | 695 ++++ contrib/luasocket/gem/luasocket.png | Bin 0 -> 11732 bytes contrib/luasocket/gem/myps2pdf | 113 + contrib/luasocket/gem/t1.lua | 25 + contrib/luasocket/gem/t1lf.txt | 5 + contrib/luasocket/gem/t2.lua | 36 + contrib/luasocket/gem/t2.txt | 4 + contrib/luasocket/gem/t2gt.qp | 5 + contrib/luasocket/gem/t3.lua | 25 + contrib/luasocket/gem/t4.lua | 10 + contrib/luasocket/gem/t5.lua | 30 + contrib/luasocket/gem/test.lua | 46 + contrib/luasocket/linux.cmd | 1 + contrib/luasocket/logo.ps | 210 ++ contrib/luasocket/ltn012.wiki | 393 +++ contrib/luasocket/ltn013.wiki | 194 ++ contrib/luasocket/luasocket-scm-0.rockspec | 105 + contrib/luasocket/macosx.cmd | 1 + contrib/luasocket/makefile.dist | 139 + contrib/luasocket/mingw.cmd | 1 + contrib/luasocket/premake5.lua | 30 + .../rockspec/luasocket-3.0rc2-1.rockspec | 105 + contrib/luasocket/samples/README | 50 + contrib/luasocket/samples/cddb.lua | 46 + contrib/luasocket/samples/daytimeclnt.lua | 22 + contrib/luasocket/samples/echoclnt.lua | 23 + contrib/luasocket/samples/echosrvr.lua | 28 + contrib/luasocket/samples/listener.lua | 25 + contrib/luasocket/samples/lpr.lua | 51 + contrib/luasocket/samples/mclisten.lua | 18 + contrib/luasocket/samples/mcsend.lua | 20 + contrib/luasocket/samples/talker.lua | 20 + contrib/luasocket/samples/tinyirc.lua | 89 + contrib/luasocket/src/auxiliar.c | 158 + contrib/luasocket/src/auxiliar.h | 48 + contrib/luasocket/src/buffer.c | 273 ++ contrib/luasocket/src/buffer.h | 45 + contrib/luasocket/src/compat.c | 19 + contrib/luasocket/src/compat.h | 11 + contrib/luasocket/src/except.c | 133 + contrib/luasocket/src/except.h | 38 + contrib/luasocket/src/ftp.lua | 329 ++ contrib/luasocket/src/headers.lua | 104 + contrib/luasocket/src/http.lua | 382 +++ contrib/luasocket/src/inet.c | 543 +++ contrib/luasocket/src/inet.h | 50 + contrib/luasocket/src/io.c | 30 + contrib/luasocket/src/io.h | 65 + contrib/luasocket/src/ltn12.lua | 309 ++ contrib/luasocket/src/luasocket.c | 114 + contrib/luasocket/src/luasocket.h | 29 + contrib/luasocket/src/mbox.lua | 92 + contrib/luasocket/src/mime.c | 721 ++++ contrib/luasocket/src/mime.h | 29 + contrib/luasocket/src/mime.lua | 90 + contrib/luasocket/src/options.c | 374 ++ contrib/luasocket/src/options.h | 62 + contrib/luasocket/src/pierror.h | 28 + contrib/luasocket/src/select.c | 220 ++ contrib/luasocket/src/select.h | 15 + contrib/luasocket/src/serial.c | 172 + contrib/luasocket/src/smtp.lua | 256 ++ contrib/luasocket/src/socket.h | 78 + contrib/luasocket/src/socket.lua | 149 + contrib/luasocket/src/tcp.c | 451 +++ contrib/luasocket/src/tcp.h | 35 + contrib/luasocket/src/timeout.c | 228 ++ contrib/luasocket/src/timeout.h | 29 + contrib/luasocket/src/tp.lua | 134 + contrib/luasocket/src/udp.c | 483 +++ contrib/luasocket/src/udp.h | 31 + contrib/luasocket/src/unix.c | 70 + contrib/luasocket/src/unix.h | 30 + contrib/luasocket/src/unixdgram.c | 408 +++ contrib/luasocket/src/unixdgram.h | 20 + contrib/luasocket/src/unixstream.c | 357 ++ contrib/luasocket/src/unixstream.h | 21 + contrib/luasocket/src/url.lua | 309 ++ contrib/luasocket/src/usocket.c | 451 +++ contrib/luasocket/src/usocket.h | 59 + contrib/luasocket/src/wsocket.c | 433 +++ contrib/luasocket/src/wsocket.h | 33 + contrib/luasocket/test/README | 14 + contrib/luasocket/test/auth/.htaccess | 4 + contrib/luasocket/test/auth/.htpasswd | 1 + contrib/luasocket/test/auth/index.html | 3002 +++++++++++++++++ contrib/luasocket/test/cgi/cat | 6 + contrib/luasocket/test/cgi/cat-index-html | 5 + contrib/luasocket/test/cgi/env | 5 + contrib/luasocket/test/cgi/query-string | 4 + contrib/luasocket/test/cgi/redirect-loop | 3 + contrib/luasocket/test/cgi/request-uri | 4 + contrib/luasocket/test/dicttest.lua | 5 + contrib/luasocket/test/excepttest.lua | 30 + contrib/luasocket/test/find-connect-limit | 32 + contrib/luasocket/test/ftptest.lua | 122 + contrib/luasocket/test/hello.lua | 3 + contrib/luasocket/test/httptest.lua | 441 +++ contrib/luasocket/test/index.html | 3002 +++++++++++++++++ contrib/luasocket/test/ltn12test.lua | 299 ++ contrib/luasocket/test/luasocket.png | Bin 0 -> 11732 bytes contrib/luasocket/test/mimetest.lua | 299 ++ contrib/luasocket/test/smtptest.lua | 259 ++ contrib/luasocket/test/stufftest.lua | 21 + contrib/luasocket/test/tcp-getoptions | 41 + contrib/luasocket/test/test_bind.lua | 6 + contrib/luasocket/test/test_getaddrinfo.lua | 15 + contrib/luasocket/test/test_socket_error.lua | 31 + contrib/luasocket/test/testclnt.lua | 803 +++++ contrib/luasocket/test/testmesg.lua | 96 + contrib/luasocket/test/testsrvr.lua | 20 + contrib/luasocket/test/testsupport.lua | 37 + contrib/luasocket/test/tftptest.lua | 20 + contrib/luasocket/test/udp-zero-length-send | 25 + .../luasocket/test/udp-zero-length-send-recv | 37 + contrib/luasocket/test/udpconnectclnt.lua | 19 + contrib/luasocket/test/udpconnectsrvr.lua | 16 + contrib/luasocket/test/unixdgramclnt.lua | 9 + contrib/luasocket/test/unixdgramsrvr.lua | 9 + contrib/luasocket/test/unixstreamclnt.lua | 8 + contrib/luasocket/test/unixstreamsrvr.lua | 9 + contrib/luasocket/test/upload.html | 15 + contrib/luasocket/test/urltest.lua | 704 ++++ contrib/luasocket/test/utestclnt.lua | 644 ++++ contrib/luasocket/test/utestsrvr.lua | 17 + contrib/luasocket/win32.cmd | 1 + mobdebug.lua | 1707 ++++++++++ premake5.lua | 22 + src/_premake_init.lua | 6 + src/_premake_main.lua | 15 + src/host/buffered_io.c | 4 +- src/host/buffered_io.h | 2 +- src/host/curl_utils.c | 2 +- src/host/premake.c | 5 + src/host/premake.h | 4 + 191 files changed, 30520 insertions(+), 4 deletions(-) create mode 100644 contrib/luasocket/.gitignore create mode 100644 contrib/luasocket/.travis.yml create mode 100644 contrib/luasocket/FIX create mode 100644 contrib/luasocket/LICENSE create mode 100644 contrib/luasocket/Lua51.props create mode 100644 contrib/luasocket/Lua52.props create mode 100644 contrib/luasocket/NEW create mode 100644 contrib/luasocket/README create mode 100644 contrib/luasocket/TODO create mode 100644 contrib/luasocket/WISH create mode 100644 contrib/luasocket/doc/dns.html create mode 100644 contrib/luasocket/doc/ftp.html create mode 100644 contrib/luasocket/doc/http.html create mode 100644 contrib/luasocket/doc/index.html create mode 100644 contrib/luasocket/doc/installation.html create mode 100644 contrib/luasocket/doc/introduction.html create mode 100644 contrib/luasocket/doc/ltn12.html create mode 100644 contrib/luasocket/doc/lua05.ppt create mode 100644 contrib/luasocket/doc/luasocket.png create mode 100644 contrib/luasocket/doc/mime.html create mode 100644 contrib/luasocket/doc/reference.css create mode 100644 contrib/luasocket/doc/reference.html create mode 100644 contrib/luasocket/doc/smtp.html create mode 100644 contrib/luasocket/doc/socket.html create mode 100644 contrib/luasocket/doc/tcp.html create mode 100644 contrib/luasocket/doc/udp.html create mode 100644 contrib/luasocket/doc/url.html create mode 100644 contrib/luasocket/etc/README create mode 100644 contrib/luasocket/etc/b64.lua create mode 100644 contrib/luasocket/etc/check-links.lua create mode 100644 contrib/luasocket/etc/check-memory.lua create mode 100644 contrib/luasocket/etc/cookie.lua create mode 100644 contrib/luasocket/etc/dict.lua create mode 100644 contrib/luasocket/etc/dispatch.lua create mode 100644 contrib/luasocket/etc/eol.lua create mode 100644 contrib/luasocket/etc/forward.lua create mode 100644 contrib/luasocket/etc/get.lua create mode 100644 contrib/luasocket/etc/links create mode 100644 contrib/luasocket/etc/lp.lua create mode 100644 contrib/luasocket/etc/qp.lua create mode 100644 contrib/luasocket/etc/tftp.lua create mode 100644 contrib/luasocket/gem/ex1.lua create mode 100644 contrib/luasocket/gem/ex10.lua create mode 100644 contrib/luasocket/gem/ex11.lua create mode 100644 contrib/luasocket/gem/ex12.lua create mode 100644 contrib/luasocket/gem/ex2.lua create mode 100644 contrib/luasocket/gem/ex3.lua create mode 100644 contrib/luasocket/gem/ex4.lua create mode 100644 contrib/luasocket/gem/ex5.lua create mode 100644 contrib/luasocket/gem/ex6.lua create mode 100644 contrib/luasocket/gem/ex7.lua create mode 100644 contrib/luasocket/gem/ex8.lua create mode 100644 contrib/luasocket/gem/ex9.lua create mode 100644 contrib/luasocket/gem/gem.c create mode 100644 contrib/luasocket/gem/gt.b64 create mode 100644 contrib/luasocket/gem/input.bin create mode 100644 contrib/luasocket/gem/ltn012.tex create mode 100644 contrib/luasocket/gem/luasocket.png create mode 100644 contrib/luasocket/gem/myps2pdf create mode 100644 contrib/luasocket/gem/t1.lua create mode 100644 contrib/luasocket/gem/t1lf.txt create mode 100644 contrib/luasocket/gem/t2.lua create mode 100644 contrib/luasocket/gem/t2.txt create mode 100644 contrib/luasocket/gem/t2gt.qp create mode 100644 contrib/luasocket/gem/t3.lua create mode 100644 contrib/luasocket/gem/t4.lua create mode 100644 contrib/luasocket/gem/t5.lua create mode 100644 contrib/luasocket/gem/test.lua create mode 100644 contrib/luasocket/linux.cmd create mode 100644 contrib/luasocket/logo.ps create mode 100644 contrib/luasocket/ltn012.wiki create mode 100644 contrib/luasocket/ltn013.wiki create mode 100644 contrib/luasocket/luasocket-scm-0.rockspec create mode 100644 contrib/luasocket/macosx.cmd create mode 100644 contrib/luasocket/makefile.dist create mode 100644 contrib/luasocket/mingw.cmd create mode 100644 contrib/luasocket/premake5.lua create mode 100644 contrib/luasocket/rockspec/luasocket-3.0rc2-1.rockspec create mode 100644 contrib/luasocket/samples/README create mode 100644 contrib/luasocket/samples/cddb.lua create mode 100644 contrib/luasocket/samples/daytimeclnt.lua create mode 100644 contrib/luasocket/samples/echoclnt.lua create mode 100644 contrib/luasocket/samples/echosrvr.lua create mode 100644 contrib/luasocket/samples/listener.lua create mode 100644 contrib/luasocket/samples/lpr.lua create mode 100644 contrib/luasocket/samples/mclisten.lua create mode 100644 contrib/luasocket/samples/mcsend.lua create mode 100644 contrib/luasocket/samples/talker.lua create mode 100644 contrib/luasocket/samples/tinyirc.lua create mode 100644 contrib/luasocket/src/auxiliar.c create mode 100644 contrib/luasocket/src/auxiliar.h create mode 100644 contrib/luasocket/src/buffer.c create mode 100644 contrib/luasocket/src/buffer.h create mode 100644 contrib/luasocket/src/compat.c create mode 100644 contrib/luasocket/src/compat.h create mode 100644 contrib/luasocket/src/except.c create mode 100644 contrib/luasocket/src/except.h create mode 100644 contrib/luasocket/src/ftp.lua create mode 100644 contrib/luasocket/src/headers.lua create mode 100644 contrib/luasocket/src/http.lua create mode 100644 contrib/luasocket/src/inet.c create mode 100644 contrib/luasocket/src/inet.h create mode 100644 contrib/luasocket/src/io.c create mode 100644 contrib/luasocket/src/io.h create mode 100644 contrib/luasocket/src/ltn12.lua create mode 100644 contrib/luasocket/src/luasocket.c create mode 100644 contrib/luasocket/src/luasocket.h create mode 100644 contrib/luasocket/src/mbox.lua create mode 100644 contrib/luasocket/src/mime.c create mode 100644 contrib/luasocket/src/mime.h create mode 100644 contrib/luasocket/src/mime.lua create mode 100644 contrib/luasocket/src/options.c create mode 100644 contrib/luasocket/src/options.h create mode 100644 contrib/luasocket/src/pierror.h create mode 100644 contrib/luasocket/src/select.c create mode 100644 contrib/luasocket/src/select.h create mode 100644 contrib/luasocket/src/serial.c create mode 100644 contrib/luasocket/src/smtp.lua create mode 100644 contrib/luasocket/src/socket.h create mode 100644 contrib/luasocket/src/socket.lua create mode 100644 contrib/luasocket/src/tcp.c create mode 100644 contrib/luasocket/src/tcp.h create mode 100644 contrib/luasocket/src/timeout.c create mode 100644 contrib/luasocket/src/timeout.h create mode 100644 contrib/luasocket/src/tp.lua create mode 100644 contrib/luasocket/src/udp.c create mode 100644 contrib/luasocket/src/udp.h create mode 100644 contrib/luasocket/src/unix.c create mode 100644 contrib/luasocket/src/unix.h create mode 100644 contrib/luasocket/src/unixdgram.c create mode 100644 contrib/luasocket/src/unixdgram.h create mode 100644 contrib/luasocket/src/unixstream.c create mode 100644 contrib/luasocket/src/unixstream.h create mode 100644 contrib/luasocket/src/url.lua create mode 100644 contrib/luasocket/src/usocket.c create mode 100644 contrib/luasocket/src/usocket.h create mode 100644 contrib/luasocket/src/wsocket.c create mode 100644 contrib/luasocket/src/wsocket.h create mode 100644 contrib/luasocket/test/README create mode 100644 contrib/luasocket/test/auth/.htaccess create mode 100644 contrib/luasocket/test/auth/.htpasswd create mode 100644 contrib/luasocket/test/auth/index.html create mode 100644 contrib/luasocket/test/cgi/cat create mode 100644 contrib/luasocket/test/cgi/cat-index-html create mode 100644 contrib/luasocket/test/cgi/env create mode 100644 contrib/luasocket/test/cgi/query-string create mode 100644 contrib/luasocket/test/cgi/redirect-loop create mode 100644 contrib/luasocket/test/cgi/request-uri create mode 100644 contrib/luasocket/test/dicttest.lua create mode 100644 contrib/luasocket/test/excepttest.lua create mode 100644 contrib/luasocket/test/find-connect-limit create mode 100644 contrib/luasocket/test/ftptest.lua create mode 100644 contrib/luasocket/test/hello.lua create mode 100644 contrib/luasocket/test/httptest.lua create mode 100644 contrib/luasocket/test/index.html create mode 100644 contrib/luasocket/test/ltn12test.lua create mode 100644 contrib/luasocket/test/luasocket.png create mode 100644 contrib/luasocket/test/mimetest.lua create mode 100644 contrib/luasocket/test/smtptest.lua create mode 100644 contrib/luasocket/test/stufftest.lua create mode 100644 contrib/luasocket/test/tcp-getoptions create mode 100644 contrib/luasocket/test/test_bind.lua create mode 100644 contrib/luasocket/test/test_getaddrinfo.lua create mode 100644 contrib/luasocket/test/test_socket_error.lua create mode 100644 contrib/luasocket/test/testclnt.lua create mode 100644 contrib/luasocket/test/testmesg.lua create mode 100644 contrib/luasocket/test/testsrvr.lua create mode 100644 contrib/luasocket/test/testsupport.lua create mode 100644 contrib/luasocket/test/tftptest.lua create mode 100644 contrib/luasocket/test/udp-zero-length-send create mode 100644 contrib/luasocket/test/udp-zero-length-send-recv create mode 100644 contrib/luasocket/test/udpconnectclnt.lua create mode 100644 contrib/luasocket/test/udpconnectsrvr.lua create mode 100644 contrib/luasocket/test/unixdgramclnt.lua create mode 100644 contrib/luasocket/test/unixdgramsrvr.lua create mode 100644 contrib/luasocket/test/unixstreamclnt.lua create mode 100644 contrib/luasocket/test/unixstreamsrvr.lua create mode 100644 contrib/luasocket/test/upload.html create mode 100644 contrib/luasocket/test/urltest.lua create mode 100644 contrib/luasocket/test/utestclnt.lua create mode 100644 contrib/luasocket/test/utestsrvr.lua create mode 100644 contrib/luasocket/win32.cmd create mode 100644 mobdebug.lua diff --git a/contrib/luasocket/.gitignore b/contrib/luasocket/.gitignore new file mode 100644 index 00000000..83074835 --- /dev/null +++ b/contrib/luasocket/.gitignore @@ -0,0 +1,16 @@ +*.o +*.so +*.so.* +*.obj +*.lib +*.dll* +*.user +*.sdf +Lua.props +Debug +Release +*.manifest +*.swp +*.suo +x64 + diff --git a/contrib/luasocket/.travis.yml b/contrib/luasocket/.travis.yml new file mode 100644 index 00000000..fce8a965 --- /dev/null +++ b/contrib/luasocket/.travis.yml @@ -0,0 +1,54 @@ +language: erlang + +env: + global: + - LUAROCKS_BASE=luarocks-2.0.13 + matrix: + - LUA=lua5.1 LUA_DEV=liblua5.1-dev LUA_VER=5.1 LUA_SFX=5.1 LUA_INCDIR=/usr/include/lua5.1 + - LUA=lua5.2 LUA_DEV=liblua5.2-dev LUA_VER=5.2 LUA_SFX=5.2 LUA_INCDIR=/usr/include/lua5.2 + - LUA=luajit LUA_DEV=libluajit-5.1-dev LUA_VER=5.1 LUA_SFX=jit LUA_INCDIR=/usr/include/luajit-2.0 + +branches: + only: + - master + +before_install: + - if [ $LUA = "luajit" ]; then + sudo add-apt-repository ppa:mwild1/ppa -y && sudo apt-get update -y; + fi + - sudo apt-get install $LUA + - sudo apt-get install $LUA_DEV + - lua$LUA_SFX -v + # Install a recent luarocks release + - wget http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz + - tar zxvpf $LUAROCKS_BASE.tar.gz + - cd $LUAROCKS_BASE + - ./configure + --lua-version=$LUA_VER --lua-suffix=$LUA_SFX --with-lua-include="$LUA_INCDIR" + - sudo make + - sudo make install + - cd $TRAVIS_BUILD_DIR + + +install: + - export DEBUG=DEBUG + - sudo -E luarocks make luasocket-scm-0.rockspec + +script: + - cd test + - lua$LUA_SFX hello.lua + - lua$LUA_SFX testsrvr.lua > /dev/null & + - lua$LUA_SFX testclnt.lua + - lua$LUA_SFX stufftest.lua + - lua$LUA_SFX excepttest.lua + - lua$LUA_SFX test_bind.lua + - lua$LUA_SFX test_getaddrinfo.lua + - lua$LUA_SFX ltn12test.lua + - lua$LUA_SFX mimetest.lua + - lua$LUA_SFX urltest.lua + - lua$LUA_SFX test_socket_error.lua + +notifications: + email: + on_success: change + on_failure: always diff --git a/contrib/luasocket/FIX b/contrib/luasocket/FIX new file mode 100644 index 00000000..40f30a15 --- /dev/null +++ b/contrib/luasocket/FIX @@ -0,0 +1,28 @@ + + + + + + +http was preserving old host header during redirects +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. +tcp{client}:send() now returns i+sent-1... +get rid of a = socket.try() in the manual, except for protected cases. replace it with assert. +get rid of "base." kludge in package.loaded +check all "require("http")" etc in the manual. +make sure sock_gethostname.* only return success if the hp is not null! +change 'l' prefix in C libraries to 'c' to avoid clash with LHF libraries + don't forget the declarations in luasocket.h and mime.h!!! +setpeername was using udp{unconnected} +fixed a bug in http.lua that caused some requests to fail (Florian Berger) +fixed a bug in select.c that prevented sockets with descriptor 0 from working (Renato Maia) +fixed a "bug" that caused dns.toip to crash under uLinux +fixed a "bug" that caused a crash in gethostbyname under VMS +DEBUG and VERSION became _DEBUG and _VERSION +send returns the right value if input is "". Alexander Marinov diff --git a/contrib/luasocket/LICENSE b/contrib/luasocket/LICENSE new file mode 100644 index 00000000..b6354510 --- /dev/null +++ b/contrib/luasocket/LICENSE @@ -0,0 +1,20 @@ +LuaSocket 3.0 license +Copyright © 2004-2013 Diego Nehab + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/contrib/luasocket/Lua51.props b/contrib/luasocket/Lua51.props new file mode 100644 index 00000000..33075a63 --- /dev/null +++ b/contrib/luasocket/Lua51.props @@ -0,0 +1,28 @@ + + + + + ..\build\vc12\bin\lua\5.1\ + ..\build\vc12\bin\lua\5.1\ + ..\build\vc12\include\lua\5.1\ + lua51.lib + + + <_PropertySheetDisplayName>Lua51 + + + + + $(LUALIB_PATH) + + + $(LUABIN_PATH) + + + $(LUAINC_PATH) + + + $(LUALIB) + + + diff --git a/contrib/luasocket/Lua52.props b/contrib/luasocket/Lua52.props new file mode 100644 index 00000000..3afb2505 --- /dev/null +++ b/contrib/luasocket/Lua52.props @@ -0,0 +1,28 @@ + + + + + ..\build\vc12\bin\lua\5.2\ + ..\build\vc12\bin\lua\5.2\ + ..\build\vc12\include\lua\5.2\ + lua52.lib + + + <_PropertySheetDisplayName>Lua52 + + + + + $(LUALIB_PATH) + + + $(LUABIN_PATH) + + + $(LUAINC_PATH) + + + $(LUALIB) + + + diff --git a/contrib/luasocket/NEW b/contrib/luasocket/NEW new file mode 100644 index 00000000..0bff64c1 --- /dev/null +++ b/contrib/luasocket/NEW @@ -0,0 +1,44 @@ +What's New + +Main changes for LuaSocket 3.0-rc1 are IPv6 support and Lua 5.2 compatibility. + + * Added: Compatible with Lua 5.2 + - Note that unless you define LUA_COMPAT_MODULE, package + tables will not be exported as globals! + * Added: IPv6 support; + - Socket.connect and socket.bind support IPv6 addresses; + - Getpeername and getsockname support IPv6 addresses, and + return the socket family as a third value; + - URL module updated to support IPv6 host names; + - New socket.tcp6 and socket.udp6 functions; + - New socket.dns.getaddrinfo and socket.dns.getnameinfo functions; + * Added: getoption method; + * Fixed: url.unescape was returning additional values; + * Fixed: mime.qp, mime.unqp, mime.b64, and mime.unb64 could + mistaking their own stack slots for functions arguments; + * Fixed: Receiving zero-length datagram is now possible; + * Improved: Hidden all internal library symbols; + * Improved: Better error messages; + * Improved: Better documentation of socket options. + * Fixed: manual sample of HTTP authentication now uses correct + "authorization" header (Alexandre Ittner); + * Fixed: failure on bind() was destroying the socket (Sam Roberts); + * Fixed: receive() returns immediatelly if prefix can satisfy + bytes requested (M Joonas Pihlaja); + * Fixed: multicast didn't work on Windows, or anywhere + else for that matter (Herbert Leuwer, Adrian Sietsma); + * Fixed: select() now reports an error when called with more + sockets than FD_SETSIZE (Lorenzo Leonini); + * Fixed: manual links to home.html changed to index.html (Robert Hahn); + * Fixed: mime.unb64() would return an empty string on results that started + with a null character (Robert Raschke); + * Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray); + * Fixed: calling sleep() with negative numbers could + block forever, wasting CPU. Now it returns immediately (MPB); + * Improved: FTP commands are now sent in upper case to + help buggy servers (Anders Eurenius); + * Improved: known headers now sent in canonic + capitalization to help buggy servers (Joseph Stewart); + * Improved: Clarified tcp:receive() in the manual (MPB); + * Improved: Decent makefiles (LHF). + * Fixed: RFC links in documentation now point to IETF (Cosmin Apreutesei). diff --git a/contrib/luasocket/README b/contrib/luasocket/README new file mode 100644 index 00000000..cd8ee59d --- /dev/null +++ b/contrib/luasocket/README @@ -0,0 +1,11 @@ +This is the LuaSocket 3.0-rc1. It has been tested on Windows 7, Mac OS X, +and Linux. + +Please use the project page at GitHub + + https://github.com/diegonehab/luasocket + +to file bug reports or propose changes. + +Have fun, +Diego Nehab. diff --git a/contrib/luasocket/TODO b/contrib/luasocket/TODO new file mode 100644 index 00000000..a838fc02 --- /dev/null +++ b/contrib/luasocket/TODO @@ -0,0 +1,81 @@ +- bizarre default values for getnameinfo should throw error instead! + +> It's just too bad it can't talk to gmail - +> reason 1: they absolutely want TLS +> reason 2: unlike all the other SMTP implementations, they +> don't +> tolerate missing < > around adresses + +- document the new bind and connect behavior. +- shouldn't we instead make the code compatible to Lua 5.2 + without any compat stuff, and use a compatibility layer to + make it work on 5.1? +- add what's new to manual +- should there be an equivalent to tohostname for IPv6? +- should we add service name resolution as well to getaddrinfo? +- Maybe the sockaddr to presentation conversion should be done with getnameinfo()? + +- add http POST sample to manual + people keep asking stupid questions +- documentation of dirty/getfd/setfd is problematic because of portability + same for unix and serial. + what to do about this? add a stronger disclaimer? +- fix makefile with decent defaults? + +Done: + +- added IPv6 support to getsockname +- simplified getpeername implementation +- added family to return of getsockname and getpeername + and added modification to the manual to describe + +- connect and bind try all adresses returned by getaddrinfo +- document headers.lua? +- update copyright date everywhere? +- remove RCSID from files? +- move version to 2.1 rather than 2.1.1? +- fixed url package to support ipv6 hosts +- changed domain to family +- implement getfamily methods. + +- remove references to Lua 5.0 from documentation, add 5.2? +- update lua and luasocket version in samples in documentation +- document ipv5_v6only default option being set? +- document tcp6 and udp6 +- document dns.getaddrinfo +- documented zero-sized datagram change? + no. +- document unix socket and serial socket? add raw support? + no. +- document getoption +- merge luaL_typeerror into auxiliar to avoid using luaL prefix? + + + + + + + + + + +replace \r\n with \0xD\0xA in everything +New mime support + +ftp send should return server replies? +make sure there are no object files in the distribution tarball +http handling of 100-continue, see DB patch +DB ftp.lua bug. +test unix.c to return just a function and works with require"unix" +get rid of setmetatable(, nil) since packages don't need this anymore in 5.1 +compat-5.1 novo +ajeitar pra lua-5.1 + +adicionar exemplos de expansão: pipe, local, named pipe +testar os options! + + +- Thread-unsafe functions to protect + gethostbyname(), gethostbyaddr(), gethostent(), +inet_ntoa(), strerror(), + diff --git a/contrib/luasocket/WISH b/contrib/luasocket/WISH new file mode 100644 index 00000000..e7e9c076 --- /dev/null +++ b/contrib/luasocket/WISH @@ -0,0 +1,22 @@ +... as an l-value to get all results of a function call? +at least ...[i] and #... +extend to full tuples? + +__and __or __not metamethods + +lua_tostring, lua_tonumber, lua_touseradta etc push values in stack +__tostring,__tonumber, __touserdata metamethods are checked +and expected to push an object of correct type on stack + +lua_rawtostring, lua_rawtonumber, lua_rawtouserdata don't +push anything on stack, return data of appropriate type, +skip metamethods and throw error if object not of exact type + +package.findfile exported +module not polluting the global namespace + +coxpcall with a coroutine pool for efficiency (reusing coroutines) + +exception mechanism formalized? just like the package system was. + +a nice bitlib in the core diff --git a/contrib/luasocket/doc/dns.html b/contrib/luasocket/doc/dns.html new file mode 100644 index 00000000..c4a0472e --- /dev/null +++ b/contrib/luasocket/doc/dns.html @@ -0,0 +1,183 @@ + + + + + + +LuaSocket: DNS support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

DNS

+ +

+IPv4 name resolution functions +dns.toip +and +dns.tohostname +return all information obtained from +the resolver in a table of the form: +

+ +
+resolved4 = {
+  name = canonic-name,
+  alias = alias-list,
+  ip = ip-address-list
+} +
+ +

+Note that the alias list can be empty. +

+ +

+The more general name resolution function +dns.getaddrinfo, which +supports both IPv6 and IPv4, +returns all information obtained from +the resolver in a table of the form: +

+ +
+resolved6 = {
+  [1] = {
+    family = family-name-1,
+    addr = address-1
+  },
+  ...
+  [n] = {
+    family = family-name-n,
+    addr = address-n
+  }
+} +
+ +

+Here, family contains the string "inet" for IPv4 +addresses, and "inet6" for IPv6 addresses. +

+ + + +

+socket.dns.getaddrinfo(address) +

+ +

+Converts from host name to address. +

+ +

+Address can be an IPv4 or IPv6 address or host name. +

+ +

+The function returns a table with all information returned by +the resolver. In case of error, the function returns nil +followed by an error message. +

+ + + +

+socket.dns.gethostname() +

+ +

+Returns the standard host name for the machine as a string. +

+ + + +

+socket.dns.tohostname(address) +

+ +

+Converts from IPv4 address to host name. +

+ +

+Address can be an IP address or host name. +

+ +

+The function returns a string with the canonic host name of the given +address, followed by a table with all information returned by +the resolver. In case of error, the function returns nil +followed by an error message. +

+ + + +

+socket.dns.toip(address) +

+ +

+Converts from host name to IPv4 address. +

+ +

+Address can be an IP address or host name. +

+ +

+Returns a string with the first IP address found for address, +followed by a table with all information returned by the resolver. +In case of error, the function returns nil followed by an error +message. +

+ + + + + + + diff --git a/contrib/luasocket/doc/ftp.html b/contrib/luasocket/doc/ftp.html new file mode 100644 index 00000000..091c88f7 --- /dev/null +++ b/contrib/luasocket/doc/ftp.html @@ -0,0 +1,288 @@ + + + + + + +LuaSocket: FTP support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

FTP

+ +

+FTP (File Transfer Protocol) is a protocol used to transfer files +between hosts. The ftp namespace offers thorough support +to FTP, under a simple interface. The implementation conforms to +RFC 959. +

+ +

+High level functions are provided supporting the most common operations. +These high level functions are implemented on top of a lower level +interface. Using the low-level interface, users can easily create their +own functions to access any operation supported by the FTP +protocol. For that, check the implementation. +

+ +

+To really benefit from this module, a good understanding of + +LTN012, Filters sources and sinks is necessary. +

+ +

+To obtain the ftp namespace, run: +

+ +
+-- loads the FTP module and any libraries it requires
+local ftp = require("socket.ftp")
+
+ +

+URLs MUST conform to +RFC 1738, +that is, an URL is a string in the form: +

+ +
+ +[ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][type=a|i] +
+ +

+The following constants in the namespace can be set to control the default behavior of +the FTP module: +

+ + + + + + +

+ftp.get(url)
+ftp.get{
+  host = string,
+  sink = LTN12 sink,
+  argument or path = string,
+  [user = string,]
+  [password = string]
+  [command = string,]
+  [port = number,]
+  [type = string,]
+  [step = LTN12 pump step,]
+  [create = function]
+} +

+ +

+The get function has two forms. The simple form has fixed +functionality: it downloads the contents of a URL and returns it as a +string. The generic form allows a lot more control, as explained +below. +

+ +

+If the argument of the get function is a table, the function +expects at least the fields host, sink, and one of +argument or path (argument takes +precedence). Host is the server to connect to. Sink is +the simple +LTN12 +sink that will receive the downloaded data. Argument or +path give the target path to the resource in the server. The +optional arguments are the following: +

+ + +

+If successful, the simple version returns the URL contents as a +string, and the generic function returns 1. In case of error, both +functions return nil and an error message describing the +error. +

+ +
+-- load the ftp support
+local ftp = require("socket.ftp")
+
+-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br",
+-- and get file "lua.tar.gz" from directory "pub/lua" as binary.
+f, e = ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
+
+ +
+-- load needed modules
+local ftp = require("socket.ftp")
+local ltn12 = require("ltn12")
+local url = require("socket.url")
+
+-- a function that returns a directory listing
+function nlst(u)
+    local t = {}
+    local p = url.parse(u)
+    p.command = "nlst"
+    p.sink = ltn12.sink.table(t)
+    local r, e = ftp.get(p)
+    return r and table.concat(t), e
+end
+
+ + + +

+ftp.put(url, content)
+ftp.put{
+  host = string,
+  source = LTN12 sink,
+  argument or path = string,
+  [user = string,]
+  [password = string]
+  [command = string,]
+  [port = number,]
+  [type = string,]
+  [step = LTN12 pump step,]
+  [create = function]
+} +

+ +

+The put function has two forms. The simple form has fixed +functionality: it uploads a string of content into a URL. The generic form +allows a lot more control, as explained below. +

+ +

+If the argument of the put function is a table, the function +expects at least the fields host, source, and one of +argument or path (argument takes +precedence). Host is the server to connect to. Source is +the simple +LTN12 +source that will provide the contents to be uploaded. +Argument or +path give the target path to the resource in the server. The +optional arguments are the following: +

+ + +

+Both functions return 1 if successful, or nil and an error +message describing the reason for failure. +

+ +
+-- load the ftp support
+local ftp = require("socket.ftp")
+
+-- Log as user "fulano" on server "ftp.example.com",
+-- using password "silva", and store a file "README" with contents 
+-- "wrong password, of course"
+f, e = ftp.put("ftp://fulano:silva@ftp.example.com/README", 
+    "wrong password, of course")
+
+ +
+-- load the ftp support
+local ftp = require("socket.ftp")
+local ltn12 = require("ltn12")
+
+-- Log as user "fulano" on server "ftp.example.com",
+-- using password "silva", and append to the remote file "LOG", sending the
+-- contents of the local file "LOCAL-LOG"
+f, e = ftp.put{
+  host = "ftp.example.com", 
+  user = "fulano",
+  password = "silva",
+  command = "appe",
+  argument = "LOG",
+  source = ltn12.source.file(io.open("LOCAL-LOG", "r"))
+}
+
+ + + + + + + + diff --git a/contrib/luasocket/doc/http.html b/contrib/luasocket/doc/http.html new file mode 100644 index 00000000..3b7a8b1a --- /dev/null +++ b/contrib/luasocket/doc/http.html @@ -0,0 +1,335 @@ + + + + + + +LuaSocket: HTTP support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +introduction · +introduction · +reference +

+
+
+
+ + + +

HTTP

+ +

+HTTP (Hyper Text Transfer Protocol) is the protocol used to exchange +information between web-browsers and servers. The http +namespace offers full support for the client side of the HTTP +protocol (i.e., +the facilities that would be used by a web-browser implementation). The +implementation conforms to the HTTP/1.1 standard, +RFC 2616. +

+ +

+The module exports functions that provide HTTP functionality in different +levels of abstraction. From the simple +string oriented requests, through generic +LTN12 based, down to even lower-level if you bother to look through the source code. +

+ +

+To obtain the http namespace, run: +

+ +
+-- loads the HTTP module and any libraries it requires
+local http = require("socket.http")
+
+ +

+URLs must conform to +RFC 1738, +that is, an URL is a string in the form: +

+ +
+
+[http://][<user>[:<password>]@]<host>[:<port>][/<path>] 
+
+
+ +

+MIME headers are represented as a Lua table in the form: +

+ +
+ + +
+headers = {
+  field-1-name = field-1-value,
+  field-2-name = field-2-value,
+  field-3-name = field-3-value,
+  ...
+  field-n-name = field-n-value
+} +
+
+ +

+Field names are case insensitive (as specified by the standard) and all +functions work with lowercase field names (but see +socket.headers.canonic). +Field values are left unmodified. +

+ +

+Note: MIME headers are independent of order. Therefore, there is no problem +in representing them in a Lua table. +

+ +

+The following constants can be set to control the default behavior of +the HTTP module: +

+ + + +

+Note: These constants are global. Changing them will also +change the behavior other code that might be using LuaSocket. +

+ + + +

+http.request(url [, body])
+http.request{
+  url = string,
+  [sink = LTN12 sink,]
+  [method = string,]
+  [headers = header-table,]
+  [source = LTN12 source],
+  [step = LTN12 pump step,]
+  [proxy = string,]
+  [redirect = boolean,]
+  [create = function]
+} +

+ +

+The request function has two forms. The simple form downloads +a URL using the GET or POST method and is based +on strings. The generic form performs any HTTP method and is +LTN12 based. +

+ +

+If the first argument of the request function is a string, it +should be an url. In that case, if a body +is provided as a string, the function will perform a POST method +in the url. Otherwise, it performs a GET in the +url +

+ +

+If the first argument is instead a table, the most important fields are +the url and the simple +LTN12 +sink that will receive the downloaded content. +Any part of the url can be overridden by including +the appropriate field in the request table. +If authentication information is provided, the function +uses the Basic Authentication Scheme (see note) +to retrieve the document. If sink is nil, the +function discards the downloaded data. The optional parameters are the +following: +

+ + +

+In case of failure, the function returns nil followed by an +error message. If successful, the simple form returns the response +body as a string, followed by the response status code, the response +headers and the response status line. The generic function returns the same +information, except the first return value is just the number 1 (the body +goes to the sink). +

+ +

+Even when the server fails to provide the contents of the requested URL (URL not found, for example), +it usually returns a message body (a web page informing the +URL was not found or some other useless page). To make sure the +operation was successful, check the returned status code. For +a list of the possible values and their meanings, refer to RFC 2616. +

+ +

+Here are a few examples with the simple interface: +

+ +
+-- load the http module
+local io = require("io")
+local http = require("socket.http")
+local ltn12 = require("ltn12")
+
+-- connect to server "www.cs.princeton.edu" and retrieves this manual
+-- file from "~diego/professional/luasocket/http.html" and print it to stdout
+http.request{ 
+    url = "http://www.cs.princeton.edu/~diego/professional/luasocket/http.html", 
+    sink = ltn12.sink.file(io.stdout)
+}
+
+-- connect to server "www.example.com" and tries to retrieve
+-- "/private/index.html". Fails because authentication is needed.
+b, c, h = http.request("http://www.example.com/private/index.html")
+-- b returns some useless page telling about the denied access, 
+-- h returns authentication information
+-- and c returns with value 401 (Authentication Required)
+
+-- tries to connect to server "wrong.host" to retrieve "/"
+-- and fails because the host does not exist.
+r, e = http.request("http://wrong.host/")
+-- r is nil, and e returns with value "host not found"
+
+ +

+And here is an example using the generic interface: +

+ +
+-- load the http module
+http = require("socket.http")
+
+-- Requests information about a document, without downloading it.
+-- Useful, for example, if you want to display a download gauge and need
+-- to know the size of the document in advance
+r, c, h = http.request {
+  method = "HEAD",
+  url = "http://www.tecgraf.puc-rio.br/~diego"
+}
+-- r is 1, c is 200, and h would return the following headers:
+-- h = {
+--   date = "Tue, 18 Sep 2001 20:42:21 GMT",
+--   server = "Apache/1.3.12 (Unix)  (Red Hat/Linux)",
+--   ["last-modified"] = "Wed, 05 Sep 2001 06:11:20 GMT",
+--   ["content-length"] = 15652,
+--   ["connection"] = "close",
+--   ["content-Type"] = "text/html"
+-- }
+
+ +

+Note: When sending a POST request, simple interface adds a +"Content-type: application/x-www-form-urlencoded" +header to the request. This is the type used by +HTML forms. If you need another type, use the generic +interface. +

+ +

+Note: Some URLs are protected by their +servers from anonymous download. For those URLs, the server must receive +some sort of authentication along with the request or it will deny +download and return status "401 Authentication Required". +

+ +

+The HTTP/1.1 standard defines two authentication methods: the Basic +Authentication Scheme and the Digest Authentication Scheme, both +explained in detail in +RFC 2068. +

+ +

The Basic Authentication Scheme sends +<user> and +<password> unencrypted to the server and is therefore +considered unsafe. Unfortunately, by the time of this implementation, +the wide majority of servers and browsers support the Basic Scheme only. +Therefore, this is the method used by the toolkit whenever +authentication is required. +

+ +
+-- load required modules
+http = require("socket.http")
+mime = require("mime")
+
+-- Connect to server "www.example.com" and tries to retrieve
+-- "/private/index.html", using the provided name and password to
+-- authenticate the request
+b, c, h = http.request("http://fulano:silva@www.example.com/private/index.html")
+
+-- Alternatively, one could fill the appropriate header and authenticate
+-- the request directly.
+r, c = http.request {
+  url = "http://www.example.com/private/index.html",
+  headers = { authorization = "Basic " .. (mime.b64("fulano:silva")) }
+}
+
+ + + + + + + diff --git a/contrib/luasocket/doc/index.html b/contrib/luasocket/doc/index.html new file mode 100644 index 00000000..7d81b411 --- /dev/null +++ b/contrib/luasocket/doc/index.html @@ -0,0 +1,215 @@ + + + + + + +LuaSocket: Network support for the Lua language + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

What is LuaSocket?

+ +

+LuaSocket is a Lua extension library +that is composed by two parts: a C core that provides support for the TCP +and UDP transport layers, and a set of Lua modules that add support for +functionality commonly needed by applications that deal with the Internet. +

+ +

+The core support has been implemented so that it is both efficient and +simple to use. It is available to any Lua application once it has been +properly initialized by the interpreter in use. The code has been tested +and runs well on several Windows and UNIX platforms.

+ +

+Among the support modules, the most commonly used implement the +SMTP +(sending e-mails), +HTTP +(WWW access) and +FTP +(uploading and downloading files) client +protocols. These provide a very natural and generic interface to the +functionality defined by each protocol. +In addition, you will find that the +MIME (common encodings), +URL +(anything you could possible want to do with one) and +LTN12 +(filters, sinks, sources and pumps) modules can be very handy. +

+ +

+The library is available under the same + +terms and conditions as the Lua language, the MIT license. The idea is +that if you can use Lua in a project, you should also be able to use +LuaSocket. +

+ +

+Copyright © 1999-2013 Diego Nehab. All rights reserved.
+Author: Diego Nehab +

+ + + +

Download

+ +

+LuaSocket version 3.0-rc1 is now available for download! +It is compatible with Lua 5.1 and 5.2, and has +been tested on Windows XP, Linux, and Mac OS X. Chances +are it works well on most UNIX distributions and Windows flavors. +

+ +

+The current version of the library can be found at +the LuaSocket +project page on GitHub. Besides the full C and Lua source code +for the library, the distribution contains several examples, +this user's manual and basic test procedures. +

+ +

Take a look at the installation section of the +manual to find out how to properly install the library. +

+ + + +

Special thanks

+ +

+This marks the first release of LuaSocket that +wholeheartedly embraces the open-source development +philosophy. After a long hiatus, Matthew Wild finally +convinced me it was time for a release including IPv6 and +Lua 5.2 support. It was more work than we anticipated. +Special thanks to Sam Roberts, Florian Zeitz, and Paul +Aurich, Liam Devine, Alexey Melnichuk, and everybody else +that has helped bring this library back to life. +

+ + + +

What's New

+ +

+Main changes for LuaSocket 3.0-rc1 are IPv6 support +and Lua 5.2 compatibility. +

+ + + + + +

Old Versions

+ +

+All previous versions of the LuaSocket library can be downloaded +here. Although these versions are no longer supported, they are +still available for those that have compatibility issues. +

+ + + + + + + diff --git a/contrib/luasocket/doc/installation.html b/contrib/luasocket/doc/installation.html new file mode 100644 index 00000000..28a9fbb5 --- /dev/null +++ b/contrib/luasocket/doc/installation.html @@ -0,0 +1,127 @@ + + + + + + +LuaSocket: Installation + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

Installation

+ +

Here we describe the standard distribution. If the +standard doesn't meet your needs, we refer you to the Lua +discussion list, where any question about the package scheme +will likely already have been answered.

+ +

Directory structure

+ +

On Unix systems, the standard distribution uses two base +directories, one for system dependent files, and another for system +independent files. Let's call these directories <CDIR> +and <LDIR>, respectively. +For example, in my laptp, Lua 5.1 is configured to +use '/usr/local/lib/lua/5.1' for +<CDIR> and '/usr/local/share/lua/5.1' for +<LDIR>. On Windows, <CDIR> +usually points to the directory where the Lua executable is +found, and <LDIR> points to a +lua/ directory inside <CDIR>. (These +settings can be overridden by environment variables +LUA_PATH and LUA_CPATH. See the Lua +documentation for details.) Here is the standard LuaSocket +distribution directory structure:

+ +
+<LDIR>/ltn12.lua
+<LDIR>/socket.lua
+<CDIR>/socket/core.dll
+<LDIR>/socket/http.lua
+<LDIR>/socket/tp.lua
+<LDIR>/socket/ftp.lua
+<LDIR>/socket/smtp.lua
+<LDIR>/socket/url.lua
+<LDIR>/mime.lua
+<CDIR>/mime/core.dll
+
+ +

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

+ +

Using LuaSocket

+ +

With the above setup, and an interpreter with shared library support, +it should be easy to use LuaSocket. Just fire the interpreter and use the +require function to gain access to whatever module you need:

+ +
+Lua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio
+> socket = require("socket")
+> print(socket._VERSION)
+--> LuaSocket 3.0-rc1
+
+ +

Each module loads their dependencies automatically, so you only need to +load the modules you directly depend upon:

+ +
+Lua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio
+> http = require("socket.http")
+> print(http.request("http://www.impa.br/~diego/software/luasocket"))
+--> homepage gets dumped to terminal
+
+ + + + + + + diff --git a/contrib/luasocket/doc/introduction.html b/contrib/luasocket/doc/introduction.html new file mode 100644 index 00000000..fd22f483 --- /dev/null +++ b/contrib/luasocket/doc/introduction.html @@ -0,0 +1,333 @@ + + + + + + +LuaSocket: Introduction to the core + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

Introduction

+ +

+LuaSocket is a Lua extension library +that is composed by two parts: a C core that provides support for the TCP +and UDP transport layers, and a set of Lua modules that add support for +the SMTP (sending e-mails), HTTP (WWW access) and FTP (uploading and +downloading files) protocols and other functionality commonly needed by +applications that deal with the Internet. This introduction is about the C +core. +

+ +

+Communication in LuaSocket is performed via I/O objects. These can +represent different network domains. Currently, support is provided for TCP +and UDP, but nothing prevents other developers from implementing SSL, Local +Domain, Pipes, File Descriptors etc. I/O objects provide a standard +interface to I/O across different domains and operating systems. +

+ +

+The API design had two goals in mind. First, users +experienced with the C API to sockets should feel comfortable using LuaSocket. +Second, the simplicity and the feel of the Lua language should be +preserved. To achieve these goals, the LuaSocket API keeps the function names and semantics the C API whenever possible, but their usage in Lua has been greatly simplified. +

+ + +

+One of the simplifications is the receive pattern capability. +Applications can read data from stream domains (such as TCP) +line by line, block by block, or until the connection is closed. +All I/O reads are buffered and the performance differences between +different receive patterns are negligible. +

+ +

+Another advantage is the flexible timeout control +mechanism. As in C, all I/O operations are blocking by default. For +example, the send, +receive and +accept methods +of the TCP domain will block the caller application until +the operation is completed (if ever!). However, with a call to the +settimeout +method, an application can specify upper limits on +the time it can be blocked by LuaSocket (the "total" timeout), on +the time LuaSocket can internally be blocked by any OS call (the +"block" timeout) or a combination of the two. Each LuaSocket +call might perform several OS calls, so that the two timeout values are +not equivalent. +

+ +

+Finally, the host name resolution is transparent, meaning that most +functions and methods accept both IP addresses and host names. In case a +host name is given, the library queries the system's resolver and +tries the main IP address returned. Note that direct use of IP addresses +is more efficient, of course. The +toip +and tohostname +functions from the DNS module are provided to convert between host names and IP addresses. +

+ +

+Together, these changes make network programming in LuaSocket much simpler +than it is in C, as the following sections will show. +

+ + + +

TCP

+ +

+TCP (Transfer Control Protocol) is reliable stream protocol. In other +words, applications communicating through TCP can send and receive data as +an error free stream of bytes. Data is split in one end and +reassembled transparently on the other end. There are no boundaries in +the data transfers. The library allows users to read data from the +sockets in several different granularities: patterns are available for +lines, arbitrary sized blocks or "read up to connection closed", all with +good performance. +

+ +

+The library distinguishes three types of TCP sockets: master, +client and server sockets. +

+ +

+Master sockets are newly created TCP sockets returned by the function +socket.tcp. A master socket is +transformed into a server socket +after it is associated with a local address by a call to the +bind method followed by a call to the +listen. Conversely, a master socket +can be changed into a client socket with the method +connect, +which associates it with a remote address. +

+ +

+On server sockets, applications can use the +accept method +to wait for a client connection. Once a connection is established, a +client socket object is returned representing this connection. The +other methods available for server socket objects are +getsockname, +setoption, +settimeout, and +close. +

+ +

+Client sockets are used to exchange data between two applications over +the Internet. Applications can call the methods +send and +receive +to send and receive data. The other methods +available for client socket objects are +getsockname, +getpeername, +setoption, +settimeout, +shutdown, and +close. +

+ +

+Example: +

+
+

+A simple echo server, using LuaSocket. The program binds to an ephemeral +port (one that is chosen by the operating system) on the local host and +awaits client connections on that port. When a connection is established, +the program reads a line from the remote end and sends it back, closing +the connection immediately. You can test it using the telnet +program. +

+ +
+-- load namespace
+local socket = require("socket")
+-- create a TCP socket and bind it to the local host, at any port
+local server = assert(socket.bind("*", 0))
+-- find out which port the OS chose for us
+local ip, port = server:getsockname()
+-- print a message informing what's up
+print("Please telnet to localhost on port " .. port)
+print("After connecting, you have 10s to enter a line to be echoed")
+-- loop forever waiting for clients
+while 1 do
+  -- wait for a connection from any client
+  local client = server:accept()
+  -- make sure we don't block waiting for this client's line
+  client:settimeout(10)
+  -- receive the line
+  local line, err = client:receive()
+  -- if there was no error, send it back to the client
+  if not err then client:send(line .. "\n") end
+  -- done with client, close the object
+  client:close()
+end
+
+
+ + + +

UDP

+ +

+UDP (User Datagram Protocol) is a non-reliable datagram protocol. In +other words, applications communicating through UDP send and receive +data as independent blocks, which are not guaranteed to reach the other +end. Even when they do reach the other end, they are not guaranteed to be +error free. Data transfers are atomic, one datagram at a time. Reading +only part of a datagram discards the rest, so that the following read +operation will act on the next datagram. The advantages are in +simplicity (no connection setup) and performance (no error checking or +error correction). +

+ +

+Note that although no guarantees are made, these days +networks are so good that, under normal circumstances, few errors +happen in practice. +

+ +

+An UDP socket object is created by the +socket.udp function. UDP +sockets do not need to be connected before use. The method +sendto +can be used immediately after creation to +send a datagram to IP address and port. Host names are not allowed +because performing name resolution for each packet would be forbiddingly +slow. Methods +receive and +receivefrom +can be used to retrieve datagrams, the latter returning the IP and port of +the sender as extra return values (thus being slightly less +efficient). +

+ +

+When communication is performed repeatedly with a single peer, an +application should call the +setpeername method to specify a +permanent partner. Methods +sendto and +receivefrom +can no longer be used, but the method +send can be used to send data +directly to the peer, and the method +receive +will only return datagrams originating +from that peer. There is about 30% performance gain due to this practice. +

+ +

+To associate an UDP socket with a local address, an application calls the +setsockname +method before sending any datagrams. Otherwise, the socket is +automatically bound to an ephemeral address before the first data +transmission and once bound the local address cannot be changed. +The other methods available for UDP sockets are +getpeername, +getsockname, +settimeout, +setoption and +close. +

+ +

+Example: +

+
+

+A simple daytime client, using LuaSocket. The program connects to a remote +server and tries to retrieve the daytime, printing the answer it got or an +error message. +

+ +
+-- change here to the host an port you want to contact
+local host, port = "localhost", 13
+-- load namespace
+local socket = require("socket")
+-- convert host name to ip address
+local ip = assert(socket.dns.toip(host))
+-- create a new UDP object
+local udp = assert(socket.udp())
+-- contact daytime host
+assert(udp:sendto("anything", ip, port))
+-- retrieve the answer and print results
+io.write(assert(udp:receive()))
+
+
+ + + +

Support modules

+ +

Although not covered in the introduction, LuaSocket offers +much more than TCP and UDP functionality. As the library +evolved, support for HTTP, FTP, +and SMTP were built on top of these. These modules +and many others are covered by the reference manual. +

+ + + + + + + diff --git a/contrib/luasocket/doc/ltn12.html b/contrib/luasocket/doc/ltn12.html new file mode 100644 index 00000000..54e66fb6 --- /dev/null +++ b/contrib/luasocket/doc/ltn12.html @@ -0,0 +1,430 @@ + + + + + + +LuaSocket: LTN12 module + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

LTN12

+ +

The ltn12 namespace implements the ideas described in + +LTN012, Filters sources and sinks. This manual simply describes the +functions. Please refer to the LTN for a deeper explanation of the +functionality provided by this module. +

+ +

+To obtain the ltn12 namespace, run: +

+ +
+-- loads the LTN21 module
+local ltn12 = require("ltn12")
+
+ + + +

Filters

+ + + +

+ltn12.filter.chain(filter1, filter2 +[, ... filterN]) +

+ +

+Returns a filter that passes all data it receives through each of a +series of given filters. +

+ +

+Filter1 to filterN are simple +filters. +

+ +

+The function returns the chained filter. +

+ +

+The nesting of filters can be arbitrary. For instance, the useless filter +below doesn't do anything but return the data that was passed to it, +unaltered. +

+ +
+-- load required modules
+local ltn12 = require("ltn12")
+local mime = require("mime")
+
+-- create a silly identity filter
+id = ltn12.filter.chain(
+  mime.encode("quoted-printable"),
+  mime.encode("base64"),
+  mime.decode("base64"),
+  mime.decode("quoted-printable")
+)
+
+ + + +

+ltn12.filter.cycle(low [, ctx, extra]) +

+ +

+Returns a high-level filter that cycles though a low-level filter by +passing it each chunk and updating a context between calls. +

+ +

+Low is the low-level filter to be cycled, +ctx is the initial context and extra is any extra +argument the low-level filter might take. +

+ +

+The function returns the high-level filter. +

+ +
+-- load the ltn12 module
+local ltn12 = require("ltn12")
+
+-- the base64 mime filter factory
+encodet['base64'] = function()
+    return ltn12.filter.cycle(b64, "")
+end
+
+ + + +

Pumps

+ + + +

+ltn12.pump.all(source, sink) +

+ +

+Pumps all data from a source to a sink. +

+ +

+If successful, the function returns a value that evaluates to +true. In case +of error, the function returns a false value, followed by an error message. +

+ + + +

+ltn12.pump.step(source, sink) +

+ +

+Pumps one chunk of data from a source to a sink. +

+ +

+If successful, the function returns a value that evaluates to +true. In case +of error, the function returns a false value, followed by an error message. +

+ + + +

Sinks

+ + + +

+ltn12.sink.chain(filter, sink) +

+ +

+Creates and returns a new sink that passes data through a filter before sending it to a given sink. +

+ + + +

+ltn12.sink.error(message) +

+ +

+Creates and returns a sink that aborts transmission with the error +message. +

+ + + +

+ltn12.sink.file(handle, message) +

+ +

+Creates a sink that sends data to a file. +

+ +

+Handle is a file handle. If handle is nil, +message should give the reason for failure. +

+ +

+The function returns a sink that sends all data to the given handle +and closes the file when done, or a sink that aborts the transmission with +the error message +

+ +

+In the following example, notice how the prototype is designed to +fit nicely with the io.open function. +

+ +
+-- load the ltn12 module
+local ltn12 = require("ltn12")
+
+-- copy a file
+ltn12.pump.all(
+  ltn12.source.file(io.open("original.png", "rb")),
+  ltn12.sink.file(io.open("copy.png", "wb"))
+)
+
+ + + +

+ltn12.sink.null() +

+ +

+Returns a sink that ignores all data it receives. +

+ + + +

+ltn12.sink.simplify(sink) +

+ +

+Creates and returns a simple sink given a fancy sink. +

+ + + +

+ltn12.sink.table([table]) +

+ +

+Creates a sink that stores all chunks in a table. The chunks can later be +efficiently concatenated into a single string. +

+ +

+Table is used to hold the chunks. If +nil, the function creates its own table. +

+ +

+The function returns the sink and the table used to store the chunks. +

+ +
+-- load needed modules
+local http = require("socket.http")
+local ltn12 = require("ltn12")
+
+-- a simplified http.get function
+function http.get(u)
+  local t = {}
+  local respt = request{
+    url = u,
+    sink = ltn12.sink.table(t)
+  }
+  return table.concat(t), respt.headers, respt.code
+end
+
+ + + +

Sources

+ + + +

+ltn12.source.cat(source1 [, source2, ..., +sourceN]) +

+ +

+Creates a new source that produces the concatenation of the data produced +by a number of sources. +

+ +

+Source1 to sourceN are the original +sources. +

+ +

+The function returns the new source. +

+ + + +

+ltn12.source.chain(source, filter) +

+ +

+Creates a new source that passes data through a filter +before returning it. +

+ +

+The function returns the new source. +

+ + + +

+ltn12.source.empty() +

+ +

+Creates and returns an empty source. +

+ + + +

+ltn12.source.error(message) +

+ +

+Creates and returns a source that aborts transmission with the error +message. +

+ + + +

+ltn12.source.file(handle, message) +

+ +

+Creates a source that produces the contents of a file. +

+ +

+Handle is a file handle. If handle is nil, +message should give the reason for failure. +

+ +

+The function returns a source that reads chunks of data from +given handle and returns it to the user, +closing the file when done, or a source that aborts the transmission with +the error message +

+ +

+In the following example, notice how the prototype is designed to +fit nicely with the io.open function. +

+ +
+-- load the ltn12 module
+local ltn12 = require("ltn12")
+
+-- copy a file
+ltn12.pump.all(
+  ltn12.source.file(io.open("original.png", "rb")),
+  ltn12.sink.file(io.open("copy.png", "wb"))
+)
+
+ + + +

+ltn12.source.simplify(source) +

+ +

+Creates and returns a simple source given a fancy source. +

+ + + +

+ltn12.source.string(string) +

+ +

+Creates and returns a source that produces the contents of a +string, chunk by chunk. +

+ + + + + + + diff --git a/contrib/luasocket/doc/lua05.ppt b/contrib/luasocket/doc/lua05.ppt new file mode 100644 index 0000000000000000000000000000000000000000..e2b7ab4b304505811d6f481ac1ab7147af3e8ebc GIT binary patch literal 304128 zcmeFYc|6qL`#=1eF)@~yL6)+NF=%>Ag9>FC4B0{?Te8GdWX~Rk1|za1nTV7nWXZlH z>pNsiLYARH)+`y4{hoP$d*8qB{rTO$KYsW9&;7@Jd0d|7y3RS*xn9>Fp(9`3fKMEKCOwrQ}@!;_AkfHrX7bfXEL1zk`_vlQ| z`v2+w!w~@Tx!CAk$3kZ?zzP8XEbxlUjV>Jl4)@jQh(rK@BOL*H7ypBdG=K#&{xfPF z9$3*k)(G!y*hvZAsDHD(pd0+)nMrDnCzGsYDSs>A<)|d04{(X@`9e?0)VyD ze>BW%3G`Cn4iMl;&!3^Q0;3?E=p*kRV))IVe~Qx)`i}(spF>vQkNj^wqfv%KJpZBR z_#^*2)LH*S=KK$t?&Cl7Z~6ah_djKZpA6ssSLOfp?|+4iX~&>{>;JFH{om;s*DUc* z?f(&{qkm+EpA6d!WYqo7`v11~-;r@GGIVCdj8UEu8-|Y2@Bj2Y9T|G|Kgei{Av0pe zKt}8t$RLJYUONA0y#H;FQSKO>8OTRxhMv)<|2?0dE;I54{~#UyFZTbHkJA$;{xGAw z5S>lXu2e`HBIGRiTC zp=ZPnL1zXr>M`hFWW-O7&WtuNe~>ZF7;VVYnNbcwXNDayIy2<6e^C4nO8h~oKX~R3 z%KpJ~bY#>&|3^MaM}~jsKeF^6`Sc(8)F1ro52^v6H6lF?23Xcmbo`&`pF6>SQ#bu@ zm38=ImzlZY0@mWkiR%ZF;)YhCtZzRb0-~*qcK@X&-~qtC#)wlGh#m>(1IE;bMyfON z8$mGoD$xX@tDt}Xqd5dY>Ej#(rq_ph0LOna`u~s&0E>%@0EI$fRH74s&L8P~2w(w` zpMS;xlwpOjf^X569#LS8i%}N_wF_7UN7qG*0i%|(UU}DVGe`ueH(XEdE!9b}Sy~2Odr@q5RMyiyP z3zR^2jX-CH>;ddDF@`FGJm`skWky9TeNKu3JP_r7a{q0E3z%nGm}M-C3rvi`K%mdG zZ}cl?FeA+v9E=Ihn23x?|HlOo7qHHxO~2wZKnE8?j{!b&F#^HJ=K=;%O?-Txt*zr}*C8{bI0F2)Ek z4EQfwSLv2q0Vlxw|Aj5YA6uG$JK*z=Z%*|82*8kT_kY(f%pY6kbidqyTl8N0FJ$=l zzhetbzeemITc;1@4-o+7umV$!`F|2G0^Jq>WdCzxCjfTzG-LPzK+ZosMHiurWr(q? z(8*0?jh{ZT{-@~{ZESeyDBE#H-yhZ0!I{vpgU%nI^iBr^Fc!6RVD-?kq+cjI4}(F# zvvkJt%yLinvKGrA3meZx4*!W9FUF#!i&MYqKl-D=@-b@IN7BD4s3ZtCWAYc<33X|d z4k;NYu61R+B7qluWhyuE?5mWXz5amIs*uzu@@$DV1vLF!%0D(XP2ypQs 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 z z<|toeXo%jzHOA9O4frbA{M*i0a&vpnObd23ohj)bHy(cql}VOGI`iM_-cyLX6(t(_ z2xUv~<{oDo!e~#oP+ZJVLw`^3!|`?AkB0ZsErUqHRzq6CpE?vOFsm+*Dd<{LocDrB zl)|m&w?9=yTB4NW|2GMUFLhw+U zrCW~nVZ$QjSClnPwobsyTo8GE-fZ>N_x{eZ6G}hr^OKuXYDv0p*rS28ev1wBr@lh* zqNzu@jHk&>LxL*CGxf8s4KQ>(6B=j~9lKBVQ?{=Sa;v|fUcCX3nqs8@**VZWVz7t!v7uqOXGedcq2U33&u1t_G`EP;;!UMd zJMRm$#bh0E+u89`oku@Nx@=H7du3;5#{ltpGVm6+0RW=FSr6y0(CavN9*Lb?N*XzSOjzD+pk-b{f~|Au|7> z?daV7v9yKMa$=$V-YO8$$C5R>#g;n7(h_h7TS#jU{^vkKN1 zBxJt(U-{;(tJ&Wl{$ipdiANN(k|pj~N@HMv2~t#1)!4v$UwB<8oz)oBo_m(Z1|8MR z2ejg-@|={SBDi`-r0Xw_s&;f7(OQT)Il1R)(ZZ*MgF#=9G|jeIH}1V7MZv9});i8O zsi>|}`;P}hMPwg1v0oEzbJGv-wKLUj{&;awq@zTYDBkqPwAUsKLjYlf{hH_J4`*hA zD*D0k;Bqbgo==36dP;pu;G7%f8Tv*v=y%qhqB>C@k?ZjW!)HDC+i1|esiIY;q-omQ!XmB1j4SB@dUNLR@NjHDn%9^AKryuzI#P7NJwLpVs!TIcS|$}+w&oUeHdb*l zW_f5i*K_gjwk%Ac<7zeTejw`io;=pkLp@caghdEPXf&e0qr(}1F+m&)sSq5Ir4Lw& zW^2L^tgrCyx%)uXixW<~`HMjzVc(I=T?${Kp+pFtm7Cqr=>`}PRfO zCl;yjL%hQfp-JRYP;{E7T#Bw-GxdNmrzn4UfQxDT_Vw(}iY)j9()5FZjE2(?j@o*v z-etZaa2_HXv*qTNGdDN4>6Xjys~q36!!IFIgK-JuDf75{y#qL!Mh>|B3-DNZ>Y;{Z zuRc-*QVs-29`@4`rdCM2E!i-9{k&TgQgGJy1KxDh*}bKF?CiR7h#?mwQvvHSU*`q_ z29iN)U1bNYb!Q}F**k)$`ng%M;+=R-Np*x6pp-cp5>Tv%g~8H{#ic9`YGSo)mzm!C z`j7+r=)2|OR+348I*GA~X_px#+hflmuL>O*bvj5N9$n@*FCrz9QKWU(#wkq={MZ)f z^?q;hY|%&YyomJ-^^Jfy8a>%)6%$iSP3Fzb;yMQ`jHYDD?!>>4M>MHIPMPcrNYXSf zVhK(!XZ=z*D(4R|a0|nPL~8V0llR1aR7X}&_E?kaAHn(M#^q6vTTAfKM8!AdxWzB! zC0V@Mjzu$p7YFG%mp5*Bu{UKnM`gagtd7oFOx&P`)&pl0C9%SX=&k^a@uJQ^++q4U zvH`6uPTy#kCv-p1qQ_#5{l)9LL4bUx+z%y8iJCp0EdM1En3i|3CZGLKGib^bT>*pZ zie`aY@R;;Kp^Pm!Bmb zzshA`AIPycOi#n5ql7DXr8k(5scuf{`IW|JZnhowfUmr_lG?M^LSR1=Vv-uZL zQ`sP1ihyZ=Ye)v)x>5xeaALWXGYb=x09k`Y6<%zHxh(%jX3WmdVLu-nz6qwh!S#u1 zjN${grc2uql_Z=?^TuFC^{dI11pxdT>1CNQK4Z?ew&MS@0B;NF6HAZ2B5inzn+a?~ zA0sw~rH%S@*kXP-9j2+ZO%xYv^st1<~V`_hx+1;i$a2z}hUz5lA1 zVd(l~z#YPJ{Z`lRv#I>xFTO7zR!fKZ*a*HblJ|}GHEFf~u4X>J0+NbKzKW9{j=^Za zyWph+>^G&7fSLQk*@;(0tPsobE6K3*%QjJ4&8R}9%ZcUbQt)(kJdKX`kZ<kY zM1P44hwSf%?~*nfW`f#EH0-K0)$d5^I?w80*W_}Zn_Bq|ccl@Mj{GW(#3Fe47!xW> z*$XPm%MH4iMWvvF(vPz}H>i^apdx^jOCt)AkGlk?y>V=L0R^N!80w8xj2rt9Re7+n zqZoA2Oj-1~I9S1oiHPU}6)8aHGzEI859=}z%(Jt4$ zC;!fW~4W|{g!7JGqaKad>$^-bmqQl1NW{`5J|>3K}(;q!I_LSWH{sraNaJ3 zED-4Cw9xc%{q=4fII#6>A65h!>zuun+=rh|IyN%l5>s5>w>)DC^v_z^76mdaa7~dt zn&s?1`I-C@d7|1iI{s43Vp$&L^WE26_r}id6m5~kt>EstH)s(f8?@TLk5Ih*Lv4>x z&8RTj7>W3!>6XX(P&_u<7uWBXG^u%d6mY+eiS0w_G$z@jT?s_f1I7$%?)xZr``%m9 za&+33rH&Nz1cxBEyKRD!JwMy?pl)k2J-vx|T>GSQz}_|l$)=+^r047VxjG(G@` z>6PVX9bmUICi4B^x#eU6gUow_mxO6%V(!mOjXW8vWwm%{;_xNGv;Xp-6vA}+NaUtX zmEJU8BA4<_9R!_gU zbj_?POWWh+Q3Yn;0vC!uI*UdVyu$stg6{ovl>9G74)P*3DB=YobMx#-arRcszKi{D zo7*-3&G>{*Kt{v5k6Di?4RUISp0PMRh0LO1PHGKg&2Miyp!Fy3NT^j2A66@Z=O>{W z>N{8?CWUf|>hnoIq!)Z!z3ThkIab_jCi6fB!U?k#4zL(rZ0S<1xN&zFK5<}V?I_0f zoUXvK_M-(Hu4u6Muwt)h-~EH2WgGfm+mV%Bm-~=r=``qWf2stFf&WSEhm|ko;KCPC zm?(`deSKD_&KxgsAi~k9rD5*nwOiTYqPc>!PpM@)lulna11h0HP9QbU(Rw++h(f?S z*5O{4<_{~$?I-niQG`uX6iw%5!vk)M=?D5|lMva)@XC8aiMWyvdPXe$UA&$Gg^?9M z=V;W;C$IW^lJ*{XJt+MJI-z51oL@7}!lqjqz?Fb2dqBhcqe1t*NKpu>I3eQtN^Dly zn+*4Px77Te&n}JKybxJWk)sJ0=AM%t-|YG3k;|8XQPDj1zK=8HTNfovGw!7e`Fb7CDo5-?4XDNAl#$v3- z!#tYZy-SqcwZ2p!LiS^^VZlj}O}Bb4DV)ldy@tWSFq%+D$dC9ncO5ur7!Kv_%DQ6a zaJyua^2nW=T@u=~@m_u^Iri(Qw_qpaDH_7GoUQd-SxBrU~x)L1L!0Erqp`dj-eZAWW&Fa8eC(+6FgRP-$7{pylVs->dhx!T7+HVdhW zE;adv&#`{lvgQmWscPM{Ab;YTl$ft3K)Z|h&rF_~CLNqoG-pveY=WxzQ_yn55x?(w%+OyI%PQIIc-A+=()Rti>J`p}N$*w>0-gtOCXtM) zGShJh6z=%FiDplv6;5)^>}PFLgh0OB;Q}n{?7tQfcj`Aum0M9oJWtidHikII{%<=m z=Z79j1dkwVhOnEdF!(EOzla7X>6BGE+N)ywm1qbrN8%Sw`az9UGo#m5!m98MRwFKp zum81+Fc>GSv?lfz0)1lDLLANlU{JVDaX*#qpEQk!N)@$3DWm@Le&Q^O>bU88yFqJR zxMbMX>&dNzN0w)3xu2!)F=)t^HcScy0?iiZmdtUC1?_zWzu@&?Jp3H%KG3-4yz3*f57HB$H34`)A+yhGk*jI#37X7$qorkk^6;E z=mtozxy>?p5_t=G61c+kYhrEG+*+9Ii9~oF7!6zE*Dfn<5z)plaU#~r5i#7>x*Z~( zeeAywcr_?+;&}FEFCo^Y8OA+6Bd+eBa*P4rQguQWD45M$w$WsARuJuSyGQ)-Tf@wo zV}Uw&zm2b76S=@V%>$hrihWn$l&w>eD z8H1Ay`)pjGsPudB=!^DKrrSP2?{(0KkJWXH?!OwDl+Ucj77p%OCtjR>CG5MGsp-A5 z{f8a%<0yB3U+N(Upw9J1-1-JJlm-qGmm6i1aT0G$#_!t*=R$pK4B#sY5k`|HQrZO& zMu_ZJjd=YDp7x~g$M>J!D~myAm_Ap}HVLghV7n{> zON5FFT`flj|2wsQe3_Be)!ZV6xs-9Mf~Vix+*3~+^b9F{<2Ox-+_I=_T0a)& z8|OTPlPh~bHH;#HaT?G&l+vGvty|5HWdFRb2@eW5yC3+aE% zph`KKUj{lI@EDD>wUy5qzt%QQ?hprLESa0rdNm5w8S2-aq&)_~vr@?g+AVWS16P^# z(cH<0-Jtzxl!^1p@pJ=^*`g?hx1bV}7$zel0OzE0R~AApP+$eTQi@$l(bTElFhw6- zGTVYK&i7`{#B(>#eTX#2<=*qv%kDvD3(~fOHgWy5*0IhlOr(J~$F`+UaySjW0cGsO zY-eJBideYI$>q^^wxW$0gdo>vKB9=idBSp5oAYB zFAn1qyO{Szwn?mRY^A56!u`udGpkW5+l?EUiC0pfnw2!ZhCW7RRVpId|LJgY5OiU> znwSVIHl|o^25E02%)}Zm6~L)Ql6o|psNW1xAY6`U0}D@zhQpwZ3W~c-Y&H4M;vduE zo4Si=c?#~X-}LKgqePvBMbQ=d3Qa-enlL z1+35gw6f=$Z4b5L3&uRIb`{aVNQ#Ij#<_EXAYsudeFk6bbhwL**}Jp6{kltkJBwyd zZvWfZ3VPN)b7mxG@-J4^C;51PY^G;#e^DZr<*u}Y&?g|@W-JY(WOAbIQp~jBre`JY za@tX$>BVk+@bTK@8V9lF@8XUCA73_jeqK-k(&5%pJQ}-;(@Of)Yx4$yu&|Z3z4qUW zZw(Uqj*C$~{P7fWKtN#5aJ)*DjnXigpK&8hqb(eFCT4A^+FSSHJy8^pCSF@RQ%2zl z6w@{xn+v}uqS#Rw8R6UbKWHfud+T*1K$5lyZhmWF|nEej0Q}9m$WYbjx#yq13(P694`g>e&IQ-k z*KHg+Qd%F4U=7w+f`Tysx9Z>;IZnK>8Eb& zyvlM<&$dF)QIBQN@$TW;o+*Cv3lt7j1d{l&x2uD9tK<6m4@+~ZqPgvCVc(rXD}KMb zw{haq7U+7Sq!jHQpg80VM?lMt#O(mpvY_YVm??W(?%ZjzOt*F|IFCK6U0pb5^L4Xj zHmCARz{R=J@+pTDX>su=$gIDsKT8c>E6*0?T~sD_ChFh}L`#bkbDbW=+;L&o2qaBQZCBDZOP=l*nReO6!UD%+wQiiY1rZ~Gqo>GoE=r}{N(O?T&L(o%0d-m>*w zk9(HjeCwI^+XX?FIcJL=oFCvOb^A(+4wv04 zyRhm6n9wrU3s3G!v<|fDz73>~R!notjbPX%ahp6S$|&GjiysC4syQYh-8NkV7hS2L zKG#02t$ou@)U`0qh~@T7yG1LlSnfPj9Zmi3+=qtt z-!sdH?jnDrwr^ge2iv9fmRNl!Z5OliNp7pjK2iw(1x4tH>7+OkTike7PyY$1fZ7wu z@j!NCh>?-eShq_iVBxJMmCj`!w(P5G!_k)x0|?*4v5r9E+Q-F=l@ov1=}AS$!};K& z4a#Ea;d5>4ejh$w3@@X2T-_C*fXJ8Z)TwpuYulg0jG_M{UV{i)cZ4_~%W^vYY}M#L zy%HmMKaON7E&vAvim|6gC=7@IY)9Qr?6n^DTvaW&T}6`|^}5IoKIIwb>{oi)J^k(3 z8$I~4FTctr1o|V%W6Il*H)3V457=c}Tg(gQ+EpCZ>J@gvY^VBLT>3Bdcles#k%aUx zS7_e(qr(znSe}2zSTY-~fu;6lD3q;ul-zLkFskkeb=Jz~?Popi_s<>5$^af{_3Ei; zJBr#-R)x;=B@G{Rm zznK%AI*6R(_Sb_(4QOmOoUB_eFTPpys~-N}PwaDMIQ~1@n=dQ|y%Ef`( z%EdwOVbJhSfW?^9?1vHwLa)d`F_&Gr^>d;(uOV+$Mg|fB;VR*JLXV&}+0+hC>DigB z7%2sdInXe@`V_Le4@9#Qex*D56UjEqCuc4@^HKT&sf^3Lf20?e@9DhN2lshyxH7;D zggXRw6yq?HCafu&W08>X@Uc)Jtr$)2fTV1>NMT9Q>pmiZmU~e`2R6>c#>VWA$5uzn zTrNq4j?NXi9&FI<&x%4TUxxOWhI`y^uE?cZ&DtZlKw}YNHBoht3t8mpsz3_eVj`A|Uz{txN zvxqXLr9^&?Y+FJ-+j7u{Jr8a+-Cu}nLo9&Dc5ue$b`1K;&vM3|h1m}wJP9BK7x^Pi zbzQ3~_0Pgm570o(?w zG`v@&Mf(S~7MgD9nLT;APxj@1YGgi{%E#x51|^vh72nzaL&k)Y2P!suCeC*#f+&8# z8YDM5{4r;01WTlS1P@cc187tb2fFRx%*f8QYJnYhC_RKDj*RiHgRe|LpUyK`!B=gn6;$m1!XUc zZK40X1I0uy-J=c&O3nflQyeN(S1*0;9=BHbifMB1$Sm=k2xnu~C$HB#EKd>rwvz8q z!D=Pg?|wxr6Zd;bDN!qpHhRfJ(%OG-0Vll?U~7}y61I}C5oF5Pub?4{}9`|w5J^cJNxs=nW$%s@StB1gwCtUK6EmBelY8>n!z%r9r=~pl=jY8AJA0Ise-q*#Z3QPE(;^0P ziB?WTJN)K&>PG6CGpL_Q{OTLTr&0`Imo_xho8V6U^5#{$T#)5$c62ePMX^hKFO7r~ zIxfospz7NgSrM}s%hFCX=CWG+Mdn|51jh71^y{RxdUljdYX9xNjRHlfemMXoI83IQ zNN`$I?gUKLSy(C*`_a-jdCM3TiqwP{idkPZfYKBdiNbA$29yX6-Rsp{drJp9{9`=y zo4vWEw${FU8*8v&J>|$-9|<@G_IRoD1LS-)(F=p*JD zxidcDO~eTq=vwM6Q=#YvO)QczAggI$?y_;mE?E5&rZ=#hV_VR5LATBXWiIypm-nHb zJvUV@+t)^QTe%vba)t9TfBMsh3D8ST?QYM->B-8G0fu}%l`YZxve|$TF+FCq3HKMb z6TkSb^dH4C=!l6;0-k~?i@H4evv7~>tD+kY$DX-q{B)y`5SNfU7GpB$k+!K{r{9zZP%(S zWPH+X9^VGws>V(QzPnXYqy;15HuLH}q$GS(i^TeKy&7TltAn_ z1Keg^QLFN$&YHDXy4!o;vOB%`awe?`y}@QOl4q4`Eb{4vK#oX2?5C#UDuSQSQNVwb zm`oQK0L7Wa+b}a&T2)<)o4(xTWY*12b7vFp83@$i9`Z8QkxIQ68X7Y1D`a2VM2#LZ z$RpcPo<2{#{!45y0HWAAv!ULV=>6{?AOPM4vitb^S(!0(ab`I9%<|&!Oy%O6u7{=P zshYUNXH^2jusB{cigRghv#hRzAN4M>{@Up8jG#OHA&l-#S_4|SqgvwPWxW8;>l;`5 zUP)i!N@7Y8(okn_5Ox2nGxZSA=(MB)01uNFo8lCNtC`jQL&bs6v*86D3ZB!*RLs}j z{*2IWQt7P!qmCEXpU4_tcWbenT;jrgPPo3Oh!cZR`@j%Ud_Vfl8?o9@P?W{s;r`&78dqLQF*m~N(+S4z=2}Cb<=;ebw|jPnxvYp9!Uo?Qg9P72Mq)7Ea|QBv zVTf%71W$^L-@I01n68n`_~IzfG1lTV2gxV1;Y2b-6<; zi$yE4^75+UMl|2u`3WVuP9WrE!=GaqYr%F2(PR^TLek~i^EU4?tRWSgUIx$=1Xn!G z{RS5R=Zi^^w(jWM>yDcFE~2s5CD8aeXo^zDO`@ih(jdEXfOx1eR%2-IZ$H}f1)zS3 zwfs7M)Md{~pj;~-pPR}+B5KmA34a2?M|5WD=@~KKY72i92x#K7*F1&Z>g2nHcC*vk z4cNx1h3Bn)`7Ic9aY@PUtsNPHtIp5vWMf-nM*+kkol|AX=;LfaIT(oFackd$iUi zbFI6p<$?J5`Nqt)37nC}-MQJZ#Mf@Hs1z6{`j7XmN$J#?OU>5t&H#95@8Ml>i`soL z`Kwl<{VC*!EA;Zv@XSlZQ{l-Tmdm>1F@vZo`32$S+8n~I;Vh_Gj)O~zYgE5(+qQ0@ z>cC~bKB#RNu*s2VKuisTR~6VGxW3#8+WAtxD`P`?eaDc?{W$_Pbt1od{e`nfbMR=Z zWlwCc=g_6Kzd?S}R(?@gQ7O~oleKu}8|3t*q%2(z+IV6D!n?H9U|$Jr%}bT&qi>hU z%1ZU{#`KT+7pBgOsTo{_kXh&x);iC`8w%VMD;EAx&eX}Y)SU7{AI+SkD80ztpD>|e z?C0NF2s?Dy8-RI3p67p`sO3tcp{1v`K7Iu_c6YQ#_m^x5Ha0CY@AZHt25d9clwy)G?vz|OJwBeTq_rw* zMrYIVUu9*}{p9an&}4_mtyB5eHw7;Kk~+@G9T!rO2P=9PI)r};vw3N{ z+D{*vUWu-1I%-S|e(CgJ0K1tRF%sS2(caW-{t1_@cJ(_hRv!YmiEM6B#K_ zJ{tdg1`dmyR4T?a7{v~Xe_O4M90%cXuRpNQycdlLO$ZW(S!&m~-})Mgv)LnzB_!E$ zw3&wM7vEBTRrbUzQCbGPvu^r&tmn5vpG^;qIJ@ft3b-K7DlbaatyDHH=*O~xxGpAwl+-C3nyEVakq`!DM(KD&lq zYa*^C|A|g@=e?Nzdp!T)zrKBHuS?m*A8N^?*Rbi0)z($ocbBZmM`!ILa4E7K!n;|^ zL|?#+krV7TU?vW7kdoHUct2D2mN)S|yI%X8bjJGNzuUu@atEi7E7{V0a&2ZGQ2*}* zsP*GpcT7PWdPCS_r&0s*M6>bS^@Km~K*FzKAKgOUmX7!xu)I$L+tS=~xUt@Nn^Dm* zMP>~WOpu9^`8>o%Pxa&rFUpMw^>oo`QdPNtVLyY&i2sG3^oE*yckw!=Qj)gLblrJD z&J)ToE>Zfollj=T!wff(G@b{l%Y>v-R)_V=P|n-c#7Yei3Szc7ef8rtEQMd@mQJ~8 z_BisI84{0`yJGg(GL?HOxX8{0C0uQGs$d|%@Lg~D#An1Ws26>@baEVd(Ght@zBrun z;lEJjuO1+jTnt|PE4bK@_qtS>y9it*PELDH;uZWP)qpusw)afz=R8lWc7lC=iKn{f zI)3&q3MhFLkuO_cSvnwCnV9=-gdb(<+>#N~&Q<7d4plb7S@JKM^*nKcrYBf9Jp$d< zj?=nghQ8{8sL*CTQZ(ncPY}i@@R*4e@t2x?w|*n)+dOS?yU0QFA=ecSf58gPVfTO3 z0HD}FnXThv$KIye!L^;>agAX*Tuoc%J^u?b_F;R%uXV z3&(!n0S6{dQ(u;3X;Wzt>hk&dh)>`Lud&!XVN6$@<)do;V~|{Zp>?Exi!vg}3DH%3OdB%?%g{f?9>PFez zJoq(x7;qcC=PO$8Lev)IbZyD*!WN-!Hv&p$FZpu}F7SA9 z%wQbrTU%=6CM@{E-5yXGVfIo~P>r45*}_)@MR5Ndg&&Urd+OV#9l+VN5>nyz9o2}e zTwk)hoLoKLLYO;28#ew-V7oVA6;Ro{^LsH(u-E2>jZhu}A8V0l`Ua%E=(5BEyT=5a zO5XY;YywQ3$=RxE*i#%jD77{A)hmnubVCH+Ur;@}cTsYwWT!?}YoQPQ+swKcZW zb1=AIAIB{iq3T|cQAPTZW^d{E{f{yq1a~*uXg)`IK$%&+P${*xO*cV7q7bGl!YDOA za7jNWs8yxs?3oY!eJ5In z5sjvdm7tsNkwJjU!xio+o%k*T>Be0<9*aHkpM+Ile! zBR{IxAuv2yzW#{V4@m$zRGKv<#*tVubtbKXDm`7=9Esi|h}R)Vb|z>36Vt+4zyhxG z6_=XgFrCwMV12FQka}_{Cr%7u=F!wSe)oo)9J(e}Cx|s@Zo162<7B_!LZflZutwlG zok{P+T;PH-xc6_*`kkG)93#6T4eopQB}1;qF#V%&Qa12Gi;xkQWSVD#wHBVG)J~8n zt_=wO6ex@fExBo@c0XzN=R@xQ)ENZhMAP4J=C0EC>^8ta4k31+%U~#|aA^l3~`nS=KVJi4ih#`iYpijWBgvZhKD)Qn&PVsFr(ve~V?y6fRLjX*+3GI^!7 z#aiTsgFKlnt0y`M07!!lDjAl7L26Fqdo~5X*fjMJ&{-%=G~fY7kWmH>tA&Z84G{u* zYzQU}nS0&SR}#QBHf~}p@3yO;W|A2H>>&Kr?%y6yqUS(eR^;2&nFAWo^k~>v!g#kb z&?lv)ZyPs`^lvC$&?Ss@hx!rLH(uvb+rby~yIA zV@%=7!!s3YA3nwR<2?E1Z{`eN#!B<4^+_O#oA;Ci2H=wW5^(zvd6S;xer*i6ud#AU)lUZJ6zo21H0Xr*ArD9(_VYxy{8_?+}lE+V^@4IJWQs4 z(OsTb&Mfhg6lf-X!=#I&&Dy_+Hv_QDl`sv+Vq_flKfMPOgn`s{bV*O3Caj!q^p5(axwdK!3xYw zfW72F(H|?o-api-yT8hZsC#ge89faCfUU027KBWH5?wN?)PnCNU6p#=7Gb&C)3c{2 zJ$#u#C$%}_Cq+9*!Iab0jr&?du^5udj}e4fNyNy?WZf_t$?{{8M>L?iqu1`;IA5$9 zTu*wtXsdi)AA4oG{j|(Ba<}8+t*U}iV7Xd1?HlX@Jtc*iz(UU$^Piv)O)@vP*3)oN zVVjzOPy72A@T`2lFo2i~DyeI}hYHNGm>GIQgJgedHGfZf%-)CfQkt<+&rs|JTnctW z>ZVCRV^kWWD0?)X$)P1sR#Rke&ch5*qSmW36;64#NgIXd#;vp9jTImeI!H4coY_zi z5QTD|9uL{BT!nCbJAAH^H<6)NPgyP8;A(Dc_gbsv;}C_w=@^5ap^>m)B6Rs_F$#5U zp?}k{gyR*sc})(K_N%Q7>gU4*X1LFKD?irV1Ul5SOD+5Q9TB2(bJ;nZ;C`72A{HPr z&>iL%$^h?M;az9b=VKR@W=g+q80%%r8?rbTMkcsZ^oI|TrJekVVC%+{llZgsU9yx! zf9u6x+lVu=;l;nzjuSRNyi4bOA8wM#S^3`HenA?JOyO%E7^xWP-Twr&kPF?(q8hq+v;ZEDXNH$+L{TkIwx@^T ze&h!Cy!xX6TYUfR>6R8ABo~kDMf67`%!y0?w;NB&s{ZYS**wM>lL^hjnK5jTQU?bB z$jRmO3Xa!wa`HkO-2{&Gk55kcM~2*0R?&!wduzCs?GAz?d@aWk2mHj?RQZ;M!IiKdBUytQ^?0#teq3Q@(kkg>Z0u@5sMp26?w^rogSB~{wimz3 zRL{LLgZbTF%-#l58PY~E(ne|69tM55v?E$y3G`k4cK6%g!<$^uLh1ONmcux}df{B? zyjkC61crU4Qai4>ilkmd012l{Gjcc+4*7uDVuC^roQI$NbVRaq9#;$k@k_po_Wb$4 z-em|xL=BeQoq&8%WwW|8ytTXP`K;KDp6pmiq_Yb@`5AbzKSAoaFcg?m#6V#su`&p#ZGvupp~FQwf+I9LrG>zXpk^?HhAu|h#W$pP&f2NkeQ2yz zLeE9I6E#z$u4a+?F*+@(@U#9k;D6EwPg6uWxjSQfisP>hP5t*GHK-8sWD%y_%Pi6R z$q_2g4ne#}U%6DV)n;zEIV}Wv2Y5sHQOolMnMoVHs0{z?q?`~{p3_4_BWW}30q_aa z!69swDS7B~KR5 zr3aM+FgIp~?s`13-y3JqFZi2-BDtQGmtV=LU8jEB$-Rd3G` zTC_H2hu(&(!-{gV2D&RZRykESpFb0eC#wH;yj!p2>)sCRBQ0%hf{n+lf)`l8g5A-sUZkprV!f}mh(mdA zUpg^k#RJMi$qkzS8sr;)51Yw-hg2wrp9LNN{vf4#HoJYeR;Ehr?;8z=lJ~~mo)<)( zu1iJy>$zCmRx7_pGs#SD+`3k!Mh5_E??l<*oC&7Zo6*Q@mFXMyGHA1?6z$%jQZB@Y zI6U}`3)M0o^sDtg7@rN3I2vZwyUoMGtoGY}xE0O8ap|hPX?+EMGv}NX6*b=58Q_Zn zT$dD4yT<8&5-sJzCz`U~=a0BmSuZUJvhZ5GZ#r(h>G3s*0pdSz4FJ8r*Hq}H`5To@ z<1ebjq%OJX{Ps(HPqYT?%wO>)5(Dhq^={lcJNEr%gy0Zc+6w+i2Y6+vnM6>W$HM{KX`9=S5GgF8pHmg| zB7h9+X*=@Nm#UontOqI5dd8ocwRhG-+B#nEu&WLy#nLs{p6^uIMjWnOOrBG`ZY5m5 zlz?>Go>g~ZZ+~7kBnQO}zG1oyM%V+j>cJm|s_T@O+X87eOJFg*Eo`{|$AvEW<1dp6h~@l00=T?1KrF}>&_ZxNqSEN!Rp!0fE=$-khc zdzZL$2hjJSs0uYYJ)@e5;W%A4`9S0Z=Q3QnwkI7?LcNLy?v3nd zOYgP21EO{`md@ArSDq9^q@OIvTUp?nl~vBqmkj5&E`)mgKcMR~)w(mjQE>=D2&ZQR z(9;lq8n6>kg!Y=%V)H;916wJn;;p!iXkS9?k|v0aF+WCWq6deM=1Fj?nVlKWtbp5@ z`y>4$Mpz!!9y^hW~TaXb$K^zkrE+o`9RyZjWE z!OrLlbxVXW+vZggD!s^=>k?^EFcTLPtq|kYLaP)85%J{G5im3+Fn)Ia+#&ohr*O-tkG|qj6b4a z5UBwzz)UaTTt4L8({*|kPgI0aF{oZ#`BCfAzbM$>{NJYDRVjlC^>YYSwgSf8Afs9@ zN+-(LX`kn$jP*x;mSY}EfTD=B&dOcOmglpLZS^en`X|A*1tX&;E9~|? z0Asx?#EMMyn$mt>C4(`Tu@J7Zu8u+2u82F0?&F;4R_o&y#qaemWnFghiFE8FuU z;uxS7O_gp(b2$|)icNyAU2suc?~V?4_D?5BYx9x&q_f>!!y6~(!{!oz{;z9>5f`i% z$5|JPZzH`;rj_(<#2GVpm=ClY!Eb22SueTNCX1r8*{euz!PJV;6EJ zunke)dqIZ*_ik$6_9POq+Wlt2%2VM<*!^Db3FS7n;YCuVGbrW_fW6^S;Fhrkv9u@= zY=7ABvJ&lbNKAuv(|k$}dby}0951A{NnEU{(zFP|n7kI$!Y33zWTMdoh?>Il6k;#I zGDEPU>jWgxbq+d4BG6@m7#Y#V*rdZNeaC&wi})}7YRo*~gf#st%6eitXd4QPR)(9Pp`;}Poql6H(;)R-BF0T?HsF6Gd{p0rdF4nEppk)zsy z?nLYDM**{w2aD$&Rw3tyOiX~&Kl{6HBMzh@FOH}*RR52;WtJ`#h3iB{9yuU~jnhIk z4emVXrUor7x(_wvKAnyrJ-M`~KvVk`iQzYZ6h5L;I;opw^KnRoM&%scddd8*0=8n) zOQ4|#bt4V!V@Px_GR5od#;x27UBwr-KMcNb7Rp}5(SM0a2Ddd5?=b})>@|?&<>x=* zc?NO~^b}j$!wb1nS*f|YIa?4Nqc-*4ljwcgK*J7Dvm&_CLM|E4H^c{~9;K#|ClfSP zanQGqECnJ1ThGr*GSHjT@|$Q{&oYjPH&&?Z9~1C5+KLi`O8Vqlp*w04U^&KmIfUct z5{>^7h9dP_6H_rC=`W1sF_qB`Rui}v>R z14U!U{$>2?j}S)?TC^8!?kElrrnbL&sgEY>;QLJz$GZgGPbR1HYkMBvl*PQWS-YO; z+A1T+wmT%exK#NC$Cy3qZ*IQ5`FvWkEWE9H({*0zB!nm7VG{7;;^4~nM!g$$pN+-s zH5^Lz_o=oYF?a-UH7<8gR*(v0n4Juu{-cz0qjFky9Ge*L{+z3tW1e@F+*63=a5ljA zSy?NTN$ItbsC-|PG>)^~GIKlpZ`bDbMdz!JFAnCWpL-tu^{4LJsuYZF%r^MtiKMwf zAp9L{9zCl@u4-{qCD@E!$ zKdL-7)|DUv;4ND^(MfeqSwDy(#Dzo|<+&Y}(=5Kqmq06wSfOS2j8Uj=6VwbfXmJCI zV3$4&XMoD>d}W@Oa(DNbdZcd_KyT-Zz6A5Tr>e@KK;<^MR3K13#ooiRA4YgA>NbNe zp3o+vyp$1TiG6s<8-`Rrh|erROT1V5w2JyHvL|?9sO7{Af6gbaJG8d7-6}<4%C6#i z7QOuxtQPj1{T86XEzig;D!KXF+XDX@9In0TXeBKMcMZ)QVFB{qSHzU`Kxy_5<~`&; zG^2Z!ZoeltTh31oYMgCdO+9grjU4QnKP~3@aY*dTQa$-(NKK!Ca0*{%((tZsE}7i4 zX)cg!6h_6w%0e!gxdFT=rq=uJDCB`ah9KX2!oEbQF3#AQz@@hDekTp%{5$s)BB}C} zMd*)2K$IGU16(nLc$Y2i)Kr%921Zl+{Nfb7fMpcQ+=I$XDe92y4_AQ*(1$)mTxq_2}T76#4w`-#X9D zOtQGn%|gWAkAo-XvDQi5rI8D-8a{<0;TMYisPy}gE-wZUWG^V8k+52-32 zw*G9_*8w{J*DK$8y6km+cp`uvvU6#xunr0|LztU&BDKd{Jfn1M*m#D&1Jarw+M}qv zMG;{GJ^E{1jm%7ef{;q%QcnBY-WawYa`E2KuCa+bPWD{fx3sL9|q}mENwtaJD{rugL%Qvfe>+SGuMagt~d>k{fu2C6b ziO~Z(nDo_I>DF*$FTD!Okd2^y#M83-}$ z`5Vbkoq1;iyscJOg38DI{hET$eolOxIapBsHJj6zbsxR>98Y+S*hXHSe>Sjl`zjam zbGu4a5t8#Wpp*AS>^Es)oN-lU98Z!kROCtpwWq4Sz7TnYVyCB1{V&9e zIPB{_?VpNv`t!_gddB)gL!R>Zn6-P`Ezq>I6ycz&8SY+FKk0Vc1`PRJ^Nb)uO~fOg zom^YQrzv#yOp*@T1f7v8Dk{KbrTOHG##C2!C?cy{!W6S+JgcT(CSLzgtOwr3@w#VM zdY1Q5AzoEU^|1k~@PCB%@RP&E#rvUJVAc-)$yqsSAIbe?(_2hOF7Fzx#4|(WK(9ll z9q;9({aqh#w`!b^{Bum25C2nk_woENVls4j{`}L$8SBN`QDoDpzy;I!@O)TMC^8kz z#)GwZ|KHIhEZs7#9}UR^0}*_MG)_)1CX{ALl6RaC;1ye;R?CyG<)sMY5Ida1eWC$a zH1<{veV-(9q@l4>6|c;1?@n2HIR3pA+O=p8@xSZ#Z^|CA^y08W zR&FO;#Ja~1*O&(L$HiAXFe1fXl2qwN5s*WtF9NTW(T)xEgU@# z>W{J_57M{Wf`V_hj>phy&;okK9u0e-oKOQ#5AoI_00&Gd4eSGe16bl6om?n>zr_9P zbvT2Z@OPz&ZY73WU`t)Gx6b&rfF=LruOv=lFUskOVII4@Y@VX z<4uqG`J|8++12g@Lk-Le(b7xyI{n6Y=`vUGF--Cxe0a}B7lOkk0}zABOFj|7fqsF0 z)&s_@@+g=y{7vIg>ZqZiVQMNXE0t&BS5ExFmXwlNP2C)2s9d!uqAYk~>t-Z`H3DcO z{bmoPNx%P5S|k4aph^rIwugNuUJ8&iy|)(T8}nrar#%-3h8Jg25$A1pZm8uwh3(ez zr(o+JQY{eN=M(NHC_uNP-rHaN2}0qwM6ue*{F#7K)6Gka66#8tjCGGC(J1ataI)X+ zUM1N~*!rQ$@4;s-Q*&w{%%^g(!T+SxJ`;3$2H_$eF7#|U4GlRVp3@w5VjQqo4gY|n z66{db<>VjZK)>Wq(fx4J+EVasx|Bf=-HDRH+O_&^{YRk{bWK)4QL^rt(HS^34bc{S z#Q~XI%K1S5Y4$qfgG9S4jZK6{N`ShNzMd_;H(!*2rHV?PYkhrdWqoC1dyeJ-oA8*= zoRQB&HAT`}UIc?^T+l;l&dO5k9mM z?<-Ym$YiPcnaF>Ze;aMg<|FK#|DGRym_Pqh5OLTxrTTYag?05ucwVMjFJt+td5-vl z`9QO>3U-Vo8?Ha#R90JyvEMY__5LH}keJLw2{oJeL#(urvBeC!jl;w#qnF!Xop$Ro zq8u9+DtyyQ^Ofy~;^O`I`>$XU+LYp)9uzG`CXQ=0Lz@vcK2+nXIV5FUa+h3QsmL)U zj^~7=K~ar;Iq%Vm%zZ8`Pgq3kSfJ!^)cB3UiS1 zwGUGPzMQ0uvPAbsNv0b$H5pOcn>A);99g3pHk*|T43FCCCka;SC_jOFKaTk|)n4Sj z;>Kl*q6cUB4EpLxK-BWeAGE@`8?`Q!v^F-yYUtH*u6x9H9`mB(AdtAeL<$Vvd+(BI z<>gfcW-@bN7GU&YGS>khH84@_+r?JA{1l!=7rr_+MnLS+fg)J^!Ts_f;Q;SEUPq;a zGJ;6wf9W{bAe@PKq=||ycS*KLQ($PM_gnrI#60Ob?nVEIk1=kmd0ZwI?IK=Z*Wcoq zwdU7y>%Msp8k3ua@4GHwr$)fn&CK#{+Th)Bz9e3jJ{ovP`61U_{^(|dxN+t{+1Sb! z`DAc~efs-rXpf9zt1E@)kiR5q2v!Om3~sZTywY8@U|&&hpyPyJFQj)54N_6fmKf?oI=wA<|$-@ptr?R=?e# z(N0B*K)?7qI6+X*oqb|0i$$UlkwSuewRy)G1+T_Ni`g*kHt7rc(z*!mhQ2NcR+rsC=5GSe#kJ+OmuGX z5523b^&fF-cTuJV27LYJaQ5tYm&b(1sE+NGy|9Lh)UQEPo+NP!f90T*MK1zWw;DFETOYU_0VgSfqM#U_to#u0Z?gF{>)oXwn?EW8P;ugXn8! z*A`9tosZ8UY2HQo@@HG<1Ebg=qAl;!&_8Rtb8(mj93&cP>?Z8Qn0iM-1E7P#BXM(_ z(QY;1h#!;D-$aOzENy8ybpkLB{k7+|{Xz@ESC-?p7I4skP+8^h*Iqos#Ke7e#_zXt z!vUI&QD5)?=m))y6H>j`LZs$bZFG z8&%)W)v?=~ZL!_4dBcs=7@zHdnoIzbhOXdZ#YEqRsZ41nXp0yy2kO(J62L{mVgXk0 zF;WWKjFKVECVzRpQI`4*qrC=quj?`fCW6-<-hWDh-6=uQ7M9RD?4TL^sXtYu=h(vp zC&b^{&R+RUy*UOOk7~qV?{`f9S{w&uv-GVTuEu3s`xW)bW(DI*$AbzyC1M~gHEmv8 ziTs7(NgigKL(*ynG-!FaHZ`S1OoV+ly$aE*m^N$g*u5Tc5w{ zbP;H2;dyqxlu8{bkI%OZBm4u4vy`u9tSh*hdxYm|&dJIO;S#XX3h_Th;8#i}VH>&$ z-00>26MP8cSG`Cp=3p(ImHMy{*1pycl z(hd3|__dJWK)0-<1{L4prjChbkiqrpM(?|)mwLbDZZ>4xqN66{F z;lTHm?Jr{K02N!*#nr#Q)N_h|#Mv2%ED))U1LgyaR=1G6}m+Mqq=`bv<$g< zl4_6myOExHuunVxt!w^^;XA4!Vs&dthqSt=@TvXI79gsm5VxVhhQ*3~_hpV%oGiGSCW0`JEYXt<@ZU5;#>Yj z{<81r(c`w$ zg^8-WquR^b*D5wPRwqv)ET=xin-sCm4^JfJ6?4!Tg8;x)*ZkFxR#KrWS3ZZ)WzGC$ z-)Ys*6UtbsN{f=xxme=#g%=YOoV@;H^20bR38Lk;B)))uC8Ee7%{Gt~WL4i?7?#xTLCw5euAPMb?Eq!oCIjm9v)wb)iT!~Xyy>Bviujt$Q#3#G3(l0N<<*7rE}!=Bg8|gn=#7uo zHVpypTfF~H;`>q~&yM%ZZ6h`XPde7RuZ>#cjhKvX5BegqD&F*Cp!JvFDPm4FH^RzO zG=X`axsu)1hQ#5pH^p2M-GJ>DCaaHlEu2yly|ZtMCxh(;wcmPXO{t!GE6&)Zv3a<#mIZ8{ zG?TqlBlf;Up06BlnXk{URQ^l3`mo8Pj0(q@Ga-_C-VaGp9*+j-YCDvdCUWi>T}C~W zGuKypbTs#zNnXwYzgocwTgF+jym>Ix@?Iv5&2`}lw_g)T6EURZmIsL+ibmLf97M$5 z9kU`2eLV8qNb8E7RhNUl&&XX132nX3RR$Kqipx%@N${u8vP6U^D9?0!YfvMw(NG^~ z?80=rpZ0f$uLnvE?yk?|7vvv#__@RS(KId*$~o-8%gO*mdK#>&n}KO=rbnM`wVSGC z4cOqBXf{cTSXG-Ak7M031_t)S zel$Fi8b04SAf;X$7Y~vH=a2t)8n^%$OH!_Yz+8>!!7A54Zz;^q-%OtU!IAsV+M~(m zP+I=IQ6nuttx3JN-=h9*4+@EZ;0769-K+To(W4!Ue+X%k;K2ZBQJ95SW> zC99A$*Ir{z;GnEjpR1Lpt}^I-OTzN-aO==(?1Z}R{g{CHFl)vTTy_=O_^>u>vSIR( zBY8UF2juhk^~3P(O)5P~n>N}~7dZSk|M2OSCxV9_W8pV3I*HI&F+0l14Md_gl09A9UOQ>F zSi?%hDh4d%)2`L>+W2!$5^#;zP!Q=ouSf0x>I zyz_$!*v6c7EOuWb?ljb7?F};oN=ler zDCc!C&ttcWG1Jl#hA4y}BTAgTa5aFa*|tTtY&==^`^7B`ez@O<7UU8DHGi9 zr)j0*jAs>#pu+DNwF&fu6R)pVJr+>(f&h*Tp!9T1?#3KeRB# zYNsySVchPE%W*88tqcW({FpdwW1Ei*y*qrhdnR{Y<+foeHEd={K3rV?cNpKHbbdT~ zH{!PGnN?@Vm1fV)y_Q|o@X(9Z;nL-BGUTVXE33IM7lYHQZ+O8Q>~chQgVp$Kz~eR9 zc!!u2PZfcyPgZUAaB-D?uJJ|+i}9<1oOF3)XryD@v#+-(Lf94;s~y}zO5kg!ZUKy6 zKU1cMtG7?uf4fh4kgdp&D8`hYEGvIp=hO}>6Zb~VmxEyP(Mv4oY5BpoVO|hpG}Vob zWs`X#;sQ85PWu%pmlthr%Z6P-rtj2U!M&Krr8_G84A|ak$Sdm=o3fc1k9oO9Y?_-y z^o7+1++|qN-=Ez(Sl=RrTTgxym^xo9J1UrKH-r3@ONyMRI&wbViLiv~v#yCDu-^6~ zbPHgo03ND6D9WBqI{^U`y_f%+1ZWMxru20{Sdlkr~QIfj#agge3Uqs-?-Xqky}fjHYip|v}| zAna~$e99g0BPX7sg|7__9#y0^m2}hl-UdHpvcD;(?HD(ij%`*}39vgcmy*D^B%KGp z?%2=GcK>N{QWY==z}u|xRwY`il>r9j_kk9z0=X5YCEdhiZvLs8TG!;|;70+rXaIU* zLl_i*R}gS#RBkO%TkHw+bh9a-lEd&w|EU+Ot)ljzZn(_>_HZs%%g0&+O5EjcB5F4|U_4I_6dp~i%v|ueqh5Y@B zyaCB`UXK`V)|Qs!W-FG3i9(c_VeEberQ@%Bb;VXpLj?Mk3XqqCRy_-jIiG8Y17Af{ZaYt#@x*G)7P6$RRl=dbJMce^spp(UJI1GsK zRa1Hs5=Q)Pi{%l;vfNl)MluAYyUCL+zg9s3)1ea7&4| zyDapN4+j9J{Iwfp^BWmcf|w&kV3GPONTkvuVz(Id@)L%kr)wtkB69zcn;LeTKC*7B zs)Pr%KC(59^T>&>IT61`u66WiJ2Iq(*3RO$M79f#8dXpC|E>xAHutPN=kEkXfn87D zZyWob;T!B+?D-cvu_(-$iWSjB(j;#rA-yvGo+d9M;x$E__Vd$6`zs-2YiqA6 zEL6t#Cv&NY{4e6_cPsLdpt^H8HY6xW`ujV&y(&dMHY!0(jON{9RoguT-;bqTH%|Ir zl<26SK!ZS5*0fdU>nBgAoF8RbC*Pjt3a?nk@A~8Ux@Jrqn9}Do1yWO`2H!`7LFGORQPLjkB=poEhP#s?d&Vh*Q2Bq z!zQQzRVWoKa^SZI^#ATf#%D`D8PKMsz2P?^R(i<*t2A*eTR^Qxr%AiVn-$xWC*R$b zouI(VK*L=$_&&KIwNUHNb*SJyT2E_%Vyak0ZnvR+x3RwVLA!m+1s_9sJEkL6iZysQ zYLWSd$k`XVn4$qwiGN>1EXLXWq6 z9^3o(_wOlXi~PLp{IL9=M|6N)RpZK7*;Z7nQLLzQ=P*Gp>3F+5OC_JHM?GgO0 zA)!V!Nnv9YCMvnYsh6zpD+(iGeVw6&e%1`0MJ45*mWBYAPc;4TbvC#Hf}v6>;e zhVK29V*fq9oGwj3g|i6lldn5G@nXEVd8;v?qv4&c$;WOZQ5uon@gt%W5yD<-3SxdU zSs%Dg<&q^g7Gwqr@XtqFoJF*KlInf?uQmGMpZ2%I&B48;n=8f?iZ4;<<<(CudWwc| zka$sg?x+{Wm9I@CSDcEg`q`+e#* z7*<^ifguBBIMh_oy1rku@{b!D8n!)SxPes9Z0{_0vG720{W84N#*I9#pO6dQGRsHB zmtCgu)g4nB-pkO_!J81rOh?!791LnrLROmPxoZSBvEE=yb8jZ^ah#5UpJJX1>=FZ* zlo3{2pJ3^QPbed}|2TPC<3)~m)F_~;a<*=JzQ0J?KG(z#XE9Wpi}!L51~ez~n|>U# zk<4ngWA{VU%V4?1_%^OVqM_a(4)9eoZ}rusf(P}D4N1&$TgC1Ic5G4fy%~NLY0_*E zpJYi`5s`m2$bS>nTLHC)@}|_1TMj+sAJk8k^8{*j5`qZdZT3uc^DeD9^m~Y6I*8tw z8!9O|R}y9#Ncc(JgArrY$YkK(x*?L!ROqrH1o0_ORBFgHovhky3<0kP^SqXI+ zzA+i}K?Xr8hGVV{Kb3|_)=-ut>&?Nz1LsHlww~K_?Hv(eA8lhA&ow}sos);BzOw|8 z)mH#9zf~yD#zGN2cr`w@5GE5t^Q(|L2Z;ZL^P|L37T7lEf&R3Qf4Fng6NNT`dJf(U z-n~$a$2^Id%7?k8VmiUgrlIlq>*UP18c>4I#P?^$##v7M;L4w!)dJ(Zw}TOFlCZW+ zS^d{wKVt4B_8d|rq}QKNHkRx6vsdvoY3xxA!f?&LuJ+c82S1J7ewvP&8`crsZqwYAbRL)}Sa^|Dn7`%5a`$SM@MxUHN; zA(v6rt<9zQmE*|M-PDV-4y7b%Ji87=0`A-};7cpbF9F>pJCPBK2LJOvDU%v3ljP{59k_3KzU00|6 z2mB+J)^tYBRMy-L3N4Aq4<|YR` zH5M0>-x}MMI$Fa*9v_JSPZ}W+1j0L4}_>E8`i8WNp zUexNUqN2~$Z3h>um>`V^Z5-e^fX3TtV4@=VP0i+DfNRY`xZf4zvq$3~L3Fa(cl*~g zg>1i6Vyx2-4rizmKMm`ptAPhWmY!17_T%92qaIs+>IJwM9M}~ASoAfB7SCp8m6b{U z#M1Le)uhfbynDi&E+~Qo58amlPLpx7Pb)k35sC}iqUvUYas^UHs9-7wZtExikd0qW zvy1K!av8iKE>e?P3I(vrn|&x;rUxed2V}#xWB(EGFw^YwQb3Y3q*ETxSB5Q3f#W)*~JvOar zm|PG{CbaSngjGhsjM9u&mJln0X#E{0S(De_W|(ucH`e_HI(q`LY3?`u;GVn7TnHYL zP^V&rh4y?vms2y|<10e8i-_*-HF9^ZKe8hCwfA(1HQ*3h3N2+B*seA_<>>&69Xmzw z0SpWXI6^{JP(T$@W5e9F~=J43=#8 zQQn=~>Sb+E8D0%D`2v)Sop`SiInrU)W=*c-ypqzXeC?fSzs?@&N6fL#k{9xW`0XFt z>m6+|I_vqqIesz0LJitznSUFt+h+|&O8Va)URfYO$30Mqvac0TGx1~#UAhNxErl&q zea#BMJN><(%75?+Go4H})IRExL$*I)!DhfUF=`G1$TrDaog_(En{$g4D#tiA;kT@B zI;feL$;bs7B^34)RfUveYpbfmtJ{=2s>x+*J|x1Wu*}ygJ)w!%chuC}I(He#Px9dW z93JRg&zZPL+dK58ewi8Y2;zrpPe{(lHdbh2)WzPfGtqr(s^qZ$-)H*p{bT>DM7N%Y zJ6P~w=T|yLMaa}om(}N}#g&-Ky6w~oBsIkoCG?TEU z8L>}477nURx-1ymCy|yevLw6j+kvX_bf714{V>AB#-SqCw-Y)+_NrK z3A8&yCt=BBgT8Hf{Bz63U^a&budVc)KV!2@r2_YI;uWv&doZH>zyaU@j~g@&KHxV) z`7PPfw9>fjrbY-v(HSt+wnb*gD3&2b13-&}TkO{{^a_QcSRIZl=_s&W5e*3ZRZI}h z&LPB6mTNqqPA37QHJa%G_S}Ac*~fH^TK2q3`&-gwN!r#ZD_}N2U@W7eB+KK}pUZN>%P@61zKS0N`TLuxBg$HsAeFVQl(m=ZUei2NK=jl16F;DYg)|jlN6Ypy*cq~| zjiBnfVo|zcihLe-A+IV0i$a%eDBA*Vc|*xG?tcXDXl{#K>a>CApxF3+1we_FZ2<^< zu~?FJ&{c5fD^oKKmg3!vSC-7{5M40(*(zQSkSwE6D&(2KhS>Z1U&BN<&O$$)#jLnL z=Xko06euu%@%M10Co~;s3(9VnwmUm8NWklTsaFX*p#n8Y^3)^M`ziZKa&2%$);x%> zc(1TUYbThn7Hq(nAPRAas!XKu%U?68kpo5j4Ktq=OiFy&#`@xg2(M@5^mrGm*i&=F z4Fk7Yb`chEcE5g#I$m8my*z0@cfEf+>`EB)^J3u}-D^?*%^rnE4s-Z1VFB!WYxSxT zE&NrcwwZswLk~)UoAaJL2^Aw_k!MQ+9cPC*-C(xT%wPfziyz5Se39;MpfDOJrXMiS zyo~C{h*w?$8tF=!+=oqQmzHFd5p-|2HHKU3--h7bFgXK-V{XHJ+$=y8MdqJ$<6Seb zS13YNNwM3%=_IUO`n?b@YU9nmpH(=T&RE>g@T4Wo>TvQ<L!esAu0v4M$B@&b1GdEJPK z?r(~TGGAyBcWFl7Q$QX>an~m!Y3Ez$|FQQifN@o2{%11NHf@v4l(f)5g-a+jg*2oO zu(Soz2eq)JjcrjDaLHt5(u|$VgqaC#MI;Dn1=NDLg3sc!vQ>+ZRbJ9nc6}^rSJXvC zMO~Fu|Covol$HE{zwg{L_s%nuG%YQ?r=6U8&-;AmJKy=f?|k>1-#+}8`Tw!_?I)=A z|FAxE#jU@2`r&^$v)Q-!#-4Be+j(z%+5Uak*G+oWv;Vbq)lnCoW^!lH@Y3P@){=moo`b_czU<01N;*v`~@vYDQ)5redJ(v7P z{8JDAZ~TJq-O=#r3)ih$dwQ&MNA0?+UK{RyYV4tzT%p^~~nqefTfmU%6*f zdh3jPUcKeO;^^2KQZ=u+c-8VvzpZ)X-CzFcH|N~*=$~GH$$7uNYTaMn{nzvb z|GnXo2OfOT`ttj>bhnMs;#+R})%BfSp?5!i+rH<&uP$4$ zeBMd>-?#YmjZ1(2(O-ORX#R}GkL)?|D)q*lKlt)@uln)IdEfp1!&iU$%2iK3a^8bi z{^+YedGLXGU-(knfBx+cpLzJ9M>n6lWO&M+;RpRo_atD?`|-Q)`p%;FpK|iI1}=GY z>%0Hnf6FaPOJzPF??eaWKJfA#3czj4mSpMUo~Z;f|E z+pavJ2`Bcw>0eHrGv`Ae#05&f>gn3;zkl{^?`t_d;GcclzD@Tw?!Io|FeJ|W|eIjX{D1!Vy>EFe(LC52`Qm$aJn%2|C-u#` z@zRF3ynpxo-w1zka7W0u&;Iq6>p%JF zKYjj7-~aEwU2*kq8vbRj;o;x&5kB2f zNe;hO-jteX`!j9d#IsU1sIeft&VD!fCKtRn6uhI2;!OI&dJEs#&Sw0|r8n&>=L1aQ zO#X7|N`4tAAOe1aE_fjem342&py(pGrl|BbcCOT-x+rN4ET(nW4|8{ zpRj1L-#NxC=M>vFFk)L7&b&;|8Nl}w@H6$%C+^X2!jI|b_&y6i3qLy3PUEwWC*#LD zUS|88W~`$fKc-_JSk65AeZK9_wf!?~9~*v>o@2#)XWRY)_{_i1eqRKi?K{VQKi7We zTw}h)whu!*dB0HkhHl1ljhZwx8-B3^naFedN0qkQE_dR@hnI9*{=1W|df|-I)?IPK zosn_x=BqPB3y3tp{2DqacbATc{$@B@r8hq`+Nwh{dWl&_SFKRCT= zim*f)aVUF`4?(qm(z;I&ccuq)+8F_+-F#+8NPXiyOV6D)eF1$oFR1?J-^@(v1wZGi zxWD^3EYX6W+M7fM?e>3f_z^zsZ3y~yp=tc_y+iLvk$`}^#0b~I~epx);QGNkP*b?2` zSL2CQ8NU!DYcIUTx8n!HkKFd$@bG5<66Q1@z@vb`yh|KFbc(9Y>&^{+NE8>gva#9Ea0f^|X%xUR)<169V@@kaW$xPLk8SweeGk4XRHC zKL+3Q&RHe*1nu4-ZAjOj`x~-XQWFqz@r(=$sT`jrNkTc&Y3gxS(>=jKNy9(mSC1|- zl#>h0yS)PISJY=yW;|&w!C8&>1pDn&hqGO#9nLDgC%E%PXorE9fsc8&DP|5{4mWUQ zjibX&>3}Pj3!m*=W|4GCHZrIa;=(pNmkXYDh=Hh+%jQ;G zwcG2rE5zntTp>0u;|ei42n5-6kq*!qEOU6AJ;7ZPqhoWA@-X(>uj zV(ExnyB(fsPteK@U*6bCzFd^#ubxWj>c+3#raad;O8Z=+DDSQ@ly}z%%DWjqyvVz~ zj_FrGC*L{aX2#p$;r0ZFa=!tJHje}e}O@46?rH-|Jw4<49)I6U#uMZX*# z`^Z`~9Ugs8u*;(l_RKhR^TObJAUA#8m#d>#rmGUgJBN@xqIQR?@|?3 z5!9z^{4!GE^OsN$m_XqJYstaGp(dYM*4bnWKgO6U=|7DhSst6Elqfr%Ei`F;(rUaj zhzzMo!+hqMmG>sYeNp&b_%V-NZcZqhqL!<)ilYMw#Ok`~8z%P*b*W0JBz{@79m?I9 zvY=e$Q%Y?{&M=;O@eb!BiyM%w808~#>#)46wE?Ufx1Uaw_8r48;c%E=`~Gp*0H~^r z@!tUW_V6QXPuk(z@%T7z0F2ZffbS^Y06^=t^X3HL;L!<>c8csUyLQJG zO{x-R0L`%lmlKH$9>0^v?^Fj90`Azt6`CE6j4L_1cA(SGPXpXt85i<6$oY7* z*rUbO(BfkcS4JsUIF@rwp+~iWQ7!d{aNNtBDNjn3+sphT;*0h&b@nmsWoGTYOzu>c z+RHp%t;DY6fb3_+@Yho7K#R9ZE7=Y@qwyU5zNT#bkG#Hn@To|jJ^ZP>etGcm;8WVJ z!lOTx?M^)Sc<}MocaQ&3V#((5hl-!+=h*^!{BvbnNe@0Ad_*wCw4s=)MOB<);d-Y0 zXu^-i`IXZtI^TQ24#@5i`c}$$GD&5bBnLBN!|!~P))dU=ZRS1r zc(TjE$SxlKRK}m`;6plo?9ymYe^METJotFEK+8w(*WOaP@u?wH-@ax!xkB2{%c_a@$mEq&fugdW8@TW3- zJpA$Cb1*WRH@_<5j|U$QKHmH~bT(Sv{Hi>^y!E{@d_4TA3?C1FJop^?^~>lH=^wkg zAaAZ!o@*Yht_&X!e?0gcdRpz#=gPLR9(+9bl>VNvy7eo!*XrC;&=h*eTmo{eo%K-N z*?Py;`4=8c#(Hk0hbPDSIVs-U@#ao7cRF}@;^E2o;Yp6CbM9)_#TW(z{s54 z;oN)L6#C)`0x}O?#qjcA;=u%yW8^bkj;Xs(Jw2JHCmTOKnMY$i8tc*6!uzE?8tc(m zkH%6k8T}mw-rVu#jyHF_xx*V&^xZ7<$9^Y@M^8L@;?Wb%q0z6!)!hMiY{`HgOp57= z2a{r$RI%{m# z{&@3eZ2H=qP4H>1^?HqwOuf0KlsC7$xi!YQ#lP2h<2_6N zc*gy6>3>U5Mb6uf95pP%*f(%<9oV;s$|CfQbHN_|k$ z>r1+K2WKH}?Lc^ADzYV(wWfB*dgIBcmFKcwN09zN8^0>-5X>}rsBs2_@qs`DIvQh6A-S{?jX~wipMsmtaY*8 za5pPvR^3S^(N8ssRzLR!`8@(dOuuxO7a@d>UAB*r#*^+c!&woVzp*!!&RQ$tDN_`S z%Ws!{+a3I8#JnOMjt+#gv8dIPPW4%xm#wtoNo(as>kR9nR%`xw=bg8-{?)1U79>FU zrdVWiI^4tXbLK5*Z(p#q{?d&R#&oW=w#Ku))`nDfES*iYT8N#$w0>baYHg2&(^hBi zDk~CBM&nUbW`(m>XF8sY#ImX6()yLDzW#x1EN!7N{k`!>ri~z4h1T7SpLUJ#bi0?^ zAu?`6f`Ei${8a7fj3eyom>#5Eej23j#)~e&V9_}UVF3i`2_~ulq`X9F{XjMmPsXfN zkChE4wiv*&be+o#xPIFmd;?;ZTRpL@R&P9$O{L?JaKcKavawA4%5Vkr02Ut(>% zbW^958R+lFh}CzdQ`uA`m9WCynQS^7$;MO3`b%S(On5WkH&X`I`7wz&V3Y0#0J72B*XdIHe3m z@C<68B7a11#rcMx2KEhj(X|<1O9==iu&0=)0${U(H3bkeC2|_-Y=zMNE@I}lwQFp} z-?nqMSStkqt-f%wetz40d0_%OYJS_oEf#1)t94F$yCe!nqrmWfGY0mcSUstkXvMNVnw*`;+4^z_8iGG&0+7SJA}pPbj704<6qH(M8AoK+b z=C_@vi(R{E-OKGFF59ry>Ptlj60v&R{!Ziwr{RN4$>d6)sb}D)-P!OoBw9?6nv)AN z>6l6njHHr*ku^dn7D1*dmXst@Ox7NmVjQ-UkpN`9XxKQMW*qclaS}odWwY(z5RyMc zq--k((rKWabuD@YvSH>Lo1zOWoNLn%ynu5JnHg{$(_I((pXIX{!|8q)`P;PQ`aI{-lUDBqG)?M)jg2OTGIcZ|aR&6F>USE1@@&Sx6=8)FvzcKs{v0zs{<5;k_;VmMi4|plkXS}2OaC-Z5JB{C zIaB#BxiB+r$?MQ@+8(cy_LwYI_4Zk48*CDR%RE-9}8XtaoC1zQ= z0Y!5em~drcAiM-PO`h#FRyLMMf>-Vf#}jRstz`Au`UfKO((zPVciLKTPW^%f?MPJ= zbCL98V=NhEUTbmNd2I{NmcRAAu|E%gh|DVcRmETI#$THtHI9f3zrVG5%i+fxJe zQXW~;CDt;#ogML{yIB`V$>kz3JuS8i++}J@eLB+5xr?%*Z#v2AWQxd3%qqA zYDH66%Y>g>0=kIBZ9vwoOj|vfV=#LWGL58C*{!Lz`ZnqEhULrWw=X(pftBMDBcy5o zAla5^ED;A;-(G*sKs<|an_p_&B8e1e?Pe>IN+v;_WeK<^$P;glBbiXo_AAw(lGv#4 zkibqKK~+A&{JO8qyzM9YTPrI?Zf`gb)%k zY0hNGbc9ULOEVK8GfkSanPwJ3W|=gcBt8ovmPs?8A+r%O+ob7WNDD$*Od4UPS`pG} z(nvY&2x&KIFe8<35keN3G%}SIBN)GXgUBkAOHVL|`s#PcrpzxtPIevYgcYYUAx=pq zwYw~n%EbBXlC?nvV|{YXj!q{dAz@r*3d2aGbXaj|#UW0+Cg$=vVa>U)srHl8vf{Fc zV7jd)&Mi&rHkInNlaGX4r>oDERZTvpzR~4-C|92;p;G@GxKI`~?b7_&sR%L41MfrD zE`KfNOe-Z68p6(nOJ^-4?B7NVs6}=FtW+$W1~E9_%JgOX+cKm}*VS8aBJH1VHJ78C z&8?D52qu%2C5X+fmZa=XMYp4#=4vV1Rq3me{54x`ZBhVR1=kFMS3vKY>#yg^_+@m9 z>ngep`q2O;>vOKRPMhIg382&nVIo823q*t{5!fN&vwXEk0iwhS&C+~zNCA?hQ^+(8 zAq7YhQwY(TjuaqBI)x1DOoV_S=@2Pp7D7Ofbcmp+g;FeCiVPD-k(y05?%p7WoFq^W zAM^0ZryN9K2}ClbCI_YkIGr@g@r`MnI6jq&Go?VWg?wzelg}XrCY7#>Q=(jX)z_6@ zrbGNL%WH#~A_%klFW&YEjpB#3F#fuL8Nyexdn)>^DKcF8f{szcKynK*m&_3 z7deUJJyiJEsiAa)qDCrN!X-D>GM6ku4(-#rPAOUf8HpdUoEgrmY{0begfqfYGX@YhmqbC4<>lkRDUel z(tI{~$FoV7K%<(|&2#5!I&udZOi|i_=xwgIPAGo~IR~O+p(7OVeJ(meV(!bMBgAmu zOwmTVr#yw zi(}~ls1f3QnMm*U@-aHh%pc5wl+3e_PVTma*V5$?Gw^M5+x!2>W} z%5%+J;-zmE;<5fS-r~D!^_LBp*@y=R!1z-c--36DB7B!+t2H8m2+Oi-Xh#YNB1|F7 z_9DDP4B@-9U@_ho>vs`cmf?MwE4_r_jP_^hLBG8jB>LV3jS6^KBlr7i%k&e^^bf6THI6ThQrg zWjR>QLfP5oS((bPw`Nj~z~(Xwv<@g!&&LYvF7eO0UUSF&R6pqSOk%-MOf(5$3v-u0GVP=WVA zN;)D_gZhNvl%9^uB*s}z8x(cg&U9>$ohFAVZ%{H_a?`OvmIhxaA}l#kutBx61lMBP zARiL=Tuga`BEtGyOnHNzL>9`iG@IUlsf0AM5rT=SOJ>8Jso8*iXS}y!VnNWaLCvT0 zy&X9)FO`7YPi|N0291A6-z8Hs;8a-NqaYLK?0)7!Wh=4ZaqC z7qVphK(O(1mV2hq;e@Xf{_d&Y;sog?L8X4rzV z#zJ6CPEeNFmBT}g1;-kLD^Sv~$6p6i1vV0HFm)B)45m{5s$DUru1UeDAPlPtux#<{ zg(G9?k5Q6Ym9=6SdvMI1jztEr>vwHzY5lsG*1Egp(2Q)ZVFR7qpm2rY^Ojom>u77x z7e*OaFt?_$tH+(Zm8T-=GMF|*Qt8w{7S<9O*o{bRPVUZCnAqM51`lF3c5S!A`~#k~ zJsyJ|TsCuK;25>w%$99~5X}VXSY`kyZ*A?x&N>v^*4CJni1lPa=bmeFyh+Pc2yY?t=9?b2vA9NrF*OshGZ{6ow<;B{V!tNjDnj3$+0C@Hd7LyB$0(A(M+~tjAU-L){wdv0N-Uf|v! zQKOk9;a9kfN{te}9Df}IE7r0tMZ7^U9%rUqK7vu2)><}u4_@};BfXRj4T_bQjOU`% z)q3xq8|&EB&sf|k1FnKbhzbqjdDzOv`#^tWb6*<+F!iRT%SsihV_CN zu`2@6B}{~Z-2!m0w+8}7FnH4ALK32#>{rR-nI=d<0=CncFmg~bwgHx*DV~%;wy%z5 z2tyu{^c2aa3ytJxYpX(Pv+>iak2BE-gCCkUEkm?Hos&#d0Y^1<+N5h`v8HHU>nmNg z&Nfj6wX)$WA&_D5*=$C8iI~UDOJKoBm_EHMS9(5OELS&G)~3g2l&PDmOL5(a)Ykl} z*+gAJbyaV4Rek#5t@;92)w9Y}O)@DP|Iru}u2JmT#A7+y;OH@{>+5n?g;tpg8<6HA z+0cRnET(j3P|3vWO!>9#7BkY-wjG!~|H#c;iQOZV_Wf%vxQ2}&C#r-_3M*RufT{P zgN;L`Hsp%@S2(>H{1=pglB@@+b+P5j_NG%?Guo^=2DnCZ2{mI!TBoh9(pGm2h6yn% z9gfQm11W6I5UZ`eCq)Ge$Up7GTDrq|En{SC8#q?j&{~Ki+Y>g4F=DagwefT+$p$X9 z$24PMQ1iw#oZOCvqP_N*wpkq1I{_LlscaGp})^jMDbWO*dY4);lohQoadOXA`_DIq)D zrOfJql3OtDGZ!?>P zIVx07v&w=pU{;T*NY4p1#e*;j{fQGv^tlg{WJ zSm6?6lsO!*#X;IxNGs}YftZCLOZq(z!@E~P>H0mQ7qmLKiKzyWT7p+3|(P~)^xMvSJG)?W;3(~Je+VNZ6cQm`qCv)BBXhW)VRF=G1JRCxZKUJYy5b!5gpRw^73WBnI3aaP)4?s&(qkiuv(X@>_Q1?%mZO(k_JB^;E%b&!M2VKvt)DxjX8qhH_3G!rfUdO@ zwQ_usP5CBM1^h>gV5(5NOr{hVjoAhl0>O z-hv(lRn5>F;fGZ15V4YNJ`N->sQg1GN_=3b1!1t?8e%zxu0~Cfd|YywH>d(be};dY z9bSWQYAv5tQ-`j=;|(Zjf*Mr2zVh6Jw@7bp^aVgnZz6bVAMyFHIkyjgRqZhTPR48j ztZFCJybrpBiSl<+&DY*HJX|Y(FGOsOdW1=kJtQru^X*d|)9M02HO+4ESEicz=2I7- zh4n*vKIbp`7D`x|h)1??fsRG-dB~+$YN6%L#Am0X{X5b7a}jTPufF=+glhVIA$mq!(LLK6_>tN zZqshT{L($6YFj#%so$tYC(%>XV<#EHn$c9k{Fyb1ziuj;zK;wPt5&R7SA29#bfBJ|6!@imL z8bxD8qiR0D(RjoUlt(5Fr6Kca<4DWQj@%YV1B8i!_dN%?D#-i)=6<%1O5g!0?*cq0o_H~BCtYTxO5`v-4T zclv(va4-6F9P$h!wiW_9h>f}stcwr*%wGc(S_i79dU}TQ)_|be(YOk^g6ft=s=}0d zAjp|{wfyVE=GrDG@7RJ{8>#J4>Njlx@llETZ5!vp8DIOduj6AMd549(WUE)I0hLi% z^t(@4YL$xNEv_~L3i#~VeS?+l78F)ztg54Ty-YxVYq+j zOo=b1A1B@PTA7et|N2^3!P@i#c?|v7iAoGqYYm;G+9$s=hFb>XOC?AQo9vCPRT+bvSz1Yu-!tcV5dCZ_P z58V{CT%|$Y!YU!}_B771P@_N2uu8nt5=J?^03A4cAJBTs;@^|5-K+dcqyQ??AgAruAe%3KHzHM18xvLz}-JCdYTUy z`?%$=ALV=ZgsXSN?NYtl`;D>nj-yxHJ1*mT9jO;R;@CznpZ=8ZUmhJ||Bhff)cM(c zzJ2e0&0RtDjQ{HZli6!PtCdkU05=9X1t+QXASI8LODi0(RAeoqzIsqom0*itsizc&|b(Ue$gP?^2zRr zdwu(-Z<|~wCm=K0t#!K?D!32iRvOaVG(t@Mr`YvxgnUH(rLKQab=|kLpk~qD+BM%b zrOR(MKHX4QGpS0bM9mCm%^O@bH=}0G6r<^UlgI!@3ywJGzeBdZllt#k2!dMEY2>qh zwO>6qqXW9_z)%-57#-?q1tpJ3hbo}Za^U5))S+Hp%X%(HJw}I0JYWx2jzx#+D*v9E zlP34nKQ)o%=gl>iUv{pIUWdBg{|V8d7Ve{Q&3`|t;+p?1_?5JM0}`*-{6Bp5QJ?ix z^XqVAqnL|Glu~DD98xeQS#{ui37m7lvLi{k^4#!aJGq-h>JwDwAE36p!?)r9=L+|h z5Lj~-JowM}@4-Yd6PhShPu(IMf(N;IJJN)vWf3RzZScG73C#ut*7y+aAEG5A2f@vd z)ZWqdv7Nt%x)D2fs^iWt=2W!EV@{2JuGwEyvwFv$C*;@6Nt|DErEQdk5;dbEQga@nqUP!l^<*FgNm*rO|4SNC zX@M{rL>+V?s=uQ=q7o(CE{CYdQxZ`To{y;eyABhg7S-JG$}bd5kwb*2sQIOjsNW1# zO;O)ffuhp9Xe314fi$G3gYb8`5Ov<84EGPwRHcxj5=pmw{O`hBMh{|PlCc?p-{OF4X=;CIJ#!5@wb+Y~Wb?$$%uueBaG%i#WbykO*2ft-H zmgRoxjNKA}U?Ez%ujYqdbS+WZjsU{!m z(&pis3%zU712wnL*fo`6-Ob1!RMW*C1?CQNdOLz91y)Jb@(1Q$_$RKFLPRGXDMYN1 zpahq1;jL%oA4 z)bYq9(>E#|9>J*9z>FrK9P=;=+r~T$!8!3*_vv4u51RuK>N}8J8UQi&>1zNh`?ODd z&$t7CwGsH}+(ci94&*EUb_Xd!uL|RvO>4zKdNaNqGWHvkmUVAAsosM(GpQy4{iw;f z3Fz03M}v4w&7FqPPAW}Kx*CL>1c`X9C}|{eG7+t_M+4*}F!TrHF)f^MNLsjfB3rm^ zQYC_?R-qZTqrIrp!f_;cFs#+(YJ!(QeWvF%=(z<_;jq7hs~>N^)1XH5Z&_Hu8a1I?KXG^)L%l|#X{a%PzZ~(k&chzDBw{RDk=oa1Kjch)K)Q* zJ0mqt7pl__w8B@aM^{b%za$+UzcJSJt~$^&&gHUy94|1PaMibWEV#qzR1NI@%%&8y zh255}OPn5)Lfp|dWyQD?WZqs3aTNbzVhnx+W6nX%VuIM4#X`|b&0-3SGKIZaEL7K< z#av9Pq5q@B9B®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[~2ha~#t@A)Fl3 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

q3K#UGuE*q~9)O#l@#|#4d9-E2eW2P*||? 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 + + + + + +LuaSocket: MIME module + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

MIME

+ +

+The mime namespace offers filters that apply and remove common +content transfer encodings, such as Base64 and Quoted-Printable. +It also provides functions to break text into lines and change +the end-of-line convention. +MIME is described mainly in +RFC 2045, +2046, +2047, +2048, and +2049. +

+ +

+All functionality provided by the MIME module +follows the ideas presented in + +LTN012, Filters sources and sinks. +

+ +

+To obtain the mime namespace, run: +

+ +
+-- loads the MIME module and everything it requires
+local mime = require("mime")
+
+ + + + +

High-level filters

+ + + + +

+mime.decode("base64")
+mime.decode("quoted-printable") +

+ +

+Returns a filter that decodes data from a given transfer content +encoding. +

+ + + +

+mime.encode("base64")
+mime.encode("quoted-printable" [, mode]) +

+ +

+Returns a filter that encodes data according to a given transfer content +encoding. +

+ +

+In the Quoted-Printable case, the user can specify whether the data is +textual or binary, by passing the mode strings "text" or +"binary". Mode defaults to "text". +

+ +

+Although both transfer content encodings specify a limit for the line +length, the encoding filters do not break text into lines (for +added flexibility). +Below is a filter that converts binary data to the Base64 transfer content +encoding and breaks it into lines of the correct size. +

+ +
+base64 = ltn12.filter.chain(
+  mime.encode("base64"),
+  mime.wrap("base64")
+)
+
+ +

+Note: Text data has to be converted to canonic form +before being encoded. +

+ +
+base64 = ltn12.filter.chain(
+  mime.normalize(),
+  mime.encode("base64"),
+  mime.wrap("base64")
+)
+
+ + + +

+mime.normalize([marker]) +

+ +

+Converts most common end-of-line markers to a specific given marker. +

+ +

+Marker is the new marker. It defaults to CRLF, the canonic +end-of-line marker defined by the MIME standard. +

+ +

+The function returns a filter that performs the conversion. +

+ +

+Note: There is no perfect solution to this problem. Different end-of-line +markers are an evil that will probably plague developers forever. +This function, however, will work perfectly for text created with any of +the most common end-of-line markers, i.e. the Mac OS (CR), the Unix (LF), +or the DOS (CRLF) conventions. Even if the data has mixed end-of-line +markers, the function will still work well, although it doesn't +guarantee that the number of empty lines will be correct. +

+ + + +

+mime.stuff()
+

+ +

+Creates and returns a filter that performs stuffing of SMTP messages. +

+ +

+Note: The smtp.send function +uses this filter automatically. You don't need to chain it with your +source, or apply it to your message body. +

+ + + +

+mime.wrap("text" [, length])
+mime.wrap("base64")
+mime.wrap("quoted-printable") +

+ +

+Returns a filter that breaks data into lines. +

+ +

+The "text" line-wrap filter simply breaks text into lines by +inserting CRLF end-of-line markers at appropriate positions. +Length defaults 76. +The "base64" line-wrap filter works just like the default +"text" line-wrap filter with default length. +The function can also wrap "quoted-printable" lines, taking care +not to break lines in the middle of an escaped character. In that case, the +line length is fixed at 76. +

+ +

+For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following: +

+ +
+qp = ltn12.filter.chain(
+  mime.normalize(),
+  mime.encode("quoted-printable"),
+  mime.wrap("quoted-printable")
+)
+
+ +

+Note: To break into lines with a different end-of-line convention, apply +a normalization filter after the line break filter. +

+ + + +

Low-level filters

+ + + +

+A, B = mime.b64(C [, D]) +

+ +

+Low-level filter to perform Base64 encoding. +

+ +

+A is the encoded version of the largest prefix of +C..D +that can be encoded unambiguously. B has the remaining bytes of +C..D, before encoding. +If D is nil, A is padded with +the encoding of the remaining bytes of C. +

+ +

+Note: The simplest use of this function is to encode a string into it's +Base64 transfer content encoding. Notice the extra parenthesis around the +call to mime.b64, to discard the second return value. +

+ +
+print((mime.b64("diego:password")))
+--> ZGllZ286cGFzc3dvcmQ=
+
+ + +

+A, n = mime.dot(m [, B]) +

+ +

+Low-level filter to perform SMTP stuffing and enable transmission of +messages containing the sequence "CRLF.CRLF". +

+ +

+A is the stuffed version of B. 'n' gives the +number of characters from the sequence CRLF seen in the end of B. +'m' should tell the same, but for the previous chunk. +

+ +

Note: The message body is defined to begin with +an implicit CRLF. Therefore, to stuff a message correctly, the +first m should have the value 2. +

+ +
+print((string.gsub(mime.dot(2, ".\r\nStuffing the message.\r\n.\r\n."), "\r\n", "\\n")))
+--> ..\nStuffing the message.\n..\n..
+
+ +

+Note: The smtp.send function +uses this filter automatically. You don't need to +apply it again. +

+ + + +

+A, B = mime.eol(C [, D, marker]) +

+ +

+Low-level filter to perform end-of-line marker translation. +For each chunk, the function needs to know if the last character of the +previous chunk could be part of an end-of-line marker or not. This is the +context the function receives besides the chunk. An updated version of +the context is returned after each new chunk. +

+ +

+A is the translated version of D. C is the +ASCII value of the last character of the previous chunk, if it was a +candidate for line break, or 0 otherwise. +B is the same as C, but for the current +chunk. Marker gives the new end-of-line marker and defaults to CRLF. +

+ +
+-- translates the end-of-line marker to UNIX
+unix = mime.eol(0, dos, "\n") 
+
+ + + +

+A, B = mime.qp(C [, D, marker]) +

+ +

+Low-level filter to perform Quoted-Printable encoding. +

+ +

+A is the encoded version of the largest prefix of +C..D +that can be encoded unambiguously. B has the remaining bytes of +C..D, before encoding. +If D is nil, A is padded with +the encoding of the remaining bytes of C. +Throughout encoding, occurrences of CRLF are replaced by the +marker, which itself defaults to CRLF. +

+ +

+Note: The simplest use of this function is to encode a string into it's +Quoted-Printable transfer content encoding. +Notice the extra parenthesis around the call to mime.qp, to discard the second return value. +

+ +
+print((mime.qp("maçã")))
+--> ma=E7=E3=
+
+ + + +

+A, m = mime.qpwrp(n [, B, length]) +

+ +

+Low-level filter to break Quoted-Printable text into lines. +

+ +

+A is a copy of B, broken into lines of at most +length bytes (defaults to 76). +'n' should tell how many bytes are left for the first +line of B and 'm' returns the number of bytes +left in the last line of A. +

+ +

+Note: Besides breaking text into lines, this function makes sure the line +breaks don't fall in the middle of an escaped character combination. Also, +this function only breaks lines that are bigger than length bytes. +

+ + + +

+A, B = mime.unb64(C [, D]) +

+ +

+Low-level filter to perform Base64 decoding. +

+ +

+A is the decoded version of the largest prefix of +C..D +that can be decoded unambiguously. B has the remaining bytes of +C..D, before decoding. +If D is nil, A is the empty string +and B returns whatever couldn't be decoded. +

+ +

+Note: The simplest use of this function is to decode a string from it's +Base64 transfer content encoding. +Notice the extra parenthesis around the call to mime.unqp, to discard the second return value. +

+ +
+print((mime.unb64("ZGllZ286cGFzc3dvcmQ=")))
+--> diego:password
+
+ + + +

+A, B = mime.unqp(C [, D]) +

+ +

+Low-level filter to remove the Quoted-Printable transfer content encoding +from data. +

+ +

+A is the decoded version of the largest prefix of +C..D +that can be decoded unambiguously. B has the remaining bytes of +C..D, before decoding. +If D is nil, A is augmented with +the encoding of the remaining bytes of C. +

+ +

+Note: The simplest use of this function is to decode a string from it's +Quoted-Printable transfer content encoding. +Notice the extra parenthesis around the call to mime.unqp, to discard the second return value. +

+ +
+print((mime.qp("ma=E7=E3=")))
+--> maçã
+
+ + + +

+A, m = mime.wrp(n [, B, length]) +

+ +

+Low-level filter to break text into lines with CRLF marker. +Text is assumed to be in the normalize form. +

+ +

+A is a copy of B, broken into lines of at most +length bytes (defaults to 76). +'n' should tell how many bytes are left for the first +line of B and 'm' returns the number of bytes +left in the last line of A. +

+ +

+Note: This function only breaks lines that are bigger than +length bytes. The resulting line length does not include the CRLF +marker. +

+ + + + + + + + diff --git a/contrib/luasocket/doc/reference.css b/contrib/luasocket/doc/reference.css new file mode 100644 index 00000000..04e38cfe --- /dev/null +++ b/contrib/luasocket/doc/reference.css @@ -0,0 +1,55 @@ +body { + margin-left: 1em; + margin-right: 1em; + font-family: "Verdana", sans-serif; + background: #ffffff; +} + +tt { + font-family: "Andale Mono", monospace; +} + +h1, h2, h3, h4 { margin-left: 0em; } + + +h3 { padding-top: 1em; } + +p { margin-left: 1em; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; + margin-left: 0em; +} + +a[href] { color: #00007f; } + +blockquote { margin-left: 3em; } + +pre.example { + background: #ccc; + padding: 1em; + margin-left: 1em; + font-family: "Andale Mono", monospace; + font-size: small; +} + +hr { + margin-left: 0em; + background: #00007f; + border: 0px; + height: 1px; +} + +ul { list-style-type: disc; } + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } +table.index ul { padding-top: 0em; margin-top: 0em; } + +h1:first-letter, +h2:first-letter, +h2:first-letter, +h3:first-letter { color: #00007f; } + +div.header, div.footer { margin-left: 0em; } diff --git a/contrib/luasocket/doc/reference.html b/contrib/luasocket/doc/reference.html new file mode 100644 index 00000000..287dc19d --- /dev/null +++ b/contrib/luasocket/doc/reference.html @@ -0,0 +1,260 @@ + + + + + + +LuaSocket: Index to reference manual + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

Reference

+ +
+DNS (in socket) +
+getaddrinfo, +gethostname, +tohostname, +toip. +
+
+ + + +
+FTP +
+get, +put. +
+
+ + + +
+HTTP +
+request. +
+
+ + + +
+LTN12 +
+filter: +chain, +cycle. +
+
+pump: +all, +step. +
+
+sink: +chain, +error, +file, +null, +simplify, +table. +
+
+source: +cat, +chain, +empty, +error, +file, +simplify, +string. +
+
+ + + +
+MIME +
+high-level: +decode, +encode, +normalize, +stuff, +wrap. +
+
+low-level: +b64, +dot, +eol, +qp, +qpwrp, +unb64, +unqp, +wrp. +
+
+ + + +
+SMTP +
+message, +send. +
+
+ + + +
+Socket +
+bind, +connect, +connect4, +connect6, +_DATAGRAMSIZE, +_DEBUG, +dns, +gettime, +headers.canonic, +newtry, +protect, +select, +sink, +skip, +sleep, +_SETSIZE, +_SOCKETINVALID, +source, +tcp, +tcp4, +tcp6, +try, +udp, +udp4, +udp6, +_VERSION. +
+
+ + + +
+TCP (in socket) +
+accept, +bind, +close, +connect, +dirty, +getfd, +getoption, +getpeername, +getsockname, +getstats, +gettimeout, +listen, +receive, +send, +setfd, +setoption, +setstats, +settimeout, +shutdown. +
+
+ + + +
+UDP (in socket) +
+close, +getoption, +getpeername, +getsockname, +gettimeout, +receive, +receivefrom, +send, +sendto, +setpeername, +setsockname, +setoption, +settimeout. +
+
+ + + +
+URL +
+absolute, +build, +build_path, +escape, +parse, +parse_path, +unescape. +
+
+ + + + + + + diff --git a/contrib/luasocket/doc/smtp.html b/contrib/luasocket/doc/smtp.html new file mode 100644 index 00000000..600ec375 --- /dev/null +++ b/contrib/luasocket/doc/smtp.html @@ -0,0 +1,418 @@ + + + + + + +LuaSocket: SMTP support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

SMTP

+ +

The smtp namespace provides functionality to send e-mail +messages. The high-level API consists of two functions: one to +define an e-mail message, and another to actually send the message. +Although almost all users will find that these functions provide more than +enough functionality, the underlying implementation allows for even more +control (if you bother to read the code). +

+ +

The implementation conforms to the Simple Mail Transfer Protocol, +RFC 2821. +Another RFC of interest is RFC 2822, +which governs the Internet Message Format. +Multipart messages (those that contain attachments) are part +of the MIME standard, but described mainly +in RFC 2046 + +

In the description below, good understanding of LTN012, Filters +sources and sinks and the MIME module is +assumed. In fact, the SMTP module was the main reason for their +creation.

+ +

+To obtain the smtp namespace, run: +

+ +
+-- loads the SMTP module and everything it requires
+local smtp = require("socket.smtp")
+
+ +

+MIME headers are represented as a Lua table in the form: +

+ +
+ + +
+headers = {
+  field-1-name = field-1-value,
+  field-2-name = field-2-value,
+  field-3-name = field-3-value,
+  ...
+  field-n-name = field-n-value
+} +
+
+ +

+Field names are case insensitive (as specified by the standard) and all +functions work with lowercase field names (but see +socket.headers.canonic). +Field values are left unmodified. +

+ +

+Note: MIME headers are independent of order. Therefore, there is no problem +in representing them in a Lua table. +

+ +

+The following constants can be set to control the default behavior of +the SMTP module: +

+ +
    +
  • DOMAIN: domain used to greet the server; +
  • PORT: default port used for the connection; +
  • SERVER: default server used for the connection; +
  • TIMEOUT: default timeout for all I/O operations; +
  • ZONE: default time zone. +
+ + + +

+smtp.message(mesgt) +

+ +

+Returns a simple +LTN12 source that sends an SMTP message body, possibly multipart (arbitrarily deep). +

+ +

+The only parameter of the function is a table describing the message. +Mesgt has the following form (notice the recursive structure): +

+ +
+ + +
+mesgt = {
+  headers = header-table,
+  body = LTN12 source or string or +multipart-mesgt
+}

+multipart-mesgt = {
+  [preamble = string,]
+  [1] = mesgt,
+  [2] = mesgt,
+  ...
+  [n] = mesgt,
+  [epilogue = string,]
+}
+
+
+ +

+For a simple message, all that is needed is a set of headers +and the body. The message body can be given as a string +or as a simple +LTN12 +source. For multipart messages, the body is a table that +recursively defines each part as an independent message, plus an optional +preamble and epilogue. +

+ +

+The function returns a simple +LTN12 +source that produces the +message contents as defined by mesgt, chunk by chunk. +Hopefully, the following +example will make things clear. When in doubt, refer to the appropriate RFC +as listed in the introduction.

+ +
+-- load the smtp support and its friends
+local smtp = require("socket.smtp")
+local mime = require("mime")
+local ltn12 = require("ltn12")
+
+-- creates a source to send a message with two parts. The first part is 
+-- plain text, the second part is a PNG image, encoded as base64.
+source = smtp.message{
+  headers = {
+     -- Remember that headers are *ignored* by smtp.send. 
+     from = "Sicrano de Oliveira <sicrano@example.com>",
+     to = "Fulano da Silva <fulano@example.com>",
+     subject = "Here is a message with attachments"
+  },
+  body = {
+    preamble = "If your client doesn't understand attachments, \r\n" ..
+               "it will still display the preamble and the epilogue.\r\n" ..
+               "Preamble will probably appear even in a MIME enabled client.",
+    -- first part: no headers means plain text, us-ascii.
+    -- The mime.eol low-level filter normalizes end-of-line markers.
+    [1] = { 
+      body = mime.eol(0, [[
+        Lines in a message body should always end with CRLF. 
+        The smtp module will *NOT* perform translation. However, the 
+        send function *DOES* perform SMTP stuffing, whereas the message
+        function does *NOT*.
+      ]])
+    },
+    -- second part: headers describe content to be a png image, 
+    -- sent under the base64 transfer content encoding.
+    -- notice that nothing happens until the message is actually sent. 
+    -- small chunks are loaded into memory right before transmission and 
+    -- translation happens on the fly.
+    [2] = { 
+      headers = {
+        ["content-type"] = 'image/png; name="image.png"',
+        ["content-disposition"] = 'attachment; filename="image.png"',
+        ["content-description"] = 'a beautiful image',
+        ["content-transfer-encoding"] = "BASE64"
+      },
+      body = ltn12.source.chain(
+        ltn12.source.file(io.open("image.png", "rb")),
+        ltn12.filter.chain(
+          mime.encode("base64"),
+          mime.wrap()
+        )
+      )
+    },
+    epilogue = "This might also show up, but after the attachments"
+  }
+}
+
+-- finally send it
+r, e = smtp.send{
+    from = "<sicrano@example.com>",
+    rcpt = "<fulano@example.com>",
+    source = source,
+}
+
+ + + + +

+smtp.send{
+  from = string,
+  rcpt = string or string-table,
+  source = LTN12 source,
+  [user = string,]
+  [password = string,]
+  [server = string,]
+  [port = number,]
+  [domain = string,]
+  [step = LTN12 pump step,]
+  [create = function]
+} +

+ +

+Sends a message to a recipient list. Since sending messages is not as +simple as downloading an URL from a FTP or HTTP server, this function +doesn't have a simple interface. However, see the +message source factory for +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 +address, or a string +in case there is just one recipient. +The contents of the message are given by a simple +LTN12 +source. Several arguments are optional: +

+
    +
  • user, password: User and password for +authentication. The function will attempt LOGIN and PLAIN authentication +methods if supported by the server (both are unsafe); +
  • server: Server to connect to. Defaults to "localhost"; +
  • port: Port to connect to. Defaults to 25; +
  • domain: Domain name used to greet the server; Defaults to the +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; +
  • create: An optional function to be used instead of +socket.tcp when the communications socket is created. +
+ +

+If successful, the function returns 1. Otherwise, the function returns +nil followed by an error message. +

+ +

+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 +addresses can take whatever form you like.

+ +

+Big note: There is a good deal of misconception with the use of the +destination address field headers, i.e., the 'To', 'Cc', +and, more importantly, the 'Bcc' headers. Do not add a +'Bcc' header to your messages because it will probably do the +exact opposite of what you expect. +

+ +

+Only recipients specified in the rcpt list will receive a copy of the +message. Each recipient of an SMTP mail message receives a copy of the +message body along with the headers, and nothing more. The headers +are part of the message and should be produced by the +LTN12 +source function. The rcpt list is not +part of the message and will not be sent to anyone. +

+ +

+RFC 2822 +has two important and short sections, "3.6.3. Destination address +fields" and "5. Security considerations", explaining the proper +use of these headers. Here is a summary of what it says: +

+ +
    +
  • To: contains the address(es) of the primary recipient(s) +of the message; +
  • Cc: (where the "Cc" means "Carbon Copy" in the sense of +making a copy on a typewriter using carbon paper) contains the +addresses of others who are to receive the message, though the +content of the message may not be directed at them; +
  • Bcc: (where the "Bcc" means "Blind Carbon +Copy") contains addresses of recipients of the message whose addresses are not to be revealed to other recipients of the message. +
+ +

+The LuaSocket send function does not care or interpret the +headers you send, but it gives you full control over what is sent and +to whom it is sent: +

+
    +
  • If someone is to receive the message, the e-mail address has +to be in the recipient list. This is the only parameter that controls who +gets a copy of the message; +
  • If there are multiple recipients, none of them will automatically +know that someone else got that message. That is, the default behavior is +similar to the Bcc field of popular e-mail clients; +
  • It is up to you to add the To header with the list of primary +recipients so that other recipients can see it; +
  • It is also up to you to add the Cc header with the +list of additional recipients so that everyone else sees it; +
  • Adding a header Bcc is nonsense, unless it is +empty. Otherwise, everyone receiving the message will see it and that is +exactly what you don't want to happen! +
+ +

+I hope this clarifies the issue. Otherwise, please refer to +RFC 2821 +and +RFC 2822. +

+ +
+-- load the smtp support
+local smtp = require("socket.smtp")
+
+-- Connects to server "localhost" and sends a message to users
+-- "fulano@example.com",  "beltrano@example.com", 
+-- and "sicrano@example.com".
+-- Note that "fulano" is the primary recipient, "beltrano" receives a
+-- carbon copy and neither of them knows that "sicrano" received a blind
+-- carbon copy of the message.
+from = "<luasocket@example.com>"
+
+rcpt = {
+  "<fulano@example.com>",
+  "<beltrano@example.com>",
+  "<sicrano@example.com>"
+}
+
+mesgt = {
+  headers = {
+    to = "Fulano da Silva <fulano@example.com>",
+    cc = '"Beltrano F. Nunes" <beltrano@example.com>',
+    subject = "My first message"
+  },
+  body = "I hope this works. If it does, I can send you another 1000 copies."
+}
+
+r, e = smtp.send{
+  from = from,
+  rcpt = rcpt, 
+  source = smtp.message(mesgt)
+}
+
+ + + + + + + diff --git a/contrib/luasocket/doc/socket.html b/contrib/luasocket/doc/socket.html new file mode 100644 index 00000000..35f8391c --- /dev/null +++ b/contrib/luasocket/doc/socket.html @@ -0,0 +1,479 @@ + + + + + + +LuaSocket: The socket namespace + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

The socket namespace

+ +

+The socket namespace contains the core functionality of LuaSocket. +

+ +

+To obtain the socket namespace, run: +

+ +
+-- loads the socket module 
+local socket = require("socket")
+
+ + + +

+socket.headers.canonic

+ +

The socket.headers.canonic table +is used by the HTTP and SMTP modules to translate from +lowercase field names back into their canonic +capitalization. When a lowercase field name exists as a key +in this table, the associated value is substituted in +whenever the field name is sent out. +

+ +

+You can obtain the headers namespace if case run-time +modifications are required by running: +

+ +
+-- loads the headers module 
+local headers = require("headers")
+
+ + + + +

+socket.bind(address, port [, backlog]) +

+ +

+This function is a shortcut that creates and returns a TCP server object +bound to a local address and port, ready to +accept client connections. Optionally, +user can also specify the backlog argument to the +listen method (defaults to 32). +

+ +

+Note: The server object returned will have the option "reuseaddr" +set to true. +

+ + + +

+socket.connect[46](address, port [, locaddr] [, locport] [, family]) +

+ +

+This function is a shortcut that creates and returns a TCP client object +connected to a remote address at a given port. Optionally, +the user can also specify the local address and port to bind +(locaddr and locport), or restrict the socket family +to "inet" or "inet6". +Without specifying family to connect, whether a tcp or tcp6 +connection is created depends on your system configuration. Two variations +of connect are defined as simple helper functions that restrict the +family, socket.connect4 and socket.connect6. +

+ + + +

+socket._DEBUG +

+ +

+This constant is set to true if the library was compiled +with debug support. +

+ + + +

+socket._DATAGRAMSIZE +

+ +

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

+ + + +

+socket.gettime() +

+ +

+Returns the UNIX time in seconds. You should subtract the values returned by this function +to get meaningful values. +

+ +
+t = socket.gettime()
+-- do stuff
+print(socket.gettime() - t .. " seconds elapsed")
+
+ + + +

+socket.newtry(finalizer) +

+ +

+Creates and returns a clean +try +function that allows for cleanup before the exception +is raised. +

+ +

+Finalizer is a function that will be called before +try throws the exception. +

+ +

+The function returns your customized try function. +

+ +

+Note: This idea saved a lot of work with the +implementation of protocols in LuaSocket: +

+ +
+foo = socket.protect(function()
+    -- connect somewhere
+    local c = socket.try(socket.connect("somewhere", 42))
+    -- create a try function that closes 'c' on error
+    local try = socket.newtry(function() c:close() end)
+    -- do everything reassured c will be closed 
+    try(c:send("hello there?\r\n"))
+    local answer = try(c:receive())
+    ...
+    try(c:send("good bye\r\n"))
+    c:close()
+end)
+
+ + + + +

+socket.protect(func) +

+ +

+Converts a function that throws exceptions into a safe function. This +function only catches exceptions thrown by the try +and newtry functions. It does not catch normal +Lua errors. +

+ +

+Func is a function that calls +try (or assert, or error) +to throw exceptions. +

+ +

+Returns an equivalent function that instead of throwing exceptions in case of +a failed try call, returns nil +followed by an error message. +

+ + + +

+socket.select(recvt, sendt [, timeout]) +

+ +

+Waits for a number of sockets to change status. +

+ +

+Recvt is an array with the sockets to test for characters +available for reading. Sockets in the sendt array are watched to +see if it is OK to immediately write on them. Timeout is the +maximum amount of time (in seconds) to wait for a change in status. A +nil, negative or omitted timeout value allows the +function to block indefinitely. Recvt and sendt can also +be empty tables or nil. Non-socket values (or values with +non-numeric indices) in the arrays will be silently ignored. +

+ +

The function returns a list with the sockets ready for +reading, a list with the sockets ready for writing and an error message. +The error message is "timeout" if a timeout +condition was met, "select failed" if the call +to select failed, and +nil otherwise. The returned tables are +doubly keyed both by integers and also by the sockets +themselves, to simplify the test if a specific socket has +changed status. +

+ +

+Note: select can monitor a limited number +of sockets, as defined by the constant socket._SETSIZE. This +number may be as high as 1024 or as low as 64 by default, +depending on the system. It is usually possible to change this +at compile time. Invoking select with a larger +number of sockets will raise an error. +

+ +

+Important note: a known bug in WinSock causes select to fail +on non-blocking TCP sockets. The function may return a socket as +writable even though the socket is not ready for sending. +

+ +

+Another important note: calling select with a server socket in the receive parameter before a call to accept does not guarantee +accept will return immediately. +Use the settimeout +method or accept might block forever. +

+ +

+Yet another note: If you close a socket and pass +it to select, it will be ignored. +

+ +

+Using select with non-socket objects: Any object that implements getfd and dirty can be used with select, allowing objects from other libraries to be used within a socket.select driven loop. +

+ + + +

+socket._SETSIZE +

+ +

+The maximum number of sockets that the select function can handle. +

+ + + + +

+socket.sink(mode, socket) +

+ +

+Creates an +LTN12 +sink from a stream socket object. +

+ +

+Mode defines the behavior of the sink. The following +options are available: +

+
    +
  • "http-chunked": sends data through socket after applying the +chunked transfer coding, closing the socket when done; +
  • "close-when-done": sends all received data through the +socket, closing the socket when done; +
  • "keep-open": sends all received data through the +socket, leaving it open when done. +
+

+Socket is the stream socket object used to send the data. +

+ +

+The function returns a sink with the appropriate behavior. +

+ + + +

+socket.skip(d [, ret1, ret2 ... retN]) +

+ +

+Drops a number of arguments and returns the remaining. +

+ +

+D is the number of arguments to drop. Ret1 to +retN are the arguments. +

+ +

+The function returns retd+1 to retN. +

+ +

+Note: This function is useful to avoid creation of dummy variables: +

+ +
+-- get the status code and separator from SMTP server reply 
+local code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
+
+ + + +

+socket.sleep(time) +

+ +

+Freezes the program execution during a given amount of time. +

+ +

+Time is the number of seconds to sleep for. If +time is negative, the function returns immediately. +

+ + + +

+socket.source(mode, socket [, length]) +

+ +

+Creates an +LTN12 +source from a stream socket object. +

+ +

+Mode defines the behavior of the source. The following +options are available: +

+
    +
  • "http-chunked": receives data from socket and removes the +chunked transfer coding before returning the data; +
  • "by-length": receives a fixed number of bytes from the +socket. This mode requires the extra argument length; +
  • "until-closed": receives data from a socket until the other +side closes the connection. +
+

+Socket is the stream socket object used to receive the data. +

+ +

+The function returns a source with the appropriate behavior. +

+ + + +

+socket._SOCKETINVALID +

+ +

+The OS value for an invalid socket. +

+ + + +

+socket.try(ret1 [, ret2 ... retN]) +

+ +

+Throws an exception in case ret1 is falsy, using +ret2 as the error message. The exception is supposed to be caught +by a protected function only. +

+ +

+Ret1 to retN can be arbitrary +arguments, but are usually the return values of a function call +nested with try. +

+ +

+The function returns ret1 to retN if +ret1 is not nil or false. +Otherwise, it calls error passing ret2 wrapped +in a table with metatable used by protect to +distinguish exceptions from runtime errors. +

+ +
+-- connects or throws an exception with the appropriate error message
+c = socket.try(socket.connect("localhost", 80))
+
+ + + +

+socket._VERSION +

+ +

+This constant has a string describing the current LuaSocket version. +

+ + + + + + + diff --git a/contrib/luasocket/doc/tcp.html b/contrib/luasocket/doc/tcp.html new file mode 100644 index 00000000..c6c6eb26 --- /dev/null +++ b/contrib/luasocket/doc/tcp.html @@ -0,0 +1,721 @@ + + + + + + +LuaSocket: TCP/IP support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

TCP

+ + + +

+server:accept() +

+ +

+Waits for a remote connection on the server +object and returns a client object representing that connection. +

+ +

+If a connection is successfully initiated, a client object is returned. +If a timeout condition is met, the method returns nil +followed by the error string 'timeout'. Other errors are +reported by nil followed by a message describing the error. +

+ +

+Note: calling socket.select +with a server object in +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. +

+ + + +

+master:bind(address, port) +

+ +

+Binds a master object to address and port on the +local host. + +

+Address can be an IP address or a host name. +Port must be an integer number in the range [0..64K). +If address +is '*', the system binds to all local interfaces +using the INADDR_ANY constant or +IN6ADDR_ANY_INIT, according to the family. +If port is 0, the system automatically +chooses an ephemeral port. +

+ +

+In case of success, the method returns 1. In case of error, the +method returns nil followed by an error message. +

+ +

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

+ + + +

+master:close()
+client:close()
+server:close() +

+ +

+Closes a TCP object. The internal socket used by the object is closed +and the local address to which the object was +bound is made available to other applications. No further operations +(except for further calls to the close method) are allowed on +a closed socket. +

+ +

+Note: It is important to close all used sockets once they are not +needed, since, in many systems, each socket uses a file descriptor, +which are limited system resources. Garbage-collected objects are +automatically closed before destruction, though. +

+ + + +

+master:connect(address, port) +

+ +

+Attempts to connect a master object to a remote host, transforming it into a +client object. +Client objects support methods +send, +receive, +getsockname, +getpeername, +settimeout, +and close. +

+ +

+Address can be an IP address or a host name. +Port must be an integer number in the range [1..64K). +

+ +

+In case of error, the method returns nil followed by a string +describing the error. In case of success, the method returns 1. +

+ +

+Note: The function socket.connect +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 +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 +established. +

+ +

+Note: Starting with LuaSocket 3.0, the host name resolution +depends on whether the socket was created by +socket.tcp, +socket.tcp4 or +socket.tcp6. Addresses from +the appropriate family (or both) are tried in the order +returned by the resolver until the +first success or until the last failure. If the timeout was +set to zero, only the first address is tried. +

+ + + +

+master:dirty()
+client:dirty()
+server:dirty() +

+ +

+Check the read buffer status. +

+ +

+Returns true if there is any data in the read buffer, false otherwise. +

+ +

+Note: This is an internal method, use at your own risk. +

+ + + + +

+master:getfd()
+client:getfd()
+server:getfd() +

+ +

+Returns the underling socket descriptor or handle associated to the object. +

+ +

+The descriptor or handle. In case the object has been closed, the return will be -1. +

+ +

+Note: This is an internal method. Unlikely to be +portable. Use at your own risk. +

+ + + + +

+client:getoption(option)
+server:getoption(option) +

+ +

+Gets options for the TCP object. +See setoption for description of the +option names and values. +

+ +

+Option is a string with the option name. +

    + +
  • 'keepalive' +
  • 'linger' +
  • 'reuseaddr' +
  • 'tcp-nodelay' +
+ +

+The method returns the option value in case of success, or +nil followed by an error message otherwise. +

+ + + + +

+client:getpeername() +

+ +

+Returns information about the remote side of a connected client object. +

+ +

+Returns a string with the IP address of the peer, the +port number that peer is using for the connection, +and a string with the family ("inet" or "inet6"). +In case of error, the method returns nil. +

+ +

+Note: It makes no sense to call this method on server objects. +

+ + + +

+master:getsockname()
+client:getsockname()
+server:getsockname() +

+ +

+Returns the local address information associated to the object. +

+ +

+The method returns a string with local IP address, a number with +the local port, +and a string with the family ("inet" or "inet6"). +In case of error, the method returns nil. +

+ + + +

+master:getstats()
+client:getstats()
+server:getstats()
+

+ +

+Returns accounting information on the socket, useful for throttling +of bandwidth. +

+ +

+The method returns the number of bytes received, the number of bytes sent, +and the age of the socket object in seconds. +

+ + + +

+master:gettimeout()
+client:gettimeout()
+server:gettimeout() +

+ +

+Returns the current block timeout followed by the curent +total timeout. +

+ + + + +

+master:listen(backlog) +

+ +

+Specifies the socket is willing to receive connections, transforming the +object into a server object. Server objects support the +accept, +getsockname, +setoption, +settimeout, +and close methods. +

+ +

+The parameter backlog specifies the number of client +connections that can +be queued waiting for service. If the queue is full and another client +attempts connection, the connection is refused. +

+ +

+In case of success, the method returns 1. In case of error, the +method returns nil followed by an error message. +

+ + + +

+client:receive([pattern [, prefix]]) +

+ +

+Reads data from a client object, according to the specified read +pattern. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. +

+ +

+Pattern can be any of the following: +

+ +
    +
  • '*a': reads from the socket until the connection is +closed. No end-of-line translation is performed; +
  • '*l': reads a line of text from the socket. The line is +terminated by a LF character (ASCII 10), optionally preceded by a +CR character (ASCII 13). The CR and LF characters are not included in +the returned line. In fact, all CR characters are +ignored by the pattern. This is the default pattern; +
  • number: causes the method to read a specified number +of bytes from the socket. +
+ +

+Prefix is an optional string to be concatenated to the beginning +of any received data before return. +

+ +

+If successful, the method returns the received pattern. In case of error, +the method returns nil followed by an error +message, followed by a (possibly empty) string containing +the partial that was received. The error message can be +the string '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. +

+ +

+Important note: This function was changed severely. It used +to support multiple patterns (but I have never seen this feature used) and +now it doesn't anymore. Partial results used to be returned in the same +way as successful results. This last feature violated the idea that all +functions should return nil on error. Thus it was changed +too. +

+ + + +

+client:send(data [, i [, j]]) +

+ +

+Sends data through client object. +

+ +

+Data is the string to be sent. The optional arguments +i and j work exactly like the standard +string.sub Lua function to allow the selection of a +substring to be sent. +

+ +

+If successful, the method returns the index of the last byte +within [i, j] that has been sent. Notice that, if +i is 1 or absent, this is effectively the total +number of bytes sent. In case of error, the method returns +nil, followed by an error message, followed +by the index of the last byte within [i, j] that +has been sent. You might want to try again from the byte +following that. 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. +

+ +

+Note: Output is not buffered. For small strings, +it is always better to concatenate them in Lua +(with the '..' operator) and send the result in one call +instead of calling the method several times. +

+ + + +

+client:setoption(option [, value])
+server:setoption(option [, value]) +

+ +

+Sets options for the TCP object. Options are only needed by low-level or +time-critical applications. You should only modify an option if you +are sure you need it. +

+ +

+Option is a string with the option name, and value +depends on the option being set: + +

    + +
  • 'keepalive': Setting this option to true enables +the periodic transmission of messages on a connected socket. Should the +connected party fail to respond to these messages, the connection is +considered broken and processes using the socket are notified; + +
  • 'linger': Controls the action taken when unsent data are +queued on a socket and a close is performed. The value is a table with a +boolean entry 'on' and a numeric entry for the time interval +'timeout' in seconds. If the 'on' field is set to +true, the system will block the process on the close attempt until +it is able to transmit the data or until 'timeout' has passed. If +'on' is false and a close is issued, the system will +process the close in a manner that allows the process to continue as +quickly as possible. I do not advise you to set this to anything other than +zero; + +
  • 'reuseaddr': Setting this option indicates that the rules +used in validating addresses supplied in a call to +bind should allow reuse of local addresses; + +
  • 'tcp-nodelay': Setting this option to true +disables the Nagle's algorithm for the connection; + +
  • 'ipv6-v6only': +Setting this option to true restricts an inet6 socket to +sending and receiving only IPv6 packets. +
+ +

+The method returns 1 in case of success, or nil +followed by an error message otherwise. +

+ +

+Note: The descriptions above come from the man pages. +

+ + + +

+master:setstats(received, sent, age)
+client:setstats(received, sent, age)
+server:setstats(received, sent, age)
+

+ +

+Resets accounting information on the socket, useful for throttling +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. +

+ +

+The method returns 1 in case of success and nil otherwise. +

+ + + +

+master:settimeout(value [, mode])
+client:settimeout(value [, mode])
+server:settimeout(value [, mode]) +

+ +

+Changes the timeout values for the object. By default, +all I/O operations are blocking. That is, any call to the methods +send, +receive, and +accept +will block indefinitely, until the operation completes. The +settimeout method defines a limit on the amount of time the +I/O methods can block. When a timeout is set and the specified amount of +time has elapsed, the affected methods give up and fail with an error code. +

+ +

+The amount of time to wait is specified as the +value parameter, in seconds. There are two timeout modes and +both can be used together for fine tuning: +

+ +
    +
  • 'b': block timeout. Specifies the upper limit on +the amount of time LuaSocket can be blocked by the operating system +while waiting for completion of any single I/O operation. This is the +default mode;
  • + +
  • 't': total timeout. Specifies the upper limit on +the amount of time LuaSocket can block a Lua script before returning from +a call.
  • +
+ +

+The nil timeout value allows operations to block +indefinitely. Negative timeout values have the same effect. +

+ +

+Note: although timeout values have millisecond precision in LuaSocket, +large blocks can cause I/O functions not to respect timeout values due +to the time the library takes to transfer blocks to and from the OS +and to and from the Lua interpreter. Also, function that accept host names +and perform automatic name resolution might be blocked by the resolver for +longer than the specified timeout value. +

+ +

+Note: The old timeout method is deprecated. The name has been +changed for sake of uniformity, since all other method names already +contained verbs making their imperative nature obvious. +

+ + + +

+client:shutdown(mode)
+

+ +

+Shuts down part of a full-duplex connection. +

+ +

+Mode tells which way of the connection should be shut down and can +take the value: +

    +
  • "both": disallow further sends and receives on the object. +This is the default mode; +
  • "send": disallow further sends on the object; +
  • "receive": disallow further receives on the object. +
+ +

+This function returns 1. +

+ + + +

+master:setfd(fd)
+client:setfd(fd)
+server:setfd(fd) +

+ +

+Sets the underling socket descriptor or handle associated to the object. The current one is simply replaced, not closed, and no other change to the object state is made. +

+ +

+No return value. +

+ +

+Note: This is an internal method. Unlikely to be +portable. Use at your own risk. +

+ + + +

+socket.tcp() +

+ +

+Creates and returns an TCP master object. A master object can +be transformed into a server object with the method +listen (after a call to bind) or into a client object with +the method connect. The only other +method supported by a master object is the +close method.

+ +

+In case of success, a new master object is returned. In case of error, +nil is returned, followed by an error message. +

+ +

+Note: The choice between IPv4 and IPv6 happens during a call to +bind or connect, depending on the address +family obtained from the resolver. +

+ +

+Note: Before the choice between IPv4 and IPv6 happens, +the internal socket object is invalid and therefore setoption will fail. +

+ + + +

+socket.tcp4() +

+ +

+Creates and returns an IPv4 TCP master object. A master object can +be transformed into a server object with the method +listen (after a call to bind) or into a client object with +the method connect. The only other +method supported by a master object is the +close method.

+ +

+In case of success, a new master object is returned. In case of error, +nil is returned, followed by an error message. +

+ + + +

+socket.tcp6() +

+ +

+Creates and returns an IPv6 TCP master object. A master object can +be transformed into a server object with the method +listen (after a call to bind) or into a client object with +the method connect. The only other +method supported by a master object is the +close method.

+ +

+In case of success, a new master object is returned. In case of error, +nil is returned, followed by an error message. +

+ +

+Note: The TCP object returned will have the option +"ipv6-v6only" set to true. +

+ + + + + + + + + diff --git a/contrib/luasocket/doc/udp.html b/contrib/luasocket/doc/udp.html new file mode 100644 index 00000000..4618aadb --- /dev/null +++ b/contrib/luasocket/doc/udp.html @@ -0,0 +1,596 @@ + + + + + + +LuaSocket: UDP support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + + +

UDP

+ + + +

+connected:close()
+unconnected:close() +

+ +

+Closes a UDP object. The internal socket +used by the object is closed and the local address to which the +object was bound is made available to other applications. No +further operations (except for further calls to the close +method) are allowed on a closed socket. +

+ +

+Note: It is important to close all used sockets +once they are not needed, since, in many systems, each socket uses +a file descriptor, which are limited system resources. +Garbage-collected objects are automatically closed before +destruction, though. +

+ + + +

+connected:getoption()
+unconnected:getoption() +

+ +

+Gets an option value from the UDP object. +See setoption for +description of the option names and values. +

+ +

Option is a string with the option name. +

    +
  • 'dontroute' +
  • 'broadcast' +
  • 'reuseaddr' +
  • 'reuseport' +
  • 'ip-multicast-loop' +
  • 'ipv6-v6only' +
  • 'ip-multicast-if' +
  • 'ip-multicast-ttl' +
  • 'ip-add-membership' +
  • 'ip-drop-membership' +
+

+ +

+The method returns the option value in case of +success, or +nil followed by an error message otherwise. +

+ + + +

+connected:getpeername() +

+ +

+Retrieves information about the peer +associated with a connected UDP object. +

+ + +

+Returns a string with the IP address of the peer, the +port number that peer is using for the connection, +and a string with the family ("inet" or "inet6"). +In case of error, the method returns nil. +

+ +

+Note: It makes no sense to call this method on unconnected objects. +

+ + + +

+connected:getsockname()
+unconnected:getsockname() +

+ +

+Returns the local address information associated to the object. +

+ + +

+The method returns a string with local IP address, a number with +the local port, +and a string with the family ("inet" or "inet6"). +In case of error, the method returns nil. +

+ +

+Note: UDP sockets are not bound to any address +until the setsockname or the +sendto method is called for the +first time (in which case it is bound to an ephemeral port and the +wild-card address). +

+ + + +

+connected:settimeout(value)
+unconnected:settimeout(value) +

+ +

+Returns the current timeout value. +

+ + + + +

+connected:receive([size])
+unconnected:receive([size]) +

+ +

+Receives a datagram from the UDP object. If +the UDP object is connected, only datagrams coming from the peer +are accepted. Otherwise, the returned datagram can come from any +host. +

+ +

+The optional size parameter +specifies the maximum size of the datagram to be retrieved. If +there are more than size bytes available in the datagram, +the excess bytes are discarded. If there are less then +size bytes available in the current datagram, the +available bytes are returned. +If size is omitted, the +compile-time constant socket._DATAGRAMSIZE is used +(it defaults to 8192 bytes). Larger sizes will cause a +temporary buffer to be allocated for the operation. +

+ +

+In case of success, the method returns the +received datagram. In case of timeout, the method returns +nil followed by the string 'timeout'. +

+ + + +

+unconnected:receivefrom([size]) +

+ +

+Works exactly as the receive +method, except it returns the IP +address and port as extra return values (and is therefore slightly less +efficient). +

+ + + +

+connected:send(datagram) +

+ +

+Sends a datagram to the UDP peer of a connected object. +

+ +

+Datagram is a string with the datagram contents. +The maximum datagram size for UDP is 64K minus IP layer overhead. +However datagrams larger than the link layer packet size will be +fragmented, which may deteriorate performance and/or reliability. +

+ +

+If successful, the method returns 1. In case of +error, the method returns nil followed by an error message. +

+ +

+Note: In UDP, the send method never blocks +and the only way it can fail is if the underlying transport layer +refuses to send a message to the specified address (i.e. no +interface accepts the address). +

+ + + +

+unconnected:sendto(datagram, ip, port) +

+ +

+Sends a datagram to the specified IP address and port number. +

+ +

+Datagram is a string with the +datagram contents. +The maximum datagram size for UDP is 64K minus IP layer overhead. +However datagrams larger than the link layer packet size will be +fragmented, which may deteriorate performance and/or reliability. +Ip is the IP address of the recipient. +Host names are not allowed for performance reasons. + +Port is the port number at the recipient. +

+ +

+If successful, the method returns 1. In case of +error, the method returns nil followed by an error message. +

+ +

+Note: In UDP, the send method never blocks +and the only way it can fail is if the underlying transport layer +refuses to send a message to the specified address (i.e. no +interface accepts the address). +

+ + + +

+connected:setoption(option [, value])
+unconnected:setoption(option [, value]) +

+ +

+Sets options for the UDP object. Options are +only needed by low-level or time-critical applications. You should +only modify an option if you are sure you need it.

+

Option is a string with the option +name, and value depends on the option being set: +

+ +
    +
  • 'dontroute': Indicates that outgoing +messages should bypass the standard routing facilities. +Receives a boolean value; +
  • 'broadcast': Requests permission to send +broadcast datagrams on the socket. +Receives a boolean value; +
  • 'reuseaddr': Indicates that the rules used in +validating addresses supplied in a bind() call +should allow reuse of local addresses. +Receives a boolean value; +
  • 'reuseport': Allows completely duplicate +bindings by multiple processes if they all set +'reuseport' before binding the port. +Receives a boolean value; +
  • 'ip-multicast-loop': +Specifies whether or not a copy of an outgoing multicast +datagram is delivered to the sending host as long as it is a +member of the multicast group. +Receives a boolean value; +
  • 'ipv6-v6only': +Specifies whether to restrict inet6 sockets to +sending and receiving only IPv6 packets. +Receive a boolean value; +
  • 'ip-multicast-if': +Sets the interface over which outgoing multicast datagrams +are sent. +Receives an IP address; +
  • 'ip-multicast-ttl': +Sets the Time To Live in the IP header for outgoing +multicast datagrams. +Receives a number; +
  • 'ip-add-membership': +Joins the multicast group specified. +Receives a table with fields +multiaddr and interface, each containing an +IP address; +
  • 'ip-drop-membership': Leaves the multicast +group specified. +Receives a table with fields +multiaddr and interface, each containing an +IP address. +
+ +

+The method returns 1 in case of success, or +nil followed by an error message otherwise. +

+ +

+Note: The descriptions above come from the man pages. +

+ + + + +

+connected:setpeername('*')
+unconnected:setpeername(address, port) +

+ +

+Changes the peer of a UDP object. This +method turns an unconnected UDP object into a connected UDP +object or vice versa. +

+ +

+For connected objects, outgoing datagrams +will be sent to the specified peer, and datagrams received from +other peers will be discarded by the OS. Connected UDP objects must +use the send and +receive methods instead of +sendto and +receivefrom. +

+ +

+Address can be an IP address or a +host name. Port is the port number. If address is +'*' and the object is connected, the peer association is +removed and the object becomes an unconnected object again. In that +case, the port argument is ignored. +

+ +

+In case of error the method returns +nil followed by an error message. In case of success, the +method returns 1. +

+ +

+Note: Since the address of the peer does not have +to be passed to and from the OS, the use of connected UDP objects +is recommended when the same peer is used for several transmissions +and can result in up to 30% performance gains. +

+ +

+Note: Starting with LuaSocket 3.0, the host name resolution +depends on whether the socket was created by socket.udp or socket.udp6. Addresses from +the appropriate family are tried in succession until the +first success or until the last failure. +

+ + + +

+unconnected:setsockname(address, port) +

+ +

+Binds the UDP object to a local address. +

+ +

+Address can be an IP address or a +host name. If address is '*' the system binds to +all local interfaces using the constant INADDR_ANY. If +port is 0, the system chooses an ephemeral port. +

+ +

+If successful, the method returns 1. In case of +error, the method returns nil followed by an error +message. +

+ +

+Note: This method can only be called before any +datagram is sent through the UDP object, and only once. Otherwise, +the system automatically binds the object to all local interfaces +and chooses an ephemeral port as soon as the first datagram is +sent. After the local address is set, either automatically by the +system or explicitly by setsockname, it cannot be +changed. +

+ + + +

+connected:settimeout(value)
+unconnected:settimeout(value) +

+ +

+Changes the timeout values for the object. By default, the +receive and +receivefrom +operations are blocking. That is, any call to the methods will block +indefinitely, until data arrives. The settimeout function defines +a limit on the amount of time the functions can block. When a timeout is +set and the specified amount of time has elapsed, the affected methods +give up and fail with an error code. +

+ +

+The amount of time to wait is specified as +the value parameter, in seconds. The nil timeout +value allows operations to block indefinitely. Negative +timeout values have the same effect. +

+ +

+Note: In UDP, the send +and sendto methods never block (the +datagram is just passed to the OS and the call returns +immediately). Therefore, the settimeout method has no +effect on them. +

+ +

+Note: The old timeout method is +deprecated. The name has been changed for sake of uniformity, since +all other method names already contained verbs making their +imperative nature obvious. +

+ + + +

+socket.udp() +

+ +

+Creates and returns an unconnected UDP object. +Unconnected objects support the +sendto, +receive, +receivefrom, +getoption, +getsockname, +setoption, +settimeout, +setpeername, +setsockname, and +close. +The setpeername +is used to connect the object. +

+ +

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

+ +

+Note: The choice between IPv4 and IPv6 happens during a call to +sendto, setpeername, or sockname, depending on the address +family obtained from the resolver. +

+ +

+Note: Before the choice between IPv4 and IPv6 happens, +the internal socket object is invalid and therefore setoption will fail. +

+ + + +

+socket.udp4() +

+ +

+Creates and returns an unconnected IPv4 UDP object. +Unconnected objects support the +sendto, +receive, +receivefrom, +getoption, +getsockname, +setoption, +settimeout, +setpeername, +setsockname, and +close. +The setpeername +is used to connect the object. +

+ +

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

+ + + +

+socket.udp6() +

+ +

+Creates and returns an unconnected IPv6 UDP object. +Unconnected objects support the +sendto, +receive, +receivefrom, +getoption, +getsockname, +setoption, +settimeout, +setpeername, +setsockname, and +close. +The setpeername +is used to connect the object. +

+ +

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

+ +

+Note: The TCP object returned will have the option +"ipv6-v6only" set to true. +

+ + + + + + + + + diff --git a/contrib/luasocket/doc/url.html b/contrib/luasocket/doc/url.html new file mode 100644 index 00000000..6ff673da --- /dev/null +++ b/contrib/luasocket/doc/url.html @@ -0,0 +1,328 @@ + + + + + + +LuaSocket: URL support + + + + + + + +
+
+
+ + + +
+LuaSocket +
Network support for the Lua language +
+

+home · +download · +installation · +introduction · +reference +

+
+
+
+ + + +

URL

+ +

+The url namespace provides functions to parse, protect, +and build URLs, as well as functions to compose absolute URLs +from base and relative URLs, according to +RFC 2396. +

+ +

+To obtain the url namespace, run: +

+ +
+-- loads the URL module 
+local url = require("socket.url")
+
+ +

+An URL is defined by the following grammar: +

+ +
+ +<url> ::= [<scheme>:][//<authority>][/<path>][;<params>][?<query>][#<fragment>]
+<authority> ::= [<userinfo>@]<host>[:<port>]
+<userinfo> ::= <user>[:<password>]
+<path> ::= {<segment>/}<segment>
+
+
+ + + +

+url.absolute(base, relative) +

+ +

+Builds an absolute URL from a base URL and a relative URL. +

+ +

+Base is a string with the base URL or +a parsed URL table. Relative is a +string with the relative URL. +

+ +

+The function returns a string with the absolute URL. +

+ +

+Note: The rules that +govern the composition are fairly complex, and are described in detail in +RFC 2396. +The example bellow should give an idea of what the rules are. +

+ +
+http://a/b/c/d;p?q
+
++
+
+g:h      =  g:h
+g        =  http://a/b/c/g
+./g      =  http://a/b/c/g
+g/       =  http://a/b/c/g/
+/g       =  http://a/g
+//g      =  http://g
+?y       =  http://a/b/c/?y
+g?y      =  http://a/b/c/g?y
+#s       =  http://a/b/c/d;p?q#s
+g#s      =  http://a/b/c/g#s
+g?y#s    =  http://a/b/c/g?y#s
+;x       =  http://a/b/c/;x
+g;x      =  http://a/b/c/g;x
+g;x?y#s  =  http://a/b/c/g;x?y#s
+.        =  http://a/b/c/
+./       =  http://a/b/c/
+..       =  http://a/b/
+../      =  http://a/b/
+../g     =  http://a/b/g
+../..    =  http://a/
+../../   =  http://a/
+../../g  =  http://a/g
+
+ + + +

+url.build(parsed_url) +

+ +

+Rebuilds an URL from its parts. +

+ +

+Parsed_url is a table with same components returned by +parse. +Lower level components, if specified, +take precedence over high level components of the URL grammar. +

+ +

+The function returns a string with the built URL. +

+ + + +

+url.build_path(segments, unsafe) +

+ +

+Builds a <path> component from a list of +<segment> parts. +Before composition, any reserved characters found in a segment are escaped into +their protected form, so that the resulting path is a valid URL path +component. +

+ +

+Segments is a list of strings with the <segment> +parts. If unsafe is anything but nil, reserved +characters are left untouched. +

+ +

+The function returns a string with the +built <path> component. +

+ + + +

+url.escape(content) +

+ +

+Applies the URL escaping content coding to a string +Each byte is encoded as a percent character followed +by the two byte hexadecimal representation of its integer +value. +

+ +

+Content is the string to be encoded. +

+ +

+The function returns the encoded string. +

+ +
+-- load url module
+url = require("socket.url")
+
+code = url.escape("/#?;")
+-- code = "%2f%23%3f%3b"
+
+ + + +

+url.parse(url, default) +

+ +

+Parses an URL given as a string into a Lua table with its components. +

+ +

+Url is the URL to be parsed. If the default table is +present, it is used to store the parsed fields. Only fields present in the +URL are overwritten. Therefore, this table can be used to pass default +values for each field. +

+ +

+The function returns a table with all the URL components: +

+ +
+parsed_url = {
+  url = string,
+  scheme = string,
+  authority = string,
+  path = string,
+  params = string,
+  query = string,
+  fragment = string,
+  userinfo = string,
+  host = string,
+  port = string,
+  user = string,
+  password = string
+} +
+ +
+-- load url module
+url = require("socket.url")
+
+parsed_url = url.parse("http://www.example.com/cgilua/index.lua?a=2#there")
+-- parsed_url = {
+--   scheme = "http",
+--   authority = "www.example.com",
+--   path = "/cgilua/index.lua"
+--   query = "a=2",
+--   fragment = "there",
+--   host = "www.puc-rio.br",
+-- }
+
+parsed_url = url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i")
+-- parsed_url = {
+--   scheme = "ftp",
+--   authority = "root:passwd@unsafe.org",
+--   path = "/pub/virus.exe",
+--   params = "type=i",
+--   userinfo = "root:passwd",
+--   host = "unsafe.org",
+--   user = "root",
+--   password = "passwd",
+-- }
+
+ + + +

+url.parse_path(path) +

+ +

+Breaks a <path> URL component into all its +<segment> parts. +

+ +

+Path is a string with the path to be parsed. +

+ +

+Since some characters are reserved in URLs, they must be escaped +whenever present in a <path> component. Therefore, before +returning a list with all the parsed segments, the function removes +escaping from all of them. +

+ + + +

+url.unescape(content) +

+ +

+Removes the URL escaping content coding from a string. +

+ +

+Content is the string to be decoded. +

+ +

+The function returns the decoded string. +

+ + + + + + + diff --git a/contrib/luasocket/etc/README b/contrib/luasocket/etc/README new file mode 100644 index 00000000..cfd3e372 --- /dev/null +++ b/contrib/luasocket/etc/README @@ -0,0 +1,89 @@ +This directory contains code that is more useful than the +samples. This code *is* supported. + + tftp.lua -- Trivial FTP client + +This module implements file retrieval by the TFTP protocol. +Its main use was to test the UDP code, but since someone +found it usefull, I turned it into a module that is almost +official (no uploads, yet). + + dict.lua -- Dict client + +The dict.lua module started with a cool simple client +for the DICT protocol, written by Luiz Henrique Figueiredo. +This new version has been converted into a library, similar +to the HTTP and FTP libraries, that can be used from within +any luasocket application. Take a look on the source code +and you will be able to figure out how to use it. + + lp.lua -- LPD client library + +The lp.lua module implements the client part of the Line +Printer Daemon protocol, used to print files on Unix +machines. It is courtesy of David Burgess! See the source +code and the lpr.lua in the examples directory. + + b64.lua + qp.lua + eol.lua + +These are tiny programs that perform Base64, +Quoted-Printable and end-of-line marker conversions. + + get.lua -- file retriever + +This little program is a client that uses the FTP and +HTTP code to implement a command line file graber. Just +run + + lua get.lua [] + +to download a remote file (either ftp:// or http://) to +the specified local file. The program also prints the +download throughput, elapsed time, bytes already downloaded +etc during download. + + check-memory.lua -- checks memory consumption + +This is just to see how much memory each module uses. + + dispatch.lua -- coroutine based dispatcher + +This is a first try at a coroutine based non-blocking +dispatcher for LuaSocket. Take a look at 'check-links.lua' +and at 'forward.lua' to see how to use it. + + check-links.lua -- HTML link checker program + +This little program scans a HTML file and checks for broken +links. It is similar to check-links.pl by Jamie Zawinski, +but uses all facilities of the LuaSocket library and the Lua +language. It has not been thoroughly tested, but it should +work. Just run + + lua check-links.lua [-n] {} > output + +and open the result to see a list of broken links. Make sure +you check the '-n' switch. It runs in non-blocking mode, +using coroutines, and is MUCH faster! + + forward.lua -- coroutine based forward server + +This is a forward server that can accept several connections +and transfers simultaneously using non-blocking I/O and the +coroutine-based dispatcher. You can run, for example + + lua forward.lua 8080:proxy.com:3128 + +to redirect all local conections to port 8080 to the host +'proxy.com' at port 3128. + + unix.c and unix.h + +This is an implementation of Unix local domain sockets and +demonstrates how to extend LuaSocket with a new type of +transport. It has been tested on Linux and on Mac OS X. + +Good luck, +Diego. diff --git a/contrib/luasocket/etc/b64.lua b/contrib/luasocket/etc/b64.lua new file mode 100644 index 00000000..11eeb2d2 --- /dev/null +++ b/contrib/luasocket/etc/b64.lua @@ -0,0 +1,19 @@ +----------------------------------------------------------------------------- +-- Little program to convert to and from Base64 +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local ltn12 = require("ltn12") +local mime = require("mime") +local source = ltn12.source.file(io.stdin) +local sink = ltn12.sink.file(io.stdout) +local convert +if arg and arg[1] == '-d' then + convert = mime.decode("base64") +else + local base64 = mime.encode("base64") + local wrap = mime.wrap() + convert = ltn12.filter.chain(base64, wrap) +end +sink = ltn12.sink.chain(convert, sink) +ltn12.pump.all(source, sink) diff --git a/contrib/luasocket/etc/check-links.lua b/contrib/luasocket/etc/check-links.lua new file mode 100644 index 00000000..283f3ac2 --- /dev/null +++ b/contrib/luasocket/etc/check-links.lua @@ -0,0 +1,111 @@ +----------------------------------------------------------------------------- +-- Little program that checks links in HTML files, using coroutines and +-- non-blocking I/O via the dispatcher module. +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local url = require("socket.url") +local dispatch = require("dispatch") +local http = require("socket.http") +dispatch.TIMEOUT = 10 + +-- make sure the user knows how to invoke us +arg = arg or {} +if #arg < 1 then + print("Usage:\n luasocket check-links.lua [-n] {}") + exit() +end + +-- '-n' means we are running in non-blocking mode +if arg[1] == "-n" then + -- if non-blocking I/O was requested, use real dispatcher interface + table.remove(arg, 1) + handler = dispatch.newhandler("coroutine") +else + -- if using blocking I/O, use fake dispatcher interface + handler = dispatch.newhandler("sequential") +end + +local nthreads = 0 + +-- get the status of a URL using the dispatcher +function getstatus(link) + local parsed = url.parse(link, {scheme = "file"}) + if parsed.scheme == "http" then + nthreads = nthreads + 1 + handler:start(function() + local r, c, h, s = http.request{ + method = "HEAD", + url = link, + create = handler.tcp + } + if r and c == 200 then io.write('\t', link, '\n') + else io.write('\t', link, ': ', tostring(c), '\n') end + nthreads = nthreads - 1 + end) + end +end + +function readfile(path) + path = url.unescape(path) + local file, error = io.open(path, "r") + if file then + local body = file:read("*a") + file:close() + return body + else return nil, error end +end + +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 + -- if there was a redirect, update base to reflect it + base = headers.location or base + end + if not body then + error = code + end + elseif parsed.scheme == "file" then + body, error = readfile(parsed.path) + else error = string.format("unhandled scheme '%s'", parsed.scheme) end + return base, body, error +end + +function getlinks(body, base) + -- get rid of comments + body = string.gsub(body, "%<%!%-%-.-%-%-%>", "") + local links = {} + -- extract links + body = string.gsub(body, '[Hh][Rr][Ee][Ff]%s*=%s*"([^"]*)"', function(href) + table.insert(links, url.absolute(base, href)) + end) + body = string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*'([^']*)'", function(href) + table.insert(links, url.absolute(base, href)) + end) + string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*(.-)>", function(href) + table.insert(links, url.absolute(base, href)) + end) + return links +end + +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(link) + end +end + +for _, address in ipairs(arg) do + checklinks(url.absolute("file:", address)) +end + +while nthreads > 0 do + handler:step() +end diff --git a/contrib/luasocket/etc/check-memory.lua b/contrib/luasocket/etc/check-memory.lua new file mode 100644 index 00000000..7bd984d8 --- /dev/null +++ b/contrib/luasocket/etc/check-memory.lua @@ -0,0 +1,17 @@ +function load(s) + collectgarbage() + local a = gcinfo() + _G[s] = require(s) + collectgarbage() + local b = gcinfo() + print(s .. ":\t " .. (b-a) .. "k") +end + +load("socket.url") +load("ltn12") +load("socket") +load("mime") +load("socket.tp") +load("socket.smtp") +load("socket.http") +load("socket.ftp") diff --git a/contrib/luasocket/etc/cookie.lua b/contrib/luasocket/etc/cookie.lua new file mode 100644 index 00000000..4adb4031 --- /dev/null +++ b/contrib/luasocket/etc/cookie.lua @@ -0,0 +1,88 @@ +local socket = require"socket" +local http = require"socket.http" +local url = require"socket.url" +local ltn12 = require"ltn12" + +local token_class = '[^%c%s%(%)%<%>%@%,%;%:%\\%"%/%[%]%?%=%{%}]' + +local function unquote(t, quoted) + local n = string.match(t, "%$(%d+)$") + if n then n = tonumber(n) end + if quoted[n] then return quoted[n] + else return t end +end + +local function parse_set_cookie(c, quoted, cookie_table) + c = c .. ";$last=last;" + local _, __, n, v, i = string.find(c, "(" .. token_class .. + "+)%s*=%s*(.-)%s*;%s*()") + local cookie = { + name = n, + value = unquote(v, quoted), + attributes = {} + } + while 1 do + _, __, n, v, i = string.find(c, "(" .. token_class .. + "+)%s*=?%s*(.-)%s*;%s*()", i) + if not n or n == "$last" then break end + cookie.attributes[#cookie.attributes+1] = { + name = n, + value = unquote(v, quoted) + } + end + cookie_table[#cookie_table+1] = cookie +end + +local function split_set_cookie(s, cookie_table) + cookie_table = cookie_table or {} + -- remove quoted strings from cookie list + local quoted = {} + s = string.gsub(s, '"(.-)"', function(q) + quoted[#quoted+1] = q + return "$" .. #quoted + end) + -- add sentinel + s = s .. ",$last=" + -- split into individual cookies + i = 1 + while 1 do + local _, __, cookie, next_token + _, __, cookie, i, next_token = string.find(s, "(.-)%s*%,%s*()(" .. + token_class .. "+)%s*=", i) + if not next_token then break end + parse_set_cookie(cookie, quoted, cookie_table) + if next_token == "$last" then break end + end + return cookie_table +end + +local function quote(s) + if string.find(s, "[ %,%;]") then return '"' .. s .. '"' + else return s end +end + +local _empty = {} +local function build_cookies(cookies) + s = "" + for i,v in ipairs(cookies or _empty) do + if v.name then + s = s .. v.name + if v.value and v.value ~= "" then + s = s .. '=' .. quote(v.value) + end + end + if v.name and #(v.attributes or _empty) > 0 then s = s .. "; " end + for j,u in ipairs(v.attributes or _empty) do + if u.name then + s = s .. u.name + if u.value and u.value ~= "" then + s = s .. '=' .. quote(u.value) + end + end + if j < #v.attributes then s = s .. "; " end + end + if i < #cookies then s = s .. ", " end + end + return s +end + diff --git a/contrib/luasocket/etc/dict.lua b/contrib/luasocket/etc/dict.lua new file mode 100644 index 00000000..8c5b711c --- /dev/null +++ b/contrib/luasocket/etc/dict.lua @@ -0,0 +1,151 @@ +----------------------------------------------------------------------------- +-- Little program to download DICT word definitions +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Load required modules +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local table = require("table") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +module("socket.dict") + +----------------------------------------------------------------------------- +-- Globals +----------------------------------------------------------------------------- +HOST = "dict.org" +PORT = 2628 +TIMEOUT = 10 + +----------------------------------------------------------------------------- +-- Low-level dict API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(host, port) + local tp = socket.try(tp.connect(host or HOST, port or PORT, TIMEOUT)) + return base.setmetatable({tp = tp}, metat) +end + +function metat.__index:greet() + return socket.try(self.tp:check(220)) +end + +function metat.__index:check(ok) + local code, status = socket.try(self.tp:check(ok)) + return code, + base.tonumber(socket.skip(2, string.find(status, "^%d%d%d (%d*)"))) +end + +function metat.__index:getdef() + local line = socket.try(self.tp:receive()) + local def = {} + while line ~= "." do + table.insert(def, line) + line = socket.try(self.tp:receive()) + end + return table.concat(def, "\n") +end + +function metat.__index:define(database, word) + database = database or "!" + socket.try(self.tp:command("DEFINE", database .. " " .. word)) + local code, count = self:check(150) + local defs = {} + for i = 1, count do + self:check(151) + table.insert(defs, self:getdef()) + end + self:check(250) + return defs +end + +function metat.__index:match(database, strat, word) + database = database or "!" + strat = strat or "." + socket.try(self.tp:command("MATCH", database .." ".. strat .." ".. word)) + self:check(152) + local mat = {} + local line = socket.try(self.tp:receive()) + while line ~= '.' do + database, word = socket.skip(2, string.find(line, "(%S+) (.*)")) + if not mat[database] then mat[database] = {} end + table.insert(mat[database], word) + line = socket.try(self.tp:receive()) + end + self:check(250) + return mat +end + +function metat.__index:quit() + self.tp:command("QUIT") + return self:check(221) +end + +function metat.__index:close() + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High-level dict API +----------------------------------------------------------------------------- +local default = { + scheme = "dict", + host = "dict.org" +} + +local function there(f) + if f == "" then return nil + else return f end +end + +local function parse(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "dict", "invalid scheme '" .. t.scheme .. "'") + socket.try(t.path, "invalid path in url") + local cmd, arg = socket.skip(2, string.find(t.path, "^/(.)(.*)$")) + socket.try(cmd == "d" or cmd == "m", " should be 'm' or 'd'") + socket.try(arg and arg ~= "", "need at least in URL") + t.command, t.argument = cmd, arg + arg = string.gsub(arg, "^:([^:]+)", function(f) t.word = f end) + socket.try(t.word, "need at least in URL") + arg = string.gsub(arg, "^:([^:]*)", function(f) t.database = there(f) end) + if cmd == "m" then + arg = string.gsub(arg, "^:([^:]*)", function(f) t.strat = there(f) end) + end + string.gsub(arg, ":([^:]*)$", function(f) t.n = base.tonumber(f) end) + return t +end + +local function tget(gett) + local con = open(gett.host, gett.port) + con:greet() + if gett.command == "d" then + local def = con:define(gett.database, gett.word) + con:quit() + con:close() + if gett.n then return def[gett.n] + else return def end + elseif gett.command == "m" then + local mat = con:match(gett.database, gett.strat, gett.word) + con:quit() + con:close() + return mat + else return nil, "invalid command" end +end + +local function sget(u) + local gett = parse(u) + return tget(gett) +end + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + diff --git a/contrib/luasocket/etc/dispatch.lua b/contrib/luasocket/etc/dispatch.lua new file mode 100644 index 00000000..24854155 --- /dev/null +++ b/contrib/luasocket/etc/dispatch.lua @@ -0,0 +1,307 @@ +----------------------------------------------------------------------------- +-- A hacked dispatcher module +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local socket = require("socket") +local coroutine = require("coroutine") +module("dispatch") + +-- if too much time goes by without any activity in one of our sockets, we +-- just kill it +TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- We implement 3 types of dispatchers: +-- sequential +-- coroutine +-- threaded +-- The user can choose whatever one is needed +----------------------------------------------------------------------------- +local handlert = {} + +-- default handler is coroutine +function newhandler(mode) + mode = mode or "coroutine" + return handlert[mode]() +end + +local function seqstart(self, func) + return func() +end + +-- sequential handler simply calls the functions and doesn't wrap I/O +function handlert.sequential() + return { + tcp = socket.tcp, + start = seqstart + } +end + +----------------------------------------------------------------------------- +-- Mega hack. Don't try to do this at home. +----------------------------------------------------------------------------- +-- we can't yield across calls to protect on Lua 5.1, so we rewrite it with +-- coroutines +-- make sure you don't require any module that uses socket.protect before +-- loading our hack +if string.sub(base._VERSION, -3) == "5.1" then + local function _protect(co, status, ...) + if not status then + local msg = ... + if base.type(msg) == 'table' then + return nil, msg[1] + else + base.error(msg, 0) + end + end + if coroutine.status(co) == "suspended" then + return _protect(co, coroutine.resume(co, coroutine.yield(...))) + else + return ... + end + end + + function socket.protect(f) + return function(...) + local co = coroutine.create(f) + return _protect(co, coroutine.resume(co, ...)) + end + end +end + +----------------------------------------------------------------------------- +-- Simple set data structure. O(1) everything. +----------------------------------------------------------------------------- +local function newset() + local reverse = {} + local set = {} + return base.setmetatable(set, {__index = { + insert = function(set, value) + if not reverse[value] then + table.insert(set, value) + reverse[value] = #set + end + end, + remove = function(set, value) + local index = reverse[value] + if index then + reverse[value] = nil + local top = table.remove(set) + if top ~= value then + reverse[top] = index + set[index] = top + end + end + end + }}) +end + +----------------------------------------------------------------------------- +-- socket.tcp() wrapper for the coroutine dispatcher +----------------------------------------------------------------------------- +local function cowrap(dispatcher, tcp, error) + if not tcp then return nil, error end + -- put it in non-blocking mode right away + tcp:settimeout(0) + -- metatable for wrap produces new methods on demand for those that we + -- don't override explicitly. + local metat = { __index = function(table, key) + table[key] = function(...) + return tcp[key](tcp,select(2,...)) + end + return table[key] + end} + -- does our user want to do his own non-blocking I/O? + local zero = false + -- create a wrap object that will behave just like a real socket object + local wrap = { } + -- we ignore settimeout to preserve our 0 timeout, but record whether + -- the user wants to do his own non-blocking I/O + function wrap:settimeout(value, mode) + if value == 0 then zero = true + else zero = false end + return 1 + end + -- send in non-blocking mode and yield on timeout + function wrap:send(data, first, last) + first = (first or 1) - 1 + local result, error + while true do + -- return control to dispatcher and tell it we want to send + -- if upon return the dispatcher tells us we timed out, + -- return an error to whoever called us + if coroutine.yield(dispatcher.sending, tcp) == "timeout" then + return nil, "timeout" + end + -- try sending + result, error, first = tcp:send(data, first+1, last) + -- 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 + -- or simply return partial read, if user requested timeout = 0 + function wrap:receive(pattern, partial) + local error = "timeout" + local value + while true do + -- return control to dispatcher and tell it we want to receive + -- if upon return the dispatcher tells us we timed out, + -- return an error to whoever called us + if coroutine.yield(dispatcher.receiving, tcp) == "timeout" then + return nil, "timeout" + end + -- try receiving + value, error, partial = tcp:receive(pattern, partial) + -- if we are done, or there was an unexpected error, + -- break away from loop. also, if the user requested + -- zero timeout, return all we got + if (error ~= "timeout") or zero then + return value, error, partial + end + end + end + -- connect in non-blocking mode and yield on timeout + function wrap:connect(host, port) + local result, error = tcp:connect(host, port) + if error == "timeout" then + -- return control to dispatcher. we will be writable when + -- connection succeeds. + -- if upon return the dispatcher tells us we have a + -- timeout, just abort + if coroutine.yield(dispatcher.sending, tcp) == "timeout" then + return nil, "timeout" + end + -- when we come back, check if connection was successful + result, error = tcp:connect(host, port) + if result or error == "already connected" then return 1 + else return nil, "non-blocking connect failed" end + else return result, error end + end + -- accept in non-blocking mode and yield on timeout + function wrap:accept() + while 1 do + -- return control to dispatcher. we will be readable when a + -- connection arrives. + -- if upon return the dispatcher tells us we have a + -- timeout, just abort + if coroutine.yield(dispatcher.receiving, tcp) == "timeout" then + return nil, "timeout" + end + local client, error = tcp:accept() + if error ~= "timeout" then + return cowrap(dispatcher, client, error) + end + end + end + -- remove cortn from context + function wrap:close() + dispatcher.stamp[tcp] = nil + dispatcher.sending.set:remove(tcp) + dispatcher.sending.cortn[tcp] = nil + dispatcher.receiving.set:remove(tcp) + dispatcher.receiving.cortn[tcp] = nil + return tcp:close() + end + return base.setmetatable(wrap, metat) +end + + +----------------------------------------------------------------------------- +-- Our coroutine dispatcher +----------------------------------------------------------------------------- +local cometat = { __index = {} } + +function schedule(cortn, status, operation, tcp) + if status then + if cortn and operation then + operation.set:insert(tcp) + operation.cortn[tcp] = cortn + operation.stamp[tcp] = socket.gettime() + end + else base.error(operation) end +end + +function kick(operation, tcp) + operation.cortn[tcp] = nil + operation.set:remove(tcp) +end + +function wakeup(operation, tcp) + local cortn = operation.cortn[tcp] + -- if cortn is still valid, wake it up + if cortn then + kick(operation, tcp) + return cortn, coroutine.resume(cortn) + -- othrewise, just get scheduler not to do anything + else + return nil, true + end +end + +function abort(operation, tcp) + local cortn = operation.cortn[tcp] + if cortn then + kick(operation, tcp) + coroutine.resume(cortn, "timeout") + end +end + +-- step through all active cortns +function cometat.__index:step() + -- check which sockets are interesting and act on them + local readable, writable = socket.select(self.receiving.set, + self.sending.set, 1) + -- for all readable connections, resume their cortns and reschedule + -- when they yield back to us + for _, tcp in base.ipairs(readable) do + schedule(wakeup(self.receiving, tcp)) + end + -- for all writable connections, do the same + for _, tcp in base.ipairs(writable) do + schedule(wakeup(self.sending, tcp)) + end + -- politely ask replacement I/O functions in idle cortns to + -- return reporting a timeout + local now = socket.gettime() + for tcp, stamp in base.pairs(self.stamp) do + if tcp.class == "tcp{client}" and now - stamp > TIMEOUT then + abort(self.sending, tcp) + abort(self.receiving, tcp) + end + end +end + +function cometat.__index:start(func) + local cortn = coroutine.create(func) + schedule(cortn, coroutine.resume(cortn)) +end + +function handlert.coroutine() + local stamp = {} + local dispatcher = { + stamp = stamp, + sending = { + name = "sending", + set = newset(), + cortn = {}, + stamp = stamp + }, + receiving = { + name = "receiving", + set = newset(), + cortn = {}, + stamp = stamp + }, + } + function dispatcher.tcp() + return cowrap(dispatcher, socket.tcp()) + end + return base.setmetatable(dispatcher, cometat) +end + diff --git a/contrib/luasocket/etc/eol.lua b/contrib/luasocket/etc/eol.lua new file mode 100644 index 00000000..eeaf0ce0 --- /dev/null +++ b/contrib/luasocket/etc/eol.lua @@ -0,0 +1,13 @@ +----------------------------------------------------------------------------- +-- Little program to adjust end of line markers. +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local mime = require("mime") +local ltn12 = require("ltn12") +local marker = '\n' +if arg and arg[1] == '-d' then marker = '\r\n' end +local filter = mime.normalize(marker) +local source = ltn12.source.chain(ltn12.source.file(io.stdin), filter) +local sink = ltn12.sink.file(io.stdout) +ltn12.pump.all(source, sink) diff --git a/contrib/luasocket/etc/forward.lua b/contrib/luasocket/etc/forward.lua new file mode 100644 index 00000000..05ced1ae --- /dev/null +++ b/contrib/luasocket/etc/forward.lua @@ -0,0 +1,65 @@ +-- load our favourite library +local dispatch = require("dispatch") +local handler = dispatch.newhandler() + +-- make sure the user knows how to invoke us +if #arg < 1 then + print("Usage") + print(" lua forward.lua ...") + os.exit(1) +end + +-- function to move data from one socket to the other +local function move(foo, bar) + local live + while 1 do + local data, error, partial = foo:receive(2048) + live = data or error == "timeout" + data = data or partial + local result, error = bar:send(data) + if not live or not result then + foo:close() + bar:close() + break + end + end +end + +-- for each tunnel, start a new server +for i, v in ipairs(arg) do + -- capture forwarding parameters + local _, _, iport, ohost, oport = string.find(v, "([^:]+):([^:]+):([^:]+)") + assert(iport, "invalid arguments") + -- create our server socket + local server = assert(handler.tcp()) + assert(server:setoption("reuseaddr", true)) + assert(server:bind("*", iport)) + assert(server:listen(32)) + -- handler for the server object loops accepting new connections + handler:start(function() + while 1 do + local client = assert(server:accept()) + assert(client:settimeout(0)) + -- for each new connection, start a new client handler + handler:start(function() + -- handler tries to connect to peer + local peer = assert(handler.tcp()) + assert(peer:settimeout(0)) + assert(peer:connect(ohost, oport)) + -- if sucessful, starts a new handler to send data from + -- client to peer + handler:start(function() + move(client, peer) + end) + -- afte starting new handler, enter in loop sending data from + -- peer to client + move(peer, client) + end) + end + end) +end + +-- simply loop stepping the server +while 1 do + handler:step() +end diff --git a/contrib/luasocket/etc/get.lua b/contrib/luasocket/etc/get.lua new file mode 100644 index 00000000..9edc2357 --- /dev/null +++ b/contrib/luasocket/etc/get.lua @@ -0,0 +1,141 @@ +----------------------------------------------------------------------------- +-- Little program to download files from URLs +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +local http = require("socket.http") +local ftp = require("socket.ftp") +local url = require("socket.url") +local ltn12 = require("ltn12") + +-- formats a number of seconds into human readable form +function nicetime(s) + local l = "s" + if s > 60 then + s = s / 60 + l = "m" + if s > 60 then + s = s / 60 + l = "h" + if s > 24 then + s = s / 24 + l = "d" -- hmmm + end + end + end + if l == "s" then return string.format("%5.0f%s", s, l) + else return string.format("%5.2f%s", s, l) end +end + +-- formats a number of bytes into human readable form +function nicesize(b) + local l = "B" + if b > 1024 then + b = b / 1024 + l = "KB" + if b > 1024 then + b = b / 1024 + l = "MB" + if b > 1024 then + b = b / 1024 + l = "GB" -- hmmm + end + end + end + return string.format("%7.2f%2s", b, l) +end + +-- returns a string with the current state of the download +local remaining_s = "%s received, %s/s throughput, %2.0f%% done, %s remaining" +local elapsed_s = "%s received, %s/s throughput, %s elapsed " +function gauge(got, delta, size) + local rate = got / delta + if size and size >= 1 then + return string.format(remaining_s, nicesize(got), nicesize(rate), + 100*got/size, nicetime((size-got)/rate)) + else + return string.format(elapsed_s, nicesize(got), + nicesize(rate), nicetime(delta)) + end +end + +-- creates a new instance of a receive_cb that saves to disk +-- kind of copied from luasocket's manual callback examples +function stats(size) + local start = socket.gettime() + local last = start + local got = 0 + return function(chunk) + -- elapsed time since start + local current = socket.gettime() + if chunk then + -- total bytes received + got = got + string.len(chunk) + -- not enough time for estimate + if current - last > 1 then + io.stderr:write("\r", gauge(got, current - start, size)) + io.stderr:flush() + last = current + end + else + -- close up + io.stderr:write("\r", gauge(got, current - start), "\n") + end + return chunk + end +end + +-- determines the size of a http file +function gethttpsize(u) + local r, c, h = http.request {method = "HEAD", url = u} + if c == 200 then + return tonumber(h["content-length"]) + end +end + +-- downloads a file using the http protocol +function getbyhttp(u, file) + local save = ltn12.sink.file(file or io.stdout) + -- only print feedback if output is not stdout + if file then save = ltn12.sink.chain(stats(gethttpsize(u)), save) end + local r, c, h, s = http.request {url = u, sink = save } + if c ~= 200 then io.stderr:write(s or c, "\n") end +end + +-- downloads a file using the ftp protocol +function getbyftp(u, file) + local save = ltn12.sink.file(file or io.stdout) + -- only print feedback if output is not stdout + -- and we don't know how big the file is + if file then save = ltn12.sink.chain(stats(), save) end + local gett = url.parse(u) + gett.sink = save + gett.type = "i" + local ret, err = ftp.get(gett) + if err then print(err) end +end + +-- determines the scheme +function getscheme(u) + -- this is an heuristic to solve a common invalid url poblem + if not string.find(u, "//") then u = "//" .. u end + local parsed = url.parse(u, {scheme = "http"}) + return parsed.scheme +end + +-- gets a file either by http or ftp, saving as +function get(u, name) + local fout = name and io.open(name, "wb") + local scheme = getscheme(u) + if scheme == "ftp" then getbyftp(u, fout) + elseif scheme == "http" then getbyhttp(u, fout) + else print("unknown scheme" .. scheme) end +end + +-- main program +arg = arg or {} +if #arg < 1 then + io.write("Usage:\n lua get.lua []\n") + os.exit(1) +else get(arg[1], arg[2]) end diff --git a/contrib/luasocket/etc/links b/contrib/luasocket/etc/links new file mode 100644 index 00000000..087f1c0e --- /dev/null +++ b/contrib/luasocket/etc/links @@ -0,0 +1,17 @@ + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla + bla diff --git a/contrib/luasocket/etc/lp.lua b/contrib/luasocket/etc/lp.lua new file mode 100644 index 00000000..25f0b95e --- /dev/null +++ b/contrib/luasocket/etc/lp.lua @@ -0,0 +1,323 @@ +----------------------------------------------------------------------------- +-- LPD support for the Lua language +-- LuaSocket toolkit. +-- Author: David Burgess +-- Modified by Diego Nehab, but David is in charge +----------------------------------------------------------------------------- +--[[ + if you have any questions: RFC 1179 +]] +-- make sure LuaSocket is loaded +local io = require("io") +local base = _G +local os = require("os") +local math = require("math") +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +module("socket.lp") + +-- default port +PORT = 515 +SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" +PRINTER = os.getenv("PRINTER") or "printer" + +local function connect(localhost, option) + local host = option.host or SERVER + local port = option.port or PORT + local skt + local try = socket.newtry(function() if skt then skt:close() end end) + if option.localbind then + -- bind to a local port (if we can) + local localport = 721 + local done, err + repeat + skt = socket.try(socket.tcp()) + try(skt:settimeout(30)) + done, err = skt:bind(localhost, localport) + if not done then + localport = localport + 1 + skt:close() + skt = nil + else break end + until localport > 731 + socket.try(skt, err) + else skt = socket.try(socket.tcp()) end + try(skt:connect(host, port)) + return { skt = skt, try = try } +end + +--[[ +RFC 1179 +5.3 03 - Send queue state (short) + + +----+-------+----+------+----+ + | 03 | Queue | SP | List | LF | + +----+-------+----+------+----+ + Command code - 3 + Operand 1 - Printer queue name + Other operands - User names or job numbers + + If the user names or job numbers or both are supplied then only those + jobs for those users or with those numbers will be sent. + + The response is an ASCII stream which describes the printer queue. + The stream continues until the connection closes. Ends of lines are + indicated with ASCII LF control characters. The lines may also + contain ASCII HT control characters. + +5.4 04 - Send queue state (long) + + +----+-------+----+------+----+ + | 04 | Queue | SP | List | LF | + +----+-------+----+------+----+ + Command code - 4 + Operand 1 - Printer queue name + Other operands - User names or job numbers + + If the user names or job numbers or both are supplied then only those + jobs for those users or with those numbers will be sent. + + The response is an ASCII stream which describes the printer queue. + The stream continues until the connection closes. Ends of lines are + indicated with ASCII LF control characters. The lines may also + contain ASCII HT control characters. +]] + +-- gets server acknowledement +local function recv_ack(con) + local ack = con.skt:receive(1) + con.try(string.char(0) == ack, "failed to receive server acknowledgement") +end + +-- sends client acknowledement +local function send_ack(con) + local sent = con.skt:send(string.char(0)) + con.try(sent == 1, "failed to send acknowledgement") +end + +-- sends queue request +-- 5.2 02 - Receive a printer job +-- +-- +----+-------+----+ +-- | 02 | Queue | LF | +-- +----+-------+----+ +-- Command code - 2 +-- Operand - Printer queue name +-- +-- Receiving a job is controlled by a second level of commands. The +-- daemon is given commands by sending them over the same connection. +-- The commands are described in the next section (6). +-- +-- After this command is sent, the client must read an acknowledgement +-- octet from the daemon. A positive acknowledgement is an octet of +-- zero bits. A negative acknowledgement is an octet of any other +-- pattern. +local function send_queue(con, queue) + queue = queue or PRINTER + local str = string.format("\2%s\10", queue) + local sent = con.skt:send(str) + con.try(sent == string.len(str), "failed to send print request") + recv_ack(con) +end + +-- sends control file +-- 6.2 02 - Receive control file +-- +-- +----+-------+----+------+----+ +-- | 02 | Count | SP | Name | LF | +-- +----+-------+----+------+----+ +-- Command code - 2 +-- Operand 1 - Number of bytes in control file +-- Operand 2 - Name of control file +-- +-- The control file must be an ASCII stream with the ends of lines +-- indicated by ASCII LF. The total number of bytes in the stream is +-- sent as the first operand. The name of the control file is sent as +-- the second. It should start with ASCII "cfA", followed by a three +-- digit job number, followed by the host name which has constructed the +-- control file. Acknowledgement processing must occur as usual after +-- the command is sent. +-- +-- The next "Operand 1" octets over the same TCP connection are the +-- intended contents of the control file. Once all of the contents have +-- been delivered, an octet of zero bits is sent as an indication that +-- the file being sent is complete. A second level of acknowledgement +-- processing must occur at this point. + +-- sends data file +-- 6.3 03 - Receive data file +-- +-- +----+-------+----+------+----+ +-- | 03 | Count | SP | Name | LF | +-- +----+-------+----+------+----+ +-- Command code - 3 +-- Operand 1 - Number of bytes in data file +-- Operand 2 - Name of data file +-- +-- The data file may contain any 8 bit values at all. The total number +-- of bytes in the stream may be sent as the first operand, otherwise +-- the field should be cleared to 0. The name of the data file should +-- start with ASCII "dfA". This should be followed by a three digit job +-- number. The job number should be followed by the host name which has +-- constructed the data file. Interpretation of the contents of the +-- data file is determined by the contents of the corresponding control +-- file. If a data file length has been specified, the next "Operand 1" +-- octets over the same TCP connection are the intended contents of the +-- data file. In this case, once all of the contents have been +-- delivered, an octet of zero bits is sent as an indication that the +-- file being sent is complete. A second level of acknowledgement +-- processing must occur at this point. + + +local function send_hdr(con, control) + local sent = con.skt:send(control) + con.try(sent and sent >= 1 , "failed to send header file") + recv_ack(con) +end + +local function send_control(con, control) + local sent = con.skt:send(control) + con.try(sent and sent >= 1, "failed to send control file") + send_ack(con) +end + +local function send_data(con,fh,size) + local buf + while size > 0 do + buf,message = fh:read(8192) + if buf then + st = con.try(con.skt:send(buf)) + size = size - st + else + con.try(size == 0, "file size mismatch") + end + end + recv_ack(con) -- note the double acknowledgement + send_ack(con) + recv_ack(con) + return size +end + + +--[[ +local control_dflt = { + "H"..string.sub(socket.hostname,1,31).."\10", -- host + "C"..string.sub(socket.hostname,1,31).."\10", -- class + "J"..string.sub(filename,1,99).."\10", -- jobname + "L"..string.sub(user,1,31).."\10", -- print banner page + "I"..tonumber(indent).."\10", -- indent column count ('f' only) + "M"..string.sub(mail,1,128).."\10", -- mail when printed user@host + "N"..string.sub(filename,1,131).."\10", -- name of source file + "P"..string.sub(user,1,31).."\10", -- user name + "T"..string.sub(title,1,79).."\10", -- title for banner ('p' only) + "W"..tonumber(width or 132).."\10", -- width of print f,l,p only + + "f"..file.."\10", -- formatted print (remove control chars) + "l"..file.."\10", -- print + "o"..file.."\10", -- postscript + "p"..file.."\10", -- pr format - requires T, L + "r"..file.."\10", -- fortran format + "U"..file.."\10", -- Unlink (data file only) +} +]] + +-- generate a varying job number +local seq = 0 +local function newjob(connection) + seq = seq + 1 + return math.floor(socket.gettime() * 1000 + seq)%1000 +end + + +local format_codes = { + binary = 'l', + text = 'f', + ps = 'o', + pr = 'p', + fortran = 'r', + l = 'l', + r = 'r', + o = 'o', + p = 'p', + f = 'f' +} + +-- lp.send{option} +-- requires option.file + +send = socket.protect(function(option) + socket.try(option and base.type(option) == "table", "invalid options") + local file = option.file + socket.try(file, "invalid file name") + local fh = socket.try(io.open(file,"rb")) + local datafile_size = fh:seek("end") -- get total size + fh:seek("set") -- go back to start of file + local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") + or "localhost" + local con = connect(localhost, option) +-- format the control file + local jobno = newjob() + local localip = socket.dns.toip(localhost) + localhost = string.sub(localhost,1,31) + local user = string.sub(option.user or os.getenv("LPRUSER") or + os.getenv("USERNAME") or os.getenv("USER") or "anonymous", 1,31) + local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); + local fmt = format_codes[option.format] or 'l' + local class = string.sub(option.class or localip or localhost,1,31) + local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") + ctlfn = string.sub(ctlfn or file,1,131) + local cfile = + string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", + localhost, + class, + option.job or "LuaSocket", + user, + fmt, lpfile, + lpfile, + ctlfn); -- mandatory part of ctl file + if (option.banner) then cfile = cfile .. 'L'..user..'\10' end + if (option.indent) then cfile = cfile .. 'I'..base.tonumber(option.indent)..'\10' end + if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end + if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end + if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then + cfile = cfile .. 'W'..base.tonumber(option,width)..'\10' + end + + con.skt:settimeout(option.timeout or 65) +-- send the queue header + send_queue(con, option.queue) +-- send the control file header + local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); + send_hdr(con,cfilecmd) + +-- send the control file + send_control(con,cfile) + +-- send the data file header + local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); + send_hdr(con,dfilecmd) + +-- send the data file + send_data(con,fh,datafile_size) + fh:close() + con.skt:close(); + return jobno, datafile_size +end) + +-- +-- lp.query({host=,queue=printer|'*', format='l'|'s', list=}) +-- +query = socket.protect(function(p) + p = p or {} + local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") + or "localhost" + local con = connect(localhost,p) + local fmt + if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end + con.try(con.skt:send(string.format("%c%s %s\n", fmt, p.queue or "*", + p.list or ""))) + local data = con.try(con.skt:receive("*a")) + con.skt:close() + return data +end) diff --git a/contrib/luasocket/etc/qp.lua b/contrib/luasocket/etc/qp.lua new file mode 100644 index 00000000..523238b1 --- /dev/null +++ b/contrib/luasocket/etc/qp.lua @@ -0,0 +1,23 @@ +----------------------------------------------------------------------------- +-- Little program to convert to and from Quoted-Printable +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local ltn12 = require("ltn12") +local mime = require("mime") +local convert +arg = arg or {} +local mode = arg and arg[1] or "-et" +if mode == "-et" then + local normalize = mime.normalize() + local qp = mime.encode("quoted-printable") + local wrap = mime.wrap("quoted-printable") + convert = ltn12.filter.chain(normalize, qp, wrap) +elseif mode == "-eb" then + local qp = mime.encode("quoted-printable", "binary") + local wrap = mime.wrap("quoted-printable") + convert = ltn12.filter.chain(qp, wrap) +else convert = mime.decode("quoted-printable") end +local source = ltn12.source.chain(ltn12.source.file(io.stdin), convert) +local sink = ltn12.sink.file(io.stdout) +ltn12.pump.all(source, sink) diff --git a/contrib/luasocket/etc/tftp.lua b/contrib/luasocket/etc/tftp.lua new file mode 100644 index 00000000..ed99cd16 --- /dev/null +++ b/contrib/luasocket/etc/tftp.lua @@ -0,0 +1,154 @@ +----------------------------------------------------------------------------- +-- TFTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Load required files +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local math = require("math") +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +local url = require("socket.url") +module("socket.tftp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +local char = string.char +local byte = string.byte + +PORT = 69 +local OP_RRQ = 1 +local OP_WRQ = 2 +local OP_DATA = 3 +local OP_ACK = 4 +local OP_ERROR = 5 +local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} + +----------------------------------------------------------------------------- +-- Packet creation functions +----------------------------------------------------------------------------- +local function RRQ(source, mode) + return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) +end + +local function WRQ(source, mode) + return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) +end + +local function ACK(block) + local low, high + low = math.mod(block, 256) + high = (block - low)/256 + return char(0, OP_ACK, high, low) +end + +local function get_OP(dgram) + local op = byte(dgram, 1)*256 + byte(dgram, 2) + return op +end + +----------------------------------------------------------------------------- +-- Packet analysis functions +----------------------------------------------------------------------------- +local function split_DATA(dgram) + local block = byte(dgram, 3)*256 + byte(dgram, 4) + local data = string.sub(dgram, 5) + return block, data +end + +local function get_ERROR(dgram) + local code = byte(dgram, 3)*256 + byte(dgram, 4) + local msg + _,_, msg = string.find(dgram, "(.*)\000", 5) + return string.format("error code %d: %s", code, msg) +end + +----------------------------------------------------------------------------- +-- The real work +----------------------------------------------------------------------------- +local function tget(gett) + local retries, dgram, sent, datahost, dataport, code + local last = 0 + socket.try(gett.host, "missing host") + local con = socket.try(socket.udp()) + local try = socket.newtry(function() con:close() end) + -- convert from name to ip if needed + gett.host = try(socket.dns.toip(gett.host)) + con:settimeout(1) + -- first packet gives data host/port to be used for data transfers + local path = string.gsub(gett.path or "", "^/", "") + path = url.unescape(path) + retries = 0 + repeat + sent = try(con:sendto(RRQ(path, "octet"), gett.host, gett.port)) + dgram, datahost, dataport = con:receivefrom() + retries = retries + 1 + until dgram or datahost ~= "timeout" or retries > 5 + try(dgram, datahost) + -- associate socket with data host/port + try(con:setpeername(datahost, dataport)) + -- default sink + local sink = gett.sink or ltn12.sink.null() + -- process all data packets + while 1 do + -- decode packet + code = get_OP(dgram) + try(code ~= OP_ERROR, get_ERROR(dgram)) + try(code == OP_DATA, "unhandled opcode " .. code) + -- get data packet parts + local block, data = split_DATA(dgram) + -- if not repeated, write + if block == last+1 then + try(sink(data)) + last = block + end + -- last packet brings less than 512 bytes of data + if string.len(data) < 512 then + try(con:send(ACK(block))) + try(con:close()) + try(sink(nil)) + return 1 + end + -- get the next packet + retries = 0 + repeat + sent = try(con:send(ACK(last))) + dgram, err = con:receive() + retries = retries + 1 + until dgram or err ~= "timeout" or retries > 5 + try(dgram, err) + end +end + +local default = { + port = PORT, + path ="/", + scheme = "tftp" +} + +local function parse(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "tftp", "invalid scheme '" .. t.scheme .. "'") + socket.try(t.host, "invalid host") + return t +end + +local function sget(u) + local gett = parse(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + diff --git a/contrib/luasocket/gem/ex1.lua b/contrib/luasocket/gem/ex1.lua new file mode 100644 index 00000000..327a5421 --- /dev/null +++ b/contrib/luasocket/gem/ex1.lua @@ -0,0 +1,4 @@ +local CRLF = "\013\010" +local input = source.chain(source.file(io.stdin), normalize(CRLF)) +local output = sink.file(io.stdout) +pump.all(input, output) diff --git a/contrib/luasocket/gem/ex10.lua b/contrib/luasocket/gem/ex10.lua new file mode 100644 index 00000000..2b1b98f2 --- /dev/null +++ b/contrib/luasocket/gem/ex10.lua @@ -0,0 +1,17 @@ +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + if chunk and ret then return 1 + else return nil, src_err or snk_err end +end + +function pump.all(src, snk, step) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end diff --git a/contrib/luasocket/gem/ex11.lua b/contrib/luasocket/gem/ex11.lua new file mode 100644 index 00000000..1cbf01fd --- /dev/null +++ b/contrib/luasocket/gem/ex11.lua @@ -0,0 +1,7 @@ +local input = source.chain( + source.file(io.open("input.bin", "rb")), + encode("base64")) +local output = sink.chain( + wrap(76), + sink.file(io.open("output.b64", "w"))) +pump.all(input, output) diff --git a/contrib/luasocket/gem/ex12.lua b/contrib/luasocket/gem/ex12.lua new file mode 100644 index 00000000..de17d76f --- /dev/null +++ b/contrib/luasocket/gem/ex12.lua @@ -0,0 +1,34 @@ +local smtp = require"socket.smtp" +local mime = require"mime" +local ltn12 = require"ltn12" + +CRLF = "\013\010" + +local message = smtp.message{ + headers = { + from = "Sicrano ", + to = "Fulano ", + subject = "A message with an attachment"}, + body = { + preamble = "Hope you can see the attachment" .. CRLF, + [1] = { + body = "Here is our logo" .. CRLF}, + [2] = { + headers = { + ["content-type"] = 'image/png; name="luasocket.png"', + ["content-disposition"] = + 'attachment; filename="luasocket.png"', + ["content-description"] = 'LuaSocket logo', + ["content-transfer-encoding"] = "BASE64"}, + body = ltn12.source.chain( + ltn12.source.file(io.open("luasocket.png", "rb")), + ltn12.filter.chain( + mime.encode("base64"), + mime.wrap()))}}} + +assert(smtp.send{ + rcpt = "", + from = "", + server = "localhost", + port = 2525, + source = message}) diff --git a/contrib/luasocket/gem/ex2.lua b/contrib/luasocket/gem/ex2.lua new file mode 100644 index 00000000..94bde661 --- /dev/null +++ b/contrib/luasocket/gem/ex2.lua @@ -0,0 +1,11 @@ +function filter.cycle(lowlevel, context, extra) + return function(chunk) + local ret + ret, context = lowlevel(context, chunk, extra) + return ret + end +end + +function normalize(marker) + return filter.cycle(eol, 0, marker) +end diff --git a/contrib/luasocket/gem/ex3.lua b/contrib/luasocket/gem/ex3.lua new file mode 100644 index 00000000..a43fefa9 --- /dev/null +++ b/contrib/luasocket/gem/ex3.lua @@ -0,0 +1,15 @@ +local function chainpair(f1, f2) + return function(chunk) + local ret = f2(f1(chunk)) + if chunk then return ret + else return (ret or "") .. (f2() or "") end + end +end + +function filter.chain(...) + local f = select(1, ...) + for i = 2, select('#', ...) do + f = chainpair(f, select(i, ...)) + end + return f +end diff --git a/contrib/luasocket/gem/ex4.lua b/contrib/luasocket/gem/ex4.lua new file mode 100644 index 00000000..c670e0e4 --- /dev/null +++ b/contrib/luasocket/gem/ex4.lua @@ -0,0 +1,5 @@ +local qp = filter.chain(normalize(CRLF), encode("quoted-printable"), + wrap("quoted-printable")) +local input = source.chain(source.file(io.stdin), qp) +local output = sink.file(io.stdout) +pump.all(input, output) diff --git a/contrib/luasocket/gem/ex5.lua b/contrib/luasocket/gem/ex5.lua new file mode 100644 index 00000000..196b30a1 --- /dev/null +++ b/contrib/luasocket/gem/ex5.lua @@ -0,0 +1,15 @@ +function source.empty(err) + return function() + return nil, err + end +end + +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(20) + if not chunk then handle:close() end + return chunk + end + else return source.empty(io_err or "unable to open file") end +end diff --git a/contrib/luasocket/gem/ex6.lua b/contrib/luasocket/gem/ex6.lua new file mode 100644 index 00000000..a3fdca0a --- /dev/null +++ b/contrib/luasocket/gem/ex6.lua @@ -0,0 +1,14 @@ +function source.chain(src, f) + return function() + if not src then + return nil + end + local chunk, err = src() + if not chunk then + src = nil + return f(nil) + else + return f(chunk) + end + end +end diff --git a/contrib/luasocket/gem/ex7.lua b/contrib/luasocket/gem/ex7.lua new file mode 100644 index 00000000..c7669884 --- /dev/null +++ b/contrib/luasocket/gem/ex7.lua @@ -0,0 +1,16 @@ +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +local function null() + return 1 +end + +function sink.null() + return null +end diff --git a/contrib/luasocket/gem/ex8.lua b/contrib/luasocket/gem/ex8.lua new file mode 100644 index 00000000..81e288c8 --- /dev/null +++ b/contrib/luasocket/gem/ex8.lua @@ -0,0 +1,5 @@ +local input = source.file(io.stdin) +local output, t = sink.table() +output = sink.chain(normalize(CRLF), output) +pump.all(input, output) +io.write(table.concat(t)) diff --git a/contrib/luasocket/gem/ex9.lua b/contrib/luasocket/gem/ex9.lua new file mode 100644 index 00000000..b857698a --- /dev/null +++ b/contrib/luasocket/gem/ex9.lua @@ -0,0 +1,3 @@ +for chunk in source.file(io.stdin) do + io.write(chunk) +end diff --git a/contrib/luasocket/gem/gem.c b/contrib/luasocket/gem/gem.c new file mode 100644 index 00000000..976f74d4 --- /dev/null +++ b/contrib/luasocket/gem/gem.c @@ -0,0 +1,54 @@ +#include "lua.h" +#include "lauxlib.h" + +#define CR '\xD' +#define LF '\xA' +#define CRLF "\xD\xA" + +#define candidate(c) (c == CR || c == LF) +static int pushchar(int c, int last, const char *marker, + luaL_Buffer *buffer) { + if (candidate(c)) { + if (candidate(last)) { + if (c == last) + luaL_addstring(buffer, marker); + return 0; + } else { + luaL_addstring(buffer, marker); + return c; + } + } else { + luaL_putchar(buffer, c); + return 0; + } +} + +static int eol(lua_State *L) { + int context = luaL_checkint(L, 1); + size_t isize = 0; + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 0); + return 2; + } + while (input < last) + context = pushchar(*input++, context, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, context); + return 2; +} + +static luaL_reg func[] = { + { "eol", eol }, + { NULL, NULL } +}; + +int luaopen_gem(lua_State *L) { + luaL_openlib(L, "gem", func, 0); + return 0; +} diff --git a/contrib/luasocket/gem/gt.b64 b/contrib/luasocket/gem/gt.b64 new file mode 100644 index 00000000..28071bdd --- /dev/null +++ b/contrib/luasocket/gem/gt.b64 @@ -0,0 +1,206 @@ +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 diff --git a/contrib/luasocket/gem/input.bin b/contrib/luasocket/gem/input.bin new file mode 100644 index 0000000000000000000000000000000000000000..d24a95495b55894b96aec1b01d0b7c02cc43f168 GIT binary patch literal 11732 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 /dev/null` + then + ofilename=`echo $1 | sed 's/\..*$/.pdf/'` + else + echo "$usage" 1>&2 + exit 1 + fi ;; +*) echo "$usage" 1>&2 ; exit 1 ;; +esac + +if [ $best == 1 ] +then + options="-dPDFSETTINGS=/prepress \ + -r1200 \ + -dMonoImageResolution=1200 \ + -dGrayImageResolution=1200 \ + -dColorImageResolution=1200 \ + -dDownsampleMonoImages=false \ + -dDownsampleGrayImages=false \ + -dDownsampleColorImages=false \ + -dAutoFilterMonoImages=false \ + -dAutoFilterGrayImages=false \ + -dAutoFilterColorImages=false \ + -dMonoImageFilter=/FlateEncode \ + -dGrayImageFilter=/FlateEncode \ + -dColorImageFilter=/FlateEncode" +else + options="-dPDFSETTINGS=/prepress \ + -r600 \ + -dDownsampleMonoImages=true \ + -dDownsampleGrayImages=true \ + -dDownsampleColorImages=true \ + -dMonoImageDownsampleThreshold=2.0 \ + -dGrayImageDownsampleThreshold=1.5 \ + -dColorImageDownsampleThreshold=1.5 \ + -dMonoImageResolution=600 \ + -dGrayImageResolution=600 \ + -dColorImageResolution=600 \ + -dAutoFilterMonoImages=false \ + -dMonoImageFilter=/FlateEncode \ + -dAutoFilterGrayImages=true \ + -dAutoFilterColorImages=true" +fi + +if [ $rot == 1 ] +then + options="$options -dAutoRotatePages=/PageByPage" +fi + +if [ $eps == 1 ] +then + options="$options -dEPSCrop" +fi + +set -x + +if [ $a4 == 1 ] +then + # Resize from A4 to letter size + psresize -Pa4 -pletter "$ifilename" myps2pdf.temp.ps + ifilename=myps2pdf.temp.ps +fi + +gs -q -dSAFER -dNOPAUSE -dBATCH \ + -sDEVICE=pdfwrite -sPAPERSIZE=letter -sOutputFile=myps2pdf.temp.pdf \ + -dCompatibilityLevel=1.3 \ + $options \ + -dMaxSubsetPct=100 \ + -dSubsetFonts=true \ + -dEmbedAllFonts=true \ + -dColorConversionStrategy=/LeaveColorUnchanged \ + -dDoThumbnails=true \ + -dPreserveEPSInfo=true \ + -c .setpdfwrite -f "$ifilename" + +if [ $do_opt == 1 ] +then + pdfopt myps2pdf.temp.pdf $ofilename +else + mv myps2pdf.temp.pdf $ofilename +fi +rm -f myps2pdf.temp.pdf myps2pdf.temp.ps diff --git a/contrib/luasocket/gem/t1.lua b/contrib/luasocket/gem/t1.lua new file mode 100644 index 00000000..0c054c90 --- /dev/null +++ b/contrib/luasocket/gem/t1.lua @@ -0,0 +1,25 @@ +source = {} +sink = {} +pump = {} +filter = {} + +-- source.chain +dofile("ex6.lua") + +-- source.file +dofile("ex5.lua") + +-- normalize +require"gem" +eol = gem.eol +dofile("ex2.lua") + +-- sink.file +require"ltn12" +sink.file = ltn12.sink.file + +-- pump.all +dofile("ex10.lua") + +-- run test +dofile("ex1.lua") diff --git a/contrib/luasocket/gem/t1lf.txt b/contrib/luasocket/gem/t1lf.txt new file mode 100644 index 00000000..8cddd1b5 --- /dev/null +++ b/contrib/luasocket/gem/t1lf.txt @@ -0,0 +1,5 @@ +this is a test file +it should have been saved as lf eol +but t1.lua will convert it to crlf eol +otherwise it is broken! + diff --git a/contrib/luasocket/gem/t2.lua b/contrib/luasocket/gem/t2.lua new file mode 100644 index 00000000..a81ed733 --- /dev/null +++ b/contrib/luasocket/gem/t2.lua @@ -0,0 +1,36 @@ +source = {} +sink = {} +pump = {} +filter = {} + +-- filter.chain +dofile("ex3.lua") + +-- normalize +require"gem" +eol = gem.eol +dofile("ex2.lua") + +-- encode +require"mime" +encode = mime.encode + +-- wrap +wrap = mime.wrap + +-- source.chain +dofile("ex6.lua") + +-- source.file +dofile("ex5.lua") + +-- sink.file +require"ltn12" +sink.file = ltn12.sink.file + +-- pump.all +dofile("ex10.lua") + +-- run test +CRLF = "\013\010" +dofile("ex4.lua") diff --git a/contrib/luasocket/gem/t2.txt b/contrib/luasocket/gem/t2.txt new file mode 100644 index 00000000..f484fe8e --- /dev/null +++ b/contrib/luasocket/gem/t2.txt @@ -0,0 +1,4 @@ +esse é um texto com acentos +quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de texto +fora que as quebras de linhas têm que ser normalizadas +vamos ver o que dá isso aqui diff --git a/contrib/luasocket/gem/t2gt.qp b/contrib/luasocket/gem/t2gt.qp new file mode 100644 index 00000000..d63d894e --- /dev/null +++ b/contrib/luasocket/gem/t2gt.qp @@ -0,0 +1,5 @@ +esse =E9 um texto com acentos +quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de t= +exto +fora que as quebras de linhas t=EAm que ser normalizadas +vamos ver o que d=E1 isso aqui diff --git a/contrib/luasocket/gem/t3.lua b/contrib/luasocket/gem/t3.lua new file mode 100644 index 00000000..4bb98ba5 --- /dev/null +++ b/contrib/luasocket/gem/t3.lua @@ -0,0 +1,25 @@ +source = {} +sink = {} +pump = {} +filter = {} + +-- source.file +dofile("ex5.lua") + +-- sink.table +dofile("ex7.lua") + +-- sink.chain +require"ltn12" +sink.chain = ltn12.sink.chain + +-- normalize +require"gem" +eol = gem.eol +dofile("ex2.lua") + +-- pump.all +dofile("ex10.lua") + +-- run test +dofile("ex8.lua") diff --git a/contrib/luasocket/gem/t4.lua b/contrib/luasocket/gem/t4.lua new file mode 100644 index 00000000..8b8071c2 --- /dev/null +++ b/contrib/luasocket/gem/t4.lua @@ -0,0 +1,10 @@ +source = {} +sink = {} +pump = {} +filter = {} + +-- source.file +dofile("ex5.lua") + +-- run test +dofile("ex9.lua") diff --git a/contrib/luasocket/gem/t5.lua b/contrib/luasocket/gem/t5.lua new file mode 100644 index 00000000..7c569ead --- /dev/null +++ b/contrib/luasocket/gem/t5.lua @@ -0,0 +1,30 @@ +source = {} +sink = {} +pump = {} +filter = {} + +-- source.chain +dofile("ex6.lua") + +-- source.file +dofile("ex5.lua") + +-- encode +require"mime" +encode = mime.encode + +-- sink.chain +require"ltn12" +sink.chain = ltn12.sink.chain + +-- wrap +wrap = mime.wrap + +-- sink.file +sink.file = ltn12.sink.file + +-- pump.all +dofile("ex10.lua") + +-- run test +dofile("ex11.lua") diff --git a/contrib/luasocket/gem/test.lua b/contrib/luasocket/gem/test.lua new file mode 100644 index 00000000..a937b9a1 --- /dev/null +++ b/contrib/luasocket/gem/test.lua @@ -0,0 +1,46 @@ +function readfile(n) + local f = io.open(n, "rb") + local s = f:read("*a") + f:close() + return s +end + +lf = readfile("t1lf.txt") +os.remove("t1crlf.txt") +os.execute("lua t1.lua < t1lf.txt > t1crlf.txt") +crlf = readfile("t1crlf.txt") +assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken") + +gt = readfile("t2gt.qp") +os.remove("t2.qp") +os.execute("lua t2.lua < t2.txt > t2.qp") +t2 = readfile("t2.qp") +assert(gt == t2, "broken") + +os.remove("t1crlf.txt") +os.execute("lua t3.lua < t1lf.txt > t1crlf.txt") +crlf = readfile("t1crlf.txt") +assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken") + +t = readfile("test.lua") +os.execute("lua t4.lua < test.lua > t") +t2 = readfile("t") +assert(t == t2, "broken") + +os.remove("output.b64") +gt = readfile("gt.b64") +os.execute("lua t5.lua") +t5 = readfile("output.b64") +assert(gt == t5, "failed") + +print("1 2 5 6 10 passed") +print("2 3 4 5 6 10 passed") +print("2 5 6 7 8 10 passed") +print("5 9 passed") +print("5 6 10 11 passed") + +os.remove("t") +os.remove("t2.qp") +os.remove("t1crlf.txt") +os.remove("t11.b64") +os.remove("output.b64") diff --git a/contrib/luasocket/linux.cmd b/contrib/luasocket/linux.cmd new file mode 100644 index 00000000..6c6636b8 --- /dev/null +++ b/contrib/luasocket/linux.cmd @@ -0,0 +1 @@ +make PLAT=linux DEBUG=DEBUG LUAINC_linux_base=/home/diego/build/ubuntu/include LUAPREFIX_linux=/home/diego/build/ubuntu diff --git a/contrib/luasocket/logo.ps b/contrib/luasocket/logo.ps new file mode 100644 index 00000000..8b5809ab --- /dev/null +++ b/contrib/luasocket/logo.ps @@ -0,0 +1,210 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: Lua logo +%%Creator: lua@tecgraf.puc-rio.br +%%CreationDate: Wed Nov 29 19:04:04 EDT 2000 +%%BoundingBox: -45 0 1035 1080 +%%Pages: 1 +%%EndComments +%%EndProlog + +%------------------------------------------------------------------------------ +% +% Copyright (C) 1998-2000. All rights reserved. +% Graphic design by Alexandre Nakonechny (nako@openlink.com.br). +% PostScript programming by the Lua team (lua@tecgraf.puc-rio.br). +% +% Permission is hereby granted, without written agreement and without license +% or royalty fees, to use, copy, and distribute this logo for any purpose, +% including commercial applications, subject to the following conditions: +% +% * The origin of this logo must not be misrepresented; you must not +% claim that you drew the original logo. We recommend that you give credit +% to the graphics designer in all printed matter that includes the logo. +% +% * The only modification you can make is to adapt the orbiting text to +% your product name. +% +% * The logo can be used in any scale as long as the relative proportions +% of its elements are maintained. +% +%------------------------------------------------------------------------------ + +/LABEL (tekcoS) def + +%-- DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------------------------ + +/PLANETCOLOR {0 0 0.5 setrgbcolor} bind def +/HOLECOLOR {1.0 setgray} bind def +/ORBITCOLOR {0.5 setgray} bind def +/LOGOFONT {/Helvetica 0.90} def +/LABELFONT {/Helvetica 0.36} def + +%------------------------------------------------------------------------------ + +/MOONCOLOR {PLANETCOLOR} bind def +/LOGOCOLOR {HOLECOLOR} bind def +/LABELCOLOR {ORBITCOLOR} bind def + +/LABELANGLE 325 def +/LOGO (Lua) def + +/DASHANGLE 10 def +/HALFDASHANGLE DASHANGLE 2 div def + +% moon radius. planet radius is 1. +/r 1 2 sqrt 2 div sub def + +/D {0 360 arc fill} bind def +/F {exch findfont exch scalefont setfont} bind def + +% place it nicely on the paper +/RESOLUTION 1024 def +RESOLUTION 2 div dup translate +RESOLUTION 2 div 2 sqrt div dup scale + +%-------------------------------------------------------------------- planet -- +PLANETCOLOR +0 0 1 D + +%---------------------------------------------------------------------- hole -- +HOLECOLOR +1 2 r mul sub dup r D + +%---------------------------------------------------------------------- moon -- +MOONCOLOR +1 1 r D + +%---------------------------------------------------------------------- logo -- +LOGOCOLOR +LOGOFONT +F +LOGO stringwidth pop 2 div neg +-0.5 moveto +LOGO show + +%------------------------------------------------------------------------------ +% based on code from Blue Book Program 10, on pages 167--169 +% available at ftp://ftp.adobe.com/pub/adobe/displaypostscript/bluebook.shar + +% str ptsize centerangle radius outsidecircletext -- +/outsidecircletext { + circtextdict begin + /radius exch def + /centerangle exch def + /ptsize exch def + /str exch def + + gsave + str radius ptsize findhalfangle + centerangle + add rotate + str + { /charcode exch def + ( ) dup 0 charcode put outsideplacechar + } forall + + grestore + end +} def + +% string radius ptsize findhalfangle halfangle +/findhalfangle { + 4 div add + exch + stringwidth pop 2 div + exch + 2 mul 3.1415926535 mul div 360 mul +} def + +/circtextdict 16 dict def +circtextdict begin + + /outsideplacechar { + /char exch def + /halfangle char radius ptsize findhalfangle def + gsave + halfangle neg rotate + 1.4 0 translate + 90 rotate + char stringwidth pop 2 div neg 0 moveto + char show + grestore + halfangle 2 mul neg rotate + } def + +end + +%--------------------------------------------------------------------- label -- +LABELFONT +F + +/LABELSIZE LABELFONT exch pop def +/LABELRADIUS LABELSIZE 3 div 1 r add sub neg 1.02 mul def + + +/HALFANGLE + LABEL LABELRADIUS LABELSIZE findhalfangle + HALFDASHANGLE div ceiling HALFDASHANGLE mul +def + +/LABELANGLE + 60 LABELANGLE HALFANGLE sub + lt + { + HALFANGLE + HALFANGLE DASHANGLE div floor DASHANGLE mul + eq + {LABELANGLE DASHANGLE div ceiling DASHANGLE mul} + {LABELANGLE HALFDASHANGLE sub DASHANGLE div round DASHANGLE mul HALFDASHANGLE add} + ifelse + } + {HALFANGLE 60 add} + ifelse +def + +LABELCOLOR +LABEL +LABELSIZE +LABELANGLE +LABELRADIUS +outsidecircletext + +%--------------------------------------------------------------------- orbit -- +ORBITCOLOR +0.03 setlinewidth +[1 r add 3.1415926535 180 div HALFDASHANGLE mul mul] 0 setdash +newpath +0 0 +1 r add +3 copy +30 +LABELANGLE HALFANGLE add +arcn +stroke +60 +LABELANGLE HALFANGLE sub +2 copy +lt {arc stroke} {4 {pop} repeat} ifelse + +%------------------------------------------------------------------ copyright -- +/COPYRIGHT +(Graphic design by A. Nakonechny. Copyright (c) 1998, All rights reserved.) +def + +LABELCOLOR +LOGOFONT +32 div +F +2 sqrt 0.99 mul +dup +neg +moveto +COPYRIGHT +90 rotate +%show + +%---------------------------------------------------------------------- done -- +showpage + +%%Trailer +%%EOF diff --git a/contrib/luasocket/ltn012.wiki b/contrib/luasocket/ltn012.wiki new file mode 100644 index 00000000..96b13ae4 --- /dev/null +++ b/contrib/luasocket/ltn012.wiki @@ -0,0 +1,393 @@ +===Filters, sources and sinks: design, motivation and examples=== +==or Functional programming for the rest of us== +by DiegoNehab + +{{{ + +}}} + +===Abstract=== +Certain operations can be implemented in the form of filters. A filter is a function that processes data received in consecutive function calls, returning partial results chunk by chunk. Examples of operations that can be implemented as filters include the end-of-line normalization for text, Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing, and there are many others. Filters become even more powerful when we allow them to be chained together to create composite filters. Filters can be seen as middle nodes in a chain of data transformations. Sources an sinks are the corresponding end points of these chains. A source is a function that produces data, chunk by chunk, and a sink is a function that takes data, chunk by chunk. In this technical note, we define an elegant interface for filters, sources, sinks and chaining. We evolve our interface progressively, until we reach a high degree of generality. We discuss difficulties that arise during the implementation of this interface and we provide solutions and examples. + +===Introduction=== + +Applications sometimes have too much information to process to fit in memory and are thus forced to process data in smaller parts. Even when there is enough memory, processing all the data atomically may take long enough to frustrate a user that wants to interact with the application. Furthermore, complex transformations can often be defined as series of simpler operations. Several different complex transformations might share the same simpler operations, so that an uniform interface to combine them is desirable. The following concepts constitute our solution to these problems. + +''Filters'' are functions that accept successive chunks of input, and produce successive chunks of output. Furthermore, the result of concatenating all the output data is the same as the result of applying the filter over the concatenation of the input data. As a consequence, boundaries are irrelevant: filters have to handle input data split arbitrarily by the user. + +A ''chain'' is a function that combines the effect of two (or more) other functions, but whose interface is indistinguishable from the interface of one of its components. Thus, a chained filter can be used wherever an atomic filter can be used. However, its effect on data is the combined effect of its component filters. Note that, as a consequence, chains can be chained themselves to create arbitrarily complex operations that can be used just like atomic operations. + +Filters can be seen as internal nodes in a network through which data flows, potentially being transformed along its way. Chains connect these nodes together. To complete the picture, we need ''sources'' and ''sinks'' as initial and final nodes of the network, respectively. Less abstractly, a source is a function that produces new data every time it is called. On the other hand, sinks are functions that give a final destination to the data they receive. Naturally, sources and sinks can be chained with filters. + +Finally, filters, chains, sources, and sinks are all passive entities: they need to be repeatedly called in order for something to happen. ''Pumps'' provide the driving force that pushes data through the network, from a source to a sink. + + Hopefully, these concepts will become clear with examples. In the following sections, we start with simplified interfaces, which we improve several times until we can find no obvious shortcomings. The evolution we present is not contrived: it follows the steps we followed ourselves as we consolidated our understanding of these concepts. + +== A concrete example == + +Some data transformations are easier to implement as filters than others. Examples of operations that can be implemented as filters include the end-of-line normalization for text, the Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing, and many others. Let's use the end-of-line normalization as an example to define our initial filter interface. We later discuss why the implementation might not be trivial. + +Assume we are given text in an unknown end-of-line convention (including possibly mixed conventions) out of the commonly found Unix (LF), Mac OS (CR), and DOS (CRLF) conventions. We would like to be able to write code like the following: + {{{ +input = source.chain(source.file(io.stdin), normalize("\r\n")) +output = sink.file(io.stdout) +pump(input, output) +}}} + +This program should read data from the standard input stream and normalize the end-of-line markers to the canonic CRLF marker defined by the MIME standard, finally sending the results to the standard output stream. For that, we use a ''file source'' to produce data from standard input, and chain it with a filter that normalizes the data. The pump then repeatedly gets data from the source, and moves it to the ''file sink'' that sends it to standard output. + +To make the discussion even more concrete, we start by discussing the implementation of the normalization filter. The {{normalize}} ''factory'' is a function that creates such a filter. Our initial filter interface is as follows: the filter receives a chunk of input data, and returns a chunk of processed data. When there is no more input data, the user notifies the filter by invoking it with a {{nil}} chunk. The filter then returns the final chunk of processed data. + +Although the interface is extremely simple, the implementation doesn't seem so obvious. Any filter respecting this interface needs to keep some kind of context between calls. This is because chunks can be broken between the CR and LF characters marking the end of a line. This need for context storage is what motivates the use of factories: each time the factory is called, it returns a filter with its own context so that we can have several independent filters being used at the same time. For the normalization filter, we know that the obvious solution (i.e. concatenating all the input into the context before producing any output) is not good enough, so we will have to find another way. + +We will break the implementation in two parts: a low-level filter, and a factory of high-level filters. The low-level filter will be implemented in C and will not carry any context between function calls. The high-level filter factory, implemented in Lua, will create and return a high-level filter that keeps whatever context the low-level filter needs, but isolates the user from its internal details. That way, we take advantage of C's efficiency to perform the dirty work, and take advantage of Lua's simplicity for the bookkeeping. + +==The Lua part of the implementation== + +Below is the implementation of the factory of high-level end-of-line normalization filters: + {{{ +function filter.cycle(low, ctx, extra) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +function normalize(marker) + return cycle(eol, 0, marker) +end +}}} + +The {{normalize}} factory simply calls a more generic factory, the {{cycle}} factory. This factory receives a low-level filter, an initial context and some extra value and returns the corresponding high-level filter. Each time the high level filer is called with a new chunk, it calls the low-level filter passing the previous context, the new chunk and the extra argument. The low-level filter produces the chunk of processed data and a new context. Finally, the high-level filter updates its internal context and returns the processed chunk of data to the user. It is the low-level filter that does all the work. Notice that this implementation takes advantage of the Lua 5.0 lexical scoping rules to store the context locally, between function calls. + +Moving to the low-level filter, we notice there is no perfect solution to the end-of-line marker normalization problem itself. The difficulty comes from an inherent ambiguity on the definition of empty lines within mixed input. However, the following solution works well for any consistent input, as well as for non-empty lines in mixed input. It also does a reasonable job with empty lines and serves as a good example of how to implement a low-level filter. + +Here is what we do: CR and LF are considered candidates for line break. We issue ''one'' end-of-line line marker if one of the candidates is seen alone, or followed by a ''different'' candidate. That is, CR CR and LF LF issue two end of line markers each, but CR LF and LF CR issue only one marker. This idea takes care of Mac OS, Mac OS X, VMS and Unix, DOS and MIME, as well as probably other more obscure conventions. + +==The C part of the implementation== + +The low-level filter is divided into two simple functions. The inner function actually does the conversion. It takes each input character in turn, deciding what to output and how to modify the context. The context tells if the last character seen was a candidate and, if so, which candidate it was. + {{{ +#define candidate(c) (c == CR || c == LF) +static int process(int c, int last, const char *marker, luaL_Buffer *buffer) { + if (candidate(c)) { + if (candidate(last)) { + if (c == last) luaL_addstring(buffer, marker); + return 0; + } else { + luaL_addstring(buffer, marker); + return c; + } + } else { + luaL_putchar(buffer, c); + return 0; + } +} +}}} + +The inner function makes use of Lua's auxiliary library's buffer interface for its efficiency and ease of use. The outer function simply interfaces with Lua. It receives the context and the input chunk (as well as an optional end-of-line marker), and returns the transformed output and the new context. + {{{ +static int eol(lua_State *L) { + int ctx = luaL_checkint(L, 1); + size_t isize = 0; + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 0); + return 2; + } + while (input < last) + ctx = process(*input++, ctx, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, ctx); + return 2; +} +}}} + +Notice that if the input chunk is {{nil}}, the operation is considered to be finished. In that case, the loop will not execute a single time and the context is reset to the initial state. This allows the filter to be reused indefinitely. It is a good idea to write filters like this, when possible. + +Besides the end-of-line normalization filter shown above, many other filters can be implemented with the same ideas. Examples include Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing etc. The challenging part is to decide what will be the context. For line breaking, for instance, it could be the number of bytes left in the current line. For Base64 encoding, it could be the bytes that remain in the division of the input into 3-byte atoms. + +===Chaining=== + +Filters become more powerful when the concept of chaining is introduced. Suppose you have a filter for Quoted-Printable encoding and you want to encode some text. According to the standard, the text has to be normalized into its canonic form prior to encoding. A nice interface that simplifies this task is a factory that creates a composite filter that passes data through multiple filters, but that can be used wherever a primitive filter is used. + {{{ +local function chain2(f1, f2) + return function(chunk) + local ret = f2(f1(chunk)) + if chunk then return ret + else return ret .. f2() end + end +end + +function filter.chain(...) + local arg = {...} + local f = arg[1] + for i = 2, #arg do + f = chain2(f, arg[i]) + end + return f +end + +local chain = filter.chain(normalize("\r\n"), encode("quoted-printable")) +while 1 do + local chunk = io.read(2048) + io.write(chain(chunk)) + if not chunk then break end +end +}}} + +The chaining factory is very simple. All it does is return a function that passes data through all filters and returns the result to the user. It uses the simpler auxiliary function that knows how to chain two filters together. In the auxiliary function, special care must be taken if the chunk is final. This is because the final chunk notification has to be pushed through both filters in turn. Thanks to the chain factory, it is easy to perform the Quoted-Printable conversion, as the above example shows. + +===Sources, sinks, and pumps=== + +As we noted in the introduction, the filters we introduced so far act as the internal nodes in a network of transformations. Information flows from node to node (or rather from one filter to the next) and is transformed on its way out. Chaining filters together is the way we found to connect nodes in the network. But what about the end nodes? In the beginning of the network, we need a node that provides the data, a source. In the end of the network, we need a node that takes in the data, a sink. + +==Sources== + +We start with two simple sources. The first is the {{empty}} source: It simply returns no data, possibly returning an error message. The second is the {{file}} source, which produces the contents of a file in a chunk by chunk fashion, closing the file handle when done. + {{{ +function source.empty(err) + return function() + return nil, err + end +end + +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(2048) + if not chunk then handle:close() end + return chunk + end + else return source.empty(io_err or "unable to open file") end +end +}}} + +A source returns the next chunk of data each time it is called. When there is no more data, it just returns {{nil}}. If there is an error, the source can inform the caller by returning {{nil}} followed by an error message. Adrian Sietsma noticed that, although not on purpose, the interface for sources is compatible with the idea of iterators in Lua 5.0. That is, a data source can be nicely used in conjunction with {{for}} loops. Using our file source as an iterator, we can rewrite our first example: + {{{ +local process = normalize("\r\n") +for chunk in source.file(io.stdin) do + io.write(process(chunk)) +end +io.write(process(nil)) +}}} + +Notice that the last call to the filter obtains the last chunk of processed data. The loop terminates when the source returns {{nil}} and therefore we need that final call outside of the loop. + +==Maintaining state between calls== + +It is often the case that a source needs to change its behavior after some event. One simple example would be a file source that wants to make sure it returns {{nil}} regardless of how many times it is called after the end of file, avoiding attempts to read past the end of the file. Another example would be a source that returns the contents of several files, as if they were concatenated, moving from one file to the next until the end of the last file is reached. + +One way to implement this kind of source is to have the factory declare extra state variables that the source can use via lexical scoping. Our file source could set the file handle itself to {{nil}} when it detects the end-of-file. Then, every time the source is called, it could check if the handle is still valid and act accordingly: + {{{ +function source.file(handle, io_err) + if handle then + return function() + if not handle then return nil end + local chunk = handle:read(2048) + if not chunk then + handle:close() + handle = nil + end + return chunk + end + else return source.empty(io_err or "unable to open file") end +end +}}} + +Another way to implement this behavior involves a change in the source interface to makes it more flexible. Let's allow a source to return a second value, besides the next chunk of data. If the returned chunk is {{nil}}, the extra return value tells us what happened. A second {{nil}} means that there is just no more data and the source is empty. Any other value is considered to be an error message. On the other hand, if the chunk was ''not'' {{nil}}, the second return value tells us whether the source wants to be replaced. If it is {{nil}}, we should proceed using the same source. Otherwise it has to be another source, which we have to use from then on, to get the remaining data. + +This extra freedom is good for someone writing a source function, but it is a pain for those that have to use it. Fortunately, given one of these ''fancy'' sources, we can transform it into a simple source that never needs to be replaced, using the following factory. + {{{ +function source.simplify(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end +}}} + +The simplification factory allows us to write fancy sources and use them as if they were simple. Therefore, our next functions will only produce simple sources, and functions that take sources will assume they are simple. + +Going back to our file source, the extended interface allows for a more elegant implementation. The new source just asks to be replaced by an empty source as soon as there is no more data. There is no repeated checking of the handle. To make things simpler to the user, the factory itself simplifies the the fancy file source before returning it to the user: + {{{ +function source.file(handle, io_err) + if handle then + return source.simplify(function() + local chunk = handle:read(2048) + if not chunk then + handle:close() + return "", source.empty() + end + return chunk + end) + else return source.empty(io_err or "unable to open file") end +end +}}} + +We can make these ideas even more powerful if we use a new feature of Lua 5.0: coroutines. Coroutines suffer from a great lack of advertisement, and I am going to play my part here. Just like lexical scoping, coroutines taste odd at first, but once you get used with the concept, it can save your day. I have to admit that using coroutines to implement our file source would be overkill, so let's implement a concatenated source factory instead. + {{{ +function source.cat(...) + local arg = {...} + local co = coroutine.create(function() + local i = 1 + while i <= #arg do + local chunk, err = arg[i]() + if chunk then coroutine.yield(chunk) + elseif err then return nil, err + else i = i + 1 end + end + end) + return function() + return shift(coroutine.resume(co)) + end +end +}}} + +The factory creates two functions. The first is an auxiliary that does all the work, in the form of a coroutine. It reads a chunk from one of the sources. If the chunk is {{nil}}, it moves to the next source, otherwise it just yields returning the chunk. When it is resumed, it continues from where it stopped and tries to read the next chunk. The second function is the source itself, and just resumes the execution of the auxiliary coroutine, returning to the user whatever chunks it returns (skipping the first result that tells us if the coroutine terminated). Imagine writing the same function without coroutines and you will notice the simplicity of this implementation. We will use coroutines again when we make the filter interface more powerful. + +==Chaining Sources== + +What does it mean to chain a source with a filter? The most useful interpretation is that the combined source-filter is a new source that produces data and passes it through the filter before returning it. Here is a factory that does it: + {{{ +function source.chain(src, f) + return source.simplify(function() + local chunk, err = src() + if not chunk then return f(nil), source.empty(err) + else return f(chunk) end + end) +end +}}} + +Our motivating example in the introduction chains a source with a filter. The idea of chaining a source with a filter is useful when one thinks about functions that might get their input data from a source. By chaining a simple source with one or more filters, the same function can be provided with filtered data even though it is unaware of the filtering that is happening behind its back. + +==Sinks== + +Just as we defined an interface for an initial source of data, we can also define an interface for a final destination of data. We call any function respecting that interface a ''sink''. Below are two simple factories that return sinks. The table factory creates a sink that stores all obtained data into a table. The data can later be efficiently concatenated into a single string with the {{table.concat}} library function. As another example, we introduce the {{null}} sink: A sink that simply discards the data it receives. + {{{ +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +local function null() + return 1 +end + +function sink.null() + return null +end +}}} + +Sinks receive consecutive chunks of data, until the end of data is notified with a {{nil}} chunk. An error is notified by an extra argument giving an error message after the {{nil}} chunk. If a sink detects an error itself and wishes not to be called again, it should return {{nil}}, optionally followed by an error message. A return value that is not {{nil}} means the source will accept more data. Finally, just as sources can choose to be replaced, so can sinks, following the same interface. Once again, it is easy to implement a {{sink.simplify}} factory that transforms a fancy sink into a simple sink. + +As an example, let's create a source that reads from the standard input, then chain it with a filter that normalizes the end-of-line convention and let's use a sink to place all data into a table, printing the result in the end. + {{{ +local load = source.chain(source.file(io.stdin), normalize("\r\n")) +local store, t = sink.table() +while 1 do + local chunk = load() + store(chunk) + if not chunk then break end +end +print(table.concat(t)) +}}} + +Again, just as we created a factory that produces a chained source-filter from a source and a filter, it is easy to create a factory that produces a new sink given a sink and a filter. The new sink passes all data it receives through the filter before handing it in to the original sink. Here is the implementation: + {{{ +function sink.chain(f, snk) + return function(chunk, err) + local r, e = snk(f(chunk)) + if not r then return nil, e end + if not chunk then return snk(nil, err) end + return 1 + end +end +}}} + +==Pumps== + +There is a while loop that has been around for too long in our examples. It's always there because everything that we designed so far is passive. Sources, sinks, filters: None of them will do anything on their own. The operation of pumping all data a source can provide into a sink is so common that we will provide a couple helper functions to do that for us. + {{{ +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 +end + +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 + end +end +}}} + +The {{pump.step}} function moves one chunk of data from the source to the sink. The {{pump.all}} function takes an optional {{step}} function and uses it to pump all the data from the source to the sink. We can now use everything we have to write a program that reads a binary file from disk and stores it in another file, after encoding it to the Base64 transfer content encoding: + {{{ +local load = source.chain( + source.file(io.open("input.bin", "rb")), + encode("base64") +) +local store = sink.chain( + wrap(76), + sink.file(io.open("output.b64", "w")), +) +pump.all(load, store) +}}} + +The way we split the filters here is not intuitive, on purpose. Alternatively, we could have chained the Base64 encode filter and the line-wrap filter together, and then chain the resulting filter with either the file source or the file sink. It doesn't really matter. + +===One last important change=== + +Turns out we still have a problem. When David Burgess was writing his gzip filter, he noticed that the decompression filter can explode a small input chunk into a huge amount of data. Although we wished we could ignore this problem, we soon agreed we couldn't. The only solution is to allow filters to return partial results, and that is what we chose to do. After invoking the filter to pass input data, the user now has to loop invoking the filter to find out if it has more output data to return. Note that these extra calls can't pass more data to the filter. + +More specifically, after passing a chunk of input data to a filter and collecting the first chunk of output data, the user invokes the filter repeatedly, passing the empty string, to get extra output chunks. When the filter itself returns an empty string, the user knows there is no more output data, and can proceed to pass the next input chunk. In the end, after the user passes a {{nil}} notifying the filter that there is no more input data, the filter might still have produced too much output data to return in a single chunk. The user has to loop again, this time passing {{nil}} each time, until the filter itself returns {{nil}} to notify the user it is finally done. + +Most filters won't need this extra freedom. Fortunately, the new filter interface is easy to implement. In fact, the end-of-line translation filter we created in the introduction already conforms to it. On the other hand, the chaining function becomes much more complicated. If it wasn't for coroutines, I wouldn't be happy to implement it. Let me know if you can find a simpler implementation that does not use coroutines! + {{{ +local function chain2(f1, f2) + local co = coroutine.create(function(chunk) + while true do + local filtered1 = f1(chunk) + local filtered2 = f2(filtered1) + local done2 = filtered1 and "" + while true do + if filtered2 == "" or filtered2 == nil then break end + coroutine.yield(filtered2) + filtered2 = f2(done2) + end + if filtered1 == "" then chunk = coroutine.yield(filtered1) + elseif filtered1 == nil then return nil + else chunk = chunk and "" end + end + end) + return function(chunk) + local _, res = coroutine.resume(co, chunk) + return res + end +end +}}} + +Chaining sources also becomes more complicated, but a similar solution is possible with coroutines. Chaining sinks is just as simple as it has always been. Interestingly, these modifications do not have a measurable negative impact in the the performance of filters that didn't need the added flexibility. They do severely improve the efficiency of filters like the gzip filter, though, and that is why we are keeping them. + +===Final considerations=== + +These ideas were created during the development of {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket] 2.0, and are available as the LTN12 module. As a result, {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket] implementation was greatly simplified and became much more powerful. The MIME module is especially integrated to LTN12 and provides many other filters. We felt these concepts deserved to be made public even to those that don't care about {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket], hence the LTN. + +One extra application that deserves mentioning makes use of an identity filter. Suppose you want to provide some feedback to the user while a file is being downloaded into a sink. Chaining the sink with an identity filter (a filter that simply returns the received data unaltered), you can update a progress counter on the fly. The original sink doesn't have to be modified. Another interesting idea is that of a T sink: A sink that sends data to two other sinks. In summary, there appears to be enough room for many other interesting ideas. + +In this technical note we introduced filters, sources, sinks, and pumps. These are useful tools for data processing in general. Sources provide a simple abstraction for data acquisition. Sinks provide an abstraction for final data destinations. Filters define an interface for data transformations. The chaining of filters, sources and sinks provides an elegant way to create arbitrarily complex data transformation from simpler transformations. Pumps just put the machinery to work. diff --git a/contrib/luasocket/ltn013.wiki b/contrib/luasocket/ltn013.wiki new file mode 100644 index 00000000..a6224240 --- /dev/null +++ b/contrib/luasocket/ltn013.wiki @@ -0,0 +1,194 @@ +===Using finalized exceptions=== +==or How to get rid of all those if statements== +by DiegoNehab + +{{{ + +}}} + +===Abstract=== +This little LTN describes a simple exception scheme that greatly simplifies error checking in Lua programs. All the needed functionality ships standard with Lua, but is hidden between the {{assert}} and {{pcall}} functions. To make it more evident, we stick to a convenient standard (you probably already use anyways) for Lua function return values, and define two very simple helper functions (either in C or in Lua itself). + +===Introduction=== + +Most Lua functions return {{nil}} in case of error, followed by a message describing the error. If you don't use this convention, you probably have good reasons. Hopefully, after reading on, you will realize your reasons are not good enough. + +If you are like me, you hate error checking. Most nice little code snippets that look beautiful when you first write them lose some of their charm when you add all that error checking code. Yet, error checking is as important as the rest of the code. How sad. + +Even if you stick to a return convention, any complex task involving several function calls makes error checking both boring and error-prone (do you see the ''error'' below?) + {{{ +function task(arg1, arg2, ...) + local ret1, err = task1(arg1) + if not ret1 then + cleanup1() + return nil, error + end + local ret2, err = task2(arg2) + if not ret then + cleanup2() + return nil, error + end + ... +end +}}} + +The standard {{assert}} function provides an interesting alternative. To use it, simply nest every function call to be error checked with a call to {{assert}}. The {{assert}} function checks the value of its first argument. If it is {{nil}}, {{assert}} throws the second argument as an error message. Otherwise, {{assert}} lets all arguments through as if had not been there. The idea greatly simplifies error checking: + {{{ +function task(arg1, arg2, ...) + local ret1 = assert(task1(arg1)) + local ret2 = assert(task2(arg2)) + ... +end +}}} + +If any task fails, the execution is aborted by {{assert}} and the error message is displayed to the user as the cause of the problem. If no error happens, the task completes as before. There isn't a single {{if}} statement and this is great. However, there are some problems with the idea. + +First, the topmost {{task}} function doesn't respect the protocol followed by the lower-level tasks: It raises an error instead of returning {{nil}} followed by the error messages. Here is where the standard {{pcall}} comes in handy. + {{{ +function xtask(arg1, arg2, ...) + local ret1 = assert(task1(arg1)) + local ret2 = assert(task2(arg2)) + ... +end + +function task(arg1, arg2, ...) + local ok, ret_or_err = pcall(xtask, arg1, arg2, ...) + if ok then return ret_or_err + else return nil, ret_or_err end +end +}}} + +Our new {{task}} function is well behaved. {{Pcall}} catches any error raised by the calls to {{assert}} and returns it after the status code. That way, errors don't get propagated to the user of the high level {{task}} function. + +These are the main ideas for our exception scheme, but there are still a few glitches to fix: + + * Directly using {{pcall}} ruined the simplicity of the code; + * What happened to the cleanup function calls? What if we have to, say, close a file? + * {{Assert}} messes with the error message before raising the error (it adds line number information). + +Fortunately, all these problems are very easy to solve and that's what we do in the following sections. + +== Introducing the {{protect}} factory == + +We used the {{pcall}} function to shield the user from errors that could be raised by the underlying implementation. Instead of directly using {{pcall}} (and thus duplicating code) every time we prefer a factory that does the same job: + {{{ +local function pack(ok, ...) + return ok, {...} +end + +function protect(f) + return function(...) + local ok, ret = pack(pcall(f, ...)) + if ok then return unpack(ret) + else return nil, ret[1] end + end +end +}}} + +The {{protect}} factory receives a function that might raise exceptions and returns a function that respects our return value convention. Now we can rewrite the top-level {{task}} function in a much cleaner way: + {{{ +task = protect(function(arg1, arg2, ...) + local ret1 = assert(task1(arg1)) + local ret2 = assert(task2(arg2)) + ... +end) +}}} + +The Lua implementation of the {{protect}} factory suffers with the creation of tables to hold multiple arguments and return values. It is possible (and easy) to implement the same function in C, without any table creation. + {{{ +static int safecall(lua_State *L) { + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) { + lua_pushnil(L); + lua_insert(L, 1); + return 2; + } else return lua_gettop(L); +} + +static int protect(lua_State *L) { + lua_pushcclosure(L, safecall, 1); + return 1; +} +}}} + +===The {{newtry}} factory=== + +Let's solve the two remaining issues with a single shot and use a concrete example to illustrate the proposed solution. Suppose you want to write a function to download an HTTP document. You have to connect, send the request and read the reply. Each of these tasks can fail, but if something goes wrong after you connected, you have to close the connection before returning the error message. + {{{ +get = protect(function(host, path) + local c + -- create a try function with a finalizer to close the socket + local try = newtry(function() + if c then c:close() end + end) + -- connect and send request + c = try(connect(host, 80)) + try(c:send("GET " .. path .. " HTTP/1.0\r\n\r\n")) + -- get headers + local h = {} + while 1 do + l = try(c:receive()) + if l == "" then break end + table.insert(h, l) + end + -- get body + local b = try(c:receive("*a")) + c:close() + return b, h +end) +}}} + +The {{newtry}} factory returns a function that works just like {{assert}}. The differences are that the {{try}} function doesn't mess with the error message and it calls an optional ''finalizer'' before raising the error. In our example, the finalizer simply closes the socket. + +Even with a simple example like this, we see that the finalized exceptions simplified our life. Let's see what we gain in general, not just in this example: + + * We don't need to declare dummy variables to hold error messages in case any ever shows up; + * We avoid using a variable to hold something that could either be a return value or an error message; + * We didn't have to use several ''if'' statements to check for errors; + * If an error happens, we know our finalizer is going to be invoked automatically; + * Exceptions get propagated, so we don't repeat these ''if'' statements until the error reaches the user. + +Try writing the same function without the tricks we used above and you will see that the code gets ugly. Longer sequences of operations with error checking would get even uglier. So let's implement the {{newtry}} function in Lua: + {{{ +function newtry(f) + return function(...) + if not arg[1] then + if f then f() end + error(arg[2], 0) + else + return ... + end + end +end +}}} + +Again, the implementation suffers from the creation of tables at each function call, so we prefer the C version: + {{{ +static int finalize(lua_State *L) { + if (!lua_toboolean(L, 1)) { + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pcall(L, 0, 0, 0); + lua_settop(L, 2); + lua_error(L); + return 0; + } else return lua_gettop(L); +} + +static int do_nothing(lua_State *L) { + (void) L; + return 0; +} + +static int newtry(lua_State *L) { + lua_settop(L, 1); + if (lua_isnil(L, 1)) + lua_pushcfunction(L, do_nothing); + lua_pushcclosure(L, finalize, 1); + return 1; +} +}}} + +===Final considerations=== + +The {{protect}} and {{newtry}} functions saved a ''lot'' of work in the implementation of {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket]. The size of some modules was cut in half by the these ideas. It's true the scheme is not as generic as the exception mechanism of programming languages like C++ or Java, but the power/simplicity ratio is favorable and I hope it serves you as well as it served {{LuaSocket}}. diff --git a/contrib/luasocket/luasocket-scm-0.rockspec b/contrib/luasocket/luasocket-scm-0.rockspec new file mode 100644 index 00000000..61bd6451 --- /dev/null +++ b/contrib/luasocket/luasocket-scm-0.rockspec @@ -0,0 +1,105 @@ +package = "LuaSocket" +version = "scm-0" +source = { + url = "https://github.com/diegonehab/luasocket/archive/master.zip", + dir = "luasocket-master", +} +description = { + summary = "Network support for the Lua language", + detailed = [[ + LuaSocket is a Lua extension library that is composed by two parts: a C core + that provides support for the TCP and UDP transport layers, and a set of Lua + modules that add support for functionality commonly needed by applications + that deal with the Internet. + ]], + homepage = "http://luaforge.net/projects/luasocket/", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} + +local function make_plat(plat) + local defines = { + unix = { + "LUASOCKET_DEBUG", + "LUASOCKET_API=__attribute__((visibility(\"default\")))", + "UNIX_API=__attribute__((visibility(\"default\")))", + "MIME_API=__attribute__((visibility(\"default\")))" + }, + macosx = { + "LUASOCKET_DEBUG", + "UNIX_HAS_SUN_LEN", + "LUASOCKET_API=__attribute__((visibility(\"default\")))", + "UNIX_API=__attribute__((visibility(\"default\")))", + "MIME_API=__attribute__((visibility(\"default\")))" + }, + win32 = { + "LUASOCKET_DEBUG", + "NDEBUG", + "LUASOCKET_API=__declspec(dllexport)", + "MIME_API=__declspec(dllexport)" + }, + mingw32 = { + "LUASOCKET_DEBUG", + "LUASOCKET_INET_PTON", + "WINVER=0x0501", + "LUASOCKET_API=__declspec(dllexport)", + "MIME_API=__declspec(dllexport)" + } + } + local modules = { + ["socket.core"] = { + sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, + ["mime.core"] = { + sources = { "src/mime.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, + ["socket.http"] = "src/http.lua", + ["socket.url"] = "src/url.lua", + ["socket.tp"] = "src/tp.lua", + ["socket.ftp"] = "src/ftp.lua", + ["socket.headers"] = "src/headers.lua", + ["socket.smtp"] = "src/smtp.lua", + ltn12 = "src/ltn12.lua", + socket = "src/socket.lua", + mime = "src/mime.lua" + } + if plat == "unix" or plat == "macosx" or plat == "haiku" then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/usocket.c" + if plat == "haiku" then + modules["socket.core"].libraries = {"network"} + end + modules["socket.unix"] = { + sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/unix.c" }, + defines = defines[plat], + incdir = "/src" + } + modules["socket.serial"] = { + sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/serial.c" }, + defines = defines[plat], + incdir = "/src" + } + end + if plat == "win32" or plat == "mingw32" then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/wsocket.c" + modules["socket.core"].libraries = { "ws2_32" } + end + return { modules = modules } +end + +build = { + type = "builtin", + platforms = { + unix = make_plat("unix"), + macosx = make_plat("macosx"), + haiku = make_plat("haiku"), + win32 = make_plat("win32"), + mingw32 = make_plat("mingw32") + }, + copy_directories = { "doc", "samples", "etc", "test" } +} diff --git a/contrib/luasocket/macosx.cmd b/contrib/luasocket/macosx.cmd new file mode 100644 index 00000000..46a07092 --- /dev/null +++ b/contrib/luasocket/macosx.cmd @@ -0,0 +1 @@ +make DEBUG=DEBUG PLAT=macosx LUAINC_macosx_base=/Users/diego/build/macosx/include LUAPREFIX_macosx=/Users/diego/build/macosx install-both diff --git a/contrib/luasocket/makefile.dist b/contrib/luasocket/makefile.dist new file mode 100644 index 00000000..45a8866c --- /dev/null +++ b/contrib/luasocket/makefile.dist @@ -0,0 +1,139 @@ +#-------------------------------------------------------------------------- +# Distribution makefile +#-------------------------------------------------------------------------- +DIST = luasocket-3.0-rc1 + +TEST = \ + test/README \ + test/hello.lua \ + test/testclnt.lua \ + test/testsrvr.lua \ + test/testsupport.lua + +SAMPLES = \ + samples/README \ + samples/cddb.lua \ + samples/daytimeclnt.lua \ + samples/echoclnt.lua \ + samples/echosrvr.lua \ + samples/mclisten.lua \ + samples/mcsend.lua \ + samples/listener.lua \ + samples/lpr.lua \ + samples/talker.lua \ + samples/tinyirc.lua + +ETC = \ + etc/README \ + etc/b64.lua \ + etc/check-links.lua \ + etc/check-memory.lua \ + etc/dict.lua \ + etc/dispatch.lua \ + etc/eol.lua \ + etc/forward.lua \ + etc/get.lua \ + etc/lp.lua \ + etc/qp.lua \ + etc/tftp.lua + +SRC = \ + src/makefile \ + src/auxiliar.c \ + src/auxiliar.h \ + src/buffer.c \ + src/buffer.h \ + src/except.c \ + src/except.h \ + src/inet.c \ + src/inet.h \ + src/io.c \ + src/io.h \ + src/luasocket.c \ + src/luasocket.h \ + src/mime.c \ + src/mime.h \ + src/options.c \ + src/options.h \ + src/select.c \ + src/select.h \ + src/socket.h \ + src/tcp.c \ + src/tcp.h \ + src/timeout.c \ + src/timeout.h \ + src/udp.c \ + src/udp.h \ + src/unix.c \ + src/serial.c \ + src/unix.h \ + src/usocket.c \ + src/usocket.h \ + src/wsocket.c \ + src/wsocket.h \ + src/ftp.lua \ + src/http.lua \ + src/ltn12.lua \ + src/mime.lua \ + src/smtp.lua \ + src/socket.lua \ + src/headers.lua \ + src/tp.lua \ + src/url.lua + +MAKE = \ + makefile \ + luasocket.sln \ + luasocket-scm-0.rockspec \ + Lua51.props \ + Lua52.props \ + socket.vcxproj.filters \ + mime.vcxproj.filters \ + socket.vcxproj \ + mime.vcxproj + +DOC = \ + doc/dns.html \ + doc/ftp.html \ + doc/index.html \ + doc/http.html \ + doc/installation.html \ + doc/introduction.html \ + doc/ltn12.html \ + doc/luasocket.png \ + doc/mime.html \ + doc/reference.css \ + doc/reference.html \ + doc/smtp.html \ + doc/socket.html \ + doc/tcp.html \ + doc/udp.html \ + doc/url.html + +dist: + mkdir -p $(DIST) + cp -vf NEW $(DIST) + cp -vf LICENSE $(DIST) + cp -vf README $(DIST) + cp -vf $(MAKE) $(DIST) + + mkdir -p $(DIST)/etc + cp -vf $(ETC) $(DIST)/etc + + mkdir -p $(DIST)/src + cp -vf $(SRC) $(DIST)/src + + mkdir -p $(DIST)/doc + cp -vf $(DOC) $(DIST)/doc + + mkdir -p $(DIST)/samples + cp -vf $(SAMPLES) $(DIST)/samples + + mkdir -p $(DIST)/test + cp -vf $(TEST) $(DIST)/test + + tar -zcvf $(DIST).tar.gz $(DIST) + zip -r $(DIST).zip $(DIST) + +clean: + \rm -rf $(DIST) $(DIST).tar.gz $(DIST).zip diff --git a/contrib/luasocket/mingw.cmd b/contrib/luasocket/mingw.cmd new file mode 100644 index 00000000..bf2b7ed2 --- /dev/null +++ b/contrib/luasocket/mingw.cmd @@ -0,0 +1 @@ +make PLAT=mingw LUAINC_mingw_base=/home/diego/build/mingw/include LUALIB_mingw_base=/home/diego/build/mingw/bin LUAPREFIX_mingw=/home/diego/build/mingw/bin DEBUG=DEBUG install-both diff --git a/contrib/luasocket/premake5.lua b/contrib/luasocket/premake5.lua new file mode 100644 index 00000000..deb37374 --- /dev/null +++ b/contrib/luasocket/premake5.lua @@ -0,0 +1,30 @@ +project "luasocket-lib" + language "C" + kind "StaticLib" + warnings "extra" + pic "on" + + includedirs { "../lua/src" } + + files + { + "src/*.c", + "src/*.h", + "src/*.lua" + } + + filter "system:windows" + removefiles + { + "src/serial.c", + "src/unixdgram.*", + "src/unixstream.*", + "src/unix.*", + "src/usocket.*", + } + + filter "system: not windows" + removefiles + { + "src/wsocket.*", + } \ No newline at end of file diff --git a/contrib/luasocket/rockspec/luasocket-3.0rc2-1.rockspec b/contrib/luasocket/rockspec/luasocket-3.0rc2-1.rockspec new file mode 100644 index 00000000..8e37a4a3 --- /dev/null +++ b/contrib/luasocket/rockspec/luasocket-3.0rc2-1.rockspec @@ -0,0 +1,105 @@ +package = "LuaSocket" +version = "3.0rc2-1" +source = { + url = "git://github.com/diegonehab/luasocket.git", + tag = "v3.0-rc2", +} +description = { + summary = "Network support for the Lua language", + detailed = [[ + LuaSocket is a Lua extension library that is composed by two parts: a C core + that provides support for the TCP and UDP transport layers, and a set of Lua + modules that add support for functionality commonly needed by applications + that deal with the Internet. + ]], + homepage = "http://luaforge.net/projects/luasocket/", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} + +local function make_plat(plat) + local defines = { + unix = { + "LUASOCKET_DEBUG", + "LUASOCKET_API=__attribute__((visibility(\"default\")))", + "UNIX_API=__attribute__((visibility(\"default\")))", + "MIME_API=__attribute__((visibility(\"default\")))" + }, + macosx = { + "LUASOCKET_DEBUG", + "UNIX_HAS_SUN_LEN", + "LUASOCKET_API=__attribute__((visibility(\"default\")))", + "UNIX_API=__attribute__((visibility(\"default\")))", + "MIME_API=__attribute__((visibility(\"default\")))" + }, + win32 = { + "LUASOCKET_DEBUG", + "NDEBUG", + "LUASOCKET_API=__declspec(dllexport)", + "MIME_API=__declspec(dllexport)" + }, + mingw32 = { + "LUASOCKET_DEBUG", + "LUASOCKET_INET_PTON", + "WINVER=0x0501", + "LUASOCKET_API=__declspec(dllexport)", + "MIME_API=__declspec(dllexport)" + } + } + local modules = { + ["socket.core"] = { + sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, + ["mime.core"] = { + sources = { "src/mime.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, + ["socket.http"] = "src/http.lua", + ["socket.url"] = "src/url.lua", + ["socket.tp"] = "src/tp.lua", + ["socket.ftp"] = "src/ftp.lua", + ["socket.headers"] = "src/headers.lua", + ["socket.smtp"] = "src/smtp.lua", + ltn12 = "src/ltn12.lua", + socket = "src/socket.lua", + mime = "src/mime.lua" + } + if plat == "unix" or plat == "macosx" or plat == "haiku" then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/usocket.c" + if plat == "haiku" then + modules["socket.core"].libraries = {"network"} + end + modules["socket.unix"] = { + sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/unix.c" }, + defines = defines[plat], + incdir = "/src" + } + modules["socket.serial"] = { + sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/serial.c" }, + defines = defines[plat], + incdir = "/src" + } + end + if plat == "win32" or plat == "mingw32" then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/wsocket.c" + modules["socket.core"].libraries = { "ws2_32" } + end + return { modules = modules } +end + +build = { + type = "builtin", + platforms = { + unix = make_plat("unix"), + macosx = make_plat("macosx"), + haiku = make_plat("haiku"), + win32 = make_plat("win32"), + mingw32 = make_plat("mingw32") + }, + copy_directories = { "doc", "samples", "etc", "test" } +} diff --git a/contrib/luasocket/samples/README b/contrib/luasocket/samples/README new file mode 100644 index 00000000..e63a6f52 --- /dev/null +++ b/contrib/luasocket/samples/README @@ -0,0 +1,50 @@ +This directory contains some sample programs using +LuaSocket. This code is not supported. + + listener.lua -- socket to stdout + talker.lua -- stdin to socket + +listener.lua and talker.lua are about the simplest +applications you can write using LuaSocket. Run + + 'lua listener.lua' and 'lua talker.lua' + +on different terminals. Whatever you type on talk.lua will +be printed by listen.lua. + + lpr.lua -- lpr client + +This is a cool program written by David Burgess to print +files using the Line Printer Daemon protocol, widely used in +Unix machines. It uses the lp.lua implementation, in the +etc directory. Just run 'lua lpr.lua +queue=' and the file will print! + + cddb.lua -- CDDB client + +This is the first try on a simple CDDB client. Not really +useful, but one day it might become a module. + + daytimeclnt.lua -- day time client + +Just run the program to retrieve the hour and date in +readable form from any server running an UDP daytime daemon. + + echoclnt.lua -- UDP echo client + echosrvr.lua -- UDP echo server + +These are a UDP echo client/server pair. They work with +other client and servers as well. + + tinyirc.lua -- irc like broadcast server + +This is a simple server that waits simultaneously on two +server sockets for telnet connections. Everything it +receives from the telnet clients is broadcasted to every +other connected client. It tests the select function and +shows how to create a simple server whith LuaSocket. Just +run tinyirc.lua and then open as many telnet connections +as you want to ports 8080 and 8081. + +Good luck, +Diego. diff --git a/contrib/luasocket/samples/cddb.lua b/contrib/luasocket/samples/cddb.lua new file mode 100644 index 00000000..49a18711 --- /dev/null +++ b/contrib/luasocket/samples/cddb.lua @@ -0,0 +1,46 @@ +local socket = require("socket") +local http = require("socket.http") + +if not arg or not arg[1] or not arg[2] then + print("luasocket cddb.lua []") + os.exit(1) +end + +local server = arg[3] or "http://freedb.freedb.org/~cddb/cddb.cgi" + +function parse(body) + local lines = string.gfind(body, "(.-)\r\n") + local status = lines() + local code, message = socket.skip(2, string.find(status, "(%d%d%d) (.*)")) + if tonumber(code) ~= 210 then + return nil, code, message + end + local data = {} + for l in lines do + local c = string.sub(l, 1, 1) + if c ~= '#' and c ~= '.' then + local key, value = socket.skip(2, string.find(l, "(.-)=(.*)")) + value = string.gsub(value, "\\n", "\n") + value = string.gsub(value, "\\\\", "\\") + value = string.gsub(value, "\\t", "\t") + data[key] = value + end + end + return data, code, message +end + +local host = socket.dns.gethostname() +local query = "%s?cmd=cddb+read+%s+%s&hello=LuaSocket+%s+LuaSocket+2.0&proto=6" +local url = string.format(query, server, arg[1], arg[2], host) +local body, headers, code = http.request(url) + +if code == 200 then + local data, code, error = parse(body) + if not data then + print(error or code) + else + for i,v in pairs(data) do + io.write(i, ': ', v, '\n') + end + end +else print(error) end diff --git a/contrib/luasocket/samples/daytimeclnt.lua b/contrib/luasocket/samples/daytimeclnt.lua new file mode 100644 index 00000000..f81e37c4 --- /dev/null +++ b/contrib/luasocket/samples/daytimeclnt.lua @@ -0,0 +1,22 @@ +----------------------------------------------------------------------------- +-- UDP sample: daytime protocol client +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require"socket" +host = host or "127.0.0.1" +port = port or 13 +if arg then + host = arg[1] or host + port = arg[2] or port +end +host = socket.dns.toip(host) +udp = socket.udp() +print("Using host '" ..host.. "' and port " ..port.. "...") +udp:setpeername(host, port) +udp:settimeout(3) +sent, err = udp:send("anything") +if err then print(err) os.exit() end +dgram, err = udp:receive() +if not dgram then print(err) os.exit() end +io.write(dgram) diff --git a/contrib/luasocket/samples/echoclnt.lua b/contrib/luasocket/samples/echoclnt.lua new file mode 100644 index 00000000..bb22557a --- /dev/null +++ b/contrib/luasocket/samples/echoclnt.lua @@ -0,0 +1,23 @@ +----------------------------------------------------------------------------- +-- UDP sample: echo protocol client +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "localhost" +port = port or 7 +if arg then + host = arg[1] or host + port = arg[2] or port +end +host = socket.dns.toip(host) +udp = assert(socket.udp()) +assert(udp:setpeername(host, port)) +print("Using remote host '" ..host.. "' and port " .. port .. "...") +while 1 do + line = io.read() + if not line or line == "" then os.exit() end + assert(udp:send(line)) + dgram = assert(udp:receive()) + print(dgram) +end diff --git a/contrib/luasocket/samples/echosrvr.lua b/contrib/luasocket/samples/echosrvr.lua new file mode 100644 index 00000000..ea564e24 --- /dev/null +++ b/contrib/luasocket/samples/echosrvr.lua @@ -0,0 +1,28 @@ +----------------------------------------------------------------------------- +-- UDP sample: echo protocol server +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "127.0.0.1" +port = port or 7 +if arg then + host = arg[1] or host + port = arg[2] or port +end +print("Binding to host '" ..host.. "' and port " ..port.. "...") +udp = assert(socket.udp()) +assert(udp:setsockname(host, port)) +assert(udp:settimeout(5)) +ip, port = udp:getsockname() +assert(ip, port) +print("Waiting packets on " .. ip .. ":" .. port .. "...") +while 1 do + dgram, ip, port = udp:receivefrom() + if dgram then + print("Echoing '" .. dgram .. "' to " .. ip .. ":" .. port) + udp:sendto(dgram, ip, port) + else + print(ip) + end +end diff --git a/contrib/luasocket/samples/listener.lua b/contrib/luasocket/samples/listener.lua new file mode 100644 index 00000000..77db2d53 --- /dev/null +++ b/contrib/luasocket/samples/listener.lua @@ -0,0 +1,25 @@ +----------------------------------------------------------------------------- +-- TCP sample: Little program to dump lines received at a given port +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "*" +port = port or 8080 +if arg then + host = arg[1] or host + port = arg[2] or port +end +print("Binding to host '" ..host.. "' and port " ..port.. "...") +s = assert(socket.bind(host, port)) +i, p = s:getsockname() +assert(i, p) +print("Waiting connection from talker on " .. i .. ":" .. p .. "...") +c = assert(s:accept()) +print("Connected. Here is the stuff:") +l, e = c:receive() +while not e do + print(l) + l, e = c:receive() +end +print(e) diff --git a/contrib/luasocket/samples/lpr.lua b/contrib/luasocket/samples/lpr.lua new file mode 100644 index 00000000..49a1dfa9 --- /dev/null +++ b/contrib/luasocket/samples/lpr.lua @@ -0,0 +1,51 @@ +local lp = require("socket.lp") + +local function usage() + print('\nUsage: lua lpr.lua [filename] [keyword=val...]\n') + print('Valid keywords are :') + print( + ' host=remote host or IP address (default "localhost")\n' .. + ' queue=remote queue or printer name (default "printer")\n' .. + ' port=remote port number (default 515)\n' .. + ' user=sending user name\n' .. + ' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' .. + ' banner=true|false\n' .. + ' indent=number of columns to indent\n' .. + ' mail=email of address to notify when print is complete\n' .. + ' title=title to use for "pr" format\n' .. + ' width=width for "text" or "pr" formats\n' .. + ' class=\n' .. + ' job=\n' .. + ' name=\n' .. + ' localbind=true|false\n' + ) + return nil +end + +if not arg or not arg[1] then + return usage() +end + +do + local opt = {} + local pat = "[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]?.?)" + for i = 2, #arg, 1 do + string.gsub(arg[i], pat, function(name, value) opt[name] = value end) + end + if not arg[2] then + return usage() + end + if arg[1] ~= "query" then + opt.file = arg[1] + r,e=lp.send(opt) + io.stdout:write(tostring(r or e),'\n') + else + r,e=lp.query(opt) + io.stdout:write(tostring(r or e), '\n') + end +end + +-- trivial tests +--lua lp.lua lp.lua queue=default host=localhost +--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1 +--lua lp.lua query queue=default host=localhost diff --git a/contrib/luasocket/samples/mclisten.lua b/contrib/luasocket/samples/mclisten.lua new file mode 100644 index 00000000..d40d7893 --- /dev/null +++ b/contrib/luasocket/samples/mclisten.lua @@ -0,0 +1,18 @@ +local socket = require"socket" +local group = "225.0.0.37" +local port = 12345 +local c = assert(socket.udp()) +print(assert(c:setoption("reuseport", true))) +print(assert(c:setsockname("*", port))) +--print("loop:", c:getoption("ip-multicast-loop")) +--print(assert(c:setoption("ip-multicast-loop", false))) +--print("loop:", c:getoption("ip-multicast-loop")) +--print("if:", c:getoption("ip-multicast-if")) +--print(assert(c:setoption("ip-multicast-if", "127.0.0.1"))) +--print("if:", c:getoption("ip-multicast-if")) +--print(assert(c:setoption("ip-multicast-if", "10.0.1.4"))) +--print("if:", c:getoption("ip-multicast-if")) +print(assert(c:setoption("ip-add-membership", {multiaddr = group, interface = "*"}))) +while 1 do + print(c:receivefrom()) +end diff --git a/contrib/luasocket/samples/mcsend.lua b/contrib/luasocket/samples/mcsend.lua new file mode 100644 index 00000000..7c24cdf2 --- /dev/null +++ b/contrib/luasocket/samples/mcsend.lua @@ -0,0 +1,20 @@ +local socket = require"socket" +local group = "225.0.0.37" +local port = 12345 +local c = assert(socket.udp()) +--print(assert(c:setoption("reuseport", true))) +--print(assert(c:setsockname("*", port))) +--print(assert(c:setoption("ip-multicast-loop", false))) +--print(assert(c:setoption("ip-multicast-ttl", 4))) +--print(assert(c:setoption("ip-multicast-if", "10.0.1.3"))) +--print(assert(c:setoption("ip-add-membership", {multiaddr = group, interface = "*"}))) +local i = 0 +while 1 do + local message = string.format("hello all %d!", i) + assert(c:sendto(message, group, port)) + print("sent " .. message) + socket.sleep(1) + c:settimeout(0.5) + print(c:receivefrom()) + i = i + 1 +end diff --git a/contrib/luasocket/samples/talker.lua b/contrib/luasocket/samples/talker.lua new file mode 100644 index 00000000..172bf890 --- /dev/null +++ b/contrib/luasocket/samples/talker.lua @@ -0,0 +1,20 @@ +----------------------------------------------------------------------------- +-- TCP sample: Little program to send text lines to a given host/port +-- LuaSocket sample files +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "localhost" +port = port or 8080 +if arg then + host = arg[1] or host + port = arg[2] or port +end +print("Attempting connection to host '" ..host.. "' and port " ..port.. "...") +c = assert(socket.connect(host, port)) +print("Connected! Please type stuff (empty line to stop):") +l = io.read() +while l and l ~= "" and not e do + assert(c:send(l .. "\n")) + l = io.read() +end diff --git a/contrib/luasocket/samples/tinyirc.lua b/contrib/luasocket/samples/tinyirc.lua new file mode 100644 index 00000000..5babb7e1 --- /dev/null +++ b/contrib/luasocket/samples/tinyirc.lua @@ -0,0 +1,89 @@ +----------------------------------------------------------------------------- +-- Select sample: simple text line server +-- LuaSocket sample files. +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "*" +port1 = port1 or 8080 +port2 = port2 or 8181 +if arg then + host = arg[1] or host + port1 = arg[2] or port1 + port2 = arg[3] or port2 +end + +server1 = assert(socket.bind(host, port1)) +server2 = assert(socket.bind(host, port2)) +server1:settimeout(1) -- make sure we don't block in accept +server2:settimeout(1) + +io.write("Servers bound\n") + +-- simple set implementation +-- the select function doesn't care about what is passed to it as long as +-- it behaves like a table +-- creates a new set data structure +function newset() + local reverse = {} + local set = {} + return setmetatable(set, {__index = { + insert = function(set, value) + if not reverse[value] then + table.insert(set, value) + reverse[value] = #set + end + end, + remove = function(set, value) + local index = reverse[value] + if index then + reverse[value] = nil + local top = table.remove(set) + if top ~= value then + reverse[top] = index + set[index] = top + end + end + end + }}) +end + +set = newset() + +io.write("Inserting servers in set\n") +set:insert(server1) +set:insert(server2) + +while 1 do + local readable, _, error = socket.select(set, nil) + for _, input in ipairs(readable) do + -- is it a server socket? + if input == server1 or input == server2 then + io.write("Waiting for clients\n") + local new = input:accept() + if new then + new:settimeout(1) + io.write("Inserting client in set\n") + set:insert(new) + end + -- it is a client socket + else + local line, error = input:receive() + if error then + input:close() + io.write("Removing client from set\n") + set:remove(input) + else + io.write("Broadcasting line '", line, "'\n") + writable, error = socket.skip(1, socket.select(nil, set, 1)) + if not error then + for __, output in ipairs(writable) do + if output ~= input then + output:send(line .. "\n") + end + end + else io.write("No client ready to receive!!!\n") end + end + end + end +end diff --git a/contrib/luasocket/src/auxiliar.c b/contrib/luasocket/src/auxiliar.c new file mode 100644 index 00000000..18fa8e4c --- /dev/null +++ b/contrib/luasocket/src/auxiliar.c @@ -0,0 +1,158 @@ +/*=========================================================================*\ +* Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include + +#include "auxiliar.h" + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes the module +\*-------------------------------------------------------------------------*/ +int auxiliar_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Creates a new class with given methods +* Methods whose names start with __ are passed directly to the metatable. +\*-------------------------------------------------------------------------*/ +void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func) { + luaL_newmetatable(L, classname); /* mt */ + /* create __index table to place methods */ + lua_pushstring(L, "__index"); /* mt,"__index" */ + lua_newtable(L); /* mt,"__index",it */ + /* put class name into class metatable */ + lua_pushstring(L, "class"); /* mt,"__index",it,"class" */ + lua_pushstring(L, classname); /* mt,"__index",it,"class",classname */ + lua_rawset(L, -3); /* mt,"__index",it */ + /* pass all methods that start with _ to the metatable, and all others + * to the index table */ + for (; func->name; func++) { /* mt,"__index",it */ + lua_pushstring(L, func->name); + lua_pushcfunction(L, func->func); + lua_rawset(L, func->name[0] == '_' ? -5: -3); + } + lua_rawset(L, -3); /* mt */ + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Prints the value of a class in a nice way +\*-------------------------------------------------------------------------*/ +int auxiliar_tostring(lua_State *L) { + char buf[32]; + if (!lua_getmetatable(L, 1)) goto error; + lua_pushstring(L, "__index"); + lua_gettable(L, -2); + if (!lua_istable(L, -1)) goto error; + lua_pushstring(L, "class"); + lua_gettable(L, -2); + if (!lua_isstring(L, -1)) goto error; + sprintf(buf, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf); + return 1; +error: + lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'"); + lua_error(L); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Insert class into group +\*-------------------------------------------------------------------------*/ +void auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) { + luaL_getmetatable(L, classname); + lua_pushstring(L, groupname); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Make sure argument is a boolean +\*-------------------------------------------------------------------------*/ +int auxiliar_checkboolean(lua_State *L, int objidx) { + if (!lua_isboolean(L, objidx)) + auxiliar_typeerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); + return lua_toboolean(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given class, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) { + void *data = auxiliar_getclassudata(L, classname, objidx); + if (!data) { + char msg[45]; + sprintf(msg, "%.35s expected", classname); + luaL_argerror(L, objidx, msg); + } + return data; +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given group, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) { + void *data = auxiliar_getgroupudata(L, groupname, objidx); + if (!data) { + char msg[45]; + sprintf(msg, "%.35s expected", groupname); + luaL_argerror(L, objidx, msg); + } + return data; +} + +/*-------------------------------------------------------------------------*\ +* Set object class +\*-------------------------------------------------------------------------*/ +void auxiliar_setclass(lua_State *L, const char *classname, int objidx) { + luaL_getmetatable(L, classname); + if (objidx < 0) objidx--; + lua_setmetatable(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Get a userdata pointer if object belongs to a given group. Return NULL +* otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) { + if (!lua_getmetatable(L, objidx)) + return NULL; + lua_pushstring(L, groupname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + return NULL; + } else { + lua_pop(L, 2); + return lua_touserdata(L, objidx); + } +} + +/*-------------------------------------------------------------------------*\ +* Get a userdata pointer if object belongs to a given class. Return NULL +* otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) { + return luaL_checkudata(L, objidx, classname); +} + +/*-------------------------------------------------------------------------*\ +* Throws error when argument does not have correct type. +* Used to be part of lauxlib in Lua 5.1, was dropped from 5.2. +\*-------------------------------------------------------------------------*/ +int auxiliar_typeerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, + luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + diff --git a/contrib/luasocket/src/auxiliar.h b/contrib/luasocket/src/auxiliar.h new file mode 100644 index 00000000..65511d4d --- /dev/null +++ b/contrib/luasocket/src/auxiliar.h @@ -0,0 +1,48 @@ +#ifndef AUXILIAR_H +#define AUXILIAR_H +/*=========================================================================*\ +* Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit (but completely independent of other LuaSocket modules) +* +* A LuaSocket class is a name associated with Lua metatables. A LuaSocket +* group is a name associated with a class. A class can belong to any number +* of groups. This module provides the functionality to: +* +* - create new classes +* - add classes to groups +* - set the class of objects +* - check if an object belongs to a given class or group +* - get the userdata associated to objects +* - print objects in a pretty way +* +* LuaSocket class names follow the convention {}. Modules +* can define any number of classes and groups. The module tcp.c, for +* example, defines the classes tcp{master}, tcp{client} and tcp{server} and +* the groups tcp{client,server} and tcp{any}. Module functions can then +* perform type-checking on their arguments by either class or group. +* +* LuaSocket metatables define the __index metamethod as being a table. This +* table has one field for each method supported by the class, and a field +* "class" with the class name. +* +* The mapping from class name to the corresponding metatable and the +* reverse mapping are done using lauxlib. +\*=========================================================================*/ + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +int auxiliar_open(lua_State *L); +void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func); +void auxiliar_add2group(lua_State *L, const char *classname, const char *group); +void auxiliar_setclass(lua_State *L, const char *classname, int objidx); +void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx); +void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx); +void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx); +void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx); +int auxiliar_checkboolean(lua_State *L, int objidx); +int auxiliar_tostring(lua_State *L); +int auxiliar_typeerror(lua_State *L, int narg, const char *tname); + +#endif /* AUXILIAR_H */ diff --git a/contrib/luasocket/src/buffer.c b/contrib/luasocket/src/buffer.c new file mode 100644 index 00000000..fff16346 --- /dev/null +++ b/contrib/luasocket/src/buffer.c @@ -0,0 +1,273 @@ +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "buffer.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b); +static int recvline(p_buffer buf, luaL_Buffer *b); +static int recvall(p_buffer buf, luaL_Buffer *b); +static int buffer_get(p_buffer buf, const char **data, size_t *count); +static void buffer_skip(p_buffer buf, size_t count); +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int buffer_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void buffer_init(p_buffer buf, p_io io, p_timeout tm) { + buf->first = buf->last = 0; + buf->io = io; + buf->tm = tm; + buf->received = buf->sent = 0; + buf->birthday = timeout_gettime(); +} + +/*-------------------------------------------------------------------------*\ +* object:getstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_getstats(lua_State *L, p_buffer buf) { + lua_pushnumber(L, (lua_Number) buf->received); + lua_pushnumber(L, (lua_Number) buf->sent); + lua_pushnumber(L, timeout_gettime() - buf->birthday); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* object:setstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_setstats(lua_State *L, p_buffer buf) { + buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); + buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); + if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* object:send() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_send(lua_State *L, p_buffer buf) { + int top = lua_gettop(L); + int err = IO_DONE; + size_t size = 0, sent = 0; + const char *data = luaL_checklstring(L, 2, &size); + long start = (long) luaL_optnumber(L, 3, 1); + long end = (long) luaL_optnumber(L, 4, -1); + timeout_markstart(buf->tm); + if (start < 0) start = (long) (size+start+1); + if (end < 0) end = (long) (size+end+1); + if (start < 1) start = (long) 1; + if (end > (long) size) end = (long) size; + if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); + /* check if there was an error */ + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushnumber(L, (lua_Number) (sent+start-1)); + } else { + lua_pushnumber(L, (lua_Number) (sent+start-1)); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* object:receive() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_receive(lua_State *L, p_buffer buf) { + int err = IO_DONE, top = lua_gettop(L); + luaL_Buffer b; + size_t size; + const char *part = luaL_optlstring(L, 3, "", &size); + timeout_markstart(buf->tm); + /* initialize buffer with optional extra prefix + * (useful for concatenating previous partial results) */ + luaL_buffinit(L, &b); + luaL_addlstring(&b, part, size); + /* receive new patterns */ + if (!lua_isnumber(L, 2)) { + const char *p= luaL_optstring(L, 2, "*l"); + if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); + else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); + else luaL_argcheck(L, 0, 2, "invalid receive pattern"); + /* get a fixed number of bytes (minus what was already partially + * received) */ + } else { + double n = lua_tonumber(L, 2); + size_t wanted = (size_t) n; + luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); + if (size == 0 || wanted > size) + err = recvraw(buf, wanted-size, &b); + } + /* check if there was an error */ + if (err != IO_DONE) { + /* we can't push anyting in the stack before pushing the + * contents of the buffer. this is the reason for the complication */ + luaL_pushresult(&b); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushvalue(L, -2); + lua_pushnil(L); + lua_replace(L, -4); + } else { + luaL_pushresult(&b); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* Determines if there is any data in the read buffer +\*-------------------------------------------------------------------------*/ +int buffer_isempty(p_buffer buf) { + return buf->first >= buf->last; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Sends a block of data (unbuffered) +\*-------------------------------------------------------------------------*/ +#define STEPSIZE 8192 +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { + p_io io = buf->io; + p_timeout tm = buf->tm; + size_t total = 0; + int err = IO_DONE; + while (total < count && err == IO_DONE) { + size_t done = 0; + size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; + err = io->send(io->ctx, data+total, step, &done, tm); + total += done; + } + *sent = total; + buf->sent += total; + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a fixed number of bytes (buffered) +\*-------------------------------------------------------------------------*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + size_t count; const char *data; + err = buffer_get(buf, &data, &count); + count = MIN(count, wanted - total); + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + total += count; + if (total >= wanted) break; + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads everything until the connection is closed (buffered) +\*-------------------------------------------------------------------------*/ +static int recvall(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + const char *data; size_t count; + err = buffer_get(buf, &data, &count); + total += count; + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + } + if (err == IO_CLOSED) { + if (total > 0) return IO_DONE; + else return IO_CLOSED; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF +* are not returned by the function and are discarded from the buffer +\*-------------------------------------------------------------------------*/ +static int recvline(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + while (err == IO_DONE) { + size_t count, pos; const char *data; + err = buffer_get(buf, &data, &count); + pos = 0; + while (pos < count && data[pos] != '\n') { + /* we ignore all \r's */ + if (data[pos] != '\r') luaL_addchar(b, data[pos]); + pos++; + } + if (pos < count) { /* found '\n' */ + buffer_skip(buf, pos+1); /* skip '\n' too */ + break; /* we are done */ + } else /* reached the end of the buffer */ + buffer_skip(buf, pos); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Skips a given number of bytes from read buffer. No data is read from the +* transport layer +\*-------------------------------------------------------------------------*/ +static void buffer_skip(p_buffer buf, size_t count) { + buf->received += count; + buf->first += count; + if (buffer_isempty(buf)) + buf->first = buf->last = 0; +} + +/*-------------------------------------------------------------------------*\ +* Return any data available in buffer, or get more data from transport layer +* if buffer is empty +\*-------------------------------------------------------------------------*/ +static int buffer_get(p_buffer buf, const char **data, size_t *count) { + int err = IO_DONE; + p_io io = buf->io; + p_timeout tm = buf->tm; + if (buffer_isempty(buf)) { + size_t got; + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); + buf->first = 0; + buf->last = got; + } + *count = buf->last - buf->first; + *data = buf->data + buf->first; + return err; +} diff --git a/contrib/luasocket/src/buffer.h b/contrib/luasocket/src/buffer.h new file mode 100644 index 00000000..1281bb39 --- /dev/null +++ b/contrib/luasocket/src/buffer.h @@ -0,0 +1,45 @@ +#ifndef BUF_H +#define BUF_H +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +* +* Line patterns require buffering. Reading one character at a time involves +* too many system calls and is very slow. This module implements the +* LuaSocket interface for input/output on connected objects, as seen by +* Lua programs. +* +* Input is buffered. Output is *not* buffered because there was no simple +* way of making sure the buffered output data would ever be sent. +* +* The module is built on top of the I/O abstraction defined in io.h and the +* timeout management is done with the timeout.h interface. +\*=========================================================================*/ +#include "lua.h" + +#include "io.h" +#include "timeout.h" + +/* buffer size in bytes */ +#define BUF_SIZE 8192 + +/* buffer control structure */ +typedef struct t_buffer_ { + double birthday; /* throttle support info: creation time, */ + size_t sent, received; /* bytes sent, and bytes received */ + p_io io; /* IO driver used for this buffer */ + p_timeout tm; /* timeout management for this buffer */ + size_t first, last; /* index of first and last bytes of stored data */ + char data[BUF_SIZE]; /* storage space for buffer data */ +} t_buffer; +typedef t_buffer *p_buffer; + +int buffer_open(lua_State *L); +void buffer_init(p_buffer buf, p_io io, p_timeout tm); +int buffer_meth_send(lua_State *L, p_buffer buf); +int buffer_meth_receive(lua_State *L, p_buffer buf); +int buffer_meth_getstats(lua_State *L, p_buffer buf); +int buffer_meth_setstats(lua_State *L, p_buffer buf); +int buffer_isempty(p_buffer buf); + +#endif /* BUF_H */ diff --git a/contrib/luasocket/src/compat.c b/contrib/luasocket/src/compat.c new file mode 100644 index 00000000..c2d99cb2 --- /dev/null +++ b/contrib/luasocket/src/compat.c @@ -0,0 +1,19 @@ +#include "compat.h" + +#if LUA_VERSION_NUM==501 +/* +** Adapted from Lua 5.2 +*/ +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup+1, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + lua_pushstring(L, l->name); + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -(nup+1)); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); + } + lua_pop(L, nup); /* remove upvalues */ +} +#endif diff --git a/contrib/luasocket/src/compat.h b/contrib/luasocket/src/compat.h new file mode 100644 index 00000000..7bf8010e --- /dev/null +++ b/contrib/luasocket/src/compat.h @@ -0,0 +1,11 @@ +#ifndef COMPAT_H +#define COMPAT_H + +#include "lua.h" +#include "lauxlib.h" + +#if LUA_VERSION_NUM==501 +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); +#endif + +#endif diff --git a/contrib/luasocket/src/except.c b/contrib/luasocket/src/except.c new file mode 100644 index 00000000..60b50058 --- /dev/null +++ b/contrib/luasocket/src/except.c @@ -0,0 +1,133 @@ +/*=========================================================================*\ +* Simple exception support +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "except.h" + +#if LUA_VERSION_NUM < 502 +#define lua_pcallk(L, na, nr, err, ctx, cont) \ + (((void)ctx),((void)cont),lua_pcall(L, na, nr, err)) +#endif + +#if LUA_VERSION_NUM < 503 +typedef int lua_KContext; +#endif + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int global_protect(lua_State *L); +static int global_newtry(lua_State *L); +static int protected_(lua_State *L); +static int finalize(lua_State *L); +static int do_nothing(lua_State *L); + +/* except functions */ +static luaL_Reg func[] = { + {"newtry", global_newtry}, + {"protect", global_protect}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Try factory +\*-------------------------------------------------------------------------*/ +static void wrap(lua_State *L) { + lua_createtable(L, 1, 0); + lua_pushvalue(L, -2); + lua_rawseti(L, -2, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); +} + +static int finalize(lua_State *L) { + if (!lua_toboolean(L, 1)) { + lua_pushvalue(L, lua_upvalueindex(2)); + lua_call(L, 0, 0); + lua_settop(L, 2); + wrap(L); + lua_error(L); + return 0; + } else return lua_gettop(L); +} + +static int do_nothing(lua_State *L) { + (void) L; + return 0; +} + +static int global_newtry(lua_State *L) { + lua_settop(L, 1); + if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, -2); + lua_pushcclosure(L, finalize, 2); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Protect factory +\*-------------------------------------------------------------------------*/ +static int unwrap(lua_State *L) { + if (lua_istable(L, -1) && lua_getmetatable(L, -1)) { + int r = lua_rawequal(L, -1, lua_upvalueindex(1)); + lua_pop(L, 1); + if (r) { + lua_pushnil(L); + lua_rawgeti(L, -2, 1); + return 1; + } + } + return 0; +} + +static int protected_finish(lua_State *L, int status, lua_KContext ctx) { + (void)ctx; + if (status != 0 && status != LUA_YIELD) { + if (unwrap(L)) return 2; + else return lua_error(L); + } else return lua_gettop(L); +} + +#if LUA_VERSION_NUM == 502 +static int protected_cont(lua_State *L) { + int ctx = 0; + int status = lua_getctx(L, &ctx); + return protected_finish(L, status, ctx); +} +#else +#define protected_cont protected_finish +#endif + +static int protected_(lua_State *L) { + int status; + lua_pushvalue(L, lua_upvalueindex(2)); + lua_insert(L, 1); + status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont); + return protected_finish(L, status, 0); +} + +static int global_protect(lua_State *L) { + lua_settop(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + lua_pushcclosure(L, protected_, 2); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Init module +\*-------------------------------------------------------------------------*/ +int except_open(lua_State *L) { + lua_newtable(L); /* metatable for wrapped exceptions */ + lua_pushboolean(L, 0); + lua_setfield(L, -2, "__metatable"); + luaL_setfuncs(L, func, 1); + return 0; +} diff --git a/contrib/luasocket/src/except.h b/contrib/luasocket/src/except.h new file mode 100644 index 00000000..2497c056 --- /dev/null +++ b/contrib/luasocket/src/except.h @@ -0,0 +1,38 @@ +#ifndef EXCEPT_H +#define EXCEPT_H +/*=========================================================================*\ +* Exception control +* LuaSocket toolkit (but completely independent from other modules) +* +* This provides support for simple exceptions in Lua. During the +* development of the HTTP/FTP/SMTP support, it became aparent that +* error checking was taking a substantial amount of the coding. These +* function greatly simplify the task of checking errors. +* +* The main idea is that functions should return nil as their first return +* values when they find an error, and return an error message (or value) +* following nil. In case of success, as long as the first value is not nil, +* the other values don't matter. +* +* The idea is to nest function calls with the "try" function. This function +* checks the first value, and, if it's falsy, wraps the second value in a +* table with metatable and calls "error" on it. Otherwise, it returns all +* values it received. Basically, it works like the Lua "assert" function, +* but it creates errors targeted specifically at "protect". +* +* The "newtry" function is a factory for "try" functions that call a +* finalizer in protected mode before calling "error". +* +* The "protect" function returns a new function that behaves exactly like +* the function it receives, but the new function catches exceptions thrown +* by "try" functions and returns nil followed by the error message instead. +* +* With these three functions, it's easy to write functions that throw +* exceptions on error, but that don't interrupt the user script. +\*=========================================================================*/ + +#include "lua.h" + +int except_open(lua_State *L); + +#endif diff --git a/contrib/luasocket/src/ftp.lua b/contrib/luasocket/src/ftp.lua new file mode 100644 index 00000000..bd528caa --- /dev/null +++ b/contrib/luasocket/src/ftp.lua @@ -0,0 +1,329 @@ +----------------------------------------------------------------------------- +-- FTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local math = require("math") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +socket.ftp = {} +local _M = socket.ftp +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout in seconds before the program gives up on a connection +_M.TIMEOUT = 60 +-- default port for ftp service +local PORT = 21 +-- this is the default anonymous password. used when no password is +-- provided in url. should be changed to your e-mail. +_M.USER = "ftp" +_M.PASSWORD = "anonymous@anonymous.org" + +----------------------------------------------------------------------------- +-- Low level FTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function _M.open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create)) + local f = base.setmetatable({ tp = tp }, metat) + -- make sure everything gets closed in an exception + f.try = socket.newtry(function() f:close() end) + return f +end + +function metat.__index:portconnect() + self.try(self.server:settimeout(_M.TIMEOUT)) + self.data = self.try(self.server:accept()) + self.try(self.data:settimeout(_M.TIMEOUT)) +end + +function metat.__index:pasvconnect() + self.data = self.try(socket.tcp()) + self.try(self.data:settimeout(_M.TIMEOUT)) + self.try(self.data:connect(self.pasvt.address, self.pasvt.port)) +end + +function metat.__index:login(user, password) + self.try(self.tp:command("user", user or _M.USER)) + local code, reply = self.try(self.tp:check{"2..", 331}) + if code == 331 then + self.try(self.tp:command("pass", password or _M.PASSWORD)) + self.try(self.tp:check("2..")) + end + return 1 +end + +function metat.__index:pasv() + self.try(self.tp:command("pasv")) + local code, reply = self.try(self.tp:check("2..")) + local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" + local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) + self.try(a and b and c and d and p1 and p2, reply) + self.pasvt = { + address = string.format("%d.%d.%d.%d", a, b, c, d), + port = p1*256 + p2 + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + +function metat.__index:epsv() + self.try(self.tp:command("epsv")) + local code, reply = self.try(self.tp:check("229")) + local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" + local d, prt, address, port = string.match(reply, pattern) + self.try(port, "invalid epsv response") + self.pasvt = { + address = self.tp:getpeername(), + port = port + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + + +function metat.__index:port(address, port) + self.pasvt = nil + if not address then + address, port = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local pl = math.mod(port, 256) + local ph = (port - pl)/256 + local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",") + self.try(self.tp:command("port", arg)) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:eprt(family, address, port) + self.pasvt = nil + if not address then + address, port = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local arg = string.format("|%s|%s|%d|", family, address, port) + self.try(self.tp:command("eprt", arg)) + self.try(self.tp:check("2..")) + return 1 +end + + +function metat.__index:send(sendt) + self.try(self.pasvt or self.server, "need port or pasv first") + -- if there is a pasvt table, we already sent a PASV command + -- we just get the data connection into self.data + if self.pasvt then self:pasvconnect() end + -- get the transfer argument and command + local argument = sendt.argument or + url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = sendt.command or "stor" + -- send the transfer command and check the reply + self.try(self.tp:command(command, argument)) + local code, reply = self.try(self.tp:check{"2..", "1.."}) + -- if there is not a pasvt table, then there is a server + -- and we already sent a PORT command + if not self.pasvt then self:portconnect() end + -- get the sink, source and step for the transfer + local step = sendt.step or ltn12.pump.step + local readt = { self.tp } + local checkstep = function(src, snk) + -- check status in control connection while downloading + local readyt = socket.select(readt, nil, 0) + if readyt[tp] then code = self.try(self.tp:check("2..")) end + return step(src, snk) + end + local sink = socket.sink("close-when-done", self.data) + -- transfer all data and check error + self.try(ltn12.pump.all(sendt.source, sink, checkstep)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + -- done with data connection + self.data:close() + -- find out how many bytes were sent + local sent = socket.skip(1, self.data:getstats()) + self.data = nil + return sent +end + +function metat.__index:receive(recvt) + self.try(self.pasvt or self.server, "need port or pasv first") + if self.pasvt then self:pasvconnect() end + local argument = recvt.argument or + url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = recvt.command or "retr" + self.try(self.tp:command(command, argument)) + local code,reply = self.try(self.tp:check{"1..", "2.."}) + if (code >= 200) and (code <= 299) then + recvt.sink(reply) + return 1 + end + if not self.pasvt then self:portconnect() end + local source = socket.source("until-closed", self.data) + local step = recvt.step or ltn12.pump.step + self.try(ltn12.pump.all(source, recvt.sink, step)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + self.data:close() + self.data = nil + return 1 +end + +function metat.__index:cwd(dir) + self.try(self.tp:command("cwd", dir)) + self.try(self.tp:check(250)) + return 1 +end + +function metat.__index:type(type) + self.try(self.tp:command("type", type)) + self.try(self.tp:check(200)) + return 1 +end + +function metat.__index:greet() + local code = self.try(self.tp:check{"1..", "2.."}) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + return 1 +end + +function metat.__index:quit() + self.try(self.tp:command("quit")) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:close() + if self.data then self.data:close() end + if self.server then self.server:close() end + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High level FTP API +----------------------------------------------------------------------------- +local function override(t) + if t.url then + local u = url.parse(t.url) + for i,v in base.pairs(t) do + u[i] = v + end + return u + else return t end +end + +local function tput(putt) + putt = override(putt) + socket.try(putt.host, "missing hostname") + local f = _M.open(putt.host, putt.port, putt.create) + f:greet() + f:login(putt.user, putt.password) + if putt.type then f:type(putt.type) end + f:epsv() + local sent = f:send(putt) + f:quit() + f:close() + return sent +end + +local default = { + path = "/", + scheme = "ftp" +} + +local function genericform(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") + socket.try(t.host, "missing hostname") + local pat = "^type=(.)$" + if t.params then + t.type = socket.skip(2, string.find(t.params, pat)) + socket.try(t.type == "a" or t.type == "i", + "invalid type '" .. t.type .. "'") + end + return t +end + +_M.genericform = genericform + +local function sput(u, body) + local putt = genericform(u) + putt.source = ltn12.source.string(body) + return tput(putt) +end + +_M.put = socket.protect(function(putt, body) + if base.type(putt) == "string" then return sput(putt, body) + else return tput(putt) end +end) + +local function tget(gett) + gett = override(gett) + socket.try(gett.host, "missing hostname") + local f = _M.open(gett.host, gett.port, gett.create) + f:greet() + f:login(gett.user, gett.password) + if gett.type then f:type(gett.type) end + f:epsv() + f:receive(gett) + f:quit() + return f:close() +end + +local function sget(u) + local gett = genericform(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +_M.command = socket.protect(function(cmdt) + cmdt = override(cmdt) + socket.try(cmdt.host, "missing hostname") + socket.try(cmdt.command, "missing command") + local f = _M.open(cmdt.host, cmdt.port, cmdt.create) + f:greet() + f:login(cmdt.user, cmdt.password) + if type(cmdt.command) == "table" then + local argument = cmdt.argument or {} + local check = cmdt.check or {} + for i,cmd in ipairs(cmdt.command) do + f.try(f.tp:command(cmd, argument[i])) + if check[i] then f.try(f.tp:check(check[i])) end + end + else + f.try(f.tp:command(cmdt.command, cmdt.argument)) + if cmdt.check then f.try(f.tp:check(cmdt.check)) end + end + f:quit() + return f:close() +end) + +_M.get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + +return _M diff --git a/contrib/luasocket/src/headers.lua b/contrib/luasocket/src/headers.lua new file mode 100644 index 00000000..1eb8223b --- /dev/null +++ b/contrib/luasocket/src/headers.lua @@ -0,0 +1,104 @@ +----------------------------------------------------------------------------- +-- Canonic header field capitalization +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +socket.headers = {} +local _M = socket.headers + +_M.canonic = { + ["accept"] = "Accept", + ["accept-charset"] = "Accept-Charset", + ["accept-encoding"] = "Accept-Encoding", + ["accept-language"] = "Accept-Language", + ["accept-ranges"] = "Accept-Ranges", + ["action"] = "Action", + ["alternate-recipient"] = "Alternate-Recipient", + ["age"] = "Age", + ["allow"] = "Allow", + ["arrival-date"] = "Arrival-Date", + ["authorization"] = "Authorization", + ["bcc"] = "Bcc", + ["cache-control"] = "Cache-Control", + ["cc"] = "Cc", + ["comments"] = "Comments", + ["connection"] = "Connection", + ["content-description"] = "Content-Description", + ["content-disposition"] = "Content-Disposition", + ["content-encoding"] = "Content-Encoding", + ["content-id"] = "Content-ID", + ["content-language"] = "Content-Language", + ["content-length"] = "Content-Length", + ["content-location"] = "Content-Location", + ["content-md5"] = "Content-MD5", + ["content-range"] = "Content-Range", + ["content-transfer-encoding"] = "Content-Transfer-Encoding", + ["content-type"] = "Content-Type", + ["cookie"] = "Cookie", + ["date"] = "Date", + ["diagnostic-code"] = "Diagnostic-Code", + ["dsn-gateway"] = "DSN-Gateway", + ["etag"] = "ETag", + ["expect"] = "Expect", + ["expires"] = "Expires", + ["final-log-id"] = "Final-Log-ID", + ["final-recipient"] = "Final-Recipient", + ["from"] = "From", + ["host"] = "Host", + ["if-match"] = "If-Match", + ["if-modified-since"] = "If-Modified-Since", + ["if-none-match"] = "If-None-Match", + ["if-range"] = "If-Range", + ["if-unmodified-since"] = "If-Unmodified-Since", + ["in-reply-to"] = "In-Reply-To", + ["keywords"] = "Keywords", + ["last-attempt-date"] = "Last-Attempt-Date", + ["last-modified"] = "Last-Modified", + ["location"] = "Location", + ["max-forwards"] = "Max-Forwards", + ["message-id"] = "Message-ID", + ["mime-version"] = "MIME-Version", + ["original-envelope-id"] = "Original-Envelope-ID", + ["original-recipient"] = "Original-Recipient", + ["pragma"] = "Pragma", + ["proxy-authenticate"] = "Proxy-Authenticate", + ["proxy-authorization"] = "Proxy-Authorization", + ["range"] = "Range", + ["received"] = "Received", + ["received-from-mta"] = "Received-From-MTA", + ["references"] = "References", + ["referer"] = "Referer", + ["remote-mta"] = "Remote-MTA", + ["reply-to"] = "Reply-To", + ["reporting-mta"] = "Reporting-MTA", + ["resent-bcc"] = "Resent-Bcc", + ["resent-cc"] = "Resent-Cc", + ["resent-date"] = "Resent-Date", + ["resent-from"] = "Resent-From", + ["resent-message-id"] = "Resent-Message-ID", + ["resent-reply-to"] = "Resent-Reply-To", + ["resent-sender"] = "Resent-Sender", + ["resent-to"] = "Resent-To", + ["retry-after"] = "Retry-After", + ["return-path"] = "Return-Path", + ["sender"] = "Sender", + ["server"] = "Server", + ["smtp-remote-recipient"] = "SMTP-Remote-Recipient", + ["status"] = "Status", + ["subject"] = "Subject", + ["te"] = "TE", + ["to"] = "To", + ["trailer"] = "Trailer", + ["transfer-encoding"] = "Transfer-Encoding", + ["upgrade"] = "Upgrade", + ["user-agent"] = "User-Agent", + ["vary"] = "Vary", + ["via"] = "Via", + ["warning"] = "Warning", + ["will-retry-until"] = "Will-Retry-Until", + ["www-authenticate"] = "WWW-Authenticate", + ["x-mailer"] = "X-Mailer", +} + +return _M \ No newline at end of file diff --git a/contrib/luasocket/src/http.lua b/contrib/luasocket/src/http.lua new file mode 100644 index 00000000..a386165e --- /dev/null +++ b/contrib/luasocket/src/http.lua @@ -0,0 +1,382 @@ +----------------------------------------------------------------------------- +-- HTTP/1.1 client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +------------------------------------------------------------------------------- +local socket = require("socket") +local url = require("socket.url") +local ltn12 = require("ltn12") +local mime = require("mime") +local string = require("string") +local headers = require("socket.headers") +local base = _G +local table = require("table") +socket.http = {} +local _M = socket.http + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- connection timeout in seconds +_M.TIMEOUT = 60 +-- user agent field sent in request +_M.USERAGENT = socket._VERSION + +-- supported schemes +local SCHEMES = { ["http"] = true } +-- default port for document retrieval +local PORT = 80 + +----------------------------------------------------------------------------- +-- Reads MIME headers from a connection, unfolding where needed +----------------------------------------------------------------------------- +local function receiveheaders(sock, headers) + local line, name, value, err + headers = headers or {} + -- get first line + line, err = sock:receive() + if err then return nil, err end + -- headers go until a blank line is found + while line ~= "" do + -- get field-name and value + name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) + if not (name and value) then return nil, "malformed reponse headers" end + name = string.lower(name) + -- get next line (value might be folded) + line, err = sock:receive() + if err then return nil, err end + -- unfold any folded values + while string.find(line, "^%s") do + value = value .. line + line = sock:receive() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +----------------------------------------------------------------------------- +-- Extra sources and sinks +----------------------------------------------------------------------------- +socket.sourcet["http-chunked"] = function(sock, headers) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + -- get chunk size, skip extention + local line, err = sock:receive() + if err then return nil, err end + local size = base.tonumber(string.gsub(line, ";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, part = sock:receive(size) + if chunk then sock:receive() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = receiveheaders(sock, headers) + if not headers then return nil, err end + end + end + }) +end + +socket.sinkt["http-chunked"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then return sock:send("0\r\n\r\n") end + local size = string.format("%X\r\n", string.len(chunk)) + return sock:send(size .. chunk .. "\r\n") + end + }) +end + +----------------------------------------------------------------------------- +-- Low level HTTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function _M.open(host, port, create) + -- create socket with user connect function, or with default + local c = socket.try((create or socket.tcp)()) + local h = base.setmetatable({ c = c }, metat) + -- create finalized try + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(_M.TIMEOUT)) + h.try(c:connect(host, port or PORT)) + -- here everything worked + return h +end + +function metat.__index:sendrequestline(method, uri) + local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) + return self.try(self.c:send(reqline)) +end + +function metat.__index:sendheaders(tosend) + local canonic = headers.canonic + local h = "\r\n" + for f, v in base.pairs(tosend) do + h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h + end + self.try(self.c:send(h)) + return 1 +end + +function metat.__index:sendbody(headers, source, step) + source = source or ltn12.source.empty() + step = step or ltn12.pump.step + -- if we don't know the size in advance, send chunked and hope for the best + local mode = "http-chunked" + if headers["content-length"] then mode = "keep-open" end + return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) +end + +function metat.__index:receivestatusline() + local status = self.try(self.c:receive(5)) + -- identify HTTP/0.9 responses, which do not contain a status line + -- this is just a heuristic, but is what the RFC recommends + if status ~= "HTTP/" then return nil, status end + -- otherwise proceed reading a status line + status = self.try(self.c:receive("*l", status)) + local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) + return self.try(base.tonumber(code), status) +end + +function metat.__index:receiveheaders() + return self.try(receiveheaders(self.c)) +end + +function metat.__index:receivebody(headers, sink, step) + sink = sink or ltn12.sink.null() + step = step or ltn12.pump.step + local length = base.tonumber(headers["content-length"]) + local t = headers["transfer-encoding"] -- shortcut + local mode = "default" -- connection close + if t and t ~= "identity" then mode = "http-chunked" + elseif base.tonumber(headers["content-length"]) then mode = "by-length" end + return self.try(ltn12.pump.all(socket.source(mode, self.c, length), + sink, step)) +end + +function metat.__index:receive09body(status, sink, step) + local source = ltn12.source.rewind(socket.source("until-closed", self.c)) + source(status) + return self.try(ltn12.pump.all(source, sink, step)) +end + +function metat.__index:close() + return self.c:close() +end + +----------------------------------------------------------------------------- +-- High level HTTP API +----------------------------------------------------------------------------- +local function adjusturi(reqt) + local u = reqt + -- if there is a proxy, we need the full url. otherwise, just a part. + if not reqt.proxy and not _M.PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, + query = reqt.query, + fragment = reqt.fragment + } + end + return url.build(u) +end + +local function adjustproxy(reqt) + local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 + else + return reqt.host, reqt.port + end +end + +local function adjustheaders(reqt) + -- default headers + local host = string.gsub(reqt.authority, "^.-@", "") + local lower = { + ["user-agent"] = _M.USERAGENT, + ["host"] = host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then + lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. + url.unescape(reqt.password))) + end + -- if we have proxy authentication information, pass it along + local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + if proxy.user and proxy.password then + lower["proxy-authorization"] = + "Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password)) + end + end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +-- default url parts +local default = { + host = "", + port = PORT, + path ="/", + scheme = "http" +} + +local function adjustrequest(reqt) + -- parse url if provided + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end + if nreqt.port == "" then nreqt.port = PORT end + if not (nreqt.host and nreqt.host ~= "") then + socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") + end + -- compute uri if user hasn't overriden + nreqt.uri = reqt.uri or adjusturi(nreqt) + -- adjust headers in request + nreqt.headers = adjustheaders(nreqt) + -- ajust host and port if there is a proxy + nreqt.host, nreqt.port = adjustproxy(nreqt) + return nreqt +end + +local function shouldredirect(reqt, code, headers) + local location = headers.location + if not location then return false end + location = string.gsub(location, "%s", "") + if location == "" then return false end + local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") + if scheme and not SCHEMES[scheme] then return false end + return (reqt.redirect ~= false) and + (code == 301 or code == 302 or code == 303 or code == 307) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and (not reqt.nredirects or reqt.nredirects < 5) +end + +local function shouldreceivebody(reqt, code) + if reqt.method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return 1 +end + +-- forward declarations +local trequest, tredirect + +--[[local]] 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.url, location), + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, + proxy = reqt.proxy, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create + } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location + return result, code, headers, status +end + +--[[local]] function trequest(reqt) + -- we loop until we get what we want, or + -- until we are sure there is no way to get it + local nreqt = adjustrequest(reqt) + local h = _M.open(nreqt.host, nreqt.port, nreqt.create) + -- send request line and headers + h:sendrequestline(nreqt.method, nreqt.uri) + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done + if not code then + h:receive09body(status, nreqt.sink, nreqt.step) + return 1, 200 + end + local headers + -- ignore any 100-continue messages + while code == 100 do + headers = h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server + -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) + end + -- here we are finally done + if shouldreceivebody(nreqt, code) then + h:receivebody(headers, nreqt.sink, nreqt.step) + end + h:close() + return 1, code, headers, status +end + +-- turns an url and a body into a generic request +local function genericform(u, b) + local t = {} + local reqt = { + url = u, + sink = ltn12.sink.table(t), + target = t + } + if b then + reqt.source = ltn12.source.string(b) + reqt.headers = { + ["content-length"] = string.len(b), + ["content-type"] = "application/x-www-form-urlencoded" + } + reqt.method = "POST" + end + return reqt +end + +_M.genericform = genericform + +local function srequest(u, b) + local reqt = genericform(u, b) + local _, code, headers, status = trequest(reqt) + return table.concat(reqt.target), code, headers, status +end + +_M.request = socket.protect(function(reqt, body) + if base.type(reqt) == "string" then return srequest(reqt, body) + else return trequest(reqt) end +end) + +return _M diff --git a/contrib/luasocket/src/inet.c b/contrib/luasocket/src/inet.c new file mode 100644 index 00000000..f4c84044 --- /dev/null +++ b/contrib/luasocket/src/inet.c @@ -0,0 +1,543 @@ +/*=========================================================================*\ +* Internet domain functions +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "inet.h" + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int inet_global_toip(lua_State *L); +static int inet_global_getaddrinfo(lua_State *L); +static int inet_global_tohostname(lua_State *L); +static int inet_global_getnameinfo(lua_State *L); +static void inet_pushresolved(lua_State *L, struct hostent *hp); +static int inet_global_gethostname(lua_State *L); + +/* DNS functions */ +static luaL_Reg func[] = { + { "toip", inet_global_toip}, + { "getaddrinfo", inet_global_getaddrinfo}, + { "tohostname", inet_global_tohostname}, + { "getnameinfo", inet_global_getnameinfo}, + { "gethostname", inet_global_gethostname}, + { NULL, NULL} +}; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int inet_open(lua_State *L) +{ + lua_pushstring(L, "dns"); + lua_newtable(L); + luaL_setfuncs(L, func, 0); + lua_settable(L, -3); + return 0; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_gethost(const char *address, struct hostent **hp) { + struct in_addr addr; + if (inet_aton(address, &addr)) + return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp); + else + return socket_gethostbyname(address, hp); +} + +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_global_tohostname(lua_State *L) { + const char *address = luaL_checkstring(L, 1); + struct hostent *hp = NULL; + int err = inet_gethost(address, &hp); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_hoststrerror(err)); + return 2; + } + lua_pushstring(L, hp->h_name); + inet_pushresolved(L, hp); + return 2; +} + +static int inet_global_getnameinfo(lua_State *L) { + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + int i, ret; + struct addrinfo hints; + struct addrinfo *resolved, *iter; + const char *host = luaL_optstring(L, 1, NULL); + const char *serv = luaL_optstring(L, 2, NULL); + + if (!(host || serv)) + luaL_error(L, "host and serv cannot be both nil"); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + + ret = getaddrinfo(host, serv, &hints, &resolved); + if (ret != 0) { + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; + } + + lua_newtable(L); + for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) { + getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, + hbuf, host? (socklen_t) sizeof(hbuf): 0, + sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0); + if (host) { + lua_pushnumber(L, i); + lua_pushstring(L, hbuf); + lua_settable(L, -3); + } + } + freeaddrinfo(resolved); + + if (serv) { + lua_pushstring(L, sbuf); + return 2; + } else { + return 1; + } +} + +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_global_toip(lua_State *L) +{ + const char *address = luaL_checkstring(L, 1); + struct hostent *hp = NULL; + int err = inet_gethost(address, &hp); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_hoststrerror(err)); + return 2; + } + lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr))); + inet_pushresolved(L, hp); + return 2; +} + +int inet_optfamily(lua_State* L, int narg, const char* def) +{ + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; + + return optvalue[luaL_checkoption(L, narg, def, optname)]; +} + +int inet_optsocktype(lua_State* L, int narg, const char* def) +{ + static const char* optname[] = { "stream", "dgram", NULL }; + static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 }; + + return optvalue[luaL_checkoption(L, narg, def, optname)]; +} + +static int inet_global_getaddrinfo(lua_State *L) +{ + const char *hostname = luaL_checkstring(L, 1); + struct addrinfo *iterator = NULL, *resolved = NULL; + struct addrinfo hints; + int i = 1, ret = 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + ret = getaddrinfo(hostname, NULL, &hints, &resolved); + if (ret != 0) { + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; + } + lua_newtable(L); + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + char hbuf[NI_MAXHOST]; + ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, + hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (ret){ + freeaddrinfo(resolved); + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; + } + lua_pushnumber(L, i); + lua_newtable(L); + switch (iterator->ai_family) { + case AF_INET: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "inet"); + lua_settable(L, -3); + break; + case AF_INET6: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "inet6"); + lua_settable(L, -3); + break; + case AF_UNSPEC: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unspec"); + lua_settable(L, -3); + break; + default: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unknown"); + lua_settable(L, -3); + break; + } + lua_pushliteral(L, "addr"); + lua_pushstring(L, hbuf); + lua_settable(L, -3); + lua_settable(L, -3); + i++; + } + freeaddrinfo(resolved); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Gets the host name +\*-------------------------------------------------------------------------*/ +static int inet_global_gethostname(lua_State *L) +{ + char name[257]; + name[256] = '\0'; + if (gethostname(name, 256) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } else { + lua_pushstring(L, name); + return 1; + } +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Retrieves socket peer name +\*-------------------------------------------------------------------------*/ +int inet_meth_getpeername(lua_State *L, p_socket ps, int family) +{ + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); + char name[INET6_ADDRSTRLEN]; + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + err = getnameinfo((struct sockaddr *) &peer, peer_len, + name, INET6_ADDRSTRLEN, + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + return 2; + } + lua_pushstring(L, name); + lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } + return 3; +} + +/*-------------------------------------------------------------------------*\ +* Retrieves socket local name +\*-------------------------------------------------------------------------*/ +int inet_meth_getsockname(lua_State *L, p_socket ps, int family) +{ + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); + char name[INET6_ADDRSTRLEN]; + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + return 2; + } + lua_pushstring(L, name); + lua_pushstring(L, port); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } + return 3; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Passes all resolver information to Lua as a table +\*-------------------------------------------------------------------------*/ +static void inet_pushresolved(lua_State *L, struct hostent *hp) +{ + char **alias; + struct in_addr **addr; + int i, resolved; + lua_newtable(L); resolved = lua_gettop(L); + lua_pushstring(L, "name"); + lua_pushstring(L, hp->h_name); + lua_settable(L, resolved); + lua_pushstring(L, "ip"); + lua_pushstring(L, "alias"); + i = 1; + alias = hp->h_aliases; + lua_newtable(L); + if (alias) { + while (*alias) { + lua_pushnumber(L, i); + lua_pushstring(L, *alias); + lua_settable(L, -3); + i++; alias++; + } + } + lua_settable(L, resolved); + i = 1; + lua_newtable(L); + addr = (struct in_addr **) hp->h_addr_list; + if (addr) { + while (*addr) { + lua_pushnumber(L, i); + lua_pushstring(L, inet_ntoa(**addr)); + lua_settable(L, -3); + i++; addr++; + } + } + lua_settable(L, resolved); +} + +/*-------------------------------------------------------------------------*\ +* Tries to create a new inet socket +\*-------------------------------------------------------------------------*/ +const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { + const char *err = socket_strerror(socket_create(ps, family, type, protocol)); + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* "Disconnects" a DGRAM socket +\*-------------------------------------------------------------------------*/ +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) +{ + switch (family) { + case AF_INET: { + struct sockaddr_in sin; + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_UNSPEC; + sin.sin_addr.s_addr = INADDR_ANY; + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; + sin6.sin6_addr = addrany; + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } + } + return NULL; +} + +/*-------------------------------------------------------------------------*\ +* Tries to connect to remote address (address, port) +\*-------------------------------------------------------------------------*/ +const char *inet_tryconnect(p_socket ps, int *family, const char *address, + const char *serv, p_timeout tm, struct addrinfo *connecthints) +{ + struct addrinfo *iterator = NULL, *resolved = NULL; + const char *err = NULL; + int current_family = *family; + /* try resolving */ + err = socket_gaistrerror(getaddrinfo(address, serv, + connecthints, &resolved)); + if (err != NULL) { + if (resolved) freeaddrinfo(resolved); + return err; + } + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + timeout_markstart(tm); + /* create new socket if necessary. if there was no + * bind, we need to create one for every new family + * that shows up while iterating. if there was a + * bind, all families will be the same and we will + * not enter this branch. */ + if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { + socket_destroy(ps); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); + if (err) continue; + current_family = iterator->ai_family; + /* set non-blocking before connect */ + socket_setnonblocking(ps); + } + /* try connecting to remote address */ + err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, + (socklen_t) iterator->ai_addrlen, tm)); + /* if success or timeout is zero, break out of loop */ + if (err == NULL || timeout_iszero(tm)) { + *family = current_family; + break; + } + } + freeaddrinfo(resolved); + /* here, if err is set, we failed */ + return err; +} + +/*-------------------------------------------------------------------------*\ +* Tries to accept a socket +\*-------------------------------------------------------------------------*/ +const char *inet_tryaccept(p_socket server, int family, p_socket client, + p_timeout tm) { + socklen_t len; + t_sockaddr_storage addr; + switch (family) { + case AF_INET6: len = sizeof(struct sockaddr_in6); break; + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } + return socket_strerror(socket_accept(server, client, (SA *) &addr, + &len, tm)); +} + +/*-------------------------------------------------------------------------*\ +* Tries to bind socket to (address, port) +\*-------------------------------------------------------------------------*/ +const char *inet_trybind(p_socket ps, int *family, const char *address, + const char *serv, struct addrinfo *bindhints) { + struct addrinfo *iterator = NULL, *resolved = NULL; + const char *err = NULL; + int current_family = *family; + /* translate luasocket special values to C */ + if (strcmp(address, "*") == 0) address = NULL; + if (!serv) serv = "0"; + /* try resolving */ + err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved)); + if (err) { + if (resolved) freeaddrinfo(resolved); + return err; + } + /* iterate over resolved addresses until one is good */ + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { + socket_destroy(ps); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); + if (err) continue; + current_family = iterator->ai_family; + } + /* try binding to local address */ + err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr, + (socklen_t) iterator->ai_addrlen)); + /* keep trying unless bind succeeded */ + if (err == NULL) { + *family = current_family; + /* set to non-blocking after bind */ + socket_setnonblocking(ps); + break; + } + } + /* cleanup and return error */ + freeaddrinfo(resolved); + /* here, if err is set, we failed */ + return err; +} + +/*-------------------------------------------------------------------------*\ +* Some systems do not provide these so that we provide our own. +\*-------------------------------------------------------------------------*/ +#ifdef LUASOCKET_INET_ATON +int inet_aton(const char *cp, struct in_addr *inp) +{ + unsigned int a = 0, b = 0, c = 0, d = 0; + int n = 0, r; + unsigned long int addr = 0; + r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n); + if (r == 0 || n == 0) return 0; + cp += n; + if (*cp) return 0; + if (a > 255 || b > 255 || c > 255 || d > 255) return 0; + if (inp) { + addr += a; addr <<= 8; + addr += b; addr <<= 8; + addr += c; addr <<= 8; + addr += d; + inp->s_addr = htonl(addr); + } + return 1; +} +#endif + +#ifdef LUASOCKET_INET_PTON +int inet_pton(int af, const char *src, void *dst) +{ + struct addrinfo hints, *res; + int ret = 1; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = af; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(src, NULL, &hints, &res) != 0) return -1; + if (af == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr; + memcpy(dst, &in->sin_addr, sizeof(in->sin_addr)); + } else if (af == AF_INET6) { + struct sockaddr_in6 *in = (struct sockaddr_in6 *) res->ai_addr; + memcpy(dst, &in->sin6_addr, sizeof(in->sin6_addr)); + } else { + ret = -1; + } + freeaddrinfo(res); + return ret; +} + +#endif diff --git a/contrib/luasocket/src/inet.h b/contrib/luasocket/src/inet.h new file mode 100644 index 00000000..feb3541d --- /dev/null +++ b/contrib/luasocket/src/inet.h @@ -0,0 +1,50 @@ +#ifndef INET_H +#define INET_H +/*=========================================================================*\ +* Internet domain functions +* LuaSocket toolkit +* +* This module implements the creation and connection of internet domain +* sockets, on top of the socket.h interface, and the interface of with the +* resolver. +* +* The function inet_aton is provided for the platforms where it is not +* available. The module also implements the interface of the internet +* getpeername and getsockname functions as seen by Lua programs. +* +* The Lua functions toip and tohostname are also implemented here. +\*=========================================================================*/ +#include "lua.h" +#include "socket.h" +#include "timeout.h" + +#ifdef _WIN32 +#define LUASOCKET_INET_ATON +#endif + +int inet_open(lua_State *L); + +const char *inet_trycreate(p_socket ps, int family, int type, int protocol); +const char *inet_tryconnect(p_socket ps, int *family, const char *address, + const char *serv, p_timeout tm, struct addrinfo *connecthints); +const char *inet_trybind(p_socket ps, int *family, const char *address, + const char *serv, struct addrinfo *bindhints); +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); +const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); + +int inet_meth_getpeername(lua_State *L, p_socket ps, int family); +int inet_meth_getsockname(lua_State *L, p_socket ps, int family); + +int inet_optfamily(lua_State* L, int narg, const char* def); +int inet_optsocktype(lua_State* L, int narg, const char* def); + +#ifdef LUASOCKET_INET_ATON +int inet_aton(const char *cp, struct in_addr *inp); +#endif + +#ifdef LUASOCKET_INET_PTON +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); +int inet_pton(int af, const char *src, void *dst); +#endif + +#endif /* INET_H */ diff --git a/contrib/luasocket/src/io.c b/contrib/luasocket/src/io.c new file mode 100644 index 00000000..a4230ce8 --- /dev/null +++ b/contrib/luasocket/src/io.c @@ -0,0 +1,30 @@ +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) { + io->send = send; + io->recv = recv; + io->error = error; + io->ctx = ctx; +} + +/*-------------------------------------------------------------------------*\ +* I/O error strings +\*-------------------------------------------------------------------------*/ +const char *io_strerror(int err) { + switch (err) { + case IO_DONE: return NULL; + case IO_CLOSED: return "closed"; + case IO_TIMEOUT: return "timeout"; + default: return "unknown error"; + } +} diff --git a/contrib/luasocket/src/io.h b/contrib/luasocket/src/io.h new file mode 100644 index 00000000..8cca08a8 --- /dev/null +++ b/contrib/luasocket/src/io.h @@ -0,0 +1,65 @@ +#ifndef IO_H +#define IO_H +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* This module defines the interface that LuaSocket expects from the +* transport layer for streamed input/output. The idea is that if any +* transport implements this interface, then the buffer.c functions +* automatically work on it. +* +* The module socket.h implements this interface, and thus the module tcp.h +* is very simple. +\*=========================================================================*/ +#include +#include "lua.h" + +#include "timeout.h" + +/* IO error codes */ +enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 +}; + +/* interface to error message function */ +typedef const char *(*p_error) ( + void *ctx, /* context needed by send */ + int err /* error code */ +); + +/* interface to send function */ +typedef int (*p_send) ( + void *ctx, /* context needed by send */ + const char *data, /* pointer to buffer with data to send */ + size_t count, /* number of bytes to send from buffer */ + size_t *sent, /* number of bytes sent uppon return */ + p_timeout tm /* timeout control */ +); + +/* interface to recv function */ +typedef int (*p_recv) ( + void *ctx, /* context needed by recv */ + char *data, /* pointer to buffer where data will be writen */ + size_t count, /* number of bytes to receive into buffer */ + size_t *got, /* number of bytes received uppon return */ + p_timeout tm /* timeout control */ +); + +/* IO driver definition */ +typedef struct t_io_ { + void *ctx; /* context needed by send/recv */ + p_send send; /* send function pointer */ + p_recv recv; /* receive function pointer */ + p_error error; /* strerror function */ +} t_io; +typedef t_io *p_io; + +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); +const char *io_strerror(int err); + +#endif /* IO_H */ + diff --git a/contrib/luasocket/src/ltn12.lua b/contrib/luasocket/src/ltn12.lua new file mode 100644 index 00000000..575c5a7f --- /dev/null +++ b/contrib/luasocket/src/ltn12.lua @@ -0,0 +1,309 @@ +----------------------------------------------------------------------------- +-- LTN12 - Filters, sources, sinks and pumps. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local table = require("table") +local unpack = unpack or table.unpack +local base = _G +local _M = {} +if module then -- heuristic for exporting a global package table + ltn12 = _M +end +local filter,source,sink,pump = {},{},{},{} + +_M.filter = filter +_M.source = source +_M.sink = sink +_M.pump = pump + +local unpack = unpack or table.unpack +local select = base.select + +-- 2048 seems to be better in windows... +_M.BLOCKSIZE = 2048 +_M._VERSION = "LTN12 1.0.3" + +----------------------------------------------------------------------------- +-- Filter stuff +----------------------------------------------------------------------------- +-- returns a high level filter that cycles a low-level filter +function filter.cycle(low, ctx, extra) + base.assert(low) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +-- chains a bunch of filters together +-- (thanks to Wim Couwenberg) +function filter.chain(...) + local arg = {...} + local n = base.select('#',...) + local top, index = 1, 1 + local retry = "" + return function(chunk) + retry = chunk and retry + while true do + if index == top then + chunk = arg[index](chunk) + if chunk == "" or top == n then return chunk + elseif chunk then index = index + 1 + else + top = top+1 + index = top + end + else + chunk = arg[index](chunk or "") + if chunk == "" then + index = index - 1 + chunk = retry + elseif chunk then + if index == n then return chunk + else index = index + 1 end + else base.error("filter returned inappropriate nil") end + end + end + end +end + +----------------------------------------------------------------------------- +-- Source stuff +----------------------------------------------------------------------------- +-- create an empty source +local function empty() + return nil +end + +function source.empty() + return empty +end + +-- returns a source that just outputs an error +function source.error(err) + return function() + return nil, err + end +end + +-- creates a file source +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(_M.BLOCKSIZE) + if not chunk then handle:close() end + return chunk + end + else return source.error(io_err or "unable to open file") end +end + +-- turns a fancy source into a simple source +function source.simplify(src) + base.assert(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end + +-- creates string source +function source.string(s) + if s then + local i = 1 + return function() + local chunk = string.sub(s, i, i+_M.BLOCKSIZE-1) + i = i + _M.BLOCKSIZE + if chunk ~= "" then return chunk + else return nil end + end + else return source.empty() end +end + +-- creates rewindable source +function source.rewind(src) + base.assert(src) + local t = {} + return function(chunk) + if not chunk then + chunk = table.remove(t) + if not chunk then return src() + else return chunk end + else + table.insert(t, chunk) + end + end +end + +-- chains a source with one or several filter(s) +function source.chain(src, f, ...) + if ... then f=filter.chain(f, ...) end + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" + local err + return function() + if not last_out then + base.error('source is empty!', 2) + end + while true do + if state == "feeding" then + last_in, err = src() + if err then return nil, err end + last_out = f(last_in) + if not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + elseif last_out ~= "" then + state = "eating" + if last_in then last_in = "" end + return last_out + end + else + last_out = f(last_in) + if last_out == "" then + if last_in == "" then + state = "feeding" + else + base.error('filter returned ""') + end + elseif not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + else + return last_out + end + end + end + end +end + +-- creates a source that produces contents of several sources, one after the +-- other, as if they were concatenated +-- (thanks to Wim Couwenberg) +function source.cat(...) + local arg = {...} + local src = table.remove(arg, 1) + return function() + while src do + local chunk, err = src() + if chunk then return chunk end + if err then return nil, err end + src = table.remove(arg, 1) + end + end +end + +----------------------------------------------------------------------------- +-- Sink stuff +----------------------------------------------------------------------------- +-- creates a sink that stores into a table +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +-- turns a fancy sink into a simple sink +function sink.simplify(snk) + base.assert(snk) + return function(chunk, err) + local ret, err_or_new = snk(chunk, err) + if not ret then return nil, err_or_new end + snk = err_or_new or snk + return 1 + end +end + +-- creates a file sink +function sink.file(handle, io_err) + if handle then + return function(chunk, err) + if not chunk then + handle:close() + return 1 + else return handle:write(chunk) end + end + else return sink.error(io_err or "unable to open file") end +end + +-- creates a sink that discards data +local function null() + return 1 +end + +function sink.null() + return null +end + +-- creates a sink that just returns an error +function sink.error(err) + return function() + return nil, err + end +end + +-- chains a sink with one or several filter(s) +function sink.chain(f, snk, ...) + if ... then + local args = { f, snk, ... } + snk = table.remove(args, #args) + f = filter.chain(unpack(args)) + end + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then + local filtered = f(chunk) + local done = chunk and "" + while true do + local ret, snkerr = snk(filtered, err) + if not ret then return nil, snkerr end + if filtered == done then return 1 end + filtered = f(done) + end + else return 1 end + end +end + +----------------------------------------------------------------------------- +-- Pump stuff +----------------------------------------------------------------------------- +-- pumps one chunk from the source to the sink +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_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 +function pump.all(src, snk, step) + base.assert(src and snk) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end + +return _M diff --git a/contrib/luasocket/src/luasocket.c b/contrib/luasocket/src/luasocket.c new file mode 100644 index 00000000..7d9c8023 --- /dev/null +++ b/contrib/luasocket/src/luasocket.c @@ -0,0 +1,114 @@ +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 26/11/1999 +* +* This library is part of an effort to progressively increase the network +* connectivity of the Lua language. The Lua interface to networking +* functions follows the Sockets API closely, trying to simplify all tasks +* involved in setting up both client and server connections. The provided +* IO routines, however, follow the Lua style, being very similar to the +* standard Lua read and write functions. +\*=========================================================================*/ + +/*=========================================================================*\ +* Standard include files +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +/*=========================================================================*\ +* LuaSocket includes +\*=========================================================================*/ +#include "luasocket.h" +#include "auxiliar.h" +#include "except.h" +#include "timeout.h" +#include "buffer.h" +#include "inet.h" +#include "tcp.h" +#include "udp.h" +#include "select.h" + +/*-------------------------------------------------------------------------*\ +* Internal function prototypes +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L); +static int global_unload(lua_State *L); +static int base_open(lua_State *L); + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"auxiliar", auxiliar_open}, + {"except", except_open}, + {"timeout", timeout_open}, + {"buffer", buffer_open}, + {"inet", inet_open}, + {"tcp", tcp_open}, + {"udp", udp_open}, + {"select", select_open}, + {NULL, NULL} +}; + +static luaL_Reg func[] = { + {"skip", global_skip}, + {"__unload", global_unload}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Skip a few arguments +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L) { + int amount = luaL_checkinteger(L, 1); + int ret = lua_gettop(L) - amount - 1; + return ret >= 0 ? ret : 0; +} + +/*-------------------------------------------------------------------------*\ +* Unloads the library +\*-------------------------------------------------------------------------*/ +static int global_unload(lua_State *L) { + (void) L; + socket_close(); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Setup basic stuff. +\*-------------------------------------------------------------------------*/ +static int base_open(lua_State *L) { + if (socket_open()) { + /* export functions (and leave namespace table on top of stack) */ + lua_newtable(L); + luaL_setfuncs(L, func, 0); +#ifdef LUASOCKET_DEBUG + lua_pushstring(L, "_DEBUG"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, LUASOCKET_VERSION); + lua_rawset(L, -3); + return 1; + } else { + lua_pushstring(L, "unable to initialize library"); + lua_error(L); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Initializes all library modules. +\*-------------------------------------------------------------------------*/ +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); + return 1; +} diff --git a/contrib/luasocket/src/luasocket.h b/contrib/luasocket/src/luasocket.h new file mode 100644 index 00000000..f75d21f2 --- /dev/null +++ b/contrib/luasocket/src/luasocket.h @@ -0,0 +1,29 @@ +#ifndef LUASOCKET_H +#define LUASOCKET_H +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 9/11/1999 +\*=========================================================================*/ +#include "lua.h" + +/*-------------------------------------------------------------------------*\ +* Current socket library version +\*-------------------------------------------------------------------------*/ +#define LUASOCKET_VERSION "LuaSocket 3.0-rc1" +#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2013 Diego Nehab" + +/*-------------------------------------------------------------------------*\ +* This macro prefixes all exported API functions +\*-------------------------------------------------------------------------*/ +#ifndef LUASOCKET_API +#define LUASOCKET_API extern +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes the library. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L); + +#endif /* LUASOCKET_H */ diff --git a/contrib/luasocket/src/mbox.lua b/contrib/luasocket/src/mbox.lua new file mode 100644 index 00000000..ed9e7814 --- /dev/null +++ b/contrib/luasocket/src/mbox.lua @@ -0,0 +1,92 @@ +local _M = {} + +if module then + mbox = _M +end + +function _M.split_message(message_s) + local message = {} + message_s = string.gsub(message_s, "\r\n", "\n") + string.gsub(message_s, "^(.-\n)\n", function (h) message.headers = h end) + string.gsub(message_s, "^.-\n\n(.*)", function (b) message.body = b end) + if not message.body then + string.gsub(message_s, "^\n(.*)", function (b) message.body = b end) + end + if not message.headers and not message.body then + message.headers = message_s + end + return message.headers or "", message.body or "" +end + +function _M.split_headers(headers_s) + local headers = {} + headers_s = string.gsub(headers_s, "\r\n", "\n") + headers_s = string.gsub(headers_s, "\n[ ]+", " ") + string.gsub("\n" .. headers_s, "\n([^\n]+)", function (h) table.insert(headers, h) end) + return headers +end + +function _M.parse_header(header_s) + header_s = string.gsub(header_s, "\n[ ]+", " ") + header_s = string.gsub(header_s, "\n+", "") + local _, __, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") + return name, value +end + +function _M.parse_headers(headers_s) + local headers_t = _M.split_headers(headers_s) + local headers = {} + for i = 1, #headers_t do + local name, value = _M.parse_header(headers_t[i]) + if name then + name = string.lower(name) + if headers[name] then + headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + end + return headers +end + +function _M.parse_from(from) + local _, __, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") + if not address then + _, __, address = string.find(from, "%s*(.+)%s*") + end + name = name or "" + address = address or "" + if name == "" then name = address end + name = string.gsub(name, '"', "") + return name, address +end + +function _M.split_mbox(mbox_s) + local mbox = {} + mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" + local nj, i, j = 1, 1, 1 + while 1 do + i, nj = string.find(mbox_s, "\n\nFrom .-\n", j) + if not i then break end + local message = string.sub(mbox_s, j, i-1) + table.insert(mbox, message) + j = nj+1 + end + return mbox +end + +function _M.parse(mbox_s) + local mbox = _M.split_mbox(mbox_s) + for i = 1, #mbox do + mbox[i] = _M.parse_message(mbox[i]) + end + return mbox +end + +function _M.parse_message(message_s) + local message = {} + message.headers, message.body = _M.split_message(message_s) + message.headers = _M.parse_headers(message.headers) + return message +end + +return _M diff --git a/contrib/luasocket/src/mime.c b/contrib/luasocket/src/mime.c new file mode 100644 index 00000000..ed441046 --- /dev/null +++ b/contrib/luasocket/src/mime.c @@ -0,0 +1,721 @@ +/*=========================================================================*\ +* MIME support functions +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "mime.h" + +/*=========================================================================*\ +* Don't want to trust escape character constants +\*=========================================================================*/ +typedef unsigned char UC; +static const char CRLF[] = "\r\n"; +static const char EQCRLF[] = "=\r\n"; + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int mime_global_wrp(lua_State *L); +static int mime_global_b64(lua_State *L); +static int mime_global_unb64(lua_State *L); +static int mime_global_qp(lua_State *L); +static int mime_global_unqp(lua_State *L); +static int mime_global_qpwrp(lua_State *L); +static int mime_global_eol(lua_State *L); +static int mime_global_dot(lua_State *L); + +static size_t dot(int c, size_t state, luaL_Buffer *buffer); +static void b64setup(UC *base); +static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer); +static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer); + +static void qpsetup(UC *class, UC *unbase); +static void qpquote(UC c, luaL_Buffer *buffer); +static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer); +static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer); + +/* code support functions */ +static luaL_Reg func[] = { + { "dot", mime_global_dot }, + { "b64", mime_global_b64 }, + { "eol", mime_global_eol }, + { "qp", mime_global_qp }, + { "qpwrp", mime_global_qpwrp }, + { "unb64", mime_global_unb64 }, + { "unqp", mime_global_unqp }, + { "wrp", mime_global_wrp }, + { NULL, NULL } +}; + +/*-------------------------------------------------------------------------*\ +* Quoted-printable globals +\*-------------------------------------------------------------------------*/ +static UC qpclass[256]; +static UC qpbase[] = "0123456789ABCDEF"; +static UC qpunbase[256]; +enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST}; + +/*-------------------------------------------------------------------------*\ +* Base64 globals +\*-------------------------------------------------------------------------*/ +static const UC b64base[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static UC b64unbase[256]; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +MIME_API int luaopen_mime_core(lua_State *L) +{ + lua_newtable(L); + luaL_setfuncs(L, func, 0); + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, MIME_VERSION); + lua_rawset(L, -3); + /* initialize lookup tables */ + qpsetup(qpclass, qpunbase); + b64setup(b64unbase); + return 1; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Incrementaly breaks a string into lines. The string can have CRLF breaks. +* A, n = wrp(l, B, length) +* A is a copy of B, broken into lines of at most 'length' bytes. +* 'l' is how many bytes are left for the first line of B. +* 'n' is the number of bytes left in the last line of A. +\*-------------------------------------------------------------------------*/ +static int mime_global_wrp(lua_State *L) +{ + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); + const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; + /* end of input black-hole */ + if (!input) { + /* if last line has not been terminated, add a line break */ + if (left < length) lua_pushstring(L, CRLF); + /* otherwise, we are done */ + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; + } + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { + case '\r': + break; + case '\n': + luaL_addstring(&buffer, CRLF); + left = length; + break; + default: + if (left <= 0) { + left = length; + luaL_addstring(&buffer, CRLF); + } + luaL_addchar(&buffer, *input); + left--; + break; + } + input++; + } + luaL_pushresult(&buffer); + lua_pushnumber(L, left); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Fill base64 decode map. +\*-------------------------------------------------------------------------*/ +static void b64setup(UC *unbase) +{ + int i; + for (i = 0; i <= 255; i++) unbase[i] = (UC) 255; + for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i; + unbase['='] = 0; +} + +/*-------------------------------------------------------------------------*\ +* Acumulates bytes in input buffer until 3 bytes are available. +* Translate the 3 bytes into Base64 form and append to buffer. +* Returns new number of bytes in buffer. +\*-------------------------------------------------------------------------*/ +static size_t b64encode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) +{ + input[size++] = c; + if (size == 3) { + UC code[4]; + unsigned long value = 0; + value += input[0]; value <<= 8; + value += input[1]; value <<= 8; + value += input[2]; + code[3] = b64base[value & 0x3f]; value >>= 6; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + size = 0; + } + return size; +} + +/*-------------------------------------------------------------------------*\ +* Encodes the Base64 last 1 or 2 bytes and adds padding '=' +* Result, if any, is appended to buffer. +* Returns 0. +\*-------------------------------------------------------------------------*/ +static size_t b64pad(const UC *input, size_t size, + luaL_Buffer *buffer) +{ + unsigned long value = 0; + UC code[4] = {'=', '=', '=', '='}; + switch (size) { + case 1: + value = input[0] << 4; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + break; + case 2: + value = input[0]; value <<= 8; + value |= input[1]; value <<= 2; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + break; + default: + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Acumulates bytes in input buffer until 4 bytes are available. +* Translate the 4 bytes from Base64 form and append to buffer. +* Returns new number of bytes in buffer. +\*-------------------------------------------------------------------------*/ +static size_t b64decode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) +{ + /* ignore invalid characters */ + if (b64unbase[c] > 64) return size; + input[size++] = c; + /* decode atom */ + if (size == 4) { + UC decoded[3]; + int valid, value = 0; + value = b64unbase[input[0]]; value <<= 6; + value |= b64unbase[input[1]]; value <<= 6; + value |= b64unbase[input[2]]; value <<= 6; + value |= b64unbase[input[3]]; + decoded[2] = (UC) (value & 0xff); value >>= 8; + decoded[1] = (UC) (value & 0xff); value >>= 8; + decoded[0] = (UC) value; + /* take care of paddding */ + valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; + luaL_addlstring(buffer, (char *) decoded, valid); + return 0; + /* need more data */ + } else return size; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally applies the Base64 transfer content encoding to a string +* A, B = b64(C, D) +* A is the encoded version of the largest prefix of C .. D that is +* divisible by 3. B has the remaining bytes of C .. D, *without* encoding. +* The easiest thing would be to concatenate the two strings and +* encode the result, but we can't afford that or Lua would dupplicate +* every chunk we received. +\*-------------------------------------------------------------------------*/ +static int mime_global_b64(lua_State *L) +{ + UC atom[3]; + size_t isize = 0, asize = 0; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 2); + /* process first part of the input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + size_t osize = 0; + asize = b64pad(atom, asize, &buffer); + luaL_pushresult(&buffer); + /* if the output is empty and the input is nil, return nil */ + lua_tolstring(L, -1, &osize); + if (osize == 0) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process the second part */ + last = input + isize; + while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally removes the Base64 transfer content encoding from a string +* A, B = b64(C, D) +* A is the encoded version of the largest prefix of C .. D that is +* divisible by 4. B has the remaining bytes of C .. D, *without* encoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_unb64(lua_State *L) +{ + UC atom[4]; + size_t isize = 0, asize = 0; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 2); + /* process first part of the input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second is nil, we are done */ + if (!input) { + size_t osize = 0; + luaL_pushresult(&buffer); + /* if the output is empty and the input is nil, return nil */ + lua_tolstring(L, -1, &osize); + if (osize == 0) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise, process the rest of the input */ + last = input + isize; + while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Quoted-printable encoding scheme +* all (except CRLF in text) can be =XX +* CLRL in not text must be =XX=XX +* 33 through 60 inclusive can be plain +* 62 through 126 inclusive can be plain +* 9 and 32 can be plain, unless in the end of a line, where must be =XX +* encoded lines must be no longer than 76 not counting CRLF +* soft line-break are =CRLF +* To encode one byte, we need to see the next two. +* Worst case is when we see a space, and wonder if a CRLF is comming +\*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*\ +* Split quoted-printable characters into classes +* Precompute reverse map for encoding +\*-------------------------------------------------------------------------*/ +static void qpsetup(UC *cl, UC *unbase) +{ + int i; + for (i = 0; i < 256; i++) cl[i] = QP_QUOTED; + for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN; + for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN; + cl['\t'] = QP_IF_LAST; + cl[' '] = QP_IF_LAST; + cl['\r'] = QP_CR; + for (i = 0; i < 256; i++) unbase[i] = 255; + unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2; + unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5; + unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8; + unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10; + unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12; + unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13; + unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15; + unbase['f'] = 15; +} + +/*-------------------------------------------------------------------------*\ +* Output one character in form =XX +\*-------------------------------------------------------------------------*/ +static void qpquote(UC c, luaL_Buffer *buffer) +{ + luaL_addchar(buffer, '='); + luaL_addchar(buffer, qpbase[c >> 4]); + luaL_addchar(buffer, qpbase[c & 0x0F]); +} + +/*-------------------------------------------------------------------------*\ +* Accumulate characters until we are sure about how to deal with them. +* Once we are sure, output to the buffer, in the correct form. +\*-------------------------------------------------------------------------*/ +static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer) +{ + input[size++] = c; + /* deal with all characters we can have */ + while (size > 0) { + switch (qpclass[input[0]]) { + /* might be the CR of a CRLF sequence */ + case QP_CR: + if (size < 2) return size; + if (input[1] == '\n') { + luaL_addstring(buffer, marker); + return 0; + } else qpquote(input[0], buffer); + break; + /* might be a space and that has to be quoted if last in line */ + case QP_IF_LAST: + if (size < 3) return size; + /* if it is the last, quote it and we are done */ + if (input[1] == '\r' && input[2] == '\n') { + qpquote(input[0], buffer); + luaL_addstring(buffer, marker); + return 0; + } else luaL_addchar(buffer, input[0]); + break; + /* might have to be quoted always */ + case QP_QUOTED: + qpquote(input[0], buffer); + break; + /* might never have to be quoted */ + default: + luaL_addchar(buffer, input[0]); + break; + } + input[0] = input[1]; input[1] = input[2]; + size--; + } + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Deal with the final characters +\*-------------------------------------------------------------------------*/ +static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer) +{ + size_t i; + for (i = 0; i < size; i++) { + if (qpclass[input[i]] == QP_PLAIN) luaL_addchar(buffer, input[i]); + else qpquote(input[i], buffer); + } + if (size > 0) luaL_addstring(buffer, EQCRLF); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally converts a string to quoted-printable +* A, B = qp(C, D, marker) +* Marker is the text to be used to replace CRLF sequences found in A. +* A is the encoded version of the largest prefix of C .. D that +* can be encoded without doubts. +* B has the remaining bytes of C .. D, *without* encoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_qp(lua_State *L) +{ + + size_t asize = 0, isize = 0; + UC atom[3]; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 3); + /* process first part of input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + asize = qppad(atom, asize, &buffer); + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Accumulate characters until we are sure about how to deal with them. +* Once we are sure, output the to the buffer, in the correct form. +\*-------------------------------------------------------------------------*/ +static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + int d; + input[size++] = c; + /* deal with all characters we can deal */ + switch (input[0]) { + /* if we have an escape character */ + case '=': + if (size < 3) return size; + /* eliminate soft line break */ + if (input[1] == '\r' && input[2] == '\n') return 0; + /* decode quoted representation */ + c = qpunbase[input[1]]; d = qpunbase[input[2]]; + /* if it is an invalid, do not decode */ + if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3); + else luaL_addchar(buffer, (char) ((c << 4) + d)); + return 0; + case '\r': + if (size < 2) return size; + if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2); + return 0; + default: + if (input[0] == '\t' || (input[0] > 31 && input[0] < 127)) + luaL_addchar(buffer, input[0]); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Incrementally decodes a string in quoted-printable +* A, B = qp(C, D) +* A is the decoded version of the largest prefix of C .. D that +* can be decoded without doubts. +* B has the remaining bytes of C .. D, *without* decoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_unqp(lua_State *L) +{ + size_t asize = 0, isize = 0; + UC atom[3]; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 2); + /* process first part of input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally breaks a quoted-printed string into lines +* A, n = qpwrp(l, B, length) +* A is a copy of B, broken into lines of at most 'length' bytes. +* 'l' is how many bytes are left for the first line of B. +* 'n' is the number of bytes left in the last line of A. +* There are two complications: lines can't be broken in the middle +* of an encoded =XX, and there might be line breaks already +\*-------------------------------------------------------------------------*/ +static int mime_global_qpwrp(lua_State *L) +{ + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); + const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + if (left < length) lua_pushstring(L, EQCRLF); + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; + } + /* process all input */ + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { + case '\r': + break; + case '\n': + left = length; + luaL_addstring(&buffer, CRLF); + break; + case '=': + if (left <= 3) { + left = length; + luaL_addstring(&buffer, EQCRLF); + } + luaL_addchar(&buffer, *input); + left--; + break; + default: + if (left <= 1) { + left = length; + luaL_addstring(&buffer, EQCRLF); + } + luaL_addchar(&buffer, *input); + left--; + break; + } + input++; + } + luaL_pushresult(&buffer); + lua_pushnumber(L, left); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Here is what we do: \n, and \r are considered candidates for line +* break. We issue *one* new line marker if any of them is seen alone, or +* followed by a different one. That is, \n\n and \r\r will issue two +* end of line markers each, but \r\n, \n\r etc will only issue *one* +* marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as +* probably other more obscure conventions. +* +* c is the current character being processed +* last is the previous character +\*-------------------------------------------------------------------------*/ +#define eolcandidate(c) (c == '\r' || c == '\n') +static int eolprocess(int c, int last, const char *marker, + luaL_Buffer *buffer) +{ + if (eolcandidate(c)) { + if (eolcandidate(last)) { + if (c == last) luaL_addstring(buffer, marker); + return 0; + } else { + luaL_addstring(buffer, marker); + return c; + } + } else { + luaL_addchar(buffer, (char) c); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Converts a string to uniform EOL convention. +* A, n = eol(o, B, marker) +* A is the converted version of the largest prefix of B that can be +* converted unambiguously. 'o' is the context returned by the previous +* call. 'n' is the new context. +\*-------------------------------------------------------------------------*/ +static int mime_global_eol(lua_State *L) +{ + int ctx = luaL_checkinteger(L, 1); + size_t isize = 0; + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + /* end of input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 0); + return 2; + } + /* process all input */ + while (input < last) + ctx = eolprocess(*input++, ctx, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, ctx); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Takes one byte and stuff it if needed. +\*-------------------------------------------------------------------------*/ +static size_t dot(int c, size_t state, luaL_Buffer *buffer) +{ + luaL_addchar(buffer, (char) c); + switch (c) { + case '\r': + return 1; + case '\n': + return (state == 1)? 2: 0; + case '.': + if (state == 2) + luaL_addchar(buffer, '.'); + default: + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Incrementally applies smtp stuffing to a string +* A, n = dot(l, D) +\*-------------------------------------------------------------------------*/ +static int mime_global_dot(lua_State *L) +{ + size_t isize = 0, state = (size_t) luaL_checknumber(L, 1); + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 2); + return 2; + } + /* process all input */ + luaL_buffinit(L, &buffer); + while (input < last) + state = dot(*input++, state, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, (lua_Number) state); + return 2; +} + diff --git a/contrib/luasocket/src/mime.h b/contrib/luasocket/src/mime.h new file mode 100644 index 00000000..99968a55 --- /dev/null +++ b/contrib/luasocket/src/mime.h @@ -0,0 +1,29 @@ +#ifndef MIME_H +#define MIME_H +/*=========================================================================*\ +* Core MIME support +* LuaSocket toolkit +* +* This module provides functions to implement transfer content encodings +* and formatting conforming to RFC 2045. It is used by mime.lua, which +* provide a higher level interface to this functionality. +\*=========================================================================*/ +#include "lua.h" + +/*-------------------------------------------------------------------------*\ +* Current MIME library version +\*-------------------------------------------------------------------------*/ +#define MIME_VERSION "MIME 1.0.3" +#define MIME_COPYRIGHT "Copyright (C) 2004-2013 Diego Nehab" +#define MIME_AUTHORS "Diego Nehab" + +/*-------------------------------------------------------------------------*\ +* This macro prefixes all exported API functions +\*-------------------------------------------------------------------------*/ +#ifndef MIME_API +#define MIME_API extern +#endif + +MIME_API int luaopen_mime_core(lua_State *L); + +#endif /* MIME_H */ diff --git a/contrib/luasocket/src/mime.lua b/contrib/luasocket/src/mime.lua new file mode 100644 index 00000000..642cd9ca --- /dev/null +++ b/contrib/luasocket/src/mime.lua @@ -0,0 +1,90 @@ +----------------------------------------------------------------------------- +-- MIME support for the Lua language. +-- Author: Diego Nehab +-- Conforming to RFCs 2045-2049 +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local ltn12 = require("ltn12") +local mime = require("mime.core") +local io = require("io") +local string = require("string") +local _M = mime + +-- encode, decode and wrap algorithm tables +local encodet, decodet, wrapt = {},{},{} + +_M.encodet = encodet +_M.decodet = decodet +_M.wrapt = wrapt + +-- creates a function that chooses a filter by name from a given table +local function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then + base.error("unknown key (" .. base.tostring(name) .. ")", 3) + else return f(opt1, opt2) end + end +end + +-- define the encoding filters +encodet['base64'] = function() + return ltn12.filter.cycle(_M.b64, "") +end + +encodet['quoted-printable'] = function(mode) + return ltn12.filter.cycle(_M.qp, "", + (mode == "binary") and "=0D=0A" or "\r\n") +end + +-- define the decoding filters +decodet['base64'] = function() + return ltn12.filter.cycle(_M.unb64, "") +end + +decodet['quoted-printable'] = function() + return ltn12.filter.cycle(_M.unqp, "") +end + +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +-- define the line-wrap filters +wrapt['text'] = function(length) + length = length or 76 + return ltn12.filter.cycle(_M.wrp, length, length) +end +wrapt['base64'] = wrapt['text'] +wrapt['default'] = wrapt['text'] + +wrapt['quoted-printable'] = function() + return ltn12.filter.cycle(_M.qpwrp, 76, 76) +end + +-- function that choose the encoding, decoding or wrap algorithm +_M.encode = choose(encodet) +_M.decode = choose(decodet) +_M.wrap = choose(wrapt) + +-- define the end-of-line normalization filter +function _M.normalize(marker) + return ltn12.filter.cycle(_M.eol, 0, marker) +end + +-- high level stuffing filter +function _M.stuff() + return ltn12.filter.cycle(_M.dot, 2) +end + +return _M \ No newline at end of file diff --git a/contrib/luasocket/src/options.c b/contrib/luasocket/src/options.c new file mode 100644 index 00000000..fabfe8ce --- /dev/null +++ b/contrib/luasocket/src/options.c @@ -0,0 +1,374 @@ +/*=========================================================================*\ +* Common option interface +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lauxlib.h" + +#include "auxiliar.h" +#include "options.h" +#include "inet.h" + + +/*=========================================================================*\ +* Internal functions prototypes +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_setint(lua_State *L, p_socket ps, int level, int name); +static int opt_getint(lua_State *L, p_socket ps, int level, int name); +static int opt_set(lua_State *L, p_socket ps, int level, int name, + void *val, int len); +static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Calls appropriate option handler +\*-------------------------------------------------------------------------*/ +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + return opt->func(L, ps); +} + +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + return opt->func(L, ps); +} + +/* enables reuse of local address */ +int opt_set_reuseaddr(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + +int opt_get_reuseaddr(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + +/* enables reuse of local port */ +int opt_set_reuseport(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); +} + +int opt_get_reuseport(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); +} + +/* disables the Naggle algorithm */ +int opt_set_tcp_nodelay(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + +int opt_get_tcp_nodelay(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + +int opt_set_keepalive(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + +int opt_get_keepalive(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + +int opt_set_dontroute(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +} + +int opt_get_dontroute(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +} + +int opt_set_broadcast(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); +} + +int opt_get_broadcast(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); +} + +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); +} + +int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); +} + +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} + +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} + +int opt_set_linger(lua_State *L, p_socket ps) +{ + struct linger li; /* obj, name, table */ + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "on"); + lua_gettable(L, 3); + if (!lua_isboolean(L, -1)) + luaL_argerror(L, 3, "boolean 'on' field expected"); + li.l_onoff = (u_short) lua_toboolean(L, -1); + lua_pushstring(L, "timeout"); + lua_gettable(L, 3); + if (!lua_isnumber(L, -1)) + luaL_argerror(L, 3, "number 'timeout' field expected"); + li.l_linger = (u_short) lua_tonumber(L, -1); + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li)); +} + +int opt_get_linger(lua_State *L, p_socket ps) +{ + struct linger li; /* obj, name */ + int len = sizeof(li); + int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len); + if (err) + return err; + lua_newtable(L); + lua_pushboolean(L, li.l_onoff); + lua_setfield(L, -2, "on"); + lua_pushinteger(L, li.l_linger); + lua_setfield(L, -2, "timeout"); + return 1; +} + +int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL); +} + +int opt_set_ip_multicast_if(lua_State *L, p_socket ps) +{ + const char *address = luaL_checkstring(L, 3); /* obj, name, ip */ + struct in_addr val; + val.s_addr = htonl(INADDR_ANY); + if (strcmp(address, "*") && !inet_aton(address, &val)) + luaL_argerror(L, 3, "ip expected"); + return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &val, sizeof(val)); +} + +int opt_get_ip_multicast_if(lua_State *L, p_socket ps) +{ + struct in_addr val; + socklen_t len = sizeof(val); + if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + lua_pushstring(L, inet_ntoa(val)); + return 1; +} + +int opt_set_ip_add_membership(lua_State *L, p_socket ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP); +} + +int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); +} + +int opt_set_ip6_add_membership(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); +} + +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP); +} + +int opt_get_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} + +int opt_set_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} + +/*=========================================================================*\ +* Auxiliar functions +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ip_mreq val; /* obj, name, table */ + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'interface' field expected"); + val.imr_interface.s_addr = htonl(INADDR_ANY); + if (strcmp(lua_tostring(L, -1), "*") && + !inet_aton(lua_tostring(L, -1), &val.imr_interface)) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ipv6_mreq val; /* obj, opt-name, table */ + memset(&val, 0, sizeof(val)); + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + /* By default we listen to interface on default route + * (sigh). However, interface= can override it. We should + * support either number, or name for it. Waiting for + * windows port of if_nametoindex */ + if (!lua_isnil(L, -1)) { + if (lua_isnumber(L, -1)) { + val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1); + } else + luaL_argerror(L, -1, "number 'interface' field expected"); + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static +int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +{ + socklen_t socklen = *len; + if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + *len = socklen; + return 0; +} + +static +int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len) +{ + if (setsockopt(*ps, level, name, (char *) val, len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int opt_getboolean(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushboolean(L, val); + return 1; +} + +int opt_get_error(lua_State *L, p_socket ps) +{ + int val = 0; + socklen_t len = sizeof(val); + if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + lua_pushstring(L, socket_strerror(val)); + return 1; +} + +static int opt_setboolean(lua_State *L, p_socket ps, int level, int name) +{ + int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static int opt_getint(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushnumber(L, val); + return 1; +} + +static int opt_setint(lua_State *L, p_socket ps, int level, int name) +{ + int val = (int) lua_tonumber(L, 3); /* obj, name, int */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} diff --git a/contrib/luasocket/src/options.h b/contrib/luasocket/src/options.h new file mode 100644 index 00000000..19ba0dfc --- /dev/null +++ b/contrib/luasocket/src/options.h @@ -0,0 +1,62 @@ +#ifndef OPTIONS_H +#define OPTIONS_H +/*=========================================================================*\ +* Common option interface +* LuaSocket toolkit +* +* This module provides a common interface to socket options, used mainly by +* modules UDP and TCP. +\*=========================================================================*/ + +#include "lua.h" +#include "socket.h" + +/* option registry */ +typedef struct t_opt { + const char *name; + int (*func)(lua_State *L, p_socket ps); +} t_opt; +typedef t_opt *p_opt; + +/* supported options for setoption */ +int opt_set_dontroute(lua_State *L, p_socket ps); +int opt_set_broadcast(lua_State *L, p_socket ps); +int opt_set_tcp_nodelay(lua_State *L, p_socket ps); +int opt_set_keepalive(lua_State *L, p_socket ps); +int opt_set_linger(lua_State *L, p_socket ps); +int opt_set_reuseaddr(lua_State *L, p_socket ps); +int opt_set_reuseport(lua_State *L, p_socket ps); +int opt_set_ip_multicast_if(lua_State *L, p_socket ps); +int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps); +int opt_set_ip_multicast_loop(lua_State *L, p_socket ps); +int opt_set_ip_add_membership(lua_State *L, p_socket ps); +int opt_set_ip_drop_membersip(lua_State *L, p_socket ps); +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps); +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps); +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps); +int opt_set_ip6_add_membership(lua_State *L, p_socket ps); +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps); +int opt_set_ip6_v6only(lua_State *L, p_socket ps); + +/* supported options for getoption */ +int opt_get_dontroute(lua_State *L, p_socket ps); +int opt_get_broadcast(lua_State *L, p_socket ps); +int opt_get_reuseaddr(lua_State *L, p_socket ps); +int opt_get_reuseport(lua_State *L, p_socket ps); +int opt_get_tcp_nodelay(lua_State *L, p_socket ps); +int opt_get_keepalive(lua_State *L, p_socket ps); +int opt_get_linger(lua_State *L, p_socket ps); +int opt_get_ip_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip_multicast_if(lua_State *L, p_socket ps); +int opt_get_error(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_v6only(lua_State *L, p_socket ps); +int opt_get_reuseport(lua_State *L, p_socket ps); + +/* invokes the appropriate option handler */ +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps); + +#endif diff --git a/contrib/luasocket/src/pierror.h b/contrib/luasocket/src/pierror.h new file mode 100644 index 00000000..cb773ab7 --- /dev/null +++ b/contrib/luasocket/src/pierror.h @@ -0,0 +1,28 @@ +#ifndef PIERROR_H +#define PIERROR_H +/*=========================================================================*\ +* Error messages +* Defines platform independent error messages +\*=========================================================================*/ + +#define PIE_HOST_NOT_FOUND "host not found" +#define PIE_ADDRINUSE "address already in use" +#define PIE_ISCONN "already connected" +#define PIE_ACCESS "permission denied" +#define PIE_CONNREFUSED "connection refused" +#define PIE_CONNABORTED "closed" +#define PIE_CONNRESET "closed" +#define PIE_TIMEDOUT "timeout" +#define PIE_AGAIN "temporary failure in name resolution" +#define PIE_BADFLAGS "invalid value for ai_flags" +#define PIE_BADHINTS "invalid value for hints" +#define PIE_FAIL "non-recoverable failure in name resolution" +#define PIE_FAMILY "ai_family not supported" +#define PIE_MEMORY "memory allocation failure" +#define PIE_NONAME "host or service not provided, or not known" +#define PIE_OVERFLOW "argument buffer overflow" +#define PIE_PROTOCOL "resolved protocol is unknown" +#define PIE_SERVICE "service not supported for socket type" +#define PIE_SOCKTYPE "ai_socktype not supported" + +#endif diff --git a/contrib/luasocket/src/select.c b/contrib/luasocket/src/select.c new file mode 100644 index 00000000..9d133b7a --- /dev/null +++ b/contrib/luasocket/src/select.c @@ -0,0 +1,220 @@ +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "socket.h" +#include "timeout.h" +#include "select.h" + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static t_socket getfd(lua_State *L); +static int dirty(lua_State *L); +static void collect_fd(lua_State *L, int tab, int itab, + fd_set *set, t_socket *max_fd); +static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); +static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start); +static void make_assoc(lua_State *L, int tab); +static int global_select(lua_State *L); + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"select", global_select}, + {NULL, NULL} +}; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int select_open(lua_State *L) { + lua_pushstring(L, "_SETSIZE"); + lua_pushinteger(L, FD_SETSIZE); + lua_rawset(L, -3); + lua_pushstring(L, "_SOCKETINVALID"); + lua_pushinteger(L, SOCKET_INVALID); + lua_rawset(L, -3); + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Waits for a set of sockets until a condition is met or timeout. +\*-------------------------------------------------------------------------*/ +static int global_select(lua_State *L) { + int rtab, wtab, itab, ret, ndirty; + t_socket max_fd = SOCKET_INVALID; + fd_set rset, wset; + t_timeout tm; + double t = luaL_optnumber(L, 3, -1); + FD_ZERO(&rset); FD_ZERO(&wset); + lua_settop(L, 3); + lua_newtable(L); itab = lua_gettop(L); + lua_newtable(L); rtab = lua_gettop(L); + lua_newtable(L); wtab = lua_gettop(L); + collect_fd(L, 1, itab, &rset, &max_fd); + collect_fd(L, 2, itab, &wset, &max_fd); + ndirty = check_dirty(L, 1, rtab, &rset); + t = ndirty > 0? 0.0: t; + timeout_init(&tm, t, -1); + timeout_markstart(&tm); + ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm); + if (ret > 0 || ndirty > 0) { + return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); + return_fd(L, &wset, max_fd+1, itab, wtab, 0); + make_assoc(L, rtab); + make_assoc(L, wtab); + return 2; + } else if (ret == 0) { + lua_pushstring(L, "timeout"); + return 3; + } else { + luaL_error(L, "select failed"); + return 3; + } +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +static t_socket getfd(lua_State *L) { + t_socket fd = SOCKET_INVALID; + lua_pushstring(L, "getfd"); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + if (lua_isnumber(L, -1)) { + double numfd = lua_tonumber(L, -1); + fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID; + } + } + lua_pop(L, 1); + return fd; +} + +static int dirty(lua_State *L) { + int is = 0; + lua_pushstring(L, "dirty"); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + is = lua_toboolean(L, -1); + } + lua_pop(L, 1); + return is; +} + +static void collect_fd(lua_State *L, int tab, int itab, + fd_set *set, t_socket *max_fd) { + int i = 1, n = 0; + /* nil is the same as an empty table */ + if (lua_isnil(L, tab)) return; + /* otherwise we need it to be a table */ + luaL_checktype(L, tab, LUA_TTABLE); + for ( ;; ) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + /* getfd figures out if this is a socket */ + fd = getfd(L); + if (fd != SOCKET_INVALID) { + /* make sure we don't overflow the fd_set */ +#ifdef _WIN32 + if (n >= FD_SETSIZE) + luaL_argerror(L, tab, "too many sockets"); +#else + if (fd >= FD_SETSIZE) + luaL_argerror(L, tab, "descriptor too large for set size"); +#endif + FD_SET(fd, set); + n++; + /* keep track of the largest descriptor so far */ + if (*max_fd == SOCKET_INVALID || *max_fd < fd) + *max_fd = fd; + /* make sure we can map back from descriptor to the object */ + lua_pushnumber(L, (lua_Number) fd); + lua_pushvalue(L, -2); + lua_settable(L, itab); + } + lua_pop(L, 1); + i = i + 1; + } +} + +static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { + int ndirty = 0, i = 1; + if (lua_isnil(L, tab)) + return 0; + for ( ;; ) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + fd = getfd(L); + if (fd != SOCKET_INVALID && dirty(L)) { + lua_pushnumber(L, ++ndirty); + lua_pushvalue(L, -2); + lua_settable(L, dtab); + FD_CLR(fd, set); + } + lua_pop(L, 1); + i = i + 1; + } + return ndirty; +} + +static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start) { + t_socket fd; + for (fd = 0; fd < max_fd; fd++) { + if (FD_ISSET(fd, set)) { + lua_pushnumber(L, ++start); + lua_pushnumber(L, (lua_Number) fd); + lua_gettable(L, itab); + lua_settable(L, tab); + } + } +} + +static void make_assoc(lua_State *L, int tab) { + int i = 1, atab; + lua_newtable(L); atab = lua_gettop(L); + for ( ;; ) { + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (!lua_isnil(L, -1)) { + lua_pushnumber(L, i); + lua_pushvalue(L, -2); + lua_settable(L, atab); + lua_pushnumber(L, i); + lua_settable(L, atab); + } else { + lua_pop(L, 1); + break; + } + i = i+1; + } +} + diff --git a/contrib/luasocket/src/select.h b/contrib/luasocket/src/select.h new file mode 100644 index 00000000..87502003 --- /dev/null +++ b/contrib/luasocket/src/select.h @@ -0,0 +1,15 @@ +#ifndef SELECT_H +#define SELECT_H +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +* +* Each object that can be passed to the select function has to export +* method getfd() which returns the descriptor to be passed to the +* underlying select function. Another method, dirty(), should return +* true if there is data ready for reading (required for buffered input). +\*=========================================================================*/ + +int select_open(lua_State *L); + +#endif /* SELECT_H */ diff --git a/contrib/luasocket/src/serial.c b/contrib/luasocket/src/serial.c new file mode 100644 index 00000000..7bdb21c3 --- /dev/null +++ b/contrib/luasocket/src/serial.c @@ -0,0 +1,172 @@ +/*=========================================================================*\ +* Serial stream +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" +#include + +/* +Reuses userdata definition from unix.h, since it is useful for all +stream-like objects. + +If we stored the serial path for use in error messages or userdata +printing, we might need our own userdata definition. + +Group usage is semi-inherited from unix.c, but unnecessary since we +have only one object type. +*/ + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); + +/* serial object methods */ +static luaL_Reg serial_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"settimeout", meth_settimeout}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_serial(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "serial{client}", serial_methods); + /* create class groups */ + auxiliar_add2group(L, "serial{client}", "serial{any}"); + lua_pushcfunction(L, global_create); + return 1; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ + + +/*-------------------------------------------------------------------------*\ +* Creates a serial object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + const char* path = luaL_checkstring(L, 1); + + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + + /* open serial device */ + t_socket sock = open(path, O_NOCTTY|O_RDWR); + + /*printf("open %s on %d\n", path, sock);*/ + + if (sock < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } + /* set its type as client object */ + auxiliar_setclass(L, "serial{client}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; +} diff --git a/contrib/luasocket/src/smtp.lua b/contrib/luasocket/src/smtp.lua new file mode 100644 index 00000000..b113d006 --- /dev/null +++ b/contrib/luasocket/src/smtp.lua @@ -0,0 +1,256 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") +local socket = require("socket") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +local headers = require("socket.headers") +local mime = require("mime") + +socket.smtp = {} +local _M = socket.smtp + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout for connection +_M.TIMEOUT = 60 +-- default server used to send e-mails +_M.SERVER = "localhost" +-- default port +_M.PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +_M.DOMAIN = os.getenv("SERVER_NAME") or "localhost" +-- default time zone (means we don't know) +_M.ZONE = "-0000" + +--------------------------------------------------------------------------- +-- Low level SMTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function metat.__index:greet(domain) + self.try(self.tp:check("2..")) + self.try(self.tp:command("EHLO", domain or _M.DOMAIN)) + return socket.skip(1, self.try(self.tp:check("2.."))) +end + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:check("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:close() + return self.tp:close() +end + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:check("3..")) + self.try(self.tp:send(mime.b64(user) .. "\r\n")) + self.try(self.tp:check("3..")) + self.try(self.tp:send(mime.b64(password) .. "\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + else + self.try(nil, "authentication not supported") + end +end + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + if base.type(mailt.rcpt) == "table" then + for i,v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + else + self:rcpt(mailt.rcpt) + end + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + +function _M.open(server, port, create) + local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT, + _M.TIMEOUT, create)) + local s = base.setmetatable({tp = tp}, metat) + -- make sure tp is closed if we get an exception + s.try = socket.newtry(function() + s:close() + end) + return s +end + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +--------------------------------------------------------------------------- +-- Multipart message source +----------------------------------------------------------------------------- +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), + math.random(0, 99999), seqno) +end + +-- send_message forward declaration +local send_message + +-- yield the headers all at once, it's faster +local function send_headers(tosend) + local canonic = headers.canonic + local h = "\r\n" + for f,v in base.pairs(tosend) do + h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h + end + coroutine.yield(h) +end + +-- yield multipart message body from a multipart message table +local function send_multipart(mesgt) + -- make sure we have our boundary and send headers + local bd = newboundary() + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or 'multipart/mixed' + headers['content-type'] = headers['content-type'] .. + '; boundary="' .. bd .. '"' + send_headers(headers) + -- send preamble + if mesgt.body.preamble then + coroutine.yield(mesgt.body.preamble) + coroutine.yield("\r\n") + end + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from source + while true do + local chunk, err = mesgt.body() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from string + coroutine.yield(mesgt.body) +end + +-- message source +function send_message(mesgt) + if base.type(mesgt.body) == "table" then send_multipart(mesgt) + elseif base.type(mesgt.body) == "function" then send_source(mesgt) + else send_string(mesgt) end +end + +-- set defaul headers +local function adjust_headers(mesgt) + local lower = lower_headers(mesgt.headers) + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE) + lower["x-mailer"] = lower["x-mailer"] or socket._VERSION + -- this can't be overriden + lower["mime-version"] = "1.0" + return lower +end + +function _M.message(mesgt) + mesgt.headers = adjust_headers(mesgt) + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + return function() + local ret, a, b = coroutine.resume(co) + if ret then return a, b + else return nil, a end + end +end + +--------------------------------------------------------------------------- +-- High level SMTP API +----------------------------------------------------------------------------- +_M.send = socket.protect(function(mailt) + local s = _M.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) + +return _M \ No newline at end of file diff --git a/contrib/luasocket/src/socket.h b/contrib/luasocket/src/socket.h new file mode 100644 index 00000000..63573de5 --- /dev/null +++ b/contrib/luasocket/src/socket.h @@ -0,0 +1,78 @@ +#ifndef SOCKET_H +#define SOCKET_H +/*=========================================================================*\ +* Socket compatibilization module +* LuaSocket toolkit +* +* BSD Sockets and WinSock are similar, but there are a few irritating +* differences. Also, not all *nix platforms behave the same. This module +* (and the associated usocket.h and wsocket.h) factor these differences and +* creates a interface compatible with the io.h module. +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Platform specific compatibilization +\*=========================================================================*/ +#ifdef _WIN32 +#include "wsocket.h" +#else +#include "usocket.h" +#endif + +/*=========================================================================*\ +* The connect and accept functions accept a timeout and their +* implementations are somewhat complicated. We chose to move +* the timeout control into this module for these functions in +* order to simplify the modules that use them. +\*=========================================================================*/ +#include "timeout.h" + +/* we are lazy... */ +typedef struct sockaddr SA; + +/*=========================================================================*\ +* Functions bellow implement a comfortable platform independent +* interface to sockets +\*=========================================================================*/ +int socket_open(void); +int socket_close(void); +void socket_destroy(p_socket ps); +void socket_shutdown(p_socket ps, int how); +int socket_sendto(p_socket ps, const char *data, size_t count, + size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_recvfrom(p_socket ps, char *data, size_t count, + size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); + +void socket_setnonblocking(p_socket ps); +void socket_setblocking(p_socket ps); + +int socket_waitfd(p_socket ps, int sw, p_timeout tm); +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm); + +int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_create(p_socket ps, int domain, int type, int protocol); +int socket_bind(p_socket ps, SA *addr, socklen_t addr_len); +int socket_listen(p_socket ps, int backlog); +int socket_accept(p_socket ps, p_socket pa, SA *addr, + socklen_t *addr_len, p_timeout tm); + +const char *socket_hoststrerror(int err); +const char *socket_gaistrerror(int err); +const char *socket_strerror(int err); + +/* these are perfect to use with the io abstraction module + and the buffered input module */ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +const char *socket_ioerror(p_socket ps, int err); + +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); +int socket_gethostbyname(const char *addr, struct hostent **hp); + +#endif /* SOCKET_H */ diff --git a/contrib/luasocket/src/socket.lua b/contrib/luasocket/src/socket.lua new file mode 100644 index 00000000..d1c0b164 --- /dev/null +++ b/contrib/luasocket/src/socket.lua @@ -0,0 +1,149 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") + +local _M = socket + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function _M.connect4(address, port, laddress, lport) + return socket.connect(address, port, laddress, lport, "inet") +end + +function _M.connect6(address, port, laddress, lport) + return socket.connect(address, port, laddress, lport, "inet6") +end + +function _M.bind(host, port, backlog) + if host == "*" then host = "0.0.0.0" end + local addrinfo, err = socket.dns.getaddrinfo(host); + if not addrinfo then return nil, err end + local sock, res + err = "no info on address" + for i, alt in base.ipairs(addrinfo) do + if alt.family == "inet" then + sock, err = socket.tcp4() + else + sock, err = socket.tcp6() + end + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + res, err = sock:bind(alt.addr, port) + if not res then + sock:close() + else + res, err = sock:listen(backlog) + if not res then + sock:close() + else + return sock + end + end + end + return nil, err +end + +_M.try = _M.newtry() + +function _M.choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +local sourcet, sinkt = {}, {} +_M.sourcet = sourcet +_M.sinkt = sinkt + +_M.BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +_M.sink = _M.choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +_M.source = _M.choose(sourcet) + +return _M diff --git a/contrib/luasocket/src/tcp.c b/contrib/luasocket/src/tcp.c new file mode 100644 index 00000000..ef9ee6f9 --- /dev/null +++ b/contrib/luasocket/src/tcp.c @@ -0,0 +1,451 @@ +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "socket.h" +#include "inet.h" +#include "options.h" +#include "tcp.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int global_create4(lua_State *L); +static int global_create6(lua_State *L); +static int global_connect(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_getfamily(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); +static int meth_getpeername(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_getoption(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); + +/* tcp object methods */ +static luaL_Reg tcp_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfamily", meth_getfamily}, + {"getfd", meth_getfd}, + {"getoption", meth_getoption}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optget[] = { + {"keepalive", opt_get_keepalive}, + {"reuseaddr", opt_get_reuseaddr}, + {"reuseport", opt_get_reuseport}, + {"tcp-nodelay", opt_get_tcp_nodelay}, + {"linger", opt_get_linger}, + {"error", opt_get_error}, + {NULL, NULL} +}; + +static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, + {"reuseport", opt_set_reuseport}, + {"tcp-nodelay", opt_set_tcp_nodelay}, + {"ipv6-v6only", opt_set_ip6_v6only}, + {"linger", opt_set_linger}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"tcp", global_create}, + {"tcp4", global_create4}, + {"tcp6", global_create6}, + {"connect", global_connect}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int tcp_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "tcp{master}", tcp_methods); + auxiliar_newclass(L, "tcp{client}", tcp_methods); + auxiliar_newclass(L, "tcp{server}", tcp_methods); + /* create class groups */ + auxiliar_add2group(L, "tcp{master}", "tcp{any}"); + auxiliar_add2group(L, "tcp{client}", "tcp{any}"); + auxiliar_add2group(L, "tcp{server}", "tcp{any}"); + /* define library functions */ + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_send(L, &tcp->buf); +} + +static int meth_receive(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_receive(L, &tcp->buf); +} + +static int meth_getstats(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_getstats(L, &tcp->buf); +} + +static int meth_setstats(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_setstats(L, &tcp->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_getoption(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return opt_meth_getoption(L, optget, &tcp->sock); +} + +static int meth_setoption(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return opt_meth_setoption(L, optset, &tcp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + lua_pushnumber(L, (int) tcp->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + tcp->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + lua_pushboolean(L, !buffer_isempty(&tcp->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) +{ + p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + const char *err = inet_tryaccept(&server->sock, server->family, &sock, tm); + /* if successful, push client socket */ + if (err == NULL) { + p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + auxiliar_setclass(L, "tcp{client}", -1); + /* initialize structure fields */ + memset(clnt, 0, sizeof(t_tcp)); + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + clnt->family = server->family; + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static int meth_bind(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + const char *err; + struct addrinfo bindhints; + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_STREAM; + bindhints.ai_family = tcp->family; + bindhints.ai_flags = AI_PASSIVE; + err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master tcp object into a client object. +\*-------------------------------------------------------------------------*/ +static int meth_connect(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + struct addrinfo connecthints; + const char *err; + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_STREAM; + /* make sure we try to connect only to the same family */ + connecthints.ai_family = tcp->family; + timeout_markstart(&tcp->tm); + err = inet_tryconnect(&tcp->sock, &tcp->family, address, port, + &tcp->tm, &connecthints); + /* have to set the class even if it failed due to non-blocking connects */ + auxiliar_setclass(L, "tcp{client}", 1); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + socket_destroy(&tcp->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Returns family as string +\*-------------------------------------------------------------------------*/ +static int meth_getfamily(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + if (tcp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; + } else if (tcp->family == AF_INET) { + lua_pushliteral(L, "inet4"); + return 1; + } else { + lua_pushliteral(L, "inet4"); + return 1; + } +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&tcp->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "tcp{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ + static const char* methods[] = { "receive", "send", "both", NULL }; + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + int how = luaL_checkoption(L, 2, "both", methods); + socket_shutdown(&tcp->sock, how); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call inet methods +\*-------------------------------------------------------------------------*/ +static int meth_getpeername(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return inet_meth_getpeername(L, &tcp->sock, tcp->family); +} + +static int meth_getsockname(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return inet_meth_getsockname(L, &tcp->sock, tcp->family); +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return timeout_meth_settimeout(L, &tcp->tm); +} + +static int meth_gettimeout(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return timeout_meth_gettimeout(L, &tcp->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master tcp object +\*-------------------------------------------------------------------------*/ +static int tcp_create(lua_State *L, int family) { + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + memset(tcp, 0, sizeof(t_tcp)); + /* set its type as master object */ + auxiliar_setclass(L, "tcp{master}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + tcp->sock = SOCKET_INVALID; + tcp->family = family; + io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &tcp->sock); + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + socket_setnonblocking(&tcp->sock); + } + return 1; +} + +static int global_create(lua_State *L) { + return tcp_create(L, AF_UNSPEC); +} + +static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); +} + +static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); +} + +static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); + const char *remoteserv = luaL_checkstring(L, 2); + const char *localaddr = luaL_optstring(L, 3, NULL); + const char *localserv = luaL_optstring(L, 4, "0"); + int family = inet_optfamily(L, 5, "unspec"); + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + struct addrinfo bindhints, connecthints; + const char *err = NULL; + /* initialize tcp structure */ + memset(tcp, 0, sizeof(t_tcp)); + io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &tcp->sock); + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + tcp->sock = SOCKET_INVALID; + tcp->family = AF_UNSPEC; + /* allow user to pick local address and port */ + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_STREAM; + bindhints.ai_family = family; + bindhints.ai_flags = AI_PASSIVE; + if (localaddr) { + err = inet_trybind(&tcp->sock, &tcp->family, localaddr, + localserv, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + } + /* try to connect to remote address and port */ + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_STREAM; + /* make sure we try to connect only to the same family */ + connecthints.ai_family = tcp->family; + err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, + &tcp->tm, &connecthints); + if (err) { + socket_destroy(&tcp->sock); + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + auxiliar_setclass(L, "tcp{client}", -1); + return 1; +} diff --git a/contrib/luasocket/src/tcp.h b/contrib/luasocket/src/tcp.h new file mode 100644 index 00000000..eded6203 --- /dev/null +++ b/contrib/luasocket/src/tcp.h @@ -0,0 +1,35 @@ +#ifndef TCP_H +#define TCP_H +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +* +* The tcp.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created tcp object, that has not been bound or connected. Server +* objects are tcp objects bound to some local address. Client objects are +* tcp objects either connected to some address or returned by the accept +* method of a server object. +\*=========================================================================*/ +#include "lua.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +typedef struct t_tcp_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; + int family; +} t_tcp; + +typedef t_tcp *p_tcp; + +int tcp_open(lua_State *L); + +#endif /* TCP_H */ diff --git a/contrib/luasocket/src/timeout.c b/contrib/luasocket/src/timeout.c new file mode 100644 index 00000000..5a601d54 --- /dev/null +++ b/contrib/luasocket/src/timeout.c @@ -0,0 +1,228 @@ +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "timeout.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int timeout_lua_gettime(lua_State *L); +static int timeout_lua_sleep(lua_State *L); + +static luaL_Reg func[] = { + { "gettime", timeout_lua_gettime }, + { "sleep", timeout_lua_sleep }, + { NULL, NULL } +}; + +/*=========================================================================*\ +* Exported functions. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initialize structure +\*-------------------------------------------------------------------------*/ +void timeout_init(p_timeout tm, double block, double total) { + tm->block = block; + tm->total = total; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was successful +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_get(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + return tm->block; + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Returns time since start of operation +* Input +* tm: timeout control structure +* Returns +* start field of structure +\*-------------------------------------------------------------------------*/ +double timeout_getstart(p_timeout tm) { + return tm->start; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was a failure +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_getretry(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + double t = tm->block - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in structure +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +p_timeout timeout_markstart(p_timeout tm) { + tm->start = timeout_gettime(); + return tm; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in s, relative to January 1, 1970 (UTC) +* Returns +* time in s. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +double timeout_gettime(void) { + FILETIME ft; + double t; + GetSystemTimeAsFileTime(&ft); + /* Windows file time (time since January 1, 1601 (UTC)) */ + t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); + /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ + return (t - 11644473600.0); +} +#else +double timeout_gettime(void) { + struct timeval v; + gettimeofday(&v, (struct timezone *) NULL); + /* Unix Epoch time (time since January 1, 1970 (UTC)) */ + return v.tv_sec + v.tv_usec/1.0e6; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int timeout_open(lua_State *L) { + luaL_setfuncs(L, func, 0); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Sets timeout values for IO operations +* Lua Input: base, time [, mode] +* time: time out value in seconds +* mode: "b" for block timeout, "t" for total timeout. (default: b) +\*-------------------------------------------------------------------------*/ +int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + double t = luaL_optnumber(L, 2, -1); + const char *mode = luaL_optstring(L, 3, "b"); + switch (*mode) { + case 'b': + tm->block = t; + break; + case 'r': case 't': + tm->total = t; + break; + default: + luaL_argcheck(L, 0, 3, "invalid timeout mode"); + break; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Gets timeout values for IO operations +* Lua Output: block, total +\*-------------------------------------------------------------------------*/ +int timeout_meth_gettimeout(lua_State *L, p_timeout tm) { + lua_pushnumber(L, tm->block); + lua_pushnumber(L, tm->total); + return 2; +} + +/*=========================================================================*\ +* Test support functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns the time the system has been up, in secconds. +\*-------------------------------------------------------------------------*/ +static int timeout_lua_gettime(lua_State *L) +{ + lua_pushnumber(L, timeout_gettime()); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sleep for n seconds. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + if (n < 0.0) n = 0.0; + if (n < DBL_MAX/1000.0) n *= 1000.0; + if (n > INT_MAX) n = INT_MAX; + Sleep((int)n); + return 0; +} +#else +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + struct timespec t, r; + if (n < 0.0) n = 0.0; + if (n > INT_MAX) n = INT_MAX; + t.tv_sec = (int) n; + n -= t.tv_sec; + t.tv_nsec = (int) (n * 1000000000); + if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; + while (nanosleep(&t, &r) != 0) { + t.tv_sec = r.tv_sec; + t.tv_nsec = r.tv_nsec; + } + return 0; +} +#endif diff --git a/contrib/luasocket/src/timeout.h b/contrib/luasocket/src/timeout.h new file mode 100644 index 00000000..af902318 --- /dev/null +++ b/contrib/luasocket/src/timeout.h @@ -0,0 +1,29 @@ +#ifndef TIMEOUT_H +#define TIMEOUT_H +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +\*=========================================================================*/ +#include "lua.h" + +/* timeout control structure */ +typedef struct t_timeout_ { + double block; /* maximum time for blocking calls */ + double total; /* total number of miliseconds for operation */ + double start; /* time of start of operation */ +} t_timeout; +typedef t_timeout *p_timeout; + +int timeout_open(lua_State *L); +void timeout_init(p_timeout tm, double block, double total); +double timeout_get(p_timeout tm); +double timeout_getretry(p_timeout tm); +p_timeout timeout_markstart(p_timeout tm); +double timeout_getstart(p_timeout tm); +double timeout_gettime(void); +int timeout_meth_settimeout(lua_State *L, p_timeout tm); +int timeout_meth_gettimeout(lua_State *L, p_timeout tm); + +#define timeout_iszero(tm) ((tm)->block == 0.0) + +#endif /* TIMEOUT_H */ diff --git a/contrib/luasocket/src/tp.lua b/contrib/luasocket/src/tp.lua new file mode 100644 index 00000000..b8ebc56d --- /dev/null +++ b/contrib/luasocket/src/tp.lua @@ -0,0 +1,134 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") + +socket.tp = {} +local _M = socket.tp + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +_M.TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- Implementation +----------------------------------------------------------------------------- +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive() + local reply = line + if err then return nil, err end + code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive() + if err then return nil, err end + current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + return code, reply +end + +-- metatable for sock object +local metat = { __index = {} } + +function metat.__index:getpeername() + return self.c:getpeername() +end + +function metat.__index:getsockname() + return self.c:getpeername() +end + +function metat.__index:check(ok) + local code, reply = get_reply(self.c) + if not code then return nil, reply end + if base.type(ok) ~= "function" then + if base.type(ok) == "table" then + for i, v in base.ipairs(ok) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + return nil, reply + else + if string.find(code, ok) then return base.tonumber(code), reply + else return nil, reply end + end + else return ok(base.tonumber(code), reply) end +end + +function metat.__index:command(cmd, arg) + cmd = string.upper(cmd) + if arg then + return self.c:send(cmd .. " " .. arg.. "\r\n") + else + return self.c:send(cmd .. "\r\n") + end +end + +function metat.__index:sink(snk, pat) + local chunk, err = self.c:receive(pat) + return snk(chunk, err) +end + +function metat.__index:send(data) + return self.c:send(data) +end + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + +function metat.__index:getfd() + return self.c:getfd() +end + +function metat.__index:dirty() + return self.c:dirty() +end + +function metat.__index:getcontrol() + return self.c +end + +function metat.__index:source(source, step) + local sink = socket.sink("keep-open", self.c) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err +end + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + +-- connect with server and return c object +function _M.connect(host, port, timeout, create) + local c, e = (create or socket.tcp)() + if not c then return nil, e end + c:settimeout(timeout or _M.TIMEOUT) + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + return base.setmetatable({c = c}, metat) +end + +return _M diff --git a/contrib/luasocket/src/udp.c b/contrib/luasocket/src/udp.c new file mode 100644 index 00000000..605c1950 --- /dev/null +++ b/contrib/luasocket/src/udp.c @@ -0,0 +1,483 @@ +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "socket.h" +#include "inet.h" +#include "options.h" +#include "udp.h" + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int global_create4(lua_State *L); +static int global_create6(lua_State *L); +static int meth_send(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_getfamily(lua_State *L); +static int meth_getsockname(lua_State *L); +static int meth_getpeername(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_setsockname(lua_State *L); +static int meth_setpeername(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_getoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); + +/* udp object methods */ +static luaL_Reg udp_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfamily", meth_getfamily}, + {"getfd", meth_getfd}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"getoption", meth_getoption}, + {"setpeername", meth_setpeername}, + {"setsockname", meth_setsockname}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {NULL, NULL} +}; + +/* socket options for setoption */ +static t_opt optset[] = { + {"dontroute", opt_set_dontroute}, + {"broadcast", opt_set_broadcast}, + {"reuseaddr", opt_set_reuseaddr}, + {"reuseport", opt_set_reuseport}, + {"ip-multicast-if", opt_set_ip_multicast_if}, + {"ip-multicast-ttl", opt_set_ip_multicast_ttl}, + {"ip-multicast-loop", opt_set_ip_multicast_loop}, + {"ip-add-membership", opt_set_ip_add_membership}, + {"ip-drop-membership", opt_set_ip_drop_membersip}, + {"ipv6-unicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_set_ip6_multicast_loop}, + {"ipv6-add-membership", opt_set_ip6_add_membership}, + {"ipv6-drop-membership", opt_set_ip6_drop_membersip}, + {"ipv6-v6only", opt_set_ip6_v6only}, + {NULL, NULL} +}; + +/* socket options for getoption */ +static t_opt optget[] = { + {"dontroute", opt_get_dontroute}, + {"broadcast", opt_get_broadcast}, + {"reuseaddr", opt_get_reuseaddr}, + {"reuseport", opt_get_reuseport}, + {"ip-multicast-if", opt_get_ip_multicast_if}, + {"ip-multicast-loop", opt_get_ip_multicast_loop}, + {"error", opt_get_error}, + {"ipv6-unicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_get_ip6_multicast_loop}, + {"ipv6-v6only", opt_get_ip6_v6only}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"udp", global_create}, + {"udp4", global_create4}, + {"udp6", global_create6}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int udp_open(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "udp{connected}", udp_methods); + auxiliar_newclass(L, "udp{unconnected}", udp_methods); + /* create class groups */ + auxiliar_add2group(L, "udp{connected}", "udp{any}"); + auxiliar_add2group(L, "udp{unconnected}", "udp{any}"); + auxiliar_add2group(L, "udp{connected}", "select{able}"); + auxiliar_add2group(L, "udp{unconnected}", "select{able}"); + /* define library functions */ + luaL_setfuncs(L, func, 0); + /* export default UDP size */ + lua_pushliteral(L, "_DATAGRAMSIZE"); + lua_pushinteger(L, UDP_DATAGRAMSIZE); + lua_rawset(L, -3); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +static const char *udp_strerror(int err) { + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +/*-------------------------------------------------------------------------*\ +* Send data through connected udp socket +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1); + p_timeout tm = &udp->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&udp->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected udp socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *ip = luaL_checkstring(L, 3); + const char *port = luaL_checkstring(L, 4); + p_timeout tm = &udp->tm; + int err; + struct addrinfo aihint; + struct addrinfo *ai; + memset(&aihint, 0, sizeof(aihint)); + aihint.ai_family = udp->family; + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + err = getaddrinfo(ip, port, &aihint, &ai); + if (err) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + return 2; + } + + /* create socket if on first sendto if AF_UNSPEC was set */ + if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) { + struct addrinfo *ap; + const char *errstr = NULL; + for (ap = ai; ap != NULL; ap = ap->ai_next) { + errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0); + if (errstr == NULL) { + socket_setnonblocking(&udp->sock); + udp->family = ap->ai_family; + break; + } + } + if (errstr != NULL) { + lua_pushnil(L); + lua_pushstring(L, errstr); + freeaddrinfo(ai); + return 2; + } + } + + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, + (socklen_t) ai->ai_addrlen, tm); + freeaddrinfo(ai); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receive(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + char buf[UDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &udp->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recv(&udp->sock, dgram, wanted, &got, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + if (wanted > sizeof(buf)) free(dgram); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + char buf[UDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; + int err; + p_timeout tm = &udp->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + lua_pushstring(L, addrstr); + lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); + if (wanted > sizeof(buf)) free(dgram); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* Returns family as string +\*-------------------------------------------------------------------------*/ +static int meth_getfamily(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + if (udp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; + } else { + lua_pushliteral(L, "inet4"); + return 1; + } +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + lua_pushnumber(L, (int) udp->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + udp->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + (void) udp; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call inet methods +\*-------------------------------------------------------------------------*/ +static int meth_getpeername(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1); + return inet_meth_getpeername(L, &udp->sock, udp->family); +} + +static int meth_getsockname(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return inet_meth_getsockname(L, &udp->sock, udp->family); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return opt_meth_setoption(L, optset, &udp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_getoption(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return opt_meth_getoption(L, optget, &udp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return timeout_meth_settimeout(L, &udp->tm); +} + +static int meth_gettimeout(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return timeout_meth_gettimeout(L, &udp->tm); +} + +/*-------------------------------------------------------------------------*\ +* Turns a master udp object into a client object. +\*-------------------------------------------------------------------------*/ +static int meth_setpeername(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + p_timeout tm = &udp->tm; + const char *address = luaL_checkstring(L, 2); + int connecting = strcmp(address, "*"); + const char *port = connecting? luaL_checkstring(L, 3): "0"; + struct addrinfo connecthints; + const char *err; + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_DGRAM; + /* make sure we try to connect only to the same family */ + connecthints.ai_family = udp->family; + if (connecting) { + err = inet_tryconnect(&udp->sock, &udp->family, address, + port, tm, &connecthints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + auxiliar_setclass(L, "udp{connected}", 1); + } else { + /* we ignore possible errors because Mac OS X always + * returns EAFNOSUPPORT */ + inet_trydisconnect(&udp->sock, udp->family, tm); + auxiliar_setclass(L, "udp{unconnected}", 1); + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + socket_destroy(&udp->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master object into a server object +\*-------------------------------------------------------------------------*/ +static int meth_setsockname(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + const char *err; + struct addrinfo bindhints; + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_DGRAM; + bindhints.ai_family = udp->family; + bindhints.ai_flags = AI_PASSIVE; + err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master udp object +\*-------------------------------------------------------------------------*/ +static int udp_create(lua_State *L, int family) { + /* allocate udp object */ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + auxiliar_setclass(L, "udp{unconnected}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + udp->sock = SOCKET_INVALID; + timeout_init(&udp->tm, -1, -1); + udp->family = family; + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + socket_setnonblocking(&udp->sock); + } + return 1; +} + +static int global_create(lua_State *L) { + return udp_create(L, AF_UNSPEC); +} + +static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); +} + +static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); +} diff --git a/contrib/luasocket/src/udp.h b/contrib/luasocket/src/udp.h new file mode 100644 index 00000000..be9b6a55 --- /dev/null +++ b/contrib/luasocket/src/udp.h @@ -0,0 +1,31 @@ +#ifndef UDP_H +#define UDP_H +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +* +* The udp.h module provides LuaSocket with support for UDP protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. UDP objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +\*=========================================================================*/ +#include "lua.h" + +#include "timeout.h" +#include "socket.h" + +#define UDP_DATAGRAMSIZE 8192 + +typedef struct t_udp_ { + t_socket sock; + t_timeout tm; + int family; +} t_udp; +typedef t_udp *p_udp; + +int udp_open(lua_State *L); + +#endif /* UDP_H */ diff --git a/contrib/luasocket/src/unix.c b/contrib/luasocket/src/unix.c new file mode 100644 index 00000000..dbc87100 --- /dev/null +++ b/contrib/luasocket/src/unix.c @@ -0,0 +1,70 @@ +/*=========================================================================*\ +* Unix domain socket +* LuaSocket toolkit +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" + +#include "unixstream.h" +#include "unixdgram.h" + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"stream", unixstream_open}, + {"dgram", unixdgram_open}, + {NULL, NULL} +}; + +static void add_alias(lua_State *L, int index, const char *name, const char *target) +{ + lua_getfield(L, index, target); + lua_setfield(L, index, name); +} + +static int compat_socket_unix_call(lua_State *L) +{ + /* Look up socket.unix.stream in the socket.unix table (which is the first + * argument). */ + lua_getfield(L, 1, "stream"); + + /* Replace the stack entry for the socket.unix table with the + * socket.unix.stream function. */ + lua_replace(L, 1); + + /* Call socket.unix.stream, passing along any arguments. */ + int n = lua_gettop(L); + lua_call(L, n-1, LUA_MULTRET); + + /* Pass along the return values from socket.unix.stream. */ + n = lua_gettop(L); + return n; +} + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int luaopen_socket_unix(lua_State *L) +{ + int i; + lua_newtable(L); + int socket_unix_table = lua_gettop(L); + + for (i = 0; mod[i].name; i++) + mod[i].func(L); + + /* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and + * "dgram" functions. */ + add_alias(L, socket_unix_table, "tcp", "stream"); + add_alias(L, socket_unix_table, "udp", "dgram"); + + /* Add a backwards compatibility function and a metatable setup to call it + * for the old socket.unix() interface. */ + lua_pushcfunction(L, compat_socket_unix_call); + lua_setfield(L, socket_unix_table, "__call"); + lua_pushvalue(L, socket_unix_table); + lua_setmetatable(L, socket_unix_table); + + return 1; +} diff --git a/contrib/luasocket/src/unix.h b/contrib/luasocket/src/unix.h new file mode 100644 index 00000000..8cc7a793 --- /dev/null +++ b/contrib/luasocket/src/unix.h @@ -0,0 +1,30 @@ +#ifndef UNIX_H +#define UNIX_H +/*=========================================================================*\ +* Unix domain object +* LuaSocket toolkit +* +* This module is just an example of how to extend LuaSocket with a new +* domain. +\*=========================================================================*/ +#include "lua.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +#ifndef UNIX_API +#define UNIX_API extern +#endif + +typedef struct t_unix_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; +} t_unix; +typedef t_unix *p_unix; + +UNIX_API int luaopen_socket_unix(lua_State *L); + +#endif /* UNIX_H */ diff --git a/contrib/luasocket/src/unixdgram.c b/contrib/luasocket/src/unixdgram.c new file mode 100644 index 00000000..4645892f --- /dev/null +++ b/contrib/luasocket/src/unixdgram.c @@ -0,0 +1,408 @@ +/*=========================================================================*\ +* Unix domain socket dgram submodule +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" +#include + +#define UNIXDGRAM_DATAGRAMSIZE 8192 + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixdgram_tryconnect(p_unix un, const char *path); +static const char *unixdgram_trybind(p_unix un, const char *path); + +/* unixdgram object methods */ +static luaL_Reg unixdgram_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"reuseaddr", opt_set_reuseaddr}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"dgram", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixdgram_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods); + auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods); + /* create class groups */ + auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}"); + auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}"); + auxiliar_add2group(L, "unixdgram{connected}", "select{able}"); + auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +static const char *unixdgram_strerror(int err) +{ + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +static int meth_send(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1); + p_timeout tm = &un->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&un->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected unixdgram socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *path = luaL_checkstring(L, 3); + p_timeout tm = &un->tm; + int err; + struct sockaddr_un remote; + size_t len = strlen(path); + + if (len >= sizeof(remote.sun_path)) { + lua_pushnil(L); + lua_pushstring(L, "path too long"); + return 2; + } + + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); +#else + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, + sizeof(remote.sun_family) + len, tm); +#endif + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + char buf[UNIXDGRAM_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recv(&un->sock, dgram, wanted, &got, tm); + /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + if (wanted > sizeof(buf)) free(dgram); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a DGRAM socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + char buf[UNIXDGRAM_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_un addr; + socklen_t addr_len = sizeof(addr); + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + addr.sun_path[0] = '\0'; + err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + + lua_pushlstring(L, dgram, got); + /* the path may be empty, when client send without bind */ + lua_pushstring(L, addr.sun_path); + if (wanted > sizeof(buf)) free(dgram); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + (void) un; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixdgram_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixdgram_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixdgram object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixdgram_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixdgram_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn unconnected object into a connected object */ + auxiliar_setclass(L, "unixdgram{connected}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +static int meth_gettimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return timeout_meth_gettimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixdgram object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) +{ + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixdgram object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixdgram{unconnected}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/contrib/luasocket/src/unixdgram.h b/contrib/luasocket/src/unixdgram.h new file mode 100644 index 00000000..7187966d --- /dev/null +++ b/contrib/luasocket/src/unixdgram.h @@ -0,0 +1,20 @@ +#ifndef UNIXDGRAM_H +#define UNIXDGRAM_H +/*=========================================================================*\ +* DGRAM object +* LuaSocket toolkit +* +* The dgram.h module provides LuaSocket with support for DGRAM protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. DGRAM objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +\*=========================================================================*/ + +#include "unix.h" + +int unixdgram_open(lua_State *L); + +#endif /* UNIXDGRAM_H */ diff --git a/contrib/luasocket/src/unixstream.c b/contrib/luasocket/src/unixstream.c new file mode 100644 index 00000000..0b9055cc --- /dev/null +++ b/contrib/luasocket/src/unixstream.c @@ -0,0 +1,357 @@ +/*=========================================================================*\ +* Unix domain socket stream sub module +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unixstream.h" +#include + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixstream_tryconnect(p_unix un, const char *path); +static const char *unixstream_trybind(p_unix un, const char *path); + +/* unixstream object methods */ +static luaL_Reg unixstream_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, + {"linger", opt_set_linger}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"stream", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixstream_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixstream{master}", unixstream_methods); + auxiliar_newclass(L, "unixstream{client}", unixstream_methods); + auxiliar_newclass(L, "unixstream{server}", unixstream_methods); + + /* create class groups */ + auxiliar_add2group(L, "unixstream{master}", "unixstream{any}"); + auxiliar_add2group(L, "unixstream{client}", "unixstream{any}"); + auxiliar_add2group(L, "unixstream{server}", "unixstream{any}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) { + p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); + /* if successful, push client socket */ + if (err == IO_DONE) { + p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + auxiliar_setclass(L, "unixstream{client}", -1); + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixstream_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixstream_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixstream object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixstream_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixstream_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn master object into a client object */ + auxiliar_setclass(L, "unixstream{client}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&un->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "unixstream{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ + static const char* methods[] = { "receive", "send", "both", NULL }; + p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + int how = luaL_checkoption(L, 2, "both", methods); + socket_shutdown(&stream->sock, how); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixstream object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixstream object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixstream{master}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/contrib/luasocket/src/unixstream.h b/contrib/luasocket/src/unixstream.h new file mode 100644 index 00000000..ef1d071e --- /dev/null +++ b/contrib/luasocket/src/unixstream.h @@ -0,0 +1,21 @@ +#ifndef UNIXSTREAM_H +#define UNIXSTREAM_H +/*=========================================================================*\ +* UNIX STREAM object +* LuaSocket toolkit +* +* The unixstream.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created unixstream object, that has not been bound or connected. Server +* objects are unixstream objects bound to some local address. Client objects are +* unixstream objects either connected to some address or returned by the accept +* method of a server object. +\*=========================================================================*/ +#include "unix.h" + +int unixstream_open(lua_State *L); + +#endif /* UNIXSTREAM_H */ diff --git a/contrib/luasocket/src/url.lua b/contrib/luasocket/src/url.lua new file mode 100644 index 00000000..110ea940 --- /dev/null +++ b/contrib/luasocket/src/url.lua @@ -0,0 +1,309 @@ +----------------------------------------------------------------------------- +-- URI parsing, composition and relative URL resolution +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local base = _G +local table = require("table") +local socket = require("socket") + +socket.url = {} +local _M = socket.url + +----------------------------------------------------------------------------- +-- Module version +----------------------------------------------------------------------------- +_M._VERSION = "URL 1.0.3" + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function _M.escape(s) + return (string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end)) +end + +----------------------------------------------------------------------------- +-- Protects a path segment, to prevent it from interfering with the +-- url parsing. +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +local function make_set(t) + local s = {} + for i,v in base.ipairs(t) do + s[t[i]] = 1 + end + return s +end + +-- these are allowed within a path segment, along with alphanum +-- other characters must be escaped +local segment_set = make_set { + "-", "_", ".", "!", "~", "*", "'", "(", + ")", ":", "@", "&", "=", "+", "$", ",", +} + +local function protect_segment(s) + return string.gsub(s, "([^A-Za-z0-9_])", function (c) + if segment_set[c] then return c + else return string.format("%%%02X", string.byte(c)) end + end) +end + +----------------------------------------------------------------------------- +-- Unencodes a escaped hexadecimal string into its binary representation +-- Input +-- s: escaped hexadecimal string to be unencoded +-- Returns +-- unescaped binary representation of escaped hexadecimal binary +----------------------------------------------------------------------------- +function _M.unescape(s) + return (string.gsub(s, "%%(%x%x)", function(hex) + return string.char(base.tonumber(hex, 16)) + end)) +end + +----------------------------------------------------------------------------- +-- Builds a path from a base path and a relative path +-- Input +-- base_path +-- relative_path +-- Returns +-- corresponding absolute path +----------------------------------------------------------------------------- +local function absolute_path(base_path, relative_path) + if string.sub(relative_path, 1, 1) == "/" then return relative_path end + local path = string.gsub(base_path, "[^/]*$", "") + path = path .. relative_path + path = string.gsub(path, "([^/]*%./)", function (s) + if s ~= "./" then return s else return "" end + end) + path = string.gsub(path, "/%.$", "/") + local reduced + while reduced ~= path do + reduced = path + path = string.gsub(reduced, "([^/]*/%.%./)", function (s) + if s ~= "../../" then return "" else return s end + end) + end + path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) + if s ~= "../.." then return "" else return s end + end) + return path +end + +----------------------------------------------------------------------------- +-- Parses a url and returns a table with all its parts according to RFC 2396 +-- The following grammar describes the names given to the URL parts +-- ::= :///;?# +-- ::= @: +-- ::= [:] +-- :: = {/} +-- Input +-- url: uniform resource locator of request +-- default: table with default values for each field +-- Returns +-- table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, user, password, host, port, +-- path, params, query, fragment +-- Obs: +-- the leading '/' in {/} is considered part of +----------------------------------------------------------------------------- +function _M.parse(url, default) + -- initialize default parameters + local parsed = {} + for i,v in base.pairs(default or parsed) do parsed[i] = v end + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + -- url = string.gsub(url, "%s", "") + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + -- get authority + url = string.gsub(url, "^//([^/]*)", function(n) + parsed.authority = n + return "" + end) + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get query string + url = string.gsub(url, "%?(.*)", function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.path = url end + local authority = parsed.authority + if not authority then return parsed end + authority = string.gsub(authority,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + authority = string.gsub(authority, ":([^:%]]*)$", + function(p) parsed.port = p; return "" end) + if authority ~= "" then + -- IPv6? + parsed.host = string.match(authority, "^%[(.+)%]$") or authority + end + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +----------------------------------------------------------------------------- +-- Rebuilds a parsed URL from its components. +-- Components are protected if any reserved or unallowed characters are found +-- Input +-- parsed: parsed URL, as returned by parse +-- Returns +-- a stringing with the corresponding URL +----------------------------------------------------------------------------- +function _M.build(parsed) + --local ppath = _M.parse_path(parsed.path or "") + --local url = _M.build_path(ppath) + local url = parsed.path or "" + if parsed.params then url = url .. ";" .. parsed.params end + if parsed.query then url = url .. "?" .. parsed.query end + local authority = parsed.authority + if parsed.host then + authority = parsed.host + if string.find(authority, ":") then -- IPv6? + authority = "[" .. authority .. "]" + end + if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end + local userinfo = parsed.userinfo + if parsed.user then + userinfo = parsed.user + if parsed.password then + userinfo = userinfo .. ":" .. parsed.password + end + end + if userinfo then authority = userinfo .. "@" .. authority end + end + if authority then url = "//" .. authority .. url end + if parsed.scheme then url = parsed.scheme .. ":" .. url end + if parsed.fragment then url = url .. "#" .. parsed.fragment end + -- url = string.gsub(url, "%s", "") + return url +end + +----------------------------------------------------------------------------- +-- Builds a absolute URL from a base and a relative URL according to RFC 2396 +-- Input +-- base_url +-- relative_url +-- Returns +-- corresponding absolute url +----------------------------------------------------------------------------- +function _M.absolute(base_url, relative_url) + local base_parsed + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = _M.build(base_parsed) + else + base_parsed = _M.parse(base_url) + end + local relative_parsed = _M.parse(relative_url) + if not base_parsed then return relative_url + elseif not relative_parsed then return base_url + elseif relative_parsed.scheme then return relative_url + else + relative_parsed.scheme = base_parsed.scheme + if not relative_parsed.authority then + relative_parsed.authority = base_parsed.authority + if not relative_parsed.path then + relative_parsed.path = base_parsed.path + if not relative_parsed.params then + relative_parsed.params = base_parsed.params + if not relative_parsed.query then + relative_parsed.query = base_parsed.query + end + end + else + relative_parsed.path = absolute_path(base_parsed.path or "", + relative_parsed.path) + end + end + return _M.build(relative_parsed) + end +end + +----------------------------------------------------------------------------- +-- Breaks a path into its segments, unescaping the segments +-- Input +-- path +-- Returns +-- segment: a table with one entry per segment +----------------------------------------------------------------------------- +function _M.parse_path(path) + local parsed = {} + path = path or "" + --path = string.gsub(path, "%s", "") + string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) + for i = 1, #parsed do + parsed[i] = _M.unescape(parsed[i]) + end + if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end + if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end + return parsed +end + +----------------------------------------------------------------------------- +-- Builds a path component from its segments, escaping protected characters. +-- Input +-- parsed: path segments +-- unsafe: if true, segments are not protected before path is built +-- Returns +-- path: corresponding path stringing +----------------------------------------------------------------------------- +function _M.build_path(parsed, unsafe) + local path = "" + local n = #parsed + if unsafe then + for i = 1, n-1 do + path = path .. parsed[i] + path = path .. "/" + end + if n > 0 then + path = path .. parsed[n] + if parsed.is_directory then path = path .. "/" end + end + else + for i = 1, n-1 do + path = path .. protect_segment(parsed[i]) + path = path .. "/" + end + if n > 0 then + path = path .. protect_segment(parsed[n]) + if parsed.is_directory then path = path .. "/" end + end + end + if parsed.is_absolute then path = "/" .. path end + return path +end + +return _M diff --git a/contrib/luasocket/src/usocket.c b/contrib/luasocket/src/usocket.c new file mode 100644 index 00000000..6e7f8f67 --- /dev/null +++ b/contrib/luasocket/src/usocket.c @@ -0,0 +1,451 @@ +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +* +* The code is now interrupt-safe. +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include +#include + +#include "socket.h" +#include "pierror.h" + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#ifndef SOCKET_SELECT +#include + +#define WAITFD_R POLLIN +#define WAITFD_W POLLOUT +#define WAITFD_C (POLLIN|POLLOUT) +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + struct pollfd pfd; + pfd.fd = *ps; + pfd.events = sw; + pfd.revents = 0; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + int t = (int)(timeout_getretry(tm)*1e3); + ret = poll(&pfd, 1, t >= 0? t: -1); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED; + return IO_DONE; +} +#else + +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_C (WAITFD_R|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, *rp, *wp; + struct timeval tv, *tp; + double t; + if (*ps >= FD_SETSIZE) return EINVAL; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + /* must set bits within loop, because select may have modifed them */ + rp = wp = NULL; + if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + t = timeout_getretry(tm); + tp = NULL; + if (t >= 0.0) { + tv.tv_sec = (int)t; + tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(*ps+1, rp, wp, NULL, tp); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED; + return IO_DONE; +} +#endif + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + /* installs a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + close(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* Select with timeout control +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + int ret; + do { + struct timeval tv; + double t = timeout_getretry(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + /* timeout = 0 means no wait */ + ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return errno; +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + if (listen(*ps, backlog)) err = errno; + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + shutdown(*ps, how); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* avoid calling on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* call connect until done or failed without being interrupted */ + do if (connect(*ps, addr, len) == 0) return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ + if (err != EINPROGRESS && err != EAGAIN) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE; + else return errno; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + err = errno; + if (err == EINTR) continue; + if (err != EAGAIN && err != ECONNABORTED) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) send(*ps, data, count, 0); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ + if (err == EPROTOTYPE) continue; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + if (err == EPIPE) return IO_CLOSED; + if (err == EPROTOTYPE) continue; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recv(*ps, data, count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recvfrom(*ps, data, count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + + +/*-------------------------------------------------------------------------*\ +* Write with timeout +* +* socket_read and socket_write are cut-n-paste of socket_send and socket_recv, +* with send/recv replaced with write/read. We can't just use write/read +* in the socket version, because behaviour when size is zero is different. +\*-------------------------------------------------------------------------*/ +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) write(*ps, data, count); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ + if (err == EPROTOTYPE) continue; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Read with timeout +* See note for socket_write +\*-------------------------------------------------------------------------*/ +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) read(*ps, data, count); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +* Make sure important error messages are standard +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; + default: return hstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case EADDRINUSE: return PIE_ADDRINUSE; + case EISCONN: return PIE_ISCONN; + case EACCES: return PIE_ACCESS; + case ECONNREFUSED: return PIE_CONNREFUSED; + case ECONNABORTED: return PIE_CONNABORTED; + case ECONNRESET: return PIE_CONNRESET; + case ETIMEDOUT: return PIE_TIMEDOUT; + default: { + return strerror(err); + } + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; +#endif + case EAI_FAIL: return PIE_FAIL; + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; + case EAI_OVERFLOW: return PIE_OVERFLOW; +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; +#endif + case EAI_SERVICE: return PIE_SERVICE; + case EAI_SOCKTYPE: return PIE_SOCKTYPE; + case EAI_SYSTEM: return strerror(errno); + default: return gai_strerror(err); + } +} + diff --git a/contrib/luasocket/src/usocket.h b/contrib/luasocket/src/usocket.h new file mode 100644 index 00000000..45f2f99f --- /dev/null +++ b/contrib/luasocket/src/usocket.h @@ -0,0 +1,59 @@ +#ifndef USOCKET_H +#define USOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* BSD include files +\*=========================================================================*/ +/* error codes */ +#include +/* close function */ +#include +/* fnctnl function and associated constants */ +#include +/* struct sockaddr */ +#include +/* socket function */ +#include +/* struct timeval */ +#include +/* gethostbyname and gethostbyaddr functions */ +#include +/* sigpipe handling */ +#include +/* IP stuff*/ +#include +#include +/* TCP options (nagle algorithm disable) */ +#include +#include + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +/* Some platforms use IPV6_JOIN_GROUP instead if + * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* !IPV6_ADD_MEMBERSHIP */ + +/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */ +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif /* IPV6_LEAVE_GROUP */ +#endif /* !IPV6_DROP_MEMBERSHIP */ + +typedef int t_socket; +typedef t_socket *p_socket; +typedef struct sockaddr_storage t_sockaddr_storage; + +#define SOCKET_INVALID (-1) + +#endif /* USOCKET_H */ diff --git a/contrib/luasocket/src/wsocket.c b/contrib/luasocket/src/wsocket.c new file mode 100644 index 00000000..8ecb0fc7 --- /dev/null +++ b/contrib/luasocket/src/wsocket.c @@ -0,0 +1,433 @@ +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include + +#include "socket.h" +#include "pierror.h" + +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err); + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + WSACleanup(); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } + if ((t = timeout_get(tm)) >= 0.0) { + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) return WSAGetLastError(); + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; + return IO_DONE; +} + +/*-------------------------------------------------------------------------*\ +* Select with int timeout in ms +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + if (n <= 0) { + Sleep((DWORD) (1000*t)); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int len = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0? err: IO_UNKNOWN; + } else return err; + +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +* this can take an awful lot of time and we will end up blocked. +* Therefore, whoever calls this function should not pass a huge buffer. +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + /* try to send something */ + int put = send(*ps, data, (int) count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int put = sendto(*ps, data, (int) count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recv(*ps, data, (int) count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recvfrom(*ps, data, (int) count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; + default: return wstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return PIE_ADDRINUSE; + case WSAECONNREFUSED : return PIE_CONNREFUSED; + case WSAEISCONN: return PIE_ISCONN; + case WSAEACCES: return PIE_ACCESS; + case WSAECONNABORTED: return PIE_CONNABORTED; + case WSAECONNRESET: return PIE_CONNRESET; + case WSAETIMEDOUT: return PIE_TIMEDOUT; + default: return wstrerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; + case WSAEACCES: return PIE_ACCESS; // "Permission denied"; + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; + case WSAEINPROGRESS: return "Operation now in progress"; + case WSAEALREADY: return "Operation already in progress"; + case WSAENOTSOCK: return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: return "Destination address required"; + case WSAEMSGSIZE: return "Message too long"; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; // "Socket type not supported"; + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; + case WSAEAFNOSUPPORT: return PIE_FAMILY; // "Address family not supported by protocol family"; + case WSAEADDRINUSE: return PIE_ADDRINUSE; // "Address already in use"; + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNRESET: return PIE_CONNRESET; // "Connection reset by peer"; + case WSAENOBUFS: return "No buffer space available"; + case WSAEISCONN: return PIE_ISCONN; // "Socket is already connected"; + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: return PIE_TIMEDOUT; // "Connection timed out"; + case WSAECONNREFUSED: return PIE_CONNREFUSED; // "Connection refused"; + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; // "Host not found"; + case WSATRY_AGAIN: return "Nonauthoritative host not found"; + case WSANO_RECOVERY: return PIE_FAIL; // "Nonrecoverable name lookup error"; + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; +#endif + case EAI_FAIL: return PIE_FAIL; + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return PIE_OVERFLOW; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; +#endif + case EAI_SERVICE: return PIE_SERVICE; + case EAI_SOCKTYPE: return PIE_SOCKTYPE; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: return strerror(errno); +#endif + default: return gai_strerror(err); + } +} + diff --git a/contrib/luasocket/src/wsocket.h b/contrib/luasocket/src/wsocket.h new file mode 100644 index 00000000..39866402 --- /dev/null +++ b/contrib/luasocket/src/wsocket.h @@ -0,0 +1,33 @@ +#ifndef WSOCKET_H +#define WSOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* WinSock include files +\*=========================================================================*/ +#include +#include + +typedef int socklen_t; +typedef SOCKADDR_STORAGE t_sockaddr_storage; +typedef SOCKET t_socket; +typedef t_socket *p_socket; + +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + +#define SOCKET_INVALID (INVALID_SOCKET) + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV (0) +#endif + +#endif /* WSOCKET_H */ diff --git a/contrib/luasocket/test/README b/contrib/luasocket/test/README new file mode 100644 index 00000000..27837e04 --- /dev/null +++ b/contrib/luasocket/test/README @@ -0,0 +1,14 @@ +This provides the automated test scripts used to make sure the library +is working properly. + +The files provided are: + + testsrvr.lua -- test server + testclnt.lua -- test client + +To run these tests, just run lua on the server and then on the client. + + hello.lua -- run to verify if installation worked + +Good luck, +Diego. diff --git a/contrib/luasocket/test/auth/.htaccess b/contrib/luasocket/test/auth/.htaccess new file mode 100644 index 00000000..2509ae36 --- /dev/null +++ b/contrib/luasocket/test/auth/.htaccess @@ -0,0 +1,4 @@ +AuthName "test-auth" + AuthType Basic + AuthUserFile /home/diego/impa/luasocket/test/auth/.htpasswd + Require valid-user diff --git a/contrib/luasocket/test/auth/.htpasswd b/contrib/luasocket/test/auth/.htpasswd new file mode 100644 index 00000000..cfb26034 --- /dev/null +++ b/contrib/luasocket/test/auth/.htpasswd @@ -0,0 +1 @@ +luasocket:$apr1$47u2O.Me$.m/5BWAtt7GVoxsouIPBR1 diff --git a/contrib/luasocket/test/auth/index.html b/contrib/luasocket/test/auth/index.html new file mode 100644 index 00000000..786694ef --- /dev/null +++ b/contrib/luasocket/test/auth/index.html @@ -0,0 +1,3002 @@ + + +This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+ + diff --git a/contrib/luasocket/test/cgi/cat b/contrib/luasocket/test/cgi/cat new file mode 100644 index 00000000..8d41255e --- /dev/null +++ b/contrib/luasocket/test/cgi/cat @@ -0,0 +1,6 @@ +#!/bin/sh +echo Content-type: text/plain +echo + +cat > /tmp/luasocket.cat.tmp +cat /tmp/luasocket.cat.tmp diff --git a/contrib/luasocket/test/cgi/cat-index-html b/contrib/luasocket/test/cgi/cat-index-html new file mode 100644 index 00000000..7595043b --- /dev/null +++ b/contrib/luasocket/test/cgi/cat-index-html @@ -0,0 +1,5 @@ +#!/bin/sh +echo Content-type: text/plain +echo + +cat ../index.html diff --git a/contrib/luasocket/test/cgi/env b/contrib/luasocket/test/cgi/env new file mode 100644 index 00000000..412a716e --- /dev/null +++ b/contrib/luasocket/test/cgi/env @@ -0,0 +1,5 @@ +#!/bin/sh +echo Content-type: text/plain +echo + +env diff --git a/contrib/luasocket/test/cgi/query-string b/contrib/luasocket/test/cgi/query-string new file mode 100644 index 00000000..2342af53 --- /dev/null +++ b/contrib/luasocket/test/cgi/query-string @@ -0,0 +1,4 @@ +#!/bin/sh +echo Content-type: text/plain +echo +echo $QUERY_STRING diff --git a/contrib/luasocket/test/cgi/redirect-loop b/contrib/luasocket/test/cgi/redirect-loop new file mode 100644 index 00000000..bd32f204 --- /dev/null +++ b/contrib/luasocket/test/cgi/redirect-loop @@ -0,0 +1,3 @@ +#!/bin/sh +echo Location: http://$HTTP_HOST$REQUEST_URI +echo diff --git a/contrib/luasocket/test/cgi/request-uri b/contrib/luasocket/test/cgi/request-uri new file mode 100644 index 00000000..20ebe9fe --- /dev/null +++ b/contrib/luasocket/test/cgi/request-uri @@ -0,0 +1,4 @@ +#!/bin/sh +echo Content-type: text/plain +echo +echo $REQUEST_URI diff --git a/contrib/luasocket/test/dicttest.lua b/contrib/luasocket/test/dicttest.lua new file mode 100644 index 00000000..a0da2e18 --- /dev/null +++ b/contrib/luasocket/test/dicttest.lua @@ -0,0 +1,5 @@ +local dict = require"socket.dict" + +print(dict.get("dict://localhost/d:teste")) + +for i,v in pairs(dict.get("dict://localhost/d:teste")) do print(v) end diff --git a/contrib/luasocket/test/excepttest.lua b/contrib/luasocket/test/excepttest.lua new file mode 100644 index 00000000..80c9cb80 --- /dev/null +++ b/contrib/luasocket/test/excepttest.lua @@ -0,0 +1,30 @@ +local socket = require("socket") + +local finalizer_called + +local func = socket.protect(function(err, ...) + local try = socket.newtry(function() + finalizer_called = true + end) + + if err then + return error(err, 0) + else + return try(...) + end +end) + +local ret1, ret2, ret3 = func(false, 1, 2, 3) +assert(not finalizer_called, "unexpected finalizer call") +assert(ret1 == 1 and ret2 == 2 and ret3 == 3, "incorrect return values") + +ret1, ret2, ret3 = func(false, false, "error message") +assert(finalizer_called, "finalizer not called") +assert(ret1 == nil and ret2 == "error message" and ret3 == nil, "incorrect return values") + +local err = {key = "value"} +ret1, ret2 = pcall(func, err) +assert(not ret1, "error not rethrown") +assert(ret2 == err, "incorrect error rethrown") + +print("OK") diff --git a/contrib/luasocket/test/find-connect-limit b/contrib/luasocket/test/find-connect-limit new file mode 100644 index 00000000..ad0c3f55 --- /dev/null +++ b/contrib/luasocket/test/find-connect-limit @@ -0,0 +1,32 @@ +#!/usr/bin/env lua +--[[ +Find out how many TCP connections we can make. + +Use ulimit to increase the max number of descriptors: + +ulimit -n 10000 +ulimit -n + +You'll probably need to be root to do this. +]] + +require "socket" + +host = arg[1] or "google.com" +port = arg[2] or 80 + +connections = {} + +repeat + c = assert(socket.connect(hostip or host, 80)) + table.insert(connections, c) + + if not hostip then + hostip = c:getpeername() + print("resolved", host, "to", hostip) + end + + print("connection #", #connections, c, "fd", c:getfd()) + +until false + diff --git a/contrib/luasocket/test/ftptest.lua b/contrib/luasocket/test/ftptest.lua new file mode 100644 index 00000000..3ea0d395 --- /dev/null +++ b/contrib/luasocket/test/ftptest.lua @@ -0,0 +1,122 @@ +local socket = require("socket") +local ftp = require("socket.ftp") +local url = require("socket.url") +local ltn12 = require("ltn12") + +-- use dscl to create user "luasocket" with password "password" +-- with home in /Users/diego/luasocket/test/ftp +-- with group com.apple.access_ftp +-- with shell set to /sbin/nologin +-- set /etc/ftpchroot to chroot luasocket +-- must set group com.apple.access_ftp on user _ftp (for anonymous access) +-- copy index.html to /var/empty/pub (home of user ftp) +-- start ftp server with +-- sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist +-- copy index.html to /Users/diego/luasocket/test/ftp +-- stop with +-- sudo -s launchctl unload -w /System/Library/LaunchDaemons/ftp.plist + +-- override protection to make sure we see all errors +--socket.protect = function(s) return s end + +dofile("testsupport.lua") + +local host = host or "localhost" +local port, index_file, index, back, err, ret + +local t = socket.gettime() + +index_file = "index.html" + +-- a function that returns a directory listing +local function nlst(u) + local t = {} + local p = url.parse(u) + p.command = "nlst" + p.sink = ltn12.sink.table(t) + local r, e = ftp.get(p) + return r and table.concat(t), e +end + +-- function that removes a remote file +local function dele(u) + local p = url.parse(u) + p.command = "dele" + p.argument = string.gsub(p.path, "^/", "") + if p.argumet == "" then p.argument = nil end + p.check = 250 + return ftp.command(p) +end + +-- read index with CRLF convention +index = readfile(index_file) + +io.write("testing wrong scheme: ") +back, err = ftp.get("wrong://banana.com/lixo") +assert(not back and err == "wrong scheme 'wrong'", err) +print("ok") + +io.write("testing invalid url: ") +back, err = ftp.get("localhost/dir1/index.html;type=i") +assert(not back and err) +print("ok") + +io.write("testing anonymous file download: ") +back, err = socket.ftp.get("ftp://" .. host .. "/pub/index.html;type=i") +assert(not err and back == index, err) +print("ok") + +io.write("erasing before upload: ") +ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html") +if not ret then print(err) +else print("ok") end + +io.write("testing upload: ") +ret, err = ftp.put("ftp://luasocket:password@" .. host .. "/index.up.html;type=i", index) +assert(ret and not err, err) +print("ok") + +io.write("downloading uploaded file: ") +back, err = ftp.get("ftp://luasocket:password@" .. host .. "/index.up.html;type=i") +assert(ret and not err and index == back, err) +print("ok") + +io.write("erasing after upload/download: ") +ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html") +assert(ret and not err, err) +print("ok") + +io.write("testing weird-character translation: ") +back, err = ftp.get("ftp://luasocket:password@" .. host .. "/%23%3f;type=i") +assert(not err and back == index, err) +print("ok") + +io.write("testing parameter overriding: ") +local back = {} +ret, err = ftp.get{ + url = "//stupid:mistake@" .. host .. "/index.html", + user = "luasocket", + password = "password", + type = "i", + sink = ltn12.sink.table(back) +} +assert(ret and not err and table.concat(back) == index, err) +print("ok") + +io.write("testing upload denial: ") +ret, err = ftp.put("ftp://" .. host .. "/index.up.html;type=a", index) +assert(not ret and err, "should have failed") +print(err) + +io.write("testing authentication failure: ") +ret, err = ftp.get("ftp://luasocket:wrong@".. host .. "/index.html;type=a") +assert(not ret and err, "should have failed") +print(err) + +io.write("testing wrong file: ") +back, err = ftp.get("ftp://".. host .. "/index.wrong.html;type=a") +assert(not back and err, "should have failed") +print(err) + +print("passed all tests") +print(string.format("done in %.2fs", socket.gettime() - t)) diff --git a/contrib/luasocket/test/hello.lua b/contrib/luasocket/test/hello.lua new file mode 100644 index 00000000..3f385dc2 --- /dev/null +++ b/contrib/luasocket/test/hello.lua @@ -0,0 +1,3 @@ +local socket = require"socket" +local mime = require"mime" +print("Hello from " .. socket._VERSION .. " and " .. mime._VERSION .. "!") diff --git a/contrib/luasocket/test/httptest.lua b/contrib/luasocket/test/httptest.lua new file mode 100644 index 00000000..63ff9217 --- /dev/null +++ b/contrib/luasocket/test/httptest.lua @@ -0,0 +1,441 @@ +-- needs Alias from /home/c/diego/tec/luasocket/test to +-- "/luasocket-test" and "/luasocket-test/" +-- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi +-- to "/luasocket-test-cgi" and "/luasocket-test-cgi/" +-- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth +local socket = require("socket") +local http = require("socket.http") +local url = require("socket.url") + +local mime = require("mime") +local ltn12 = require("ltn12") + +-- override protection to make sure we see all errors +-- socket.protect = function(s) return s end + +dofile("testsupport.lua") + +local host, proxy, request, response, index_file +local ignore, expect, index, prefix, cgiprefix, index_crlf + +http.TIMEOUT = 10 + +local t = socket.gettime() + +--host = host or "diego.student.princeton.edu" +--host = host or "diego.student.princeton.edu" +host = host or "localhost" +proxy = proxy or "http://localhost:3128" +prefix = prefix or "/luasocket-test" +cgiprefix = cgiprefix or "/luasocket-test-cgi" +index_file = "index.html" + +-- read index with CRLF convention +index = readfile(index_file) + +local check_result = function(response, expect, ignore) + for i,v in pairs(response) do + if not ignore[i] then + if v ~= expect[i] then + local f = io.open("err", "w") + f:write(tostring(v), "\n\n versus\n\n", tostring(expect[i])) + f:close() + fail(i .. " differs!") + end + end + end + for i,v in pairs(expect) do + if not ignore[i] then + if v ~= response[i] then + local f = io.open("err", "w") + f:write(tostring(response[i]), "\n\n versus\n\n", tostring(v)) + v = string.sub(type(v) == "string" and v or "", 1, 70) + f:close() + fail(i .. " differs!") + end + end + end + print("ok") +end + +local check_request = function(request, expect, ignore) + local t + if not request.sink then request.sink, t = ltn12.sink.table() end + request.source = request.source or + (request.body and ltn12.source.string(request.body)) + local response = {} + response.code, response.headers, response.status = + socket.skip(1, http.request(request)) + if t and #t > 0 then response.body = table.concat(t) end + check_result(response, expect, ignore) +end + +------------------------------------------------------------------------ +io.write("testing request uri correctness: ") +local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" +local back, c, h = http.request("http://" .. host .. forth) +if not back then fail(c) end +back = url.parse(back) +if similar(back.query, "this+is+the+query+string") then print("ok") +else fail(back.query) end + +------------------------------------------------------------------------ +io.write("testing query string correctness: ") +forth = "this+is+the+query+string" +back = http.request("http://" .. host .. cgiprefix .. + "/query-string?" .. forth) +if similar(back, forth) then print("ok") +else fail("failed!") end + +------------------------------------------------------------------------ +io.write("testing document retrieval: ") +request = { + url = "http://" .. host .. prefix .. "/index.html" +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing redirect loop: ") +request = { + url = "http://" .. host .. cgiprefix .. "/redirect-loop" +} +expect = { + code = 302 +} +ignore = { + status = 1, + headers = 1, + body = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing invalid url: ") +local r, e = http.request{url = host .. prefix} +assert(r == nil and e == "invalid host ''") +r, re = http.request(host .. prefix) +assert(r == nil and e == re, tostring(r) ..", " .. tostring(re) .. + " vs " .. tostring(e)) +print("ok") + +io.write("testing invalid empty port: ") +request = { + url = "http://" .. host .. ":" .. prefix .. "/index.html" +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing post method: ") +-- wanted to test chunked post, but apache doesn't support it... +request = { + url = "http://" .. host .. cgiprefix .. "/cat", + method = "POST", + body = index, + -- remove content-length header to send chunked body + headers = { ["content-length"] = string.len(index) } +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +--[[ +io.write("testing proxy with post method: ") +request = { + url = "http://" .. host .. cgiprefix .. "/cat", + method = "POST", + body = index, + headers = { ["content-length"] = string.len(index) }, + proxy= proxy +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) +]] + +------------------------------------------------------------------------ +io.write("testing simple post function: ") +back = http.request("http://" .. host .. cgiprefix .. "/cat", index) +assert(back == index) +print("ok") + +------------------------------------------------------------------------ +io.write("testing ltn12.(sink|source).file: ") +request = { + url = "http://" .. host .. cgiprefix .. "/cat", + method = "POST", + source = ltn12.source.file(io.open(index_file, "rb")), + sink = ltn12.sink.file(io.open(index_file .. "-back", "wb")), + headers = { ["content-length"] = string.len(index) } +} +expect = { + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) +back = readfile(index_file .. "-back") +assert(back == index) +os.remove(index_file .. "-back") + +------------------------------------------------------------------------ +io.write("testing ltn12.(sink|source).chain and mime.(encode|decode): ") + +local function b64length(len) + local a = math.ceil(len/3)*4 + local l = math.ceil(a/76) + return a + l*2 +end + +local source = ltn12.source.chain( + ltn12.source.file(io.open(index_file, "rb")), + ltn12.filter.chain( + mime.encode("base64"), + mime.wrap("base64") + ) +) + +local sink = ltn12.sink.chain( + mime.decode("base64"), + ltn12.sink.file(io.open(index_file .. "-back", "wb")) +) + +request = { + url = "http://" .. host .. cgiprefix .. "/cat", + method = "POST", + source = source, + sink = sink, + headers = { ["content-length"] = b64length(string.len(index)) } +} +expect = { + code = 200 +} +ignore = { + body_cb = 1, + status = 1, + headers = 1 +} +check_request(request, expect, ignore) +back = readfile(index_file .. "-back") +assert(back == index) +os.remove(index_file .. "-back") + +------------------------------------------------------------------------ +io.write("testing http redirection: ") +request = { + url = "http://" .. host .. prefix +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +--[[ +io.write("testing proxy with redirection: ") +request = { + url = "http://" .. host .. prefix, + proxy = proxy +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) +]] + +------------------------------------------------------------------------ +io.write("testing automatic auth failure: ") +request = { + url = "http://really:wrong@" .. host .. prefix .. "/auth/index.html" +} +expect = { + code = 401 +} +ignore = { + body = 1, + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing http redirection failure: ") +request = { + url = "http://" .. host .. prefix, + redirect = false +} +expect = { + code = 301 +} +ignore = { + body = 1, + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing document not found: ") +request = { + url = "http://" .. host .. "/wrongdocument.html" +} +expect = { + code = 404 +} +ignore = { + body = 1, + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing auth failure: ") +request = { + url = "http://" .. host .. prefix .. "/auth/index.html" +} +expect = { + code = 401 +} +ignore = { + body = 1, + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing manual basic auth: ") +request = { + url = "http://" .. host .. prefix .. "/auth/index.html", + headers = { + authorization = "Basic " .. (mime.b64("luasocket:password")) + } +} +expect = { + code = 200, + body = index +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing automatic basic auth: ") +request = { + url = "http://luasocket:password@" .. host .. prefix .. "/auth/index.html" +} +expect = { + code = 200, + body = index +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing auth info overriding: ") +request = { + url = "http://really:wrong@" .. host .. prefix .. "/auth/index.html", + user = "luasocket", + password = "password" +} +expect = { + code = 200, + body = index +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +io.write("testing cgi output retrieval (probably chunked...): ") +request = { + url = "http://" .. host .. cgiprefix .. "/cat-index-html" +} +expect = { + body = index, + code = 200 +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +------------------------------------------------------------------------ +local body +io.write("testing simple request function: ") +body = http.request("http://" .. host .. prefix .. "/index.html") +assert(body == index) +print("ok") + +------------------------------------------------------------------------ +io.write("testing HEAD method: ") +local r, c, h = http.request { + method = "HEAD", + url = "http://www.tecgraf.puc-rio.br/~diego/" +} +assert(r and h and (c == 200), c) +print("ok") + +------------------------------------------------------------------------ +io.write("testing host not found: ") +local c, e = socket.connect("example.invalid", 80) +local r, re = http.request{url = "http://example.invalid/does/not/exist"} +assert(r == nil and e == re, tostring(r) .. " " .. tostring(re)) +r, re = http.request("http://example.invalid/does/not/exist") +assert(r == nil and e == re) +print("ok") + +------------------------------------------------------------------------ +print("passed all tests") +os.remove("err") + +print(string.format("done in %.2fs", socket.gettime() - t)) diff --git a/contrib/luasocket/test/index.html b/contrib/luasocket/test/index.html new file mode 100644 index 00000000..786694ef --- /dev/null +++ b/contrib/luasocket/test/index.html @@ -0,0 +1,3002 @@ + + +This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+This document can contain anything, as long as it is long enough!
+ + diff --git a/contrib/luasocket/test/ltn12test.lua b/contrib/luasocket/test/ltn12test.lua new file mode 100644 index 00000000..e3f85fb4 --- /dev/null +++ b/contrib/luasocket/test/ltn12test.lua @@ -0,0 +1,299 @@ +local ltn12 = require("ltn12") + +dofile("testsupport.lua") + +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +local function show(name, input, output) + local sin = format(input) + local sout = format(output) + io.write(name, ": ", sin, " -> ", sout, "\n") +end + +local function chunked(length) + local tmp + return function(chunk) + local ret + if chunk and chunk ~= "" then + tmp = chunk + end + ret = string.sub(tmp, 1, length) + tmp = string.sub(tmp, length+1) + if not chunk and ret == "" then ret = nil end + return ret + end +end + +local function named(f, name) + return function(chunk) + local ret = f(chunk) + show(name, chunk, ret) + return ret + end +end + +-------------------------------- +local function split(size) + local buffer = "" + local last_out = "" + local last_in = "" + local function output(chunk) + local part = string.sub(buffer, 1, size) + buffer = string.sub(buffer, size+1) + last_out = (part ~= "" or chunk) and part + last_in = chunk + return last_out + end + return function(chunk, done) + if done then + return not last_in and not last_out + end + -- check if argument is consistent with state + if not chunk then + if last_in and last_in ~= "" and last_out ~= "" then + error("nil chunk following data chunk", 2) + end + if not last_out then error("extra nil chunk", 2) end + return output(chunk) + elseif chunk == "" then + if last_out == "" then error('extra "" chunk', 2) end + if not last_out then error('"" chunk following nil return', 2) end + if not last_in then error('"" chunk following nil chunk', 2) end + return output(chunk) + else + if not last_in then error("data chunk following nil chunk", 2) end + if last_in ~= "" and last_out ~= "" then + error("data chunk following data chunk", 2) + end + buffer = chunk + return output(chunk) + end + end +end + +-------------------------------- +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +-------------------------------- +local function merge(size) + local buffer = "" + local last_out = "" + local last_in = "" + local function output(chunk) + local part + if string.len(buffer) >= size or not chunk then + part = buffer + buffer = "" + else + part = "" + end + last_out = (part ~= "" or chunk) and part + last_in = chunk + return last_out + end + return function(chunk, done) + if done then + return not last_in and not last_out + end + -- check if argument is consistent with state + if not chunk then + if last_in and last_in ~= "" and last_out ~= "" then + error("nil chunk following data chunk", 2) + end + if not last_out then error("extra nil chunk", 2) end + return output(chunk) + elseif chunk == "" then + if last_out == "" then error('extra "" chunk', 2) end + if not last_out then error('"" chunk following nil return', 2) end + if not last_in then error('"" chunk following nil chunk', 2) end + return output(chunk) + else + if not last_in then error("data chunk following nil chunk", 2) end + if last_in ~= "" and last_out ~= "" then + error("data chunk following data chunk", 2) + end + buffer = buffer .. chunk + return output(chunk) + end + end +end + +-------------------------------- +io.write("testing sink.table: ") +local sink, t = ltn12.sink.table() +local s, c = "", "" +for i = 0, 10 do + c = string.rep(string.char(i), i) + s = s .. c + assert(sink(c), "returned error") +end +assert(sink(nil), "returned error") +assert(table.concat(t) == s, "mismatch") +print("ok") + +-------------------------------- +io.write("testing sink.chain (with split): ") +sink, t = ltn12.sink.table() +local filter = split(3) +sink = ltn12.sink.chain(filter, sink) +s = "123456789012345678901234567890" +assert(sink(s), "returned error") +assert(sink(s), "returned error") +assert(sink(nil), "returned error") +assert(table.concat(t) == s .. s, "mismatch") +assert(filter(nil, 1), "filter not empty") +print("ok") + +-------------------------------- +io.write("testing sink.chain (with merge): ") +sink, t = ltn12.sink.table() +filter = merge(10) +sink = ltn12.sink.chain(filter, sink) +s = string.rep("123", 30) +s = s .. string.rep("4321", 30) +for i = 1, 30 do + assert(sink("123"), "returned error") +end +for i = 1, 30 do + assert(sink("4321"), "returned error") +end +assert(sink(nil), "returned error") +assert(filter(nil, 1), "filter not empty") +assert(table.concat(t) == s, "mismatch") +print("ok") + +-------------------------------- +io.write("testing source.string and pump.all: ") +local source = ltn12.source.string(s) +sink, t = ltn12.sink.table() +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == s, "mismatch") +print("ok") + +-------------------------------- +io.write("testing source.chain (with split): ") +source = ltn12.source.string(s) +filter = split(5) +source = ltn12.source.chain(source, filter) +sink, t = ltn12.sink.table() +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == s, "mismatch") +assert(filter(nil, 1), "filter not empty") +print("ok") + +-------------------------------- +io.write("testing source.chain (with several filters): ") +local function double(x) -- filter turning "ABC" into "AABBCC" + if not x then return end + local b={} + for k in x:gmatch'.' do table.insert(b, k..k) end + return table.concat(b) +end +source = ltn12.source.string(s) +source = ltn12.source.chain(source, double, double, double) +sink, t = ltn12.sink.table() +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == double(double(double(s))), "mismatch") +print("ok") + +-------------------------------- +io.write("testing source.chain (with split) and sink.chain (with merge): ") +source = ltn12.source.string(s) +filter = split(5) +source = ltn12.source.chain(source, filter) +local filter2 = merge(13) +sink, t = ltn12.sink.table() +sink = ltn12.sink.chain(filter2, sink) +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == s, "mismatch") +assert(filter(nil, 1), "filter not empty") +assert(filter2(nil, 1), "filter2 not empty") +print("ok") + +-------------------------------- +io.write("testing sink.chain (with several filters): ") +source = ltn12.source.string(s) +sink, t = ltn12.sink.table() +sink = ltn12.sink.chain(double, double, double, sink) +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == double(double(double(s))), "mismatch") +print("ok") + +-------------------------------- +io.write("testing filter.chain (and sink.chain, with split, merge): ") +source = ltn12.source.string(s) +filter = split(5) +filter2 = merge(13) +local chain = ltn12.filter.chain(filter, filter2) +sink, t = ltn12.sink.table() +sink = ltn12.sink.chain(chain, sink) +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == s, "mismatch") +assert(filter(nil, 1), "filter not empty") +assert(filter2(nil, 1), "filter2 not empty") +print("ok") + +-------------------------------- +io.write("testing filter.chain (and sink.chain, a bunch): ") +source = ltn12.source.string(s) +filter = split(5) +filter2 = merge(13) +local filter3 = split(7) +local filter4 = merge(11) +local filter5 = split(10) +chain = ltn12.filter.chain(filter, filter2, filter3, filter4, filter5) +sink, t = ltn12.sink.table() +sink = ltn12.sink.chain(chain, sink) +assert(ltn12.pump.all(source, sink)) +assert(table.concat(t) == s, "mismatch") +assert(filter(nil, 1), "filter not empty") +assert(filter2(nil, 1), "filter2 not empty") +assert(filter3(nil, 1), "filter3 not empty") +assert(filter4(nil, 1), "filter4 not empty") +assert(filter5(nil, 1), "filter5 not empty") +print("ok") + +-------------------------------- +io.write("testing filter.chain (and source.chain, with split, merge): ") +source = ltn12.source.string(s) +filter = split(5) +filter2 = merge(13) +local chain = ltn12.filter.chain(filter, filter2) +sink, t = ltn12.sink.table() +source = ltn12.source.chain(source, chain) +assert(ltn12.pump.all(source, sink), "returned error") +assert(table.concat(t) == s, "mismatch") +assert(filter(nil, 1), "filter not empty") +assert(filter2(nil, 1), "filter2 not empty") +print("ok") + +-------------------------------- +io.write("testing filter.chain (and source.chain, a bunch): ") +source = ltn12.source.string(s) +filter = split(5) +filter2 = merge(13) +local filter3 = split(7) +local filter4 = merge(11) +local filter5 = split(10) +chain = ltn12.filter.chain(filter, filter2, filter3, filter4, filter5) +sink, t = ltn12.sink.table() +source = ltn12.source.chain(source, chain) +assert(ltn12.pump.all(source, sink)) +assert(table.concat(t) == s, "mismatch") +assert(filter(nil, 1), "filter not empty") +assert(filter2(nil, 1), "filter2 not empty") +assert(filter3(nil, 1), "filter3 not empty") +assert(filter4(nil, 1), "filter4 not empty") +assert(filter5(nil, 1), "filter5 not empty") +print("ok") + diff --git a/contrib/luasocket/test/luasocket.png b/contrib/luasocket/test/luasocket.png new file mode 100644 index 0000000000000000000000000000000000000000..d24a95495b55894b96aec1b01d0b7c02cc43f168 GIT binary patch literal 11732 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 4 then which = 1 end + return eol[which] + end)) + for i = 1, 4 do + for j = 1, 4 do + f:write(eol[i]) + f:write(eol[j]) + end + end + -- try long spaced and tabbed lines + f:write("\r\n") + for i = 0, 255 do + f:write(string.char(9)) + end + f:write("\r\n") + for i = 0, 255 do + f:write(' ') + end + f:write("\r\n") + for i = 0, 255 do + f:write(string.char(9),' ') + end + f:write("\r\n") + for i = 0, 255 do + f:write(' ',string.char(32)) + end + f:write("\r\n") + + f:close() +end + +local function cleanup_qptest() + os.remove(qptest) + os.remove(eqptest) + os.remove(dqptest) +end + +-- create test file +local function create_b64test() + local f = assert(io.open(b64test, "wb")) + local t = {} + for j = 1, 100 do + for i = 1, 100 do + t[i] = math.random(0, 255) + end + f:write(string.char(unpack(t))) + end + f:close() +end + +local function encode_b64test() + local e1 = mime.encode("base64") + local e2 = mime.encode("base64") + local e3 = mime.encode("base64") + local e4 = mime.encode("base64") + local sp4 = mime.wrap() + local sp3 = mime.wrap(59) + local sp2 = mime.wrap("base64", 30) + local sp1 = mime.wrap(27) + local chain = ltn12.filter.chain(e1, sp1, e2, sp2, e3, sp3, e4, sp4) + transform(b64test, eb64test, chain) +end + +local function decode_b64test() + local d1 = named(mime.decode("base64"), "d1") + local d2 = named(mime.decode("base64"), "d2") + local d3 = named(mime.decode("base64"), "d3") + local d4 = named(mime.decode("base64"), "d4") + local chain = named(ltn12.filter.chain(d1, d2, d3, d4), "chain") + transform(eb64test, db64test, chain) +end + +local function cleanup_b64test() + os.remove(b64test) + os.remove(eb64test) + os.remove(db64test) +end + +local function compare_b64test() +io.write("testing b64 chained encode: ") + compare(b64test, db64test) +end + +local function identity_test() +io.write("testing identity: ") + local chain = named(ltn12.filter.chain( + named(mime.encode("quoted-printable"), "1 eq"), + named(mime.encode("base64"), "2 eb"), + named(mime.decode("base64"), "3 db"), + named(mime.decode("quoted-printable"), "4 dq") + ), "chain") + transform(b64test, eb64test, chain) + compare(b64test, eb64test) + os.remove(eb64test) +end + + +local function padcheck(original, encoded) + local e = (mime.b64(original)) + local d = (mime.unb64(encoded)) + if e ~= encoded then fail("encoding failed") end + if d ~= original then fail("decoding failed") end +end + +local function chunkcheck(original, encoded) + local len = string.len(original) + for i = 0, len do + local a = string.sub(original, 1, i) + local b = string.sub(original, i+1) + local e, r = mime.b64(a, b) + local f = (mime.b64(r)) + if (e .. (f or "") ~= encoded) then fail(e .. (f or "")) end + end +end + +local function padding_b64test() +io.write("testing b64 padding: ") + padcheck("a", "YQ==") + padcheck("ab", "YWI=") + padcheck("abc", "YWJj") + padcheck("abcd", "YWJjZA==") + padcheck("abcde", "YWJjZGU=") + padcheck("abcdef", "YWJjZGVm") + padcheck("abcdefg", "YWJjZGVmZw==") + padcheck("abcdefgh", "YWJjZGVmZ2g=") + padcheck("abcdefghi", "YWJjZGVmZ2hp") + padcheck("abcdefghij", "YWJjZGVmZ2hpag==") + chunkcheck("abcdefgh", "YWJjZGVmZ2g=") + chunkcheck("abcdefghi", "YWJjZGVmZ2hp") + chunkcheck("abcdefghij", "YWJjZGVmZ2hpag==") + print("ok") +end + +local function test_b64lowlevel() +io.write("testing b64 low-level: ") + local a, b + a, b = mime.b64("", "") + assert(a == "" and b == "") + a, b = mime.b64(nil, "blablabla") + assert(a == nil and b == nil) + a, b = mime.b64("", nil) + assert(a == nil and b == nil) + a, b = mime.unb64("", "") + assert(a == "" and b == "") + a, b = mime.unb64(nil, "blablabla") + assert(a == nil and b == nil) + a, b = mime.unb64("", nil) + assert(a == nil and b == nil) + local binary=string.char(0x00,0x44,0x1D,0x14,0x0F,0xF4,0xDA,0x11,0xA9,0x78,0x00,0x14,0x38,0x50,0x60,0xCE) + local encoded = mime.b64(binary) + local decoded=mime.unb64(encoded) + assert(binary == decoded) + print("ok") +end + +local t = socket.gettime() + +create_b64test() +identity_test() +encode_b64test() +decode_b64test() +compare_b64test() +cleanup_b64test() +padding_b64test() +test_b64lowlevel() + +create_qptest() +encode_qptest() +decode_qptest() +compare_qptest() +encode_qptest("binary") +decode_qptest() +compare_qptest() +cleanup_qptest() + + +print(string.format("done in %.2fs", socket.gettime() - t)) diff --git a/contrib/luasocket/test/smtptest.lua b/contrib/luasocket/test/smtptest.lua new file mode 100644 index 00000000..b5380ffc --- /dev/null +++ b/contrib/luasocket/test/smtptest.lua @@ -0,0 +1,259 @@ +local sent = {} + +local from = "diego@localhost" +local server = "localhost" +local rcpt = "luasocket@localhost" + +local files = { + "/var/spool/mail/luasocket", + "/var/spool/mail/luasock1", + "/var/spool/mail/luasock2", + "/var/spool/mail/luasock3", +} + +local t = socket.time() +local err + +dofile("mbox.lua") +local parse = mbox.parse +dofile("testsupport.lua") + +local total = function() + local t = 0 + for i = 1, #sent do + t = t + sent[i].count + end + return t +end + +local similar = function(s1, s2) + return + string.lower(string.gsub(s1, "%s", "")) == + string.lower(string.gsub(s2, "%s", "")) +end + +local fail = function(s) + s = s or "failed!" + print(s) + os.exit() +end + +local readfile = function(name) + local f = io.open(name, "r") + if not f then + fail("unable to open file!") + return nil + end + local s = f:read("*a") + f:close() + return s +end + +local empty = function() + for i,v in ipairs(files) do + local f = io.open(v, "w") + if not f then + fail("unable to open file!") + end + f:close() + end +end + +local get = function() + local s = "" + for i,v in ipairs(files) do + s = s .. "\n" .. readfile(v) + end + return s +end + +local check_headers = function(sent, got) + sent = sent or {} + got = got or {} + for i,v in pairs(sent) do + if not similar(v, got[i]) then fail("header " .. v .. "failed!") end + end +end + +local check_body = function(sent, got) + sent = sent or "" + got = got or "" + if not similar(sent, got) then fail("bodies differ!") end +end + +local check = function(sent, m) + io.write("checking ", m.headers.title, ": ") + for i = 1, #sent do + local s = sent[i] + if s.title == m.headers.title and s.count > 0 then + check_headers(s.headers, m.headers) + check_body(s.body, m.body) + s.count = s.count - 1 + print("ok") + return + end + end + fail("not found") +end + +local insert = function(sent, message) + if type(message.rcpt) == "table" then + message.count = #message.rcpt + else message.count = 1 end + message.headers = message.headers or {} + message.headers.title = message.title + table.insert(sent, message) +end + +local mark = function() + local time = socket.time() + return { time = time } +end + +local wait = function(sentinel, n) + local to + io.write("waiting for ", n, " messages: ") + while 1 do + local mbox = parse(get()) + if n == #mbox then break end + if socket.time() - sentinel.time > 50 then + to = 1 + break + end + socket.sleep(1) + io.write(".") + io.stdout:flush() + end + if to then fail("timeout") + else print("ok") end +end + +local stuffed_body = [[ +This message body needs to be +stuffed because it has a dot +. +by itself on a line. +Otherwise the mailer would +think that the dot +. +is the end of the message +and the remaining text would cause +a lot of trouble. +]] + +insert(sent, { + from = from, + rcpt = { + "luasocket@localhost", + "luasock3@dell-diego.cs.princeton.edu", + "luasock1@dell-diego.cs.princeton.edu" + }, + body = "multiple rcpt body", + title = "multiple rcpt", +}) + +insert(sent, { + from = from, + rcpt = { + "luasock2@localhost", + "luasock3", + "luasock1" + }, + headers = { + header1 = "header 1", + header2 = "header 2", + header3 = "header 3", + header4 = "header 4", + header5 = "header 5", + header6 = "header 6", + }, + body = stuffed_body, + title = "complex message", +}) + +insert(sent, { + from = from, + rcpt = rcpt, + server = server, + body = "simple message body", + title = "simple message" +}) + +insert(sent, { + from = from, + rcpt = rcpt, + server = server, + body = stuffed_body, + title = "stuffed message body" +}) + +insert(sent, { + from = from, + rcpt = rcpt, + headers = { + header1 = "header 1", + header2 = "header 2", + header3 = "header 3", + header4 = "header 4", + header5 = "header 5", + header6 = "header 6", + }, + title = "multiple headers" +}) + +insert(sent, { + from = from, + rcpt = rcpt, + title = "minimum message" +}) + +io.write("testing host not found: ") +local c, e = socket.connect("wrong.host", 25) +local ret, err = socket.smtp.mail{ + from = from, + rcpt = rcpt, + server = "wrong.host" +} +if ret or e ~= err then fail("wrong error message") +else print("ok") end + +io.write("testing invalid from: ") +local ret, err = socket.smtp.mail{ + from = ' " " (( _ * ', + rcpt = rcpt, +} +if ret or not err then fail("wrong error message") +else print(err) end + +io.write("testing no rcpt: ") +local ret, err = socket.smtp.mail{ + from = from, +} +if ret or not err then fail("wrong error message") +else print(err) end + +io.write("clearing mailbox: ") +empty() +print("ok") + +io.write("sending messages: ") +for i = 1, #sent do + ret, err = socket.smtp.mail(sent[i]) + if not ret then fail(err) end + io.write("+") + io.stdout:flush() +end +print("ok") + +wait(mark(), total()) + +io.write("parsing mailbox: ") +local mbox = parse(get()) +print(#mbox .. " messages found!") + +for i = 1, #mbox do + check(sent, mbox[i]) +end + +print("passed all tests") +print(string.format("done in %.2fs", socket.time() - t)) diff --git a/contrib/luasocket/test/stufftest.lua b/contrib/luasocket/test/stufftest.lua new file mode 100644 index 00000000..490053f2 --- /dev/null +++ b/contrib/luasocket/test/stufftest.lua @@ -0,0 +1,21 @@ +local mime = require("mime") + +function test_dot(original, right) + local result, n = mime.dot(2, original) + assert(result == right, "->" .. result .. "<-") + print("ok") +end + +function test_stuff(original, right) + local result, n = mime.dot(2, original) + assert(result == right, "->" .. result .. "<-") + print("ok") +end + +test_dot("abc", "abc") +test_dot("", "") +test_dot("\r\n", "\r\n") +test_dot("\r\n.", "\r\n..") +test_dot(".\r\n.", "..\r\n..") +test_dot(".\r\n.", "..\r\n..") +test_dot("abcd.\r\n.", "abcd.\r\n..") diff --git a/contrib/luasocket/test/tcp-getoptions b/contrib/luasocket/test/tcp-getoptions new file mode 100644 index 00000000..f9b3d1bb --- /dev/null +++ b/contrib/luasocket/test/tcp-getoptions @@ -0,0 +1,41 @@ +#!/usr/bin/env lua + +require"socket" + +port = 8765 + +function options(o) + print("options for", o) + + for _, opt in ipairs{"keepalive", "reuseaddr", "tcp-nodelay"} do + print("getoption", opt, o:getoption(opt)) + end + + print("getoption", "linger", + "on", o:getoption("linger").on, + "timeout", o:getoption("linger").timeout) +end + +local m = socket.tcp() + +options(m) + +assert(m:bind("*", port)) +assert(m:listen()) + +options(m) + +m:close() + +local m = socket.bind("*", port) + +options(m) + +local c = socket.connect("localhost", port) + +options(c) + +local s = m:accept() + +options(s) + diff --git a/contrib/luasocket/test/test_bind.lua b/contrib/luasocket/test/test_bind.lua new file mode 100644 index 00000000..93c42d72 --- /dev/null +++ b/contrib/luasocket/test/test_bind.lua @@ -0,0 +1,6 @@ +local socket = require "socket" +local u = socket.udp() assert(u:setsockname("*", 5088)) u:close() +local u = socket.udp() assert(u:setsockname("*", 0)) u:close() +local t = socket.tcp() assert(t:bind("*", 5088)) t:close() +local t = socket.tcp() assert(t:bind("*", 0)) t:close() +print("done!") \ No newline at end of file diff --git a/contrib/luasocket/test/test_getaddrinfo.lua b/contrib/luasocket/test/test_getaddrinfo.lua new file mode 100644 index 00000000..4b52ff92 --- /dev/null +++ b/contrib/luasocket/test/test_getaddrinfo.lua @@ -0,0 +1,15 @@ +local socket = require "socket" +local addresses = assert(socket.dns.getaddrinfo("localhost")) +assert(type(addresses) == 'table') + +local ipv4mask = "^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?$" + +for i, alt in ipairs(addresses) do + if alt.family == 'inet' then + assert(type(alt.addr) == 'string') + assert(alt.addr:find(ipv4mask)) + assert(alt.addr == '127.0.0.1') + end +end + +print("done!") diff --git a/contrib/luasocket/test/test_socket_error.lua b/contrib/luasocket/test/test_socket_error.lua new file mode 100644 index 00000000..bda64081 --- /dev/null +++ b/contrib/luasocket/test/test_socket_error.lua @@ -0,0 +1,31 @@ +local socket = require "socket" + +local host, port = "127.0.0.1", "5462" + +assert(socket.bind(host, port)):close() + +local sock = socket.tcp() +sock:settimeout(0) + +local ok, err = sock:connect(host, port) +assert(not ok) +assert('timeout' == err) + +for i = 1, 10 do + -- select pass even if socket has error + local _, rec, err = socket.select(nil, {sock}, 1) + local _, ss = next(rec) + if ss then + assert(ss == sock) + else + assert('timeout' == err, 'unexpected error :' .. tostring(err)) + end + err = sock:getoption("error") -- i get 'connection refused' on WinXP + if err then + print("Passed! Error is '" .. err .. "'.") + os.exit(0) + end +end + +print("Fail! No error detected!") +os.exit(1) diff --git a/contrib/luasocket/test/testclnt.lua b/contrib/luasocket/test/testclnt.lua new file mode 100644 index 00000000..170e1878 --- /dev/null +++ b/contrib/luasocket/test/testclnt.lua @@ -0,0 +1,803 @@ +local socket = require"socket" + +host = host or "localhost" +port = port or "8383" + +function printf(...) + io.stderr:write(string.format(...)) +end + +function pass(...) + printf(...) + io.stderr:write("\n") +end + +function fail(...) + io.stderr:write("ERROR: ") + printf(...) + io.stderr:write("!\n") + os.exit() +end + +function warn(...) + local s = string.format(...) + io.stderr:write("WARNING: ", s, "\n") +end + +function remote(...) + local s = string.format(...) + s = string.gsub(s, "\n", ";") + s = string.gsub(s, "%s+", " ") + s = string.gsub(s, "^%s*", "") + control:send(s .. "\n") + control:receive() +end + +function test(test) + io.stderr:write("----------------------------------------------\n", + "testing: ", test, "\n", + "----------------------------------------------\n") +end + +function check_timeout(tm, sl, elapsed, err, opp, mode, alldone) + if tm < sl then + if opp == "send" then + if not err then warn("must be buffered") + elseif err == "timeout" then pass("proper timeout") + else fail("unexpected error '%s'", err) end + else + if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + end + else + if mode == "total" then + if elapsed > tm then + if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + elseif elapsed < tm then + if err then fail(err) + else pass("ok") end + else + if alldone then + if err then fail("unexpected error '%s'", err) + else pass("ok") end + else + if err ~= "timeout" then fail(err) + else pass("proper timeoutk") end + end + end + else + if err then fail(err) + else pass("ok") end + end + end +end + +if not socket._DEBUG then + fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") +end + +io.stderr:write("----------------------------------------------\n", +"LuaSocket Test Procedures\n", +"----------------------------------------------\n") + +start = socket.gettime() + +function reconnect() + if data then data:close() end + remote [[ + if data then data:close() data = nil end + data = server:accept() + data:setoption("tcp-nodelay", true) + ]] + data, err = socket.connect(host, port) + if not data then fail(err) end + data:setoption("tcp-nodelay", true) +end + +printf("attempting control connection...") +control, err = socket.connect(host, port) +if err then fail(err) +else pass("connected!") end +control:setoption("tcp-nodelay", true) + +------------------------------------------------------------------------ +function test_methods(sock, methods) + for _, v in pairs(methods) do + if type(sock[v]) ~= "function" then + fail(sock.class .. " method '" .. v .. "' not registered") + end + end + pass(sock.class .. " methods are ok") +end + +------------------------------------------------------------------------ +function test_mixed(len) + reconnect() + io.stderr:write("length " .. len .. ": ") + local inter = math.ceil(len/4) + local p1 = "unix " .. string.rep("x", inter) .. "line\n" + local p2 = "dos " .. string.rep("y", inter) .. "line\r\n" + local p3 = "raw " .. string.rep("z", inter) .. "bytes" + local p4 = "end" .. string.rep("w", inter) .. "bytes" + local bp1, bp2, bp3, bp4 +remote (string.format("str = data:receive(%d)", + string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) + sent, err = data:send(p1..p2..p3..p4) + if err then fail(err) end +remote "data:send(str); data:close()" + bp1, err = data:receive() + if err then fail(err) end + bp2, err = data:receive() + if err then fail(err) end + bp3, err = data:receive(string.len(p3)) + if err then fail(err) end + bp4, err = data:receive("*a") + if err then fail(err) end + if bp1.."\n" == p1 and bp2.."\r\n" == p2 and bp3 == p3 and bp4 == p4 then + pass("patterns match") + else fail("patterns don't match") end +end + +------------------------------------------------------------------------ +if not math.mod then + math.mod = math.fmod +end +function test_asciiline(len) + reconnect() + io.stderr:write("length " .. len .. ": ") + local str, str10, back, err + str = string.rep("x", math.mod(len, 10)) + str10 = string.rep("aZb.c#dAe?", math.floor(len/10)) + str = str .. str10 +remote "str = data:receive()" + sent, err = data:send(str.."\n") + if err then fail(err) end +remote "data:send(str ..'\\n')" + back, err = data:receive() + if err then fail(err) end + if back == str then pass("lines match") + else fail("lines don't match") end +end + +------------------------------------------------------------------------ +function test_rawline(len) + reconnect() + io.stderr:write("length " .. len .. ": ") + local str, str10, back, err + str = string.rep(string.char(47), math.mod(len, 10)) + str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + math.floor(len/10)) + str = str .. str10 +remote "str = data:receive()" + sent, err = data:send(str.."\n") + if err then fail(err) end +remote "data:send(str..'\\n')" + back, err = data:receive() + if err then fail(err) end + if back == str then pass("lines match") + else fail("lines don't match") end +end + +------------------------------------------------------------------------ +function test_raw(len) + reconnect() + io.stderr:write("length " .. len .. ": ") + local half = math.floor(len/2) + local s1, s2, back, err + s1 = string.rep("x", half) + s2 = string.rep("y", len-half) +remote (string.format("str = data:receive(%d)", len)) + sent, err = data:send(s1) + if err then fail(err) end + sent, err = data:send(s2) + if err then fail(err) end +remote "data:send(str)" + back, err = data:receive(len) + if err then fail(err) end + if back == s1..s2 then pass("blocks match") + else fail("blocks don't match") end +end + +------------------------------------------------------------------------ +function test_totaltimeoutreceive(len, tm, sl) + reconnect() + local str, err, partial + printf("%d bytes, %ds total timeout, %ds pause: ", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = string.rep('a', %d) + data:send(str) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + data:send(str) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm, "total") +local t = socket.gettime() + str, err, partial, elapsed = data:receive(2*len) + check_timeout(tm, sl, elapsed, err, "receive", "total", + string.len(str or partial) == 2*len) +end + +------------------------------------------------------------------------ +function test_totaltimeoutsend(len, tm, sl) + reconnect() + local str, err, total + printf("%d bytes, %ds total timeout, %ds pause: ", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = data:receive(%d) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + str = data:receive(%d) + ]], 2*tm, len, sl, sl, len)) + data:settimeout(tm, "total") + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) + check_timeout(tm, sl, elapsed, err, "send", "total", + total == 2*len) +end + +------------------------------------------------------------------------ +function test_blockingtimeoutreceive(len, tm, sl) + reconnect() + local str, err, partial + printf("%d bytes, %ds blocking timeout, %ds pause: ", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = string.rep('a', %d) + data:send(str) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + data:send(str) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm) + str, err, partial, elapsed = data:receive(2*len) + check_timeout(tm, sl, elapsed, err, "receive", "blocking", + string.len(str or partial) == 2*len) +end + +------------------------------------------------------------------------ +function test_blockingtimeoutsend(len, tm, sl) + reconnect() + local str, err, total + printf("%d bytes, %ds blocking timeout, %ds pause: ", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = data:receive(%d) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + str = data:receive(%d) + ]], 2*tm, len, sl, sl, len)) + data:settimeout(tm) + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) + check_timeout(tm, sl, elapsed, err, "send", "blocking", + total == 2*len) +end + +------------------------------------------------------------------------ +function empty_connect() + printf("empty connect: ") + reconnect() + if data then data:close() data = nil end + remote [[ + if data then data:close() data = nil end + data = server:accept() + ]] + data, err = socket.connect("", port) + if not data then + pass("ok") + data = socket.connect(host, port) + else + pass("gethostbyname returns localhost on empty string...") + end +end + +------------------------------------------------------------------------ +function isclosed(c) + return c:getfd() == -1 or c:getfd() == (2^32-1) +end + +function active_close() + local tcp = socket.tcp4() + if isclosed(tcp) then fail("should not be closed") end + tcp:close() + if not isclosed(tcp) then fail("should be closed") end + tcp = socket.tcp() + if not isclosed(tcp) then fail("should be closed") end + tcp = nil + local udp = socket.udp4() + if isclosed(udp) then fail("should not be closed") end + udp:close() + if not isclosed(udp) then fail("should be closed") end + udp = socket.udp() + if not isclosed(udp) then fail("should be closed") end + udp = nil + pass("ok") +end + +------------------------------------------------------------------------ +function test_closed() + local back, partial, err + local str = 'little string' + reconnect() + printf("trying read detection: ") + remote (string.format ([[ + data:send('%s') + data:close() + data = nil + ]], str)) + -- try to get a line + back, err, partial = data:receive() + if not err then fail("should have gotten 'closed'.") + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") + elseif str ~= partial then fail("didn't receive partial result.") + else pass("graceful 'closed' received") end + reconnect() + printf("trying write detection: ") + remote [[ + data:close() + data = nil + ]] + total, err, partial = data:send(string.rep("ugauga", 100000)) + if not err then + pass("failed: output buffer is at least %d bytes long!", total) + elseif err ~= "closed" then + fail("got '"..err.."' instead of 'closed'.") + else + pass("graceful 'closed' received after %d bytes were sent", partial) + end +end + +------------------------------------------------------------------------ +function test_selectbugs() + local r, s, e = socket.select(nil, nil, 0.1) + assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("both nil: ok") + local udp = socket.udp() + udp:close() + r, s, e = socket.select({ udp }, { udp }, 0.1) + assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("closed sockets: ok") + e = pcall(socket.select, "wrong", 1, 0.1) + assert(e == false, tostring(e)) + e = pcall(socket.select, {}, 1, 0.1) + assert(e == false, tostring(e)) + pass("invalid input: ok") + local toomany = {} + for i = 1, socket._SETSIZE+1 do + toomany[#toomany+1] = socket.udp4() + end + if #toomany > socket._SETSIZE then + local e = pcall(socket.select, toomany, nil, 0.1) + assert(e == false, tostring(e)) + pass("too many sockets (" .. #toomany .. "): ok") + else + pass("unable to create enough sockets (max was "..#toomany..")") + pass("try using ulimit") + end + for _, c in ipairs(toomany) do c:close() end +end + +------------------------------------------------------------------------ +function accept_timeout() + printf("accept with timeout (if it hangs, it failed): ") + local s, e = socket.bind("*", 0, 0) + assert(s, e) + local t = socket.gettime() + s:settimeout(1) + local c, e = s:accept() + assert(not c, "should not accept") + assert(e == "timeout", string.format("wrong error message (%s)", e)) + t = socket.gettime() - t + assert(t < 2, string.format("took to long to give up (%gs)", t)) + s:close() + pass("good") +end + +------------------------------------------------------------------------ +function connect_timeout() + printf("connect with timeout (if it hangs, it failed!): ") + local t = socket.gettime() + local c, e = socket.tcp() + assert(c, e) + c:settimeout(0.1) + local t = socket.gettime() + local r, e = c:connect("10.0.0.1", 81) + assert(not r, "should not connect") + assert(socket.gettime() - t < 2, "took too long to give up.") + c:close() + pass("ok") +end + +------------------------------------------------------------------------ +function accept_errors() + printf("not listening: ") + local d, e = socket.bind("*", 0) + assert(d, e); + local c, e = socket.tcp(); + assert(c, e); + d:setfd(c:getfd()) + d:settimeout(2) + local r, e = d:accept() + assert(not r and e) + pass("ok") + printf("not supported: ") + local c, e = socket.udp() + assert(c, e); + d:setfd(c:getfd()) + local r, e = d:accept() + assert(not r and e) + pass("ok") +end + +------------------------------------------------------------------------ +function connect_errors() + printf("connection refused: ") + local c, e = socket.connect("localhost", 1); + assert(not c and e) + pass("ok") + printf("host not found: ") + local c, e = socket.connect("host.is.invalid", 1); + assert(not c and e, e) + pass("ok") +end + +------------------------------------------------------------------------ +function rebind_test() + local c ,c1 = socket.bind("127.0.0.1", 0) + if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end + assert(c,c1) + local i, p = c:getsockname() + local s, e = socket.tcp() + assert(s, e) + s:setoption("reuseaddr", false) + r, e = s:bind(i, p) + assert(not r, "managed to rebind!") + assert(e) + pass("ok") +end + +------------------------------------------------------------------------ +function getstats_test() + reconnect() + local t = 0 + for i = 1, 25 do + local c = math.random(1, 100) + remote (string.format ([[ + str = data:receive(%d) + data:send(str) + ]], c)) + data:send(string.rep("a", c)) + data:receive(c) + t = t + c + local r, s, a = data:getstats() + assert(r == t, "received count failed" .. tostring(r) + .. "/" .. tostring(t)) + assert(s == t, "sent count failed" .. tostring(s) + .. "/" .. tostring(t)) + end + pass("ok") +end + + +------------------------------------------------------------------------ +function test_nonblocking(size) + reconnect() + printf("testing " .. 2*size .. " bytes: ") +remote(string.format([[ + data:send(string.rep("a", %d)) + socket.sleep(0.5) + data:send(string.rep("b", %d) .. "\n") +]], size, size)) + local err = "timeout" + local part = "" + local str + data:settimeout(0) + while 1 do + str, err, part = data:receive("*l", part) + if err ~= "timeout" then break end + end + assert(str == (string.rep("a", size) .. string.rep("b", size))) + reconnect() +remote(string.format([[ + str = data:receive(%d) + socket.sleep(0.5) + str = data:receive(2*%d, str) + data:send(str) +]], size, size)) + data:settimeout(0) + local start = 0 + while 1 do + ret, err, start = data:send(str, start+1) + if err ~= "timeout" then break end + end + data:send("\n") + data:settimeout(-1) + local back = data:receive(2*size) + assert(back == str, "'" .. back .. "' vs '" .. str .. "'") + pass("ok") +end + +------------------------------------------------------------------------ +function test_readafterclose() + local back, partial, err + local str = 'little string' + reconnect() + printf("trying repeated '*a' pattern") + remote (string.format ([[ + data:send('%s') + data:close() + data = nil + ]], str)) + back, err, partial = data:receive("*a") + assert(back == str, "unexpected data read") + back, err, partial = data:receive("*a") + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") + reconnect() + printf("trying active close before '*a'") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive("*a") + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") + reconnect() + printf("trying active close before '*l'") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive() + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") + reconnect() + printf("trying active close before raw 1") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive(1) + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") + reconnect() + printf("trying active close before raw 0") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive(0) + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") +end + +------------------------------------------------------------------------ +function test_writeafterclose() + local str = 'little string' + reconnect() + remote (string.format ([[ + data:close() + data = nil + ]])) + local sent, err, errsent + while not err do + sent, err, errsent, time = data:send(str) + end + assert(err == "closed", "got " .. err .. " instead of 'closed'") + pass("ok") +end + +------------------------------------------------------------------------ + +function test_partialrecv() + local str = 'little string' + reconnect() +remote([[ + data:send("7890") +]]) + data:settimeout(1) + back, err = data:receive(10, "123456") + assert(back == "1234567890", "failed on exact mixed length") + back, err = data:receive(8, "87654321") + assert(back == "87654321", "failed on exact length") + back, err = data:receive(4, "87654321") + assert(back == "87654321", "failed on smaller length") + pass("ok") +end + +------------------------------------------------------------------------ +test("method registration") + +local tcp_methods = { + "accept", + "bind", + "close", + "connect", + "dirty", + "getfamily", + "getfd", + "getoption", + "getpeername", + "getsockname", + "getstats", + "setstats", + "listen", + "receive", + "send", + "setfd", + "setoption", + "setpeername", + "setsockname", + "settimeout", + "shutdown", +} +test_methods(socket.tcp(), tcp_methods) +do local sock = socket.tcp6() +if sock then test_methods(socket.tcp6(), tcp_methods) +else io.stderr:write("Warning! IPv6 does not support!\n") end +end + +local udp_methods = { + "close", + "dirty", + "getfamily", + "getfd", + "getoption", + "getpeername", + "getsockname", + "receive", + "receivefrom", + "send", + "sendto", + "setfd", + "setoption", + "setpeername", + "setsockname", + "settimeout" +} + +------------------------------------------------------------------------ +test_methods(socket.udp(), udp_methods) +do local sock = socket.tcp6() +if sock then test_methods(socket.udp6(), udp_methods) +else io.stderr:write("Warning! IPv6 does not support!\n") end +end + +test("closed connection detection: ") +test_closed() + +test("partial receive") +test_partialrecv() + +test("select function") +test_selectbugs() + +test("read after close") +test_readafterclose() + +test("write after close") +test_writeafterclose() + +test("connect function") +connect_timeout() +empty_connect() +connect_errors() + +test("rebinding: ") +rebind_test() + +test("active close: ") +active_close() + +test("accept function: ") +accept_timeout() +accept_errors() + +test("getstats test") +getstats_test() + +test("character line") +test_asciiline(1) +test_asciiline(17) +test_asciiline(200) +test_asciiline(4091) +test_asciiline(80199) +test_asciiline(8000000) +test_asciiline(80199) +test_asciiline(4091) +test_asciiline(200) +test_asciiline(17) +test_asciiline(1) + +test("mixed patterns") +test_mixed(1) +test_mixed(17) +test_mixed(200) +test_mixed(4091) +test_mixed(801990) +test_mixed(4091) +test_mixed(200) +test_mixed(17) +test_mixed(1) + +test("binary line") +test_rawline(1) +test_rawline(17) +test_rawline(200) +test_rawline(4091) +test_rawline(80199) +test_rawline(8000000) +test_rawline(80199) +test_rawline(4091) +test_rawline(200) +test_rawline(17) +test_rawline(1) + +test("raw transfer") +test_raw(1) +test_raw(17) +test_raw(200) +test_raw(4091) +test_raw(80199) +test_raw(8000000) +test_raw(80199) +test_raw(4091) +test_raw(200) +test_raw(17) +test_raw(1) + +test("non-blocking transfer") +test_nonblocking(1) +test_nonblocking(17) +test_nonblocking(200) +test_nonblocking(4091) +test_nonblocking(80199) +test_nonblocking(800000) +test_nonblocking(80199) +test_nonblocking(4091) +test_nonblocking(200) +test_nonblocking(17) +test_nonblocking(1) + +test("total timeout on send") +test_totaltimeoutsend(800091, 1, 3) +test_totaltimeoutsend(800091, 2, 3) +test_totaltimeoutsend(800091, 5, 2) +test_totaltimeoutsend(800091, 3, 1) + +test("total timeout on receive") +test_totaltimeoutreceive(800091, 1, 3) +test_totaltimeoutreceive(800091, 2, 3) +test_totaltimeoutreceive(800091, 3, 2) +test_totaltimeoutreceive(800091, 3, 1) + +test("blocking timeout on send") +test_blockingtimeoutsend(800091, 1, 3) +test_blockingtimeoutsend(800091, 2, 3) +test_blockingtimeoutsend(800091, 3, 2) +test_blockingtimeoutsend(800091, 3, 1) + +test("blocking timeout on receive") +test_blockingtimeoutreceive(800091, 1, 3) +test_blockingtimeoutreceive(800091, 2, 3) +test_blockingtimeoutreceive(800091, 3, 2) +test_blockingtimeoutreceive(800091, 3, 1) + +test("shutting server down") +reconnect() +remote("os.exit()") + +test(string.format("done in %.2fs", socket.gettime() - start)) diff --git a/contrib/luasocket/test/testmesg.lua b/contrib/luasocket/test/testmesg.lua new file mode 100644 index 00000000..135a008d --- /dev/null +++ b/contrib/luasocket/test/testmesg.lua @@ -0,0 +1,96 @@ +-- load the smtp support and its friends +local smtp = require("socket.smtp") +local mime = require("mime") +local ltn12 = require("ltn12") + +function filter(s) + if s then io.write(s) end + return s +end + +source = smtp.message { + headers = { ['content-type'] = 'multipart/alternative' }, + body = { + [1] = { + headers = { ['Content-type'] = 'text/html' }, + body = " Hi, there... " + }, + [2] = { + headers = { ['content-type'] = 'text/plain' }, + body = "Hi, there..." + } + } +} + +r, e = smtp.send{ + rcpt = {"", + "" }, + from = "", + source = ltn12.source.chain(source, filter), + --server = "mail.cs.princeton.edu" + server = "localhost", + port = 2525 +} + +print(r, e) + +-- creates a source to send a message with two parts. The first part is +-- plain text, the second part is a PNG image, encoded as base64. +source = smtp.message{ + headers = { + -- Remember that headers are *ignored* by smtp.send. + from = "Sicrano ", + to = "Fulano ", + subject = "Here is a message with attachments" + }, + body = { + preamble = "If your client doesn't understand attachments, \r\n" .. + "it will still display the preamble and the epilogue.\r\n" .. + "Preamble might show up even in a MIME enabled client.", + -- first part: No headers means plain text, us-ascii. + -- The mime.eol low-level filter normalizes end-of-line markers. + [1] = { + body = mime.eol(0, [[ + Lines in a message body should always end with CRLF. + The smtp module will *NOT* perform translation. It will + perform necessary stuffing, though. + ]]) + }, + -- second part: Headers describe content the to be an image, + -- sent under the base64 transfer content encoding. + -- Notice that nothing happens until the message is sent. Small + -- chunks are loaded into memory and translation happens on the fly. + [2] = { + headers = { + ["ConTenT-tYpE"] = 'image/png; name="luasocket.png"', + ["content-disposition"] = 'attachment; filename="luasocket.png"', + ["content-description"] = 'our logo', + ["content-transfer-encoding"] = "BASE64" + }, + body = ltn12.source.chain( + ltn12.source.file(io.open("luasocket.png", "rb")), + ltn12.filter.chain( + mime.encode("base64"), + mime.wrap() + ) + ) + }, + epilogue = "This might also show up, but after the attachments" + } +} + + +r, e = smtp.send{ + rcpt = {"", + "" }, + from = "", + source = ltn12.source.chain(source, filter), + --server = "mail.cs.princeton.edu", + --port = 25 + server = "localhost", + port = 2525 +} + +print(r, e) + + diff --git a/contrib/luasocket/test/testsrvr.lua b/contrib/luasocket/test/testsrvr.lua new file mode 100644 index 00000000..1eb2d5b1 --- /dev/null +++ b/contrib/luasocket/test/testsrvr.lua @@ -0,0 +1,20 @@ +socket = require("socket"); +host = host or "localhost"; +port = port or "8383"; +server = assert(socket.bind(host, port)); +ack = "\n"; +while 1 do + print("server: waiting for client connection..."); + control = assert(server:accept()); + while 1 do + command, emsg = control:receive(); + if emsg == "closed" then + control:close() + break + end + assert(command, emsg) + assert(control:send(ack)); + print(command); + ((loadstring or load)(command))(); + end +end diff --git a/contrib/luasocket/test/testsupport.lua b/contrib/luasocket/test/testsupport.lua new file mode 100644 index 00000000..b9860882 --- /dev/null +++ b/contrib/luasocket/test/testsupport.lua @@ -0,0 +1,37 @@ +function readfile(name) + local f = io.open(name, "rb") + if not f then return nil end + local s = f:read("*a") + f:close() + return s +end + +function similar(s1, s2) + return string.lower(string.gsub(s1 or "", "%s", "")) == + string.lower(string.gsub(s2 or "", "%s", "")) +end + +function fail(msg) + msg = msg or "failed" + error(msg, 2) +end + +function compare(input, output) + local original = readfile(input) + local recovered = readfile(output) + if original ~= recovered then fail("comparison failed") + else print("ok") end +end + +local G = _G +local set = rawset +local warn = print + +local setglobal = function(table, key, value) + warn("changed " .. key) + set(table, key, value) +end + +setmetatable(G, { + __newindex = setglobal +}) diff --git a/contrib/luasocket/test/tftptest.lua b/contrib/luasocket/test/tftptest.lua new file mode 100644 index 00000000..35078e83 --- /dev/null +++ b/contrib/luasocket/test/tftptest.lua @@ -0,0 +1,20 @@ +-- load tftpclnt.lua +local tftp = require("socket.tftp") + +-- needs tftp server running on localhost, with root pointing to +-- a directory with index.html in it + +function readfile(file) + local f = io.open(file, "r") + if not f then return nil end + local a = f:read("*a") + f:close() + return a +end + +host = host or "diego.student.princeton.edu" +retrieved, err = tftp.get("tftp://" .. host .."/index.html") +assert(not err, err) +original = readfile("test/index.html") +assert(original == retrieved, "files differ!") +print("passed") diff --git a/contrib/luasocket/test/udp-zero-length-send b/contrib/luasocket/test/udp-zero-length-send new file mode 100644 index 00000000..a594944c --- /dev/null +++ b/contrib/luasocket/test/udp-zero-length-send @@ -0,0 +1,25 @@ +#!/usr/bin/lua + +--[[ +Show that luasocket returns an error message on zero-length UDP sends, +even though the send is valid, and in fact the UDP packet is sent +to the peer: + +% sudo tcpdump -i lo -n +tcpdump: verbose output suppressed, use -v or -vv for full protocol decode +listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes +13:40:16.652808 IP 127.0.0.1.56573 > 127.0.0.1.5432: UDP, length 0 + +]] + +require"socket" + +s = assert(socket.udp()) +r = assert(socket.udp()) +assert(r:setsockname("*", 5432)) +assert(s:setpeername("127.0.0.1", 5432)) + +ssz, emsg = s:send("") + +print(ssz == 0 and "OK" or "FAIL",[[send:("")]], ssz, emsg) + diff --git a/contrib/luasocket/test/udp-zero-length-send-recv b/contrib/luasocket/test/udp-zero-length-send-recv new file mode 100644 index 00000000..541efd43 --- /dev/null +++ b/contrib/luasocket/test/udp-zero-length-send-recv @@ -0,0 +1,37 @@ +#!/usr/bin/lua + +--[[ +Show that luasocket returns an error message on zero-length UDP sends, +even though the send is valid, and in fact the UDP packet is sent +to the peer: + +% sudo tcpdump -i lo -n +tcpdump: verbose output suppressed, use -v or -vv for full protocol decode +listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes +13:40:16.652808 IP 127.0.0.1.56573 > 127.0.0.1.5432: UDP, length 0 + +]] + +require"socket" + +s = assert(socket.udp()) +r = assert(socket.udp()) +assert(r:setsockname("*", 5432)) +assert(s:setpeername("127.0.0.1", 5432)) + +ok, emsg = s:send("") +if ok ~= 0 then + print("send of zero failed with:", ok, emsg) +end + +assert(r:settimeout(2)) + +ok, emsg = r:receive() + +if not ok or string.len(ok) ~= 0 then + print("fail - receive of zero failed with:", ok, emsg) + os.exit(1) +end + +print"ok" + diff --git a/contrib/luasocket/test/udpconnectclnt.lua b/contrib/luasocket/test/udpconnectclnt.lua new file mode 100644 index 00000000..ad6ab6a4 --- /dev/null +++ b/contrib/luasocket/test/udpconnectclnt.lua @@ -0,0 +1,19 @@ +local socket = require"socket" +local udp = socket.udp +local localhost = "127.0.0.1" +local port = assert(arg[1], "missing port argument") + +se = udp(); se:setoption("reuseaddr", true) +se:setsockname(localhost, 5062) +print("se", se:getsockname()) +sc = udp(); sc:setoption("reuseaddr", true) +sc:setsockname(localhost, 5061) +print("sc", sc:getsockname()) + +se:sendto("this is a test from se", localhost, port) +socket.sleep(1) +sc:sendto("this is a test from sc", localhost, port) +socket.sleep(1) +se:sendto("this is a test from se", localhost, port) +socket.sleep(1) +sc:sendto("this is a test from sc", localhost, port) diff --git a/contrib/luasocket/test/udpconnectsrvr.lua b/contrib/luasocket/test/udpconnectsrvr.lua new file mode 100644 index 00000000..5a9772ec --- /dev/null +++ b/contrib/luasocket/test/udpconnectsrvr.lua @@ -0,0 +1,16 @@ +local socket = require"socket" +local udp = socket.udp +local localhost = "127.0.0.1" +local s = assert(udp()) +assert(tostring(s):find("udp{unconnected}")) +print("setpeername", s:setpeername(localhost, 5061)) +print("getsockname", s:getsockname()) +assert(tostring(s):find("udp{connected}")) +print(s:receive()) +print("setpeername", s:setpeername("*")) +print("getsockname", s:getsockname()) +s:sendto("a", localhost, 12345) +print("getsockname", s:getsockname()) +assert(tostring(s):find("udp{unconnected}")) +print(s:receivefrom()) +s:close() diff --git a/contrib/luasocket/test/unixdgramclnt.lua b/contrib/luasocket/test/unixdgramclnt.lua new file mode 100644 index 00000000..9bd60f7f --- /dev/null +++ b/contrib/luasocket/test/unixdgramclnt.lua @@ -0,0 +1,9 @@ +socket = require"socket" +socket.unix = require"socket.unix" +c = assert(socket.unix.dgram()) +print(c:bind("/tmp/bar")) +while 1 do + local l = io.read("*l") + assert(c:sendto(l, "/tmp/foo")) + print(assert(c:receivefrom())) +end diff --git a/contrib/luasocket/test/unixdgramsrvr.lua b/contrib/luasocket/test/unixdgramsrvr.lua new file mode 100644 index 00000000..4c11f552 --- /dev/null +++ b/contrib/luasocket/test/unixdgramsrvr.lua @@ -0,0 +1,9 @@ + socket = require"socket" + socket.unix = require"socket.unix" + u = assert(socket.unix.dgram()) + assert(u:bind("/tmp/foo")) + while 1 do + x, r = assert(u:receivefrom()) + print(x, r) + assert(u:sendto(">" .. x, r)) + end diff --git a/contrib/luasocket/test/unixstreamclnt.lua b/contrib/luasocket/test/unixstreamclnt.lua new file mode 100644 index 00000000..4f2e1e3c --- /dev/null +++ b/contrib/luasocket/test/unixstreamclnt.lua @@ -0,0 +1,8 @@ +socket = require"socket" +socket.unix = require"socket.unix" +c = assert(socket.unix.stream()) +assert(c:connect("/tmp/foo")) +while 1 do + local l = io.read() + assert(c:send(l .. "\n")) +end diff --git a/contrib/luasocket/test/unixstreamsrvr.lua b/contrib/luasocket/test/unixstreamsrvr.lua new file mode 100644 index 00000000..0a5c644b --- /dev/null +++ b/contrib/luasocket/test/unixstreamsrvr.lua @@ -0,0 +1,9 @@ + socket = require"socket" + socket.unix = require"socket.unix" + u = assert(socket.unix.stream()) + assert(u:bind("/tmp/foo")) + assert(u:listen()) + c = assert(u:accept()) + while 1 do + print(assert(c:receive())) + end diff --git a/contrib/luasocket/test/upload.html b/contrib/luasocket/test/upload.html new file mode 100644 index 00000000..b4674a88 --- /dev/null +++ b/contrib/luasocket/test/upload.html @@ -0,0 +1,15 @@ + + +POST test + + + +
+
+ +

+ +

+
+ + diff --git a/contrib/luasocket/test/urltest.lua b/contrib/luasocket/test/urltest.lua new file mode 100644 index 00000000..1090a7ec --- /dev/null +++ b/contrib/luasocket/test/urltest.lua @@ -0,0 +1,704 @@ +local socket = require("socket") +socket.url = require("socket.url") +dofile("testsupport.lua") + +local check_build_url = function(parsed) + local built = socket.url.build(parsed) + if built ~= parsed.url then + print("built is different from expected") + print(built) + print(expected) + os.exit() + end +end + +local check_protect = function(parsed, path, unsafe) + local built = socket.url.build_path(parsed, unsafe) + if built ~= path then + print(built, path) + print("path composition failed.") + os.exit() + end +end + +local check_invert = function(url) + local parsed = socket.url.parse(url) + parsed.path = socket.url.build_path(socket.url.parse_path(parsed.path)) + local rebuilt = socket.url.build(parsed) + if rebuilt ~= url then + print(url, rebuilt) + print("original and rebuilt are different") + os.exit() + end +end + +local check_parse_path = function(path, expect) + local parsed = socket.url.parse_path(path) + for i = 1, math.max(#parsed, #expect) do + if parsed[i] ~= expect[i] then + print(path) + os.exit() + end + end + if expect.is_directory ~= parsed.is_directory then + print(path) + print("is_directory mismatch") + os.exit() + end + if expect.is_absolute ~= parsed.is_absolute then + print(path) + print("is_absolute mismatch") + os.exit() + end + local built = socket.url.build_path(expect) + if built ~= path then + print(built, path) + print("path composition failed.") + os.exit() + end +end + +local check_absolute_url = function(base, relative, absolute) + local res = socket.url.absolute(base, relative) + if res ~= absolute then + io.write("absolute: In test for '", relative, "' expected '", + absolute, "' but got '", res, "'\n") + os.exit() + end +end + +local check_parse_url = function(gaba) + local url = gaba.url + gaba.url = nil + local parsed = socket.url.parse(url) + for i, v in pairs(gaba) do + if v ~= parsed[i] then + io.write("parse: In test for '", url, "' expected ", i, " = '", + v, "' but got '", tostring(parsed[i]), "'\n") + for i,v in pairs(parsed) do print(i,v) end + os.exit() + end + end + for i, v in pairs(parsed) do + if v ~= gaba[i] then + io.write("parse: In test for '", url, "' expected ", i, " = '", + tostring(gaba[i]), "' but got '", v, "'\n") + for i,v in pairs(parsed) do print(i,v) end + os.exit() + end + end +end + +print("testing URL parsing") +check_parse_url{ + url = "scheme://user:pass$%?#wd@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:pass$%?#wd@host:port", + host = "host", + port = "port", + userinfo = "user:pass$%?#wd", + password = "pass$%?#wd", + user = "user", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} +check_parse_url{ + url = "scheme://user:pass?#wd@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:pass?#wd@host:port", + host = "host", + port = "port", + userinfo = "user:pass?#wd", + password = "pass?#wd", + user = "user", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} +check_parse_url{ + url = "scheme://user:pass-wd@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:pass-wd@host:port", + host = "host", + port = "port", + userinfo = "user:pass-wd", + password = "pass-wd", + user = "user", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} +check_parse_url{ + url = "scheme://user:pass#wd@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:pass#wd@host:port", + host = "host", + port = "port", + userinfo = "user:pass#wd", + password = "pass#wd", + user = "user", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} +check_parse_url{ + url = "scheme://user:pass#wd@host:port/path;params?query", + scheme = "scheme", + authority = "user:pass#wd@host:port", + host = "host", + port = "port", + userinfo = "user:pass#wd", + password = "pass#wd", + user = "user", + path = "/path", + params = "params", + query = "query", +} +check_parse_url{ + url = "scheme://userinfo@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://user:password@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:password@host:port", + host = "host", + port = "port", + userinfo = "user:password", + user = "user", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment", +} + +check_parse_url{ + url = "scheme://userinfo@host:port/path;params?query#", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "params", + query = "query", + fragment = "" +} + +check_parse_url{ + url = "scheme://userinfo@host:port/path;params?#fragment", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "params", + query = "", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://userinfo@host:port/path;params#fragment", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "params", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://userinfo@host:port/path;?query#fragment", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "", + query = "query", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://userinfo@host:port/path?query#fragment", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + query = "query", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://userinfo@host:port/;params?query#fragment", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/", + params = "params", + query = "query", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", +} + +check_parse_url{ + url = "//userinfo@host:port/path;params?query#fragment", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_parse_url{ + url = "//userinfo@host:port/path", + authority = "userinfo@host:port", + host = "host", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", +} + +check_parse_url{ + url = "//userinfo@host/path", + authority = "userinfo@host", + host = "host", + userinfo = "userinfo", + user = "userinfo", + path = "/path", +} + +check_parse_url{ + url = "//user:password@host/path", + authority = "user:password@host", + host = "host", + userinfo = "user:password", + password = "password", + user = "user", + path = "/path", +} + +check_parse_url{ + url = "//user:@host/path", + authority = "user:@host", + host = "host", + userinfo = "user:", + password = "", + user = "user", + path = "/path", +} + +check_parse_url{ + url = "//user@host:port/path", + authority = "user@host:port", + host = "host", + userinfo = "user", + user = "user", + port = "port", + path = "/path", +} + +check_parse_url{ + url = "//host:port/path", + authority = "host:port", + port = "port", + host = "host", + path = "/path", +} + +check_parse_url{ + url = "//host/path", + authority = "host", + host = "host", + path = "/path", +} + +check_parse_url{ + url = "//host", + authority = "host", + host = "host", +} + +check_parse_url{ + url = "/path", + path = "/path", +} + +check_parse_url{ + url = "path", + path = "path", +} + +-- IPv6 tests + +check_parse_url{ + url = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", + scheme = "http", + host = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", + authority = "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80", + port = "80", + path = "/index.html" +} + +check_parse_url{ + url = "http://[1080:0:0:0:8:800:200C:417A]/index.html", + scheme = "http", + host = "1080:0:0:0:8:800:200C:417A", + authority = "[1080:0:0:0:8:800:200C:417A]", + path = "/index.html" +} + +check_parse_url{ + url = "http://[3ffe:2a00:100:7031::1]", + scheme = "http", + host = "3ffe:2a00:100:7031::1", + authority = "[3ffe:2a00:100:7031::1]", +} + +check_parse_url{ + url = "http://[1080::8:800:200C:417A]/foo", + scheme = "http", + host = "1080::8:800:200C:417A", + authority = "[1080::8:800:200C:417A]", + path = "/foo" +} + +check_parse_url{ + url = "http://[::192.9.5.5]/ipng", + scheme = "http", + host = "::192.9.5.5", + authority = "[::192.9.5.5]", + path = "/ipng" +} + +check_parse_url{ + url = "http://[::FFFF:129.144.52.38]:80/index.html", + scheme = "http", + host = "::FFFF:129.144.52.38", + port = "80", + authority = "[::FFFF:129.144.52.38]:80", + path = "/index.html" +} + +check_parse_url{ + url = "http://[2010:836B:4179::836B:4179]", + scheme = "http", + host = "2010:836B:4179::836B:4179", + authority = "[2010:836B:4179::836B:4179]", +} + +check_parse_url{ + url = "//userinfo@[::FFFF:129.144.52.38]:port/path;params?query#fragment", + authority = "userinfo@[::FFFF:129.144.52.38]:port", + host = "::FFFF:129.144.52.38", + port = "port", + userinfo = "userinfo", + user = "userinfo", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_parse_url{ + url = "scheme://user:password@[::192.9.5.5]:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:password@[::192.9.5.5]:port", + host = "::192.9.5.5", + port = "port", + userinfo = "user:password", + user = "user", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +print("testing URL building") +check_build_url { + url = "scheme://user:password@host:port/path;params?query#fragment", + scheme = "scheme", + host = "host", + port = "port", + user = "user", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url{ + url = "//userinfo@[::FFFF:129.144.52.38]:port/path;params?query#fragment", + host = "::FFFF:129.144.52.38", + port = "port", + user = "userinfo", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url{ + url = "scheme://user:password@[::192.9.5.5]:port/path;params?query#fragment", + scheme = "scheme", + host = "::192.9.5.5", + port = "port", + user = "user", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://user:password@host/path;params?query#fragment", + scheme = "scheme", + host = "host", + user = "user", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://user@host/path;params?query#fragment", + scheme = "scheme", + host = "host", + user = "user", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://host/path;params?query#fragment", + scheme = "scheme", + host = "host", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://host/path;params#fragment", + scheme = "scheme", + host = "host", + path = "/path", + params = "params", + fragment = "fragment" +} + +check_build_url { + url = "scheme://host/path#fragment", + scheme = "scheme", + host = "host", + path = "/path", + fragment = "fragment" +} + +check_build_url { + url = "scheme://host/path", + scheme = "scheme", + host = "host", + path = "/path", +} + +check_build_url { + url = "//host/path", + host = "host", + path = "/path", +} + +check_build_url { + url = "/path", + path = "/path", +} + +check_build_url { + url = "scheme://user:password@host:port/path;params?query#fragment", + scheme = "scheme", + host = "host", + port = "port", + user = "user", + userinfo = "not used", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://user:password@host:port/path;params?query#fragment", + scheme = "scheme", + host = "host", + port = "port", + user = "user", + userinfo = "not used", + authority = "not used", + password = "password", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://user:password@host:port/path;params?query#fragment", + scheme = "scheme", + host = "host", + port = "port", + userinfo = "user:password", + authority = "not used", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +check_build_url { + url = "scheme://user:password@host:port/path;params?query#fragment", + scheme = "scheme", + authority = "user:password@host:port", + path = "/path", + params = "params", + query = "query", + fragment = "fragment" +} + +-- standard RFC tests +print("testing absolute resolution") +check_absolute_url("http://a/b/c/d;p?q#f", "g:h", "g:h") +check_absolute_url("http://a/b/c/d;p?q#f", "g", "http://a/b/c/g") +check_absolute_url("http://a/b/c/d;p?q#f", "./g", "http://a/b/c/g") +check_absolute_url("http://a/b/c/d;p?q#f", "g/", "http://a/b/c/g/") +check_absolute_url("http://a/b/c/d;p?q#f", "/g", "http://a/g") +check_absolute_url("http://a/b/c/d;p?q#f", "//g", "http://g") +check_absolute_url("http://a/b/c/d;p?q#f", "?y", "http://a/b/c/d;p?y") +check_absolute_url("http://a/b/c/d;p?q#f", "g?y", "http://a/b/c/g?y") +check_absolute_url("http://a/b/c/d;p?q#f", "g?y/./x", "http://a/b/c/g?y/./x") +check_absolute_url("http://a/b/c/d;p?q#f", "#s", "http://a/b/c/d;p?q#s") +check_absolute_url("http://a/b/c/d;p?q#f", "g#s", "http://a/b/c/g#s") +check_absolute_url("http://a/b/c/d;p?q#f", "g#s/./x", "http://a/b/c/g#s/./x") +check_absolute_url("http://a/b/c/d;p?q#f", "g?y#s", "http://a/b/c/g?y#s") +check_absolute_url("http://a/b/c/d;p?q#f", ";x", "http://a/b/c/d;x") +check_absolute_url("http://a/b/c/d;p?q#f", "g;x", "http://a/b/c/g;x") +check_absolute_url("http://a/b/c/d;p?q#f", "g;x?y#s", "http://a/b/c/g;x?y#s") +check_absolute_url("http://a/b/c/d;p?q#f", ".", "http://a/b/c/") +check_absolute_url("http://a/b/c/d;p?q#f", "./", "http://a/b/c/") +check_absolute_url("http://a/b/c/d;p?q#f", "..", "http://a/b/") +check_absolute_url("http://a/b/c/d;p?q#f", "../", "http://a/b/") +check_absolute_url("http://a/b/c/d;p?q#f", "../g", "http://a/b/g") +check_absolute_url("http://a/b/c/d;p?q#f", "../..", "http://a/") +check_absolute_url("http://a/b/c/d;p?q#f", "../../", "http://a/") +check_absolute_url("http://a/b/c/d;p?q#f", "../../g", "http://a/g") +check_absolute_url("http://a/b/c/d;p?q#f", "", "http://a/b/c/d;p?q#f") +check_absolute_url("http://a/b/c/d;p?q#f", "/./g", "http://a/./g") +check_absolute_url("http://a/b/c/d;p?q#f", "/../g", "http://a/../g") +check_absolute_url("http://a/b/c/d;p?q#f", "g.", "http://a/b/c/g.") +check_absolute_url("http://a/b/c/d;p?q#f", ".g", "http://a/b/c/.g") +check_absolute_url("http://a/b/c/d;p?q#f", "g..", "http://a/b/c/g..") +check_absolute_url("http://a/b/c/d;p?q#f", "..g", "http://a/b/c/..g") +check_absolute_url("http://a/b/c/d;p?q#f", "./../g", "http://a/b/g") +check_absolute_url("http://a/b/c/d;p?q#f", "./g/.", "http://a/b/c/g/") +check_absolute_url("http://a/b/c/d;p?q#f", "g/./h", "http://a/b/c/g/h") +check_absolute_url("http://a/b/c/d;p?q#f", "g/../h", "http://a/b/c/h") + +-- extra tests +check_absolute_url("//a/b/c/d;p?q#f", "d/e/f", "//a/b/c/d/e/f") +check_absolute_url("/a/b/c/d;p?q#f", "d/e/f", "/a/b/c/d/e/f") +check_absolute_url("a/b/c/d", "d/e/f", "a/b/c/d/e/f") +check_absolute_url("a/b/c/d/../", "d/e/f", "a/b/c/d/e/f") +check_absolute_url("http://velox.telemar.com.br", "/dashboard/index.html", + "http://velox.telemar.com.br/dashboard/index.html") + +print("testing path parsing and composition") +check_parse_path("/eu/tu/ele", { "eu", "tu", "ele"; is_absolute = 1 }) +check_parse_path("/eu/", { "eu"; is_absolute = 1, is_directory = 1 }) +check_parse_path("eu/tu/ele/nos/vos/eles/", + { "eu", "tu", "ele", "nos", "vos", "eles"; is_directory = 1}) +check_parse_path("/", { is_absolute = 1, is_directory = 1}) +check_parse_path("", { }) +check_parse_path("eu%01/%02tu/e%03l%04e/nos/vos%05/e%12les/", + { "eu\1", "\2tu", "e\3l\4e", "nos", "vos\5", "e\18les"; is_directory = 1}) +check_parse_path("eu/tu", { "eu", "tu" }) + +print("testing path protection") +check_protect({ "eu", "-_.!~*'():@&=+$,", "tu" }, "eu/-_.!~*'():@&=+$,/tu") +check_protect({ "eu ", "~diego" }, "eu%20/~diego") +check_protect({ "/eu>", "", "/ tm then + if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + elseif elapsed < tm then + if err then fail(err) + else pass("ok") end + else + if alldone then + if err then fail("unexpected error '%s'", err) + else pass("ok") end + else + if err ~= "timeout" then fail(err) + else pass("proper timeoutk") end + end + end + else + if err then fail(err) + else pass("ok") end + end + end +end + +if not socket._DEBUG then + fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") +end + +io.stderr:write("----------------------------------------------\n", +"LuaSocket Test Procedures\n", +"----------------------------------------------\n") + +start = socket.gettime() + +function reconnect() + io.stderr:write("attempting data connection... ") + if data then data:close() end + remote [[ + i = i or 1 + if data then data:close() data = nil end + print("accepting") + data = server:accept() + i = i + 1 + print("done " .. i) + ]] + data, err = uconnect(host, port) + if not data then fail(err) + else pass("connected!") end +end + +pass("attempting control connection...") +control, err = uconnect(host, port) +if err then fail(err) +else pass("connected!") end + +------------------------------------------------------------------------ +function test_methods(sock, methods) + for _, v in pairs(methods) do + if type(sock[v]) ~= "function" then + fail(sock.class .. " method '" .. v .. "' not registered") + end + end + pass(sock.class .. " methods are ok") +end + +------------------------------------------------------------------------ +function test_mixed(len) + reconnect() + local inter = math.ceil(len/4) + local p1 = "unix " .. string.rep("x", inter) .. "line\n" + local p2 = "dos " .. string.rep("y", inter) .. "line\r\n" + local p3 = "raw " .. string.rep("z", inter) .. "bytes" + local p4 = "end" .. string.rep("w", inter) .. "bytes" + local bp1, bp2, bp3, bp4 +remote (string.format("str = data:receive(%d)", + string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) + sent, err = data:send(p1..p2..p3..p4) + if err then fail(err) end +remote "data:send(str); data:close()" + bp1, err = data:receive() + if err then fail(err) end + bp2, err = data:receive() + if err then fail(err) end + bp3, err = data:receive(string.len(p3)) + if err then fail(err) end + bp4, err = data:receive("*a") + if err then fail(err) end + if bp1.."\n" == p1 and bp2.."\r\n" == p2 and bp3 == p3 and bp4 == p4 then + pass("patterns match") + else fail("patterns don't match") end +end + +------------------------------------------------------------------------ +function test_asciiline(len) + reconnect() + local str, str10, back, err + str = string.rep("x", math.mod(len, 10)) + str10 = string.rep("aZb.c#dAe?", math.floor(len/10)) + str = str .. str10 +remote "str = data:receive()" + sent, err = data:send(str.."\n") + if err then fail(err) end +remote "data:send(str ..'\\n')" + back, err = data:receive() + if err then fail(err) end + if back == str then pass("lines match") + else fail("lines don't match") end +end + +------------------------------------------------------------------------ +function test_rawline(len) + reconnect() + local str, str10, back, err + str = string.rep(string.char(47), math.mod(len, 10)) + str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + math.floor(len/10)) + str = str .. str10 +remote "str = data:receive()" + sent, err = data:send(str.."\n") + if err then fail(err) end +remote "data:send(str..'\\n')" + back, err = data:receive() + if err then fail(err) end + if back == str then pass("lines match") + else fail("lines don't match") end +end + +------------------------------------------------------------------------ +function test_raw(len) + reconnect() + local half = math.floor(len/2) + local s1, s2, back, err + s1 = string.rep("x", half) + s2 = string.rep("y", len-half) +remote (string.format("str = data:receive(%d)", len)) + sent, err = data:send(s1) + if err then fail(err) end + sent, err = data:send(s2) + if err then fail(err) end +remote "data:send(str)" + back, err = data:receive(len) + if err then fail(err) end + if back == s1..s2 then pass("blocks match") + else fail("blocks don't match") end +end + +------------------------------------------------------------------------ +function test_totaltimeoutreceive(len, tm, sl) + reconnect() + local str, err, partial + pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = string.rep('a', %d) + data:send(str) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + data:send(str) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm, "total") +local t = socket.gettime() + str, err, partial, elapsed = data:receive(2*len) + check_timeout(tm, sl, elapsed, err, "receive", "total", + string.len(str or partial) == 2*len) +end + +------------------------------------------------------------------------ +function test_totaltimeoutsend(len, tm, sl) + reconnect() + local str, err, total + pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = data:receive(%d) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + str = data:receive(%d) + ]], 2*tm, len, sl, sl, len)) + data:settimeout(tm, "total") + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) + check_timeout(tm, sl, elapsed, err, "send", "total", + total == 2*len) +end + +------------------------------------------------------------------------ +function test_blockingtimeoutreceive(len, tm, sl) + reconnect() + local str, err, partial + pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = string.rep('a', %d) + data:send(str) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + data:send(str) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm) + str, err, partial, elapsed = data:receive(2*len) + check_timeout(tm, sl, elapsed, err, "receive", "blocking", + string.len(str or partial) == 2*len) +end + +------------------------------------------------------------------------ +function test_blockingtimeoutsend(len, tm, sl) + reconnect() + local str, err, total + pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = data:receive(%d) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + str = data:receive(%d) + ]], 2*tm, len, sl, sl, len)) + data:settimeout(tm) + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) + check_timeout(tm, sl, elapsed, err, "send", "blocking", + total == 2*len) +end + +------------------------------------------------------------------------ +function empty_connect() + reconnect() + if data then data:close() data = nil end + remote [[ + if data then data:close() data = nil end + data = server:accept() + ]] + data, err = socket.connect("", port) + if not data then + pass("ok") + data = socket.connect(host, port) + else + pass("gethostbyname returns localhost on empty string...") + end +end + +------------------------------------------------------------------------ +function isclosed(c) + return c:getfd() == -1 or c:getfd() == (2^32-1) +end + +function active_close() + reconnect() + if isclosed(data) then fail("should not be closed") end + data:close() + if not isclosed(data) then fail("should be closed") end + data = nil + local udp = socket.udp() + if isclosed(udp) then fail("should not be closed") end + udp:close() + if not isclosed(udp) then fail("should be closed") end + pass("ok") +end + +------------------------------------------------------------------------ +function test_closed() + local back, partial, err + local str = 'little string' + reconnect() + pass("trying read detection") + remote (string.format ([[ + data:send('%s') + data:close() + data = nil + ]], str)) + -- try to get a line + back, err, partial = data:receive() + if not err then fail("should have gotten 'closed'.") + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") + elseif str ~= partial then fail("didn't receive partial result.") + else pass("graceful 'closed' received") end + reconnect() + pass("trying write detection") + remote [[ + data:close() + data = nil + ]] + total, err, partial = data:send(string.rep("ugauga", 100000)) + if not err then + pass("failed: output buffer is at least %d bytes long!", total) + elseif err ~= "closed" then + fail("got '"..err.."' instead of 'closed'.") + else + pass("graceful 'closed' received after %d bytes were sent", partial) + end +end + +------------------------------------------------------------------------ +function test_selectbugs() + local r, s, e = socket.select(nil, nil, 0.1) + assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("both nil: ok") + local udp = socket.udp() + udp:close() + r, s, e = socket.select({ udp }, { udp }, 0.1) + assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("closed sockets: ok") + e = pcall(socket.select, "wrong", 1, 0.1) + assert(e == false) + e = pcall(socket.select, {}, 1, 0.1) + assert(e == false) + pass("invalid input: ok") +end + +------------------------------------------------------------------------ +function accept_timeout() + io.stderr:write("accept with timeout (if it hangs, it failed): ") + local s, e = socket.bind("*", 0, 0) + assert(s, e) + local t = socket.gettime() + s:settimeout(1) + local c, e = s:accept() + assert(not c, "should not accept") + assert(e == "timeout", string.format("wrong error message (%s)", e)) + t = socket.gettime() - t + assert(t < 2, string.format("took to long to give up (%gs)", t)) + s:close() + pass("good") +end + +------------------------------------------------------------------------ +function connect_timeout() + io.stderr:write("connect with timeout (if it hangs, it failed!): ") + local t = socket.gettime() + local c, e = socket.tcp() + assert(c, e) + c:settimeout(0.1) + local t = socket.gettime() + local r, e = c:connect("127.0.0.2", 80) + assert(not r, "should not connect") + assert(socket.gettime() - t < 2, "took too long to give up.") + c:close() + print("ok") +end + +------------------------------------------------------------------------ +function accept_errors() + io.stderr:write("not listening: ") + local d, e = socket.bind("*", 0) + assert(d, e); + local c, e = socket.tcp(); + assert(c, e); + d:setfd(c:getfd()) + d:settimeout(2) + local r, e = d:accept() + assert(not r and e) + print("ok: ", e) + io.stderr:write("not supported: ") + local c, e = socket.udp() + assert(c, e); + d:setfd(c:getfd()) + local r, e = d:accept() + assert(not r and e) + print("ok: ", e) +end + +------------------------------------------------------------------------ +function connect_errors() + io.stderr:write("connection refused: ") + local c, e = socket.connect("localhost", 1); + assert(not c and e) + print("ok: ", e) + io.stderr:write("host not found: ") + local c, e = socket.connect("host.is.invalid", 1); + assert(not c and e, e) + print("ok: ", e) +end + +------------------------------------------------------------------------ +function rebind_test() + local c = socket.bind("localhost", 0) + local i, p = c:getsockname() + local s, e = socket.tcp() + assert(s, e) + s:setoption("reuseaddr", false) + r, e = s:bind("localhost", p) + assert(not r, "managed to rebind!") + assert(e) + print("ok: ", e) +end + +------------------------------------------------------------------------ +function getstats_test() + reconnect() + local t = 0 + for i = 1, 25 do + local c = math.random(1, 100) + remote (string.format ([[ + str = data:receive(%d) + data:send(str) + ]], c)) + data:send(string.rep("a", c)) + data:receive(c) + t = t + c + local r, s, a = data:getstats() + assert(r == t, "received count failed" .. tostring(r) + .. "/" .. tostring(t)) + assert(s == t, "sent count failed" .. tostring(s) + .. "/" .. tostring(t)) + end + print("ok") +end + + +------------------------------------------------------------------------ +function test_nonblocking(size) + reconnect() +print("Testing " .. 2*size .. " bytes") +remote(string.format([[ + data:send(string.rep("a", %d)) + socket.sleep(0.5) + data:send(string.rep("b", %d) .. "\n") +]], size, size)) + local err = "timeout" + local part = "" + local str + data:settimeout(0) + while 1 do + str, err, part = data:receive("*l", part) + if err ~= "timeout" then break end + end + assert(str == (string.rep("a", size) .. string.rep("b", size))) + reconnect() +remote(string.format([[ + str = data:receive(%d) + socket.sleep(0.5) + str = data:receive(%d, str) + data:send(str) +]], size, size)) + data:settimeout(0) + local start = 0 + while 1 do + ret, err, start = data:send(str, start+1) + if err ~= "timeout" then break end + end + data:send("\n") + data:settimeout(-1) + local back = data:receive(2*size) + assert(back == str, "'" .. back .. "' vs '" .. str .. "'") + print("ok") +end + +------------------------------------------------------------------------ + +test("method registration") +test_methods(socket.unix(), { + "accept", + "bind", + "close", + "connect", + "dirty", + "getfd", + "getstats", + "setstats", + "listen", + "receive", + "send", + "setfd", + "setoption", + "setpeername", + "setsockname", + "settimeout", + "shutdown", +}) + +test("connect function") +--connect_timeout() +--empty_connect() +--connect_errors() + +--test("rebinding: ") +--rebind_test() + +test("active close: ") +active_close() + +test("closed connection detection: ") +test_closed() + +test("accept function: ") +accept_timeout() +accept_errors() + +test("getstats test") +getstats_test() + +test("character line") +test_asciiline(1) +test_asciiline(17) +test_asciiline(200) +test_asciiline(4091) +test_asciiline(80199) +test_asciiline(8000000) +test_asciiline(80199) +test_asciiline(4091) +test_asciiline(200) +test_asciiline(17) +test_asciiline(1) + +test("mixed patterns") +test_mixed(1) +test_mixed(17) +test_mixed(200) +test_mixed(4091) +test_mixed(801990) +test_mixed(4091) +test_mixed(200) +test_mixed(17) +test_mixed(1) + +test("binary line") +test_rawline(1) +test_rawline(17) +test_rawline(200) +test_rawline(4091) +test_rawline(80199) +test_rawline(8000000) +test_rawline(80199) +test_rawline(4091) +test_rawline(200) +test_rawline(17) +test_rawline(1) + +test("raw transfer") +test_raw(1) +test_raw(17) +test_raw(200) +test_raw(4091) +test_raw(80199) +test_raw(8000000) +test_raw(80199) +test_raw(4091) +test_raw(200) +test_raw(17) +test_raw(1) + +test("non-blocking transfer") +test_nonblocking(1) +test_nonblocking(17) +test_nonblocking(200) +test_nonblocking(4091) +test_nonblocking(80199) +test_nonblocking(8000000) +test_nonblocking(80199) +test_nonblocking(4091) +test_nonblocking(200) +test_nonblocking(17) +test_nonblocking(1) + +test("total timeout on send") +test_totaltimeoutsend(800091, 1, 3) +test_totaltimeoutsend(800091, 2, 3) +test_totaltimeoutsend(800091, 5, 2) +test_totaltimeoutsend(800091, 3, 1) + +test("total timeout on receive") +test_totaltimeoutreceive(800091, 1, 3) +test_totaltimeoutreceive(800091, 2, 3) +test_totaltimeoutreceive(800091, 3, 2) +test_totaltimeoutreceive(800091, 3, 1) + +test("blocking timeout on send") +test_blockingtimeoutsend(800091, 1, 3) +test_blockingtimeoutsend(800091, 2, 3) +test_blockingtimeoutsend(800091, 3, 2) +test_blockingtimeoutsend(800091, 3, 1) + +test("blocking timeout on receive") +test_blockingtimeoutreceive(800091, 1, 3) +test_blockingtimeoutreceive(800091, 2, 3) +test_blockingtimeoutreceive(800091, 3, 2) +test_blockingtimeoutreceive(800091, 3, 1) + +test(string.format("done in %.2fs", socket.gettime() - start)) diff --git a/contrib/luasocket/test/utestsrvr.lua b/contrib/luasocket/test/utestsrvr.lua new file mode 100644 index 00000000..a96b570e --- /dev/null +++ b/contrib/luasocket/test/utestsrvr.lua @@ -0,0 +1,17 @@ +socket=require("socket"); +os.remove("/tmp/luasocket") +socket.unix = require("socket.unix"); +host = host or "luasocket"; +server = assert(socket.unix()) +assert(server:bind(host)) +assert(server:listen(5)) +ack = "\n"; +while 1 do + print("server: waiting for client connection..."); + control = assert(server:accept()); + while 1 do + command = assert(control:receive()); + assert(control:send(ack)); + ((loadstring or load)(command))(); + end +end diff --git a/contrib/luasocket/win32.cmd b/contrib/luasocket/win32.cmd new file mode 100644 index 00000000..30457215 --- /dev/null +++ b/contrib/luasocket/win32.cmd @@ -0,0 +1 @@ +make LUAPREFIX_win32='c:\cygwin\home\diego\vc12' LUAV=5.1 PLAT=win32 LUALIBNAME_win32=lualib.lib PLATFORM_win32=Debug install-both diff --git a/mobdebug.lua b/mobdebug.lua new file mode 100644 index 00000000..6834ab2d --- /dev/null +++ b/mobdebug.lua @@ -0,0 +1,1707 @@ +-- +-- MobDebug -- Lua remote debugger +-- Copyright 2011-15 Paul Kulchenko +-- Based on RemDebug 1.0 Copyright Kepler Project 2005 +-- + +-- use loaded modules or load explicitly on those systems that require that +local require = require +local io = io or require "io" +local table = table or require "table" +local string = string or require "string" +local coroutine = coroutine or require "coroutine" +local debug = require "debug" +-- protect require "os" as it may fail on embedded systems without os module +local os = os or (function(module) + local ok, res = pcall(require, module) + return ok and res or nil +end)("os") + +local mobdebug = { + _NAME = "mobdebug", + _VERSION = "0.702", + _COPYRIGHT = "Paul Kulchenko", + _DESCRIPTION = "Mobile Remote Debugger for the Lua programming language", + port = os and os.getenv and tonumber((os.getenv("MOBDEBUG_PORT"))) or 8172, + checkcount = 200, + yieldtimeout = 0.02, -- yield timeout (s) + connecttimeout = 2, -- connect timeout (s) +} + +local HOOKMASK = "lcr" +local error = error +local getfenv = getfenv +local setfenv = setfenv +local loadstring = loadstring or load -- "load" replaced "loadstring" in Lua 5.2 +local pairs = pairs +local setmetatable = setmetatable +local tonumber = tonumber +local unpack = table.unpack or unpack +local rawget = rawget +local gsub, sub, find = string.gsub, string.sub, string.find + +-- if strict.lua is used, then need to avoid referencing some global +-- variables, as they can be undefined; +-- use rawget to avoid complaints from strict.lua at run-time. +-- it's safe to do the initialization here as all these variables +-- should get defined values (if any) before the debugging starts. +-- there is also global 'wx' variable, which is checked as part of +-- the debug loop as 'wx' can be loaded at any time during debugging. +local genv = _G or _ENV +local jit = rawget(genv, "jit") +local MOAICoroutine = rawget(genv, "MOAICoroutine") + +-- ngx_lua debugging requires a special handling as its coroutine.* +-- methods use a different mechanism that doesn't allow resume calls +-- from debug hook handlers. +-- Instead, the "original" coroutine.* methods are used. +-- `rawget` needs to be used to protect against `strict` checks, but +-- ngx_lua hides those in a metatable, so need to use that. +local metagindex = getmetatable(genv) and getmetatable(genv).__index +local ngx = type(metagindex) == "table" and metagindex.rawget and metagindex:rawget("ngx") or nil +local corocreate = ngx and coroutine._create or coroutine.create +local cororesume = ngx and coroutine._resume or coroutine.resume +local coroyield = ngx and coroutine._yield or coroutine.yield +local corostatus = ngx and coroutine._status or coroutine.status +local corowrap = coroutine.wrap + +if not setfenv then -- Lua 5.2+ + -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html + -- this assumes f is a function + local function findenv(f) + local level = 1 + repeat + local name, value = debug.getupvalue(f, level) + if name == '_ENV' then return level, value end + level = level + 1 + until name == nil + return nil end + getfenv = function (f) return(select(2, findenv(f)) or _G) end + setfenv = function (f, t) + local level = findenv(f) + if level then debug.setupvalue(f, level, t) end + return f end +end + +-- check for OS and convert file names to lower case on windows +-- (its file system is case insensitive, but case preserving), as setting a +-- breakpoint on x:\Foo.lua will not work if the file was loaded as X:\foo.lua. +-- OSX and Windows behave the same way (case insensitive, but case preserving). +-- OSX can be configured to be case-sensitive, so check for that. This doesn't +-- handle the case of different partitions having different case-sensitivity. +local win = os and os.getenv and (os.getenv('WINDIR') or (os.getenv('OS') or ''):match('[Ww]indows')) and true or false +local mac = not win and (os and os.getenv and os.getenv('DYLD_LIBRARY_PATH') or not io.open("/proc")) and true or false +local iscasepreserving = win or (mac and io.open('/library') ~= nil) + +-- turn jit off based on Mike Pall's comment in this discussion: +-- http://www.freelists.org/post/luajit/Debug-hooks-and-JIT,2 +-- "You need to turn it off at the start if you plan to receive +-- reliable hook calls at any later point in time." +if jit and jit.off then jit.off() end + +local socket = require "socket" +local coro_debugger +local coro_debugee +local coroutines = {}; setmetatable(coroutines, {__mode = "k"}) -- "weak" keys +local events = { BREAK = 1, WATCH = 2, RESTART = 3, STACK = 4 } +local breakpoints = {} +local watches = {} +local lastsource +local lastfile +local watchescnt = 0 +local abort -- default value is nil; this is used in start/loop distinction +local seen_hook = false +local checkcount = 0 +local step_into = false +local step_over = false +local step_level = 0 +local stack_level = 0 +local server +local buf +local outputs = {} +local iobase = {print = print} +local basedir = "" +local deferror = "execution aborted at default debugee" +local debugee = function () + local a = 1 + for _ = 1, 10 do a = a + 1 end + error(deferror) +end +local function q(s) return string.gsub(s, '([%(%)%.%%%+%-%*%?%[%^%$%]])','%%%1') end + +local serpent = (function() ---- include Serpent module for serialization +local n, v = "serpent", "0.30" -- (C) 2012-17 Paul Kulchenko; MIT License +local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" +local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'} +local badtype = {thread = true, userdata = true, cdata = true} +local getmetatable = debug and debug.getmetatable or getmetatable +local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+ +local keyword, globals, G = {}, {}, (_G or _ENV) +for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false', + 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', + 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end +for k,v in pairs(G) do globals[v] = k end -- build func to name mapping +for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do + for k,v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g..'.'..k end end + +local function s(t, opts) + local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum + local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge + local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) + local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring + local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge) + local numformat = opts.numformat or "%.17g" + local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0 + local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", + -- tostring(val) is needed because __tostring may return a non-string value + function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end + local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) + or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 + or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end + local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end + local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal + and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end + local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] + local n = name == nil and '' or name + local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] + local safe = plain and n or '['..safestr(n)..']' + return (path or '')..(plain and path and '.' or '')..safe, safe end + local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding + local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'} + local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end + table.sort(k, function(a,b) + -- sort numeric keys first: k[key] is not nil for numerical keys + return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum)) + < (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end + local function val2str(t, name, indent, insref, path, plainindex, level) + local ttype, level, mt = type(t), (level or 0), getmetatable(t) + local spath, sname = safename(path, name) + local tag = plainindex and + ((type(name) == "number") and '' or name..space..'='..space) or + (name ~= nil and sname..space..'='..space or '') + if seen[t] then -- already seen this element + sref[#sref+1] = spath..space..'='..space..seen[t] + return tag..'nil'..comment('ref', level) end + -- protect from those cases where __tostring may fail + if type(mt) == 'table' then + local to, tr = pcall(function() return mt.__tostring(t) end) + local so, sr = pcall(function() return mt.__serialize(t) end) + if (opts.metatostring ~= false and to or so) then -- knows how to serialize itself + seen[t] = insref or spath + t = so and sr or tr + ttype = type(t) + end -- new value falls through to be serialized + end + if ttype == "table" then + if level >= maxl then return tag..'{}'..comment('maxlvl', level) end + seen[t] = insref or spath + if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty + if maxlen and maxlen < 0 then return tag..'{}'..comment('maxlen', level) end + local maxn, o, out = math.min(#t, maxnum or #t), {}, {} + for key = 1, maxn do o[key] = key end + if not maxnum or #o < maxnum then + local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables + for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end + if maxnum and #o > maxnum then o[maxnum+1] = nil end + if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end + local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) + for n, key in ipairs(o) do + local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse + if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing + or opts.keyallow and not opts.keyallow[key] + or opts.keyignore and opts.keyignore[key] + or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types + or sparse and value == nil then -- skipping nils; do nothing + elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then + if not seen[key] and not globals[key] then + sref[#sref+1] = 'placeholder' + local sname = safename(iname, gensym(key)) -- iname is table for local variables + sref[#sref] = val2str(key,sname,indent,sname,iname,true) end + sref[#sref+1] = 'placeholder' + local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']' + sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path)) + else + out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1) + if maxlen then + maxlen = maxlen - #out[#out] + if maxlen < 0 then break end + end + end + end + local prefix = string.rep(indent or '', level) + local head = indent and '{\n'..prefix..indent or '{' + local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space)) + local tail = indent and "\n"..prefix..'}' or '}' + return (custom and custom(tag,head,body,tail,level) or tag..head..body..tail)..comment(t, level) + elseif badtype[ttype] then + seen[t] = insref or spath + return tag..globerr(t, level) + elseif ttype == 'function' then + seen[t] = insref or spath + if opts.nocode then return tag.."function() --[[..skipped..]] end"..comment(t, level) end + local ok, res = pcall(string.dump, t) + local func = ok and "((loadstring or load)("..safestr(res)..",'@serialized'))"..comment(t, level) + return tag..(func or globerr(t, level)) + else return tag..safestr(t) end -- handle all other types + end + local sepr = indent and "\n" or ";"..space + local body = val2str(t, name, indent) -- this call also populates sref + local tail = #sref>1 and table.concat(sref, sepr)..sepr or '' + local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or '' + return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end" +end + +local function deserialize(data, opts) + local env = (opts and opts.safe == false) and G + or setmetatable({}, { + __index = function(t,k) return t end, + __call = function(t,...) error("cannot call functions") end + }) + local f, res = (loadstring or load)('return '..data, nil, nil, env) + if not f then f, res = (loadstring or load)(data, nil, nil, env) end + if not f then return f, res end + if setfenv then setfenv(f, env) end + return pcall(f) +end + +local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end +return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, + load = deserialize, + dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end, + line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, + block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } +end)() ---- end of Serpent module + +mobdebug.line = serpent.line +mobdebug.dump = serpent.dump +mobdebug.linemap = nil +mobdebug.loadstring = loadstring + +local function removebasedir(path, basedir) + if iscasepreserving then + -- check if the lowercased path matches the basedir + -- if so, return substring of the original path (to not lowercase it) + return path:lower():find('^'..q(basedir:lower())) + and path:sub(#basedir+1) or path + else + return string.gsub(path, '^'..q(basedir), '') + end +end + +local function stack(start) + local function vars(f) + local func = debug.getinfo(f, "f").func + local i = 1 + local locals = {} + -- get locals + while true do + local name, value = debug.getlocal(f, i) + if not name then break end + if string.sub(name, 1, 1) ~= '(' then + locals[name] = {value, select(2,pcall(tostring,value))} + end + i = i + 1 + end + -- get varargs (these use negative indices) + i = 1 + while true do + local name, value = debug.getlocal(f, -i) + -- `not name` should be enough, but LuaJIT 2.0.0 incorrectly reports `(*temporary)` names here + if not name or name ~= "(*vararg)" then break end + locals[name:gsub("%)$"," "..i..")")] = {value, select(2,pcall(tostring,value))} + i = i + 1 + end + -- get upvalues + i = 1 + local ups = {} + while func do -- check for func as it may be nil for tail calls + local name, value = debug.getupvalue(func, i) + if not name then break end + ups[name] = {value, select(2,pcall(tostring,value))} + i = i + 1 + end + return locals, ups + end + + local stack = {} + local linemap = mobdebug.linemap + for i = (start or 0), 100 do + local source = debug.getinfo(i, "Snl") + if not source then break end + + local src = source.source + if src:find("@") == 1 then + src = src:sub(2):gsub("\\", "/") + if src:find("%./") == 1 then src = src:sub(3) end + end + + table.insert(stack, { -- remove basedir from source + {source.name, removebasedir(src, basedir), + linemap and linemap(source.linedefined, source.source) or source.linedefined, + linemap and linemap(source.currentline, source.source) or source.currentline, + source.what, source.namewhat, source.short_src}, + vars(i+1)}) + if source.what == 'main' then break end + end + return stack +end + +local function set_breakpoint(file, line) + if file == '-' and lastfile then file = lastfile + elseif iscasepreserving then file = string.lower(file) end + if not breakpoints[line] then breakpoints[line] = {} end + breakpoints[line][file] = true +end + +local function remove_breakpoint(file, line) + if file == '-' and lastfile then file = lastfile + elseif file == '*' and line == 0 then breakpoints = {} + elseif iscasepreserving then file = string.lower(file) end + if breakpoints[line] then breakpoints[line][file] = nil end +end + +local function has_breakpoint(file, line) + return breakpoints[line] + and breakpoints[line][iscasepreserving and string.lower(file) or file] +end + +local function restore_vars(vars) + if type(vars) ~= 'table' then return end + + -- locals need to be processed in the reverse order, starting from + -- the inner block out, to make sure that the localized variables + -- are correctly updated with only the closest variable with + -- the same name being changed + -- first loop find how many local variables there is, while + -- the second loop processes them from i to 1 + local i = 1 + while true do + local name = debug.getlocal(3, i) + if not name then break end + i = i + 1 + end + i = i - 1 + local written_vars = {} + while i > 0 do + local name = debug.getlocal(3, i) + if not written_vars[name] then + if string.sub(name, 1, 1) ~= '(' then + debug.setlocal(3, i, rawget(vars, name)) + end + written_vars[name] = true + end + i = i - 1 + end + + i = 1 + local func = debug.getinfo(3, "f").func + while true do + local name = debug.getupvalue(func, i) + if not name then break end + if not written_vars[name] then + if string.sub(name, 1, 1) ~= '(' then + debug.setupvalue(func, i, rawget(vars, name)) + end + written_vars[name] = true + end + i = i + 1 + end +end + +local function capture_vars(level, thread) + level = (level or 0)+2 -- add two levels for this and debug calls + local func = (thread and debug.getinfo(thread, level, "f") or debug.getinfo(level, "f") or {}).func + if not func then return {} end + + local vars = {['...'] = {}} + local i = 1 + while true do + local name, value = debug.getupvalue(func, i) + if not name then break end + if string.sub(name, 1, 1) ~= '(' then vars[name] = value end + i = i + 1 + end + i = 1 + while true do + local name, value + if thread then + name, value = debug.getlocal(thread, level, i) + else + name, value = debug.getlocal(level, i) + end + if not name then break end + if string.sub(name, 1, 1) ~= '(' then vars[name] = value end + i = i + 1 + end + -- get varargs (these use negative indices) + i = 1 + while true do + local name, value + if thread then + name, value = debug.getlocal(thread, level, -i) + else + name, value = debug.getlocal(level, -i) + end + -- `not name` should be enough, but LuaJIT 2.0.0 incorrectly reports `(*temporary)` names here + if not name or name ~= "(*vararg)" then break end + vars['...'][i] = value + i = i + 1 + end + -- returned 'vars' table plays a dual role: (1) it captures local values + -- and upvalues to be restored later (in case they are modified in "eval"), + -- and (2) it provides an environment for evaluated chunks. + -- getfenv(func) is needed to provide proper environment for functions, + -- including access to globals, but this causes vars[name] to fail in + -- restore_vars on local variables or upvalues with `nil` values when + -- 'strict' is in effect. To avoid this `rawget` is used in restore_vars. + setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) }) + return vars +end + +local function stack_depth(start_depth) + for i = start_depth, 0, -1 do + if debug.getinfo(i, "l") then return i+1 end + end + return start_depth +end + +local function is_safe(stack_level) + -- the stack grows up: 0 is getinfo, 1 is is_safe, 2 is debug_hook, 3 is user function + if stack_level == 3 then return true end + for i = 3, stack_level do + -- return if it is not safe to abort + local info = debug.getinfo(i, "S") + if not info then return true end + if info.what == "C" then return false end + end + return true +end + +local function in_debugger() + local this = debug.getinfo(1, "S").source + -- only need to check few frames as mobdebug frames should be close + for i = 3, 7 do + local info = debug.getinfo(i, "S") + if not info then return false end + if info.source == this then return true end + end + return false +end + +local function is_pending(peer) + -- if there is something already in the buffer, skip check + if not buf and checkcount >= mobdebug.checkcount then + peer:settimeout(0) -- non-blocking + buf = peer:receive(1) + peer:settimeout() -- back to blocking + checkcount = 0 + end + return buf +end + +local function readnext(peer, num) + peer:settimeout(0) -- non-blocking + local res, err, partial = peer:receive(num) + peer:settimeout() -- back to blocking + return res or partial or '', err +end + +local function handle_breakpoint(peer) + -- check if the buffer has the beginning of SETB/DELB command; + -- this is to avoid reading the entire line for commands that + -- don't need to be handled here. + if not buf or not (buf:sub(1,1) == 'S' or buf:sub(1,1) == 'D') then return end + + -- check second character to avoid reading STEP or other S* and D* commands + if #buf == 1 then buf = buf .. readnext(peer, 1) end + if buf:sub(2,2) ~= 'E' then return end + + -- need to read few more characters + buf = buf .. readnext(peer, 5-#buf) + if buf ~= 'SETB ' and buf ~= 'DELB ' then return end + + local res, _, partial = peer:receive() -- get the rest of the line; blocking + if not res then + if partial then buf = buf .. partial end + return + end + + local _, _, cmd, file, line = (buf..res):find("^([A-Z]+)%s+(.-)%s+(%d+)%s*$") + if cmd == 'SETB' then set_breakpoint(file, tonumber(line)) + elseif cmd == 'DELB' then remove_breakpoint(file, tonumber(line)) + else + -- this looks like a breakpoint command, but something went wrong; + -- return here to let the "normal" processing to handle, + -- although this is likely to not go well. + return + end + + buf = nil +end + +local function normalize_path(file) + local n + repeat + file, n = file:gsub("/+%.?/+","/") -- remove all `//` and `/./` references + until n == 0 + -- collapse all up-dir references: this will clobber UNC prefix (\\?\) + -- and disk on Windows when there are too many up-dir references: `D:\foo\..\..\bar`; + -- handle the case of multiple up-dir references: `foo/bar/baz/../../../more`; + -- only remove one at a time as otherwise `../../` could be removed; + repeat + file, n = file:gsub("[^/]+/%.%./", "", 1) + until n == 0 + -- there may still be a leading up-dir reference left (as `/../` or `../`); remove it + return (file:gsub("^(/?)%.%./", "%1")) +end + +local function debug_hook(event, line) + -- (1) LuaJIT needs special treatment. Because debug_hook is set for + -- *all* coroutines, and not just the one being debugged as in regular Lua + -- (http://lua-users.org/lists/lua-l/2011-06/msg00513.html), + -- need to avoid debugging mobdebug's own code as LuaJIT doesn't + -- always correctly generate call/return hook events (there are more + -- calls than returns, which breaks stack depth calculation and + -- 'step' and 'step over' commands stop working; possibly because + -- 'tail return' events are not generated by LuaJIT). + -- the next line checks if the debugger is run under LuaJIT and if + -- one of debugger methods is present in the stack, it simply returns. + if jit then + -- when luajit is compiled with LUAJIT_ENABLE_LUA52COMPAT, + -- coroutine.running() returns non-nil for the main thread. + local coro, main = coroutine.running() + if not coro or main then coro = 'main' end + local disabled = coroutines[coro] == false + or coroutines[coro] == nil and coro ~= (coro_debugee or 'main') + if coro_debugee and disabled or not coro_debugee and (disabled or in_debugger()) + then return end + end + + -- (2) check if abort has been requested and it's safe to abort + if abort and is_safe(stack_level) then error(abort) end + + -- (3) also check if this debug hook has not been visited for any reason. + -- this check is needed to avoid stepping in too early + -- (for example, when coroutine.resume() is executed inside start()). + if not seen_hook and in_debugger() then return end + + if event == "call" then + stack_level = stack_level + 1 + elseif event == "return" or event == "tail return" then + stack_level = stack_level - 1 + elseif event == "line" then + if mobdebug.linemap then + local ok, mappedline = pcall(mobdebug.linemap, line, debug.getinfo(2, "S").source) + if ok then line = mappedline end + if not line then return end + end + + -- may need to fall through because of the following: + -- (1) step_into + -- (2) step_over and stack_level <= step_level (need stack_level) + -- (3) breakpoint; check for line first as it's known; then for file + -- (4) socket call (only do every Xth check) + -- (5) at least one watch is registered + if not ( + step_into or step_over or breakpoints[line] or watchescnt > 0 + or is_pending(server) + ) then checkcount = checkcount + 1; return end + + checkcount = mobdebug.checkcount -- force check on the next command + + -- this is needed to check if the stack got shorter or longer. + -- unfortunately counting call/return calls is not reliable. + -- the discrepancy may happen when "pcall(load, '')" call is made + -- or when "error()" is called in a function. + -- in either case there are more "call" than "return" events reported. + -- this validation is done for every "line" event, but should be "cheap" + -- as it checks for the stack to get shorter (or longer by one call). + -- start from one level higher just in case we need to grow the stack. + -- this may happen after coroutine.resume call to a function that doesn't + -- have any other instructions to execute. it triggers three returns: + -- "return, tail return, return", which needs to be accounted for. + stack_level = stack_depth(stack_level+1) + + local caller = debug.getinfo(2, "S") + + -- grab the filename and fix it if needed + local file = lastfile + if (lastsource ~= caller.source) then + file, lastsource = caller.source, caller.source + -- technically, users can supply names that may not use '@', + -- for example when they call loadstring('...', 'filename.lua'). + -- Unfortunately, there is no reliable/quick way to figure out + -- what is the filename and what is the source code. + -- If the name doesn't start with `@`, assume it's a file name if it's all on one line. + if find(file, "^@") or not find(file, "[\r\n]") then + file = gsub(gsub(file, "^@", ""), "\\", "/") + -- normalize paths that may include up-dir or same-dir references + -- if the path starts from the up-dir or reference, + -- prepend `basedir` to generate absolute path to keep breakpoints working. + -- ignore qualified relative path (`D:../`) and UNC paths (`\\?\`) + if find(file, "^%.%./") then file = basedir..file end + if find(file, "/%.%.?/") then file = normalize_path(file) end + -- need this conversion to be applied to relative and absolute + -- file names as you may write "require 'Foo'" to + -- load "foo.lua" (on a case insensitive file system) and breakpoints + -- set on foo.lua will not work if not converted to the same case. + if iscasepreserving then file = string.lower(file) end + if find(file, "^%./") then file = sub(file, 3) + else file = gsub(file, "^"..q(basedir), "") end + -- some file systems allow newlines in file names; remove these. + file = gsub(file, "\n", ' ') + else + file = mobdebug.line(file) + end + + -- set to true if we got here; this only needs to be done once per + -- session, so do it here to at least avoid setting it for every line. + seen_hook = true + lastfile = file + end + + if is_pending(server) then handle_breakpoint(server) end + + local vars, status, res + if (watchescnt > 0) then + vars = capture_vars(1) + for index, value in pairs(watches) do + setfenv(value, vars) + local ok, fired = pcall(value) + if ok and fired then + status, res = cororesume(coro_debugger, events.WATCH, vars, file, line, index) + break -- any one watch is enough; don't check multiple times + end + end + end + + -- need to get into the "regular" debug handler, but only if there was + -- no watch that was fired. If there was a watch, handle its result. + local getin = (status == nil) and + (step_into + -- when coroutine.running() return `nil` (main thread in Lua 5.1), + -- step_over will equal 'main', so need to check for that explicitly. + or (step_over and step_over == (coroutine.running() or 'main') and stack_level <= step_level) + or has_breakpoint(file, line) + or is_pending(server)) + + if getin then + vars = vars or capture_vars(1) + step_into = false + step_over = false + status, res = cororesume(coro_debugger, events.BREAK, vars, file, line) + end + + -- handle 'stack' command that provides stack() information to the debugger + while status and res == 'stack' do + -- resume with the stack trace and variables + if vars then restore_vars(vars) end -- restore vars so they are reflected in stack values + status, res = cororesume(coro_debugger, events.STACK, stack(3), file, line) + end + + -- need to recheck once more as resume after 'stack' command may + -- return something else (for example, 'exit'), which needs to be handled + if status and res and res ~= 'stack' then + if not abort and res == "exit" then mobdebug.onexit(1, true); return end + if not abort and res == "done" then mobdebug.done(); return end + abort = res + -- only abort if safe; if not, there is another (earlier) check inside + -- debug_hook, which will abort execution at the first safe opportunity + if is_safe(stack_level) then error(abort) end + elseif not status and res then + error(res, 2) -- report any other (internal) errors back to the application + end + + if vars then restore_vars(vars) end + + -- last command requested Step Over/Out; store the current thread + if step_over == true then step_over = coroutine.running() or 'main' end + end +end + +local function stringify_results(params, status, ...) + if not status then return status, ... end -- on error report as it + + params = params or {} + if params.nocode == nil then params.nocode = true end + if params.comment == nil then params.comment = 1 end + + local t = {...} + for i,v in pairs(t) do -- stringify each of the returned values + local ok, res = pcall(mobdebug.line, v, params) + t[i] = ok and res or ("%q"):format(res):gsub("\010","n"):gsub("\026","\\026") + end + -- stringify table with all returned values + -- this is done to allow each returned value to be used (serialized or not) + -- intependently and to preserve "original" comments + return pcall(mobdebug.dump, t, {sparse = false}) +end + +local function isrunning() + return coro_debugger and (corostatus(coro_debugger) == 'suspended' or corostatus(coro_debugger) == 'running') +end + +-- this is a function that removes all hooks and closes the socket to +-- report back to the controller that the debugging is done. +-- the script that called `done` can still continue. +local function done() + if not (isrunning() and server) then return end + + if not jit then + for co, debugged in pairs(coroutines) do + if debugged then debug.sethook(co) end + end + end + + debug.sethook() + server:close() + + coro_debugger = nil -- to make sure isrunning() returns `false` + seen_hook = nil -- to make sure that the next start() call works + abort = nil -- to make sure that callback calls use proper "abort" value +end + +local function debugger_loop(sev, svars, sfile, sline) + local command + local app, osname + local eval_env = svars or {} + local function emptyWatch () return false end + local loaded = {} + for k in pairs(package.loaded) do loaded[k] = true end + + while true do + local line, err + local wx = rawget(genv, "wx") -- use rawread to make strict.lua happy + if (wx or mobdebug.yield) and server.settimeout then server:settimeout(mobdebug.yieldtimeout) end + while true do + line, err = server:receive() + if not line and err == "timeout" then + -- yield for wx GUI applications if possible to avoid "busyness" + app = app or (wx and wx.wxGetApp and wx.wxGetApp()) + if app then + local win = app:GetTopWindow() + local inloop = app:IsMainLoopRunning() + osname = osname or wx.wxPlatformInfo.Get():GetOperatingSystemFamilyName() + if win and not inloop then + -- process messages in a regular way + -- and exit as soon as the event loop is idle + if osname == 'Unix' then wx.wxTimer(app):Start(10, true) end + local exitLoop = function() + win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_IDLE) + win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_TIMER) + app:ExitMainLoop() + end + win:Connect(wx.wxEVT_IDLE, exitLoop) + win:Connect(wx.wxEVT_TIMER, exitLoop) + app:MainLoop() + end + elseif mobdebug.yield then mobdebug.yield() + end + elseif not line and err == "closed" then + error("Debugger connection closed", 0) + else + -- if there is something in the pending buffer, prepend it to the line + if buf then line = buf .. line; buf = nil end + break + end + end + if server.settimeout then server:settimeout() end -- back to blocking + command = string.sub(line, string.find(line, "^[A-Z]+")) + if command == "SETB" then + local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + set_breakpoint(file, tonumber(line)) + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "DELB" then + local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + remove_breakpoint(file, tonumber(line)) + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "EXEC" then + -- extract any optional parameters + local params = string.match(line, "--%s*(%b{})%s*$") + local _, _, chunk = string.find(line, "^[A-Z]+%s+(.+)$") + if chunk then + local func, res = mobdebug.loadstring(chunk) + local status + if func then + local pfunc = params and loadstring("return "..params) -- use internal function + params = pfunc and pfunc() + params = (type(params) == "table" and params or {}) + local stack = tonumber(params.stack) + -- if the requested stack frame is not the current one, then use a new capture + -- with a specific stack frame: `capture_vars(0, coro_debugee)` + local env = stack and coro_debugee and capture_vars(stack-1, coro_debugee) or eval_env + setfenv(func, env) + status, res = stringify_results(params, pcall(func, unpack(env['...'] or {}))) + end + if status then + if mobdebug.onscratch then mobdebug.onscratch(res) end + server:send("200 OK " .. tostring(#res) .. "\n") + server:send(res) + else + -- fix error if not set (for example, when loadstring is not present) + if not res then res = "Unknown error" end + server:send("401 Error in Expression " .. tostring(#res) .. "\n") + server:send(res) + end + else + server:send("400 Bad Request\n") + end + elseif command == "LOAD" then + local _, _, size, name = string.find(line, "^[A-Z]+%s+(%d+)%s+(%S.-)%s*$") + size = tonumber(size) + + if abort == nil then -- no LOAD/RELOAD allowed inside start() + if size > 0 then server:receive(size) end + if sfile and sline then + server:send("201 Started " .. sfile .. " " .. tostring(sline) .. "\n") + else + server:send("200 OK 0\n") + end + else + -- reset environment to allow required modules to load again + -- remove those packages that weren't loaded when debugger started + for k in pairs(package.loaded) do + if not loaded[k] then package.loaded[k] = nil end + end + + if size == 0 and name == '-' then -- RELOAD the current script being debugged + server:send("200 OK 0\n") + coroyield("load") + else + -- receiving 0 bytes blocks (at least in luasocket 2.0.2), so skip reading + local chunk = size == 0 and "" or server:receive(size) + if chunk then -- LOAD a new script for debugging + local func, res = mobdebug.loadstring(chunk, "@"..name) + if func then + server:send("200 OK 0\n") + debugee = func + coroyield("load") + else + server:send("401 Error in Expression " .. tostring(#res) .. "\n") + server:send(res) + end + else + server:send("400 Bad Request\n") + end + end + end + elseif command == "SETW" then + local _, _, exp = string.find(line, "^[A-Z]+%s+(.+)%s*$") + if exp then + local func, res = mobdebug.loadstring("return(" .. exp .. ")") + if func then + watchescnt = watchescnt + 1 + local newidx = #watches + 1 + watches[newidx] = func + server:send("200 OK " .. tostring(newidx) .. "\n") + else + server:send("401 Error in Expression " .. tostring(#res) .. "\n") + server:send(res) + end + else + server:send("400 Bad Request\n") + end + elseif command == "DELW" then + local _, _, index = string.find(line, "^[A-Z]+%s+(%d+)%s*$") + index = tonumber(index) + if index > 0 and index <= #watches then + watchescnt = watchescnt - (watches[index] ~= emptyWatch and 1 or 0) + watches[index] = emptyWatch + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "RUN" then + server:send("200 OK\n") + + local ev, vars, file, line, idx_watch = coroyield() + eval_env = vars + if ev == events.BREAK then + server:send("202 Paused " .. file .. " " .. tostring(line) .. "\n") + elseif ev == events.WATCH then + server:send("203 Paused " .. file .. " " .. tostring(line) .. " " .. tostring(idx_watch) .. "\n") + elseif ev == events.RESTART then + -- nothing to do + else + server:send("401 Error in Execution " .. tostring(#file) .. "\n") + server:send(file) + end + elseif command == "STEP" then + server:send("200 OK\n") + step_into = true + + local ev, vars, file, line, idx_watch = coroyield() + eval_env = vars + if ev == events.BREAK then + server:send("202 Paused " .. file .. " " .. tostring(line) .. "\n") + elseif ev == events.WATCH then + server:send("203 Paused " .. file .. " " .. tostring(line) .. " " .. tostring(idx_watch) .. "\n") + elseif ev == events.RESTART then + -- nothing to do + else + server:send("401 Error in Execution " .. tostring(#file) .. "\n") + server:send(file) + end + elseif command == "OVER" or command == "OUT" then + server:send("200 OK\n") + step_over = true + + -- OVER and OUT are very similar except for + -- the stack level value at which to stop + if command == "OUT" then step_level = stack_level - 1 + else step_level = stack_level end + + local ev, vars, file, line, idx_watch = coroyield() + eval_env = vars + if ev == events.BREAK then + server:send("202 Paused " .. file .. " " .. tostring(line) .. "\n") + elseif ev == events.WATCH then + server:send("203 Paused " .. file .. " " .. tostring(line) .. " " .. tostring(idx_watch) .. "\n") + elseif ev == events.RESTART then + -- nothing to do + else + server:send("401 Error in Execution " .. tostring(#file) .. "\n") + server:send(file) + end + elseif command == "BASEDIR" then + local _, _, dir = string.find(line, "^[A-Z]+%s+(.+)%s*$") + if dir then + basedir = iscasepreserving and string.lower(dir) or dir + -- reset cached source as it may change with basedir + lastsource = nil + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "SUSPEND" then + -- do nothing; it already fulfilled its role + elseif command == "DONE" then + coroyield("done") + return -- done with all the debugging + elseif command == "STACK" then + -- first check if we can execute the stack command + -- as it requires yielding back to debug_hook it cannot be executed + -- if we have not seen the hook yet as happens after start(). + -- in this case we simply return an empty result + local vars, ev = {} + if seen_hook then + ev, vars = coroyield("stack") + end + if ev and ev ~= events.STACK then + server:send("401 Error in Execution " .. tostring(#vars) .. "\n") + server:send(vars) + else + local params = string.match(line, "--%s*(%b{})%s*$") + local pfunc = params and loadstring("return "..params) -- use internal function + params = pfunc and pfunc() + params = (type(params) == "table" and params or {}) + if params.nocode == nil then params.nocode = true end + if params.sparse == nil then params.sparse = false end + -- take into account additional levels for the stack frames and data management + if tonumber(params.maxlevel) then params.maxlevel = tonumber(params.maxlevel)+4 end + + local ok, res = pcall(mobdebug.dump, vars, params) + if ok then + server:send("200 OK " .. tostring(res) .. "\n") + else + server:send("401 Error in Execution " .. tostring(#res) .. "\n") + server:send(res) + end + end + elseif command == "OUTPUT" then + local _, _, stream, mode = string.find(line, "^[A-Z]+%s+(%w+)%s+([dcr])%s*$") + if stream and mode and stream == "stdout" then + -- assign "print" in the global environment + local default = mode == 'd' + genv.print = default and iobase.print or corowrap(function() + -- wrapping into coroutine.wrap protects this function from + -- being stepped through in the debugger. + -- don't use vararg (...) as it adds a reference for its values, + -- which may affect how they are garbage collected + while true do + local tbl = {coroutine.yield()} + if mode == 'c' then iobase.print(unpack(tbl)) end + for n = 1, #tbl do + tbl[n] = select(2, pcall(mobdebug.line, tbl[n], {nocode = true, comment = false})) end + local file = table.concat(tbl, "\t").."\n" + server:send("204 Output " .. stream .. " " .. tostring(#file) .. "\n" .. file) + end + end) + if not default then genv.print() end -- "fake" print to start printing loop + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "EXIT" then + server:send("200 OK\n") + coroyield("exit") + else + server:send("400 Bad Request\n") + end + end +end + +local function output(stream, data) + if server then return server:send("204 Output "..stream.." "..tostring(#data).."\n"..data) end +end + +local function connect(controller_host, controller_port) + local sock, err = socket.tcp() + if not sock then return nil, err end + + if sock.settimeout then sock:settimeout(mobdebug.connecttimeout) end + local res, err = sock:connect(controller_host, tostring(controller_port)) + if sock.settimeout then sock:settimeout() end + + if not res then return nil, err end + return sock +end + +local lasthost, lastport + +-- Starts a debug session by connecting to a controller +local function start(controller_host, controller_port) + -- only one debugging session can be run (as there is only one debug hook) + if isrunning() then return end + + lasthost = controller_host or lasthost + lastport = controller_port or lastport + + controller_host = lasthost or "localhost" + controller_port = lastport or mobdebug.port + + local err + server, err = mobdebug.connect(controller_host, controller_port) + if server then + -- correct stack depth which already has some calls on it + -- so it doesn't go into negative when those calls return + -- as this breaks subsequence checks in stack_depth(). + -- start from 16th frame, which is sufficiently large for this check. + stack_level = stack_depth(16) + + -- provide our own traceback function to report errors remotely + -- but only under Lua 5.1/LuaJIT as it's not called under Lua 5.2+ + -- (http://lua-users.org/lists/lua-l/2016-05/msg00297.html) + local function f() return function()end end + if f() ~= f() then -- Lua 5.1 or LuaJIT + local dtraceback = debug.traceback + debug.traceback = function (...) + if select('#', ...) >= 1 then + local thr, err, lvl = ... + if type(thr) ~= 'thread' then err, lvl = thr, err end + local trace = dtraceback(err, (lvl or 1)+1) + if genv.print == iobase.print then -- no remote redirect + return trace + else + genv.print(trace) -- report the error remotely + return -- don't report locally to avoid double reporting + end + end + -- direct call to debug.traceback: return the original. + -- debug.traceback(nil, level) doesn't work in Lua 5.1 + -- (http://lua-users.org/lists/lua-l/2011-06/msg00574.html), so + -- simply remove first frame from the stack trace + local tb = dtraceback("", 2) -- skip debugger frames + -- if the string is returned, then remove the first new line as it's not needed + return type(tb) == "string" and tb:gsub("^\n","") or tb + end + end + coro_debugger = corocreate(debugger_loop) + debug.sethook(debug_hook, HOOKMASK) + seen_hook = nil -- reset in case the last start() call was refused + step_into = true -- start with step command + return true + else + print(("Could not connect to %s:%s: %s") + :format(controller_host, controller_port, err or "unknown error")) + end +end + +local function controller(controller_host, controller_port, scratchpad) + -- only one debugging session can be run (as there is only one debug hook) + if isrunning() then return end + + lasthost = controller_host or lasthost + lastport = controller_port or lastport + + controller_host = lasthost or "localhost" + controller_port = lastport or mobdebug.port + + local exitonerror = not scratchpad + local err + server, err = mobdebug.connect(controller_host, controller_port) + if server then + local function report(trace, err) + local msg = err .. "\n" .. trace + server:send("401 Error in Execution " .. tostring(#msg) .. "\n") + server:send(msg) + return err + end + + seen_hook = true -- allow to accept all commands + coro_debugger = corocreate(debugger_loop) + + while true do + step_into = true -- start with step command + abort = false -- reset abort flag from the previous loop + if scratchpad then checkcount = mobdebug.checkcount end -- force suspend right away + + coro_debugee = corocreate(debugee) + debug.sethook(coro_debugee, debug_hook, HOOKMASK) + local status, err = cororesume(coro_debugee, unpack(arg or {})) + + -- was there an error or is the script done? + -- 'abort' state is allowed here; ignore it + if abort then + if tostring(abort) == 'exit' then break end + else + if status then -- no errors + if corostatus(coro_debugee) == "suspended" then + -- the script called `coroutine.yield` in the "main" thread + error("attempt to yield from the main thread", 3) + end + break -- normal execution is done + elseif err and not string.find(tostring(err), deferror) then + -- report the error back + -- err is not necessarily a string, so convert to string to report + report(debug.traceback(coro_debugee), tostring(err)) + if exitonerror then break end + -- check if the debugging is done (coro_debugger is nil) + if not coro_debugger then break end + -- resume once more to clear the response the debugger wants to send + -- need to use capture_vars(0) to capture only two (default) level, + -- as even though there is controller() call, because of the tail call, + -- the caller may not exist for it; + -- This is not entirely safe as the user may see the local + -- variable from console, but they will be reset anyway. + -- This functionality is used when scratchpad is paused to + -- gain access to remote console to modify global variables. + local status, err = cororesume(coro_debugger, events.RESTART, capture_vars(0)) + if not status or status and err == "exit" then break end + end + end + end + else + print(("Could not connect to %s:%s: %s") + :format(controller_host, controller_port, err or "unknown error")) + return false + end + return true +end + +local function scratchpad(controller_host, controller_port) + return controller(controller_host, controller_port, true) +end + +local function loop(controller_host, controller_port) + return controller(controller_host, controller_port, false) +end + +local function on() + if not (isrunning() and server) then return end + + -- main is set to true under Lua5.2 for the "main" chunk. + -- Lua5.1 returns co as `nil` in that case. + local co, main = coroutine.running() + if main then co = nil end + if co then + coroutines[co] = true + debug.sethook(co, debug_hook, HOOKMASK) + else + if jit then coroutines.main = true end + debug.sethook(debug_hook, HOOKMASK) + end +end + +local function off() + if not (isrunning() and server) then return end + + -- main is set to true under Lua5.2 for the "main" chunk. + -- Lua5.1 returns co as `nil` in that case. + local co, main = coroutine.running() + if main then co = nil end + + -- don't remove coroutine hook under LuaJIT as there is only one (global) hook + if co then + coroutines[co] = false + if not jit then debug.sethook(co) end + else + if jit then coroutines.main = false end + if not jit then debug.sethook() end + end + + -- check if there is any thread that is still being debugged under LuaJIT; + -- if not, turn the debugging off + if jit then + local remove = true + for _, debugged in pairs(coroutines) do + if debugged then remove = false; break end + end + if remove then debug.sethook() end + end +end + +-- Handles server debugging commands +local function handle(params, client, options) + -- when `options.verbose` is not provided, use normal `print`; verbose output can be + -- disabled (`options.verbose == false`) or redirected (`options.verbose == function()...end`) + local verbose = not options or options.verbose ~= nil and options.verbose + local print = verbose and (type(verbose) == "function" and verbose or print) or function() end + local file, line, watch_idx + local _, _, command = string.find(params, "^([a-z]+)") + if command == "run" or command == "step" or command == "out" + or command == "over" or command == "exit" then + client:send(string.upper(command) .. "\n") + client:receive() -- this should consume the first '200 OK' response + while true do + local done = true + local breakpoint = client:receive() + if not breakpoint then + print("Program finished") + return nil, nil, false + end + local _, _, status = string.find(breakpoint, "^(%d+)") + if status == "200" then + -- don't need to do anything + elseif status == "202" then + _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$") + if file and line then + print("Paused at file " .. file .. " line " .. line) + end + elseif status == "203" then + _, _, file, line, watch_idx = string.find(breakpoint, "^203 Paused%s+(.-)%s+(%d+)%s+(%d+)%s*$") + if file and line and watch_idx then + print("Paused at file " .. file .. " line " .. line .. " (watch expression " .. watch_idx .. ": [" .. watches[watch_idx] .. "])") + end + elseif status == "204" then + local _, _, stream, size = string.find(breakpoint, "^204 Output (%w+) (%d+)$") + if stream and size then + local size = tonumber(size) + local msg = size > 0 and client:receive(size) or "" + print(msg) + if outputs[stream] then outputs[stream](msg) end + -- this was just the output, so go back reading the response + done = false + end + elseif status == "401" then + local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)$") + if size then + local msg = client:receive(tonumber(size)) + print("Error in remote application: " .. msg) + return nil, nil, msg + end + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response '" .. breakpoint .. "'" + end + if done then break end + end + elseif command == "done" then + client:send(string.upper(command) .. "\n") + -- no response is expected + elseif command == "setb" or command == "asetb" then + _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + -- if this is a file name, and not a file source + if not file:find('^".*"$') then + file = string.gsub(file, "\\", "/") -- convert slash + file = removebasedir(file, basedir) + end + client:send("SETB " .. file .. " " .. line .. "\n") + if command == "asetb" or client:receive() == "200 OK" then + set_breakpoint(file, line) + else + print("Error: breakpoint not inserted") + end + else + print("Invalid command") + end + elseif command == "setw" then + local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$") + if exp then + client:send("SETW " .. exp .. "\n") + local answer = client:receive() + local _, _, watch_idx = string.find(answer, "^200 OK (%d+)%s*$") + if watch_idx then + watches[watch_idx] = exp + print("Inserted watch exp no. " .. watch_idx) + else + local _, _, size = string.find(answer, "^401 Error in Expression (%d+)$") + if size then + local err = client:receive(tonumber(size)):gsub(".-:%d+:%s*","") + print("Error: watch expression not set: " .. err) + else + print("Error: watch expression not set") + end + end + else + print("Invalid command") + end + elseif command == "delb" or command == "adelb" then + _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + -- if this is a file name, and not a file source + if not file:find('^".*"$') then + file = string.gsub(file, "\\", "/") -- convert slash + file = removebasedir(file, basedir) + end + client:send("DELB " .. file .. " " .. line .. "\n") + if command == "adelb" or client:receive() == "200 OK" then + remove_breakpoint(file, line) + else + print("Error: breakpoint not removed") + end + else + print("Invalid command") + end + elseif command == "delallb" then + local file, line = "*", 0 + client:send("DELB " .. file .. " " .. tostring(line) .. "\n") + if client:receive() == "200 OK" then + remove_breakpoint(file, line) + else + print("Error: all breakpoints not removed") + end + elseif command == "delw" then + local _, _, index = string.find(params, "^[a-z]+%s+(%d+)%s*$") + if index then + client:send("DELW " .. index .. "\n") + if client:receive() == "200 OK" then + watches[index] = nil + else + print("Error: watch expression not removed") + end + else + print("Invalid command") + end + elseif command == "delallw" then + for index, exp in pairs(watches) do + client:send("DELW " .. index .. "\n") + if client:receive() == "200 OK" then + watches[index] = nil + else + print("Error: watch expression at index " .. index .. " [" .. exp .. "] not removed") + end + end + elseif command == "eval" or command == "exec" + or command == "load" or command == "loadstring" + or command == "reload" then + local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$") + if exp or (command == "reload") then + if command == "eval" or command == "exec" then + exp = (exp:gsub("%-%-%[(=*)%[.-%]%1%]", "") -- remove comments + :gsub("%-%-.-\n", " ") -- remove line comments + :gsub("\n", " ")) -- convert new lines + if command == "eval" then exp = "return " .. exp end + client:send("EXEC " .. exp .. "\n") + elseif command == "reload" then + client:send("LOAD 0 -\n") + elseif command == "loadstring" then + local _, _, _, file, lines = string.find(exp, "^([\"'])(.-)%1%s(.+)") + if not file then + _, _, file, lines = string.find(exp, "^(%S+)%s(.+)") + end + client:send("LOAD " .. tostring(#lines) .. " " .. file .. "\n") + client:send(lines) + else + local file = io.open(exp, "r") + if not file and pcall(require, "winapi") then + -- if file is not open and winapi is there, try with a short path; + -- this may be needed for unicode paths on windows + winapi.set_encoding(winapi.CP_UTF8) + local shortp = winapi.short_path(exp) + file = shortp and io.open(shortp, "r") + end + if not file then return nil, nil, "Cannot open file " .. exp end + -- read the file and remove the shebang line as it causes a compilation error + local lines = file:read("*all"):gsub("^#!.-\n", "\n") + file:close() + + local file = string.gsub(exp, "\\", "/") -- convert slash + file = removebasedir(file, basedir) + client:send("LOAD " .. tostring(#lines) .. " " .. file .. "\n") + if #lines > 0 then client:send(lines) end + end + while true do + local params, err = client:receive() + if not params then + return nil, nil, "Debugger connection " .. (err or "error") + end + local done = true + local _, _, status, len = string.find(params, "^(%d+).-%s+(%d+)%s*$") + if status == "200" then + len = tonumber(len) + if len > 0 then + local status, res + local str = client:receive(len) + -- handle serialized table with results + local func, err = loadstring(str) + if func then + status, res = pcall(func) + if not status then err = res + elseif type(res) ~= "table" then + err = "received "..type(res).." instead of expected 'table'" + end + end + if err then + print("Error in processing results: " .. err) + return nil, nil, "Error in processing results: " .. err + end + print(unpack(res)) + return res[1], res + end + elseif status == "201" then + _, _, file, line = string.find(params, "^201 Started%s+(.-)%s+(%d+)%s*$") + elseif status == "202" or params == "200 OK" then + -- do nothing; this only happens when RE/LOAD command gets the response + -- that was for the original command that was aborted + elseif status == "204" then + local _, _, stream, size = string.find(params, "^204 Output (%w+) (%d+)$") + if stream and size then + local size = tonumber(size) + local msg = size > 0 and client:receive(size) or "" + print(msg) + if outputs[stream] then outputs[stream](msg) end + -- this was just the output, so go back reading the response + done = false + end + elseif status == "401" then + len = tonumber(len) + local res = client:receive(len) + print("Error in expression: " .. res) + return nil, nil, res + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response after EXEC/LOAD '" .. params .. "'" + end + if done then break end + end + else + print("Invalid command") + end + elseif command == "listb" then + for l, v in pairs(breakpoints) do + for f in pairs(v) do + print(f .. ": " .. l) + end + end + elseif command == "listw" then + for i, v in pairs(watches) do + print("Watch exp. " .. i .. ": " .. v) + end + elseif command == "suspend" then + client:send("SUSPEND\n") + elseif command == "stack" then + local opts = string.match(params, "^[a-z]+%s+(.+)$") + client:send("STACK" .. (opts and " "..opts or "") .."\n") + local resp = client:receive() + local _, _, status, res = string.find(resp, "^(%d+)%s+%w+%s+(.+)%s*$") + if status == "200" then + local func, err = loadstring(res) + if func == nil then + print("Error in stack information: " .. err) + return nil, nil, err + end + local ok, stack = pcall(func) + if not ok then + print("Error in stack information: " .. stack) + return nil, nil, stack + end + for _,frame in ipairs(stack) do + print(mobdebug.line(frame[1], {comment = false})) + end + return stack + elseif status == "401" then + local _, _, len = string.find(resp, "%s+(%d+)%s*$") + len = tonumber(len) + local res = len > 0 and client:receive(len) or "Invalid stack information." + print("Error in expression: " .. res) + return nil, nil, res + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response after STACK" + end + elseif command == "output" then + local _, _, stream, mode = string.find(params, "^[a-z]+%s+(%w+)%s+([dcr])%s*$") + if stream and mode then + client:send("OUTPUT "..stream.." "..mode.."\n") + local resp, err = client:receive() + if not resp then + print("Unknown error: "..err) + return nil, nil, "Debugger connection error: "..err + end + local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$") + if status == "200" then + print("Stream "..stream.." redirected") + outputs[stream] = type(options) == 'table' and options.handler or nil + -- the client knows when she is doing, so install the handler + elseif type(options) == 'table' and options.handler then + outputs[stream] = options.handler + else + print("Unknown error") + return nil, nil, "Debugger error: can't redirect "..stream + end + else + print("Invalid command") + end + elseif command == "basedir" then + local _, _, dir = string.find(params, "^[a-z]+%s+(.+)$") + if dir then + dir = string.gsub(dir, "\\", "/") -- convert slash + if not string.find(dir, "/$") then dir = dir .. "/" end + + local remdir = dir:match("\t(.+)") + if remdir then dir = dir:gsub("/?\t.+", "/") end + basedir = dir + + client:send("BASEDIR "..(remdir or dir).."\n") + local resp, err = client:receive() + if not resp then + print("Unknown error: "..err) + return nil, nil, "Debugger connection error: "..err + end + local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$") + if status == "200" then + print("New base directory is " .. basedir) + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response after BASEDIR" + end + else + print(basedir) + end + elseif command == "help" then + print("setb -- sets a breakpoint") + print("delb -- removes a breakpoint") + print("delallb -- removes all breakpoints") + print("setw -- adds a new watch expression") + print("delw -- removes the watch expression at index") + print("delallw -- removes all watch expressions") + print("run -- runs until next breakpoint") + print("step -- runs until next line, stepping into function calls") + print("over -- runs until next line, stepping over function calls") + print("out -- runs until line after returning from current function") + print("listb -- lists breakpoints") + print("listw -- lists watch expressions") + print("eval -- evaluates expression on the current context and returns its value") + print("exec -- executes statement on the current context") + print("load -- loads a local file for debugging") + print("reload -- restarts the current debugging session") + print("stack -- reports stack trace") + print("output stdout -- capture and redirect io stream (default|copy|redirect)") + print("basedir [] -- sets the base path of the remote application, or shows the current one") + print("done -- stops the debugger and continues application execution") + print("exit -- exits debugger and the application") + else + local _, _, spaces = string.find(params, "^(%s*)$") + if spaces then + return nil, nil, "Empty command" + else + print("Invalid command") + return nil, nil, "Invalid command" + end + end + return file, line +end + +-- Starts debugging server +local function listen(host, port) + host = host or "*" + port = port or mobdebug.port + + local socket = require "socket" + + print("Lua Remote Debugger") + print("Run the program you wish to debug") + + local server = socket.bind(host, port) + local client = server:accept() + + client:send("STEP\n") + client:receive() + + local breakpoint = client:receive() + local _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$") + if file and line then + print("Paused at file " .. file ) + print("Type 'help' for commands") + else + local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)%s*$") + if size then + print("Error in remote application: ") + print(client:receive(size)) + end + end + + while true do + io.write("> ") + local file, line, err = handle(io.read("*line"), client) + if not file and err == false then break end -- completed debugging + end + + client:close() +end + +local cocreate +local function coro() + if cocreate then return end -- only set once + cocreate = cocreate or coroutine.create + coroutine.create = function(f, ...) + return cocreate(function(...) + mobdebug.on() + return f(...) + end, ...) + end +end + +local moconew +local function moai() + if moconew then return end -- only set once + moconew = moconew or (MOAICoroutine and MOAICoroutine.new) + if not moconew then return end + MOAICoroutine.new = function(...) + local thread = moconew(...) + -- need to support both thread.run and getmetatable(thread).run, which + -- was used in earlier MOAI versions + local mt = thread.run and thread or getmetatable(thread) + local patched = mt.run + mt.run = function(self, f, ...) + return patched(self, function(...) + mobdebug.on() + return f(...) + end, ...) + end + return thread + end +end + +-- make public functions available +mobdebug.setbreakpoint = set_breakpoint +mobdebug.removebreakpoint = remove_breakpoint +mobdebug.listen = listen +mobdebug.loop = loop +mobdebug.scratchpad = scratchpad +mobdebug.handle = handle +mobdebug.connect = connect +mobdebug.start = start +mobdebug.on = on +mobdebug.off = off +mobdebug.moai = moai +mobdebug.coro = coro +mobdebug.done = done +mobdebug.pause = function() step_into = true end +mobdebug.yield = nil -- callback +mobdebug.output = output +mobdebug.onexit = os and os.exit or done +mobdebug.onscratch = nil -- callback +mobdebug.basedir = function(b) if b then basedir = b end return basedir end + +return mobdebug diff --git a/premake5.lua b/premake5.lua index a181548d..53aa5f97 100644 --- a/premake5.lua +++ b/premake5.lua @@ -73,6 +73,11 @@ trigger = "no-zlib", description = "Disable Zlib/Zip 3rd party lib" } + + newoption { + trigger = "no-luasocket", + description = "Disable Luasocket 3rd party lib" + } newoption { trigger = "bytecode", @@ -100,9 +105,14 @@ if not _OPTIONS["no-zlib"] then defines { "PREMAKE_COMPRESSION" } end + if not _OPTIONS["no-curl"] then defines { "CURL_STATICLIB", "PREMAKE_CURL"} end + + if not _OPTIONS["no-luasocket"] then + defines { "PREMAKE_LUASOCKET"} + end filter { 'system:windows' } platforms { 'x86', 'x64' } @@ -134,10 +144,16 @@ includedirs { "contrib/zlib", "contrib/libzip" } links { "zip-lib", "zlib-lib" } end + if not _OPTIONS["no-curl"] then includedirs { "contrib/curl/include" } links { "curl-lib" } end + + if not _OPTIONS["no-luasocket"] then + includedirs { "contrib/luasocket/src" } + links { "luasocket-lib" } + end files { @@ -195,14 +211,20 @@ group "contrib" include "contrib/lua" include "contrib/luashim" + if not _OPTIONS["no-zlib"] then include "contrib/zlib" include "contrib/libzip" end + if not _OPTIONS["no-curl"] then include "contrib/mbedtls" include "contrib/curl" end + + if not _OPTIONS["no-luasocket"] then + include "contrib/luasocket" + end group "Binary Modules" include "binmodules/example" diff --git a/src/_premake_init.lua b/src/_premake_init.lua index 2343ed57..0a24059c 100644 --- a/src/_premake_init.lua +++ b/src/_premake_init.lua @@ -1612,6 +1612,12 @@ description = "Treat warnings from project scripts as errors" } + newoption + { + trigger = "debugger", + description = "Start MobDebug remote debugger. Works with ZeroBrane Studio" + } + newoption { trigger = "file", diff --git a/src/_premake_main.lua b/src/_premake_main.lua index ea99a754..0977af4c 100644 --- a/src/_premake_main.lua +++ b/src/_premake_main.lua @@ -254,6 +254,20 @@ end end +--- +-- Start up MobDebug and try to hook up with ZeroBrane +--- + + function m.tryHookDebugger() + + if (_OPTIONS["debugger"]) then + + print("Starting debugger...") + local mobdebug = require('mobdebug') + mobdebug.start() + + end + end --- -- Override point, for logic that should run before baking. @@ -354,6 +368,7 @@ -- m.elements = { + m.tryHookDebugger, m.installModuleLoader, m.locateUserScript, m.prepareEnvironment, diff --git a/src/host/buffered_io.c b/src/host/buffered_io.c index 018402be..ea0bd6c8 100644 --- a/src/host/buffered_io.c +++ b/src/host/buffered_io.c @@ -9,7 +9,7 @@ #include "premake.h" #include "buffered_io.h" -void buffer_init(Buffer* b) +void buffer_initialize(Buffer* b) { b->capacity = 0; b->length = 0; @@ -69,7 +69,7 @@ void buffer_printf(Buffer* b, const char *fmt, ...) int buffered_new(lua_State* L) { Buffer* b = (Buffer*)malloc(sizeof(Buffer)); - buffer_init(b); + buffer_initialize(b); lua_pushlightuserdata(L, b); return 1; } diff --git a/src/host/buffered_io.h b/src/host/buffered_io.h index 50bdfb99..125b0405 100644 --- a/src/host/buffered_io.h +++ b/src/host/buffered_io.h @@ -15,7 +15,7 @@ typedef struct struct_Buffer char* data; } Buffer; -void buffer_init(Buffer* b); +void buffer_initialize(Buffer* b); void buffer_destroy(Buffer* b); void buffer_puts(Buffer* b, const void* ptr, size_t len); diff --git a/src/host/curl_utils.c b/src/host/curl_utils.c index 99aad2c0..5c89eb76 100644 --- a/src/host/curl_utils.c +++ b/src/host/curl_utils.c @@ -74,7 +74,7 @@ CURL* curlRequest(lua_State* L, curl_state* state, int optionsIndex, int progres state->RefIndex = 0; state->errorBuffer[0] = '\0'; state->headers = NULL; - buffer_init(&state->S); + buffer_initialize(&state->S); curl_init(); curl = curl_easy_init(); diff --git a/src/host/premake.c b/src/host/premake.c index 1a497539..bacafefa 100644 --- a/src/host/premake.c +++ b/src/host/premake.c @@ -193,6 +193,11 @@ int premake_init(lua_State* L) luaL_register(L, "zip", zip_functions); #endif +#ifdef PREMAKE_LUASOCKET + luaL_requiref(L, "socket", luaopen_socket_core, 1); + lua_pop(L, 1); +#endif + lua_pushlightuserdata(L, &s_shimTable); lua_rawseti(L, LUA_REGISTRYINDEX, 'SHIM'); diff --git a/src/host/premake.h b/src/host/premake.h index 1ce8c1c1..6a2843b0 100644 --- a/src/host/premake.h +++ b/src/host/premake.h @@ -171,6 +171,10 @@ int http_download(lua_State* L); int zip_extract(lua_State* L); #endif +#ifdef PREMAKE_LUASOCKET +int luaopen_socket_core(lua_State* L); +#endif + #ifdef _MSC_VER #ifndef snprintf #define snprintf _snprintf