Handle hostnames with upper-case letters
[webmin.git] / lpadmin / solaris-lib.pl
1 # solaris-lib.pl
2 # Functions for solaris/hpux-style printer management
3
4 # Find lpsched command
5 # But first define the default.
6 $config{'lpschedcmd'} = "/usr/lib/lp/lpsched";
7 if (-x "/usr/lib/lpsched") {
8         $config{'lpschedcmd'} = "/usr/lib/lpsched";
9 }
10
11 $interface_dir = "/usr/lib/lp/model";
12 $default_interface = "$interface_dir/standard";
13 $netstandard_interface = "$interface_dir/netstandard";
14 $foomatic_interface = "$interface_dir/standard_foomatic";
15 $foomatic_netstandard_interface = "$interface_dir/netstandard_foomatic";
16
17 # list_printers()
18 # Returns an array of known printer names
19 sub list_printers
20 {
21 return () if (!&sched_running());
22 local(@rv, $_);
23 if (open(CONF, "/etc/printers.conf")) {
24         # Printers can be read from a file
25         while(<CONF>) {
26                 s/\r|\n//g;
27                 s/#.*$//;
28                 if (/^(\S+):/ && $1 ne '_default') { push(@rv, $1); }
29                 }
30         close(CONF);
31         }
32 else {
33         # Must use a command to get printers
34         open(STAT, "lpstat -v |");
35         while(<STAT>) {
36                 if (/\s+for\s+(\S+):/i && $1 ne '_default') { push(@rv, $1); }
37                 }
38         close(STAT);
39         }
40 return &unique(@rv);
41 }
42
43 # get_printer(name, [nostatus])
44 # Returns a reference to an associative array of printer details
45 sub get_printer
46 {
47 local($stat, @rv, $body, $avl, $con, $sys, %prn, $_, $out);
48 local $esc = quotemeta($_[0]);
49 $out = &backquote_command("lpstat -l -p $esc", 1);
50 if ($out =~ /^printer\s+(\S+)\s*(.*)\s+(enabled|disabled)\s+since\s+([^\.]*)\.\s+(.*)\.\n([\000-\377]*)$/) {
51         # printer exists
52         $prn{'name'} = $1;
53         $prn{'enabled'} = $3 eq "enabled";
54         $body = $6;
55         }
56 elsif ($out =~ /^printer\s+(\S+)\s+waiting for auto-retry.\s+(\S+)\.\n([\000-\377]*)$/) {
57         # printer has some problem
58         $prn{'name'} = $1;
59         $prn{'enabled'} = 1;
60         $body = $3;
61         }
62 else {
63         # no printer found
64         return undef;
65         }
66 if (!$prn{'enabled'} && $body =~ /^\s+(.*)/) {
67         $prn{'enabled_why'} = $1 eq "unknown reason" ? "" : $1;
68         }
69 if ($body =~ /Description: (.*)/) { $prn{'desc'} = $1; }
70 if ($body =~ /Printer types: (.*)/) { $prn{'ptype'} = $1; }
71 if ($body =~ /Interface: (.*)/ && $1 ne $default_interface)
72         { $prn{'iface'} = $1; }
73 if ($body =~ /Banner not required/) { $prn{'banner'} = 0; }
74 else { $prn{'banner'} = 1; }
75 if ($body =~ /Content types: (.*)/) { $prn{'ctype'} = [ split(/[ ,]+/, $1) ]; }
76 if ($body =~ /Users (allowed|denied):\n((\s+\S+\n)+)/) {
77         local(@l);
78         @l = grep { $_ } split(/\s+/, $2);
79         if ($1 eq "allowed") {
80                 if ($l[0] eq "(all)") { $prn{'allow_all'} = 1; }
81                 elsif ($l[0] eq "(none)") { $prn{'deny_all'} = 1; }
82                 else { $prn{'allow'} = \@l; }
83                 }
84         else { $prn{'deny'} = \@l; }
85         }
86 if ($body =~ /Options:\s*(.*)/) {
87         local $opts = $1;
88         local $o;
89         foreach $o (split(/,\s*/, $opts)) {
90                 local ($on, $ov) = split(/=/, $o);
91                 $prn{'options'}->{$on} = $ov;
92                 }
93         }
94 if ($body =~ /PPD:\s+(\S+)/ && $1 ne "none" && $1 ne "/dev/null") {
95         $prn{'ppd'} = $1;
96         }
97
98 if (!$_[1]) {
99         # request availability
100         $avl = &backquote_command("lpstat -a $esc 2>&1", 1);
101         if ($avl =~ /^\S+\s+not accepting.*\n\s+(.*)/) {
102                 $prn{'accepting'} = 0;
103                 $prn{'accepting_why'} = $1;
104                 if ($prn{'accepting_why'} eq "unknown reason") {
105                         $prn{'accepting_why'} = "";
106                         }
107                 }
108         else { $prn{'accepting'} = 1; }
109         }
110
111 # request connection
112 $con = &backquote_command("lpstat -v $esc 2>&1", 1);
113 if ($con =~ /^device for \S+:\s+(\S+)/) {
114         # Prints to a local file
115         $prn{'dev'} = $1;
116         if ($prn{'dev'} eq "/dev/null" &&
117             ($prn{'iface'} eq $netstandard_interface ||
118              $prn{'iface'} eq $foomatic_netstandard_interface)) {
119                 # Actually a remote TCP printer
120                 local ($dh, $dp) = split(/:/, $prn{'options'}->{'dest'});
121                 if ($dh) {
122                         $prn{'dhost'} = $dh;
123                         $prn{'dport'} = $dp;
124                         delete($prn{'dev'});
125                         delete($prn{'iface'});
126                         }
127                 }
128         }
129 elsif ($con =~ /^system for \S+:\s+(\S+)\s+\(as printer (\S+)\)/ ||
130        $con =~ /^system for \S+:\s+(\S+)/) {
131         # Prints to a remote server
132         $prn{'rhost'} = $1;
133         $prn{'rqueue'} = $2 || $prn{'name'};
134         $sys = &backquote_command("lpsystem -l $prn{'rhost'} 2>&1", 1);
135         $sys =~ /Type:\s+(\S+)/; $prn{'rtype'} = $1;
136         }
137
138 # Check if this is the default printer
139 if (!defined($default_printer)) {
140         if (&backquote_command("lpstat -d 2>&1", 1) =~ /destination:\s+(\S+)/) {
141                 $default_printer = $1;
142                 }
143         }
144 if ($default_printer eq $prn{'name'}) { $prn{'default'} = 1; }
145
146 return \%prn;
147 }
148
149 # get_jobs(printer)
150 sub get_jobs
151 {
152 local @jobs;
153 if ($_[0] =~ /\-/ || $config{'always_lpq'}) {
154         # Apparently lpq must be used if the printer name contains a -
155         local $esc = quotemeta($_[0]);
156         local $lpq = &has_command("lpq") || "/usr/ucb/lpq";
157         open(LPQ, "$lpq -P$esc |");
158         while(<LPQ>) {
159                 s/\r|\n//g;
160                 if (/^Rank\s+Owner\s+/i) { $doneheader++; }
161                 elsif ($doneheader &&
162                        /^(\S+)\s+(\S+)\s+(\d+)\s+(.*\S)\s+(\d+)\s+(\S+)$/) {
163                         local(%job);
164                         $job{'id'} = $3;
165                         $job{'user'} = $2;
166                         $job{'size'} = $5;
167                         $job{'file'} = $4;
168                         $job{'printing'} = ($1 eq "active");
169                         push(@jobs, \%job);
170                         }
171                 }
172         close(LPQ);
173         }
174 else {
175         # Can use the normal lpstat command
176         local $esc = quotemeta($_[0]);
177         open(STAT, "lpstat -o $esc |");
178         while(<STAT>) {
179                 if (/^(\S+-(\d+))\s+(\S+)\s+(\d+)\s+(\S+ \d+ \d+:\d+)\s+(.*)/) {
180                         local(%job, $d, $f, @pf);
181                         $job{'id'} = $1;
182                         local $id = $2;
183                         $job{'user'} = $3;
184                         $job{'size'} = $4;
185                         $job{'when'} = $5;
186                         $job{'printing'} = ($6 =~ /^on /);
187                         if ($job{'user'} =~ /(\S+)\!/ &&
188                             -d ($d="/var/spool/lp/tmp/$1")) {
189                                 opendir(DIR, $d);
190                                 foreach $f (readdir(DIR)) {
191                                         push(@pf, "$d/$f")
192                                                 if ($f =~ /^$id-[1-9]/);
193                                         }
194                                 closedir(DIR);
195                                 $job{'printfile'} = @pf ? \@pf : undef;
196                                 }
197                         push(@jobs, \%job);
198                         }
199                 }
200         close(STAT);
201         }
202 return @jobs;
203 }
204
205 # printer_support(option)
206 sub printer_support
207 {
208 return $_[0] !~ /^(msize|alias|riface|rnoqueue|ipp)$/;
209 }
210
211 # list_classes()
212 # Returns an associative array of print classes
213 sub list_classes
214 {
215 local($stat, %rv);
216 $stat = &backquote_command("lpstat -c 2>&1", 1);
217 while($stat =~ /^members of class (\S+):\n((\s+\S+\n)+)([\000-\377]*)$/) {
218         $stat = $4;
219         $rv{$1} = [ grep { $_ ne "" } split(/\s+/, $2) ];
220         }
221 return \%rv;
222 }
223
224 # create_printer(&details)
225 # Create a new printer
226 sub create_printer
227 {
228 &modify_printer($_[0]);
229 }
230
231 # modify_printer(&details)
232 # Change an existing printer
233 sub modify_printer
234 {
235 local(%prn, $cmd, $out);
236 %prn = %{$_[0]};
237 local $old = &get_printer($prn{'name'});
238
239 # call lpsystem if needed
240 local $tesc = quotemeta($prn{'rtype'});
241 local $resc = quotemeta($prn{'rhost'});
242 local $qesc = quotemeta($prn{'rqueue'});
243 if ($prn{'rhost'}) {
244         $out = &backquote_logged(
245                 "lpsystem -t $tesc $resc 2>&1");
246         if ($?) { &error("lpsystem failed : <pre>$out</pre>"); }
247         }
248
249 # call lpadmin
250 local $esc = quotemeta($prn{'name'});
251 local $desc = quotemeta($prn{'desc'}) || "''";
252 $cmd = "lpadmin -p $esc -D $desc";
253 if ($prn{'allow_all'}) { $cmd .= " -u allow:all"; }
254 elsif ($prn{'deny_all'}) { $cmd .= " -u deny:all"; }
255 elsif ($prn{'allow'}) {
256         &system_logged("lpadmin -p $esc -u deny:all >/dev/null 2>&1");
257         $cmd .= " -u allow:".join(',', map { quotemeta($_) } @{$prn{'allow'}});
258         }
259 elsif ($prn{'deny'}) {
260         &system_logged("lpadmin -p $esc -u allow:all >/dev/null 2>&1");
261         $cmd .= " -u deny:".join(',', map { quotemeta($_) } @{$prn{'deny'}});
262         }
263 if ($prn{'dev'}) {
264         # Just printing to a device file
265         local $vesc = quotemeta($prn{'dev'});
266         $cmd .= " -v $vesc";
267         if ($prn{'iface'}) {
268                 local $iesc = quotemeta($prn{'iface'});
269                 $cmd .= " -i $iesc";
270                 }
271         else {
272                 if ($prn{'ppd'}) {
273                         $cmd .= " -i $foomatic_interface";
274                         }
275                 else {
276                         $cmd .= " -i $default_interface";
277                         }
278                 }
279         if ($prn{'banner'}) { $cmd .= " -o banner"; }
280         else { $cmd .= " -o nobanner"; }
281         }
282 elsif ($prn{'dhost'}) {
283         # Printing to a remote host
284         local $hesc = quotemeta($prn{'dhost'});
285         local $pesc = quotemeta($prn{'dport'});
286         $cmd .= " -v /dev/null -o dest=$hesc:$pesc -o protocol=tcp";
287         if ($prn{'ppd'}) {
288                 $cmd .= " -i $foomatic_netstandard_interface";
289                 }
290         else {
291                 $cmd .= " -i $netstandard_interface";
292                 }
293         if ($prn{'banner'}) { $cmd .= " -o banner"; }
294         else { $cmd .= " -o nobanner"; }
295         }
296 else {
297         # Printing to remote LPR server
298         $cmd .= " -s $resc!$qesc";
299         }
300
301 # Add any content types
302 local @ctype = @{$prn{'ctype'}};
303 if (@ctype) {
304         $cmd .= " -I ".join(',' , @ctype);
305         }
306
307 # Add PPD option
308 if ($_[0]->{'ppd'}) {
309         $cmd .= " -n ".quotemeta($_[0]->{'ppd'});
310         }
311 elsif ($old->{'ppd'}) {
312         # Need to clear out PPD .. but how?
313         $cmd .= " -n /dev/null";
314         }
315
316 local $out = &backquote_logged("cd / ; $cmd 2>&1");
317 if ($?) { &error("lpadmin failed : <pre>$out ($cmd)</pre>"); }
318
319 # make the default
320 if ($prn{'default'}) {
321         $out = &backquote_logged("cd / ; lpadmin -d $esc 2>&1");
322         if ($?) { &error("Failed to set default : <pre>$out</pre>"); }
323         }
324
325 # Build filter table 
326 &open_execute_command(STAT, "/usr/bin/ls -1 /etc/lp/fd/*.fd", 1, 1);
327 while(<STAT>) {
328         $file = substr($_, rindex($_, "/") +1, -4 );
329         &system_logged("/usr/sbin/lpfilter -f $file -F /etc/lp/fd/$file.fd");
330         }
331 close(STAT);
332
333 # enable or disable
334 if ($prn{'enabled'}) { $cmd = "enable $esc"; }
335 elsif ($prn{'enabled_why'}) {
336         local $wesc = quotemeta($prn{'enabled_why'});
337         $cmd = "enable $esc ; disable -r $wesc $esc";
338         }
339 else { $cmd = "enable $esc ; disable $esc"; }
340 $out = &backquote_logged("$cmd 2>&1");
341
342 # accepting or rejecting requests
343 if ($prn{'accepting'}) { $cmd = "accept $esc"; }
344 elsif ($prn{'accepting_why'}) {
345         local $wesc = quotemeta($prn{'accepting_why'});
346         $cmd = "accept $esc ; reject -r $wesc $esc";
347         }
348 else { $cmd = "accept $esc ; reject $esc"; }
349 $out = &backquote_logged("$cmd 2>&1");
350 }
351
352 # delete_printer(name)
353 # Deletes some existing printer
354 sub delete_printer
355 {
356 local($out);
357 local $esc = quotemeta($_[0]);
358 $out = &backquote_logged("lpadmin -x $esc 2>&1");
359 if ($?) { &error("lpadmin failed : <pre>$out</pre>"); }
360 }
361
362 # cancel_job(printer, id)
363 # Cancels some print job
364 sub cancel_job
365 {
366 local($out);
367 local $iesc = quotemeta($_[1]);
368 if ($_[0] =~ /\-/ || $config{'always_lpq'}) {
369         # lprm must be used if printer name contains a -
370         local $esc = quotemeta($_[0]);
371         local $lprm = &has_command("lprm") || "/usr/ucb/lprm";
372         $out = &backquote_logged("$lprm -P$esc $iesc 2>&1");
373         if ($?) { &error("cancel failed : <pre>$out</pre>"); }
374         }
375 else {
376         $out = &backquote_logged("cancel $iesc 2>&1");
377         if ($?) { &error("cancel failed : <pre>$out</pre>"); }
378         }
379 sleep(1);
380 }
381
382 # sched_running()
383 # Returns the pid if lpsched is running, 0 if not, -1 if cannot be stopped
384 sub sched_running
385 {
386 local @pid = &find_byname("lpsched");
387 if (@pid) { return $pid[0]; }
388 return 0;
389 }
390
391 # start_sched()
392 # Start lpsched
393 sub start_sched
394 {
395 local $out = &backquote_logged("$config{'lpschedcmd'} 2>&1");
396 if ($?) { &error("failed to start lpsched : <tt>$out</tt>"); }
397 }
398
399 # stop_sched(pid)
400 # Stop the running lpsched process
401 sub stop_sched
402 {
403 local $out = &backquote_logged("lpshut 2>&1");
404 if ($?) { &error("lpshut failed : <tt>$out</tt>"); }
405 }
406
407 # print_command(printer, file)
408 # Returns the command to print some file on some printer
409 sub print_command
410 {
411 local $esc = quotemeta($_[0]);
412 local $fesc = quotemeta($_[1]);
413 return "lp -d $esc $fesc";
414 }
415
416 # check_print_system()
417 sub check_print_system
418 {
419 local $lpschedcmd = $config{'lpschedcmd'};
420 &has_command("lpstat") || return &text('solaris_ecmd', "<tt>lpstat</tt>");
421 &has_command("$lpschedcmd") || return &text('solaris_ecmd', "<tt>$lpschedcmd</tt>");
422 return undef;
423 }
424
425 #
426 # get_device_files()
427 #
428 sub get_device_files
429 {
430
431 local @files;
432 #
433 # There are a string of files that could be used here, include only the
434 # ones that actually exist.  Start with the parallel ports, and then the
435 # term ports, lp and printers/*.
436
437 $devlist = "/dev/bpp*";
438 $devlist .= " /dev/ecpp*";
439 $devlist .= " /dev/term/*";
440 $devlist .= " /dev/lp*";
441 $devlist .= " /dev/printers/*";
442 open(DEV, "/bin/find $devlist -print 2>/dev/null |");
443 while(<DEV>) {
444         push (@files, $_);
445         }
446 close(DEV);
447 #
448 # And also include /dev/null
449 #
450 push (@files, "/dev/null");
451 return @files;
452
453 }
454
455 #
456 # get_device_names(@files)
457 #
458 sub get_device_names
459 {
460 local @files = @_;
461 local @names;
462 #
463 # There are a string of files that could be used here, include only the
464 # ones that actually exist.  Start with the parallel ports, and then the
465 # term ports, lp and printers/*.
466
467 for (@files) {
468         if (/bpp/) { push(@names, "$text{'solaris_paralel'} - $_"); }
469         elsif (/ecpp/) { push(@names, "$text{'solaris_paralel'} - $_"); }
470         elsif (/term\/a/) { push(@names, &text('solaris_serial', 'A') . " - $_"); }
471         elsif (/term\/b/) { push(@names, &text('solaris_serial', 'B') . " - $_"); }
472         elsif (/null/) { push(@names, "$text{'solaris_null'} - $_"); }
473         else { push(@names, "$_"); }
474 }
475
476 return @names;
477
478 }
479
480 @device_files = &get_device_files();
481 @device_names = &get_device_names(@device_files);
482
483