Handle hostnames with upper-case letters
[webmin.git] / lpadmin / cups-driver.pl
1 # cups-driver.pl
2 # Functions for CUPS printer drivers
3
4 $webmin_windows_driver = 0;
5 $cups_ppd_dir = "/etc/cups/ppd";
6 $lpoptions = &has_command("lpoptions.cups") ? "lpoptions.cups" : "lpoptions";
7
8 # is_windows_driver(path, &printer)
9 # Returns the server, share, username, password, workgroup, program
10 # if path is a webmin windows driver
11 sub is_windows_driver
12 {
13 if ($_[1]->{'dev'} =~ /^smb:\/\/(\S*):(\S*)\@(\S*)\/(\S*)\/(\S*)$/) {
14         return { 'user' => $1,
15                  'pass' => $2,
16                  'workgroup' => $3,
17                  'server' => $4,
18                  'share' => $5,
19                  'program' => $_[0] };
20         }
21 elsif ($_[1]->{'dev'} =~ /^smb:\/\/(\S*):(\S*)\@(\S*)\/(\S*)$/) {
22         return { 'user' => $1,
23                  'pass' => $2,
24                  'server' => $3,
25                  'share' => $4,
26                  'program' => $_[0] };
27         }
28 elsif ($_[1]->{'dev'} =~ /^smb:\/\/(\S*)\/(\S*)\/(\S*)$/) {
29         return { 'workgroup' => $1,
30                  'server' => $2,
31                  'share' => $3,
32                  'program' => $_[0] };
33         }
34 elsif ($_[1]->{'dev'} =~ /^smb:\/\/(\S*)\/(\S*)$/) {
35         return { 'server' => $1,
36                  'share' => $2,
37                  'program' => $_[0] };
38         }
39 else {
40         return undef;
41         }
42 }
43
44 # is_driver(path, &printer)
45 # Returns the driver name if some path is a CUPS driver, or undef
46 sub is_driver
47 {
48 if (!$_[0] || !-r $_[0]) {
49         return { 'mode' => 0,
50                  'desc' => $text{'cups_none'} };
51         }
52 local $ppd = &parse_cups_ppd($_[0]);
53 if ($ppd->{'NickName'}) {
54         # Looks like a CUPS PPD file!
55         return { 'mode' => 1,
56                  'manuf' => $ppd->{'Manufacturer'},
57                  'model' => $ppd->{'ModelName'},
58                  'nick' => $ppd->{'NickName'},
59                  'desc' => "$ppd->{'Manufacturer'} $ppd->{'ModelName'}" };
60         }
61 else {
62         # Some other kind of interface file
63         return { 'mode' => 2,
64                  'file' => $_[0],
65                  'desc' => $_[0] };
66         }
67 }
68
69 # create_windows_driver(&printer, &driver)
70 sub create_windows_driver
71 {
72 if ($_[1]->{'workgroup'} && $_[1]->{'user'}) {
73         $_[0]->{'dev'} = "smb://$_[1]->{'user'}:$_[1]->{'pass'}\@$_[1]->{'workgroup'}/$_[1]->{'server'}/$_[1]->{'share'}";
74         }
75 elsif ($_[1]->{'workgroup'}) {
76         $_[0]->{'dev'} = "smb://$_[1]->{'workgroup'}/$_[1]->{'server'}/$_[1]->{'share'}";
77         }
78 elsif ($_[1]->{'user'}) {
79         $_[0]->{'dev'} = "smb://$_[1]->{'user'}:$_[1]->{'pass'}\@$_[1]->{'server'}/$_[1]->{'share'}";
80         }
81 else {
82         $_[0]->{'dev'} = "smb://$_[1]->{'server'}/$_[1]->{'share'}";
83         }
84 return $_[1]->{'program'};
85 }
86
87 # create_driver(&printer, &driver)
88 sub create_driver
89 {
90 local $drv = "$cups_ppd_dir/$_[0]->{'name'}.ppd";
91 undef($cups_driver_options);
92 if ($_[1]->{'mode'} == 0) {
93         &unlink_file($drv);
94         return undef;
95         }
96 elsif ($_[1]->{'mode'} == 2) {
97         &unlink_file($drv);
98         return $_[1]->{'file'};
99         }
100 else {
101         # Copy the driver into place
102         if ($_[1]->{'ppd'} =~ /\.gz$/) {
103                 &system_logged("gunzip -c ".quotemeta($_[1]->{'ppd'}).
104                                " >".quotemeta($drv));
105                 }
106         else {
107                 &copy_source_dest($_[1]->{'ppd'}, $drv);
108                 }
109         chmod(0777, $drv);
110         $cups_driver_options = $_[1]->{'opts'}; # for modify_printer
111         return $drv;
112         }
113 }
114
115 # delete_driver(name)
116 sub delete_driver
117 {
118 &unlink_file("$cups_ppd_dir/$_[0].ppd");
119 }
120
121 # driver_input(&printer, &driver)
122 sub driver_input
123 {
124 printf "<tr> <td><input type=radio name=mode value=0 %s> %s</td>\n",
125         $_[1]->{'mode'} == 0 ? 'checked' : '', $text{'cups_none'};
126 print "<td>($text{'cups_nonemsg'})</td> </tr>\n";
127 printf "<tr> <td><input type=radio name=mode value=2 %s> %s</td>",
128         $_[1]->{'mode'} == 2 ? 'checked' : '', $text{'cups_prog'};
129 printf "<td><input name=program size=40 value='%s'></td> </tr>\n",
130         $_[1]->{'mode'} == 2 ? $_[0]->{'iface'} : '';
131
132 # Display all the CUPS drivers
133 printf "<tr> <td valign=top><input type=radio name=mode value=1 %s> %s</td>\n",
134         $_[1]->{'mode'} == 1 ? 'checked' : '', $text{'cups_driver'};
135 local (@ppds, $d, $f, $ppd, %cache, $outofdate, @files, %donefile);
136 local $findver = &backquote_command("find --version 2>&1", 1);
137 local $flag = $findver =~ /GNU\s+find\s+version\s+([0-9\.]+)/i && $1 >= 4.2 ?
138                 "-L" : "";
139 foreach my $mp (split(/\s+/, $config{'model_path'})) {
140         &open_execute_command(FIND, "find $flag ".quotemeta($mp).
141                                     " -type f -print", 1, 1);
142         while(<FIND>) {
143                 chop;
144                 next if (/\.xml$/);     # Ignore XML PPD sources
145                 /([^\/]+)$/;
146                 next if ($donefile{$1}++);
147                 push(@files, $_);
148                 }
149         close(FIND);
150         }
151 &read_file("$module_config_directory/ppd-cache", \%cache);
152 foreach $f (@files) {
153         if (!defined($cache{$f})) {
154                 $outofdate = $f;
155                 last;
156                 }
157         }
158 if ($outofdate || scalar(keys %cache) != scalar(@files)) {
159         # Cache is out of date
160         undef(%cache);
161         local %donecache;
162         foreach $f (@files) {
163                 local $ppd = &parse_cups_ppd($f);
164                 local $nn = $ppd->{'NickName'};
165                 $cache{$f} = $donecache{$nn} ? "duplicate" : $ppd->{'NickName'};
166                 $donecache{$nn}++;
167                 }
168         &write_file("$module_config_directory/ppd-cache", \%cache);
169         }
170 local %done;
171 print "<td><select name=ppd size=10>\n";
172 foreach $f (sort { $cache{$a} cmp $cache{$b} } keys %cache) {
173         if ($cache{$f} && $cache{$f} ne "duplicate" &&
174             !$done{$cache{$f}}++) {
175                 printf "<option value=%s %s>%s\n",
176                         $f, $_[1]->{'nick'} eq $cache{$f} ? 'selected' : '',
177                         $cache{$f};
178                 $currppd = $f if ($_[1]->{'nick'} eq $cache{$f});
179                 }
180         }
181 print "</select>\n";
182
183 # Display driver option inputs
184 if ($currppd) {
185         local $ppd = &parse_cups_ppd($currppd);
186         print "<br><b>",&text('cups_opts', $ppd->{'NickName'}),
187               "</b><table>\n";
188         open(OPTS, "$lpoptions -p '$_[0]->{'name'}' -l 2>/dev/null |");
189         while(<OPTS>) {
190                 if (/^(\S+)\/([^:]+):\s*(.*)/ && $1 ne "PageRegion") {
191                         print "<tr>\n" if ($i%2 == 0);
192                         local $code = $1;
193                         local $disp = $2;
194                         local @opts = split(/\s+/, $3);
195                         print "<td><b>$disp:</b></td>\n";
196                         print "<td><select name=ppd_$code>\n";
197                         foreach $o (@opts) {
198                                 local $sel = ($o =~ s/^\*//);
199                                 printf "<option value='%s' %s>%s\n",
200                                         $o, $sel ? "selected" : "",
201                                         $ppd->{$code}->{$o} ?
202                                           $ppd->{$code}->{$o} : $o;
203                                 }
204                         print "</select></td>\n";
205                         print "<tr>\n" if ($i%2 == 1);
206                         }
207                 }
208         close(OPTS);
209         print "</table>\n";
210         }
211
212 print "</td> </tr>\n";
213 return undef;
214 }
215
216 # parse_driver()
217 # Parse driver selection from %in and return a driver structure
218 sub parse_driver
219 {
220 if ($in{'mode'} == 0) {
221         return { 'mode' => 0 };
222         }
223 elsif ($in{'mode'} == 2) {
224         $in{'program'} =~ /^(\S+)/ && -x $1 ||
225                 &error(&text('cups_eprog', $in{'program'}));
226         return { 'mode' => 2,
227                  'file' => $in{'program'} };
228         }
229 elsif ($in{'mode'} == 1) {
230         # CUPS printer driver
231         local $ppd = &parse_cups_ppd($in{'ppd'});
232         local $rv = { 'mode' => 1,
233                       'ppd' => $in{'ppd'},
234                       'nick' => $ppd->{'NickName'},
235                       'manuf' => $ppd->{'Manufacturer'},
236                       'model' => $ppd->{'ModelName'} };
237         foreach $i (keys %in) {
238                 $rv->{'opts'}->{$1} = $in{$i} if ($i =~ /^ppd_(.*)$/);
239                 }
240         return $rv;
241         }
242 }
243
244 1;
245