Handle hostnames with upper-case letters
[webmin.git] / lpadmin / cups-lib.pl
1 # cups-lib.pl
2 # Functions for CUPS printer management
3
4 $lpstat = &has_command("lpstat.cups") ? "lpstat.cups" : "lpstat";
5 $lpadmin = &has_command("lpadmin.cups") ? "lpadmin.cups" : "lpadmin";
6 $lpr = &has_command("lpr.cups") ? "lpr.cups" : "lpr";
7 $lprm = &has_command("lprm.cups") ? "lprm.cups" : "lprm";
8 $lpq = &has_command("lpq.cups") ? "lpq.cups" : "lpq";
9
10 # list_printers()
11 # Returns an array of known printer names
12 sub list_printers
13 {
14 return () if (!&sched_running());
15 local(@rv, $_);
16 open(STAT, "$lpstat -v |");
17 while(<STAT>) {
18         if (/\s+for\s+(\S+):/i) { push(@rv, $1); }
19         }
20 close(STAT);
21 return &unique(@rv);
22 }
23
24 # get_printer(name, [nostatus])
25 # Returns a reference to an associative array of printer details
26 sub get_printer
27 {
28 local($stat, @rv, $body, $avl, $con, $sys, %prn, $_, $out);
29 local $esc = quotemeta($_[0]);
30 $out = &backquote_command("$lpstat -l -p $esc", 1);
31 return undef if ($out =~ /non-existent\s+printer/);
32 if ($out =~ /^printer\s+(\S+)\s+(\S+).*\n([\000-\377]*)$/) {
33         # printer exists..
34         $prn{'name'} = $1;
35         $prn{'enabled'} = $2 ne "disabled";
36         $body = $3;
37         }
38 else {
39         # no printer found
40         return undef;
41         }
42 if (!$prn{'enabled'} && $body =~ /^\s+(.*)/) {
43         $prn{'enabled_why'} = lc($1) eq "paused" || lc($1) eq "reason unknown" ?
44                               "" : $1;
45         }
46 if ($body =~ /Description: (.*)/) { $prn{'desc'} = $1; }
47 if ($body =~ /Printer types: (.*)/) { $prn{'ptype'} = $1; }
48 if ($body =~ /Interface: (.*)/) { $prn{'iface'} = $1; }
49 if ($body =~ /Banner not required/) { $prn{'banner'} = 0; }
50 else { $prn{'banner'} = 1; }
51
52 if (!$_[1]) {
53         # request availability
54         $avl = &backquote_command("$lpstat -a $prn{'name'} 2>&1", 1);
55         if ($avl =~ /^\S+\s+not accepting.*\n\s*(.*)/) {
56                 $prn{'accepting'} = 0;
57                 $prn{'accepting_why'} = lc($1) eq "reason unknown" ? "" : $1;
58                 }
59         else { $prn{'accepting'} = 1; }
60         }
61
62 # Try to find the device URI, from printers.conf or lpstat -v
63 local $uri;
64 foreach my $file ("/etc/printers.conf", "/etc/cups/printers.conf") {
65         next if (!-r $file);
66         local $lref = &read_file_lines($file);
67         local $inprinter;
68         foreach $l (@$lref) {
69                 if ($l =~ /^\s*<(Default)?Printer\s+(\S+)>/) {
70                         # Start of a new printer
71                         $inprinter = $2;
72                         }
73                 elsif ($l =~ /^\s*DeviceURI\s+(.*)/ &&
74                        $inprinter eq $prn{'name'}) {
75                         $uri = $1;
76                         }
77                 }
78         }
79 if (!$uri) {
80         $con = &backquote_command("$lpstat -v $prn{'name'} 2>&1", 1);
81         if ($con =~ /^device for \S+:\s+(\S+)/) {
82                 $uri = $1;
83                 }
84         }
85
86 # request connection
87 if ($uri =~ /^(lpd|ipp):\/\/([^\s\/]+)\/(\S+)/) {
88         $prn{'rhost'} = $2;
89         $prn{'rqueue'} = $3;
90         if ($1 eq 'ipp') {
91                 $prn{'rtype'} = 'ipp';
92                 $prn{'rhost'} =~ s/:631//;
93                 }
94         else {
95                 $prn{'rhost'} =~ s/:515//;
96                 }
97         }
98 elsif ($uri =~ /^socket:\/\/(\S+):(\d+)/) {
99         $prn{'dhost'} = $1;
100         $prn{'dport'} = $2;
101         }
102 elsif ($uri =~ /^(file|serial|parallel|usb):([^\s\?]+)/) {
103         $prn{'dev'} = $2;
104         }
105 else {
106         $prn{'dev'} = $uri;
107         }
108
109 # Check if this is the default printer
110 if (&backquote_command("$lpstat -d 2>&1", 1) =~ /destination:\s+(\S+)/ &&
111     $1 eq $prn{'name'}) {
112         $prn{'default'} = 1;
113         }
114
115 return \%prn;
116 }
117
118 # get_jobs(printer)
119 sub get_jobs
120 {
121 local (@jobs, $htype);
122 local $esc = quotemeta($_[0]);
123 open(LPQ, "$lpq -P$esc |");
124 while(<LPQ>) {
125         s/\r|\n//g;
126         next if (/^Rank/i || /^$_[0]/);
127         if (/^(\S+)\s+(\S+)\s+(\d+)\s+(.*\S)\s+(\d+)\s+(\S.*)$/ ||
128             /^(\S+)\s+(\S{1,8}?)\s*(\d+)\s+(.*\S)\s+(\d+)\s+(\S.*)$/) {
129                 # Normal lpq output
130                 local(%job, $f, @pq);
131                 $job{'id'} = $3;
132                 $job{'user'} = $2;
133                 $job{'size'} = $5;
134                 $job{'file'} = $4;
135                 $job{'printing'} = ($1 eq "active");
136                 local $d = $config{'spool_dir'};
137                 opendir(DIR, $d);
138                 while($f = readdir(DIR)) {
139                         if ($f =~ /^d(\d+)\-(\d+)$/ && $1 == $job{'id'}) {
140                                 push(@pq, "$d/$f");
141                                 local @st = stat("$d/$f");
142                                 if (@st) {
143                                         $job{'time'} ||= $st[9];
144                                         }
145                                 }
146                         }
147                 closedir(DIR);
148                 $job{'printfile'} = @pq ? \@pq : undef;
149                 if ($job{'time'}) {
150                         $job{'when'} = &make_date($job{'time'});
151                         }
152                 push(@jobs, \%job);
153                 }
154         }
155 close(LPQ);
156 return @jobs;
157 }
158
159 # printer_support(option)
160 sub printer_support
161 {
162 return $_[0] !~ /^(msize|alias|rnoqueue|ctype|sysv|allow)$/;
163 }
164
165 # list_classes()
166 # Returns an associative array of print classes
167 sub list_classes
168 {
169 local($stat, %rv);
170 $stat = &backquote_command("$lpstat -c 2>&1", 1);
171 while($stat =~ /^members of class (\S+):\n((\s+\S+\n)+)([\000-\377]*)$/) {
172         $stat = $4;
173         $rv{$1} = [ grep { $_ ne "" } split(/\s+/, $2) ];
174         }
175 return \%rv;
176 }
177
178 # create_printer(&details)
179 # Create a new printer
180 sub create_printer
181 {
182 &modify_printer($_[0]);
183 }
184
185 # modify_printer(&details)
186 # Change an existing printer
187 sub modify_printer
188 {
189 local(%prn, $cmd, $out);
190 %prn = %{$_[0]};
191
192 # call lpadmin
193 local $esc = quotemeta($prn{'name'});
194 local $desc = quotemeta($prn{'desc'}) || "''";
195 $cmd = "$lpadmin -p $esc -D $desc";
196 local $vesc = quotemeta($prn{'dev'});
197 if ($prn{'dev'} =~ /\/dev\/tty/) {
198         $cmd .= " -v serial:$vesc";
199         }
200 elsif ($prn{'dev'} =~ /\/dev\/lp/) {
201         $cmd .= " -v parallel:$vesc";
202         }
203 elsif ($prn{'dev'} =~ /\/dev\/usb/) {
204         $cmd .= " -v usb:$vesc";
205         }
206 elsif ($prn{'dev'}) {
207         $cmd .= " -v $vesc";
208         }
209 elsif ($prn{'rhost'}) {
210         local $resc = quotemeta($prn{'rhost'});
211         local $qesc = quotemeta($prn{'rqueue'});
212         if ($prn{'rtype'} eq 'ipp') {
213                 $cmd .= " -v ipp://$resc/$qesc";
214                 }
215         else {
216                 $cmd .= " -v lpd://$resc/$qesc";
217                 }
218         }
219 else {
220         local $resc = quotemeta($prn{'dhost'});
221         local $pesc = quotemeta($prn{'dport'});
222         $cmd .= " -v socket://$resc:$pesc";
223         }
224 if ($prn{'iface'}) {
225         local $iesc = quotemeta($prn{'iface'});
226         $cmd .= " -i $iesc";
227         }
228 foreach $o (keys %$cups_driver_options) {
229         $cmd .= " -o ".quotemeta($o)."=".quotemeta($cups_driver_options->{$o});
230         }
231 $out = &backquote_logged("cd / ; $cmd 2>&1");
232 if ($?) { &error("$lpadmin failed : <pre>$out</pre>"); }
233
234 # make the default
235 if ($prn{'default'}) {
236         $out = &backquote_logged("cd / ; $lpadmin -d $esc 2>&1");
237         if ($?) { &error("Failed to set default : <pre>$out</pre>"); }
238         }
239
240 # enable or disable
241 local $enable = &has_command("cupsenable") || &has_command("enable");
242 local $disable = &has_command("cupsdisable") || &has_command("disable");
243 if ($prn{'enabled'}) { $cmd = "$enable $esc"; }
244 elsif ($prn{'enabled_why'}) {
245         local $wesc = quotemeta($prn{'enabled_why'});
246         $cmd = "$enable $esc ; $disable -r $wesc $esc";
247         }
248 else { $cmd = "$enable $esc ; $disable $esc"; }
249 $out = &backquote_logged("$cmd 2>&1");
250
251 # accepting or rejecting requests
252 if ($prn{'accepting'}) { $cmd = "accept $esc"; }
253 elsif ($prn{'accepting_why'}) {
254         local $wesc = quotemeta($prn{'accepting_why'});
255         $cmd = "accept $esc ; reject -r $wesc $esc";
256         }
257 else { $cmd = "accept $esc ; reject $esc"; }
258 $out = &backquote_logged("$cmd 2>&1");
259 }
260
261 # delete_printer(name)
262 # Deletes some existing printer
263 sub delete_printer
264 {
265 local($out);
266 local $esc = quotemeta($_[0]);
267 $out = &backquote_logged("$lpadmin -x $esc 2>&1");
268 if ($?) { &error("$lpadmin failed : <pre>$out</pre>"); }
269 }
270
271 # cancel_job(printer, id)
272 # Cancels some print job
273 sub cancel_job
274 {
275 local($out);
276 local $esc = quotemeta($_[0]);
277 local $iesc = quotemeta($_[1]);
278 $out = &backquote_logged("$lprm -P$esc $iesc 2>&1");
279 if ($?) { &error("$lprm failed : <pre>$out</pre>"); }
280 sleep(1);
281 }
282
283 # sched_running()
284 # Returns the pid if lpsched is running, 0 if not, -1 if cannot be stopped
285 sub sched_running
286 {
287 @pid = &find_byname("cups");
288 if (@pid) { return $pid[0]; }
289 return 0;
290 }
291
292 # start_sched()
293 # Start lpsched
294 sub start_sched
295 {
296 local $out = &backquote_logged("cupsd 2>&1");
297 if ($?) { &error("failed to start cups : <tt>$out</tt>"); }
298 sleep(3);
299 }
300
301 # stop_sched(pid)
302 # Stop the running lpsched process
303 sub stop_sched
304 {
305 local @pid = ( &find_byname("cupsd") );
306 &kill_logged('TERM', @pid) || &error("Failed to stop cups : $!");
307 }
308
309 # print_command(printer, file)
310 # Returns the command to print some file on some printer
311 sub print_command
312 {
313 local $esc = quotemeta($_[0]);
314 local $fesc = quotemeta($_[1]);
315 return "$lpr -P$esc $fesc";
316 }
317
318 # check_print_system()
319 # Returns an error message if CUPS is not installed
320 sub check_print_system
321 {
322 &has_command($lpstat) || return &text('cups_ecmd', "<tt>$lpstat</tt>");
323 &has_command($lpadmin) || return &text('cups_ecmd', "<tt>$lpadmin</tt>");
324 return undef;
325 }
326
327 if (-r "/dev/lp0") {
328         @device_files = ("/dev/lp0", "/dev/lp1", "/dev/lp2", "/dev/lp3",
329                          "/dev/ttyS0", "/dev/ttyS1", "/dev/null");
330         }
331 else {
332         @device_files = ("/dev/lp1", "/dev/lp2", "/dev/lp2", "/dev/lp3",
333                          "/dev/ttyS0", "/dev/ttyS1", "/dev/null");
334         }
335 if (-r "/dev/usblp0") {
336         push(@device_files, "/dev/usblp0", "/dev/usblp1",
337                             "/dev/usblp2", "/dev/usblp3");
338         }
339 elsif (-r "/dev/usb/lp0") {
340         push(@device_files, "/dev/usb/lp0", "/dev/usb/lp1",
341                             "/dev/usb/lp2", "/dev/usb/lp3");
342         }
343 @device_names = (&text('linux_paralel', "1"), &text('linux_paralel', "2"),
344                  &text('linux_paralel', "3"), &text('linux_paralel', "4"),
345                  &text('linux_serial', "1"), &text('linux_serial', "2"),
346                  $text{'linux_null'},  &text('linux_usb', 1),
347                  &text('linux_usb', 2), &text('linux_usb', 3),
348                  &text('linux_usb', 4));
349