diff options
Diffstat (limited to 'etc/lp.lua')
| -rw-r--r-- | etc/lp.lua | 323 |
1 files changed, 0 insertions, 323 deletions
diff --git a/etc/lp.lua b/etc/lp.lua deleted file mode 100644 index 25f0b95..0000000 --- a/etc/lp.lua +++ /dev/null | |||
| @@ -1,323 +0,0 @@ | |||
| 1 | ----------------------------------------------------------------------------- | ||
| 2 | -- LPD support for the Lua language | ||
| 3 | -- LuaSocket toolkit. | ||
| 4 | -- Author: David Burgess | ||
| 5 | -- Modified by Diego Nehab, but David is in charge | ||
| 6 | ----------------------------------------------------------------------------- | ||
| 7 | --[[ | ||
| 8 | if you have any questions: RFC 1179 | ||
| 9 | ]] | ||
| 10 | -- make sure LuaSocket is loaded | ||
| 11 | local io = require("io") | ||
| 12 | local base = _G | ||
| 13 | local os = require("os") | ||
| 14 | local math = require("math") | ||
| 15 | local string = require("string") | ||
| 16 | local socket = require("socket") | ||
| 17 | local ltn12 = require("ltn12") | ||
| 18 | module("socket.lp") | ||
| 19 | |||
| 20 | -- default port | ||
| 21 | PORT = 515 | ||
| 22 | SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" | ||
| 23 | PRINTER = os.getenv("PRINTER") or "printer" | ||
| 24 | |||
| 25 | local function connect(localhost, option) | ||
| 26 | local host = option.host or SERVER | ||
| 27 | local port = option.port or PORT | ||
| 28 | local skt | ||
| 29 | local try = socket.newtry(function() if skt then skt:close() end end) | ||
| 30 | if option.localbind then | ||
| 31 | -- bind to a local port (if we can) | ||
| 32 | local localport = 721 | ||
| 33 | local done, err | ||
| 34 | repeat | ||
| 35 | skt = socket.try(socket.tcp()) | ||
| 36 | try(skt:settimeout(30)) | ||
| 37 | done, err = skt:bind(localhost, localport) | ||
| 38 | if not done then | ||
| 39 | localport = localport + 1 | ||
| 40 | skt:close() | ||
| 41 | skt = nil | ||
| 42 | else break end | ||
| 43 | until localport > 731 | ||
| 44 | socket.try(skt, err) | ||
| 45 | else skt = socket.try(socket.tcp()) end | ||
| 46 | try(skt:connect(host, port)) | ||
| 47 | return { skt = skt, try = try } | ||
| 48 | end | ||
| 49 | |||
| 50 | --[[ | ||
| 51 | RFC 1179 | ||
| 52 | 5.3 03 - Send queue state (short) | ||
| 53 | |||
| 54 | +----+-------+----+------+----+ | ||
| 55 | | 03 | Queue | SP | List | LF | | ||
| 56 | +----+-------+----+------+----+ | ||
| 57 | Command code - 3 | ||
| 58 | Operand 1 - Printer queue name | ||
| 59 | Other operands - User names or job numbers | ||
| 60 | |||
| 61 | If the user names or job numbers or both are supplied then only those | ||
| 62 | jobs for those users or with those numbers will be sent. | ||
| 63 | |||
| 64 | The response is an ASCII stream which describes the printer queue. | ||
| 65 | The stream continues until the connection closes. Ends of lines are | ||
| 66 | indicated with ASCII LF control characters. The lines may also | ||
| 67 | contain ASCII HT control characters. | ||
| 68 | |||
| 69 | 5.4 04 - Send queue state (long) | ||
| 70 | |||
| 71 | +----+-------+----+------+----+ | ||
| 72 | | 04 | Queue | SP | List | LF | | ||
| 73 | +----+-------+----+------+----+ | ||
| 74 | Command code - 4 | ||
| 75 | Operand 1 - Printer queue name | ||
| 76 | Other operands - User names or job numbers | ||
| 77 | |||
| 78 | If the user names or job numbers or both are supplied then only those | ||
| 79 | jobs for those users or with those numbers will be sent. | ||
| 80 | |||
| 81 | The response is an ASCII stream which describes the printer queue. | ||
| 82 | The stream continues until the connection closes. Ends of lines are | ||
| 83 | indicated with ASCII LF control characters. The lines may also | ||
| 84 | contain ASCII HT control characters. | ||
| 85 | ]] | ||
| 86 | |||
| 87 | -- gets server acknowledement | ||
| 88 | local function recv_ack(con) | ||
| 89 | local ack = con.skt:receive(1) | ||
| 90 | con.try(string.char(0) == ack, "failed to receive server acknowledgement") | ||
| 91 | end | ||
| 92 | |||
| 93 | -- sends client acknowledement | ||
| 94 | local function send_ack(con) | ||
| 95 | local sent = con.skt:send(string.char(0)) | ||
| 96 | con.try(sent == 1, "failed to send acknowledgement") | ||
| 97 | end | ||
| 98 | |||
| 99 | -- sends queue request | ||
| 100 | -- 5.2 02 - Receive a printer job | ||
| 101 | -- | ||
| 102 | -- +----+-------+----+ | ||
| 103 | -- | 02 | Queue | LF | | ||
| 104 | -- +----+-------+----+ | ||
| 105 | -- Command code - 2 | ||
| 106 | -- Operand - Printer queue name | ||
| 107 | -- | ||
| 108 | -- Receiving a job is controlled by a second level of commands. The | ||
| 109 | -- daemon is given commands by sending them over the same connection. | ||
| 110 | -- The commands are described in the next section (6). | ||
| 111 | -- | ||
| 112 | -- After this command is sent, the client must read an acknowledgement | ||
| 113 | -- octet from the daemon. A positive acknowledgement is an octet of | ||
| 114 | -- zero bits. A negative acknowledgement is an octet of any other | ||
| 115 | -- pattern. | ||
| 116 | local function send_queue(con, queue) | ||
| 117 | queue = queue or PRINTER | ||
| 118 | local str = string.format("\2%s\10", queue) | ||
| 119 | local sent = con.skt:send(str) | ||
| 120 | con.try(sent == string.len(str), "failed to send print request") | ||
| 121 | recv_ack(con) | ||
| 122 | end | ||
| 123 | |||
| 124 | -- sends control file | ||
| 125 | -- 6.2 02 - Receive control file | ||
| 126 | -- | ||
| 127 | -- +----+-------+----+------+----+ | ||
| 128 | -- | 02 | Count | SP | Name | LF | | ||
| 129 | -- +----+-------+----+------+----+ | ||
| 130 | -- Command code - 2 | ||
| 131 | -- Operand 1 - Number of bytes in control file | ||
| 132 | -- Operand 2 - Name of control file | ||
| 133 | -- | ||
| 134 | -- The control file must be an ASCII stream with the ends of lines | ||
| 135 | -- indicated by ASCII LF. The total number of bytes in the stream is | ||
| 136 | -- sent as the first operand. The name of the control file is sent as | ||
| 137 | -- the second. It should start with ASCII "cfA", followed by a three | ||
| 138 | -- digit job number, followed by the host name which has constructed the | ||
| 139 | -- control file. Acknowledgement processing must occur as usual after | ||
| 140 | -- the command is sent. | ||
| 141 | -- | ||
| 142 | -- The next "Operand 1" octets over the same TCP connection are the | ||
| 143 | -- intended contents of the control file. Once all of the contents have | ||
| 144 | -- been delivered, an octet of zero bits is sent as an indication that | ||
| 145 | -- the file being sent is complete. A second level of acknowledgement | ||
| 146 | -- processing must occur at this point. | ||
| 147 | |||
| 148 | -- sends data file | ||
| 149 | -- 6.3 03 - Receive data file | ||
| 150 | -- | ||
| 151 | -- +----+-------+----+------+----+ | ||
| 152 | -- | 03 | Count | SP | Name | LF | | ||
| 153 | -- +----+-------+----+------+----+ | ||
| 154 | -- Command code - 3 | ||
| 155 | -- Operand 1 - Number of bytes in data file | ||
| 156 | -- Operand 2 - Name of data file | ||
| 157 | -- | ||
| 158 | -- The data file may contain any 8 bit values at all. The total number | ||
| 159 | -- of bytes in the stream may be sent as the first operand, otherwise | ||
| 160 | -- the field should be cleared to 0. The name of the data file should | ||
| 161 | -- start with ASCII "dfA". This should be followed by a three digit job | ||
| 162 | -- number. The job number should be followed by the host name which has | ||
| 163 | -- constructed the data file. Interpretation of the contents of the | ||
| 164 | -- data file is determined by the contents of the corresponding control | ||
| 165 | -- file. If a data file length has been specified, the next "Operand 1" | ||
| 166 | -- octets over the same TCP connection are the intended contents of the | ||
| 167 | -- data file. In this case, once all of the contents have been | ||
| 168 | -- delivered, an octet of zero bits is sent as an indication that the | ||
| 169 | -- file being sent is complete. A second level of acknowledgement | ||
| 170 | -- processing must occur at this point. | ||
| 171 | |||
| 172 | |||
| 173 | local function send_hdr(con, control) | ||
| 174 | local sent = con.skt:send(control) | ||
| 175 | con.try(sent and sent >= 1 , "failed to send header file") | ||
| 176 | recv_ack(con) | ||
| 177 | end | ||
| 178 | |||
| 179 | local function send_control(con, control) | ||
| 180 | local sent = con.skt:send(control) | ||
| 181 | con.try(sent and sent >= 1, "failed to send control file") | ||
| 182 | send_ack(con) | ||
| 183 | end | ||
| 184 | |||
| 185 | local function send_data(con,fh,size) | ||
| 186 | local buf | ||
| 187 | while size > 0 do | ||
| 188 | buf,message = fh:read(8192) | ||
| 189 | if buf then | ||
| 190 | st = con.try(con.skt:send(buf)) | ||
| 191 | size = size - st | ||
| 192 | else | ||
| 193 | con.try(size == 0, "file size mismatch") | ||
| 194 | end | ||
| 195 | end | ||
| 196 | recv_ack(con) -- note the double acknowledgement | ||
| 197 | send_ack(con) | ||
| 198 | recv_ack(con) | ||
| 199 | return size | ||
| 200 | end | ||
| 201 | |||
| 202 | |||
| 203 | --[[ | ||
| 204 | local control_dflt = { | ||
| 205 | "H"..string.sub(socket.hostname,1,31).."\10", -- host | ||
| 206 | "C"..string.sub(socket.hostname,1,31).."\10", -- class | ||
| 207 | "J"..string.sub(filename,1,99).."\10", -- jobname | ||
| 208 | "L"..string.sub(user,1,31).."\10", -- print banner page | ||
| 209 | "I"..tonumber(indent).."\10", -- indent column count ('f' only) | ||
| 210 | "M"..string.sub(mail,1,128).."\10", -- mail when printed user@host | ||
| 211 | "N"..string.sub(filename,1,131).."\10", -- name of source file | ||
| 212 | "P"..string.sub(user,1,31).."\10", -- user name | ||
| 213 | "T"..string.sub(title,1,79).."\10", -- title for banner ('p' only) | ||
| 214 | "W"..tonumber(width or 132).."\10", -- width of print f,l,p only | ||
| 215 | |||
| 216 | "f"..file.."\10", -- formatted print (remove control chars) | ||
| 217 | "l"..file.."\10", -- print | ||
| 218 | "o"..file.."\10", -- postscript | ||
| 219 | "p"..file.."\10", -- pr format - requires T, L | ||
| 220 | "r"..file.."\10", -- fortran format | ||
| 221 | "U"..file.."\10", -- Unlink (data file only) | ||
| 222 | } | ||
| 223 | ]] | ||
| 224 | |||
| 225 | -- generate a varying job number | ||
| 226 | local seq = 0 | ||
| 227 | local function newjob(connection) | ||
| 228 | seq = seq + 1 | ||
| 229 | return math.floor(socket.gettime() * 1000 + seq)%1000 | ||
| 230 | end | ||
| 231 | |||
| 232 | |||
| 233 | local format_codes = { | ||
| 234 | binary = 'l', | ||
| 235 | text = 'f', | ||
| 236 | ps = 'o', | ||
| 237 | pr = 'p', | ||
| 238 | fortran = 'r', | ||
| 239 | l = 'l', | ||
| 240 | r = 'r', | ||
| 241 | o = 'o', | ||
| 242 | p = 'p', | ||
| 243 | f = 'f' | ||
| 244 | } | ||
| 245 | |||
| 246 | -- lp.send{option} | ||
| 247 | -- requires option.file | ||
| 248 | |||
| 249 | send = socket.protect(function(option) | ||
| 250 | socket.try(option and base.type(option) == "table", "invalid options") | ||
| 251 | local file = option.file | ||
| 252 | socket.try(file, "invalid file name") | ||
| 253 | local fh = socket.try(io.open(file,"rb")) | ||
| 254 | local datafile_size = fh:seek("end") -- get total size | ||
| 255 | fh:seek("set") -- go back to start of file | ||
| 256 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") | ||
| 257 | or "localhost" | ||
| 258 | local con = connect(localhost, option) | ||
| 259 | -- format the control file | ||
| 260 | local jobno = newjob() | ||
| 261 | local localip = socket.dns.toip(localhost) | ||
| 262 | localhost = string.sub(localhost,1,31) | ||
| 263 | local user = string.sub(option.user or os.getenv("LPRUSER") or | ||
| 264 | os.getenv("USERNAME") or os.getenv("USER") or "anonymous", 1,31) | ||
| 265 | local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); | ||
| 266 | local fmt = format_codes[option.format] or 'l' | ||
| 267 | local class = string.sub(option.class or localip or localhost,1,31) | ||
| 268 | local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") | ||
| 269 | ctlfn = string.sub(ctlfn or file,1,131) | ||
| 270 | local cfile = | ||
| 271 | string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", | ||
| 272 | localhost, | ||
| 273 | class, | ||
| 274 | option.job or "LuaSocket", | ||
| 275 | user, | ||
| 276 | fmt, lpfile, | ||
| 277 | lpfile, | ||
| 278 | ctlfn); -- mandatory part of ctl file | ||
| 279 | if (option.banner) then cfile = cfile .. 'L'..user..'\10' end | ||
| 280 | if (option.indent) then cfile = cfile .. 'I'..base.tonumber(option.indent)..'\10' end | ||
| 281 | if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end | ||
| 282 | if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end | ||
| 283 | if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then | ||
| 284 | cfile = cfile .. 'W'..base.tonumber(option,width)..'\10' | ||
| 285 | end | ||
| 286 | |||
| 287 | con.skt:settimeout(option.timeout or 65) | ||
| 288 | -- send the queue header | ||
| 289 | send_queue(con, option.queue) | ||
| 290 | -- send the control file header | ||
| 291 | local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); | ||
| 292 | send_hdr(con,cfilecmd) | ||
| 293 | |||
| 294 | -- send the control file | ||
| 295 | send_control(con,cfile) | ||
| 296 | |||
| 297 | -- send the data file header | ||
| 298 | local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); | ||
| 299 | send_hdr(con,dfilecmd) | ||
| 300 | |||
| 301 | -- send the data file | ||
| 302 | send_data(con,fh,datafile_size) | ||
| 303 | fh:close() | ||
| 304 | con.skt:close(); | ||
| 305 | return jobno, datafile_size | ||
| 306 | end) | ||
| 307 | |||
| 308 | -- | ||
| 309 | -- lp.query({host=,queue=printer|'*', format='l'|'s', list=}) | ||
| 310 | -- | ||
| 311 | query = socket.protect(function(p) | ||
| 312 | p = p or {} | ||
| 313 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") | ||
| 314 | or "localhost" | ||
| 315 | local con = connect(localhost,p) | ||
| 316 | local fmt | ||
| 317 | if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end | ||
| 318 | con.try(con.skt:send(string.format("%c%s %s\n", fmt, p.queue or "*", | ||
| 319 | p.list or ""))) | ||
| 320 | local data = con.try(con.skt:receive("*a")) | ||
| 321 | con.skt:close() | ||
| 322 | return data | ||
| 323 | end) | ||
