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 ©_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