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) | ||