2 # Functions for configuring and adding printers
4 BEGIN { push(@INC, ".."); };
7 do "$config{'print_style'}-lib.pl";
8 if ($config{'driver_style'}) {
9 do "$config{'driver_style'}-driver.pl";
12 do "webmin-driver.pl";
14 %access = &get_module_acl();
16 $drivers_directory = "$module_config_directory/drivers";
22 for($i=0; $i<@device_files; $i++) {
23 if ($device_files[$i] eq $_[0]) { return $device_names[$i]; }
29 # Does this system have ghostscript installed?
32 return &has_command($config{'gs_path'});
36 # Does this system have smbclient installed?
39 return &has_command($config{'smbclient_path'});
43 # Does this system have hpnp installed?
46 return &has_command($config{'hpnp_path'});
49 # create_webmin_driver(&printer, &driver)
50 # lpadmin drivers are files in /etc/webmin/lpadmin/drivers. Each is a
51 # dynamically generated shell script which calls GS
52 sub create_webmin_driver
54 # check for non-driver
55 if ($_[1]->{'mode'} == 0) {
58 elsif ($_[1]->{'mode'} == 2) {
59 return $_[1]->{'program'};
62 local($drv, $d, $gsdrv, $res, $perl);
63 &lock_file($drivers_directory);
64 mkdir($drivers_directory, 0755);
65 &unlock_file($drivers_directory);
66 $drv = "$drivers_directory/$_[0]->{'name'}";
68 # Find ghostscript driver
69 if ($_[1]->{'mode'} == 3) {
73 foreach $d (&list_webmin_drivers()) {
74 if ($d->[1] eq $_[1]->{'type'}) {
80 # Create script to call GS
81 &open_lock_tempfile(DRV, ">$drv");
82 &print_tempfile(DRV, "#!/bin/sh\n");
83 &print_tempfile(DRV, "# Name: $_[0]->{'name'}\n");
84 &print_tempfile(DRV, "# Type: ",$_[1]->{'upp'} ? 'uniprint'
85 : $_[1]->{'type'},"\n");
86 &print_tempfile(DRV, "# DPI: ",$_[1]->{'upp'} ? $_[1]->{'upp'}
87 : $_[1]->{'dpi'},"\n");
88 if ($gconfig{'ld_env'}) {
89 &print_tempfile(DRV, "$gconfig{'ld_env'}=$gconfig{'ld_path'}\n");
91 &print_tempfile(DRV, "PATH=$gconfig{'path'}\n");
92 if ($config{'gs_fontpath'}) {
93 &print_tempfile(DRV, "GS_FONTPATH=$config{'gs_fontpath'}\n");
95 if ($config{'gs_lib'}) {
96 &print_tempfile(DRV, "GS_LIB=$config{'gs_lib'}\n");
98 &print_tempfile(DRV, "export $gconfig{'ld_env'} PATH GS_FONTPATH GS_LIB\n");
99 $res = $_[1]->{'upp'} ? "\@$_[1]->{'upp'}.upp" :
100 $_[1]->{'dpi'} ? "-r$_[1]->{'dpi'}" : "";
101 $perl = &get_perl_path();
102 if ($config{'iface_arg'}) {
103 for($i=0; $i<$config{'iface_arg'}-1; $i++) {
104 &print_tempfile(DRV, "shift\n");
106 &print_tempfile(DRV, "cat \$* | $perl -e 'while(<STDIN>) { print if (!/^\\s*#####/); }' >/tmp/\$\$.gsin\n");
109 &print_tempfile(DRV, "$perl -e 'while(<STDIN>) { print if (!/^\\s*#####/); }' >/tmp/\$\$.gsin\n");
111 &print_tempfile(DRV, "$config{'gs_path'} -sOutputFile=/tmp/\$\$.gs -dSAFER -sDEVICE=$gsdrv $res -dNOPAUSE /tmp/\$\$.gsin </dev/null >/dev/null 2>&1\n");
112 &print_tempfile(DRV, "rm /tmp/\$\$.gsin\n");
113 &print_tempfile(DRV, "cat /tmp/\$\$.gs\n");
114 &print_tempfile(DRV, "rm /tmp/\$\$.gs\n");
115 &close_tempfile(DRV);
116 if ($config{'iface_owner'}) {
117 &system_logged("chown '$config{'iface_owner'}' '$drv' >/dev/null 2>&1");
119 &system_logged("chmod '$config{'iface_perms'}' '$drv' >/dev/null 2>&1");
123 # create_webmin_windows_driver(&printer, &driver)
124 # Create an interface program that can print to a remote windows printer
125 # using some printer driver
126 sub create_webmin_windows_driver
129 &lock_file($drivers_directory);
130 mkdir($drivers_directory, 0755);
131 &unlock_file($drivers_directory);
132 $drv = "$drivers_directory/$_[0]->{'name'}.smb";
134 # Create script to call smbclient
135 &open_lock_tempfile(DRV, ">$drv");
136 &print_tempfile(DRV, "#!/bin/sh\n");
137 &print_tempfile(DRV, "# Name: $_[0]->{'name'}\n");
138 &print_tempfile(DRV, "# Server: $_[1]->{'server'}\n");
139 &print_tempfile(DRV, "# Share: $_[1]->{'share'}\n");
140 &print_tempfile(DRV, "# User: $_[1]->{'user'}\n");
141 &print_tempfile(DRV, "# Password: $_[1]->{'pass'}\n");
142 &print_tempfile(DRV, "# Workgroup: $_[1]->{'workgroup'}\n");
143 &print_tempfile(DRV, "# Program: $_[1]->{'program'}\n");
144 if ($gconfig{'ld_env'}) {
145 &print_tempfile(DRV, "$gconfig{'ld_env'}=$gconfig{'ld_path'}\n");
147 &print_tempfile(DRV, "PATH=$gconfig{'path'}\n");
148 &print_tempfile(DRV, "export $gconfig{'ld_env'} PATH\n");
149 if (!$_[1]->{'program'}) {
150 if ($config{'iface_arg'}) {
151 for($i=0; $i<$config{'iface_arg'}-1; $i++) {
152 &print_tempfile(DRV, "shift\n");
154 &print_tempfile(DRV, "cat \$* >/tmp/\$\$.smb\n");
156 else { &print_tempfile(DRV, "cat >/tmp/\$\$.smb\n"); }
159 &print_tempfile(DRV, "$_[1]->{'program'} \"\$1\" \"\$2\" \"\$3\" \"\$4\" ",
160 "\"\$5\" \"\$6\" \"\$7\" \"\$8\" \"\$9\" ",
161 "\"\$10\" \"\$11\" \"\$12\" \"\$13\" >/tmp/\$\$.smb\n");
162 &system_logged("chmod a+rx '$_[1]->{'program'}'");
164 &print_tempfile(DRV, "$config{'smbclient_path'} '//$_[1]->{'server'}/$_[1]->{'share'}' ",
165 $_[1]->{'pass'} ? $_[1]->{'pass'} : "-N",
166 $_[1]->{'user'} ? " -U $_[1]->{'user'}" : "",
167 $_[1]->{'workgroup'} ? " -W $_[1]->{'workgroup'}" : "",
168 " -c \"print /tmp/\$\$.smb\"\n");
169 &print_tempfile(DRV, "rm /tmp/\$\$.smb\n");
170 &close_tempfile(DRV);
171 if ($config{'iface_owner'}) {
172 &system_logged("chown '$config{'iface_owner'}' '$drv' >/dev/null 2>&1");
174 &system_logged("chmod '$config{'iface_perms'}' '$drv' >/dev/null 2>&1");
178 # create_hpnp_driver(&printer, &driver)
179 # Create an interface program that can print to a hpnp server using some
181 sub create_hpnp_driver
184 &lock_file($drivers_directory);
185 mkdir($drivers_directory, 0755);
186 &unlock_file($drivers_directory);
187 $drv = "$drivers_directory/$_[0]->{'name'}.hpnp";
189 # Create script to call hpnp
190 &open_lock_tempfile(DRV, ">$drv");
191 &print_tempfile(DRV, "#!/bin/sh\n");
192 &print_tempfile(DRV, "# Name: $_[0]->{'name'}\n");
193 &print_tempfile(DRV, "# Server: $_[1]->{'server'}\n");
194 &print_tempfile(DRV, "# Port: $_[1]->{'port'}\n");
195 &print_tempfile(DRV, "# Program: $_[1]->{'program'}\n");
196 if ($gconfig{'ld_env'}) {
197 &print_tempfile(DRV, "$gconfig{'ld_env'}=$gconfig{'ld_path'}\n");
199 &print_tempfile(DRV, "PATH=$gconfig{'path'}\n");
200 &print_tempfile(DRV, "export $gconfig{'ld_env'} PATH\n");
201 if (!$_[1]->{'program'}) {
202 if ($config{'iface_arg'}) {
203 for($i=0; $i<$config{'iface_arg'}-1; $i++) {
204 &print_tempfile(DRV, "shift\n");
206 &print_tempfile(DRV, "cat \$* >/tmp/\$\$.hpnp\n");
208 else { &print_tempfile(DRV, "cat >/tmp/\$\$.hpnp\n"); }
211 &print_tempfile(DRV, "$_[1]->{'program'} \"\$1\" \"\$2\" \"\$3\" \"\$4\" ",
212 "\"\$5\" \"\$6\" \"\$7\" \"\$8\" \"\$9\" ",
213 "\"\$10\" \"\$11\" \"\$12\" \"\$13\" >/tmp/\$\$.hpnp\n");
214 &system_logged("chmod a+rx '$_[1]->{'program'}'");
216 &print_tempfile(DRV, "$config{'hpnp_path'} -x $_[1]->{'server'}",
217 $_[1]->{'port'} ? " -p $_[1]->{'port'}" : "",
218 " /tmp/\$\$.hpnp\n");
219 &print_tempfile(DRV, "rm /tmp/\$\$.hpnp\n");
220 &close_tempfile(DRV);
221 if ($config{'iface_owner'}) {
222 &system_logged("chown $config{'iface_owner'} $drv >/dev/null 2>&1");
224 &system_logged("chmod '$config{'iface_perms'}' '$drv' >/dev/null 2>&1");
229 # is_webmin_driver(path)
230 # Returns a structure of driver information
233 local($l, $i, $u, $desc);
235 return { 'mode' => 0,
238 if (&has_ghostscript()) {
240 for($i=0; $i<4; $i++) { $l .= <DRV>; }
242 if ($l =~ /# Name: (.*)\n# Type: (.*)\n# DPI: (.*)\n/) {
243 if ($2 eq 'uniprint') {
245 foreach $u (&list_uniprint()) {
246 $desc = $u->[1] if ($u->[0] eq $upp);
249 return { 'mode' => 3,
251 'desc' => $desc ? $desc : $upp };
254 return { 'type' => $2,
257 'desc' => $3 ? "$2 ($3 DPI)" : $2 };
261 return { 'desc' => $_[0],
266 # is_webmin_windows_driver(path)
267 # Returns a structure containing information about a windows driver, or undef
268 # Returns the server, share, username, password, workgroup, program
269 # if path is a webmin windows driver
270 sub is_webmin_windows_driver
273 if (!&has_smbclient()) { return undef; }
275 for($i=0; $i<8; $i++) { $l .= <DRV>; }
277 if ($l =~ /# Name: (.*)\n# Server: (.*)\n# Share: (.*)\n# User: (.*)\n# Password: (.*)\n# Workgroup: (.*)\n# Program: (.*)\n/) {
278 return { 'server' => $2,
285 elsif ($l =~ /# Name: (.*)\n# Server: (.*)\n# Share: (.*)\n# User: (.*)\n# Password: (.*)\n# Program: (.*)\n/) {
286 return { 'server' => $2,
292 else { return undef; }
295 # delete_webmin_driver(name)
296 # Delete the drivers for some printer
297 sub delete_webmin_driver
299 &lock_file("$drivers_directory/$_[0]");
300 &lock_file("$drivers_directory/$_[0].smb");
301 &lock_file("$drivers_directory/$_[0].hpnp");
302 unlink("$drivers_directory/$_[0]");
303 unlink("$drivers_directory/$_[0].smb");
304 unlink("$drivers_directory/$_[0].hpnp");
305 &unlock_file("$drivers_directory/$_[0]");
306 &unlock_file("$drivers_directory/$_[0].smb");
307 &unlock_file("$drivers_directory/$_[0].hpnp");
310 # is_hpnp_driver(path, &printer)
311 # Returns a structure of hpnp details if path is a webmin hpnp driver
315 if (!&has_hpnp()) { return undef; }
317 for($i=0; $i<5; $i++) { $l .= <DRV>; }
319 if ($l =~ /# Name: (.*)\n# Server: (.*)\n# Port: (.*)\n# Program: (.*)\n/) {
320 return { 'server' => $2,
324 else { return undef; }
327 # webmin_driver_input(&printer, &driver)
328 sub webmin_driver_input
330 local ($prn, $drv) = @_;
332 printf "<tr> <td><input type=radio name=drv value=0 %s> %s</td>\n",
333 $drv->{'mode'} == 0 ? "checked" : "", $text{'webmin_none'};
334 print "<td>($text{'webmin_nonemsg'})</td> </tr>\n";
336 printf "<tr> <td><input type=radio name=drv value=2 %s> %s</td>\n",
337 $drv->{'mode'} == 2 ? "checked" : "", $text{'webmin_prog'};
338 printf "<td><input name=iface value=\"%s\" size=35></td> </tr>\n",
339 $drv->{'mode'} == 2 ? $drv->{'prog'} : "";
341 if (&has_ghostscript()) {
342 local $out = &backquote_command("$config{'gs_path'} -help 2>&1", 1);
343 if ($out =~ /Available devices:\n((\s+.*\n)+)/i) {
344 print "<tr> <td valign=top>\n";
345 printf "<input type=radio name=drv value=1 %s>\n",
346 $drv->{'mode'} == 1 ? "checked" : "";
347 print "$text{'webmin_driver'}</td> <td valign=top>";
348 foreach $d (split(/\s+/, $1)) { $drvsupp{$d}++; }
349 print "<select name=driver size=7>\n";
350 foreach $d (&list_webmin_drivers()) {
351 if ($drvsupp{$d->[0]}) {
352 printf "<option value='%s' %s>%s (%s)\n",
354 $d->[1] eq $drv->{'type'} ? "selected" : "",
358 print "</select> ";
359 print "<select name=dpi size=7>\n";
360 printf "<option value=\"\" %s>Default\n",
361 $drv->{'dpi'} ? "" : "selected";
362 foreach $d (75, 100, 150, 200, 300, 600, 720, 1440) {
363 printf "<option value=\"$d\" %s>$d DPI\n",
364 $drv->{'dpi'} == $d ? "selected" : "";
366 print "</select></td> </tr>\n";
368 if ($drvsupp{'uniprint'}) {
369 print "<tr> <td valign=top>\n";
370 printf "<input type=radio name=drv value=3 %s>\n",
371 $drv->{'mode'} == 3 ? "checked" : "";
372 print "$text{'webmin_uniprint'}</td> <td valign=top>";
373 print "<select name=uniprint size=5>\n";
374 foreach $u (&list_uniprint()) {
375 printf "<option value=%s %s>%s\n",
377 $u->[0] eq $drv->{'upp'} ? 'selected' : '',
380 print "</select></td> </tr>\n";
384 print "<tr> <td colspan=2>",
385 &text('webmin_edrivers', "<tt>$config{'gs_path'}</tt>"),
389 elsif ($config{'gs_path'}) {
390 print "<tr> <td colspan=2>",
391 &text('webmin_egs', "<tt>$config{'gs_path'}</tt>"),
397 # parse_webmin_driver()
398 # Parse driver selection from %in and return a driver structure
399 sub parse_webmin_driver
401 if ($in{'drv'} == 0) {
402 return { 'mode' => 0 };
404 elsif ($in{'drv'} == 2) {
405 my @iface = split(/\s+/, $in{'iface'});
406 -x $iface[0] || &error(&text('webmin_edriver', $iface[0]));
407 return { 'mode' => 2,
408 'program' => $in{'iface'} };
410 elsif ($in{'drv'} == 1) {
411 return { 'mode' => 1,
412 'type' => $in{'driver'},
413 'dpi' => $in{'dpi'} };
415 elsif ($in{'drv'} == 3) {
416 return { 'mode' => 3,
417 'upp' => $in{'uniprint'} };
423 # list_webmin_drivers()
424 sub list_webmin_drivers
427 open(DRIVERS, "$module_root_directory/drivers");
430 push(@rv, [ $1, $2 ]);
436 # can_edit_printer(printer)
439 foreach $p (split(/\s+/, $access{'printers'})) {
440 return 1 if ($p eq '*' || $p eq $_[0]);
445 # can_edit_jobs(printer, user)
449 if ($access{'cancel'} == 1) {
452 elsif ($access{'cancel'} == 0) {
456 foreach $p (split(/\s+/, $access{'jobs'})) {
457 $rv = 1 if ($p eq $_[0]);
461 if ($access{'user'} eq '*') {
464 elsif ($access{'user'} eq $_[1]) {
467 elsif (!$access{'user'} && $remote_user eq $_[1]) {
475 # Returns a list of uniprint drivers support by the installed ghostscript
479 local $out = &backquote_command("$config{'gs_path'} -help 2>&1", 1);
480 if ($out =~ /Search path:\n((\s+.*\n)+)/i) {
481 foreach $d (split(/\s+/, $1)) {
482 next if ($d !~ /^\//);
484 while($f = readdir(DIR)) {
485 next if ($f !~ /^(.*)\.upp$/);
490 next if ($line !~ /upModel="(.*)"/i);
491 push(@rv, [ $upp, $1 ]);
496 return sort { $a->[0] cmp $b->[0] } @rv;
501 local ($drv, $wdrv, $hdrv);
502 if (!$webmin_windows_driver) {
503 $wdrv = &is_webmin_windows_driver($_[0]->{'iface'}, $_[0]);
505 $wdrv = &is_windows_driver($_[0]->{'iface'}, $_[0]) if (!$wdrv);
506 $hdrv = &is_hpnp_driver($_[0]->{'iface'}, $_[0]);
507 local $iface = $wdrv ? $wdrv->{'program'} :
508 $hdrv ? $hdrv->{'program'} : $_[0]->{'iface'};
510 if (!$webmin_print_driver) {
511 $drv = &is_webmin_driver($iface, $_[0]);
513 $drv = &is_driver($iface, $_[0])
514 if ($drv->{'mode'} == 0 || $drv->{'mode'} == 2);
515 $drv->{'desc'} =~ s/\([^\)]+\)$//;
517 return { 'driver' => $drv->{'desc'},
518 'mode' => $drv->{'mode'},
519 'dest' => $wdrv ? "\\\\$wdrv->{'server'}\\$wdrv->{'share'}" :
520 $hdrv ? "HPNP $hdrv->{'server'}:$hdrv->{'port'}" :
521 $_[0]->{'rhost'} ? "$_[0]->{'rhost'}:$_[0]->{'rqueue'}" :
522 $_[0]->{'dhost'} ? "$_[0]->{'dhost'}:$_[0]->{'dport'}" :
523 &dev_name($_[0]->{'dev'}) };
526 # parse_cups_ppd(file)
527 # Converts a CUPS-style .ppd file into a hash of names and values
532 if ($file =~ /\.gz$/) {
533 open(PPD, "gunzip -c ".quotemeta($file)." |");
539 if (/^\s*\*(\S+):\s*"(.*)"/ || /^\s*\*(\S+):\s*(\S+)/) {
542 elsif (/^\s*\*(\S+)\s+(\S+)\/([^:]+):/) {
543 $ppd{$1}->{$2} = $3 if (!defined($ppd{$1}->{$2}));
550 # list_cluster_servers()
551 # Returns a list of servers on which printers are managed
552 sub list_cluster_servers
554 &foreign_require("servers", "servers-lib.pl");
555 local %ids = map { $_, 1 } split(/\s+/, $config{'servers'});
556 return grep { $ids{$_->{'id'}} } &servers::list_servers();
559 # add_cluster_server(&server)
560 sub add_cluster_server
562 local @sids = split(/\s+/, $config{'servers'});
563 $config{'servers'} = join(" ", @sids, $_[0]->{'id'});
564 &save_module_config();
567 # delete_cluster_server(&server)
568 sub delete_cluster_server
570 local @sids = split(/\s+/, $config{'servers'});
571 $config{'servers'} = join(" ", grep { $_ != $_[0]->{'id'} } @sids);
572 &save_module_config();
575 # server_name(&server)
578 return $_[0]->{'desc'} ? $_[0]->{'desc'} : $_[0]->{'host'};
581 # save_printer_cluster(new, &printer, &driver, &connection, webmin-driver, mode)
582 # Creates or updates the specified printer on all cluster hosts, and returns a
583 # list of error messages
586 return ( ) if (!$config{'servers'});
587 return ( ) if (&is_readonly_mode());
588 local ($new, $prn, $drv, $conn, $webmin, $mode) = @_;
589 &remote_error_setup(\&slave_error_handler);
592 foreach $slave (&list_cluster_servers()) {
594 $slave_error = undef;
595 &remote_foreign_require($slave, "lpadmin", "lpadmin-lib.pl");
597 push(@slaveerrs, [ $slave, $slave_error ]);
601 # Create the driver and the printer
602 local $err = &remote_foreign_call($slave,
603 "lpadmin", "save_printer_and_driver",
604 $new, $prn, $drv, $conn, $webmin, $mode);
606 push(@slaveerrs, [ $slave, $slave_error ]);
609 push(@slaveerrs, [ $slave, &text('save_edup', $prn->{'name'}) ]);
612 push(@slaveerrs, [ $slave, $text{'save_evalid'} ]);
615 push(@slaveerrs, [ $slave, &text('save_egone', $prn->{'name'}) ]);
621 # delete_on_cluster(&printer)
622 # Deletes the specified printer on all cluster hosts, and returns a list of
624 sub delete_on_cluster
626 return ( ) if (!$config{'servers'});
627 return ( ) if (&is_readonly_mode());
629 &remote_error_setup(\&slave_error_handler);
632 foreach $slave (&list_cluster_servers()) {
634 $slave_error = undef;
635 &remote_foreign_require($slave, "lpadmin", "lpadmin-lib.pl");
637 push(@slaveerrs, [ $slave, $slave_error ]);
641 # Call the delete function
642 local $err = &remote_foreign_call($slave,
643 "lpadmin", "delete_printer_and_driver", $prn);
645 push(@slaveerrs, [ $slave, $slave_error ]);
648 push(@slaveerrs, [ $slave, &text('save_egone', $prn->{'name'}) ]);
654 # save_printer_and_driver(new, &printer, &driver, &connection, webmin-driver, mode)
655 # Attempts to setup or modify a printer and driver. Returns 0 if OK, 1 if the
656 # printer already exists, 2 if some print system error occurred, or 3 if it
657 # doesn't exist but should.
658 sub save_printer_and_driver
660 local ($new, $prn, $drv, $conn, $webmin, $mode) = @_;
661 if ($new && &get_printer($prn->{'name'})) {
664 elsif (!$new && !&get_printer($prn->{'name'})) {
667 local $dfunc = $webmin ? \&create_webmin_driver : \&create_driver;
668 if ($mode <= 2 || $mode == 5) {
669 # Device, file or LPR host
670 $prn->{'iface'} = &$dfunc($prn, $drv);
674 $prn->{'dev'} = "/dev/null";
675 $prn->{'iface'} = $webmin ? &create_webmin_windows_driver($prn, $conn)
676 : &create_windows_driver($prn, $conn);
680 $prn->{'dev'} = "/dev/null";
681 $prn->{'iface'} = &create_hpnp_driver($prn, $conn);
684 # Call os-specific validation function
685 if (defined(&validate_printer)) {
686 local $err = &validate_printer($prn);
690 # Actually create or update it
692 &create_printer($prn);
695 &modify_printer($prn);
697 &system_logged("$config{'apply_cmd'} >/dev/null 2>&1 </dev/null")
698 if ($config{'apply_cmd'});
702 # delete_printer_and_driver(&printer)
703 # Deletes a printer, returning 1 if it could not be found, or 0 if everything
705 sub delete_printer_and_driver
708 &delete_printer($prn->{'name'});
709 &delete_driver($prn->{'name'});
710 &delete_webmin_driver($prn->{'name'});
713 sub slave_error_handler
715 $slave_error = $_[0];
718 # delete_from_acls(name)
719 # Remove some named printer from all ACLs
724 &read_acl(undef, \%wusers);
725 foreach my $u (keys %wusers) {
726 my %uaccess = &get_module_acl($u);
727 if ($uaccess{'printers'} ne '*') {
728 $uaccess{'printers'} =
729 join(' ', grep { $_ ne $name }
730 split(/\s+/, $uaccess{'printers'}));
731 &save_module_acl(\%uaccess, $u);