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
11sub is_windows_driver
12{
13if ($_[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	}
21elsif ($_[1]->{'dev'} =~ /^smb:\/\/(\S*):(\S*)\@(\S*)\/(\S*)$/) {
22	return { 'user' => $1,
23		 'pass' => $2,
24		 'server' => $3,
25		 'share' => $4,
26		 'program' => $_[0] };
27	}
28elsif ($_[1]->{'dev'} =~ /^smb:\/\/(\S*)\/(\S*)\/(\S*)$/) {
29	return { 'workgroup' => $1,
30		 'server' => $2,
31		 'share' => $3,
32		 'program' => $_[0] };
33	}
34elsif ($_[1]->{'dev'} =~ /^smb:\/\/(\S*)\/(\S*)$/) {
35	return { 'server' => $1,
36		 'share' => $2,
37		 'program' => $_[0] };
38	}
39else {
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
46sub is_driver
47{
48if (!$_[0] || !-r $_[0]) {
49	return { 'mode' => 0,
50		 'desc' => $text{'cups_none'} };
51	}
52local $ppd = &parse_cups_ppd($_[0]);
53if ($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	}
61else {
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)
70sub create_windows_driver
71{
72if ($_[1]->{'workgroup'} && $_[1]->{'user'}) {
73	$_[0]->{'dev'} = "smb://$_[1]->{'user'}:$_[1]->{'pass'}\@$_[1]->{'workgroup'}/$_[1]->{'server'}/$_[1]->{'share'}";
74	}
75elsif ($_[1]->{'workgroup'}) {
76	$_[0]->{'dev'} = "smb://$_[1]->{'workgroup'}/$_[1]->{'server'}/$_[1]->{'share'}";
77	}
78elsif ($_[1]->{'user'}) {
79	$_[0]->{'dev'} = "smb://$_[1]->{'user'}:$_[1]->{'pass'}\@$_[1]->{'server'}/$_[1]->{'share'}";
80	}
81else {
82	$_[0]->{'dev'} = "smb://$_[1]->{'server'}/$_[1]->{'share'}";
83	}
84return $_[1]->{'program'};
85}
86
87# create_driver(&printer, &driver)
88sub create_driver
89{
90local $drv = "$cups_ppd_dir/$_[0]->{'name'}.ppd";
91undef($cups_driver_options);
92if ($_[1]->{'mode'} == 0) {
93	&unlink_file($drv);
94	return undef;
95	}
96elsif ($_[1]->{'mode'} == 2) {
97	&unlink_file($drv);
98	return $_[1]->{'file'};
99	}
100else {
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)
116sub delete_driver
117{
118&unlink_file("$cups_ppd_dir/$_[0].ppd");
119}
120
121# driver_input(&printer, &driver)
122sub driver_input
123{
124printf "<tr> <td><input type=radio name=mode value=0 %s> %s</td>\n",
125	$_[1]->{'mode'} == 0 ? 'checked' : '', $text{'cups_none'};
126print "<td>($text{'cups_nonemsg'})</td> </tr>\n";
127printf "<tr> <td><input type=radio name=mode value=2 %s> %s</td>",
128	$_[1]->{'mode'} == 2 ? 'checked' : '', $text{'cups_prog'};
129printf "<td><input name=program size=40 value='%s'></td> </tr>\n",
130	$_[1]->{'mode'} == 2 ? $_[0]->{'iface'} : '';
131
132# Display all the CUPS drivers
133printf "<tr> <td valign=top><input type=radio name=mode value=1 %s> %s</td>\n",
134	$_[1]->{'mode'} == 1 ? 'checked' : '', $text{'cups_driver'};
135local (@ppds, $d, $f, $ppd, %cache, $outofdate, @files, %donefile);
136local $findver = &backquote_command("find --version 2>&1", 1);
137local $flag = $findver =~ /GNU\s+find\s+version\s+([0-9\.]+)/i && $1 >= 4.2 ?
138		"-L" : "";
139foreach 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);
152foreach $f (@files) {
153	if (!defined($cache{$f})) {
154		$outofdate = $f;
155		last;
156		}
157	}
158if ($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	}
170local %done;
171print "<td><select name=ppd size=10>\n";
172foreach $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	}
181print "</select>\n";
182
183# Display driver option inputs
184if ($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
212print "</td> </tr>\n";
213return undef;
214}
215
216# parse_driver()
217# Parse driver selection from %in and return a driver structure
218sub parse_driver
219{
220if ($in{'mode'} == 0) {
221	return { 'mode' => 0 };
222	}
223elsif ($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	}
229elsif ($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
2441;
245
246